import React, { useState, useMemo } from 'react'; import { ChevronLeft, ChevronRight, Clock, CheckCircle2, AlertCircle, Plus, X, Calendar as CalendarIcon, Trash2, List, Grid, DollarSign, ChevronDown } from 'lucide-react'; import { CalendarEvent, Expense, Receivable } from '../types'; import { useToast } from '../contexts/ToastContext'; import { CustomSelect } from './CustomSelect'; interface CalendarViewProps { expenses?: Expense[]; receivables?: Receivable[]; } const initialManualEvents: CalendarEvent[] = [ { id: '1', title: 'Reunião Uda Studios', date: new Date().toISOString().split('T')[0], type: 'meeting', completed: false, description: 'Alinhamento mensal sobre o progresso do projeto de redesign.' }, ]; export const CalendarView: React.FC = ({ expenses = [], receivables = [] }) => { const { addToast } = useToast(); const [currentDate, setCurrentDate] = useState(new Date()); const [manualEvents, setManualEvents] = useState(initialManualEvents); // Combine Manual Events with Financial Data const events = useMemo(() => { const expenseEvents: CalendarEvent[] = expenses.map(e => ({ id: `exp-${e.id}`, title: `Pagar: ${e.title}`, date: e.dueDate, type: 'payment', description: `Valor: R$ ${e.amount.toLocaleString('pt-BR')} - Categoria: ${e.category} - Status: ${e.status === 'paid' ? 'Pago' : 'Pendente'}`, completed: e.status === 'paid' })); const receivableEvents: CalendarEvent[] = receivables.map(r => ({ id: `rec-${r.id}`, title: `Receber: ${r.description}`, date: r.dueDate, type: 'deadline', // Usaremos deadline logicamente, mas com cor especial visualmente description: `Valor: R$ ${r.value.toLocaleString('pt-BR')} - Cliente: ${r.companyName} - Status: ${r.status === 'paid' ? 'Recebido' : 'Pendente'}`, completed: r.status === 'paid' })); return [...manualEvents, ...expenseEvents, ...receivableEvents]; }, [manualEvents, expenses, receivables]); // View State const [viewMode, setViewMode] = useState<'month' | 'agenda'>('month'); // Create Modal State const [isModalOpen, setIsModalOpen] = useState(false); const [newEvent, setNewEvent] = useState>({ type: 'meeting', date: new Date().toISOString().split('T')[0] }); // Detail Modal State const [isDetailModalOpen, setIsDetailModalOpen] = useState(false); const [selectedEvent, setSelectedEvent] = useState(null); const getDaysInMonth = (year: number, month: number) => new Date(year, month + 1, 0).getDate(); const getFirstDayOfMonth = (year: number, month: number) => new Date(year, month, 1).getDay(); const daysInMonth = getDaysInMonth(currentDate.getFullYear(), currentDate.getMonth()); const firstDay = getFirstDayOfMonth(currentDate.getFullYear(), currentDate.getMonth()); // --- Actions --- const handleCreateEvent = () => { if (!newEvent.title || !newEvent.date) return; const event: CalendarEvent = { ...newEvent, id: Math.random().toString(36).substr(2, 9), completed: false } as CalendarEvent; setManualEvents([...manualEvents, event]); setIsModalOpen(false); setNewEvent({ type: 'meeting', date: new Date().toISOString().split('T')[0] }); addToast({ type: 'success', title: 'Evento Criado', message: 'Agendamento salvo com sucesso.' }); }; const handleEventClick = (event: CalendarEvent) => { setSelectedEvent(event); setIsDetailModalOpen(true); }; const handleDeleteEvent = () => { if (!selectedEvent) return; // Prevent deleting financial data from calendar if (selectedEvent.id.startsWith('exp-') || selectedEvent.id.startsWith('rec-')) { addToast({ type: 'warning', title: 'Ação Bloqueada', message: "Para excluir este registro, acesse o módulo Financeiro." }); return; } if (window.confirm('Deseja excluir este evento?')) { setManualEvents(manualEvents.filter(e => e.id !== selectedEvent.id)); setIsDetailModalOpen(false); setSelectedEvent(null); addToast({ type: 'info', title: 'Evento Removido' }); } }; const handleToggleComplete = () => { if (!selectedEvent) return; // Prevent modifying financial data status from calendar (simplification for now) if (selectedEvent.id.startsWith('exp-') || selectedEvent.id.startsWith('rec-')) { addToast({ type: 'warning', title: 'Ação Bloqueada', message: "Para baixar este pagamento, vá ao módulo Financeiro." }); return; } const updatedEvents = manualEvents.map(e => e.id === selectedEvent.id ? { ...e, completed: !e.completed } : e ); setManualEvents(updatedEvents); setSelectedEvent({ ...selectedEvent, completed: !selectedEvent.completed }); addToast({ type: 'success', title: selectedEvent.completed ? 'Reaberto' : 'Concluído', message: 'Status do evento atualizado.' }); }; const getEventStyle = (event: CalendarEvent) => { if (event.id.startsWith('rec-')) { return 'bg-green-50 border-green-400 text-green-700'; } if (event.type === 'payment' || event.id.startsWith('exp-')) { return 'bg-red-50 border-red-400 text-red-700'; } if (event.type === 'deadline') { return 'bg-amber-50 border-amber-400 text-amber-700'; } return 'bg-blue-50 border-blue-400 text-blue-700'; }; const getEventTypeLabel = (event: CalendarEvent) => { if (event.id.startsWith('rec-')) return 'Recebimento'; if (event.type === 'payment' || event.id.startsWith('exp-')) return 'Pagamento'; if (event.type === 'deadline') return 'Prazo'; return 'Reunião'; }; // --- Render Helpers --- const handlePrevMonth = () => setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)); const handleNextMonth = () => setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)); const days = []; // Empty slots for (let i = 0; i < firstDay; i++) { days.push(
); } const todayStr = new Date().toISOString().split('T')[0]; // Days for (let d = 1; d <= daysInMonth; d++) { const dateStr = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`; const dayEvents = events.filter(e => e.date === dateStr); const isToday = dateStr === todayStr; days.push(
{d} {dayEvents.length > 0 && {dayEvents.length}}
{dayEvents.map(ev => (
{ e.stopPropagation(); handleEventClick(ev); }} className={`text-[10px] px-2 py-1 rounded border-l-2 truncate cursor-pointer transition-all hover:brightness-95 ${getEventStyle(ev)} ${ev.completed ? 'opacity-50 line-through' : ''}`}> {ev.title}
))}
); } // Upcoming Deadlines Logic: Filter events from "today" onwards const upcomingEvents = events .filter(e => e.date >= todayStr && !e.completed) .sort((a, b) => a.date.localeCompare(b.date)); const sidebarEvents = upcomingEvents.slice(0, 5); // Styles for Inputs const labelClass = "block text-sm font-bold text-slate-800 mb-1"; const inputClass = "w-full p-3 bg-white border border-slate-300 rounded-xl text-slate-900 placeholder-slate-400 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none transition-shadow"; return (
{/* Create Event Modal */} {isModalOpen && (
setIsModalOpen(false)}>

Novo Evento

setNewEvent({...newEvent, title: e.target.value})} placeholder="Ex: Reunião com Cliente" />
setNewEvent({...newEvent, date: e.target.value})} />
setNewEvent({...newEvent, type: val})} options={[ { value: 'meeting', label: 'Reunião' }, { value: 'deadline', label: 'Prazo / Tarefa' } ]} />