From 0f82df517b6ac301d95267e038a5e8be96b6f0d1 Mon Sep 17 00:00:00 2001 From: frakarr Date: Sun, 7 Dec 2025 23:30:06 +0100 Subject: [PATCH] feat: Enhance admin access and UI for privileged users Grant 'poweruser' role access to administrative settings and sections. Update UI to reflect elevated privileges and adjust default tab navigation for these users. Modify server-side access control to include 'poweruser' alongside 'admin' for privileged routes. --- pages/Settings.tsx | 134 +++++++++++++++++++++++++++++++-------------- server/server.js | 5 +- 2 files changed, 97 insertions(+), 42 deletions(-) diff --git a/pages/Settings.tsx b/pages/Settings.tsx index dcfe311..c6d84fb 100644 --- a/pages/Settings.tsx +++ b/pages/Settings.tsx @@ -6,12 +6,13 @@ import { Save, Building, Coins, Plus, Pencil, Trash2, X, CalendarCheck, AlertTri export const SettingsPage: React.FC = () => { const currentUser = CondoService.getCurrentUser(); - const isAdmin = currentUser?.role === 'admin'; + const isSuperAdmin = currentUser?.role === 'admin'; + const isPrivileged = currentUser?.role === 'admin' || currentUser?.role === 'poweruser'; // Tab configuration - type TabType = 'profile' | 'features' | 'general' | 'condos' | 'families' | 'users' | 'notices' | 'alerts' | 'smtp'; + type TabType = 'profile' | 'features' | 'general' | 'condos' | 'families' | 'users' | 'notices' | 'alerts'; - const [activeTab, setActiveTab] = useState(isAdmin ? 'general' : 'profile'); + const [activeTab, setActiveTab] = useState(isPrivileged ? 'general' : 'profile'); const [loading, setLoading] = useState(true); // Profile State @@ -87,6 +88,9 @@ export const SettingsPage: React.FC = () => { sendHour: 9, active: true }); + + // SMTP Modal State + const [showSmtpModal, setShowSmtpModal] = useState(false); // Notices (Bacheca) State const [notices, setNotices] = useState([]); @@ -116,7 +120,7 @@ export const SettingsPage: React.FC = () => { useEffect(() => { const fetchData = async () => { try { - if (isAdmin) { + if (isPrivileged) { // First fetch global/structural data const condoList = await CondoService.getCondos(); const activeC = await CondoService.getActiveCondo(); @@ -165,7 +169,7 @@ export const SettingsPage: React.FC = () => { } }; fetchData(); - }, [isAdmin]); + }, [isPrivileged]); // --- Profile Handlers --- const handleProfileSubmit = async (e: React.FormEvent) => { @@ -223,7 +227,7 @@ export const SettingsPage: React.FC = () => { try { await CondoService.updateSettings(globalSettings); setSuccessMsg('Configurazione SMTP salvata!'); - setTimeout(() => setSuccessMsg(''), 3000); + setTimeout(() => { setSuccessMsg(''); setShowSmtpModal(false); }, 2000); } catch (e) { console.error(e); } finally { @@ -492,9 +496,15 @@ export const SettingsPage: React.FC = () => { const tabs: {id: TabType, label: string, icon: React.ReactNode}[] = [ { id: 'profile', label: 'Profilo', icon: }, ]; - if (isAdmin) { + + // "Funzionalità" visible ONLY to SuperAdmin + if (isSuperAdmin) { + tabs.push({ id: 'features', label: 'Funzionalità', icon: }); + } + + // Other tabs visible to Privileged (Admin + PowerUser) + if (isPrivileged) { tabs.push( - { id: 'features', label: 'Funzionalità', icon: }, { id: 'general', label: 'Condominio', icon: } ); @@ -512,8 +522,7 @@ export const SettingsPage: React.FC = () => { } tabs.push( - { id: 'alerts', label: 'Avvisi Email', icon: }, - { id: 'smtp', label: 'SMTP', icon: } + { id: 'alerts', label: 'Avvisi Email', icon: } ); } @@ -560,8 +569,8 @@ export const SettingsPage: React.FC = () => { )} - {/* Features Tab */} - {isAdmin && activeTab === 'features' && globalSettings && ( + {/* Features Tab (SUPER ADMIN ONLY) */} + {isSuperAdmin && activeTab === 'features' && globalSettings && (

@@ -627,7 +636,7 @@ export const SettingsPage: React.FC = () => { )} {/* General Tab */} - {isAdmin && activeTab === 'general' && ( + {isPrivileged && activeTab === 'general' && (
{!activeCondo ? (
Nessun condominio selezionato.
@@ -688,7 +697,7 @@ export const SettingsPage: React.FC = () => { )} {/* Condos List Tab */} - {isAdmin && activeTab === 'condos' && ( + {isPrivileged && activeTab === 'condos' && (

I Tuoi Condomini

@@ -714,7 +723,7 @@ export const SettingsPage: React.FC = () => { )} {/* Families Tab */} - {isAdmin && activeTab === 'families' && ( + {isPrivileged && activeTab === 'families' && (
{!activeCondo ? (
@@ -764,7 +773,7 @@ export const SettingsPage: React.FC = () => { )} {/* Users Tab */} - {isAdmin && activeTab === 'users' && ( + {isPrivileged && activeTab === 'users' && (
+
+

Avvisi Automatici

Configura email automatiche per scadenze.

@@ -875,26 +896,6 @@ export const SettingsPage: React.FC = () => {
)} - {/* SMTP TAB */} - {isAdmin && activeTab === 'smtp' && ( -
-

Configurazione SMTP

-
-
-
setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), host: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/>
-
setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), port: parseInt(e.target.value)} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/>
-
-
-
setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), user: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/>
-
setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), pass: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/>
-
-
setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), fromEmail: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/>
-
setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), secure: e.target.checked} as any} : null)} className="w-4 h-4"/>
-
{successMsg}
-
-
- )} - {/* ALERT MODAL (Existing) */} {showAlertModal && (
@@ -931,6 +932,59 @@ export const SettingsPage: React.FC = () => {
)} + + {/* SMTP CONFIG MODAL */} + {showSmtpModal && ( +
+
+
+

+ Configurazione SMTP +

+ +
+ +
+
+
+ + setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), host: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="smtp.gmail.com"/> +
+
+ + setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), port: parseInt(e.target.value)} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="587"/> +
+
+
+
+ + setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), user: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/> +
+
+ + setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), pass: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700"/> +
+
+
+ + setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), fromEmail: e.target.value} as any} : null)} className="w-full border p-2.5 rounded-lg text-slate-700" placeholder="no-reply@condominio.it"/> +
+
+ setGlobalSettings(prev => prev ? {...prev, smtpConfig: {...(prev.smtpConfig || {}), secure: e.target.checked} as any} : null)} className="w-4 h-4 text-blue-600"/> + +
+ +
+ {successMsg} +
+ + +
+
+
+
+
+ )} {/* NOTICE MODAL (Existing) */} {showNoticeModal && ( @@ -1194,4 +1248,4 @@ export const SettingsPage: React.FC = () => { )}
); -}; \ No newline at end of file +}; diff --git a/server/server.js b/server/server.js index c19835b..39c0ecd 100644 --- a/server/server.js +++ b/server/server.js @@ -89,11 +89,12 @@ const authenticateToken = (req, res, next) => { }; const requireAdmin = (req, res, next) => { - if (req.user && req.user.role === 'admin') { + // Allow both 'admin' and 'poweruser' to access administrative routes + if (req.user && (req.user.role === 'admin' || req.user.role === 'poweruser')) { next(); } else { console.warn(`Access denied for user ${req.user?.email} with role ${req.user?.role}`); - res.status(403).json({ message: 'Access denied: Admins only' }); + res.status(403).json({ message: 'Access denied: Privileged users only' }); } };