Update Settings.tsx
This commit is contained in:
@@ -7,7 +7,7 @@ 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, Calendar, List, UserCog, Mail, Power, MapPin, CreditCard,
|
Eye, Calendar, List, UserCog, Mail, Power, MapPin, CreditCard,
|
||||||
ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send
|
ToggleLeft, ToggleRight, LayoutGrid, PieChart, Users, Send, Cloud, HardDrive
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
export const SettingsPage: React.FC = () => {
|
export const SettingsPage: React.FC = () => {
|
||||||
@@ -16,7 +16,7 @@ export const SettingsPage: React.FC = () => {
|
|||||||
const isPrivileged = currentUser?.role === 'admin' || currentUser?.role === 'poweruser';
|
const isPrivileged = currentUser?.role === 'admin' || currentUser?.role === 'poweruser';
|
||||||
|
|
||||||
// Tab configuration
|
// Tab configuration
|
||||||
type TabType = 'profile' | 'features' | 'general' | 'condos' | 'families' | 'users' | 'notices' | 'alerts';
|
type TabType = 'profile' | 'features' | 'general' | 'condos' | 'families' | 'users' | 'notices' | 'alerts' | 'storage';
|
||||||
|
|
||||||
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);
|
||||||
@@ -240,7 +240,7 @@ export const SettingsPage: React.FC = () => {
|
|||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
await CondoService.updateSettings(globalSettings);
|
await CondoService.updateSettings(globalSettings);
|
||||||
setSuccessMsg('Funzionalità salvate!');
|
setSuccessMsg('Configurazione salvata!');
|
||||||
setTimeout(() => setSuccessMsg(''), 3000);
|
setTimeout(() => setSuccessMsg(''), 3000);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
@@ -282,6 +282,18 @@ export const SettingsPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleStorageSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!globalSettings) return;
|
||||||
|
setSaving(true);
|
||||||
|
try {
|
||||||
|
await CondoService.updateSettings(globalSettings);
|
||||||
|
setSuccessMsg('Configurazione Storage salvata!');
|
||||||
|
setTimeout(() => setSuccessMsg(''), 3000);
|
||||||
|
} catch (e) { console.error(e); }
|
||||||
|
finally { setSaving(false); }
|
||||||
|
};
|
||||||
|
|
||||||
const handleNewYear = async () => {
|
const handleNewYear = async () => {
|
||||||
if (!globalSettings) return;
|
if (!globalSettings) return;
|
||||||
const nextYear = globalSettings.currentYear + 1;
|
const nextYear = globalSettings.currentYear + 1;
|
||||||
@@ -300,7 +312,7 @@ export const SettingsPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// CRUD Handlers
|
// CRUD Handlers (omitted repeated code for brevity, logic is same as before)
|
||||||
const openAddCondoModal = () => {
|
const openAddCondoModal = () => {
|
||||||
setEditingCondo(null);
|
setEditingCondo(null);
|
||||||
setCondoForm({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100, dueDay: 10 });
|
setCondoForm({ name: '', address: '', streetNumber: '', city: '', province: '', zipCode: '', notes: '', paypalClientId: '', defaultMonthlyQuota: 100, dueDay: 10 });
|
||||||
@@ -572,7 +584,8 @@ export const SettingsPage: React.FC = () => {
|
|||||||
tabs.push({ id: 'notices', label: 'Bacheca', icon: <Megaphone className="w-4 h-4"/> });
|
tabs.push({ id: 'notices', label: 'Bacheca', icon: <Megaphone className="w-4 h-4"/> });
|
||||||
}
|
}
|
||||||
tabs.push(
|
tabs.push(
|
||||||
{ id: 'alerts', label: 'Avvisi Email', icon: <Bell className="w-4 h-4"/> }
|
{ id: 'alerts', label: 'Avvisi Email', icon: <Bell className="w-4 h-4"/> },
|
||||||
|
{ id: 'storage', label: 'Cloud & Storage', icon: <HardDrive className="w-4 h-4"/> }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,6 +661,10 @@ export const SettingsPage: React.FC = () => {
|
|||||||
<div><p className="font-bold text-slate-800">Gestione Tickets</p><p className="text-sm text-slate-500">Abilita il sistema di segnalazione guasti.</p></div>
|
<div><p className="font-bold text-slate-800">Gestione Tickets</p><p className="text-sm text-slate-500">Abilita il sistema di segnalazione guasti.</p></div>
|
||||||
<button type="button" onClick={() => toggleFeature('tickets')} className={`${globalSettings.features.tickets ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.tickets ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
|
<button type="button" onClick={() => toggleFeature('tickets')} className={`${globalSettings.features.tickets ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.tickets ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
|
||||||
|
<div><p className="font-bold text-slate-800">Gestione Documenti</p><p className="text-sm text-slate-500">Archivio digitale con tag e upload.</p></div>
|
||||||
|
<button type="button" onClick={() => toggleFeature('documents')} className={`${globalSettings.features.documents ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.documents ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
|
||||||
|
</div>
|
||||||
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
|
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
|
||||||
<div><p className="font-bold text-slate-800">Pagamenti PayPal</p><p className="text-sm text-slate-500">Permetti ai condomini di pagare online.</p></div>
|
<div><p className="font-bold text-slate-800">Pagamenti PayPal</p><p className="text-sm text-slate-500">Permetti ai condomini di pagare online.</p></div>
|
||||||
<button type="button" onClick={() => toggleFeature('payPal')} className={`${globalSettings.features.payPal ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.payPal ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
|
<button type="button" onClick={() => toggleFeature('payPal')} className={`${globalSettings.features.payPal ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.payPal ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
|
||||||
@@ -664,8 +681,6 @@ export const SettingsPage: React.FC = () => {
|
|||||||
<div><p className="font-bold text-slate-800">Spese Straordinarie</p><p className="text-sm text-slate-500">Gestione lavori e preventivi.</p></div>
|
<div><p className="font-bold text-slate-800">Spese Straordinarie</p><p className="text-sm text-slate-500">Gestione lavori e preventivi.</p></div>
|
||||||
<button type="button" onClick={() => toggleFeature('extraordinaryExpenses')} className={`${globalSettings.features.extraordinaryExpenses !== false ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.extraordinaryExpenses !== false ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
|
<button type="button" onClick={() => toggleFeature('extraordinaryExpenses')} className={`${globalSettings.features.extraordinaryExpenses !== false ? 'bg-green-500' : 'bg-slate-300'} relative inline-flex h-6 w-11 items-center rounded-full transition-colors`}><span className={`${globalSettings.features.extraordinaryExpenses !== false ? 'translate-x-6' : 'translate-x-1'} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}/></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* NEW TOGGLE for Condo Financials View */}
|
|
||||||
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
|
<div className="flex items-center justify-between p-4 bg-slate-50 rounded-xl border border-slate-100">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-slate-800">Visualizza Spese Condominiali agli Utenti</p>
|
<p className="font-bold text-slate-800">Visualizza Spese Condominiali agli Utenti</p>
|
||||||
@@ -682,6 +697,67 @@ export const SettingsPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* STORAGE CONFIG TAB */}
|
||||||
|
{isPrivileged && activeTab === 'storage' && globalSettings?.storageConfig && (
|
||||||
|
<div className="animate-fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-6 max-w-2xl">
|
||||||
|
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Cloud className="w-5 h-5 text-blue-600" /> Configurazione Storage</h3>
|
||||||
|
<p className="text-sm text-slate-500 mb-6">Scegli dove salvare i documenti caricati.</p>
|
||||||
|
|
||||||
|
<form onSubmit={handleStorageSubmit} className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-bold text-slate-700 mb-2 block">Provider Attivo</label>
|
||||||
|
<select
|
||||||
|
className="w-full border p-2.5 rounded-lg bg-slate-50 font-medium text-slate-700"
|
||||||
|
value={globalSettings.storageConfig.provider}
|
||||||
|
onChange={e => setGlobalSettings({
|
||||||
|
...globalSettings,
|
||||||
|
storageConfig: { ...globalSettings.storageConfig!, provider: e.target.value as any }
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<option value="local_db">Database Locale (Solo Demo)</option>
|
||||||
|
<option value="s3">AWS S3 / Compatible</option>
|
||||||
|
<option value="google_drive">Google Drive</option>
|
||||||
|
<option value="dropbox">Dropbox</option>
|
||||||
|
<option value="onedrive">OneDrive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{globalSettings.storageConfig.provider === 'local_db' && (
|
||||||
|
<div className="bg-amber-50 text-amber-800 p-4 rounded-lg text-sm border border-amber-200">
|
||||||
|
<p className="font-bold">Modalità Demo Attiva</p>
|
||||||
|
<p>I file vengono salvati direttamente nel database (Base64). Non raccomandato per produzione o file grandi.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{globalSettings.storageConfig.provider === 's3' && (
|
||||||
|
<div className="space-y-3 border-l-4 border-blue-500 pl-4">
|
||||||
|
<h4 className="font-bold text-sm text-slate-700">Configurazione S3</h4>
|
||||||
|
<input placeholder="Bucket Name" className="w-full border p-2 rounded" value={globalSettings.storageConfig.bucket || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, bucket: e.target.value}})}/>
|
||||||
|
<input placeholder="Region (es. eu-central-1)" className="w-full border p-2 rounded" value={globalSettings.storageConfig.region || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, region: e.target.value}})}/>
|
||||||
|
<input placeholder="Access Key ID" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiKey || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiKey: e.target.value}})}/>
|
||||||
|
<input type="password" placeholder="Secret Access Key" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiSecret || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiSecret: e.target.value}})}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(globalSettings.storageConfig.provider === 'google_drive' || globalSettings.storageConfig.provider === 'dropbox' || globalSettings.storageConfig.provider === 'onedrive') && (
|
||||||
|
<div className="space-y-3 border-l-4 border-purple-500 pl-4">
|
||||||
|
<h4 className="font-bold text-sm text-slate-700">Autenticazione API</h4>
|
||||||
|
<p className="text-xs text-slate-500">Inserisci le credenziali dell'applicazione sviluppatore.</p>
|
||||||
|
<input placeholder="Client ID / App Key" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiKey || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiKey: e.target.value}})}/>
|
||||||
|
<input type="password" placeholder="Client Secret / App Secret" className="w-full border p-2 rounded" value={globalSettings.storageConfig.apiSecret || ''} onChange={e => setGlobalSettings({...globalSettings, storageConfig: {...globalSettings.storageConfig!, apiSecret: e.target.value}})}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="pt-2 flex justify-between items-center">
|
||||||
|
<span className="text-green-600 font-medium">{successMsg}</span>
|
||||||
|
<button type="submit" disabled={saving} className="bg-blue-600 text-white px-6 py-2.5 rounded-lg font-medium hover:bg-blue-700 flex gap-2">
|
||||||
|
<Save className="w-4 h-4" /> Salva Impostazioni
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* GENERAL TAB */}
|
{/* GENERAL TAB */}
|
||||||
{isPrivileged && activeTab === 'general' && (
|
{isPrivileged && activeTab === 'general' && (
|
||||||
<div className="space-y-6 animate-fade-in">
|
<div className="space-y-6 animate-fade-in">
|
||||||
@@ -837,12 +913,13 @@ export const SettingsPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Condo Modal (Aggiornato con dueDay) */}
|
{/* Condo Modal */}
|
||||||
{showCondoModal && (
|
{showCondoModal && (
|
||||||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
|
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4 backdrop-blur-sm">
|
||||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200 overflow-y-auto max-h-[90vh]">
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg p-6 animate-in fade-in zoom-in duration-200 overflow-y-auto max-h-[90vh]">
|
||||||
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingCondo ? 'Modifica Condominio' : 'Nuovo Condominio'}</h3>
|
<h3 className="font-bold text-lg mb-4 text-slate-800">{editingCondo ? 'Modifica Condominio' : 'Nuovo Condominio'}</h3>
|
||||||
<form onSubmit={handleCondoSubmit} className="space-y-4">
|
<form onSubmit={handleCondoSubmit} className="space-y-4">
|
||||||
|
{/* Form fields same as before... omitted for brevity */}
|
||||||
<div>
|
<div>
|
||||||
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome Condominio" value={condoForm.name} onChange={e => setCondoForm({...condoForm, name: e.target.value})} required />
|
<input className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="Nome Condominio" value={condoForm.name} onChange={e => setCondoForm({...condoForm, name: e.target.value})} required />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user