const { useState, useMemo } = React; window.AdminAuditoria = function({ logs = [], globalUsers = [], absences = [], actions = [], setMapLogs, setPreviewImage, refreshData }) { const [searchName, setSearchName] = useState(''); const [searchDate, setSearchDate] = useState(window.getNICDate()); const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); const [selectedActionType, setSelectedActionType] = useState('ALL'); const [editingLog, setEditingLog] = useState(null); const [showNewMarkModal, setShowNewMarkModal] = useState(false); const [formUser, setFormUser] = useState(''); const [formType, setFormType] = useState('entrada'); const [formDate, setFormDate] = useState(window.getNICDate()); const [formTime, setFormTime] = useState('08:00'); const [formJustification, setFormJustification] = useState(''); const applyQuickFilter = (type) => { const today = new Date(); const y = today.getFullYear(); const m = String(today.getMonth() + 1).padStart(2, '0'); const d = String(today.getDate()).padStart(2, '0'); if (type === 'today') { setSearchDate(`${y}-${m}-${d}`); setStartDate(''); setEndDate(''); } else if (type === 'this_week') { const firstDay = new Date(today.setDate(today.getDate() - today.getDay() + 1)); const lastDay = new Date(today.setDate(today.getDate() - today.getDay() + 7)); setSearchDate(''); setStartDate(window.getNICDateFromObj(firstDay)); setEndDate(window.getNICDateFromObj(lastDay)); } else if (type === 'this_month') { const firstDay = new Date(y, today.getMonth(), 1); const lastDay = new Date(y, today.getMonth() + 1, 0); setSearchDate(''); setStartDate(window.getNICDateFromObj(firstDay)); setEndDate(window.getNICDateFromObj(lastDay)); } }; const formatLogType = (typeStr) => { if (!typeStr) return { label: 'MARCA', isStart: false, isEnd: false }; if (typeStr === 'entrada') return { label: 'Entrada Trabajo', isStart: true, isEnd: false }; if (typeStr === 'salida') return { label: 'Cierre de Día (Salida)', isStart: false, isEnd: true }; const isStart = typeStr.endsWith('_in'); const isEnd = typeStr.endsWith('_out') || typeStr.startsWith('vuelta_'); let cleanId = typeStr.replace('_in', '').replace('_out', '').replace('vuelta_', '').toLowerCase(); const matchedAction = (actions || []).find(a => (a.id || '').toLowerCase() === cleanId); let baseName = matchedAction ? matchedAction.label : cleanId.replace(/_\d+/g, '').replace(/_/g, ' ').toUpperCase(); let finalLabel = baseName; if (isStart) finalLabel = `Inicio ${baseName}`; else if (isEnd) finalLabel = `Fin ${baseName}`; return { label: finalLabel, isStart, isEnd }; }; const combinedAuditList = useMemo(() => { const mappedLogs = (logs || []).map(l => ({ ...l, isAbsenceRecord: false })); const mappedAbsences = (absences || []).map(a => ({ id: `abs_${a.id}`, realAbsenceId: a.id, userId: a.userId, userName: a.userName, type: a.type === 'injustificada' ? 'Falta Injustificada' : 'Falta Justificada', date: a.date, timestamp: a.createdAt || Date.now(), justification: a.notes ? `Observación: ${a.notes}` : 'Sin nota', isAbsenceRecord: true, absenceType: a.type })); return [...mappedLogs, ...mappedAbsences]; }, [logs, absences]); const filteredLogs = useMemo(() => { const result = combinedAuditList.filter(l => { const matchName = (l.userName || '').toLowerCase().includes(searchName.toLowerCase()); let matchDate = true; if (searchDate) { matchDate = l.date === searchDate; } else if (startDate && endDate) { matchDate = l.date >= startDate && l.date <= endDate; } let matchType = true; if (selectedActionType === 'ENTRADA_SALIDA') { matchType = l.type === 'entrada' || l.type === 'salida'; } else if (selectedActionType === 'ALMUERZO') { matchType = String(l.type).includes('almuerzo'); } else if (selectedActionType === 'AUSENCIAS') { matchType = l.isAbsenceRecord; } else if (selectedActionType === 'ALERTAS') { matchType = (l.justification && l.justification.startsWith('ALERTA:')) || window.isUserOffDay(globalUsers.find(u=>u.id===l.userId)?.scheduleDays, l.date); } else if (selectedActionType !== 'ALL') { matchType = String(l.type).includes(selectedActionType); } return matchName && matchDate && matchType; }); return result.sort((a,b) => { if (a.date !== b.date) return a.date > b.date ? -1 : 1; if (a.userName !== b.userName) return a.userName.localeCompare(b.userName); return a.timestamp - b.timestamp; }); }, [combinedAuditList, searchName, searchDate, startDate, endDate, selectedActionType, globalUsers]); // MONITOREO GPS EN VIVO ESTRICTO (SOLO POSICIONES REALES DE HOY) const handleOpenLiveTrackingMap = () => { const todayNIC = window.getNICDate(); // Filtrar colaboradores con GPS activo que hayan emitido señal real hoy const activeTrackers = (globalUsers || []).filter(u => { if (!u.gpsTrackingEnabled || !u.lastLat || !u.lastLng || !u.lastGpsTime) return false; // Validar que la última señal emitida pertenezca a la fecha de hoy const signalDate = window.getNICDateFromObj(new Date(u.lastGpsTime)); return signalDate === todayNIC; }); if (activeTrackers.length === 0) { alert("No hay vendedores o colaboradores transmitiendo posición GPS en tiempo real en este momento."); return; } const mapItems = activeTrackers.map(u => ({ id: `live_${u.id}`, userName: `${u.name} (@${u.alias})`, type: 'visita_in', timestamp: u.lastGpsTime, location: { lat: u.lastLat, lng: u.lastLng }, justification: `Última transmisión en vivo: ${window.formatNICTimeFull(u.lastGpsTime)}` })); setMapLogs(mapItems); }; const handleOpenEdit = (log) => { setEditingLog(log); setFormUser(log.userId); setFormType(log.type); setFormDate(log.date); const dateObj = new Date(log.timestamp); const hours = String(dateObj.getHours()).padStart(2, '0'); const mins = String(dateObj.getMinutes()).padStart(2, '0'); setFormTime(`${hours}:${mins}`); setFormJustification(log.justification || ''); }; const handleSaveLogAudit = async (e) => { e.preventDefault(); const [h, m] = formTime.split(':').map(Number); const [y, mon, d] = formDate.split('-').map(Number); const targetTimestamp = new Date(y, mon - 1, d, h, m, 0).getTime(); const userObj = globalUsers.find(u => Number(u.id) === Number(formUser)); if (!userObj) return alert("Selecciona un colaborador."); const payload = { id: editingLog ? editingLog.id : null, userId: userObj.id, userName: userObj.name, type: formType, date: formDate, timestamp: targetTimestamp, justification: `${formJustification ? formJustification + ' | ' : ''}Ajuste Manual por Administrador (${new Date().toLocaleDateString()})` }; const res = await window.apiCall(editingLog ? 'update_log' : 'save_log', payload); if (res && res.success) { setEditingLog(null); setShowNewMarkModal(false); if (refreshData) await refreshData(); } else { alert("Error al procesar marca."); } }; const handleDeleteLog = async (logId) => { if (!confirm("¿Eliminar este registro de marcaje de la auditoría?")) return; const res = await window.apiCall('delete_log', { id: logId }); if (res && res.success && refreshData) await refreshData(); }; const printReport = () => window.print(); return (
Quedará asentado en auditoría quién realizó el ajuste
Busca, filtra, exporta y corrige marcajes del personal
Impreso el: {new Date().toLocaleDateString()} a las {new Date().toLocaleTimeString()} | Filtro: {selectedActionType} | Mostrando {filteredLogs.length} registros.
| Colaborador / Usuario | Actividad Registrada | Fecha y Hora | Observación de Auditoría | Pruebas Físicas |
|---|---|---|---|---|
| No se encontraron registros en la fecha seleccionada. | ||||
| {l.userName} | {l.type} | {window.formatDateToNIC(l.date)} | {l.justification} {l.absenceType === 'injustificada' ? '(Deduce Séptimo Día en Nómina)' : ''} | Ausencia |
| {l.userName || 'Usuario'} {l.client && ({l.client})} | {displayType} | {window.formatNICTimeFull ? window.formatNICTimeFull(l.timestamp) : new Date(l.timestamp).toLocaleString()} |
{isOffDay && (
★ Marca en Día Libre
)}
{isAlert || isMissedOut ? (
{l.justification}
) : l.justification ? (
{l.justification}
) : (
Normal
)}
|
{l.location && l.location.lat ? (
) : N/A}
{l.photo ? (
X }
|