Update Settings.tsx

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

View File

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