Files
Condopay/pages/Settings.tsx
2025-12-12 00:00:13 +01:00

1200 lines
79 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, Cloud, HardDrive
} 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' | 'storage' | '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,
dueDay: 10
});
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();
// Ensure storageConfig exists locally even if API missed it
if (!gSettings.storageConfig) {
gSettings.storageConfig = { provider: 'local_db' };
}
setCondos(condoList);
setActiveCondo(activeC);
setGlobalSettings(gSettings);
// Fetch condo-specific data individually to prevent one failure from blocking others
if (activeC) {
// Families
try {
const fams = await CondoService.getFamilies(activeC.id);
setFamilies(fams);
} catch(e) { console.error("Error fetching families", e); }
// Users
try {
const usrs = await CondoService.getUsers(activeC.id);
setUsers(usrs);
} catch(e) { console.error("Error fetching users", e); }
// Alerts
try {
const alrts = await CondoService.getAlerts(activeC.id);
setAlerts(alrts);
} catch(e) { console.error("Error fetching alerts", e); }
// Notices
try {
const allNotices = await CondoService.getNotices(activeC.id);
setNotices(allNotices);
// Fetch read stats for notices
const stats: Record<string, NoticeRead[]> = {};
for (const n of allNotices) {
try {
const reads = await CondoService.getNoticeReadStatus(n.id);
stats[n.id] = reads;
} catch(e) { console.warn("Error reading notice status", e); }
}
setNoticeReadStats(stats);
} catch(e) { console.error("Error fetching notices", e); }
} else {
setFamilies([]);
setUsers([]);
setAlerts([]);
setNotices([]);
}
} else {
const activeC = await CondoService.getActiveCondo();
setActiveCondo(activeC);
}
} catch(e) {
console.error("Global fetch 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('Configurazione salvata!');
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 handleStorageSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!globalSettings) return;
setSaving(true);
try {
await CondoService.updateSettings(globalSettings);
setSuccessMsg('Configurazione Storage salvata!');
setTimeout(() => setSuccessMsg(''), 3000);
} catch (e) { console.error(e); }
finally { setSaving(false); }
};
const handleNewYear = async () => {
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 (omitted repeated code for brevity, logic is same as before)
const openAddCondoModal = () => {
setEditingCondo(null);
setCondoForm({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100, dueDay: 10 });
setShowCondoModal(true);
};
const openEditCondoModal = (c: Condo) => {
setEditingCondo(c);
setCondoForm({ name: c.name, address: c.address || '', streetNumber: c.streetNumber || '', city: c.city || '', province: c.province || '', zipCode: c.zipCode || '', notes: c.notes || '', paypalClientId: c.paypalClientId || '', defaultMonthlyQuota: c.defaultMonthlyQuota, dueDay: c.dueDay || 10 });
setShowCondoModal(true);
};
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,
dueDay: condoForm.dueDay
};
const savedCondo = await CondoService.saveCondo(payload);
const list = await CondoService.getCondos();
setCondos(list);
if (activeCondo?.id === savedCondo.id) { setActiveCondo(savedCondo); }
if (!activeCondo && list.length === 1) { CondoService.setActiveCondo(savedCondo.id); }
setShowCondoModal(false);
window.dispatchEvent(new Event('condo-updated'));
} catch (e) {
console.error(e);
alert("Errore nel salvataggio del condominio. Assicurati di essere amministratore.");
}
};
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"/> },
{ id: 'storage', label: 'Cloud & Storage', icon: <HardDrive className="w-4 h-4"/> } // Moved up here
);
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">Gestione Documenti</p><p className="text-sm text-slate-500">Archivio digitale con tag e upload.</p></div>
<button type="button" onClick={() => toggleFeature('documents')} className={`${globalSettings.features.documents ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.documents ? '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>
<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>
)}
{/* STORAGE CONFIG TAB */}
{isPrivileged && activeTab === 'storage' && (
<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-4 flex items-center gap-2"><Cloud className="w-5 h-5 text-blue-600" /> Configurazione Storage</h3>
<p className="text-sm text-slate-500 mb-6">Scegli dove salvare i documenti caricati.</p>
<form onSubmit={handleStorageSubmit} className="space-y-6">
{globalSettings?.storageConfig ? (
<>
<div>
<label className="text-sm font-bold text-slate-700 mb-2 block">Provider Attivo</label>
<select
className="w-full border p-2.5 rounded-lg bg-slate-50 font-medium text-slate-700"
value={globalSettings.storageConfig.provider}
onChange={e => setGlobalSettings({
...globalSettings,
storageConfig: { ...globalSettings.storageConfig!, provider: e.target.value as any }
})}
>
<option value="local_db">Database Locale (Solo Demo)</option>
<option value="s3">AWS S3 / Compatible</option>
<option value="google_drive">Google Drive</option>
<option value="dropbox">Dropbox</option>
<option value="onedrive">OneDrive</option>
</select>
</div>
{globalSettings.storageConfig.provider === 'local_db' && (
<div className="bg-amber-50 text-amber-800 p-4 rounded-lg text-sm border border-amber-200">
<p className="font-bold">Modalità Demo Attiva</p>
<p>I file vengono salvati direttamente nel database (Base64). Non raccomandato per produzione o file grandi.</p>
</div>
)}
{globalSettings.storageConfig.provider === 's3' && (
<div className="space-y-3 border-l-4 border-blue-500 pl-4">
<h4 className="font-bold text-sm text-slate-700">Configurazione S3</h4>
<input placeholder="Bucket Name" className="w-full border p-2 rounded" value={globalSettings.storageConfig.bucket || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, bucket: e.target.value}})}/>
<input placeholder="Region (es. eu-central-1)" className="w-full border p-2 rounded" value={globalSettings.storageConfig.region || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, region: e.target.value}})}/>
<input placeholder="Access Key ID" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiKey || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiKey: e.target.value}})}/>
<input type="password" placeholder="Secret Access Key" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiSecret || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiSecret: e.target.value}})}/>
</div>
)}
{(globalSettings.storageConfig.provider === 'google_drive' || globalSettings.storageConfig.provider === 'dropbox' || globalSettings.storageConfig.provider === 'onedrive') && (
<div className="space-y-3 border-l-4 border-purple-500 pl-4">
<h4 className="font-bold text-sm text-slate-700">Autenticazione API</h4>
<p className="text-xs text-slate-500">Inserisci le credenziali dell'applicazione sviluppatore.</p>
<input placeholder="Client ID / App Key" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiKey || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiKey: e.target.value}})}/>
<input type="password" placeholder="Client Secret / App Secret" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiSecret || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiSecret: e.target.value}})}/>
</div>
)}
<div className="pt-2 flex justify-between items-center">
<span className="text-green-600 font-medium">{successMsg}</span>
<button type="submit" disabled={saving} 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 Impostazioni
</button>
</div>
</>
) : (
<div className="text-center p-4 text-slate-500">Inizializzazione configurazione storage...</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 className="flex gap-4">
<div className="flex-1">
<label className="block text-sm font-medium text-slate-700 mb-1">Quota Mensile (€)</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="flex-1">
<label className="block text-sm font-medium text-slate-700 mb-1">Giorno Scadenza (1-31)</label>
<input type="number" min="1" max="31" value={activeCondo.dueDay || 10} onChange={(e) => setActiveCondo({ ...activeCondo, dueDay: parseInt(e.target.value) })} className="w-full border p-2.5 rounded-lg text-slate-700" required />
</div>
</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">
<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">Elenco Famiglie</h3><p className="text-xs text-blue-600">Condominio: {activeCondo?.name}</p></div><button onClick={openAddFamilyModal} 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="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 border-slate-200"><tr><th className="px-4 py-3">Nominativo</th><th className="px-4 py-3">Interno</th><th className="px-4 py-3 hidden md:table-cell">Email</th><th className="px-4 py-3 text-right">Azioni</th></tr></thead>
<tbody className="divide-y divide-slate-100">
{families.map(f => (
<tr key={f.id} className="hover:bg-slate-50"><td className="px-4 py-3 font-medium text-slate-800">{f.name}</td><td className="px-4 py-3">{f.unitNumber}</td><td className="px-4 py-3 hidden md:table-cell text-slate-500">{f.contactEmail}</td><td className="px-4 py-3 text-right"><button onClick={() => openEditFamilyModal(f)} className="text-blue-600 hover:underline mr-3">Modifica</button><button onClick={() => handleDeleteFamily(f.id)} className="text-red-600 hover:underline">Elimina</button></td></tr>
))}
{families.length === 0 && <tr><td colSpan={4} className="p-8 text-center text-slate-400">Nessuna famiglia registrata.</td></tr>}
</tbody>
</table>
</div>
</div>
)}
{/* USERS TAB */}
{isPrivileged && activeTab === 'users' && (
<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">Utenti & Accessi</h3><p className="text-xs text-blue-600">Gestione account di accesso</p></div><button onClick={openAddUserModal} 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="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 border-slate-200"><tr><th className="px-4 py-3">Email</th><th className="px-4 py-3">Ruolo</th><th className="px-4 py-3 hidden md:table-cell">Famiglia</th><th className="px-4 py-3 text-right">Azioni</th></tr></thead>
<tbody className="divide-y divide-slate-100">
{users.map(u => {
const fam = families.find(f => f.id === u.familyId);
return (
<tr key={u.id} className="hover:bg-slate-50"><td className="px-4 py-3 font-medium text-slate-800">{u.email}</td><td className="px-4 py-3"><span className={`px-2 py-1 rounded text-xs font-bold uppercase ${u.role === 'admin' ? 'bg-purple-100 text-purple-700' : u.role === 'poweruser' ? 'bg-orange-100 text-orange-700' : 'bg-slate-100 text-slate-600'}`}>{u.role}</span></td><td className="px-4 py-3 hidden md:table-cell">{fam ? `${fam.name} (${fam.unitNumber})` : '-'}</td><td className="px-4 py-3 text-right"><button onClick={() => openEditUserModal(u)} className="text-blue-600 hover:underline mr-3">Modifica</button><button onClick={() => handleDeleteUser(u.id)} className="text-red-600 hover:underline">Elimina</button></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 Avvisi</h3></div><button onClick={openAddNoticeModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex gap-2"><Plus className="w-4 h-4" /> Nuovo Avviso</button></div>
<div className="space-y-3">
{notices.map(notice => {
const reads = noticeReadStats[notice.id] || [];
const targetCount = notice.targetFamilyIds?.length || 0;
return (
<div key={notice.id} className="bg-white p-4 rounded-xl border border-slate-200 shadow-sm flex flex-col md:flex-row justify-between gap-4">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h4 className="font-bold text-slate-800">{notice.title}</h4>
<span className={`px-2 py-0.5 text-[10px] font-bold uppercase rounded ${notice.active ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-500'}`}>{notice.active ? 'Attivo' : 'Archiviato'}</span>
<span className="text-xs text-slate-400">• {new Date(notice.date).toLocaleDateString()}</span>
</div>
<p className="text-sm text-slate-600 line-clamp-1">{notice.content}</p>
<div className="mt-2 flex gap-3 text-xs">
<span className="text-blue-600 font-medium bg-blue-50 px-2 py-0.5 rounded">
Destinatari: {targetCount === 0 ? 'Tutti' : `${targetCount} Famiglie`}
</span>
<button onClick={() => openReadDetails(notice.id)} className="text-slate-500 hover:text-blue-600 flex items-center gap-1">
<Eye className="w-3 h-3"/> Letto da: <strong>{reads.length}</strong>
</button>
</div>
</div>
<div className="flex items-center gap-2">
<button onClick={() => toggleNoticeActive(notice)} className="p-2 text-slate-500 hover:bg-slate-100 rounded" title={notice.active ? "Archivia" : "Attiva"}><Power className="w-4 h-4"/></button>
<button onClick={() => openEditNoticeModal(notice)} className="p-2 text-blue-600 hover:bg-blue-50 rounded" title="Modifica"><Pencil className="w-4 h-4"/></button>
<button onClick={() => handleDeleteNotice(notice.id)} className="p-2 text-red-600 hover:bg-red-50 rounded" title="Elimina"><Trash2 className="w-4 h-4"/></button>
</div>
</div>
);
})}
{notices.length === 0 && <div className="text-center p-8 text-slate-400">Nessun avviso in bacheca.</div>}
</div>
</div>
)}
{/* ALERTS TAB */}
{isPrivileged && activeTab === 'alerts' && (
<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">Avvisi Automatici Email</h3><p className="text-xs text-blue-600">Configura email periodiche</p></div>
<div className="flex gap-2">
<button onClick={() => setShowSmtpModal(true)} className="bg-white text-blue-600 border border-blue-200 px-4 py-2 rounded-lg font-medium hover:bg-blue-50 flex gap-2"><Server className="w-4 h-4"/> Configura SMTP</button>
<button onClick={openAddAlertModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex gap-2"><Plus className="w-4 h-4" /> Nuovo Alert</button>
</div>
</div>
<div className="space-y-3">
{alerts.map(alert => (
<div key={alert.id} className="bg-white p-4 rounded-xl border border-slate-200 shadow-sm flex justify-between items-center">
<div>
<h4 className="font-bold text-slate-800">{alert.subject}</h4>
<p className="text-sm text-slate-500">Invia {alert.daysOffset} giorni {alert.offsetType === 'before_next_month' ? 'prima del prossimo mese' : 'dopo il mese corrente'} alle {alert.sendHour}:00</p>
</div>
<div className="flex gap-2">
<button onClick={() => openEditAlertModal(alert)} className="text-blue-600 hover:underline text-sm">Modifica</button>
<button onClick={() => handleDeleteAlert(alert.id)} className="text-red-600 hover:underline text-sm">Elimina</button>
</div>
</div>
))}
{alerts.length === 0 && <div className="text-center p-8 text-slate-400">Nessun alert configurato.</div>}
</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">
{/* Form fields same as before... omitted for brevity */}
<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 gap-4">
<div className="flex-1">
<label className="text-sm font-medium text-slate-600 block mb-1">Quota Default €</label>
<input type="number" className="border p-2 rounded w-full text-slate-700" value={condoForm.defaultMonthlyQuota} onChange={e => setCondoForm({...condoForm, defaultMonthlyQuota: parseFloat(e.target.value)})} />
</div>
<div className="flex-1">
<label className="text-sm font-medium text-slate-600 block mb-1">Giorno Scadenza (1-31)</label>
<input type="number" min="1" max="31" className="border p-2 rounded w-full text-slate-700" value={condoForm.dueDay} onChange={e => setCondoForm({...condoForm, dueDay: parseInt(e.target.value)})} />
</div>
</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-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingFamily ? 'Modifica Famiglia' : 'Nuova Famiglia'}</h3>
<form onSubmit={handleFamilySubmit} className="space-y-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Cognome / Nome Famiglia" value={familyForm.name} onChange={e => setFamilyForm({...familyForm, name: e.target.value})} required />
<div className="grid grid-cols-3 gap-3">
<input className="border p-2.5 rounded-lg text-slate-700" placeholder="Interno" value={familyForm.unitNumber} onChange={e => setFamilyForm({...familyForm, unitNumber: e.target.value})} required />
<input className="border p-2.5 rounded-lg text-slate-700" placeholder="Scala" value={familyForm.stair} onChange={e => setFamilyForm({...familyForm, stair: e.target.value})} />
<input className="border p-2.5 rounded-lg text-slate-700" placeholder="Piano" value={familyForm.floor} onChange={e => setFamilyForm({...familyForm, floor: e.target.value})} />
</div>
<input type="email" className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Email Contatto Principal" value={familyForm.contactEmail} onChange={e => setFamilyForm({...familyForm, contactEmail: e.target.value})} />
<input type="number" className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Quota Personalizzata (Opzionale)" value={familyForm.customMonthlyQuota} onChange={e => setFamilyForm({...familyForm, customMonthlyQuota: e.target.value})} />
<textarea className="w-full border p-2.5 rounded-lg text-slate-700 h-20" placeholder="Note..." value={familyForm.notes} onChange={e => setFamilyForm({...familyForm, notes: e.target.value})} />
<div className="flex gap-2 pt-2">
<button type="button" onClick={() => setShowFamilyModal(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>
)}
{/* 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 type="email" className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Email" value={userForm.email} onChange={e => setUserForm({...userForm, email: e.target.value})} required />
<input type="text" className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome" value={userForm.name} onChange={e => setUserForm({...userForm, name: e.target.value})} />
<input type="password" className="w-full border p-2.5 rounded-lg text-slate-700" placeholder={editingUser ? "Password (lascia vuoto per mantenere)" : "Password"} value={userForm.password} onChange={e => setUserForm({...userForm, password: e.target.value})} />
<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})}>
<option value="user">Utente (Condomino)</option>
<option value="poweruser">Power User (Gestore)</option>
<option value="admin">Amministratore</option>
</select>
<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="">-- Associa a Famiglia --</option>
{families.map(f => <option key={f.id} value={f.id}>{f.name} (Int. {f.unitNumber})</option>)}
</select>
<div className="flex items-center gap-2">
<input type="checkbox" checked={userForm.receiveAlerts} onChange={e => setUserForm({...userForm, receiveAlerts: e.target.checked})} />
<span className="text-sm text-slate-600">Ricevi notifiche 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">Annulla</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg">Salva</button>
</div>
</form>
</div>
</div>
)}
{/* Notice Modal */}
{showNoticeModal && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl 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">{editingNotice ? 'Modifica Avviso' : 'Nuovo Avviso in Bacheca'}</h3>
<div className="flex-1 overflow-y-auto pr-2">
<form id="noticeForm" onSubmit={handleNoticeSubmit} className="space-y-4">
<input className="w-full border p-2.5 rounded-lg text-slate-700 font-bold" placeholder="Titolo Avviso" value={noticeForm.title} onChange={e => setNoticeForm({...noticeForm, title: e.target.value})} required />
<div className="grid grid-cols-2 gap-4">
<select className="w-full border p-2.5 rounded-lg text-slate-700 bg-white" value={noticeForm.type} onChange={e => setNoticeForm({...noticeForm, type: e.target.value as any})}>
<option value="info">Informazione</option>
<option value="warning">Avviso Urgente</option>
<option value="maintenance">Manutenzione</option>
<option value="event">Evento / Assemblea</option>
</select>
<div className="flex items-center gap-2 border p-2.5 rounded-lg bg-slate-50">
<span className="text-sm text-slate-600">Stato:</span>
<div className="flex gap-2">
<button type="button" onClick={() => setNoticeForm({...noticeForm, active: true})} className={`px-2 py-1 text-xs rounded ${noticeForm.active ? 'bg-green-500 text-white' : 'bg-slate-200'}`}>Attivo</button>
<button type="button" onClick={() => setNoticeForm({...noticeForm, active: false})} className={`px-2 py-1 text-xs rounded ${!noticeForm.active ? 'bg-slate-500 text-white' : 'bg-slate-200'}`}>Bozza</button>
</div>
</div>
</div>
<textarea className="w-full border p-2.5 rounded-lg text-slate-700 h-32" placeholder="Testo dell'avviso..." value={noticeForm.content} onChange={e => setNoticeForm({...noticeForm, content: e.target.value})} required />
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Link esterno (es. PDF verbale)" value={noticeForm.link} onChange={e => setNoticeForm({...noticeForm, link: e.target.value})} />
{/* Targeting */}
<div className="border-t pt-4">
<label className="block text-sm font-bold text-slate-700 mb-2">Visibilità</label>
<div className="flex gap-4 mb-3">
<label className="flex items-center gap-2 cursor-pointer">
<input type="radio" name="targetMode" checked={noticeTargetMode === 'all'} onChange={() => setNoticeTargetMode('all')} className="text-blue-600"/>
<span className="text-sm">Pubblico (Tutti)</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input type="radio" name="targetMode" checked={noticeTargetMode === 'specific'} onChange={() => setNoticeTargetMode('specific')} className="text-blue-600"/>
<span className="text-sm">Seleziona Famiglie</span>
</label>
</div>
{noticeTargetMode === 'specific' && (
<div className="border rounded-lg p-2 max-h-40 overflow-y-auto bg-slate-50 grid grid-cols-2 gap-2">
{families.map(f => (
<label key={f.id} className="flex items-center gap-2 text-sm p-1 hover:bg-white rounded cursor-pointer">
<input
type="checkbox"
checked={noticeForm.targetFamilyIds.includes(f.id)}
onChange={() => toggleNoticeFamilyTarget(f.id)}
className="rounded text-blue-600"
/>
<span className="truncate">{f.name} ({f.unitNumber})</span>
</label>
))}
</div>
)}
</div>
</form>
</div>
<div className="pt-4 border-t mt-4 flex gap-2">
<button onClick={() => setShowNoticeModal(false)} className="flex-1 border p-2.5 rounded-lg text-slate-600">Annulla</button>
<button type="submit" form="noticeForm" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg">Salva e Pubblica</button>
</div>
</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-lg p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800">Configura Alert Email</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 h-24" placeholder="Corpo del messaggio..." 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 block mb-1">Offset Giorni</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)})} required />
</div>
<div>
<label className="text-xs font-bold text-slate-500 block mb-1">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 prox mese</option>
<option value="after_current_month">Giorni dopo inizio mese</option>
</select>
</div>
</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 Configurazione</button>
</div>
</form>
</div>
</div>
)}
{/* SMTP Modal */}
{showSmtpModal && globalSettings && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200">
<h3 className="font-bold text-lg mb-4 text-slate-800 flex items-center gap-2"><Server className="w-5 h-5"/> Configurazione SMTP</h3>
<form onSubmit={handleSmtpSubmit} className="space-y-4">
<div className="grid grid-cols-3 gap-3">
<div className="col-span-2"><input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Host (es. smtp.gmail.com)" value={globalSettings.smtpConfig?.host || ''} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, host: e.target.value}})} /></div>
<div><input type="number" className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Porta" value={globalSettings.smtpConfig?.port || 587} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, port: parseInt(e.target.value)}})} /></div>
</div>
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Utente SMTP" value={globalSettings.smtpConfig?.user || ''} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, user: e.target.value}})} />
<input type="password" className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Password SMTP" value={globalSettings.smtpConfig?.pass || ''} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, pass: e.target.value}})} />
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Email Mittente (From)" value={globalSettings.smtpConfig?.fromEmail || ''} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, fromEmail: e.target.value}})} />
<div className="flex items-center gap-2">
<input type="checkbox" checked={globalSettings.smtpConfig?.secure || false} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, secure: e.target.checked}})} />
<span className="text-sm text-slate-600">Usa SSL/TLS (Secure)</span>
</div>
{testSmtpMsg && <div className={`text-xs p-2 rounded ${testSmtpMsg.includes('Successo') ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>{testSmtpMsg}</div>}
<div className="flex gap-2 pt-2">
<button type="button" onClick={handleSmtpTest} disabled={testingSmtp} className="flex-1 border border-slate-300 text-slate-600 p-2.5 rounded-lg hover:bg-slate-50">{testingSmtp ? 'Test...' : 'Test Connessione'}</button>
<button type="submit" className="flex-1 bg-blue-600 text-white p-2.5 rounded-lg hover:bg-blue-700">Salva</button>
</div>
<button type="button" onClick={() => setShowSmtpModal(false)} className="w-full text-center text-sm text-slate-400 mt-2 hover:text-slate-600">Chiudi</button>
</form>
</div>
</div>
)}
{/* 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-sm p-6 animate-in fade-in zoom-in duration-200 flex flex-col max-h-[80vh]">
<div className="flex justify-between items-center mb-4">
<h3 className="font-bold text-slate-800">Dettaglio Letture</h3>
<button onClick={() => setShowReadDetailsModal(false)}><X className="w-5 h-5 text-slate-400"/></button>
</div>
<div className="flex-1 overflow-y-auto">
{(noticeReadStats[selectedNoticeId] || []).length === 0 ? (
<p className="text-slate-500 text-sm text-center py-4">Nessuna lettura registrata.</p>
) : (
<ul className="divide-y">
{(noticeReadStats[selectedNoticeId] || []).map((read, idx) => {
const u = users.find(usr => usr.id === read.userId);
return (
<li key={idx} className="py-2 text-sm">
<div className="font-medium text-slate-700">{u?.name || u?.email || 'Utente sconosciuto'}</div>
<div className="text-xs text-slate-400">{new Date(read.readAt).toLocaleString()}</div>
</li>
);
})}
</ul>
)}
</div>
</div>
</div>
)}
</div>
);
};