import React, { useEffect, useState } from 'react'; import { CondoService } from '../services/mockDb'; 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 || isEditing) return; // Don't auto-calc shares in Edit mode to prevent messing up existing complex logic visually, backend handles logic const count = selectedIds.length; if (count === 0) { setFormShares([]); return; } const percentage = 100 / count; const newShares: ExpenseShare[] = selectedIds.map(fid => ({ 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/editing) useEffect(() => { if (!isEditing) { 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 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 || []); // Shares and attachments are not fully editable in this simple view to avoid conflicts // We only allow editing Header Info + Items. Shares will be auto-recalculated by backend based on new total. setFormShares([]); setFormAttachments([]); setSelectedFamilyIds([]); setShowModal(true); } catch(e) { alert("Errore caricamento dettagli"); } }; 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 // Attachments and shares handled by backend logic to keep safe }); } 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 />