feat: Enhance condo and family data models

Adds new fields for detailed address information and notes to the Condo and Family types.
Updates database schema and server API endpoints to support these new fields, improving data richness for location and specific family/condo details.
This commit is contained in:
2025-12-07 16:10:33 +01:00
parent 28148ee550
commit fd107c1ef8
9 changed files with 422 additions and 294 deletions

View File

@@ -2,7 +2,7 @@
import React, { useEffect, useState } from 'react';
import { CondoService } from '../services/mockDb';
import { AppSettings, Family, User, AlertDefinition, Condo, Notice, NoticeIconType, NoticeRead } from '../types';
import { Save, Building, Coins, Plus, Pencil, Trash2, X, CalendarCheck, AlertTriangle, User as UserIcon, Server, Bell, Clock, FileText, Lock, Megaphone, CheckCircle2, Info, Hammer, Link as LinkIcon, Eye, Calendar, List, UserCog, Mail, Power } from 'lucide-react';
import { Save, Building, Coins, Plus, Pencil, Trash2, X, CalendarCheck, AlertTriangle, User as UserIcon, Server, Bell, Clock, FileText, Lock, Megaphone, CheckCircle2, Info, Hammer, Link as LinkIcon, Eye, Calendar, List, UserCog, Mail, Power, MapPin } from 'lucide-react';
export const SettingsPage: React.FC = () => {
const currentUser = CondoService.getCurrentUser();
@@ -32,7 +32,16 @@ export const SettingsPage: React.FC = () => {
const [condos, setCondos] = useState<Condo[]>([]);
const [showCondoModal, setShowCondoModal] = useState(false);
const [editingCondo, setEditingCondo] = useState<Condo | null>(null);
const [condoForm, setCondoForm] = useState({ name: '', address: '', defaultMonthlyQuota: 100 });
const [condoForm, setCondoForm] = useState({
name: '',
address: '',
streetNumber: '',
city: '',
province: '',
zipCode: '',
notes: '',
defaultMonthlyQuota: 100
});
const [saving, setSaving] = useState(false);
const [successMsg, setSuccessMsg] = useState('');
@@ -44,9 +53,12 @@ export const SettingsPage: React.FC = () => {
const [familyForm, setFamilyForm] = useState<{
name: string;
unitNumber: string;
stair: string;
floor: string;
notes: string;
contactEmail: string;
customMonthlyQuota: string; // Use string for input handling, parse to number on save
}>({ name: '', unitNumber: '', contactEmail: '', customMonthlyQuota: '' });
customMonthlyQuota: string;
}>({ name: '', unitNumber: '', stair: '', floor: '', notes: '', contactEmail: '', customMonthlyQuota: '' });
// Users State
const [users, setUsers] = useState<User[]>([]);
@@ -104,30 +116,42 @@ export const SettingsPage: React.FC = () => {
const fetchData = async () => {
try {
if (isAdmin) {
const [condoList, activeC, gSettings, fams, usrs, alrts, allNotices] = await Promise.all([
CondoService.getCondos(),
CondoService.getActiveCondo(),
CondoService.getSettings(),
CondoService.getFamilies(),
CondoService.getUsers(),
CondoService.getAlerts(),
CondoService.getNotices()
]);
// First fetch global/structural data
const condoList = await CondoService.getCondos();
const activeC = await CondoService.getActiveCondo();
const gSettings = await CondoService.getSettings();
setCondos(condoList);
setActiveCondo(activeC);
setGlobalSettings(gSettings);
setFamilies(fams);
setUsers(usrs);
setAlerts(alrts);
setNotices(allNotices);
// Fetch read stats for notices
const stats: Record<string, NoticeRead[]> = {};
for (const n of allNotices) {
const reads = await CondoService.getNoticeReadStatus(n.id);
stats[n.id] = reads;
// Fetch condo-specific data ONLY if there is an active condo
if (activeC) {
const [fams, usrs, alrts, allNotices] = await Promise.all([
CondoService.getFamilies(activeC.id),
CondoService.getUsers(activeC.id),
CondoService.getAlerts(activeC.id),
CondoService.getNotices(activeC.id)
]);
setFamilies(fams);
setUsers(usrs);
setAlerts(alrts);
setNotices(allNotices);
// Fetch read stats for notices
const stats: Record<string, NoticeRead[]> = {};
for (const n of allNotices) {
const reads = await CondoService.getNoticeReadStatus(n.id);
stats[n.id] = reads;
}
setNoticeReadStats(stats);
} else {
setFamilies([]);
setUsers([]);
setAlerts([]);
setNotices([]);
}
setNoticeReadStats(stats);
} else {
const activeC = await CondoService.getActiveCondo();
@@ -211,24 +235,37 @@ export const SettingsPage: React.FC = () => {
// --- Condo Management Handlers ---
const openAddCondoModal = () => {
setEditingCondo(null);
setCondoForm({ name: '', address: '', defaultMonthlyQuota: 100 });
setCondoForm({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', defaultMonthlyQuota: 100 });
setShowCondoModal(true);
};
const openEditCondoModal = (c: Condo) => {
setEditingCondo(c);
setCondoForm({ name: c.name, address: c.address || '', defaultMonthlyQuota: c.defaultMonthlyQuota });
setCondoForm({
name: c.name,
address: c.address || '',
streetNumber: c.streetNumber || '',
city: c.city || '',
province: c.province || '',
zipCode: c.zipCode || '',
notes: c.notes || '',
defaultMonthlyQuota: c.defaultMonthlyQuota
});
setShowCondoModal(true);
};
const handleCondoSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
// If editingCondo exists, use its ID. If not, empty string tells service to create new.
const payload: Condo = {
id: editingCondo ? editingCondo.id : '',
name: condoForm.name,
address: condoForm.address,
streetNumber: condoForm.streetNumber,
city: condoForm.city,
province: condoForm.province,
zipCode: condoForm.zipCode,
notes: condoForm.notes,
defaultMonthlyQuota: condoForm.defaultMonthlyQuota
};
@@ -265,7 +302,7 @@ export const SettingsPage: React.FC = () => {
// --- Family Handlers ---
const openAddFamilyModal = () => {
setEditingFamily(null);
setFamilyForm({ name: '', unitNumber: '', contactEmail: '', customMonthlyQuota: '' });
setFamilyForm({ name: '', unitNumber: '', stair: '', floor: '', notes: '', contactEmail: '', customMonthlyQuota: '' });
setShowFamilyModal(true);
};
@@ -274,6 +311,9 @@ export const SettingsPage: React.FC = () => {
setFamilyForm({
name: family.name,
unitNumber: family.unitNumber,
stair: family.stair || '',
floor: family.floor || '',
notes: family.notes || '',
contactEmail: family.contactEmail || '',
customMonthlyQuota: family.customMonthlyQuota ? family.customMonthlyQuota.toString() : ''
});
@@ -296,23 +336,22 @@ export const SettingsPage: React.FC = () => {
? parseFloat(familyForm.customMonthlyQuota)
: undefined;
const payload: any = {
name: familyForm.name,
unitNumber: familyForm.unitNumber,
stair: familyForm.stair,
floor: familyForm.floor,
notes: familyForm.notes,
contactEmail: familyForm.contactEmail,
customMonthlyQuota: quota
};
if (editingFamily) {
const updatedFamily = {
...editingFamily,
name: familyForm.name,
unitNumber: familyForm.unitNumber,
contactEmail: familyForm.contactEmail,
customMonthlyQuota: quota
};
const updatedFamily = { ...editingFamily, ...payload };
await CondoService.updateFamily(updatedFamily);
setFamilies(families.map(f => f.id === updatedFamily.id ? updatedFamily : f));
} else {
const newFamily = await CondoService.addFamily({
name: familyForm.name,
unitNumber: familyForm.unitNumber,
contactEmail: familyForm.contactEmail,
customMonthlyQuota: quota
});
const newFamily = await CondoService.addFamily(payload);
setFamilies([...families, newFamily]);
}
setShowFamilyModal(false);
@@ -349,7 +388,8 @@ export const SettingsPage: React.FC = () => {
} else {
await CondoService.createUser(userForm);
}
setUsers(await CondoService.getUsers());
// Refresh user list for active condo
setUsers(await CondoService.getUsers(activeCondo?.id));
setShowUserModal(false);
} catch (e) { alert("Errore nel salvataggio utente"); }
};
@@ -379,7 +419,8 @@ export const SettingsPage: React.FC = () => {
date: editingNotice ? editingNotice.date : new Date().toISOString()
};
await CondoService.saveNotice(payload);
setNotices(await CondoService.getNotices());
// Refresh notices for active condo
setNotices(await CondoService.getNotices(activeCondo?.id));
setShowNoticeModal(false);
} catch (e) { console.error(e); }
};
@@ -409,6 +450,7 @@ export const SettingsPage: React.FC = () => {
e.preventDefault();
try {
const payload: AlertDefinition = { id: editingAlert ? editingAlert.id : '', subject: alertForm.subject!, body: alertForm.body!, daysOffset: Number(alertForm.daysOffset), offsetType: alertForm.offsetType as any, sendHour: Number(alertForm.sendHour), active: alertForm.active! };
// Save alert with current condoId
const saved = await CondoService.saveAlert(payload);
setAlerts(editingAlert ? alerts.map(a => a.id === saved.id ? saved : a) : [...alerts, saved]);
setShowAlertModal(false);
@@ -488,7 +530,39 @@ export const SettingsPage: React.FC = () => {
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Building className="w-5 h-5 text-blue-600" /> Dati Condominio Corrente</h3>
<form onSubmit={handleGeneralSubmit} className="space-y-5">
<input type="text" value={activeCondo.name} onChange={(e) => setActiveCondo({ ...activeCondo, name: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome" required />
<input type="text" value={activeCondo.address || ''} onChange={(e) => setActiveCondo({ ...activeCondo, address: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Indirizzo" />
<div>
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Indirizzo</label>
<input type="text" value={activeCondo.address || ''} onChange={(e) => setActiveCondo({ ...activeCondo, address: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Via/Piazza..." required/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Civico</label>
<input type="text" value={activeCondo.streetNumber || ''} onChange={(e) => setActiveCondo({ ...activeCondo, streetNumber: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" required/>
</div>
<div>
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">CAP</label>
<input type="text" value={activeCondo.zipCode || ''} onChange={(e) => setActiveCondo({ ...activeCondo, zipCode: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700"/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Città</label>
<input type="text" value={activeCondo.city || ''} onChange={(e) => setActiveCondo({ ...activeCondo, city: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" required/>
</div>
<div>
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Provincia</label>
<input type="text" value={activeCondo.province || ''} onChange={(e) => setActiveCondo({ ...activeCondo, province: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" required/>
</div>
</div>
<div>
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Note (Opzionali)</label>
<textarea value={activeCondo.notes || ''} onChange={(e) => setActiveCondo({ ...activeCondo, notes: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700 h-24"></textarea>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Quota Mensile Standard ()</label>
<input type="number" value={activeCondo.defaultMonthlyQuota} onChange={(e) => setActiveCondo({ ...activeCondo, defaultMonthlyQuota: parseFloat(e.target.value) })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Quota Default" required />
@@ -519,7 +593,10 @@ export const SettingsPage: React.FC = () => {
<div key={condo.id} className={`bg-white p-5 rounded-xl border shadow-sm relative ${activeCondo?.id === condo.id ? 'border-blue-500 ring-1 ring-blue-500' : 'border-slate-200'}`}>
{activeCondo?.id === condo.id && <div className="absolute top-3 right-3 text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded font-bold uppercase">Attivo</div>}
<h4 className="font-bold text-slate-800 text-lg mb-1">{condo.name}</h4>
<p className="text-sm text-slate-500 mb-3">{condo.address || 'Nessun indirizzo'}</p>
<div className="text-sm text-slate-500 mb-3 space-y-1">
<p className="flex items-center gap-1"><MapPin className="w-3 h-3"/> {condo.address} {condo.streetNumber}</p>
{condo.city && <p className="pl-4">{condo.zipCode} {condo.city} ({condo.province})</p>}
</div>
<div className="border-t pt-3 flex gap-2">
<button onClick={() => openEditCondoModal(condo)} className="flex-1 py-1.5 bg-slate-50 text-slate-700 rounded text-sm font-medium hover:bg-slate-100">Modifica</button>
<button onClick={() => handleDeleteCondo(condo.id)} className="flex-1 py-1.5 bg-red-50 text-red-600 rounded text-sm font-medium hover:bg-red-100">Elimina</button>
@@ -545,12 +622,22 @@ export const SettingsPage: React.FC = () => {
</div>
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<table className="w-full text-left text-sm text-slate-600">
<thead className="bg-slate-50 text-slate-700 font-semibold border-b"><tr><th className="px-6 py-4">Nome</th><th className="px-6 py-4">Interno</th><th className="px-6 py-4">Email</th><th className="px-6 py-4">Quota</th><th className="px-6 py-4 text-right">Azioni</th></tr></thead>
<thead className="bg-slate-50 text-slate-700 font-semibold border-b"><tr><th className="px-6 py-4">Nome</th><th className="px-6 py-4">Dettagli</th><th className="px-6 py-4">Email</th><th className="px-6 py-4">Quota</th><th className="px-6 py-4 text-right">Azioni</th></tr></thead>
<tbody className="divide-y divide-slate-100">
{families.map(family => (
<tr key={family.id} className="hover:bg-slate-50">
<td className="px-6 py-4 font-medium text-slate-900">{family.name}</td>
<td className="px-6 py-4">{family.unitNumber}</td>
<td className="px-6 py-4 text-xs">
<div className="space-y-0.5">
<span className="block text-slate-700 font-medium">Int: {family.unitNumber || '-'}</span>
{(family.stair || family.floor) && (
<span className="block text-slate-500">
{family.stair ? `Scala: ${family.stair} ` : ''}
{family.floor ? `Piano: ${family.floor}` : ''}
</span>
)}
</div>
</td>
<td className="px-6 py-4 text-slate-400">{family.contactEmail}</td>
<td className="px-6 py-4">
{family.customMonthlyQuota ? (
@@ -654,152 +741,6 @@ export const SettingsPage: React.FC = () => {
</div>
)}
{/* Alerts Tab */}
{isAdmin && activeTab === 'alerts' && (
<div className="space-y-4 animate-fade-in">
<div className="flex justify-between items-center">
<h3 className="font-bold text-slate-800">Avvisi Automatici Email</h3>
<button onClick={openAddAlertModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center gap-2 hover:bg-blue-700 transition-colors"><Plus className="w-4 h-4"/> Nuovo</button>
</div>
{alerts.map(a => (
<div key={a.id} className="bg-white p-4 rounded-xl border flex justify-between items-center">
<div><h4 className="font-bold text-slate-800">{a.subject}</h4><p className="text-sm text-slate-500">{a.body}</p></div>
<div className="flex gap-2"><button onClick={() => openEditAlertModal(a)} className="text-blue-600 p-2 hover:bg-blue-50 rounded"><Pencil className="w-4 h-4"/></button><button onClick={() => handleDeleteAlert(a.id)} className="text-red-600 p-2 hover:bg-red-50 rounded"><Trash2 className="w-4 h-4"/></button></div>
</div>
))}
</div>
)}
{isAdmin && activeTab === 'smtp' && (
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 max-w-2xl animate-fade-in">
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Server className="w-5 h-5"/> Server SMTP Globale</h3>
{globalSettings && (
<form onSubmit={handleSmtpSubmit} className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<input type="text" placeholder="Host" value={globalSettings.smtpConfig?.host} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, host: e.target.value}})} className="border p-2 rounded text-slate-700"/>
<input type="number" placeholder="Port" value={globalSettings.smtpConfig?.port} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, port: parseInt(e.target.value)}})} className="border p-2 rounded text-slate-700"/>
</div>
<div className="grid grid-cols-2 gap-4">
<input type="text" placeholder="User" value={globalSettings.smtpConfig?.user} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, user: e.target.value}})} className="border p-2 rounded text-slate-700"/>
<input type="password" placeholder="Pass" value={globalSettings.smtpConfig?.pass} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, pass: e.target.value}})} className="border p-2 rounded text-slate-700"/>
</div>
<button type="submit" className="bg-blue-600 text-white px-6 py-2 rounded-lg w-full">Salva Configurazione SMTP</button>
</form>
)}
</div>
)}
{/* MODALS */}
{/* CONDO MODAL */}
{showCondoModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-md p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingCondo ? 'Modifica Condominio' : 'Nuovo Condominio'}</h3>
<form onSubmit={handleCondoSubmit} className="space-y-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome Condominio" value={condoForm.name} onChange={e => setCondoForm({...condoForm, name: e.target.value})} required />
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Indirizzo" value={condoForm.address} onChange={e => setCondoForm({...condoForm, address: e.target.value})} />
<div className="flex items-center gap-2"><span className="text-sm font-medium text-slate-600">Quota Default </span><input type="number" className="border p-2 rounded w-24 text-slate-700" value={condoForm.defaultMonthlyQuota} onChange={e => setCondoForm({...condoForm, defaultMonthlyQuota: parseFloat(e.target.value)})} /></div>
<div className="flex gap-2 pt-2">
<button type="button" onClick={() => setShowCondoModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600 hover:bg-slate-50">Annulla</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg hover:bg-blue-700">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* NOTICE MODAL */}
{showNoticeModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800 flex items-center gap-2">
<Megaphone className="w-5 h-5 text-blue-600"/>
{editingNotice ? 'Modifica Avviso' : 'Nuovo Avviso'}
</h3>
<form onSubmit={handleNoticeSubmit} className="space-y-4">
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Titolo</label>
<input className="w-full border p-2.5 rounded-lg text-slate-700" value={noticeForm.title} onChange={e => setNoticeForm({...noticeForm, title: e.target.value})} required />
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Tipo</label>
<select className="w-full border p-2.5 rounded-lg text-slate-700 bg-white" value={noticeForm.type} onChange={e => setNoticeForm({...noticeForm, type: e.target.value as any})}>
<option value="info">Informazione</option>
<option value="warning">Avviso / Pericolo</option>
<option value="maintenance">Manutenzione</option>
<option value="event">Evento</option>
</select>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Condominio</label>
<select className="w-full border p-2.5 rounded-lg text-slate-700 bg-white" value={noticeForm.condoId} onChange={e => setNoticeForm({...noticeForm, condoId: e.target.value})}>
{condos.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
</select>
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Contenuto</label>
<textarea className="w-full border p-2.5 rounded-lg text-slate-700 h-24" value={noticeForm.content} onChange={e => setNoticeForm({...noticeForm, content: e.target.value})} required />
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Link Documento (Opzionale)</label>
<div className="relative">
<LinkIcon className="absolute left-3 top-3 w-4 h-4 text-slate-400"/>
<input className="w-full border p-2.5 pl-9 rounded-lg text-slate-700" placeholder="https://..." value={noticeForm.link} onChange={e => setNoticeForm({...noticeForm, link: e.target.value})} />
</div>
</div>
<div className="flex items-center gap-2">
<input type="checkbox" checked={noticeForm.active} onChange={e => setNoticeForm({...noticeForm, active: e.target.checked})} className="w-4 h-4 text-blue-600"/>
<span className="text-sm font-medium text-slate-700">Visibile ai condomini</span>
</div>
<div className="flex gap-2 pt-2">
<button type="button" onClick={() => setShowNoticeModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600 hover:bg-slate-50">Annulla</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg hover:bg-blue-700">Pubblica</button>
</div>
</form>
</div>
</div>
)}
{/* FAMILY MODAL */}
{showFamilyModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-2xl shadow-xl w-full max-w-md p-6">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingFamily ? 'Modifica Famiglia' : 'Nuova Famiglia'}</h3>
<form onSubmit={handleFamilySubmit} className="space-y-4">
<input type="text" required value={familyForm.name} onChange={(e) => setFamilyForm({...familyForm, name: e.target.value})} className="w-full border rounded-lg p-3 text-slate-700" placeholder="Nome Famiglia"/>
<input type="text" required value={familyForm.unitNumber} onChange={(e) => setFamilyForm({...familyForm, unitNumber: e.target.value})} className="w-full border rounded-lg p-3 text-slate-700" placeholder="Interno"/>
<input type="email" value={familyForm.contactEmail} onChange={(e) => setFamilyForm({...familyForm, contactEmail: e.target.value})} className="w-full border rounded-lg p-3 text-slate-700" placeholder="Email"/>
<div className="pt-2 border-t border-slate-100">
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Quota Mensile Personalizzata</label>
<p className="text-xs text-slate-400 mb-2">Lasciare vuoto per usare il default del condominio ( {activeCondo?.defaultMonthlyQuota})</p>
<input
type="number"
step="0.01"
value={familyForm.customMonthlyQuota}
onChange={(e) => setFamilyForm({...familyForm, customMonthlyQuota: e.target.value})}
className="w-full border rounded-lg p-3 text-slate-700"
placeholder="Es. 120.00"
/>
</div>
<div className="flex gap-3 pt-4">
<button type="button" onClick={() => setShowFamilyModal(false)} className="flex-1 p-3 border rounded-lg text-slate-600">Annulla</button>
<button type="submit" className="flex-1 p-3 bg-blue-600 text-white rounded-lg">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* ALERT MODAL */}
{showAlertModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
@@ -918,6 +859,123 @@ export const SettingsPage: React.FC = () => {
</div>
</div>
)}
{/* CONDO MODAL */}
{showCondoModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingCondo ? 'Modifica Condominio' : 'Nuovo Condominio'}</h3>
<form onSubmit={handleCondoSubmit} className="space-y-4">
{/* Name */}
<div>
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome Condominio" value={condoForm.name} onChange={e => setCondoForm({...condoForm, name: e.target.value})} required />
</div>
{/* Address Line 1 */}
<div className="grid grid-cols-12 gap-3">
<div className="col-span-9">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Via/Piazza..." value={condoForm.address} onChange={e => setCondoForm({...condoForm, address: e.target.value})} required />
</div>
<div className="col-span-3">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Civico" value={condoForm.streetNumber} onChange={e => setCondoForm({...condoForm, streetNumber: e.target.value})} required />
</div>
</div>
{/* Address Line 2 */}
<div className="grid grid-cols-12 gap-3">
<div className="col-span-3">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="CAP" value={condoForm.zipCode} onChange={e => setCondoForm({...condoForm, zipCode: e.target.value})} />
</div>
<div className="col-span-5">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Città" value={condoForm.city} onChange={e => setCondoForm({...condoForm, city: e.target.value})} required />
</div>
<div className="col-span-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Provincia" value={condoForm.province} onChange={e => setCondoForm({...condoForm, province: e.target.value})} required />
</div>
</div>
{/* Notes */}
<div>
<textarea className="w-full border p-2.5 rounded-lg text-slate-700 h-20" placeholder="Note (opzionali)" value={condoForm.notes} onChange={e => setCondoForm({...condoForm, notes: e.target.value})} />
</div>
{/* Quota */}
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-slate-600">Quota Default </span>
<input type="number" className="border p-2 rounded w-24 text-slate-700" value={condoForm.defaultMonthlyQuota} onChange={e => setCondoForm({...condoForm, defaultMonthlyQuota: parseFloat(e.target.value)})} />
</div>
<div className="flex gap-2 pt-2">
<button type="button" onClick={() => setShowCondoModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600 hover:bg-slate-50">Annulla</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg hover:bg-blue-700">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* FAMILY MODAL */}
{showFamilyModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-2xl shadow-xl w-full max-w-lg p-6">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingFamily ? 'Modifica Famiglia' : 'Nuova Famiglia'}</h3>
<form onSubmit={handleFamilySubmit} className="space-y-4">
{/* Name & Email */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="md:col-span-2">
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Nome Famiglia</label>
<input type="text" required value={familyForm.name} onChange={(e) => setFamilyForm({...familyForm, name: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" placeholder="Es. Rossi"/>
</div>
<div className="md:col-span-2">
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Email Contatto (Obbligatoria)</label>
<input type="email" required value={familyForm.contactEmail} onChange={(e) => setFamilyForm({...familyForm, contactEmail: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" placeholder="email@esempio.it"/>
</div>
</div>
{/* Location Details */}
<div className="grid grid-cols-3 gap-3">
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Interno</label>
<input type="text" value={familyForm.unitNumber} onChange={(e) => setFamilyForm({...familyForm, unitNumber: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" />
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Scala</label>
<input type="text" value={familyForm.stair} onChange={(e) => setFamilyForm({...familyForm, stair: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" />
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Piano</label>
<input type="text" value={familyForm.floor} onChange={(e) => setFamilyForm({...familyForm, floor: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" />
</div>
</div>
{/* Notes */}
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Note</label>
<textarea value={familyForm.notes} onChange={(e) => setFamilyForm({...familyForm, notes: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700 h-16" placeholder="Note opzionali..."></textarea>
</div>
<div className="pt-2 border-t border-slate-100">
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Quota Mensile Personalizzata</label>
<p className="text-xs text-slate-400 mb-2">Lasciare vuoto per usare il default del condominio ( {activeCondo?.defaultMonthlyQuota})</p>
<input
type="number"
step="0.01"
value={familyForm.customMonthlyQuota}
onChange={(e) => setFamilyForm({...familyForm, customMonthlyQuota: e.target.value})}
className="w-full border rounded-lg p-2.5 text-slate-700"
placeholder="Es. 120.00"
/>
</div>
<div className="flex gap-3 pt-4">
<button type="button" onClick={() => setShowFamilyModal(false)} className="flex-1 p-3 border rounded-lg text-slate-600">Annulla</button>
<button type="submit" className="flex-1 p-3 bg-blue-600 text-white rounded-lg">Salva</button>
</div>
</form>
</div>
</div>
)}
</div>
);
};