Files
Condopay/pages/Settings.tsx
2025-12-11 22:03:03 +01:00

1136 lines
77 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { CondoService } from '../services/mockDb';
import { AppSettings, Family, User, AlertDefinition, Condo, Notice, NoticeIconType, NoticeRead } from '../types';
import {
Save, Building, Coins, Plus, Pencil, Trash2, X, CalendarCheck,
AlertTriangle, User as UserIcon, Server, Bell, Clock, FileText,
Lock, Megaphone, CheckCircle2, Info, Hammer, Link as LinkIcon,
Eye, Calendar, List, UserCog, Mail, Power, MapPin, CreditCard,
ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send
} from 'lucide-react';
export const SettingsPage: React.FC = () => {
const currentUser = CondoService.getCurrentUser();
const isSuperAdmin = currentUser?.role === 'admin';
const isPrivileged = currentUser?.role === 'admin' || currentUser?.role === 'poweruser';
// Tab configuration
type TabType = 'profile' | 'features' | 'general' | 'condos' | 'families' | 'users' | 'notices' | 'alerts';
const [activeTab, setActiveTab] = useState<TabType>(isPrivileged ? 'general' : 'profile');
const [loading, setLoading] = useState(true);
// Profile State
const [profileForm, setProfileForm] = useState({
name: currentUser?.name || '',
phone: currentUser?.phone || '',
password: '',
receiveAlerts: currentUser?.receiveAlerts ?? true
});
const [profileSaving, setProfileSaving] = useState(false);
const [profileMsg, setProfileMsg] = useState('');
// General Settings State
const [activeCondo, setActiveCondo] = useState<Condo | undefined>(undefined);
const [globalSettings, setGlobalSettings] = useState<AppSettings | null>(null);
// Condos Management State
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
});
const [saving, setSaving] = useState(false);
const [successMsg, setSuccessMsg] = useState('');
// Families State
const [families, setFamilies] = useState<Family[]>([]);
const [showFamilyModal, setShowFamilyModal] = useState(false);
const [editingFamily, setEditingFamily] = useState<Family | null>(null);
const [familyForm, setFamilyForm] = useState<{
name: string;
unitNumber: string;
stair: string;
floor: string;
notes: string;
contactEmail: string;
customMonthlyQuota: string;
}>({ name: '', unitNumber: '', stair: '', floor: '', notes: '', contactEmail: '', customMonthlyQuota: '' });
// Users State
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
});
// Alerts State
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
});
// SMTP Modal State
const [showSmtpModal, setShowSmtpModal] = useState(false);
const [testingSmtp, setTestingSmtp] = useState(false);
const [testSmtpMsg, setTestSmtpMsg] = useState('');
// Notices (Bacheca) State
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: NoticeIconType;
link: string;
condoId: string;
active: boolean;
targetFamilyIds: string[];
}>({
title: '',
content: '',
type: 'info',
link: '',
condoId: '',
active: true,
targetFamilyIds: []
});
const [noticeReadStats, setNoticeReadStats] = useState<Record<string, NoticeRead[]>>({});
// Notice Details Modal
const [showReadDetailsModal, setShowReadDetailsModal] = useState(false);
const [selectedNoticeId, setSelectedNoticeId] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
if (isPrivileged) {
// First fetch global/structural data
const condoList = await CondoService.getCondos();
const activeC = await CondoService.getActiveCondo();
const gSettings = await CondoService.getSettings();
setCondos(condoList);
setActiveCondo(activeC);
setGlobalSettings(gSettings);
// Fetch condo-specific data ONLY if there is an active condo
if (activeC) {
const [fams, usrs, alrts, allNotices] = await Promise.all([
CondoService.getFamilies(activeC.id),
CondoService.getUsers(activeC.id),
CondoService.getAlerts(activeC.id),
CondoService.getNotices(activeC.id)
]);
setFamilies(fams);
setUsers(usrs);
setAlerts(alrts);
setNotices(allNotices);
// Fetch read stats for notices
const stats: Record<string, NoticeRead[]> = {};
for (const n of allNotices) {
const reads = await CondoService.getNoticeReadStatus(n.id);
stats[n.id] = reads;
}
setNoticeReadStats(stats);
} else {
setFamilies([]);
setUsers([]);
setAlerts([]);
setNotices([]);
}
} else {
const activeC = await CondoService.getActiveCondo();
setActiveCondo(activeC);
}
} catch(e) {
console.error(e);
} finally {
setLoading(false);
}
};
fetchData();
}, [isPrivileged]);
// --- HANDLERS ---
const handleProfileSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setProfileSaving(true);
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) => {
e.preventDefault();
if (!activeCondo) return;
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) => {
e.preventDefault();
if (!globalSettings) return;
setSaving(true);
try {
await CondoService.updateSettings(globalSettings);
setSuccessMsg('Funzionalità salvate!');
setTimeout(() => setSuccessMsg(''), 3000);
window.location.reload();
} catch(e) {
console.error(e);
} finally {
setSaving(false);
}
};
const handleSmtpSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!globalSettings) return;
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 () => {
if (!globalSettings?.smtpConfig) return;
setTestingSmtp(true);
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 handleNewYear = async () => {
if (!globalSettings) return;
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);
}
}
};
// CRUD Handlers
const openAddCondoModal = () => {
setEditingCondo(null);
setCondoForm({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100 });
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 });
setShowCondoModal(true);
};
const handleCondoSubmit = async (e: React.FormEvent) => {
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
};
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.");
}
};
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 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']) => {
if (!globalSettings) return;
setGlobalSettings({
...globalSettings,
features: {
...globalSettings.features,
[key]: !globalSettings.features[key]
}
});
};
const tabs: {id: TabType, label: string, icon: React.ReactNode}[] = [
{ id: 'profile', label: 'Profilo', icon: <UserIcon className="w-4 h-4"/> },
];
if (isSuperAdmin) {
tabs.push({ id: 'features', label: 'Funzionalità', icon: <LayoutGrid className="w-4 h-4"/> });
}
if (isPrivileged) {
tabs.push(
{ id: 'general', label: 'Condominio', icon: <Building 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: '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 Email', icon: <Bell className="w-4 h-4"/> }
);
}
if (loading) return <div className="p-8 text-center text-slate-400">Caricamento...</div>;
return (
<div className="max-w-5xl mx-auto space-y-6 pb-20">
{/* HEADER */}
<div>
<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>
</div>
{/* TABS NAVIGATION */}
<div className="flex border-b border-slate-200 overflow-x-auto no-scrollbar pb-1 gap-1">
{tabs.map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
px-4 py-3 font-medium text-sm whitespace-nowrap flex items-center gap-2 rounded-t-lg transition-colors
${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'}
`}
>
{tab.icon}{tab.label}
</button>
))}
</div>
{/* PROFILE TAB */}
{activeTab === 'profile' && (
<div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6 max-w-2xl">
<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">
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div>
<label className="text-sm font-medium text-slate-700">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"/>
</div>
<div>
<label className="text-sm font-medium text-slate-700">Email</label>
<input type="email" value={currentUser?.email || ''} disabled className="w-full border bg-slate-50 p-2.5 rounded-lg text-slate-500"/>
</div>
<div>
<label className="text-sm font-medium text-slate-700">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"/>
</div>
<div>
<label className="text-sm font-medium text-slate-700">Password</label>
<input type="password" placeholder="Opzionale" value={profileForm.password} onChange={(e) => setProfileForm({...profileForm, password: e.target.value})} className="w-full border p-2.5 rounded-lg text-slate-700"/>
</div>
</div>
<button type="submit" disabled={profileSaving} className="bg-blue-600 text-white px-6 py-2.5 rounded-lg font-medium hover:bg-blue-700 flex gap-2"><Save className="w-4 h-4" /> Aggiorna</button>
{profileMsg && <p className={`text-sm ${profileMsg.includes('Errore') ? 'text-red-500' : 'text-green-600'}`}>{profileMsg}</p>}
</form>
</div>
)}
{/* FEATURES TAB (ADMIN ONLY) */}
{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="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>
</div>
<form onSubmit={handleFeaturesSubmit} className="space-y-6">
<div className="space-y-4">
{/* Toggles */}
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
<div><p className="font-bold text-slate-800">Gestione Multicondominio</p><p className="text-sm text-slate-500">Abilita la gestione di più stabili.</p></div>
<button type="button" onClick={() => toggleFeature('multiCondo')} className={`${globalSettings.features.multiCondo ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.multiCondo ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
</div>
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
<div><p className="font-bold text-slate-800">Gestione Tickets</p><p className="text-sm text-slate-500">Abilita il sistema di segnalazione guasti.</p></div>
<button type="button" onClick={() => toggleFeature('tickets')} className={`${globalSettings.features.tickets ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.tickets ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
</div>
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
<div><p className="font-bold text-slate-800">Pagamenti PayPal</p><p className="text-sm text-slate-500">Permetti ai condomini di pagare online.</p></div>
<button type="button" onClick={() => toggleFeature('payPal')} className={`${globalSettings.features.payPal ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.payPal ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
</div>
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
<div><p className="font-bold text-slate-800">Bacheca Avvisi</p><p className="text-sm text-slate-500">Mostra la bacheca digitale.</p></div>
<button type="button" onClick={() => toggleFeature('notices')} className={`${globalSettings.features.notices ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.notices ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
</div>
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
<div><p className="font-bold text-slate-800">Reportistica Avanzata</p><p className="text-sm text-slate-500">Abilita grafici incassi e bilancio.</p></div>
<button type="button" onClick={() => toggleFeature('reports')} className={`${globalSettings.features.reports ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.reports ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
</div>
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
<div><p className="font-bold text-slate-800">Spese Straordinarie</p><p className="text-sm text-slate-500">Gestione lavori e preventivi.</p></div>
<button type="button" onClick={() => toggleFeature('extraordinaryExpenses')} className={`${globalSettings.features.extraordinaryExpenses !== false ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.extraordinaryExpenses !== false ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
</div>
{/* NEW TOGGLE for Condo Financials View */}
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
<div>
<p className="font-bold text-slate-800">Visualizza Spese Condominiali agli Utenti</p>
<p className="text-sm text-slate-500">Se attivo, gli utenti potranno vedere (sola lettura) il registro delle spese condominiali.</p>
</div>
<button type="button" onClick={() => toggleFeature('condoFinancialsView')} className={`${globalSettings.features.condoFinancialsView ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}>
<span className={`${globalSettings.features.condoFinancialsView ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/>
</button>
</div>
</div>
<div className="pt-2 flex justify-between items-center"><span className="text-green-600 font-medium">{successMsg}</span><button type="submit" className="bg-blue-600 text-white px-6 py-2.5 rounded-lg font-medium hover:bg-blue-700 flex gap-2"><Save className="w-4 h-4" /> Salva Configurazione</button></div>
</form>
</div>
)}
{/* GENERAL TAB */}
{isPrivileged && activeTab === 'general' && (
<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> : (
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 md:p-8 max-w-2xl">
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Building className="w-5 h-5 text-blue-600" /> Dati Condominio Corrente</h3>
<form onSubmit={handleGeneralSubmit} className="space-y-5">
<input type="text" value={activeCondo.name} onChange={(e) => setActiveCondo({ ...activeCondo, name: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome" required />
<div><label className="block text-xs font-bold text-slate-500 uppercase mb-1">Indirizzo</label><input type="text" value={activeCondo.address || ''} onChange={(e) => setActiveCondo({ ...activeCondo, address: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Via/Piazza..." required/></div>
<div className="grid grid-cols-2 gap-4"><div><label className="block text-xs font-bold text-slate-500 uppercase mb-1">Civico</label><input type="text" value={activeCondo.streetNumber || ''} onChange={(e) => setActiveCondo({ ...activeCondo, streetNumber: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" required/></div><div><label className="block text-xs font-bold text-slate-500 uppercase mb-1">CAP</label><input type="text" value={activeCondo.zipCode || ''} onChange={(e) => setActiveCondo({ ...activeCondo, zipCode: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700"/></div></div>
<div className="grid grid-cols-2 gap-4"><div><label className="block text-xs font-bold text-slate-500 uppercase mb-1">Città</label><input type="text" value={activeCondo.city || ''} onChange={(e) => setActiveCondo({ ...activeCondo, city: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" required/></div><div><label className="block text-xs font-bold text-slate-500 uppercase mb-1">Provincia</label><input type="text" value={activeCondo.province || ''} onChange={(e) => setActiveCondo({ ...activeCondo, province: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700" required/></div></div>
<div><label className="block text-xs font-bold text-slate-500 uppercase mb-1">Note (Opzionali)</label><textarea value={activeCondo.notes || ''} onChange={(e) => setActiveCondo({ ...activeCondo, notes: e.target.value })} className="w-full border p-2.5 rounded-lg text-slate-700 h-24"></textarea></div>
{globalSettings?.features.payPal && (<div className="bg-blue-50 p-4 rounded-lg border border-blue-100"><div className="flex items-center gap-2 mb-2 text-blue-800"><CreditCard className="w-4 h-4"/><span className="text-xs font-bold uppercase">Configurazione Pagamenti</span></div><div><label className="text-xs font-semibold text-slate-600 block mb-1">PayPal Client ID</label><input className="w-full border p-2 rounded text-slate-700 text-sm" placeholder="Es: Afg..." value={activeCondo.paypalClientId || ''} onChange={e => setActiveCondo({...activeCondo, paypalClientId: e.target.value})} /><p className="text-[10px] text-slate-500 mt-1">Necessario per abilitare i pagamenti online delle rate.</p></div></div>)}
<div><label className="block text-sm font-medium text-slate-700 mb-1">Quota Mensile Standard ()</label><input type="number" value={activeCondo.defaultMonthlyQuota} onChange={(e) => setActiveCondo({ ...activeCondo, defaultMonthlyQuota: parseFloat(e.target.value) })} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Quota Default" required /></div>
<div className="pt-2 flex justify-between"><span className="text-green-600">{successMsg}</span><button type="submit" className="bg-blue-600 text-white px-6 py-2.5 rounded-lg hover:bg-blue-700 flex gap-2"><Save className="w-4 h-4"/> Salva</button></div>
</form>
</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>)}
</div>
)}
{/* CONDOS LIST TAB */}
{isPrivileged && activeTab === 'condos' && (
<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="grid grid-cols-1 md:grid-cols-2 gap-4">
{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'}`}>
{activeCondo?.id === condo.id && <div className="absolute top-3 right-3 text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded font-bold uppercase">Attivo</div>}
<h4 className="font-bold text-slate-800 text-lg mb-1">{condo.name}</h4>
<div className="text-sm text-slate-500 mb-3 space-y-1"><p className="flex items-center gap-1"><MapPin className="w-3 h-3"/> {condo.address} {condo.streetNumber}</p>{condo.city && <p className="pl-4">{condo.zipCode} {condo.city} ({condo.province})</p>}</div>
<div className="border-t pt-3 flex gap-2"><button onClick={() => openEditCondoModal(condo)} className="flex-1 py-1.5 bg-slate-50 text-slate-700 rounded text-sm font-medium hover:bg-slate-100">Modifica</button><button onClick={() => handleDeleteCondo(condo.id)} className="flex-1 py-1.5 bg-red-50 text-red-600 rounded text-sm font-medium hover:bg-red-100">Elimina</button></div>
</div>
))}
</div>
</div>
)}
{/* FAMILIES TAB */}
{isPrivileged && activeTab === 'families' && (
<div className="space-y-4 animate-fade-in">
{!activeCondo ? (<div className="p-8 text-center bg-slate-50 rounded-xl border border-dashed border-slate-300"><p className="text-slate-500">Seleziona o crea un condominio per gestire le famiglie.</p></div>) : (
<>
<div className="flex justify-between items-center"><div className="text-sm text-slate-500">Famiglie in: <span className="font-bold text-slate-800">{activeCondo.name}</span></div><button onClick={openAddFamilyModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2"><Plus className="w-4 h-4" /> Aggiungi</button></div>
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<table className="w-full text-left text-sm text-slate-600"><thead className="bg-slate-50 text-slate-700 font-semibold border-b"><tr><th className="px-6 py-4">Nome</th><th className="px-6 py-4">Dettagli</th><th className="px-6 py-4">Email</th><th className="px-6 py-4">Quota</th><th className="px-6 py-4 text-right">Azioni</th></tr></thead>
<tbody className="divide-y divide-slate-100">
{families.map(family => (
<tr key={family.id} className="hover:bg-slate-50">
<td className="px-6 py-4 font-medium text-slate-900">{family.name}</td>
<td className="px-6 py-4 text-xs"><div className="space-y-0.5"><span className="block text-slate-700 font-medium">Int: {family.unitNumber || '-'}</span>{(family.stair || family.floor) && (<span className="block text-slate-500">{family.stair ? `Scala: ${family.stair} ` : ''} {family.floor ? `Piano: ${family.floor}` : ''}</span>)}</div></td>
<td className="px-6 py-4 text-slate-400">{family.contactEmail}</td>
<td className="px-6 py-4">{family.customMonthlyQuota ? (<span className="font-bold text-blue-600"> {family.customMonthlyQuota}</span>) : (<span className="text-slate-400 italic">Default ( {activeCondo.defaultMonthlyQuota})</span>)}</td>
<td className="px-6 py-4 text-right"><div className="flex justify-end gap-2"><button onClick={() => openEditFamilyModal(family)} className="text-blue-600"><Pencil className="w-4 h-4" /></button><button onClick={() => handleDeleteFamily(family.id)} className="text-red-600"><Trash2 className="w-4 h-4" /></button></div></td>
</tr>
))}
</tbody>
</table>
</div>
</>
)}
</div>
)}
{/* USERS TAB */}
{isPrivileged && activeTab === 'users' && (
<div className="space-y-4 animate-fade-in">
<div className="flex justify-end"><button onClick={openAddUserModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2 hover:bg-blue-700 transition-colors" type="button"><Plus className="w-4 h-4" /> Nuovo Utente</button></div>
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<table className="w-full text-left text-sm text-slate-600"><thead className="bg-slate-50 text-slate-700 font-semibold border-b"><tr><th className="px-6 py-4">Utente</th><th className="px-6 py-4">Ruolo</th><th className="px-6 py-4 text-right">Azioni</th></tr></thead>
<tbody className="divide-y divide-slate-100">
{users.map(u => (<tr key={u.id} className="hover:bg-slate-50"><td className="px-6 py-4"><div className="font-medium text-slate-900">{u.name}</div><div className="text-xs text-slate-400">{u.email}</div></td><td className="px-6 py-4"><span className="bg-slate-100 px-2 py-1 rounded text-xs font-bold uppercase">{u.role}</span></td><td className="px-6 py-4 text-right"><div className="flex justify-end gap-2"><button onClick={() => openEditUserModal(u)} className="text-blue-600"><Pencil className="w-4 h-4"/></button><button onClick={() => handleDeleteUser(u.id)} className="text-red-600"><Trash2 className="w-4 h-4"/></button></div></td></tr>))}
</tbody>
</table>
</div>
</div>
)}
{/* NOTICES TAB */}
{isPrivileged && activeTab === 'notices' && (
<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">Bacheca Condominiale</h3><p className="text-sm text-blue-600">Pubblica avvisi visibili a tutti i condomini.</p></div><button onClick={openAddNoticeModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2 hover:bg-blue-700 transition-colors"><Plus className="w-4 h-4" /> Nuovo Avviso</button></div>
<div className="grid gap-4">
{notices.map(notice => {
const isTargeted = notice.targetFamilyIds && notice.targetFamilyIds.length > 0;
return (
<div key={notice.id} className={`bg-white p-5 rounded-xl border shadow-sm relative transition-all ${notice.active ? 'border-slate-200' : 'border-slate-100 opacity-60 bg-slate-50'}`}>
<div className="flex items-start justify-between">
<div className="flex items-start gap-3">
<div className={`p-2 rounded-lg ${notice.type === 'warning' ? 'bg-amber-100 text-amber-600' : notice.type === 'maintenance' ? 'bg-orange-100 text-orange-600' : 'bg-blue-100 text-blue-600'}`}>{notice.type === 'warning' ? <AlertTriangle className="w-5 h-5"/> : notice.type === 'maintenance' ? <Hammer className="w-5 h-5"/> : notice.type === 'event' ? <Calendar className="w-5 h-5"/> : <Info className="w-5 h-5"/>}</div>
<div><h4 className="font-bold text-slate-800 flex items-center gap-2">{notice.title}{isTargeted && (<span className="text-[10px] bg-slate-100 text-slate-500 border border-slate-200 px-2 py-0.5 rounded-full uppercase flex items-center gap-1"><Users className="w-3 h-3" /> Privato</span>)}</h4><p className="text-xs text-slate-400 font-medium uppercase tracking-wide mb-1">{getCondoName(notice.condoId)} {new Date(notice.date).toLocaleDateString()}</p><p className="text-sm text-slate-600 line-clamp-2">{notice.content}</p>{notice.link && <a href={notice.link} className="text-xs text-blue-600 underline mt-1 flex items-center gap-1"><LinkIcon className="w-3 h-3"/> Allegato</a>}{isTargeted && (<p className="text-[10px] text-slate-400 mt-2">Visibile a: {notice.targetFamilyIds!.length} famiglie</p>)}</div>
</div>
<div className="flex flex-col items-end gap-3"><button onClick={() => toggleNoticeActive(notice)} className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${notice.active ? 'bg-green-500' : 'bg-slate-300'}`} title={notice.active ? "Attivo" : "Disattivato"}><span className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${notice.active ? 'translate-x-6' : 'translate-x-1'}`} /></button><button onClick={() => openReadDetails(notice.id)} className="text-center group" title="Vedi dettaglio letture"><span className="block text-lg font-bold text-slate-700 group-hover:text-blue-600">{noticeReadStats[notice.id]?.length || 0}</span><span className="text-[10px] text-slate-400 uppercase font-bold flex items-center gap-1 group-hover:text-blue-500"><Eye className="w-3 h-3"/> Letture</span></button></div>
</div>
<div className="mt-4 pt-3 border-t border-slate-100 flex justify-end gap-2"><button onClick={() => openEditNoticeModal(notice)} className="text-sm text-blue-600 font-medium px-3 py-1 hover:bg-blue-50 rounded">Modifica</button><button onClick={() => handleDeleteNotice(notice.id)} className="text-sm text-red-600 font-medium px-3 py-1 hover:bg-red-50 rounded">Elimina</button></div>
</div>
);
})}
{notices.length === 0 && <div className="text-center p-8 text-slate-400">Nessun avviso pubblicato.</div>}
</div>
</div>
)}
{/* ALERTS TAB */}
{isPrivileged && activeTab === 'alerts' && (
<div className="space-y-6 animate-fade-in">
<div className="flex justify-end"><button onClick={() => setShowSmtpModal(true)} className="flex items-center gap-2 bg-slate-100 hover:bg-slate-200 text-slate-700 px-4 py-2 rounded-lg font-medium border border-slate-200 transition-colors"><Mail className="w-4 h-4" />Impostazioni SMTP</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">Avvisi Automatici</h3><p className="text-sm text-blue-600">Configura email automatiche per scadenze.</p></div><button onClick={openAddAlertModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2"><Plus className="w-4 h-4" /> Nuovo Avviso</button></div>
<div className="grid gap-4">
{alerts.map(alert => (
<div key={alert.id} className="bg-white p-5 rounded-xl border border-slate-200 shadow-sm relative">
<h4 className="font-bold text-slate-800">{alert.subject}</h4><p className="text-sm text-slate-600 mt-1">{alert.body}</p>
<div className="mt-3 flex gap-2 text-xs font-bold text-slate-500 uppercase"><span className="bg-slate-100 px-2 py-1 rounded">Offset: {alert.daysOffset} giorni ({alert.offsetType})</span><span className="bg-slate-100 px-2 py-1 rounded">Ore: {alert.sendHour}:00</span><span className={`px-2 py-1 rounded ${alert.active ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>{alert.active ? 'Attivo' : 'Inattivo'}</span></div>
<div className="mt-4 pt-3 border-t border-slate-100 flex justify-end gap-2"><button onClick={() => openEditAlertModal(alert)} className="text-blue-600 text-sm font-medium">Modifica</button><button onClick={() => handleDeleteAlert(alert.id)} className="text-red-600 text-sm font-medium">Elimina</button></div>
</div>
))}
{alerts.length === 0 && <div className="text-center p-8 text-slate-400">Nessun alert configurato.</div>}
</div>
</div>
)}
{/* --- MODALS --- */}
{/* Notice Modal */}
{showNoticeModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200 flex flex-col max-h-[90vh]">
<h3 className="font-bold text-lg mb-4 text-slate-800 flex-shrink-0">{editingNotice ? 'Modifica Avviso' : 'Nuovo Avviso'}</h3>
<div className="overflow-y-auto flex-1 pr-2">
<form id="noticeForm" onSubmit={handleNoticeSubmit} className="space-y-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Titolo" value={noticeForm.title} onChange={e => setNoticeForm({...noticeForm, title: e.target.value})} required />
<textarea className="w-full border p-2.5 rounded-lg text-slate-700 h-24" placeholder="Contenuto avviso..." value={noticeForm.content} onChange={e => setNoticeForm({...noticeForm, content: e.target.value})} required />
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-slate-500 font-bold uppercase mb-1 block">Tipo</label>
<select className="w-full border p-2.5 rounded-lg text-slate-700 bg-white" value={noticeForm.type} onChange={e => setNoticeForm({...noticeForm, type: e.target.value as any})}>
<option value="info">Info</option>
<option value="warning">Avviso</option>
<option value="maintenance">Manutenzione</option>
<option value="event">Evento</option>
</select>
</div>
<div>
<label className="text-xs text-slate-500 font-bold uppercase mb-1 block">Stato</label>
<select className="w-full border p-2.5 rounded-lg text-slate-700 bg-white" value={noticeForm.active ? 'true' : 'false'} onChange={e => setNoticeForm({...noticeForm, active: e.target.value === 'true'})}>
<option value="true">Attivo</option>
<option value="false">Bozza / Nascosto</option>
</select>
</div>
</div>
<div>
<label className="text-xs text-slate-500 font-bold uppercase mb-2 block">A chi è rivolto?</label>
<div className="flex gap-4 mb-3">
<label className="flex items-center gap-2 text-sm text-slate-700 cursor-pointer">
<input type="radio" name="targetMode" value="all" checked={noticeTargetMode === 'all'} onChange={() => setNoticeTargetMode('all')} className="w-4 h-4 text-blue-600"/>
Tutti i condomini
</label>
<label className="flex items-center gap-2 text-sm text-slate-700 cursor-pointer">
<input type="radio" name="targetMode" value="specific" checked={noticeTargetMode === 'specific'} onChange={() => setNoticeTargetMode('specific')} className="w-4 h-4 text-blue-600"/>
Seleziona Famiglie
</label>
</div>
{noticeTargetMode === 'specific' && (
<div className="border border-slate-200 rounded-lg p-2 max-h-40 overflow-y-auto bg-slate-50">
{families.length === 0 ? (
<p className="text-xs text-slate-400 text-center py-2">Nessuna famiglia disponibile.</p>
) : (
<div className="space-y-1">
{families.map(fam => (
<label key={fam.id} className="flex items-center gap-2 p-1.5 hover:bg-white rounded cursor-pointer transition-colors">
<input type="checkbox" checked={noticeForm.targetFamilyIds.includes(fam.id)} onChange={() => toggleNoticeFamilyTarget(fam.id)} className="w-4 h-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500"/>
<span className="text-sm text-slate-700">{fam.name} <span className="text-slate-400 text-xs">(Int. {fam.unitNumber})</span></span>
</label>
))}
</div>
)}
</div>
)}
</div>
<div>
<label className="text-xs text-slate-500 font-bold uppercase mb-1 block">Link Esterno (Opzionale)</label>
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="https://..." value={noticeForm.link} onChange={e => setNoticeForm({...noticeForm, link: e.target.value})} />
</div>
</form>
</div>
<div className="flex gap-2 pt-4 border-t border-slate-100 flex-shrink-0">
<button type="button" onClick={() => setShowNoticeModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600 hover:bg-slate-50">Annulla</button>
<button type="submit" form="noticeForm" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg hover:bg-blue-700">Salva</button>
</div>
</div>
</div>
)}
{/* Notice Read Details Modal */}
{showReadDetailsModal && selectedNoticeId && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-md p-6 animate-in fade-in zoom-in duration-200">
<div className="flex justify-between items-center mb-4">
<h3 className="font-bold text-lg text-slate-800">Dettaglio Letture</h3>
<button onClick={() => setShowReadDetailsModal(false)} className="p-1 rounded hover:bg-slate-100 text-slate-500"><X className="w-5 h-5"/></button>
</div>
<div className="max-h-64 overflow-y-auto pr-2">
{noticeReadStats[selectedNoticeId] && noticeReadStats[selectedNoticeId].length > 0 ? (
<div className="space-y-3">
{noticeReadStats[selectedNoticeId].map((read, idx) => {
const user = users.find(u => u.id === read.userId);
return (
<div key={idx} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg border border-slate-100">
<div className="flex items-center gap-3">
<div className="bg-blue-100 p-2 rounded-full"><UserIcon className="w-4 h-4 text-blue-600"/></div>
<div>
<p className="text-sm font-bold text-slate-800">{user?.name || 'Utente Sconosciuto'}</p>
<p className="text-xs text-slate-500">{user?.email}</p>
</div>
</div>
<div className="text-right">
<p className="text-xs text-slate-500">{new Date(read.readAt).toLocaleDateString()}</p>
<p className="text-xs font-mono text-slate-400">{new Date(read.readAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</p>
</div>
</div>
);
})}
</div>
) : (
<p className="text-center text-slate-400 py-8">Nessuna lettura registrata.</p>
)}
</div>
</div>
</div>
)}
{/* User Modal */}
{showUserModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-md p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingUser ? 'Modifica Utente' : 'Nuovo Utente'}</h3>
<form onSubmit={handleUserSubmit} className="space-y-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome Completo" value={userForm.name} onChange={e => setUserForm({...userForm, name: e.target.value})} required />
<input className="w-full border p-2.5 rounded-lg text-slate-700" type="email" placeholder="Email" value={userForm.email} onChange={e => setUserForm({...userForm, email: e.target.value})} required />
<input className="w-full border p-2.5 rounded-lg text-slate-700" type="tel" placeholder="Telefono" value={userForm.phone} onChange={e => setUserForm({...userForm, phone: e.target.value})} />
<input className="w-full border p-2.5 rounded-lg text-slate-700" type="password" placeholder={editingUser ? "Nuova Password (opzionale)" : "Password"} value={userForm.password} onChange={e => setUserForm({...userForm, password: e.target.value})} required={!editingUser} />
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-slate-500 font-bold uppercase mb-1 block">Ruolo</label>
<select className="w-full border p-2.5 rounded-lg text-slate-700 bg-white" value={userForm.role} onChange={e => setUserForm({...userForm, role: e.target.value as any})}>
<option value="user">Utente</option>
<option value="admin">Admin</option>
<option value="poweruser">Power User</option>
</select>
</div>
<div>
<label className="text-xs text-slate-500 font-bold uppercase mb-1 block">Famiglia</label>
<select className="w-full border p-2.5 rounded-lg text-slate-700 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}</option>)}
</select>
</div>
</div>
<div className="flex items-center gap-2">
<input type="checkbox" checked={userForm.receiveAlerts} onChange={e => setUserForm({...userForm, receiveAlerts: e.target.checked})} className="w-4 h-4 text-blue-600"/>
<span className="text-sm font-medium text-slate-700">Ricevi avvisi email</span>
</div>
<div className="flex gap-2 pt-2">
<button type="button" onClick={() => setShowUserModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600 hover:bg-slate-50">Annulla</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg hover:bg-blue-700">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* Condo Modal */}
{showCondoModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200 overflow-y-auto max-h-[90vh]">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingCondo ? 'Modifica Condominio' : 'Nuovo Condominio'}</h3>
<form onSubmit={handleCondoSubmit} className="space-y-4">
<div>
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome Condominio" value={condoForm.name} onChange={e => setCondoForm({...condoForm, name: e.target.value})} required />
</div>
<div className="grid grid-cols-12 gap-3">
<div className="col-span-9">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Via/Piazza..." value={condoForm.address} onChange={e => setCondoForm({...condoForm, address: e.target.value})} required />
</div>
<div className="col-span-3">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Civico" value={condoForm.streetNumber} onChange={e => setCondoForm({...condoForm, streetNumber: e.target.value})} required />
</div>
</div>
<div className="grid grid-cols-12 gap-3">
<div className="col-span-3">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="CAP" value={condoForm.zipCode} onChange={e => setCondoForm({...condoForm, zipCode: e.target.value})} />
</div>
<div className="col-span-5">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Città" value={condoForm.city} onChange={e => setCondoForm({...condoForm, city: e.target.value})} required />
</div>
<div className="col-span-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Provincia" value={condoForm.province} onChange={e => setCondoForm({...condoForm, province: e.target.value})} required />
</div>
</div>
{globalSettings?.features.payPal && (
<div className="bg-blue-50 p-3 rounded-lg border border-blue-100">
<div className="flex items-center gap-2 mb-2 text-blue-800">
<CreditCard className="w-4 h-4"/>
<span className="text-xs font-bold uppercase">Configurazione Pagamenti</span>
</div>
<div>
<label className="text-xs font-semibold text-slate-600 block mb-1">PayPal Client ID (REST API App)</label>
<input className="w-full border p-2 rounded text-slate-700 text-sm" placeholder="Es: Afg..." value={condoForm.paypalClientId} onChange={e => setCondoForm({...condoForm, paypalClientId: e.target.value})} />
<p className="text-[10px] text-slate-500 mt-1">Necessario per abilitare i pagamenti online delle rate.</p>
</div>
</div>
)}
<div>
<textarea className="w-full border p-2.5 rounded-lg text-slate-700 h-16" placeholder="Note (opzionali)" value={condoForm.notes} onChange={e => setCondoForm({...condoForm, notes: e.target.value})} />
</div>
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-slate-600">Quota Default </span>
<input type="number" className="border p-2 rounded w-24 text-slate-700" value={condoForm.defaultMonthlyQuota} onChange={e => setCondoForm({...condoForm, defaultMonthlyQuota: parseFloat(e.target.value)})} />
</div>
<div className="flex gap-2 pt-2">
<button type="button" onClick={() => setShowCondoModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600 hover:bg-slate-50">Annulla</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg hover:bg-blue-700">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* Family Modal */}
{showFamilyModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-2xl shadow-xl w-full max-w-lg p-6">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingFamily ? 'Modifica Famiglia' : 'Nuova Famiglia'}</h3>
<form onSubmit={handleFamilySubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="md:col-span-2">
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Nome Famiglia</label>
<input type="text" required value={familyForm.name} onChange={(e) => setFamilyForm({...familyForm, name: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" placeholder="Es. Rossi"/>
</div>
<div className="md:col-span-2">
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Email Contatto (Obbligatoria)</label>
<input type="email" required value={familyForm.contactEmail} onChange={(e) => setFamilyForm({...familyForm, contactEmail: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" placeholder="email@esempio.it"/>
</div>
</div>
<div className="grid grid-cols-3 gap-3">
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Interno</label>
<input type="text" value={familyForm.unitNumber} onChange={(e) => setFamilyForm({...familyForm, unitNumber: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" />
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Scala</label>
<input type="text" value={familyForm.stair} onChange={(e) => setFamilyForm({...familyForm, stair: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" />
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Piano</label>
<input type="text" value={familyForm.floor} onChange={(e) => setFamilyForm({...familyForm, floor: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" />
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Note</label>
<textarea value={familyForm.notes} onChange={(e) => setFamilyForm({...familyForm, notes: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700 h-16" placeholder="Note opzionali..."></textarea>
</div>
<div className="pt-2 border-t border-slate-100">
<label className="block text-xs font-bold text-slate-500 uppercase mb-1">Quota Mensile Personalizzata</label>
<p className="text-xs text-slate-400 mb-2">Lasciare vuoto per usare il default del condominio ( {activeCondo?.defaultMonthlyQuota})</p>
<input type="number" step="0.01" value={familyForm.customMonthlyQuota} onChange={(e) => setFamilyForm({...familyForm, customMonthlyQuota: e.target.value})} className="w-full border rounded-lg p-2.5 text-slate-700" placeholder="Es. 120.00"/>
</div>
<div className="flex gap-3 pt-4">
<button type="button" onClick={() => setShowFamilyModal(false)} className="flex-1 p-3 border rounded-lg text-slate-600">Annulla</button>
<button type="submit" className="flex-1 p-3 bg-blue-600 text-white rounded-lg">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* Alert Modal */}
{showAlertModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-md p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingAlert ? 'Modifica Avviso' : 'Nuovo Avviso Automatico'}</h3>
<form onSubmit={handleAlertSubmit} className="space-y-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Oggetto Email" value={alertForm.subject} onChange={e => setAlertForm({...alertForm, subject: e.target.value})} required />
<textarea className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Corpo Email" value={alertForm.body} onChange={e => setAlertForm({...alertForm, body: e.target.value})} required />
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-slate-500 font-bold uppercase">Giorni Offset</label>
<input type="number" className="w-full border p-2.5 rounded-lg text-slate-700" value={alertForm.daysOffset} onChange={e => setAlertForm({...alertForm, daysOffset: parseInt(e.target.value)})} />
</div>
<div>
<label className="text-xs text-slate-500 font-bold uppercase">Ora Invio</label>
<input type="number" min="0" max="23" className="w-full border p-2.5 rounded-lg text-slate-700" value={alertForm.sendHour} onChange={e => setAlertForm({...alertForm, sendHour: parseInt(e.target.value)})} />
</div>
</div>
<div>
<label className="text-xs text-slate-500 font-bold uppercase">Tipo Offset</label>
<select className="w-full border p-2.5 rounded-lg text-slate-700 bg-white" value={alertForm.offsetType} onChange={e => setAlertForm({...alertForm, offsetType: e.target.value as any})}>
<option value="before_next_month">Giorni prima del prossimo mese</option>
<option value="after_current_month">Giorni dopo inizio mese</option>
</select>
</div>
<div className="flex gap-2 pt-2">
<button type="button" onClick={() => setShowAlertModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600">Annulla</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* SMTP Modal */}
{showSmtpModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200">
<div className="flex justify-between items-center mb-6">
<h3 className="text-lg font-bold text-slate-800 flex items-center gap-2"><Server className="w-5 h-5 text-blue-600"/> Configurazione SMTP</h3>
<button onClick={() => setShowSmtpModal(false)} className="text-slate-400 hover:text-slate-600"><X className="w-6 h-6"/></button>
</div>
<form onSubmit={handleSmtpSubmit} className="space-y-5">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Host</label>
<input type="text" value={globalSettings?.smtpConfig?.host || ''} onChange={(e) => setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), host: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="smtp.gmail.com"/>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Porta</label>
<input type="number" value={globalSettings?.smtpConfig?.port || 0} onChange={(e) => setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), port: parseInt(e.target.value)} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="587"/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Utente</label>
<input type="text" value={globalSettings?.smtpConfig?.user || ''} onChange={(e) => setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), user: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Password</label>
<input type="password" value={globalSettings?.smtpConfig?.pass || ''} onChange={(e) => setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), pass: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/>
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Email Mittente</label>
<input type="email" value={globalSettings?.smtpConfig?.fromEmail || ''} onChange={(e) => setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), fromEmail: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="no-reply@condominio.it"/>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<input type="checkbox" checked={globalSettings?.smtpConfig?.secure || false} onChange={(e) => setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), secure: e.target.checked} as any} : null)} className="w-4 h-4 text-blue-600"/>
<label className="text-sm text-slate-700 font-medium">Usa SSL/TLS (Secure)</label>
</div>
<button type="button" onClick={handleSmtpTest} disabled={testingSmtp} className="text-xs font-bold bg-amber-100 text-amber-700 px-3 py-1.5 rounded hover:bg-amber-200 transition-colors flex items-center gap-1 disabled:opacity-50">
{testingSmtp ? <span className="animate-pulse">Test...</span> : <><Send className="w-3 h-3"/> Test Configurazione</>}
</button>
</div>
{testSmtpMsg && <p className={`text-xs font-medium text-center ${testSmtpMsg.startsWith('Errore') ? 'text-red-500' : 'text-green-600'}`}>{testSmtpMsg}</p>}
<div className="pt-2 flex justify-between items-center border-t border-slate-100 mt-2">
<span className="text-green-600 text-sm font-medium">{successMsg}</span>
<div className="flex gap-2">
<button type="button" onClick={() => setShowSmtpModal(false)} className="px-4 py-2 text-slate-600 border rounded-lg hover:bg-slate-50">Chiudi</button>
<button type="submit" className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 font-medium">Salva</button>
</div>
</div>
</form>
</div>
</div>
)}
</div>
);
};