Update Settings.tsx

This commit is contained in:
2026-01-09 22:35:03 +01:00
committed by GitHub
parent 807db246fd
commit f608bd636d

View File

@@ -1,25 +1,23 @@
import React, { useEffect, useState } from 'react';
import { CondoService } from '../services/mockDb';
import { AppSettings, Family, User, AlertDefinition, Condo, Notice, NoticeIconType, NoticeRead } from '../types';
import { AppSettings, Family, User, AlertDefinition, Condo, Notice, NoticeIconType, NoticeRead, BrandingConfig, StorageConfig } 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, MapPin, CreditCard,
ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send, Cloud, HardDrive, ChevronRight, Palette, Monitor,
Sun, Moon, Check
ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send, Cloud, HardDrive, ChevronRight, Palette, Image as ImageIcon, Upload
} from 'lucide-react';
const PALETTES = [
{ name: 'Blue (Standard)', color: '#2563eb' },
{ name: 'Indigo', color: '#4f46e5' },
{ name: 'Emerald', color: '#059669' },
{ name: 'Rose', color: '#e11d48' },
{ name: 'Amber', color: '#d97706' },
{ name: 'Slate', color: '#475569' },
{ name: 'Purple', color: '#9333ea' }
];
const COLOR_MAP: Record<string, string> = {
blue: '#2563eb',
purple: '#9333ea',
green: '#16a34a',
red: '#dc2626',
orange: '#ea580c',
slate: '#475569'
};
export const SettingsPage: React.FC = () => {
const currentUser = CondoService.getCurrentUser();
@@ -31,75 +29,80 @@ export const SettingsPage: React.FC = () => {
const [activeTab, setActiveTab] = useState<TabType>(isPrivileged ? 'general' : 'profile');
const [loading, setLoading] = useState(true);
// Profile State
// Profile Form
const [profileForm, setProfileForm] = useState({
name: currentUser?.name || '',
phone: currentUser?.phone || '',
password: '',
receiveAlerts: currentUser?.receiveAlerts ?? true,
theme: localStorage.getItem('app-theme') || 'light'
receiveAlerts: currentUser?.receiveAlerts ?? true
});
const [profileSaving, setProfileSaving] = useState(false);
const [profileMsg, setProfileMsg] = useState('');
// Branding State
const [brandingForm, setBrandingForm] = useState({
appName: '',
appIcon: '',
loginBg: '',
primaryColor: '#2563eb'
});
// Branding & Features
const [activeCondo, setActiveCondo] = useState<Condo | undefined>(undefined);
const [globalSettings, setGlobalSettings] = useState<AppSettings | null>(null);
const [brandingForm, setBrandingForm] = useState<BrandingConfig>({ appName: 'CondoPay', primaryColor: 'blue', logoUrl: '', loginBackgroundUrl: '' });
// Condos List
const [condos, setCondos] = useState<Condo[]>([]);
const [showCondoModal, setShowCondoModal] = useState(false);
const [editingCondo, setEditingCondo] = useState<Condo | null>(null);
const [condoForm, setCondoForm] = useState({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100, dueDay: 10 });
const [condoForm, setCondoForm] = useState<Partial<Condo>>({
name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100, dueDay: 10
});
const [saving, setSaving] = useState(false);
const [successMsg, setSuccessMsg] = useState('');
// Families Management
const [families, setFamilies] = useState<Family[]>([]);
const [showFamilyModal, setShowFamilyModal] = useState(false);
const [editingFamily, setEditingFamily] = useState<Family | null>(null);
const [familyForm, setFamilyForm] = useState({ name: '', unitNumber: '', stair: '', floor: '', notes: '', contactEmail: '', customMonthlyQuota: '' });
// Users Management
const [users, setUsers] = useState<User[]>([]);
const [showUserModal, setShowUserModal] = useState(false);
const [editingUser, setEditingUser] = useState<User | null>(null);
const [userForm, setUserForm] = useState({ name: '', email: '', password: '', phone: '', role: 'user', familyId: '', receiveAlerts: true });
const [userForm, setUserForm] = useState({ name: '', email: '', password: '', phone: '', role: 'user' as User['role'], familyId: '', receiveAlerts: true });
// Alerts & Email Rules
const [alerts, setAlerts] = useState<AlertDefinition[]>([]);
const [showAlertModal, setShowAlertModal] = useState(false);
const [editingAlert, setEditingAlert] = useState<AlertDefinition | null>(null);
const [alertForm, setAlertForm] = useState<Partial<AlertDefinition>>({ subject: '', body: '', daysOffset: 1, offsetType: 'before_next_month', sendHour: 9, active: true });
const [showSmtpModal, setShowSmtpModal] = useState(false);
const [testingSmtp, setTestingSmtp] = useState(false);
const [testSmtpMsg, setTestSmtpMsg] = useState('');
// Notices (Bacheca)
const [notices, setNotices] = useState<Notice[]>([]);
const [showNoticeModal, setShowNoticeModal] = useState(false);
const [editingNotice, setEditingNotice] = useState<Notice | null>(null);
const [noticeTargetMode, setNoticeTargetMode] = useState<'all' | 'specific'>('all');
const [noticeForm, setNoticeForm] = useState<{ title: string; content: string; type: 'info' | 'warning' | 'maintenance' | 'event'; link: string; condoId: string; active: boolean; targetFamilyIds: string[]; }>({ title: '', content: '', type: 'info', link: '', condoId: '', active: true, targetFamilyIds: [] });
const [noticeForm, setNoticeForm] = useState({ title: '', content: '', type: 'info' as NoticeIconType, link: '', condoId: '', active: true, targetFamilyIds: [] as string[] });
const [noticeReadStats, setNoticeReadStats] = useState<Record<string, NoticeRead[]>>({});
const [showReadDetailsModal, setShowReadDetailsModal] = useState(false);
const [selectedNoticeId, setSelectedNoticeId] = useState<string | null>(null);
// Cloud Storage Settings
const [storageForm, setStorageForm] = useState<StorageConfig>({ provider: 'local_db', apiKey: '', apiSecret: '', bucket: '', region: '' });
useEffect(() => {
const fetchData = async () => {
try {
const activeC = await CondoService.getActiveCondo();
setActiveCondo(activeC);
const gSettings = await CondoService.getSettings();
if (isPrivileged) {
const condoList = await CondoService.getCondos();
const gSettings = await CondoService.getSettings();
setCondos(condoList);
setGlobalSettings(gSettings);
setBrandingForm({
appName: gSettings.branding?.appName || 'CondoPay',
appIcon: gSettings.branding?.appIcon || '',
loginBg: gSettings.branding?.loginBg || '',
primaryColor: gSettings.branding?.primaryColor || '#2563eb'
});
setActiveCondo(activeC);
setGlobalSettings(gSettings);
setBrandingForm(gSettings.branding || { appName: 'CondoPay', primaryColor: 'blue' });
setStorageForm(gSettings.storageConfig || { provider: 'local_db', apiKey: '', apiSecret: '', bucket: '', region: '' });
if (isPrivileged) {
setCondos(await CondoService.getCondos());
if (activeC) {
setFamilies(await CondoService.getFamilies(activeC.id));
setUsers(await CondoService.getUsers(activeC.id));
@@ -113,211 +116,520 @@ export const SettingsPage: React.FC = () => {
setNoticeReadStats(stats);
}
}
} catch(e) { console.error(e); } finally { setLoading(false); }
} catch(e) { console.error("Error loading settings data", e); } finally { setLoading(false); }
};
fetchData();
}, [isPrivileged]);
// --- SUBMIT HANDLERS ---
const handleProfileSubmit = async (e: React.FormEvent) => {
e.preventDefault(); setProfileSaving(true); setProfileMsg('');
try {
await CondoService.updateProfile(profileForm);
// Apply theme
localStorage.setItem('app-theme', profileForm.theme);
if (profileForm.theme === 'dark') document.documentElement.classList.add('dark');
else document.documentElement.classList.remove('dark');
setProfileMsg('Profilo aggiornato!');
setTimeout(() => setProfileMsg(''), 3000);
} catch (e) { setProfileMsg('Errore'); } finally { setProfileSaving(false); }
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) => {
e.preventDefault(); if (!activeCondo) return; setSaving(true); setSuccessMsg('');
try { await CondoService.saveCondo(activeCondo); setSuccessMsg('Dati condominio aggiornati!'); setTimeout(() => setSuccessMsg(''), 3000); setCondos(await CondoService.getCondos()); } catch (e) { console.error(e); } finally { setSaving(false); }
};
const handleFeaturesSubmit = async (e: React.FormEvent) => {
e.preventDefault(); if (!globalSettings) return; setSaving(true);
try { await CondoService.updateSettings(globalSettings); setSuccessMsg('Configurazione salvata!'); setTimeout(() => { setSuccessMsg(''); window.location.reload(); }, 1500); } catch (e) { console.error(e); } finally { setSaving(false); }
};
const handleBrandingSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!globalSettings) return;
setSaving(true);
try {
e.preventDefault(); if (!globalSettings) return; setSaving(true);
try {
const updated = { ...globalSettings, branding: brandingForm };
await CondoService.updateSettings(updated);
setGlobalSettings(updated);
setSuccessMsg('Branding aggiornato!');
window.dispatchEvent(new Event('branding-updated'));
// Apply color immediately to UI
document.documentElement.style.setProperty('--primary-color', brandingForm.primaryColor);
setTimeout(() => setSuccessMsg(''), 3000);
setSuccessMsg('Branding salvato!'); setTimeout(() => setSuccessMsg(''), 3000);
} catch(e) { console.error(e); } finally { setSaving(false); }
};
const handleStorageSubmit = async (e: React.FormEvent) => {
e.preventDefault(); if (!globalSettings) return; setSaving(true);
try { const updated = { ...globalSettings, storageConfig: storageForm }; await CondoService.updateSettings(updated); setGlobalSettings(updated); setSuccessMsg('Configurazione Storage salvata!'); setTimeout(() => setSuccessMsg(''), 3000); } catch (e) { console.error(e); } finally { setSaving(false); }
};
const handleLogoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) { const reader = new FileReader(); reader.onload = (ev) => setBrandingForm(prev => ({...prev, logoUrl: ev.target?.result as string})); reader.readAsDataURL(file); }
};
const handleBgUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) { const reader = new FileReader(); reader.onload = (ev) => setBrandingForm(prev => ({...prev, loginBackgroundUrl: ev.target?.result as string})); reader.readAsDataURL(file); }
};
// --- CRUD Handlers (Simplified for brevity as logic is unchanged) ---
const handleGeneralSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!activeCondo) return; setSaving(true); try { await CondoService.saveCondo(activeCondo); setSuccessMsg('Aggiornato!'); setTimeout(() => setSuccessMsg(''), 3000); } catch (e) {} finally { setSaving(false); } };
const handleFeaturesSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!globalSettings) return; setSaving(true); try { await CondoService.updateSettings(globalSettings); setSuccessMsg('Salvato!'); window.location.reload(); } catch (e) {} finally { setSaving(false); } };
const handleSmtpSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!globalSettings) return; setSaving(true); try { await CondoService.updateSettings(globalSettings); setShowSmtpModal(false); } catch (e) {} finally { setSaving(false); } };
const handleStorageSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!globalSettings) return; setSaving(true); try { await CondoService.updateSettings(globalSettings); setSuccessMsg('Salvato!'); } catch (e) {} finally { setSaving(false); } };
const handleCondoSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { await CondoService.saveCondo({ id: editingCondo?.id || '', ...condoForm }); setCondos(await CondoService.getCondos()); setShowCondoModal(false); } catch (e) {} };
const handleDeleteCondo = async (id: string) => { if(!confirm("Eliminare?")) return; await CondoService.deleteCondo(id); setCondos(await CondoService.getCondos()); };
const handleFamilySubmit = async (e: React.FormEvent) => { e.preventDefault(); try { const quota = familyForm.customMonthlyQuota ? parseFloat(familyForm.customMonthlyQuota) : undefined; if (editingFamily) { await CondoService.updateFamily({ ...editingFamily, ...familyForm, customMonthlyQuota: quota }); } else { await CondoService.addFamily({ ...familyForm, customMonthlyQuota: quota }); } setFamilies(await CondoService.getFamilies(activeCondo?.id)); setShowFamilyModal(false); } catch (e) {} };
const handleDeleteFamily = async (id: string) => { if(!confirm("Eliminare?")) return; await CondoService.deleteFamily(id); setFamilies(families.filter(f => f.id !== id)); };
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) {} };
const handleDeleteUser = async (id: string) => { if(!confirm("Eliminare?")) return; await CondoService.deleteUser(id); setUsers(users.filter(u => u.id !== id)); };
const handleNoticeSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { const now = new Date(); const sqlDate = now.toISOString().slice(0, 19).replace('T', ' '); await CondoService.saveNotice({ id: editingNotice?.id || '', ...noticeForm, targetFamilyIds: noticeTargetMode === 'all' ? [] : noticeForm.targetFamilyIds, date: editingNotice ? editingNotice.date : sqlDate }); setNotices(await CondoService.getNotices(activeCondo?.id)); setShowNoticeModal(false); } catch (e) {} };
const handleDeleteNotice = async (id: string) => { if(!confirm("Eliminare?")) return; await CondoService.deleteNotice(id); setNotices(notices.filter(n => n.id !== id)); };
const handleAlertSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { await CondoService.saveAlert({ id: editingAlert?.id || '', ...alertForm } as any); setAlerts(await CondoService.getAlerts(activeCondo?.id)); setShowAlertModal(false); } catch (e) {} };
const handleDeleteAlert = async (id: string) => { if(!confirm("Eliminare?")) return; await CondoService.deleteAlert(id); setAlerts(alerts.filter(a => a.id !== id)); };
// --- CRUD HELPERS ---
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(c); setShowCondoModal(true); };
const handleCondoSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { await CondoService.saveCondo({ id: editingCondo?.id || '', ...condoForm } as Condo); setCondos(await CondoService.getCondos()); setShowCondoModal(false); window.dispatchEvent(new Event('condo-updated')); } catch (e) { alert("Errore"); } };
const handleDeleteCondo = async (id: string) => { if(window.confirm("Eliminare definitivamente il condominio?")) { await CondoService.deleteCondo(id); setCondos(await CondoService.getCondos()); } };
const openAddFamilyModal = () => { setEditingFamily(null); setFamilyForm({ name: '', unitNumber: '', stair: '', floor: '', notes: '', contactEmail: '', customMonthlyQuota: '' }); setShowFamilyModal(true); };
const openEditFamilyModal = (f: Family) => { setEditingFamily(f); setFamilyForm({ name: f.name, unitNumber: f.unitNumber, stair: f.stair || '', floor: f.floor || '', notes: f.notes || '', contactEmail: f.contactEmail || '', customMonthlyQuota: f.customMonthlyQuota?.toString() || '' }); setShowFamilyModal(true); };
const handleFamilySubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingFamily) { await CondoService.updateFamily({ ...editingFamily, ...familyForm, customMonthlyQuota: familyForm.customMonthlyQuota ? parseFloat(familyForm.customMonthlyQuota) : undefined }); } else { await CondoService.addFamily({ ...familyForm, customMonthlyQuota: familyForm.customMonthlyQuota ? parseFloat(familyForm.customMonthlyQuota) : undefined }); } setFamilies(await CondoService.getFamilies(activeCondo?.id)); setShowFamilyModal(false); } catch (e) { alert("Errore"); } };
const handleDeleteFamily = async (id: string) => { if (window.confirm("Eliminare la famiglia?")) { await CondoService.deleteFamily(id); setFamilies(await CondoService.getFamilies(activeCondo?.id)); } };
const openAddUserModal = () => { setEditingUser(null); setUserForm({ name: '', email: '', password: '', phone: '', role: 'user', familyId: '', receiveAlerts: true }); setShowUserModal(true); };
const openEditUserModal = (u: User) => { setEditingUser(u); setUserForm({ name: u.name || '', email: u.email, password: '', phone: u.phone || '', role: u.role || 'user', familyId: u.familyId || '', receiveAlerts: u.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 salvataggio utente"); } };
const handleDeleteUser = async (id: string) => { if(window.confirm("Eliminare questo account utente?")) { await CondoService.deleteUser(id); setUsers(await CondoService.getUsers(activeCondo?.id)); } };
const openAddNoticeModal = () => { setEditingNotice(null); setNoticeForm({ title: '', content: '', type: 'info', link: '', condoId: activeCondo?.id || '', active: true, targetFamilyIds: [] }); setNoticeTargetMode('all'); setShowNoticeModal(true); };
const openEditNoticeModal = (n: Notice) => { setEditingNotice(n); setNoticeForm({ title: n.title, content: n.content, type: n.type, link: n.link || '', condoId: n.condoId, active: n.active, targetFamilyIds: n.targetFamilyIds || [] }); setNoticeTargetMode(n.targetFamilyIds && n.targetFamilyIds.length > 0 ? 'specific' : 'all'); setShowNoticeModal(true); };
const handleNoticeSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { const payload = { ...noticeForm, targetFamilyIds: noticeTargetMode === 'all' ? [] : noticeForm.targetFamilyIds }; if (editingNotice) { await CondoService.saveNotice({ ...editingNotice, ...payload }); } else { await CondoService.saveNotice(payload as any); } setNotices(await CondoService.getNotices(activeCondo?.id)); setShowNoticeModal(false); } catch (e) { alert("Errore"); } };
const handleDeleteNotice = async (id: string) => { if(confirm('Eliminare l\'avviso?')) { await CondoService.deleteNotice(id); setNotices(await CondoService.getNotices(activeCondo?.id)); } };
const openAddAlertModal = () => { setEditingAlert(null); setAlertForm({ subject: '', body: '', daysOffset: 1, offsetType: 'before_next_month', sendHour: 9, active: true }); setShowAlertModal(true); };
const openEditAlertModal = (a: AlertDefinition) => { setEditingAlert(a); setAlertForm(a); setShowAlertModal(true); };
const handleAlertSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { await CondoService.saveAlert({ ...editingAlert, ...alertForm } as AlertDefinition); setAlerts(await CondoService.getAlerts(activeCondo?.id)); setShowAlertModal(false); } catch(e) { alert("Errore"); } };
const handleDeleteAlert = async (id: string) => { if(window.confirm("Eliminare la regola di avviso?")) { await CondoService.deleteAlert(id); setAlerts(await CondoService.getAlerts(activeCondo?.id)); } };
const handleTestSmtp = async (e: React.FormEvent) => {
e.preventDefault(); if (!globalSettings?.smtpConfig) return; setTestingSmtp(true); setTestSmtpMsg('');
try { await CondoService.testSmtpConfig(globalSettings.smtpConfig); setTestSmtpMsg('Invio riuscito!'); } catch (e: any) { setTestSmtpMsg('Errore: ' + e.message); } finally { setTestingSmtp(false); }
};
const toggleFeature = (key: keyof AppSettings['features']) => {
if (!globalSettings) return;
setGlobalSettings({ ...globalSettings, features: { ...globalSettings.features, [key]: !globalSettings.features[key] } });
};
// --- RENDER HELPERS ---
const tabs: {id: TabType, label: string, icon: React.ReactNode}[] = [
{ id: 'profile', label: 'Profilo & Tema', icon: <UserIcon className="w-4 h-4"/> },
{ id: 'profile', label: 'Il Tuo Profilo', icon: <UserIcon className="w-4 h-4"/> },
];
if (isSuperAdmin) {
tabs.push({ id: 'features', label: 'Funzionalità', icon: <LayoutGrid className="w-4 h-4"/> });
tabs.push({ id: 'branding', label: 'Personalizzazione', icon: <Palette className="w-4 h-4"/> });
tabs.push(
{ id: 'features', label: 'Funzionalità', icon: <LayoutGrid className="w-4 h-4"/> },
{ id: 'branding', label: 'Personalizzazione', icon: <Palette className="w-4 h-4"/> }
);
}
if (isPrivileged) {
tabs.push({ id: 'general', label: 'Condominio', icon: <Building className="w-4 h-4"/> });
if (globalSettings?.features.documents) tabs.push({ id: 'storage', label: 'Storage', icon: <HardDrive className="w-4 h-4"/> });
if (globalSettings?.features.multiCondo) tabs.push({ id: 'condos', label: 'Condomini', icon: <List className="w-4 h-4"/> });
tabs.push({ id: 'families', label: 'Famiglie', icon: <Coins className="w-4 h-4"/> }, { id: 'users', label: 'Utenti', icon: <UserCog className="w-4 h-4"/> });
if (globalSettings?.features.notices) tabs.push({ id: 'notices', label: 'Bacheca', icon: <Megaphone className="w-4 h-4"/> });
tabs.push({ id: 'alerts', label: 'Avvisi', icon: <Bell className="w-4 h-4"/> });
if (globalSettings?.features.documents) tabs.push({ id: 'storage', label: 'Cloud Storage', icon: <HardDrive className="w-4 h-4"/> });
if (globalSettings?.features.multiCondo) tabs.push({ id: 'condos', label: 'Lista Condomini', icon: <List className="w-4 h-4"/> });
tabs.push(
{ id: 'families', label: 'Famiglie', icon: <Coins className="w-4 h-4"/> },
{ id: 'users', label: 'Account Utenti', icon: <UserCog className="w-4 h-4"/> }
);
if (globalSettings?.features.notices) tabs.push({ id: 'notices', label: 'Bacheca Avvisi', icon: <Megaphone className="w-4 h-4"/> });
tabs.push({ id: 'alerts', label: 'Scadenze & Email', icon: <Bell className="w-4 h-4"/> });
}
if (loading) return <div className="p-8 text-center text-slate-400">Caricamento...</div>;
// Componente per lo stato vuoto quando non c'è un condominio attivo
const NoCondoState = () => (
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-8 text-center">
<Building className="w-12 h-12 text-amber-400 mx-auto mb-4" />
<h3 className="text-xl font-bold text-amber-800 mb-2">Nessun Condominio Selezionato</h3>
<p className="text-amber-700 mb-6">Per accedere a questa sezione devi prima creare o selezionare un condominio.</p>
<button onClick={openAddCondoModal} className="bg-amber-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-amber-700 inline-flex items-center gap-2">
<Plus className="w-5 h-5"/> Crea il tuo primo Condominio
</button>
</div>
);
if (loading) return <div className="p-12 text-center text-slate-400">Inizializzazione impostazioni...</div>;
return (
<div className="max-w-6xl mx-auto pb-20">
<div className="mb-6">
<h2 className="text-2xl font-bold text-slate-800 dark:text-slate-100">Impostazioni</h2>
<p className="text-slate-500 dark:text-slate-400">{activeCondo ? `Gestione: ${activeCondo.name}` : 'Pannello di Controllo'}</p>
<div className="max-w-6xl mx-auto pb-32 animate-fade-in">
<div className="mb-8">
<h2 className="text-3xl font-bold text-slate-900">Pannello di Controllo</h2>
<p className="text-slate-500 mt-1">{activeCondo ? `Configurazione per: ${activeCondo.name}` : 'Benvenuto! Inizia configurando il tuo stabile.'}</p>
</div>
<div className="flex flex-col md:flex-row gap-6">
<div className="md:w-64 flex-shrink-0">
<div className="bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 overflow-hidden sticky top-4">
<div className="flex md:flex-col overflow-x-auto no-scrollbar p-2 gap-2">
<div className="flex flex-col md:flex-row gap-8">
{/* Sidebar Nav */}
<div className="md:w-72 flex-shrink-0">
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden sticky top-4">
<div className="flex md:flex-col overflow-x-auto no-scrollbar p-3 gap-1.5">
{tabs.map(tab => (
<button key={tab.id} onClick={() => setActiveTab(tab.id)} className={`flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm font-medium transition-all whitespace-nowrap ${activeTab === tab.id ? 'bg-primary/10 text-primary shadow-sm' : 'text-slate-600 dark:text-slate-400 hover:bg-slate-50 dark:hover:bg-slate-700'}`}>
{tab.icon} <span>{tab.label}</span>
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-bold transition-all whitespace-nowrap ${activeTab === tab.id ? 'bg-blue-600 text-white shadow-lg shadow-blue-200' : 'text-slate-600 hover:bg-slate-50'}`}
>
{tab.icon}
<span>{tab.label}</span>
</button>
))}
</div>
</div>
</div>
{/* Content Area */}
<div className="flex-1 min-w-0">
{activeTab === 'profile' && (
<div className="animate-fade-in bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-6">
<h3 className="text-lg font-bold text-slate-800 dark:text-slate-100 mb-6 flex items-center gap-2"><UserIcon className="w-5 h-5 text-primary" /> Profilo & Preferenze</h3>
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex items-center gap-3 mb-8"><div className="p-3 bg-blue-50 rounded-2xl text-blue-600"><UserIcon className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Il Tuo Profilo</h3></div>
<form onSubmit={handleProfileSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div><label className="text-sm font-medium text-slate-700 dark:text-slate-300">Nome</label><input type="text" value={profileForm.name} onChange={e => setProfileForm({...profileForm, name: e.target.value})} className="w-full border p-2.5 rounded-lg text-slate-700 dark:bg-slate-700 dark:text-slate-100"/></div>
<div><label className="text-sm font-medium text-slate-700 dark:text-slate-300">Tema App</label>
<div className="flex gap-2 mt-1">
<button type="button" onClick={() => setProfileForm({...profileForm, theme: 'light'})} className={`flex-1 py-2 px-3 rounded-lg border flex items-center justify-center gap-2 transition-all ${profileForm.theme === 'light' ? 'bg-white border-primary text-primary ring-1 ring-primary' : 'bg-slate-50 dark:bg-slate-700 border-slate-200 dark:border-slate-600 text-slate-500'}`}><Sun className="w-4 h-4"/> Chiaro</button>
<button type="button" onClick={() => setProfileForm({...profileForm, theme: 'dark'})} className={`flex-1 py-2 px-3 rounded-lg border flex items-center justify-center gap-2 transition-all ${profileForm.theme === 'dark' ? 'bg-slate-800 border-primary text-primary ring-1 ring-primary' : 'bg-slate-50 dark:bg-slate-700 border-slate-200 dark:border-slate-600 text-slate-500'}`}><Moon className="w-4 h-4"/> Scuro</button>
</div>
</div>
<div><label className="text-sm font-medium text-slate-700 dark:text-slate-300">Telefono</label><input type="tel" value={profileForm.phone} onChange={e => setProfileForm({...profileForm, phone: e.target.value})} className="w-full border p-2.5 rounded-lg text-slate-700 dark:bg-slate-700 dark:text-slate-100"/></div>
<div><label className="text-sm font-medium text-slate-700 dark:text-slate-300">Password</label><input type="password" placeholder="Nuova password" value={profileForm.password} onChange={e => setProfileForm({...profileForm, password: e.target.value})} className="w-full border p-2.5 rounded-lg text-slate-700 dark:bg-slate-700 dark:text-slate-100"/></div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome Completo</label><input type="text" value={profileForm.name} onChange={(e) => setProfileForm({...profileForm, name: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl focus:ring-2 focus:ring-blue-500 outline-none" required /></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Email</label><input type="email" value={currentUser?.email || ''} disabled className="w-full border border-slate-100 bg-slate-50 p-3 rounded-xl text-slate-400 cursor-not-allowed" /></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Telefono</label><input type="tel" value={profileForm.phone} onChange={(e) => setProfileForm({...profileForm, phone: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl focus:ring-2 focus:ring-blue-500 outline-none" /></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Password</label><input type="password" placeholder="Opzionale" value={profileForm.password} onChange={(e) => setProfileForm({...profileForm, password: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl focus:ring-2 focus:ring-blue-500 outline-none" /></div>
</div>
<button type="submit" disabled={profileSaving} className="bg-primary text-white px-6 py-2.5 rounded-lg font-bold hover:opacity-90 flex gap-2"><Save className="w-4 h-4" /> Aggiorna Impostazioni</button>
{profileMsg && <p className="text-sm text-green-600 font-medium">{profileMsg}</p>}
<div className="pt-2 border-t flex justify-between items-center"><button type="submit" disabled={profileSaving} className="bg-blue-600 text-white px-8 py-3 rounded-xl font-bold flex items-center gap-2 hover:bg-blue-700 shadow-lg shadow-blue-200 disabled:opacity-50"><Save className="w-4 h-4" /> {profileSaving ? '...' : 'Salva'}</button>{profileMsg && <p className="text-sm font-bold text-green-600">{profileMsg}</p>}</div>
</form>
</div>
)}
{activeTab === 'branding' && isSuperAdmin && (
<div className="animate-fade-in bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-6">
<h3 className="text-lg font-bold text-slate-800 dark:text-slate-100 mb-6 flex items-center gap-2"><Palette className="w-5 h-5 text-primary" /> Personalizzazione Piattaforma</h3>
<form onSubmit={handleBrandingSubmit} className="space-y-6">
<div className="space-y-4">
<div>
<label className="text-sm font-bold text-slate-700 dark:text-slate-300 mb-1 block">Nome Applicazione</label>
<input type="text" className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={brandingForm.appName} onChange={e => setBrandingForm({...brandingForm, appName: e.target.value})} placeholder="Es: MyCondo Manager" />
</div>
<div>
<label className="text-sm font-bold text-slate-700 dark:text-slate-300 mb-1 block">Icona App (Link URL Pubblico)</label>
<input type="url" className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={brandingForm.appIcon} onChange={e => setBrandingForm({...brandingForm, appIcon: e.target.value})} placeholder="https://image.com/logo.png" />
</div>
<div>
<label className="text-sm font-bold text-slate-700 dark:text-slate-300 mb-1 block">Sfondo Pagina Login (Link URL Pubblico)</label>
<input type="url" className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={brandingForm.loginBg} onChange={e => setBrandingForm({...brandingForm, loginBg: e.target.value})} placeholder="https://image.com/bg.jpg" />
</div>
<div>
<label className="text-sm font-bold text-slate-700 dark:text-slate-300 mb-3 block">Colore Primario / Palette</label>
<div className="grid grid-cols-4 sm:grid-cols-7 gap-3">
{PALETTES.map(p => (
<button
key={p.color}
type="button"
onClick={() => setBrandingForm({...brandingForm, primaryColor: p.color})}
className={`h-12 rounded-lg border-2 flex items-center justify-center transition-all ${brandingForm.primaryColor === p.color ? 'border-primary ring-2 ring-primary/20 scale-105' : 'border-transparent'}`}
style={{ backgroundColor: p.color }}
title={p.name}
>
{brandingForm.primaryColor === p.color && <Check className="w-6 h-6 text-white"/>}
</button>
))}
<div className="col-span-full flex items-center gap-3 mt-2">
<input type="color" value={brandingForm.primaryColor} onChange={e => setBrandingForm({...brandingForm, primaryColor: e.target.value})} className="h-10 w-10 border-none bg-transparent cursor-pointer" />
<span className="text-sm text-slate-500 font-medium">Scegli un colore personalizzato ({brandingForm.primaryColor})</span>
</div>
</div>
{isSuperAdmin && activeTab === 'branding' && globalSettings && (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex items-center gap-3 mb-8"><div className="p-3 bg-purple-50 rounded-2xl text-purple-600"><Palette className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Branding</h3></div>
<form onSubmit={handleBrandingSubmit} className="space-y-8">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome App</label><input className="w-full border border-slate-200 p-3 rounded-xl font-bold text-slate-800" value={brandingForm.appName} onChange={e => setBrandingForm({...brandingForm, appName: e.target.value})}/></div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-4 block">Colore Primario</label>
<div className="flex gap-4 flex-wrap items-center bg-slate-50 p-6 rounded-2xl border border-slate-100">
{Object.keys(COLOR_MAP).map(colorKey => (
<button key={colorKey} type="button" onClick={() => setBrandingForm({...brandingForm, primaryColor: colorKey})} className={`w-12 h-12 rounded-2xl border-4 transition-all hover:scale-110 flex items-center justify-center shadow-sm ${brandingForm.primaryColor === colorKey ? 'border-white ring-4 ring-slate-900' : 'border-transparent'}`} style={{ backgroundColor: COLOR_MAP[colorKey] }}>{brandingForm.primaryColor === colorKey && <CheckCircle2 className="w-6 h-6 text-white"/>}</button>
))}
<div className="h-10 w-px bg-slate-200 mx-2" />
<div className="flex items-center gap-2"><input type="color" value={brandingForm.primaryColor.startsWith('#') ? brandingForm.primaryColor : '#2563eb'} onChange={e => setBrandingForm({...brandingForm, primaryColor: e.target.value})} className="w-12 h-12 rounded-2xl border-2 cursor-pointer p-0 bg-transparent"/><input type="text" value={brandingForm.primaryColor} onChange={e => setBrandingForm({...brandingForm, primaryColor: e.target.value})} className="w-28 text-sm font-mono border border-slate-200 rounded-xl p-3 uppercase font-bold"/></div>
</div>
</div>
<div className="pt-4 border-t dark:border-slate-700">
<button type="submit" disabled={saving} className="bg-primary text-white px-8 py-3 rounded-lg font-bold shadow-lg hover:opacity-90 flex gap-2 items-center"><Save className="w-5 h-5"/> Salva Personalizzazioni</button>
{successMsg && <p className="text-sm text-green-600 mt-2 font-bold">{successMsg}</p>}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Logo</label><div className="border-4 border-dashed border-slate-100 rounded-3xl p-8 text-center relative h-48 flex items-center justify-center bg-slate-50/50 hover:bg-slate-50 transition-colors"><input type="file" onChange={handleLogoUpload} className="absolute inset-0 opacity-0 cursor-pointer z-10" />{brandingForm.logoUrl ? <img src={brandingForm.logoUrl} alt="Logo" className="max-h-full max-w-full object-contain" /> : <ImageIcon className="w-12 h-12 text-slate-300"/>}</div></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Sfondo Login</label><div className="border-4 border-dashed border-slate-100 rounded-3xl p-8 text-center relative h-48 flex items-center justify-center bg-slate-50/50 hover:bg-slate-50 transition-colors"><input type="file" onChange={handleBgUpload} className="absolute inset-0 opacity-0 cursor-pointer z-10" />{brandingForm.loginBackgroundUrl ? <img src={brandingForm.loginBackgroundUrl} alt="Bg" className="w-full h-full object-cover rounded-xl" /> : <Upload className="w-12 h-12 text-slate-300"/>}</div></div>
</div>
<div className="pt-6 border-t flex justify-end"><button type="submit" className="bg-slate-900 text-white px-10 py-4 rounded-2xl font-bold hover:bg-black shadow-xl"><Save className="w-5 h-5" /> Applica</button></div>
</form>
</div>
)}
{activeTab === 'features' && isSuperAdmin && globalSettings && (
<div className="animate-fade-in bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-bold text-slate-800 dark:text-slate-100 flex items-center gap-2"><LayoutGrid className="w-5 h-5 text-primary" /> Funzionalità Piattaforma</h3>
</div>
<form onSubmit={handleFeaturesSubmit} className="space-y-6">
<div className="space-y-4">
{Object.keys(globalSettings.features).map((key) => (
<div key={key} className="flex items-center justify-between p-4 bg-slate-50 dark:bg-slate-700/50 rounded-xl border border-slate-100 dark:border-slate-700">
<div><p className="font-bold text-slate-800 dark:text-slate-100 capitalize">{key.replace(/([A-Z])/g, ' $1')}</p></div>
<button type="button" onClick={() => {
const newFeats = { ...globalSettings.features, [key]: !globalSettings.features[key as keyof AppSettings['features']] };
setGlobalSettings({ ...globalSettings, features: newFeats });
}} className={`${globalSettings.features[key as keyof AppSettings['features']] ? 'bg-green-500' : 'bg-slate-300 dark:bg-slate-600'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features[key as keyof AppSettings['features']] ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
{isSuperAdmin && activeTab === 'features' && globalSettings && (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex items-center gap-3 mb-8"><div className="p-3 bg-green-50 rounded-2xl text-green-600"><LayoutGrid className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Moduli</h3></div>
<form onSubmit={handleFeaturesSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{['multiCondo', 'tickets', 'documents', 'payPal', 'notices', 'reports', 'extraordinaryExpenses', 'condoFinancialsView'].map(k => (
<div key={k} onClick={() => toggleFeature(k as any)} className={`flex items-center justify-between p-5 rounded-2xl border cursor-pointer ${globalSettings.features[k as keyof AppSettings['features']] ? 'bg-blue-50 border-blue-200' : 'bg-white border-slate-200'}`}>
<span className="font-bold text-slate-800 capitalize">{k.replace(/([A-Z])/g, ' $1').trim()}</span>
<span className={`h-6 w-11 rounded-full relative transition-colors ${globalSettings.features[k as keyof AppSettings['features']] ? 'bg-blue-600' : 'bg-slate-300'}`}><span className={`absolute top-1 left-1 bg-white w-4 h-4 rounded-full transition-transform ${globalSettings.features[k as keyof AppSettings['features']] ? 'translate-x-5' : ''}`}/></span>
</div>
))}
</div>
<button type="submit" className="bg-primary text-white px-6 py-2.5 rounded-lg font-bold"><Save className="w-4 h-4 inline mr-2"/> Salva Funzionalità</button>
<div className="pt-8 border-t flex justify-end"><button type="submit" className="bg-blue-600 text-white px-8 py-3 rounded-xl font-bold hover:bg-blue-700 shadow-lg">Salva</button></div>
</form>
</div>
)}
{activeTab === 'general' && isPrivileged && activeCondo && (
<div className="bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-6">
<h3 className="text-lg font-bold text-slate-800 dark:text-slate-100 mb-4 flex items-center gap-2"><Building className="w-5 h-5 text-primary" /> Dati Condominio</h3>
<form onSubmit={handleGeneralSubmit} className="space-y-5">
<input className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={activeCondo.name} onChange={e => setActiveCondo({...activeCondo, name: e.target.value})} placeholder="Nome" required />
<div className="grid grid-cols-2 gap-4">
<input className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={activeCondo.address || ''} onChange={e => setActiveCondo({...activeCondo, address: e.target.value})} placeholder="Indirizzo" required />
<input className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={activeCondo.city || ''} onChange={e => setActiveCondo({...activeCondo, city: e.target.value})} placeholder="Città" required />
{/* TAB CONDOMINIO (GENERAL) */}
{activeTab === 'general' && (
<>
{!activeCondo ? <NoCondoState /> : (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex items-center gap-3 mb-8"><div className="p-3 bg-blue-50 rounded-2xl text-blue-600"><Building className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Dati Condominio</h3></div>
<form onSubmit={handleGeneralSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome</label><input type="text" value={activeCondo.name} onChange={e => setActiveCondo({...activeCondo, name: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl font-bold" required /></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Indirizzo</label><input type="text" value={activeCondo.address || ''} onChange={e => setActiveCondo({...activeCondo, address: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl" required /></div>
<div className="grid grid-cols-2 gap-4">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Città</label><input type="text" value={activeCondo.city || ''} onChange={e => setActiveCondo({...activeCondo, city: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl" /></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Prov</label><input type="text" value={activeCondo.province || ''} onChange={e => setActiveCondo({...activeCondo, province: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl" /></div>
</div>
<div className="grid grid-cols-2 gap-4">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Quota Mensile</label><input type="number" value={activeCondo.defaultMonthlyQuota} onChange={e => setActiveCondo({...activeCondo, defaultMonthlyQuota: parseFloat(e.target.value)})} className="w-full border border-slate-200 p-3 rounded-xl font-bold" /></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Scadenza (Giorno)</label><input type="number" value={activeCondo.dueDay || 10} onChange={e => setActiveCondo({...activeCondo, dueDay: parseInt(e.target.value)})} className="w-full border border-slate-200 p-3 rounded-xl" /></div>
</div>
</div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">PayPal Client ID</label><input type="text" value={activeCondo.paypalClientId || ''} onChange={e => setActiveCondo({...activeCondo, paypalClientId: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl text-xs font-mono" /></div>
<div className="pt-6 border-t flex justify-end"><button type="submit" className="bg-blue-600 text-white px-8 py-3 rounded-xl font-bold hover:bg-blue-700 shadow-lg">Salva Dati</button></div>
</form>
</div>
<div className="flex gap-4">
<div className="flex-1">
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300">Quota Mensile </label>
<input type="number" className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={activeCondo.defaultMonthlyQuota} onChange={e => setActiveCondo({...activeCondo, defaultMonthlyQuota: parseFloat(e.target.value)})} required />
)}
</>
)}
{/* TAB STORAGE */}
{activeTab === 'storage' && (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex items-center gap-3 mb-8"><div className="p-3 bg-amber-50 rounded-2xl text-amber-600"><HardDrive className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Storage Cloud</h3></div>
<form onSubmit={handleStorageSubmit} className="space-y-6">
<select value={storageForm.provider} onChange={e => setStorageForm({...storageForm, provider: e.target.value as any})} className="w-full border border-slate-200 p-3 rounded-xl font-bold text-slate-700"><option value="local_db">Database Locale</option><option value="s3">Amazon S3</option></select>
{storageForm.provider !== 'local_db' && (
<div className="space-y-6 p-6 bg-slate-50 rounded-2xl border border-slate-100">
<input value={storageForm.apiKey || ''} onChange={e => setStorageForm({...storageForm, apiKey: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="API Key / Access Key" />
<input value={storageForm.apiSecret || ''} onChange={e => setStorageForm({...storageForm, apiSecret: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Secret Key" type="password" />
<input value={storageForm.bucket || ''} onChange={e => setStorageForm({...storageForm, bucket: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Bucket Name" />
</div>
<div className="flex-1">
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300">Scadenza Giorno</label>
<input type="number" className="w-full border p-2.5 rounded-lg dark:bg-slate-700 dark:text-slate-100" value={activeCondo.dueDay || 10} onChange={e => setActiveCondo({...activeCondo, dueDay: parseInt(e.target.value)})} required />
</div>
</div>
<button type="submit" className="bg-primary text-white px-6 py-2.5 rounded-lg font-bold">Salva Modifiche</button>
)}
<div className="pt-4 border-t flex justify-end"><button type="submit" className="bg-slate-900 text-white px-8 py-3 rounded-xl font-bold">Salva</button></div>
</form>
</div>
)}
{/* TAB LISTA CONDOMINI */}
{activeTab === 'condos' && (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex justify-between items-center mb-8"><h3 className="text-xl font-bold text-slate-800">Elenco Condomini</h3><button onClick={openAddCondoModal} className="bg-blue-600 text-white px-4 py-2 rounded-xl font-bold flex gap-2"><Plus className="w-4 h-4"/> Aggiungi</button></div>
<div className="grid gap-4">
{condos.map(c => (
<div key={c.id} className="flex items-center justify-between p-5 rounded-2xl border bg-slate-50 border-slate-100">
<div><p className="font-bold text-slate-800">{c.name}</p><p className="text-xs text-slate-500">{c.city}</p></div>
<div className="flex gap-2">
<button onClick={() => CondoService.setActiveCondo(c.id)} className="px-4 py-2 bg-white border border-slate-200 rounded-lg text-xs font-bold hover:bg-slate-50">Usa</button>
<button onClick={() => openEditCondoModal(c)} className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg"><Pencil className="w-5 h-5"/></button>
<button onClick={() => handleDeleteCondo(c.id)} className="p-2 text-red-500 hover:bg-red-50 rounded-lg"><Trash2 className="w-5 h-5"/></button>
</div>
</div>
))}
</div>
</div>
)}
{/* TAB FAMIGLIE */}
{activeTab === 'families' && (
<>
{!activeCondo ? <NoCondoState /> : (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex justify-between items-center mb-8"><h3 className="text-xl font-bold text-slate-800">Famiglie</h3><button onClick={openAddFamilyModal} className="bg-blue-600 text-white px-4 py-2 rounded-xl font-bold flex gap-2"><Plus className="w-4 h-4"/> Aggiungi</button></div>
<div className="overflow-x-auto border border-slate-100 rounded-2xl">
<table className="w-full text-sm text-left">
<thead className="bg-slate-50 text-slate-500 font-bold uppercase text-[10px]"><tr><th className="px-6 py-4">Nome</th><th className="px-6 py-4">Unità</th><th className="px-6 py-4 text-right">Azioni</th></tr></thead>
<tbody className="divide-y divide-slate-50">{families.map(f => (<tr key={f.id} className="hover:bg-slate-50"><td className="px-6 py-4 font-bold text-slate-800">{f.name}</td><td className="px-6 py-4">{f.unitNumber}</td><td className="px-6 py-4 text-right"><div className="flex justify-end gap-2"><button onClick={() => openEditFamilyModal(f)} className="text-blue-600 p-2"><Pencil className="w-4 h-4"/></button><button onClick={() => handleDeleteFamily(f.id)} className="text-red-500 p-2"><Trash2 className="w-4 h-4"/></button></div></td></tr>))}</tbody>
</table>
</div>
</div>
)}
</>
)}
{/* TAB USERS (ADMIN) */}
{activeTab === 'users' && (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex justify-between items-center mb-8"><h3 className="text-xl font-bold text-slate-800">Utenti</h3><button onClick={openAddUserModal} className="bg-blue-600 text-white px-4 py-2 rounded-xl font-bold flex gap-2"><Plus className="w-4 h-4"/> Aggiungi</button></div>
<div className="grid gap-4 md:grid-cols-2">
{users.map(u => (
<div key={u.id} className="p-5 border border-slate-100 rounded-2xl bg-slate-50 flex items-center justify-between">
<div><p className="font-bold text-slate-800">{u.name}</p><p className="text-xs text-slate-400">{u.email}</p><span className="text-[10px] uppercase font-bold bg-white px-2 py-0.5 rounded border border-slate-200 mt-1 inline-block">{u.role}</span></div>
<div className="flex gap-1"><button onClick={() => openEditUserModal(u)} className="p-2 text-blue-600 hover:bg-white rounded-lg"><Pencil className="w-4 h-4"/></button><button onClick={() => handleDeleteUser(u.id)} className="p-2 text-red-500 hover:bg-white rounded-lg"><Trash2 className="w-4 h-4"/></button></div>
</div>
))}
</div>
</div>
)}
{/* TAB NOTICES */}
{activeTab === 'notices' && (
<>
{!activeCondo ? <NoCondoState /> : (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex justify-between items-center mb-8"><h3 className="text-xl font-bold text-slate-800">Bacheca</h3><button onClick={openAddNoticeModal} className="bg-blue-600 text-white px-4 py-2 rounded-xl font-bold flex gap-2"><Plus className="w-4 h-4"/> Pubblica</button></div>
<div className="grid gap-4">{notices.map(n => (<div key={n.id} className="p-5 border rounded-2xl flex justify-between items-center"><div><h4 className="font-bold text-slate-800">{n.title}</h4><p className="text-sm text-slate-500 line-clamp-1">{n.content}</p></div><div className="flex gap-2"><button onClick={() => { setSelectedNoticeId(n.id); setShowReadDetailsModal(true); }} className="p-2 text-slate-500 bg-slate-50 rounded-lg"><Eye className="w-4 h-4"/></button><button onClick={() => openEditNoticeModal(n)} className="p-2 text-blue-600 bg-blue-50 rounded-lg"><Pencil className="w-4 h-4"/></button><button onClick={() => handleDeleteNotice(n.id)} className="p-2 text-red-500 bg-red-50 rounded-lg"><Trash2 className="w-4 h-4"/></button></div></div>))}</div>
</div>
)}
</>
)}
{/* TAB ALERTS */}
{activeTab === 'alerts' && (
<>
{!activeCondo ? <NoCondoState /> : (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex justify-between items-center mb-8"><h3 className="text-xl font-bold text-slate-800">Alerts</h3><div className="flex gap-2"><button onClick={() => setShowSmtpModal(true)} className="bg-slate-100 text-slate-700 px-4 py-2 rounded-xl font-bold">SMTP</button><button onClick={openAddAlertModal} className="bg-blue-600 text-white px-4 py-2 rounded-xl font-bold"><Plus className="w-4 h-4"/></button></div></div>
<div className="grid gap-4">{alerts.map(a => (<div key={a.id} className="p-5 border rounded-2xl flex justify-between"><div><h4 className="font-bold text-slate-800">{a.subject}</h4><p className="text-xs text-slate-500">{a.daysOffset}gg {a.offsetType}</p></div><button onClick={() => handleDeleteAlert(a.id)} className="text-red-500"><Trash2 className="w-4 h-4"/></button></div>))}</div>
</div>
)}
</>
)}
</div>
</div>
{/* --- MODALS --- */}
{showCondoModal && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-lg p-8 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-2xl mb-6 text-slate-800">{editingCondo ? 'Modifica Condominio' : 'Nuovo Condominio'}</h3>
<form onSubmit={handleCondoSubmit} className="space-y-4">
<input className="w-full border p-3 rounded-xl" placeholder="Nome Condominio" value={condoForm.name} onChange={e => setCondoForm({...condoForm, name: e.target.value})} required />
<input className="w-full border p-3 rounded-xl" placeholder="Indirizzo" value={condoForm.address} onChange={e => setCondoForm({...condoForm, address: e.target.value})} />
<div className="flex gap-4 pt-4"><button type="button" onClick={() => setShowCondoModal(false)} className="flex-1 bg-slate-100 py-3 rounded-xl font-bold hover:bg-slate-200">Annulla</button><button type="submit" className="flex-1 bg-blue-600 text-white py-3 rounded-xl font-bold hover:bg-blue-700">Salva</button></div>
</form>
</div>
</div>
)}
{showFamilyModal && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md p-8 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-2xl mb-6 text-slate-800">{editingFamily ? 'Modifica Famiglia' : 'Nuova Famiglia'}</h3>
<form onSubmit={handleFamilySubmit} className="space-y-4">
<input className="w-full border p-3 rounded-xl" placeholder="Nome (es. Fam. Rossi)" value={familyForm.name} onChange={e => setFamilyForm({...familyForm, name: e.target.value})} required />
<input className="w-full border p-3 rounded-xl" placeholder="Interno / Unità" value={familyForm.unitNumber} onChange={e => setFamilyForm({...familyForm, unitNumber: e.target.value})} required />
<input className="w-full border p-3 rounded-xl" placeholder="Email Contatto" type="email" value={familyForm.contactEmail} onChange={e => setFamilyForm({...familyForm, contactEmail: e.target.value})} />
<input className="w-full border p-3 rounded-xl" placeholder="Quota Personalizzata (Opzionale)" type="number" step="0.01" value={familyForm.customMonthlyQuota} onChange={e => setFamilyForm({...familyForm, customMonthlyQuota: e.target.value})} />
<div className="flex gap-4 pt-4"><button type="button" onClick={() => setShowFamilyModal(false)} className="flex-1 bg-slate-100 py-3 rounded-xl font-bold hover:bg-slate-200">Annulla</button><button type="submit" className="flex-1 bg-blue-600 text-white py-3 rounded-xl font-bold hover:bg-blue-700">Salva</button></div>
</form>
</div>
</div>
)}
{showUserModal && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md p-8 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-2xl mb-6 text-slate-800">{editingUser ? 'Modifica Utente' : 'Nuovo Utente'}</h3>
<form onSubmit={handleUserSubmit} className="space-y-4">
<input type="email" className="w-full border p-3 rounded-xl" placeholder="Email" value={userForm.email} onChange={e => setUserForm({...userForm, email: e.target.value})} required />
<input className="w-full border p-3 rounded-xl" placeholder="Nome" value={userForm.name} onChange={e => setUserForm({...userForm, name: e.target.value})} />
<input type="password" className="w-full border p-3 rounded-xl" placeholder="Password (lascia vuoto se invariata)" value={userForm.password} onChange={e => setUserForm({...userForm, password: e.target.value})} />
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Ruolo</label>
<select className="w-full border p-3 rounded-xl bg-white" value={userForm.role} onChange={(e: any) => setUserForm({...userForm, role: e.target.value})}>
<option value="user">Utente Standard</option>
<option value="poweruser">Power User</option>
<option value="admin">Amministratore</option>
</select>
</div>
{userForm.role === 'user' && (
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Associa Famiglia</label>
<select className="w-full border p-3 rounded-xl bg-white" value={userForm.familyId} onChange={(e) => setUserForm({...userForm, familyId: e.target.value})}>
<option value="">Nessuna</option>
{families.map(f => <option key={f.id} value={f.id}>{f.name} ({f.unitNumber})</option>)}
</select>
</div>
)}
<div className="flex gap-4 pt-4"><button type="button" onClick={() => setShowUserModal(false)} className="flex-1 bg-slate-100 py-3 rounded-xl font-bold hover:bg-slate-200">Annulla</button><button type="submit" className="flex-1 bg-blue-600 text-white py-3 rounded-xl font-bold hover:bg-blue-700">Salva</button></div>
</form>
</div>
</div>
)}
{showNoticeModal && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-lg p-8 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-2xl mb-6 text-slate-800">{editingNotice ? 'Modifica Avviso' : 'Nuovo Avviso'}</h3>
<form onSubmit={handleNoticeSubmit} className="space-y-4">
<input className="w-full border p-3 rounded-xl" placeholder="Titolo" value={noticeForm.title} onChange={e => setNoticeForm({...noticeForm, title: e.target.value})} required />
<textarea className="w-full border p-3 rounded-xl h-24" placeholder="Contenuto..." value={noticeForm.content} onChange={e => setNoticeForm({...noticeForm, content: e.target.value})} required />
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Tipo</label>
<select className="w-full border p-3 rounded-xl bg-white" value={noticeForm.type} onChange={(e: any) => setNoticeForm({...noticeForm, type: e.target.value})}>
<option value="info">Info</option>
<option value="warning">Avviso</option>
<option value="maintenance">Lavori</option>
<option value="event">Evento</option>
</select>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Visibilità</label>
<div className="flex gap-2 mt-2">
<button type="button" onClick={() => setNoticeTargetMode('all')} className={`flex-1 py-2 rounded-lg text-xs font-bold border ${noticeTargetMode === 'all' ? 'bg-blue-50 border-blue-200 text-blue-600' : 'bg-white border-slate-200 text-slate-500'}`}>Tutti</button>
<button type="button" onClick={() => setNoticeTargetMode('specific')} className={`flex-1 py-2 rounded-lg text-xs font-bold border ${noticeTargetMode === 'specific' ? 'bg-blue-50 border-blue-200 text-blue-600' : 'bg-white border-slate-200 text-slate-500'}`}>Specifici</button>
</div>
</div>
</div>
{noticeTargetMode === 'specific' && (
<div className="border rounded-xl p-2 max-h-32 overflow-y-auto">
{families.map(f => (
<label key={f.id} className="flex items-center gap-2 p-2 hover:bg-slate-50 rounded cursor-pointer">
<input
type="checkbox"
checked={noticeForm.targetFamilyIds.includes(f.id)}
onChange={e => {
if (e.target.checked) setNoticeForm(prev => ({...prev, targetFamilyIds: [...prev.targetFamilyIds, f.id]}));
else setNoticeForm(prev => ({...prev, targetFamilyIds: prev.targetFamilyIds.filter(id => id !== f.id)}));
}}
className="rounded text-blue-600"
/>
<span className="text-sm text-slate-700">{f.name}</span>
</label>
))}
</div>
)}
<input className="w-full border p-3 rounded-xl" placeholder="Link esterno (es. PDF Drive)" value={noticeForm.link} onChange={e => setNoticeForm({...noticeForm, link: e.target.value})} />
<div className="flex gap-4 pt-4"><button type="button" onClick={() => setShowNoticeModal(false)} className="flex-1 bg-slate-100 py-3 rounded-xl font-bold hover:bg-slate-200">Annulla</button><button type="submit" className="flex-1 bg-blue-600 text-white py-3 rounded-xl font-bold hover:bg-blue-700">Pubblica</button></div>
</form>
</div>
</div>
)}
{showAlertModal && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-lg p-8 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-2xl mb-6 text-slate-800">Regola Avviso Automatico</h3>
<form onSubmit={handleAlertSubmit} className="space-y-4">
<input className="w-full border p-3 rounded-xl" placeholder="Oggetto Email" value={alertForm.subject} onChange={e => setAlertForm({...alertForm, subject: e.target.value})} required />
<textarea className="w-full border p-3 rounded-xl h-24" placeholder="Testo Email..." value={alertForm.body} onChange={e => setAlertForm({...alertForm, body: e.target.value})} required />
<div className="grid grid-cols-2 gap-4">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Giorni Offset</label><input type="number" className="w-full border p-3 rounded-xl" value={alertForm.daysOffset} onChange={e => setAlertForm({...alertForm, daysOffset: parseInt(e.target.value)})} /></div>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Tipo Offset</label><select className="w-full border p-3 rounded-xl bg-white" value={alertForm.offsetType} onChange={(e: any) => setAlertForm({...alertForm, offsetType: e.target.value})}><option value="before_next_month">Giorni prima scadenza</option><option value="after_current_month">Giorni dopo scadenza</option></select></div>
</div>
<div className="flex gap-4 pt-4"><button type="button" onClick={() => setShowAlertModal(false)} className="flex-1 bg-slate-100 py-3 rounded-xl font-bold hover:bg-slate-200">Annulla</button><button type="submit" className="flex-1 bg-blue-600 text-white py-3 rounded-xl font-bold hover:bg-blue-700">Salva</button></div>
</form>
</div>
</div>
)}
{showSmtpModal && globalSettings?.smtpConfig && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md p-8 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-2xl mb-6 text-slate-800">Configurazione SMTP</h3>
<form onSubmit={async (e) => {
e.preventDefault();
// Update settings directly via API
const updated = { ...globalSettings, smtpConfig: globalSettings.smtpConfig };
await CondoService.updateSettings(updated);
setGlobalSettings(updated);
setShowSmtpModal(false);
}} className="space-y-4">
<input className="w-full border p-3 rounded-xl" placeholder="Host (smtp.gmail.com)" value={globalSettings.smtpConfig.host} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, host: e.target.value}})} required />
<div className="grid grid-cols-2 gap-4">
<input type="number" className="w-full border p-3 rounded-xl" placeholder="Porta (465)" value={globalSettings.smtpConfig.port} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, port: parseInt(e.target.value)}})} required />
<label className="flex items-center gap-2 border p-3 rounded-xl bg-slate-50 cursor-pointer"><input type="checkbox" checked={globalSettings.smtpConfig.secure} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, secure: e.target.checked}})} /> <span className="text-sm font-bold text-slate-600">SSL/TLS</span></label>
</div>
<input className="w-full border p-3 rounded-xl" placeholder="Utente/Email" value={globalSettings.smtpConfig.user} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, user: e.target.value}})} required />
<input className="w-full border p-3 rounded-xl" type="password" placeholder="Password" value={globalSettings.smtpConfig.pass} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, pass: e.target.value}})} required />
<input className="w-full border p-3 rounded-xl" placeholder="From Email (Opzionale)" value={globalSettings.smtpConfig.fromEmail || ''} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, fromEmail: e.target.value}})} />
{testSmtpMsg && <div className={`p-3 rounded-xl text-sm font-bold text-center ${testSmtpMsg.includes('Errore') ? 'bg-red-50 text-red-600' : 'bg-green-50 text-green-600'}`}>{testSmtpMsg}</div>}
<div className="flex gap-2 pt-2">
<button type="button" onClick={handleTestSmtp} disabled={testingSmtp} className="flex-1 bg-slate-100 text-slate-600 py-3 rounded-xl font-bold hover:bg-slate-200">{testingSmtp ? '...' : 'Test'}</button>
<button type="submit" className="flex-1 bg-blue-600 text-white py-3 rounded-xl font-bold hover:bg-blue-700">Salva Config</button>
</div>
</form>
<button onClick={() => setShowSmtpModal(false)} className="absolute top-4 right-4 text-slate-400 hover:text-slate-600"><X className="w-6 h-6"/></button>
</div>
</div>
)}
{showReadDetailsModal && selectedNoticeId && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md p-6 animate-in fade-in zoom-in duration-200 max-h-[80vh] flex flex-col">
<div className="flex justify-between items-center mb-4">
<h3 className="font-bold text-lg text-slate-800">Stato Lettura</h3>
<button onClick={() => setShowReadDetailsModal(false)}><X className="w-5 h-5 text-slate-400"/></button>
</div>
<div className="flex-1 overflow-y-auto">
{users.length === 0 ? <p className="text-center text-slate-400">Nessun utente.</p> : (
<div className="space-y-2">
{users.map(u => {
const readInfo = noticeReadStats[selectedNoticeId]?.find(r => r.userId === u.id);
return (
<div key={u.id} className="flex items-center justify-between p-3 border rounded-xl bg-slate-50">
<div>
<p className="font-bold text-sm text-slate-700">{u.name}</p>
<p className="text-xs text-slate-400">{u.email}</p>
</div>
{readInfo ? (
<div className="text-right">
<span className="text-xs font-bold text-green-600 bg-green-100 px-2 py-1 rounded-full flex items-center gap-1 justify-end"><CheckCircle2 className="w-3 h-3"/> Letto</span>
<p className="text-[10px] text-slate-400 mt-1">{new Date(readInfo.readAt).toLocaleString()}</p>
</div>
) : (
<span className="text-xs font-bold text-slate-400 bg-white border px-2 py-1 rounded-full">Non letto</span>
)}
</div>
);
})}
</div>
)}
</div>
</div>
</div>
)}
</div>
);
};