Update Settings.tsx

This commit is contained in:
2026-01-10 00:12:47 +01:00
committed by GitHub
parent b4dc0f2d4d
commit c3f034d9de

View File

@@ -7,7 +7,8 @@ import {
AlertTriangle, User as UserIcon, Server, Bell, Clock, FileText, AlertTriangle, User as UserIcon, Server, Bell, Clock, FileText,
Lock, Megaphone, CheckCircle2, Info, Hammer, Link as LinkIcon, Lock, Megaphone, CheckCircle2, Info, Hammer, Link as LinkIcon,
Eye, EyeOff, Calendar, List, UserCog, Mail, Power, MapPin, CreditCard, Eye, EyeOff, Calendar, List, UserCog, Mail, Power, MapPin, CreditCard,
ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send, Cloud, HardDrive, ChevronRight, Palette, Image as ImageIcon, Upload ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send, Cloud, HardDrive, ChevronRight, Palette, Image as ImageIcon, Upload,
Phone, Hash, Type, Globe, Key, Database, AlignLeft, Euro
} from 'lucide-react'; } from 'lucide-react';
const COLOR_MAP: Record<string, string> = { const COLOR_MAP: Record<string, string> = {
@@ -263,10 +264,34 @@ export const SettingsPage: React.FC = () => {
<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> <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-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome 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>
<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> <label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome Completo</label>
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Telefono</label><input type="tel" value={profileForm.phone} onChange={(e) => setProfileForm({...profileForm, phone: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl focus:ring-2 focus:ring-blue-500 outline-none" /></div> <div className="relative">
<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> <UserIcon className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="text" value={profileForm.name} onChange={(e) => setProfileForm({...profileForm, name: e.target.value})} className="w-full border border-slate-200 p-3 pl-10 rounded-xl focus:ring-2 focus:ring-blue-500 outline-none" required />
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Email</label>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="email" value={currentUser?.email || ''} disabled className="w-full border border-slate-100 bg-slate-50 p-3 pl-10 rounded-xl text-slate-400 cursor-not-allowed" />
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Telefono</label>
<div className="relative">
<Phone className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="tel" value={profileForm.phone} onChange={(e) => setProfileForm({...profileForm, phone: e.target.value})} className="w-full border border-slate-200 p-3 pl-10 rounded-xl focus:ring-2 focus:ring-blue-500 outline-none" />
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Password</label>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<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 pl-10 rounded-xl focus:ring-2 focus:ring-blue-500 outline-none" />
</div>
</div>
</div> </div>
<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 justify-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> <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 justify-center gap-2 hover:bg-blue-700 shadow-lg shadow-blue-200 disabled:opacity-50"><Save className="w-4 h-4" /> {profileSaving ? '...' : 'Salva'}</button>{profileMsg && <p className="text-sm font-bold text-green-600">{profileMsg}</p>}</div>
</form> </form>
@@ -277,7 +302,13 @@ export const SettingsPage: React.FC = () => {
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8"> <div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8">
<div className="flex items-center gap-3 mb-8"><div className="p-3 bg-purple-50 rounded-2xl text-purple-600"><Palette className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Branding</h3></div> <div className="flex items-center gap-3 mb-8"><div className="p-3 bg-purple-50 rounded-2xl text-purple-600"><Palette className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Branding</h3></div>
<form onSubmit={handleBrandingSubmit} className="space-y-8"> <form onSubmit={handleBrandingSubmit} className="space-y-8">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome App</label><input className="w-full border border-slate-200 p-3 rounded-xl font-bold text-slate-800" value={brandingForm.appName} onChange={e => setBrandingForm({...brandingForm, appName: e.target.value})}/></div> <div>
<label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome App</label>
<div className="relative">
<Type className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border border-slate-200 p-3 pl-10 rounded-xl font-bold text-slate-800" value={brandingForm.appName} onChange={e => setBrandingForm({...brandingForm, appName: e.target.value})}/>
</div>
</div>
<div> <div>
<label className="text-xs font-bold text-slate-500 uppercase mb-4 block">Colore Primario</label> <label className="text-xs font-bold text-slate-500 uppercase mb-4 block">Colore Primario</label>
<div className="flex gap-4 flex-wrap items-center bg-slate-50 p-6 rounded-2xl border border-slate-100"> <div className="flex gap-4 flex-wrap items-center bg-slate-50 p-6 rounded-2xl border border-slate-100">
@@ -285,7 +316,13 @@ export const SettingsPage: React.FC = () => {
<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> <button key={colorKey} type="button" onClick={() => setBrandingForm({...brandingForm, primaryColor: colorKey})} className={`w-12 h-12 rounded-2xl border-4 transition-all hover:scale-110 flex items-center justify-center shadow-sm ${brandingForm.primaryColor === colorKey ? 'border-white ring-4 ring-slate-900' : 'border-transparent'}`} style={{ backgroundColor: COLOR_MAP[colorKey] }}>{brandingForm.primaryColor === colorKey && <CheckCircle2 className="w-6 h-6 text-white"/>}</button>
))} ))}
<div className="h-10 w-px bg-slate-200 mx-2" /> <div className="h-10 w-px bg-slate-200 mx-2" />
<div className="flex items-center gap-2"><input type="color" value={brandingForm.primaryColor.startsWith('#') ? brandingForm.primaryColor : '#2563eb'} onChange={e => setBrandingForm({...brandingForm, primaryColor: e.target.value})} className="w-12 h-12 rounded-2xl border-2 cursor-pointer p-0 bg-transparent"/><input type="text" value={brandingForm.primaryColor} onChange={e => setBrandingForm({...brandingForm, primaryColor: e.target.value})} className="w-28 text-sm font-mono border border-slate-200 rounded-xl p-3 uppercase font-bold"/></div> <div 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"/>
<div className="relative">
<Hash className="absolute left-2 top-3.5 w-4 h-4 text-slate-400" />
<input type="text" value={brandingForm.primaryColor} onChange={e => setBrandingForm({...brandingForm, primaryColor: e.target.value})} className="w-32 text-sm font-mono border border-slate-200 rounded-xl p-3 pl-8 uppercase font-bold"/>
</div>
</div>
</div> </div>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
@@ -332,25 +369,56 @@ export const SettingsPage: React.FC = () => {
<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> <div className="flex items-center gap-3 mb-8"><div className="p-3 bg-blue-50 rounded-2xl text-blue-600"><Building className="w-6 h-6" /></div><h3 className="text-xl font-bold text-slate-800">Dati Condominio</h3></div>
<form onSubmit={handleGeneralSubmit} className="space-y-6"> <form onSubmit={handleGeneralSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div><label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome</label><input type="text" value={activeCondo.name} onChange={e => setActiveCondo({...activeCondo, name: e.target.value})} className="w-full border border-slate-200 p-3 rounded-xl font-bold" required /></div> <div>
<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> <label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Nome</label>
<div className="grid grid-cols-2 gap-4"> <div className="relative">
<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> <Building className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<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> <input type="text" value={activeCondo.name} onChange={e => setActiveCondo({...activeCondo, name: e.target.value})} className="w-full border border-slate-200 p-3 pl-10 rounded-xl font-bold" required />
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Indirizzo</label>
<div className="relative">
<MapPin className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="text" value={activeCondo.address || ''} onChange={e => setActiveCondo({...activeCondo, address: e.target.value})} className="w-full border border-slate-200 p-3 pl-10 rounded-xl" required />
</div>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <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>
<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> <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>
<div className="relative">
<Euro className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="number" value={activeCondo.defaultMonthlyQuota} onChange={e => setActiveCondo({...activeCondo, defaultMonthlyQuota: parseFloat(e.target.value)})} className="w-full border border-slate-200 p-3 pl-10 rounded-xl font-bold" />
</div>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase mb-2 block">Scadenza (Giorno)</label>
<div className="relative">
<Calendar className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<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 pl-10 rounded-xl" />
</div>
</div>
</div> </div>
</div> </div>
<div> <div>
<label className="text-xs font-bold text-slate-500 uppercase mb-2 block">PayPal Client ID</label> <label className="text-xs font-bold text-slate-500 uppercase mb-2 block">PayPal Client ID</label>
<div className="relative"> <div className="relative">
<CreditCard className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input <input
type={showPayPalKey ? "text" : "password"} type={showPayPalKey ? "text" : "password"}
value={activeCondo.paypalClientId || ''} value={activeCondo.paypalClientId || ''}
onChange={e => setActiveCondo({...activeCondo, paypalClientId: e.target.value})} onChange={e => setActiveCondo({...activeCondo, paypalClientId: e.target.value})}
className="w-full border border-slate-200 p-3 rounded-xl text-xs font-mono pr-10" className="w-full border border-slate-200 p-3 pl-10 rounded-xl text-xs font-mono pr-10"
placeholder="sandbox_..." placeholder="sandbox_..."
/> />
<button <button
@@ -391,22 +459,43 @@ export const SettingsPage: React.FC = () => {
<div className="space-y-6 p-6 bg-slate-50 rounded-2xl border border-slate-100"> <div className="space-y-6 p-6 bg-slate-50 rounded-2xl border border-slate-100">
{storageForm.provider === 's3' && ( {storageForm.provider === 's3' && (
<> <>
<input value={storageForm.bucket || ''} onChange={e => setStorageForm({...storageForm, bucket: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Bucket Name" /> <div className="relative">
<input value={storageForm.region || ''} onChange={e => setStorageForm({...storageForm, region: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Region (es. eu-central-1)" /> <Database className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input value={storageForm.apiKey || ''} onChange={e => setStorageForm({...storageForm, apiKey: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Access Key ID" /> <input value={storageForm.bucket || ''} onChange={e => setStorageForm({...storageForm, bucket: e.target.value})} className="w-full border p-3 pl-10 rounded-xl" placeholder="Bucket Name" />
<input value={storageForm.apiSecret || ''} onChange={e => setStorageForm({...storageForm, apiSecret: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Secret Access Key" type="password" /> </div>
<div className="relative">
<Globe className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input value={storageForm.region || ''} onChange={e => setStorageForm({...storageForm, region: e.target.value})} className="w-full border p-3 pl-10 rounded-xl" placeholder="Region (es. eu-central-1)" />
</div>
<div className="relative">
<Key className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input value={storageForm.apiKey || ''} onChange={e => setStorageForm({...storageForm, apiKey: e.target.value})} className="w-full border p-3 pl-10 rounded-xl" placeholder="Access Key ID" />
</div>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input value={storageForm.apiSecret || ''} onChange={e => setStorageForm({...storageForm, apiSecret: e.target.value})} className="w-full border p-3 pl-10 rounded-xl" placeholder="Secret Access Key" type="password" />
</div>
</> </>
)} )}
{storageForm.provider === 'google_drive' && ( {storageForm.provider === 'google_drive' && (
<> <>
<input value={storageForm.bucket || ''} onChange={e => setStorageForm({...storageForm, bucket: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Parent Folder ID (Opzionale)" /> <div className="relative">
<input value={storageForm.apiKey || ''} onChange={e => setStorageForm({...storageForm, apiKey: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Service Account Email (client_email)" /> <Database className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input value={storageForm.bucket || ''} onChange={e => setStorageForm({...storageForm, bucket: e.target.value})} className="w-full border p-3 pl-10 rounded-xl" placeholder="Parent Folder ID (Opzionale)" />
</div>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input value={storageForm.apiKey || ''} onChange={e => setStorageForm({...storageForm, apiKey: e.target.value})} className="w-full border p-3 pl-10 rounded-xl" placeholder="Service Account Email (client_email)" />
</div>
<textarea value={storageForm.apiSecret || ''} onChange={e => setStorageForm({...storageForm, apiSecret: e.target.value})} className="w-full border p-3 rounded-xl h-32 text-xs font-mono" placeholder="Private Key (-----BEGIN PRIVATE KEY...)" /> <textarea value={storageForm.apiSecret || ''} onChange={e => setStorageForm({...storageForm, apiSecret: e.target.value})} className="w-full border p-3 rounded-xl h-32 text-xs font-mono" placeholder="Private Key (-----BEGIN PRIVATE KEY...)" />
</> </>
)} )}
{(storageForm.provider === 'dropbox' || storageForm.provider === 'onedrive') && ( {(storageForm.provider === 'dropbox' || storageForm.provider === 'onedrive') && (
<> <>
<input value={storageForm.apiKey || ''} onChange={e => setStorageForm({...storageForm, apiKey: e.target.value})} className="w-full border p-3 rounded-xl" placeholder="Access Token" type="password" /> <div className="relative">
<Key className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input value={storageForm.apiKey || ''} onChange={e => setStorageForm({...storageForm, apiKey: e.target.value})} className="w-full border p-3 pl-10 rounded-xl" placeholder="Access Token" type="password" />
</div>
<p className="text-xs text-slate-500">Nota: Inserisci un Access Token valido a lunga durata.</p> <p className="text-xs text-slate-500">Nota: Inserisci un Access Token valido a lunga durata.</p>
</> </>
)} )}
@@ -494,7 +583,7 @@ export const SettingsPage: React.FC = () => {
{!activeCondo ? <NoCondoState /> : ( {!activeCondo ? <NoCondoState /> : (
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8"> <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 items-center justify-center gap-2"><Plus className="w-4 h-4"/> Pubblica</button></div> <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 items-center justify-center 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 className="grid gap-4">{notices.map(n => (<div key={n.id} className="p-5 border rounded-2xl flex justify-between items-start gap-4"><div className="min-w-0 flex-1"><h4 className="font-bold text-slate-800 break-words">{n.title}</h4><p className="text-sm text-slate-500 break-words mt-1">{n.content}</p></div><div className="flex gap-2 flex-shrink-0"><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> </div>
)} )}
</> </>
@@ -521,8 +610,14 @@ export const SettingsPage: React.FC = () => {
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-lg p-8 animate-in fade-in zoom-in duration-200"> <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> <h3 className="font-bold text-2xl mb-6 text-slate-800">{editingCondo ? 'Modifica Condominio' : 'Nuovo Condominio'}</h3>
<form onSubmit={handleCondoSubmit} className="space-y-4"> <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 /> <div className="relative">
<input className="w-full border p-3 rounded-xl" placeholder="Indirizzo" value={condoForm.address} onChange={e => setCondoForm({...condoForm, address: e.target.value})} /> <Building className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Nome Condominio" value={condoForm.name} onChange={e => setCondoForm({...condoForm, name: e.target.value})} required />
</div>
<div className="relative">
<MapPin className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Indirizzo" value={condoForm.address} onChange={e => setCondoForm({...condoForm, address: e.target.value})} />
</div>
<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> <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> </form>
</div> </div>
@@ -534,10 +629,22 @@ export const SettingsPage: React.FC = () => {
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md p-8 animate-in fade-in zoom-in duration-200"> <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> <h3 className="font-bold text-2xl mb-6 text-slate-800">{editingFamily ? 'Modifica Famiglia' : 'Nuova Famiglia'}</h3>
<form onSubmit={handleFamilySubmit} className="space-y-4"> <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 /> <div className="relative">
<input className="w-full border p-3 rounded-xl" placeholder="Interno / Unità" value={familyForm.unitNumber} onChange={e => setFamilyForm({...familyForm, unitNumber: e.target.value})} required /> <UserIcon className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<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 pl-10 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="Quota Personalizzata (Opzionale)" type="number" step="0.01" value={familyForm.customMonthlyQuota} onChange={e => setFamilyForm({...familyForm, customMonthlyQuota: e.target.value})} /> </div>
<div className="relative">
<Building className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Interno / Unità" value={familyForm.unitNumber} onChange={e => setFamilyForm({...familyForm, unitNumber: e.target.value})} required />
</div>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Email Contatto" type="email" value={familyForm.contactEmail} onChange={e => setFamilyForm({...familyForm, contactEmail: e.target.value})} />
</div>
<div className="relative">
<Euro className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Quota Personalizzata (Opzionale)" type="number" step="0.01" value={familyForm.customMonthlyQuota} onChange={e => setFamilyForm({...familyForm, customMonthlyQuota: e.target.value})} />
</div>
<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> <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> </form>
</div> </div>
@@ -549,10 +656,22 @@ export const SettingsPage: React.FC = () => {
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md p-8 animate-in fade-in zoom-in duration-200"> <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> <h3 className="font-bold text-2xl mb-6 text-slate-800">{editingUser ? 'Modifica Utente' : 'Nuovo Utente'}</h3>
<form onSubmit={handleUserSubmit} className="space-y-4"> <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 /> <div className="relative">
<input className="w-full border p-3 rounded-xl" placeholder="Nome" value={userForm.name} onChange={e => setUserForm({...userForm, name: e.target.value})} /> <Mail className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="tel" className="w-full border p-3 rounded-xl" placeholder="Telefono" value={userForm.phone} onChange={e => setUserForm({...userForm, phone: e.target.value})} /> <input type="email" className="w-full border p-3 pl-10 rounded-xl" placeholder="Email" value={userForm.email} onChange={e => setUserForm({...userForm, email: e.target.value})} required />
<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>
<div className="relative">
<UserIcon className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Nome" value={userForm.name} onChange={e => setUserForm({...userForm, name: e.target.value})} />
</div>
<div className="relative">
<Phone className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="tel" className="w-full border p-3 pl-10 rounded-xl" placeholder="Telefono" value={userForm.phone} onChange={e => setUserForm({...userForm, phone: e.target.value})} />
</div>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="password" className="w-full border p-3 pl-10 rounded-xl" placeholder="Password (lascia vuoto se invariata)" value={userForm.password} onChange={e => setUserForm({...userForm, password: e.target.value})} />
</div>
<div> <div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Ruolo</label> <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})}> <select className="w-full border p-3 rounded-xl bg-white" value={userForm.role} onChange={(e: any) => setUserForm({...userForm, role: e.target.value})}>
@@ -581,8 +700,14 @@ export const SettingsPage: React.FC = () => {
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-lg p-8 animate-in fade-in zoom-in duration-200"> <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> <h3 className="font-bold text-2xl mb-6 text-slate-800">{editingNotice ? 'Modifica Avviso' : 'Nuovo Avviso'}</h3>
<form onSubmit={handleNoticeSubmit} className="space-y-4"> <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 /> <div className="relative">
<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 /> <Type className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Titolo" value={noticeForm.title} onChange={e => setNoticeForm({...noticeForm, title: e.target.value})} required />
</div>
<div className="relative">
<AlignLeft className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<textarea className="w-full border p-3 pl-10 rounded-xl h-24" placeholder="Contenuto..." value={noticeForm.content} onChange={e => setNoticeForm({...noticeForm, content: e.target.value})} required />
</div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div> <div>
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Tipo</label> <label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Tipo</label>
@@ -619,7 +744,10 @@ export const SettingsPage: React.FC = () => {
))} ))}
</div> </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="relative">
<LinkIcon className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Link esterno (es. PDF Drive)" value={noticeForm.link} onChange={e => setNoticeForm({...noticeForm, link: e.target.value})} />
</div>
<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> <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> </form>
</div> </div>
@@ -631,8 +759,14 @@ export const SettingsPage: React.FC = () => {
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-lg p-8 animate-in fade-in zoom-in duration-200"> <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> <h3 className="font-bold text-2xl mb-6 text-slate-800">Regola Avviso Automatico</h3>
<form onSubmit={handleAlertSubmit} className="space-y-4"> <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 /> <div className="relative">
<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 /> <Type className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Oggetto Email" value={alertForm.subject} onChange={e => setAlertForm({...alertForm, subject: e.target.value})} required />
</div>
<div className="relative">
<AlignLeft className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<textarea className="w-full border p-3 pl-10 rounded-xl h-24" placeholder="Testo Email..." value={alertForm.body} onChange={e => setAlertForm({...alertForm, body: e.target.value})} required />
</div>
<div className="grid grid-cols-2 gap-4"> <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">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><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>
@@ -655,14 +789,29 @@ export const SettingsPage: React.FC = () => {
setGlobalSettings(updated); setGlobalSettings(updated);
setShowSmtpModal(false); setShowSmtpModal(false);
}} className="space-y-4"> }} 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="relative">
<Server className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="Host (smtp.gmail.com)" value={globalSettings.smtpConfig.host} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, host: e.target.value}})} required />
</div>
<div className="grid grid-cols-2 gap-4"> <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 /> <div className="relative">
<Hash className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input type="number" className="w-full border p-3 pl-10 rounded-xl" placeholder="Porta (465)" value={globalSettings.smtpConfig.port} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, port: parseInt(e.target.value)}})} required />
</div>
<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> <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> </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 /> <div className="relative">
<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 /> <UserIcon className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<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}})} /> <input className="w-full border p-3 pl-10 rounded-xl" placeholder="Utente/Email" value={globalSettings.smtpConfig.user} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, user: e.target.value}})} required />
</div>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" type="password" placeholder="Password" value={globalSettings.smtpConfig.pass} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, pass: e.target.value}})} required />
</div>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5 text-slate-400" />
<input className="w-full border p-3 pl-10 rounded-xl" placeholder="From Email (Opzionale)" value={globalSettings.smtpConfig.fromEmail || ''} onChange={e => setGlobalSettings({...globalSettings, smtpConfig: {...globalSettings.smtpConfig!, fromEmail: e.target.value}})} />
</div>
{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>} {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>}