const { useState, useMemo } = React; window.AdminDashboard = function({ globalUsers, logs, roles, reminders = [], setView, onLogout }) { const [tab, setTab] = useState('logs'); const [searchName, setSearchName] = useState(''); const [searchDate, setSearchDate] = useState(window.getNICDate()); const [editingUser, setEditingUser] = useState(null); const [editingLog, setEditingLog] = useState(null); const [userToDelete, setUserToDelete] = useState(null); const [submitError, setSubmitError] = useState(''); // Gestión de Roles y Permisos const [newRoleName, setNewRoleName] = useState(''); const [selectedPermissions, setSelectedPermissions] = useState([]); // --- GESTIÓN DE NOTIFICACIONES (NUEVO) --- const [notifTitle, setNotifTitle] = useState(''); const [notifBody, setNotifBody] = useState(''); const [notifExpiry, setNotifExpiry] = useState(''); const [targetUsers, setTargetUsers] = useState([]); // Acciones base configurables (Requerimiento: poder configurar qué se marca) const availableActions = [ { id: 'almuerzo', label: 'Almuerzo' }, { id: 'break', label: 'Break / Merienda' }, { id: 'visita_in', label: 'Visita Cliente' }, { id: 'reunion', label: 'Reunión' }, { id: 'capacitacion', label: 'Capacitación' }, { id: 'gestiones', label: 'Gestiones de Campo' } ]; const filteredLogs = useMemo(() => { return logs.filter(l => { const matchName = l.userName.toLowerCase().includes(searchName.toLowerCase()); const matchDate = searchDate ? l.date === searchDate : true; return matchName && matchDate; }).sort((a,b) => b.timestamp - a.timestamp); }, [logs, searchName, searchDate]); const handleUserSubmit = async (e) => { e.preventDefault(); setSubmitError(''); const f = new FormData(e.target); const email = f.get('email').toLowerCase().trim(); // VALIDACIÓN: Verificar si el correo ya existe y mostrar estado (NUEVO) if (!editingUser) { const existing = globalUsers.find(u => u.email === email); if (existing) { setSubmitError(`ADVERTENCIA: El correo ya existe. Pertenece a "${existing.name}" y su estado actual es [${existing.status.toUpperCase()}]`); return; } } const data = { name: f.get('name'), email, password: f.get('pass'), role: f.get('role'), status: f.get('status'), hiringDate: f.get('date'), vacationDaysUsed: parseInt(f.get('vused')) || 0, vacationStart: f.get('vs') || '', vacationEnd: f.get('ve') || '', overrideCheckOut: editingUser ? (editingUser.overrideCheckOut || false) : false }; try { if(editingUser) { await db.collection('artifacts').doc(appId).collection('public').doc('data').collection('users').doc(editingUser.id).update(data); } else { await db.collection('artifacts').doc(appId).collection('public').doc('data').collection('users').add(data); } setEditingUser(null); e.target.reset(); } catch (err) { setSubmitError("Error al conectar con la base de datos."); } }; const unlockUser = async (uId) => { try { await db.collection('artifacts').doc(appId).collection('public').doc('data').collection('users').doc(uId).update({ overrideCheckOut: true }); alert("Usuario desbloqueado. Podrá volver a marcar hoy."); } catch (err) { alert("Error al desbloquear."); } }; const deleteUser = async () => { try { await db.collection('artifacts').doc(appId).collection('public').doc('data').collection('users').doc(userToDelete.id).delete(); setUserToDelete(null); } catch (err) { alert("Error al eliminar cuenta."); } }; const updateLog = async (e) => { e.preventDefault(); const f = new FormData(e.target); const ntime = f.get('ntime'); try { await db.collection('artifacts').doc(appId).collection('public').doc('data').collection('logs').doc(editingLog.id).update({ timestamp: new Date(ntime).getTime(), type: f.get('ntype'), client: f.get('nclient'), edited: true, date: ntime.split('T')[0] }); setEditingLog(null); } catch (err) { alert("Error al editar."); } }; const togglePermission = (id) => { setSelectedPermissions(prev => prev.includes(id) ? prev.filter(p => p !== id) : [...prev, id]); }; const saveRole = async () => { if (!newRoleName) return; try { const newRoleObj = { name: newRoleName.toLowerCase().trim(), permissions: selectedPermissions }; const updated = [...roles.filter(r => r.name !== newRoleObj.name), newRoleObj]; await db.collection('artifacts').doc(appId).collection('public').doc('data').collection('settings').doc('roles').set({ list: updated }); setNewRoleName(''); setSelectedPermissions([]); } catch (err) { alert("Error al guardar rol."); } }; // --- FUNCIÓN DE NOTIFICACIONES (NUEVO) --- const sendNotification = async () => { if (!notifTitle || targetUsers.length === 0) return alert("Completa el título y elige al menos un usuario destinatario."); try { await db.collection('artifacts').doc(appId).collection('public').doc('data').collection('reminders').add({ title: notifTitle, message: notifBody, expiresAt: notifExpiry ? new Date(notifExpiry).getTime() : null, targetUsers: targetUsers, completedBy: {}, createdAt: Date.now(), createdBy: 'Admin' }); setNotifTitle(''); setNotifBody(''); setNotifExpiry(''); setTargetUsers([]); alert("Aviso enviado correctamente."); } catch (e) { alert("Error al enviar la notificación."); } }; return (
{tab === 'logs' && (
setSearchName(e.target.value)} placeholder="Consultor..." className="p-4 bg-slate-50 border-2 border-slate-100 rounded-2xl outline-none font-bold" /> setSearchDate(e.target.value)} className="p-4 bg-slate-50 border-2 border-slate-100 rounded-2xl outline-none font-bold" />
{filteredLogs.map(l => ( ))}
UsuarioMarcaFecha / Hora (12h NI)AuditAcción
{l.type.replace('_',' ')} {l.client && ({l.client})} {window.formatNICTimeFull(l.timestamp)}{l.edited &&
Editado
}
{l.photo && }{l.location && }
)} {tab === 'users' && (

{editingUser ? "Editar Perfil" : "Nuevo Registro"}

{submitError &&
{submitError}
}
{editingUser && }
{globalUsers.map(u => (
{u.name[0]}

{u.name}

{u.role} • {u.status} {u.overrideCheckOut && (DESBLOQUEADO)}

))}
)} {tab === 'roles' && (

Configurar Rol y Permisos

setNewRoleName(e.target.value)} placeholder="Nombre del Rol" className="w-full p-4 bg-slate-50 border-2 border-slate-100 rounded-2xl outline-none font-bold uppercase" />

Habilitar botones para este rol:

{availableActions.map(action => ( ))}
{roles.map(r => (

{r.name}

{r.permissions.map(p => {p})}
))}
)} {/* --- NUEVO TAB: NOTIFICACIONES --- */} {tab === 'notif' && (

Nuevo Aviso

setNotifTitle(e.target.value)} placeholder="Título del aviso" className="w-full p-4 bg-slate-50 border-2 rounded-2xl font-bold outline-none" />