Update Settings.tsx

This commit is contained in:
2025-12-12 00:08:37 +01:00
committed by GitHub
parent e050a372a5
commit 0906b0d972

View File

@@ -7,7 +7,7 @@ import {
AlertTriangle, User as UserIcon, Server, Bell, Clock, FileText, AlertTriangle, User as UserIcon, Server, Bell, Clock, FileText,
Lock, Megaphone, CheckCircle2, Info, Hammer, Link as LinkIcon, Lock, Megaphone, CheckCircle2, Info, Hammer, Link as LinkIcon,
Eye, Calendar, List, UserCog, Mail, Power, MapPin, CreditCard, Eye, Calendar, List, UserCog, Mail, Power, MapPin, CreditCard,
ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send, Cloud, HardDrive ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send, Cloud, HardDrive, ChevronRight
} from 'lucide-react'; } from 'lucide-react';
export const SettingsPage: React.FC = () => { export const SettingsPage: React.FC = () => {
@@ -203,359 +203,62 @@ export const SettingsPage: React.FC = () => {
fetchData(); fetchData();
}, [isPrivileged]); }, [isPrivileged]);
// --- HANDLERS --- // --- HANDLERS (Omitted details for brevity as they are unchanged) ---
const handleProfileSubmit = async (e: React.FormEvent) => { const handleProfileSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault(); setProfileSaving(true); setProfileMsg('');
setProfileSaving(true); try { await CondoService.updateProfile(profileForm); setProfileMsg('Profilo aggiornato con successo!'); setTimeout(() => setProfileMsg(''), 3000); setProfileForm(prev => ({ ...prev, password: '' })); } catch (e) { setProfileMsg('Errore aggiornamento profilo'); } finally { setProfileSaving(false); }
setProfileMsg('');
try {
await CondoService.updateProfile(profileForm);
setProfileMsg('Profilo aggiornato con successo!');
setTimeout(() => setProfileMsg(''), 3000);
setProfileForm(prev => ({ ...prev, password: '' }));
} catch (e) {
setProfileMsg('Errore aggiornamento profilo');
} finally {
setProfileSaving(false);
}
}; };
const handleGeneralSubmit = async (e: React.FormEvent) => { const handleGeneralSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault(); if (!activeCondo) return; setSaving(true); setSuccessMsg('');
if (!activeCondo) return; try { await CondoService.saveCondo(activeCondo); setSuccessMsg('Dati condominio aggiornati!'); setTimeout(() => setSuccessMsg(''), 3000); const list = await CondoService.getCondos(); setCondos(list); } catch (e) { console.error(e); } finally { setSaving(false); }
setSaving(true);
setSuccessMsg('');
try {
await CondoService.saveCondo(activeCondo);
setSuccessMsg('Dati condominio aggiornati!');
setTimeout(() => setSuccessMsg(''), 3000);
const list = await CondoService.getCondos();
setCondos(list);
} catch (e) {
console.error(e);
} finally {
setSaving(false);
}
}; };
const handleFeaturesSubmit = async (e: React.FormEvent) => { const handleFeaturesSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault(); if (!globalSettings) return; setSaving(true);
if (!globalSettings) return; try { await CondoService.updateSettings(globalSettings); setSuccessMsg('Configurazione salvata!'); setTimeout(() => setSuccessMsg(''), 3000); window.location.reload(); } catch(e) { console.error(e); } finally { setSaving(false); }
setSaving(true);
try {
await CondoService.updateSettings(globalSettings);
setSuccessMsg('Configurazione salvata!');
setTimeout(() => setSuccessMsg(''), 3000);
window.location.reload();
} catch(e) {
console.error(e);
} finally {
setSaving(false);
}
}; };
const handleSmtpSubmit = async (e: React.FormEvent) => { const handleSmtpSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault(); if (!globalSettings) return; setSaving(true);
if (!globalSettings) return; try { await CondoService.updateSettings(globalSettings); setSuccessMsg('Configurazione SMTP salvata!'); setTimeout(() => { setSuccessMsg(''); setShowSmtpModal(false); }, 2000); } catch (e) { console.error(e); } finally { setSaving(false); }
setSaving(true);
try {
await CondoService.updateSettings(globalSettings);
setSuccessMsg('Configurazione SMTP salvata!');
setTimeout(() => {
setSuccessMsg('');
setShowSmtpModal(false);
}, 2000);
} catch (e) {
console.error(e);
} finally {
setSaving(false);
}
}; };
const handleSmtpTest = async () => { const handleSmtpTest = async () => {
if (!globalSettings?.smtpConfig) return; if (!globalSettings?.smtpConfig) return; setTestingSmtp(true); setTestSmtpMsg('');
setTestingSmtp(true); try { await CondoService.testSmtpConfig(globalSettings.smtpConfig); setTestSmtpMsg('Successo! Email di prova inviata.'); } catch(e: any) { setTestSmtpMsg('Errore: ' + (e.message || "Impossibile connettersi")); } finally { setTestingSmtp(false); }
setTestSmtpMsg('');
try {
await CondoService.testSmtpConfig(globalSettings.smtpConfig);
setTestSmtpMsg('Successo! Email di prova inviata.');
} catch(e: any) {
setTestSmtpMsg('Errore: ' + (e.message || "Impossibile connettersi"));
} finally {
setTestingSmtp(false);
}
}; };
const handleStorageSubmit = async (e: React.FormEvent) => { const handleStorageSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault(); if (!globalSettings) return; setSaving(true);
if (!globalSettings) return; try { await CondoService.updateSettings(globalSettings); setSuccessMsg('Configurazione Storage salvata!'); setTimeout(() => setSuccessMsg(''), 3000); } catch (e) { console.error(e); } finally { setSaving(false); }
setSaving(true);
try {
await CondoService.updateSettings(globalSettings);
setSuccessMsg('Configurazione Storage salvata!');
setTimeout(() => setSuccessMsg(''), 3000);
} catch (e) { console.error(e); }
finally { setSaving(false); }
}; };
const handleNewYear = async () => { const handleNewYear = async () => {
if (!globalSettings) return; if (!globalSettings) return; const nextYear = globalSettings.currentYear + 1;
const nextYear = globalSettings.currentYear + 1; if (window.confirm(`Sei sicuro di voler chiudere l'anno ${globalSettings.currentYear} e aprire il ${nextYear}?`)) { setSaving(true); try { const newSettings = { ...globalSettings, currentYear: nextYear }; await CondoService.updateSettings(newSettings); setGlobalSettings(newSettings); setSuccessMsg(`Anno ${nextYear} aperto!`); } catch(e) { console.error(e); } finally { setSaving(false); } }
if (window.confirm(`Sei sicuro di voler chiudere l'anno ${globalSettings.currentYear} e aprire il ${nextYear}?`)) {
setSaving(true);
try {
const newSettings = { ...globalSettings, currentYear: nextYear };
await CondoService.updateSettings(newSettings);
setGlobalSettings(newSettings);
setSuccessMsg(`Anno ${nextYear} aperto!`);
} catch(e) {
console.error(e);
} finally {
setSaving(false);
}
}
};
// CRUD Handlers (omitted repeated code for brevity, logic is same as before)
const openAddCondoModal = () => {
setEditingCondo(null);
setCondoForm({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100, dueDay: 10 });
setShowCondoModal(true);
};
const openEditCondoModal = (c: Condo) => {
setEditingCondo(c);
setCondoForm({ name: c.name, address: c.address || '', streetNumber: c.streetNumber || '', city: c.city || '', province: c.province || '', zipCode: c.zipCode || '', notes: c.notes || '', paypalClientId: c.paypalClientId || '', defaultMonthlyQuota: c.defaultMonthlyQuota, dueDay: c.dueDay || 10 });
setShowCondoModal(true);
}; };
// CRUD Handlers
const openAddCondoModal = () => { setEditingCondo(null); setCondoForm({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100, dueDay: 10 }); setShowCondoModal(true); };
const openEditCondoModal = (c: Condo) => { setEditingCondo(c); setCondoForm({ name: c.name, address: c.address || '', streetNumber: c.streetNumber || '', city: c.city || '', province: c.province || '', zipCode: c.zipCode || '', notes: c.notes || '', paypalClientId: c.paypalClientId || '', defaultMonthlyQuota: c.defaultMonthlyQuota, dueDay: c.dueDay || 10 }); setShowCondoModal(true); };
const handleCondoSubmit = async (e: React.FormEvent) => { const handleCondoSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault(); try { 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, paypalClientId: condoForm.paypalClientId, defaultMonthlyQuota: condoForm.defaultMonthlyQuota, dueDay: condoForm.dueDay }; const savedCondo = await CondoService.saveCondo(payload); const list = await CondoService.getCondos(); setCondos(list); if (activeCondo?.id === savedCondo.id) { setActiveCondo(savedCondo); } if (!activeCondo && list.length === 1) { CondoService.setActiveCondo(savedCondo.id); } setShowCondoModal(false); window.dispatchEvent(new Event('condo-updated')); } catch (e) { console.error(e); alert("Errore nel salvataggio del condominio. Assicurati di essere amministratore."); }
try {
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,
paypalClientId: condoForm.paypalClientId,
defaultMonthlyQuota: condoForm.defaultMonthlyQuota,
dueDay: condoForm.dueDay
}; };
const savedCondo = await CondoService.saveCondo(payload); const handleDeleteCondo = async (id: string) => { if(!window.confirm("Eliminare questo condominio? Attenzione: operazione irreversibile.")) return; try { await CondoService.deleteCondo(id); setCondos(await CondoService.getCondos()); window.dispatchEvent(new Event('condo-updated')); } catch (e) { console.error(e); } };
const list = await CondoService.getCondos(); const openAddFamilyModal = () => { setEditingFamily(null); setFamilyForm({ name: '', unitNumber: '', stair: '', floor: '', notes: '', contactEmail: '', customMonthlyQuota: '' }); setShowFamilyModal(true); };
setCondos(list); const openEditFamilyModal = (family: Family) => { setEditingFamily(family); 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() : '' }); setShowFamilyModal(true); };
if (activeCondo?.id === savedCondo.id) { setActiveCondo(savedCondo); } const handleDeleteFamily = async (id: string) => { if (!window.confirm('Eliminare questa famiglia?')) return; try { await CondoService.deleteFamily(id); setFamilies(families.filter(f => f.id !== id)); } catch (e) { console.error(e); } };
if (!activeCondo && list.length === 1) { CondoService.setActiveCondo(savedCondo.id); } const handleFamilySubmit = async (e: React.FormEvent) => { e.preventDefault(); try { const quota = familyForm.customMonthlyQuota && familyForm.customMonthlyQuota.trim() !== '' ? 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, ...payload }; await CondoService.updateFamily(updatedFamily); setFamilies(families.map(f => f.id === updatedFamily.id ? updatedFamily : f)); } else { const newFamily = await CondoService.addFamily(payload); setFamilies([...families, newFamily]); } setShowFamilyModal(false); } catch (e: any) { console.error(e); alert(`Errore: ${e.message || "Impossibile salvare la famiglia."}`); } };
setShowCondoModal(false); const openAddUserModal = () => { setEditingUser(null); setUserForm({ name: '', email: '', password: '', phone: '', role: 'user', familyId: '', receiveAlerts: true }); setShowUserModal(true); };
window.dispatchEvent(new Event('condo-updated')); const openEditUserModal = (user: User) => { setEditingUser(user); setUserForm({ name: user.name || '', email: user.email, password: '', phone: user.phone || '', role: user.role || 'user', familyId: user.familyId || '', receiveAlerts: user.receiveAlerts ?? true }); setShowUserModal(true); };
} catch (e) { const handleUserSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingUser) { await CondoService.updateUser(editingUser.id, userForm); } else { await CondoService.createUser(userForm); } setUsers(await CondoService.getUsers(activeCondo?.id)); setShowUserModal(false); } catch (e) { alert("Errore nel salvataggio utente"); } };
console.error(e); const handleDeleteUser = async (id: string) => { if(!window.confirm("Eliminare utente?")) return; await CondoService.deleteUser(id); setUsers(users.filter(u => u.id !== id)); };
alert("Errore nel salvataggio del condominio. Assicurati di essere amministratore."); const openAddNoticeModal = () => { setEditingNotice(null); setNoticeTargetMode('all'); setNoticeForm({ title: '', content: '', type: 'info', link: '', condoId: activeCondo?.id || '', active: true, targetFamilyIds: [] }); setShowNoticeModal(true); };
} const openEditNoticeModal = (n: Notice) => { setEditingNotice(n); const isTargeted = n.targetFamilyIds && n.targetFamilyIds.length > 0; setNoticeTargetMode(isTargeted ? 'specific' : 'all'); setNoticeForm({ title: n.title, content: n.content, type: n.type, link: n.link || '', condoId: n.condoId, active: n.active, targetFamilyIds: n.targetFamilyIds || [] }); setShowNoticeModal(true); };
}; const handleNoticeSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { const payload: Notice = { id: editingNotice ? editingNotice.id : '', ...noticeForm, targetFamilyIds: noticeTargetMode === 'all' ? [] : noticeForm.targetFamilyIds, date: editingNotice ? editingNotice.date : new Date().toISOString() }; await CondoService.saveNotice(payload); setNotices(await CondoService.getNotices(activeCondo?.id)); setShowNoticeModal(false); } catch (e) { console.error(e); } };
const handleDeleteNotice = async (id: string) => { if(!window.confirm("Eliminare annuncio?")) return; await CondoService.deleteNotice(id); setNotices(notices.filter(n => n.id !== id)); };
const handleDeleteCondo = async (id: string) => { const toggleNoticeActive = async (notice: Notice) => { try { const updated = { ...notice, active: !notice.active }; await CondoService.saveNotice(updated); setNotices(notices.map(n => n.id === notice.id ? updated : n)); } catch (e) { console.error(e); } };
if(!window.confirm("Eliminare questo condominio? Attenzione: operazione irreversibile.")) return; const toggleNoticeFamilyTarget = (familyId: string) => { setNoticeForm(prev => { const current = prev.targetFamilyIds; if (current.includes(familyId)) { return { ...prev, targetFamilyIds: current.filter(id => id !== familyId) }; } else { return { ...prev, targetFamilyIds: [...current, familyId] }; } }); };
try { const openReadDetails = (noticeId: string) => { setSelectedNoticeId(noticeId); setShowReadDetailsModal(true); };
await CondoService.deleteCondo(id); const openAddAlertModal = () => { setEditingAlert(null); setAlertForm({ subject: '', body: '', daysOffset: 1, offsetType: 'before_next_month', sendHour: 9, active: true }); setShowAlertModal(true); };
setCondos(await CondoService.getCondos()); const openEditAlertModal = (alert: AlertDefinition) => { setEditingAlert(alert); setAlertForm(alert); setShowAlertModal(true); };
window.dispatchEvent(new Event('condo-updated')); const handleAlertSubmit = async (e: React.FormEvent) => { 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! }; const saved = await CondoService.saveAlert(payload); setAlerts(editingAlert ? alerts.map(a => a.id === saved.id ? saved : a) : [...alerts, saved]); setShowAlertModal(false); } catch (e) { console.error(e); } };
} catch (e) { const handleDeleteAlert = async (id: string) => { if(!window.confirm("Eliminare avviso?")) return; await CondoService.deleteAlert(id); setAlerts(alerts.filter(a => a.id !== id)); };
console.error(e);
}
};
const openAddFamilyModal = () => {
setEditingFamily(null);
setFamilyForm({ name: '', unitNumber: '', stair: '', floor: '', notes: '', contactEmail: '', customMonthlyQuota: '' });
setShowFamilyModal(true);
};
const openEditFamilyModal = (family: Family) => {
setEditingFamily(family);
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() : '' });
setShowFamilyModal(true);
};
const handleDeleteFamily = async (id: string) => {
if (!window.confirm('Eliminare questa famiglia?')) return;
try {
await CondoService.deleteFamily(id);
setFamilies(families.filter(f => f.id !== id));
} catch (e) {
console.error(e);
}
};
const handleFamilySubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const quota = familyForm.customMonthlyQuota && familyForm.customMonthlyQuota.trim() !== '' ? 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, ...payload };
await CondoService.updateFamily(updatedFamily);
setFamilies(families.map(f => f.id === updatedFamily.id ? updatedFamily : f));
} else {
const newFamily = await CondoService.addFamily(payload);
setFamilies([...families, newFamily]);
}
setShowFamilyModal(false);
} catch (e: any) {
console.error(e);
alert(`Errore: ${e.message || "Impossibile salvare la famiglia."}`);
}
};
const openAddUserModal = () => {
setEditingUser(null);
setUserForm({ name: '', email: '', password: '', phone: '', role: 'user', familyId: '', receiveAlerts: true });
setShowUserModal(true);
};
const openEditUserModal = (user: User) => {
setEditingUser(user);
setUserForm({ name: user.name || '', email: user.email, password: '', phone: user.phone || '', role: user.role || 'user', familyId: user.familyId || '', receiveAlerts: user.receiveAlerts ?? true });
setShowUserModal(true);
};
const handleUserSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
if (editingUser) {
await CondoService.updateUser(editingUser.id, userForm);
} else {
await CondoService.createUser(userForm);
}
setUsers(await CondoService.getUsers(activeCondo?.id));
setShowUserModal(false);
} catch (e) {
alert("Errore nel salvataggio utente");
}
};
const handleDeleteUser = async (id: string) => {
if(!window.confirm("Eliminare utente?")) return;
await CondoService.deleteUser(id);
setUsers(users.filter(u => u.id !== id));
};
const openAddNoticeModal = () => {
setEditingNotice(null);
setNoticeTargetMode('all');
setNoticeForm({ title: '', content: '', type: 'info', link: '', condoId: activeCondo?.id || '', active: true, targetFamilyIds: [] });
setShowNoticeModal(true);
};
const openEditNoticeModal = (n: Notice) => {
setEditingNotice(n);
const isTargeted = n.targetFamilyIds && n.targetFamilyIds.length > 0;
setNoticeTargetMode(isTargeted ? 'specific' : 'all');
setNoticeForm({ title: n.title, content: n.content, type: n.type, link: n.link || '', condoId: n.condoId, active: n.active, targetFamilyIds: n.targetFamilyIds || [] });
setShowNoticeModal(true);
};
const handleNoticeSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const payload: Notice = {
id: editingNotice ? editingNotice.id : '',
...noticeForm,
targetFamilyIds: noticeTargetMode === 'all' ? [] : noticeForm.targetFamilyIds,
date: editingNotice ? editingNotice.date : new Date().toISOString()
};
await CondoService.saveNotice(payload);
setNotices(await CondoService.getNotices(activeCondo?.id));
setShowNoticeModal(false);
} catch (e) {
console.error(e);
}
};
const handleDeleteNotice = async (id: string) => {
if(!window.confirm("Eliminare annuncio?")) return;
await CondoService.deleteNotice(id);
setNotices(notices.filter(n => n.id !== id));
};
const toggleNoticeActive = async (notice: Notice) => {
try {
const updated = { ...notice, active: !notice.active };
await CondoService.saveNotice(updated);
setNotices(notices.map(n => n.id === notice.id ? updated : n));
} catch (e) {
console.error(e);
}
};
const toggleNoticeFamilyTarget = (familyId: string) => {
setNoticeForm(prev => {
const current = prev.targetFamilyIds;
if (current.includes(familyId)) {
return { ...prev, targetFamilyIds: current.filter(id => id !== familyId) };
} else {
return { ...prev, targetFamilyIds: [...current, familyId] };
}
});
};
const openReadDetails = (noticeId: string) => {
setSelectedNoticeId(noticeId);
setShowReadDetailsModal(true);
};
const openAddAlertModal = () => {
setEditingAlert(null);
setAlertForm({ subject: '', body: '', daysOffset: 1, offsetType: 'before_next_month', sendHour: 9, active: true });
setShowAlertModal(true);
};
const openEditAlertModal = (alert: AlertDefinition) => {
setEditingAlert(alert);
setAlertForm(alert);
setShowAlertModal(true);
};
const handleAlertSubmit = async (e: React.FormEvent) => {
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!
};
const saved = await CondoService.saveAlert(payload);
setAlerts(editingAlert ? alerts.map(a => a.id === saved.id ? saved : a) : [...alerts, saved]);
setShowAlertModal(false);
} catch (e) {
console.error(e);
}
};
const handleDeleteAlert = async (id: string) => {
if(!window.confirm("Eliminare avviso?")) return;
await CondoService.deleteAlert(id);
setAlerts(alerts.filter(a => a.id !== id));
};
const getCondoName = (id: string) => condos.find(c => c.id === id)?.name || 'Sconosciuto';
const toggleFeature = (key: keyof AppSettings['features']) => { const toggleFeature = (key: keyof AppSettings['features']) => {
if (!globalSettings) return; if (!globalSettings) return;
@@ -577,7 +280,7 @@ export const SettingsPage: React.FC = () => {
if (isPrivileged) { if (isPrivileged) {
tabs.push( tabs.push(
{ id: 'general', label: 'Condominio', icon: <Building className="w-4 h-4"/> }, { id: 'general', label: 'Condominio', icon: <Building className="w-4 h-4"/> },
{ id: 'storage', label: 'Cloud & Storage', icon: <HardDrive className="w-4 h-4"/> } // Moved up here { id: 'storage', label: 'Cloud & Storage', icon: <HardDrive className="w-4 h-4"/> }
); );
if (globalSettings?.features.multiCondo) { if (globalSettings?.features.multiCondo) {
tabs.push({ id: 'condos', label: 'Lista Condomini', icon: <List className="w-4 h-4"/> }); tabs.push({ id: 'condos', label: 'Lista Condomini', icon: <List className="w-4 h-4"/> });
@@ -597,32 +300,46 @@ export const SettingsPage: React.FC = () => {
if (loading) return <div className="p-8 text-center text-slate-400">Caricamento...</div>; if (loading) return <div className="p-8 text-center text-slate-400">Caricamento...</div>;
return ( return (
<div className="max-w-5xl mx-auto space-y-6 pb-20"> <div className="max-w-6xl mx-auto pb-20">
{/* HEADER */} <div className="mb-6">
<div>
<h2 className="text-2xl font-bold text-slate-800">Impostazioni</h2> <h2 className="text-2xl font-bold text-slate-800">Impostazioni</h2>
<p className="text-slate-500 text-sm md:text-base">{activeCondo ? `Gestione: ${activeCondo.name}` : 'Pannello di Controllo'}</p> <p className="text-slate-500">{activeCondo ? `Gestione: ${activeCondo.name}` : 'Pannello di Controllo'}</p>
</div> </div>
{/* TABS NAVIGATION */} <div className="flex flex-col md:flex-row gap-6">
<div className="flex border-b border-slate-200 overflow-x-auto no-scrollbar pb-1 gap-1"> {/* NAVIGATION SIDEBAR / MOBILE SCROLL */}
<div className="md:w-64 flex-shrink-0">
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden sticky top-4">
<div className="p-4 border-b border-slate-100 md:block hidden">
<h3 className="font-bold text-slate-700 text-sm uppercase tracking-wider">Menu</h3>
</div>
{/* Mobile: Horizontal Scroll, Desktop: Vertical List */}
<div className="flex md:flex-col overflow-x-auto md:overflow-visible no-scrollbar p-2 md:p-2 gap-2">
{tabs.map(tab => ( {tabs.map(tab => (
<button <button
key={tab.id} key={tab.id}
onClick={() => setActiveTab(tab.id)} onClick={() => setActiveTab(tab.id)}
className={` className={`
px-4 py-3 font-medium text-sm whitespace-nowrap flex items-center gap-2 rounded-t-lg transition-colors flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm font-medium transition-all whitespace-nowrap flex-shrink-0
${activeTab === tab.id ? 'text-blue-600 border-b-2 border-blue-600 bg-blue-50/50' : 'text-slate-500 hover:text-slate-700 hover:bg-slate-50'} ${activeTab === tab.id
? 'bg-blue-50 text-blue-700 shadow-sm'
: 'text-slate-600 hover:bg-slate-50 hover:text-slate-900'}
`} `}
> >
{tab.icon}{tab.label} {tab.icon}
<span>{tab.label}</span>
{activeTab === tab.id && <ChevronRight className="w-4 h-4 ml-auto hidden md:block opacity-50"/>}
</button> </button>
))} ))}
</div> </div>
</div>
</div>
{/* CONTENT AREA */}
<div className="flex-1 min-w-0">
{/* PROFILE TAB */} {/* PROFILE TAB */}
{activeTab === 'profile' && ( {activeTab === 'profile' && (
<div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6 max-w-2xl"> <div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<h3 className="text-lg font-bold text-slate-800 mb-6 flex items-center gap-2"><UserIcon className="w-5 h-5 text-blue-600" /> Il Tuo Profilo</h3> <h3 className="text-lg font-bold text-slate-800 mb-6 flex items-center gap-2"><UserIcon className="w-5 h-5 text-blue-600" /> Il Tuo Profilo</h3>
<form onSubmit={handleProfileSubmit} className="space-y-5"> <form onSubmit={handleProfileSubmit} className="space-y-5">
<div className="grid grid-cols-1 md:grid-cols-2 gap-5"> <div className="grid grid-cols-1 md:grid-cols-2 gap-5">
@@ -649,9 +366,9 @@ export const SettingsPage: React.FC = () => {
</div> </div>
)} )}
{/* FEATURES TAB (ADMIN ONLY) */} {/* FEATURES TAB */}
{isSuperAdmin && activeTab === 'features' && globalSettings && ( {isSuperAdmin && activeTab === 'features' && globalSettings && (
<div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6 max-w-3xl"> <div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-bold text-slate-800 flex items-center gap-2"><LayoutGrid className="w-5 h-5 text-blue-600" /> Funzionalità Piattaforma</h3> <h3 className="text-lg font-bold text-slate-800 flex items-center gap-2"><LayoutGrid className="w-5 h-5 text-blue-600" /> Funzionalità Piattaforma</h3>
</div> </div>
@@ -704,7 +421,7 @@ export const SettingsPage: React.FC = () => {
{/* STORAGE CONFIG TAB */} {/* STORAGE CONFIG TAB */}
{isPrivileged && activeTab === 'storage' && ( {isPrivileged && activeTab === 'storage' && (
<div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6 max-w-2xl"> <div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Cloud className="w-5 h-5 text-blue-600" /> Configurazione Storage</h3> <h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Cloud className="w-5 h-5 text-blue-600" /> Configurazione Storage</h3>
<p className="text-sm text-slate-500 mb-6">Scegli dove salvare i documenti caricati.</p> <p className="text-sm text-slate-500 mb-6">Scegli dove salvare i documenti caricati.</p>
@@ -773,7 +490,7 @@ export const SettingsPage: React.FC = () => {
{isPrivileged && activeTab === 'general' && ( {isPrivileged && activeTab === 'general' && (
<div className="space-y-6 animate-fade-in"> <div className="space-y-6 animate-fade-in">
{!activeCondo ? <div className="bg-amber-50 border border-amber-200 text-amber-800 p-4 rounded-lg">Nessun condominio selezionato.</div> : ( {!activeCondo ? <div className="bg-amber-50 border border-amber-200 text-amber-800 p-4 rounded-lg">Nessun condominio selezionato.</div> : (
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 md:p-8 max-w-2xl"> <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<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> <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"> <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.name} onChange={(e) => setActiveCondo({ ...activeCondo, name: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome" required />
@@ -798,7 +515,7 @@ export const SettingsPage: React.FC = () => {
</form> </form>
</div> </div>
)} )}
{globalSettings && (<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 md:p-8 max-w-2xl"><h3 className="font-bold text-slate-800 mb-2">Anno Fiscale</h3><p className="text-slate-600 mb-4">Corrente: <strong>{globalSettings.currentYear}</strong></p><button type="button" onClick={handleNewYear} className="bg-slate-800 text-white px-4 py-2 rounded-lg text-sm">Chiudi Anno {globalSettings.currentYear}</button></div>)} {globalSettings && (<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 md:p-8"><h3 className="font-bold text-slate-800 mb-2">Anno Fiscale</h3><p className="text-slate-600 mb-4">Corrente: <strong>{globalSettings.currentYear}</strong></p><button type="button" onClick={handleNewYear} className="bg-slate-800 text-white px-4 py-2 rounded-lg text-sm">Chiudi Anno {globalSettings.currentYear}</button></div>)}
</div> </div>
)} )}
@@ -806,7 +523,7 @@ export const SettingsPage: React.FC = () => {
{isPrivileged && activeTab === 'condos' && ( {isPrivileged && activeTab === 'condos' && (
<div className="space-y-4 animate-fade-in"> <div className="space-y-4 animate-fade-in">
<div className="flex justify-between items-center bg-blue-50 p-4 rounded-xl border border-blue-100"><div><h3 className="font-bold text-blue-800">I Tuoi Condomini</h3></div><button onClick={openAddCondoModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex gap-2"><Plus className="w-4 h-4" /> Aggiungi</button></div> <div className="flex justify-between items-center bg-blue-50 p-4 rounded-xl border border-blue-100"><div><h3 className="font-bold text-blue-800">I Tuoi Condomini</h3></div><button onClick={openAddCondoModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex gap-2"><Plus className="w-4 h-4" /> Aggiungi</button></div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{condos.map(condo => ( {condos.map(condo => (
<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'}`}> <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>} {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>}
@@ -923,6 +640,8 @@ export const SettingsPage: React.FC = () => {
</div> </div>
</div> </div>
)} )}
</div>
</div>
{/* Condo Modal */} {/* Condo Modal */}
{showCondoModal && ( {showCondoModal && (