Registro de Asistencia Técnica
Registro de Asistencia Técnica
DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Registro de Asistencia del Personal Técnico</title>
<script src="[Link]
<link href="[Link]
family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f3f4f6;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 1rem;
}
.container {
max-width: 500px;
width: 100%;
background-color: #ffffff;
padding: 2.5rem;
border-radius: 1rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0,
0, 0, 0.05);
text-align: center;
}
.btn {
display: block;
width: 100%;
padding: 1rem;
font-size: 1.25rem;
font-weight: 600;
color: white;
border-radius: 0.75rem;
transition: background-color 0.2s, transform 0.2s;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 1.5rem;
}
.btn-green {
background-color: #10b981;
}
.btn-green:hover {
background-color: #059669;
transform: translateY(-2px);
}
.btn-blue {
background-color: #2563eb;
}
.btn-blue:hover {
background-color: #1d4ed8;
transform: translateY(-2px);
}
.btn-purple {
background-color: #8b5cf6;
}
.btn-purple:hover {
background-color: #7c3aed;
transform: translateY(-2px);
}
.btn-red {
background-color: #ef4444;
}
.btn-red:hover {
background-color: #dc2626;
transform: translateY(-2px);
}
h1 {
font-size: 2.5rem;
font-weight: 700;
color: #1f2937;
margin-bottom: 4rem;
text-transform: uppercase;
}
#message-box {
margin-top: 1rem;
padding: 1rem;
border-radius: 0.5rem;
text-align: center;
display: none;
font-weight: 600;
}
.message-success {
background-color: #d1fae5;
color: #065f46;
}
.message-error {
background-color: #fee2e2;
color: #991b1b;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
.modal-content {
background-color: #ffffff;
padding: 2rem;
border-radius: 1rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
width: 90%;
max-width: 600px;
position: relative;
}
.modal-close-btn {
position: absolute;
top: 1rem;
right: 1rem;
font-size: 1.5rem;
cursor: pointer;
color: #9ca3af;
}
.personnel-list-item {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f9fafb;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
margin-bottom: 0.5rem;
}
.personnel-list-item span {
font-weight: 500;
}
.action-btns button {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
font-weight: 500;
border-radius: 0.375rem;
margin-left: 0.5rem;
}
.edit-btn {
background-color: #f97316;
color: white;
}
.delete-btn {
background-color: #ef4444;
color: white;
}
.opciones-personal-btn {
margin-bottom: 1rem;
}
.[Link] {
background-color: #e5e7eb;
color: #9ca3af;
cursor: not-allowed;
box-shadow: none;
transform: none;
}
#report-list {
max-height: 400px;
overflow-y: auto;
}
.person-report-container {
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
margin-bottom: 1rem;
padding: 1rem;
}
.person-report-header {
font-weight: 700;
font-size: 1.125rem;
margin-bottom: 0.75rem;
text-align: left;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e5e7eb;
}
.report-item {
padding: 0.5rem 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.report-item .action-info {
font-size: 0.875rem;
color: #4b5563;
}
.report-dot {
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 0.5rem;
}
.dot-green { background-color: #10b981; }
.dot-yellow { background-color: #f59e0b; }
.dot-red { background-color: #ef4444; }
.watermark {
position: fixed;
bottom: 1rem;
right: 1rem;
font-size: 0.75rem;
color: #6b7280; /* Color más oscuro para mejor visibilidad */
user-select: none;
z-index: 20;
}
</style>
</head>
<body>
<div class="container" id="main-view">
<h1 class="text-3xl font-bold text-gray-800 mb-8">Registro de Asistencia
del Personal Técnico</h1>
<div id="home-buttons" class="space-y-4">
<button id="personal-operativo-btn" class="btn btn-blue text-
white">Ingresar como técnico</button>
<button id="administrador-btn" class="btn bg-gray-600 text-white
hover:bg-gray-700">Ingresar como administrador</button>
</div>
<div id="message-box"></div>
</div>
<div class="watermark">Elaborado por "DIEGO TELLO"</div>
<script type="module">
import { initializeApp } from
"[Link]
import { getAuth, signInAnonymously, signInWithCustomToken,
onAuthStateChanged } from "[Link]
[Link]";
import { getFirestore, doc, addDoc, collection, query, where, getDocs,
onSnapshot, Timestamp } from "[Link]
[Link]";
let db;
let auth;
let currentUserId;
let loggedInUser = '';
let isAuthReady = false;
const initialPersonnelList = [
{ name: "JOSELITO", password: "123" },
{ name: "DIEGO", password: "123" },
{ name: "JUAN", password: "123" },
{ name: "JESUS", password: "123" },
{ name: "CARLOS", password: "123" },
{ name: "PEDRO", password: "123" },
{ name: "SEBASTIAN", password: "123" },
{ name: "MAURICIO", password: "123" }
];
function showSuccessModal(message) {
[Link] = message;
[Link]('hidden');
setTimeout(() => {
[Link]('hidden');
}, 3000);
}
function loadPersonnel() {
let personnel = [Link]([Link]('personnelList'));
if (!personnel || [Link] === 0) {
personnel = initialPersonnelList;
savePersonnel(personnel);
}
return personnel;
}
function savePersonnel(personnel) {
[Link]('personnelList', [Link](personnel));
}
function renderPersonnel() {
const personnel = loadPersonnel();
[Link] = '';
[Link] = '<option value="" disabled selected>--
Seleccione --</option>';
if (lastAction) {
switch ([Link]) {
case 'Inicio de Turno':
[Link]('disabled');
[Link] = false;
[Link]('disabled');
[Link] = false;
break;
case 'Inicio de Descanso':
[Link]('disabled');
[Link] = false;
break;
case 'Fin de Descanso':
[Link]('disabled');
[Link] = false;
break;
case 'Fin de Turno':
[Link]('disabled');
[Link] = false;
break;
default:
// En caso de que no haya una acción previa, habilitar solo
el inicio de turno.
[Link]('disabled');
[Link] = false;
break;
}
} else {
// Si no hay ninguna acción previa, solo se puede iniciar el turno.
[Link]('disabled');
[Link] = false;
}
}
if ([Link] > 0) {
[Link]((a, b) => [Link]() -
[Link]());
return actions[0];
}
} catch (e) {
[Link]("Error fetching last action: ", e);
}
return null;
}
function formatTimestamp(timestamp) {
const date = [Link]();
return [Link]('es-ES', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
try {
const reportsCollection = collection(db,
`artifacts/${appId}/public/data/reports`);
let q = query(reportsCollection);
if (dateString) {
// FIX: Construir la fecha de manera explícita para evitar
problemas de zona horaria.
const [year, month, day] = [Link]('-').map(Number);
const startOfDayLocal = new Date(year, month - 1, day, 0, 0, 0,
0);
const endOfDayLocal = new Date(year, month - 1, day, 23, 59,
59, 999);
q = query(reportsCollection,
where("timestamp", ">=", startTimestamp),
where("timestamp", "<=", endTimestamp)
);
}
if ([Link] > 0) {
[Link](personName => {
const personReportContainer =
[Link]('div');
[Link] = 'person-report-
container';
reportData[personName].forEach(item => {
const reportItem = [Link]('div');
[Link] = 'report-item';
[Link] = `
<div class="flex items-center">
<span class="report-dot ${dotClass}"></span>
<div class="action-info">Acción: $
{[Link]}</div>
</div>
<div class="action-info">$
{formatTimestamp([Link])}</div>
`;
[Link](reportItem);
});
[Link](personReportContainer);
});
} else {
[Link] = '<p class="text-center text-gray-
500">No hay registros para mostrar en esta fecha.</p>';
}
} catch (e) {
[Link]("Error fetching report: ", e);
[Link] = '<p class="text-center text-red-
500">Error al cargar el reporte. Inténtelo de nuevo.</p>';
}
}
function hideAllModals() {
const modals = [Link]('.modal-overlay');
[Link](modal => [Link]('hidden'));
}
if (initialAuthToken) {
await signInWithCustomToken(auth, initialAuthToken)
.catch(error => {
[Link]("Error signing in with custom token:",
error);
signInAnonymously(auth);
});
} else {
await signInAnonymously(auth)
.catch(error => [Link]("Error signing in anonymously:",
error));
}
hideAllModals();
});
[Link]('click', () => {
renderPersonnel();
[Link]('hidden');
});
[Link]('click', () => {
[Link]('hidden');
});
[Link]('click', () => {
hideAllModals();
[Link]('hidden');
renderPersonnel();
});
[Link]('click', () => {
hideAllModals();
[Link]('hidden');
// Establece la fecha del reporte a la fecha actual para que se cargue
al abrir
const today = new Date();
const year = [Link]();
const month = ([Link]() + 1).toString().padStart(2, '0');
const day = [Link]().toString().padStart(2, '0');
const todayDateString = `${year}-${month}-${day}`;
[Link] = todayDateString;
renderReport(todayDateString);
});
[Link]('click', () => {
[Link]('hidden');
[Link]();
});
[Link]('click', () => {
[Link]('hidden');
[Link]();
});
[Link]('click', () => {
[Link]('hidden');
});
[Link]('click', () => {
[Link]('hidden');
[Link]();
personnelToEdit = null;
[Link]('save-person-btn').textContent = 'Guardar
Personal';
});
[Link]('click', () => {
[Link]('hidden');
});
[Link]('click', () => {
[Link]('hidden');
});
savePersonnel(personnel);
renderPersonnel();
[Link]();
});
if ([Link]('edit-btn')) {
const index = [Link];
let personnel = loadPersonnel();
const personToEdit = personnel[index];
[Link]('person-name').value = [Link];
[Link]('person-password').value =
[Link];
[Link]('save-person-btn').textContent =
'Actualizar Personal';
personnelToEdit = index;
}
});
[Link]('click', () =>
handleActionAndReturn('Inicio de Turno'));
[Link]('click', () =>
handleActionAndReturn('Inicio de Descanso'));
[Link]('click', () => handleActionAndReturn('Fin
de Descanso'));
[Link]('click', () => handleActionAndReturn('Fin de
Turno'));
</script>
</body>
</html>