import React, { useEffect, useState } from 'react'; import { CondoService } from '../services/api'; import { ExtraordinaryExpense, Family, ExpenseItem, ExpenseShare } from '../types'; import { Plus, Calendar, FileText, CheckCircle2, Clock, Users, X, Save, Paperclip, Euro, Trash2, Eye, Briefcase, Pencil } from 'lucide-react'; export const ExtraordinaryAdmin: React.FC = () => { const [expenses, setExpenses] = useState([]); const [families, setFamilies] = useState([]); const [loading, setLoading] = useState(true); const [showModal, setShowModal] = useState(false); const [showDetailsModal, setShowDetailsModal] = useState(false); const [selectedExpense, setSelectedExpense] = useState(null); const [isEditing, setIsEditing] = useState(false); const [editingId, setEditingId] = useState(null); // Form State const [formTitle, setFormTitle] = useState(''); const [formDesc, setFormDesc] = useState(''); const [formStart, setFormStart] = useState(''); const [formEnd, setFormEnd] = useState(''); const [formContractor, setFormContractor] = useState(''); const [formItems, setFormItems] = useState([{ description: '', amount: 0 }]); const [formShares, setFormShares] = useState([]); // Working state for shares const [formAttachments, setFormAttachments] = useState<{fileName: string, fileType: string, data: string}[]>([]); const [selectedFamilyIds, setSelectedFamilyIds] = useState([]); // Helper to track checkboxes useEffect(() => { loadData(); }, []); const loadData = async () => { setLoading(true); try { const [expList, famList] = await Promise.all([ CondoService.getExpenses(), CondoService.getFamilies() ]); setExpenses(expList); setFamilies(famList); } catch(e) { console.error(e); } finally { setLoading(false); } }; // Calculation Helpers const totalAmount = formItems.reduce((acc, item) => acc + (item.amount || 0), 0); const recalculateShares = (selectedIds: string[], manualMode = false) => { if (manualMode) return; const count = selectedIds.length; if (count === 0) { setFormShares([]); return; } const percentage = 100 / count; const newShares: ExpenseShare[] = selectedIds.map(fid => { // Preserve existing data if available const existing = formShares.find(s => s.familyId === fid); if (existing) { // If editing and we just toggled someone else, re-calc percentages evenly? // Or keep manual adjustments? // For simplicity: auto-recalc resets percentages evenly. return { ...existing, percentage: parseFloat(percentage.toFixed(2)), amountDue: parseFloat(((totalAmount * percentage) / 100).toFixed(2)) }; } return { familyId: fid, percentage: parseFloat(percentage.toFixed(2)), amountDue: parseFloat(((totalAmount * percentage) / 100).toFixed(2)), amountPaid: 0, status: 'UNPAID' }; }); // Adjust rounding error on last item if (newShares.length > 0) { const sumDue = newShares.reduce((a, b) => a + b.amountDue, 0); const diff = totalAmount - sumDue; if (diff !== 0) { newShares[newShares.length - 1].amountDue += diff; } } setFormShares(newShares); }; const handleFamilyToggle = (familyId: string) => { const newSelected = selectedFamilyIds.includes(familyId) ? selectedFamilyIds.filter(id => id !== familyId) : [...selectedFamilyIds, familyId]; setSelectedFamilyIds(newSelected); recalculateShares(newSelected); }; const handleShareChange = (index: number, field: 'percentage', value: number) => { const newShares = [...formShares]; newShares[index].percentage = value; newShares[index].amountDue = (totalAmount * value) / 100; setFormShares(newShares); }; const handleItemChange = (index: number, field: keyof ExpenseItem, value: any) => { const newItems = [...formItems]; // @ts-ignore newItems[index][field] = value; setFormItems(newItems); }; // Trigger share recalc when total changes (if not manual) // We only trigger auto-recalc if not editing existing complex shares, // OR if editing but user hasn't manually messed with them yet (simplification: always recalc on total change for now) useEffect(() => { if (selectedFamilyIds.length > 0) { recalculateShares(selectedFamilyIds); } }, [totalAmount]); // eslint-disable-line react-hooks/exhaustive-deps const handleFileChange = async (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { const newAtts = []; for(let i=0; i((resolve) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as string); reader.readAsDataURL(file); }); newAtts.push({ fileName: file.name, fileType: file.type, data: base64 }); } setFormAttachments([...formAttachments, ...newAtts]); } }; const openCreateModal = () => { setIsEditing(false); setEditingId(null); setFormTitle(''); setFormDesc(''); setFormStart(''); setFormEnd(''); setFormContractor(''); setFormItems([{description:'', amount:0}]); setFormShares([]); setFormAttachments([]); setSelectedFamilyIds([]); setShowModal(true); }; const openEditModal = async (exp: ExtraordinaryExpense) => { // Fetch full details first to get items and shares try { const detail = await CondoService.getExpenseDetails(exp.id); setIsEditing(true); setEditingId(exp.id); setFormTitle(detail.title); setFormDesc(detail.description); setFormStart(detail.startDate ? new Date(detail.startDate).toISOString().split('T')[0] : ''); setFormEnd(detail.endDate ? new Date(detail.endDate).toISOString().split('T')[0] : ''); setFormContractor(detail.contractorName); setFormItems(detail.items || []); // Populate shares for editing const currentShares = detail.shares || []; setFormShares(currentShares); setSelectedFamilyIds(currentShares.map(s => s.familyId)); // Attachments (Cannot edit attachments in this simple view for now, cleared) setFormAttachments([]); setShowModal(true); } catch(e) { alert("Errore caricamento dettagli"); } }; const handleDeleteExpense = async (id: string) => { if (!confirm("Sei sicuro di voler eliminare questo progetto? Questa azione è irreversibile e cancellerà anche lo storico dei pagamenti associati.")) return; try { await CondoService.deleteExpense(id); loadData(); } catch (e) { alert("Errore eliminazione progetto"); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (isEditing && editingId) { await CondoService.updateExpense(editingId, { title: formTitle, description: formDesc, startDate: formStart, endDate: formEnd, contractorName: formContractor, items: formItems, shares: formShares // Now we send shares to sync }); } else { await CondoService.createExpense({ title: formTitle, description: formDesc, startDate: formStart, endDate: formEnd, contractorName: formContractor, items: formItems, shares: formShares, attachments: formAttachments }); } setShowModal(false); loadData(); } catch(e) { alert('Errore salvataggio'); } }; const openDetails = async (expense: ExtraordinaryExpense) => { const fullDetails = await CondoService.getExpenseDetails(expense.id); setSelectedExpense(fullDetails); setShowDetailsModal(true); }; const openAttachment = async (expenseId: string, attId: string) => { try { const file = await CondoService.getExpenseAttachment(expenseId, attId); const win = window.open(); if (win) { if (file.fileType.startsWith('image/') || file.fileType === 'application/pdf') { win.document.write(``); } else { win.document.write(`Download ${file.fileName}`); } } } catch(e) { alert("Errore file"); } }; return (

Spese Straordinarie

Gestione lavori e appalti

{/* List */}
{expenses.map(exp => (
Lavori € {exp.totalAmount.toLocaleString()}

{exp.title}

{exp.description}

{exp.contractorName}
{new Date(exp.startDate).toLocaleDateString()}
))}
{/* CREATE/EDIT MODAL */} {showModal && (

{isEditing ? 'Modifica Progetto' : 'Crea Progetto Straordinario'}

{/* General Info */}
setFormTitle(e.target.value)} required /> setFormContractor(e.target.value)} required />