import React, { useState, useEffect, useRef } from 'react'; import { Bell, X, CheckCircle, Trash2, ShieldCheck, FileText, Paperclip } from 'lucide-react'; import { SchoolData, Notification, View } from '../types'; import { dbService } from '../services/dbService'; interface Props { data: SchoolData; updateData: (newData: Partial) => void; setView: (view: View) => void; onNavigateToStudent?: (studentId: string) => void; } const AdminNotifications: React.FC = ({ data, updateData, setView, onNavigateToStudent }) => { const [isOpen, setIsOpen] = useState(false); const [viewingAttachment, setViewingAttachment] = useState(null); const [notifWithAttachment, setNotifWithAttachment] = useState(null); const prevCountRef = useRef(0); const audioRef = useRef(null); const handleDeleteAttachment = () => { if (!notifWithAttachment) return; const updatedNotifs = (data.notifications || []).map(n => n.id === notifWithAttachment.id ? { ...n, attachment: undefined } : n ); updateData({ notifications: updatedNotifs }); dbService.saveData({ ...data, notifications: updatedNotifs }); setViewingAttachment(null); setNotifWithAttachment(null); }; const adminNotifs = (data.notifications || []).filter(n => n.studentId === 'admin').sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); const unreadCount = adminNotifs.filter(n => !n.read).length; // Som de notificação quando chega uma nova useEffect(() => { if (unreadCount > prevCountRef.current && prevCountRef.current >= 0) { try { if (!audioRef.current) { const ctx = new (window.AudioContext || (window as any).webkitAudioContext)(); const oscillator = ctx.createOscillator(); const gainNode = ctx.createGain(); oscillator.connect(gainNode); gainNode.connect(ctx.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(880, ctx.currentTime); oscillator.frequency.setValueAtTime(1100, ctx.currentTime + 0.1); oscillator.frequency.setValueAtTime(880, ctx.currentTime + 0.2); gainNode.gain.setValueAtTime(0.3, ctx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.4); oscillator.start(ctx.currentTime); oscillator.stop(ctx.currentTime + 0.4); } } catch(e) { console.warn('Som de notificação indisponível', e); } } prevCountRef.current = unreadCount; }, [unreadCount]); const handleAction = (notif: Notification) => { if (!notif.read) handleMarkAsRead(notif.id); if (notif.title.toLowerCase().includes('justificativa') || notif.message.toLowerCase().includes('justificativa')) { const targetId = (notif as any).fromStudentId || notif.studentId; if (onNavigateToStudent && targetId !== 'admin') { onNavigateToStudent(targetId); } else { setView(View.AttendanceQuery); } setIsOpen(false); } }; const handleMarkAsRead = (id: string) => { const updatedAll = (data.notifications || []).map(n => n.id === id ? { ...n, read: true } : n ); updateData({ notifications: updatedAll }); dbService.saveData({ ...data, notifications: updatedAll }); }; const handleClearRead = () => { const others = (data.notifications || []).filter(n => n.studentId !== 'admin' || (n.studentId === 'admin' && !n.read)); updateData({ notifications: others }); dbService.saveData({ ...data, notifications: others }); }; // Aceitar justificativa diretamente pela notificação const handleAcceptJustification = (notif: Notification) => { // Procura registros de falta pendentes de aceitação const pendingAbsences = (data.attendance || []).filter(a => a.type === 'absence' && a.justification && !a.justificationAccepted ); if (pendingAbsences.length > 0) { // Tenta achar pelo studentId mencionado ou associado à notificação const targetId = (notif as any).fromStudentId; const matchedAbsence = targetId ? pendingAbsences.find(a => a.studentId === targetId) || pendingAbsences[0] : pendingAbsences[0]; const updatedAttendance = (data.attendance || []).map(a => a.id === matchedAbsence.id ? { ...a, justificationAccepted: true } : a ); const updatedNotifs = (data.notifications || []).map(n => n.id === notif.id ? { ...n, read: true } : n ); updateData({ attendance: updatedAttendance, notifications: updatedNotifs }); dbService.saveData({ ...data, attendance: updatedAttendance, notifications: updatedNotifs }); } else { // Se não encontrou pendentes, apenas marca como lida handleMarkAsRead(notif.id); } }; return (
{isOpen && (

Avaliações Pendentes {unreadCount > 0 && {unreadCount}}

{adminNotifs.length === 0 ? (

Nenhuma notificação

Sua caixa de entrada está limpa.

) : (
{adminNotifs.map(notif => { const isJustificativa = notif.title.toLowerCase().includes('justificativa') || notif.message.toLowerCase().includes('justificativa'); let displayMessage = notif.message; let attachmentFromMessage = null; if (notif.message.startsWith('{')) { try { const parsed = JSON.parse(notif.message); displayMessage = parsed.motivo || displayMessage; attachmentFromMessage = parsed.arquivo || parsed.arquivo_base64 || null; } catch(e) {} } const finalAttachment = notif.attachment || attachmentFromMessage; return (
handleAction(notif)} className={`p-3 rounded-xl border transition-all cursor-pointer relative overflow-hidden group ${notif.read ? 'bg-slate-50 border-transparent opacity-70' : 'bg-white border-indigo-100 hover:border-indigo-300 shadow-sm'}`}> {!notif.read &&
}

{notif.title}

{new Date(notif.createdAt).toLocaleDateString('pt-BR')}

{displayMessage}

{(!notif.read) && (
{isJustificativa && ( )} {isJustificativa && ( )} {finalAttachment && ( )}
)}
); })}
)}
)} {viewingAttachment && (

Visualização do Documento

{viewingAttachment.startsWith('data:application/pdf') || viewingAttachment.includes('.pdf') ? (