'use client'; import { useState } from 'react'; import { DashboardShell } from '@/components/layouts/dashboard-shell'; import { Card, CardContent, CardHeader, CardTitle, Button, Input, Label } from '@horux/shared-ui'; import { useEventos, useCreateEvento, useUpdateEvento, useDeleteEvento } from '@/lib/hooks/use-calendario'; import { useAuthStore } from '@/stores/auth-store'; import { Calendar, ChevronLeft, ChevronRight, Check, Clock, FileText, CreditCard, Plus, X, Pencil, Trash2, Lock, Globe, AlertTriangle, } from 'lucide-react'; import { cn } from '@horux/shared-ui'; import type { EventoFiscal } from '@horux/shared'; const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']; const tipoIcons: Record = { declaracion: FileText, pago: CreditCard, obligacion: Clock, informativa: FileText, custom: Calendar, 'obligacion-pendiente': Clock, 'obligacion-completada': Check, 'obligacion-atrasada': AlertTriangle, }; const tipoColors: Record = { declaracion: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200', pago: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200', obligacion: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200', informativa: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200', custom: 'bg-violet-100 text-violet-800 dark:bg-violet-900 dark:text-violet-200', 'obligacion-pendiente': 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200', 'obligacion-completada': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200', 'obligacion-atrasada': 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200', }; interface RecordatorioForm { titulo: string; descripcion: string; fechaLimite: string; notas: string; privado: boolean; } const emptyForm: RecordatorioForm = { titulo: '', descripcion: '', fechaLimite: '', notas: '', privado: false, }; export default function CalendarioPage() { const [año, setAño] = useState(new Date().getFullYear()); const [mes, setMes] = useState(new Date().getMonth() + 1); const { data: eventos, isLoading } = useEventos(año); const createEvento = useCreateEvento(); const updateEvento = useUpdateEvento(); const deleteEvento = useDeleteEvento(); const { user } = useAuthStore(); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState(emptyForm); const canEdit = ['owner', 'cfo', 'contador', 'auxiliar', 'supervisor'].includes(user?.role || ''); const handlePrevMonth = () => { if (mes === 1) { setMes(12); setAño(año - 1); } else setMes(mes - 1); }; const handleNextMonth = () => { if (mes === 12) { setMes(1); setAño(año + 1); } else setMes(mes + 1); }; const handleToggleComplete = (evento: EventoFiscal) => { if (!evento.id) return; if (evento.tipo === 'custom') { updateEvento.mutate({ id: evento.id, data: { completado: !evento.completado } }); } }; const handleOpenCreate = () => { setEditingId(null); const defaultDate = `${año}-${String(mes).padStart(2, '0')}-15`; setForm({ ...emptyForm, fechaLimite: defaultDate }); setShowForm(true); }; const handleOpenEdit = (evento: EventoFiscal) => { if (!evento.id || evento.tipo !== 'custom') return; setEditingId(evento.id); setForm({ titulo: evento.titulo, descripcion: evento.descripcion || '', fechaLimite: evento.fechaLimite, notas: evento.notas || '', privado: (evento as any).privado ?? false, }); setShowForm(true); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingId) { await updateEvento.mutateAsync({ id: editingId, data: { titulo: form.titulo, descripcion: form.descripcion, fechaLimite: form.fechaLimite, notas: form.notas, privado: form.privado } as any, }); } else { await createEvento.mutateAsync({ titulo: form.titulo, descripcion: form.descripcion, tipo: 'custom', fechaLimite: form.fechaLimite, recurrencia: 'unica', notas: form.notas, privado: form.privado, } as any); } setShowForm(false); setForm(emptyForm); setEditingId(null); } catch { alert('Error al guardar recordatorio'); } }; const handleDelete = async (id: number) => { if (!confirm('¿Eliminar este recordatorio?')) return; try { await deleteEvento.mutateAsync(id); } catch { alert('Error al eliminar'); } }; const handleCancelForm = () => { setShowForm(false); setForm(emptyForm); setEditingId(null); }; // Generate calendar days const firstDay = new Date(año, mes - 1, 1).getDay(); const daysInMonth = new Date(año, mes, 0).getDate(); const days = Array.from({ length: 42 }, (_, i) => { const day = i - firstDay + 1; if (day < 1 || day > daysInMonth) return null; return day; }); const getEventosForDay = (day: number) => { return eventos?.filter(e => { const fecha = new Date(e.fechaLimite + 'T00:00:00'); return fecha.getFullYear() === año && fecha.getMonth() + 1 === mes && fecha.getDate() === day; }) || []; }; const eventosDelMes = eventos?.filter(e => { const f = new Date(e.fechaLimite + 'T00:00:00'); return f.getFullYear() === año && f.getMonth() + 1 === mes; }) || []; return ( {/* Modal de crear/editar */} {showForm && (
{editingId ? 'Editar Recordatorio' : 'Nuevo Recordatorio'}
setForm({ ...form, titulo: e.target.value })} placeholder="Reunión con contador" required />
setForm({ ...form, fechaLimite: e.target.value })} required />
setForm({ ...form, descripcion: e.target.value })} placeholder="Revisión de declaración mensual" />
setForm({ ...form, notas: e.target.value })} placeholder="Llevar estados de cuenta" />
{form.privado ? 'Solo tú puedes verlo' : 'Visible para todo el equipo'}
)}
{/* Calendar */} {meses[mes - 1]} {año}
{canEdit && !showForm && ( )}
{/* Leyenda de colores por estado de obligación */}
Pendiente Completada Atrasada Recordatorio custom
{['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'].map(d => (
{d.charAt(0)} {d}
))} {days.map((day, i) => { const dayEventos = day ? getEventosForDay(day) : []; const isToday = day === new Date().getDate() && mes === new Date().getMonth() + 1 && año === new Date().getFullYear(); return (
{day && ( <>
{day}
{dayEventos.slice(0, 2).map((e, idx) => { const Icon = tipoIcons[e.tipo] || Calendar; return (
e.tipo === 'custom' && canEdit && handleOpenEdit(e)} > {e.titulo}
); })} {dayEventos.length > 2 && (
+{dayEventos.length - 2} más
)}
)}
); })}
{/* Event List */} Eventos del Mes {isLoading ? (
Cargando...
) : eventosDelMes.length === 0 ? (
No hay eventos este mes
) : (
{eventosDelMes.map((evento, idx) => { const Icon = tipoIcons[evento.tipo] || FileText; const isCustom = evento.tipo === 'custom'; return (

{evento.titulo}

{isCustom && (evento as any).privado && ( )}
{evento.descripcion && (

{evento.descripcion}

)}

{new Date(evento.fechaLimite + 'T00:00:00').toLocaleDateString('es-MX', { day: 'numeric', month: 'short', })}

{isCustom && canEdit && (
)}
); })}
)}
); }