feat: Introduce app feature flags
This commit refactors the application settings to include a new `AppFeatures` interface. This allows for granular control over which features are enabled for the application. The `AppFeatures` object includes boolean flags for: - `multiCondo`: Enables or disables the multi-condominium management feature. - `tickets`: Placeholder for future ticket system integration. - `payPal`: Enables or disables PayPal payment gateway integration. - `notices`: Enables or disables the display and management of notices. These flags are now fetched and stored in the application state, influencing UI elements and logic across various pages to conditionally render features based on their enabled status. For example, the multi-condo selection in `Layout.tsx` and the notice display in `FamilyList.tsx` are now gated by these feature flags. The `FamilyDetail.tsx` page also uses the `payPal` flag to conditionally enable the PayPal payment option. The `SettingsPage.tsx` has been updated to include a new 'features' tab for managing these flags.
This commit is contained in:
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { NavLink, Outlet } from 'react-router-dom';
|
||||
import { Users, Settings, Building, LogOut, Menu, X, ChevronDown, Check, LayoutDashboard, Megaphone, Info, AlertTriangle, Hammer, Calendar, MessageSquareWarning } from 'lucide-react';
|
||||
import { CondoService } from '../services/mockDb';
|
||||
import { Condo, Notice } from '../types';
|
||||
import { Condo, Notice, AppSettings } from '../types';
|
||||
|
||||
export const Layout: React.FC = () => {
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
@@ -13,25 +13,43 @@ export const Layout: React.FC = () => {
|
||||
const [condos, setCondos] = useState<Condo[]>([]);
|
||||
const [activeCondo, setActiveCondo] = useState<Condo | undefined>(undefined);
|
||||
const [showCondoDropdown, setShowCondoDropdown] = useState(false);
|
||||
const [settings, setSettings] = useState<AppSettings | null>(null);
|
||||
|
||||
// Notice Modal State
|
||||
const [activeNotice, setActiveNotice] = useState<Notice | null>(null);
|
||||
|
||||
const fetchContext = async () => {
|
||||
if (isAdmin) {
|
||||
const list = await CondoService.getCondos();
|
||||
setCondos(list);
|
||||
}
|
||||
// Fetch global settings to check features
|
||||
try {
|
||||
const globalSettings = await CondoService.getSettings();
|
||||
setSettings(globalSettings);
|
||||
|
||||
if (isAdmin && globalSettings.features.multiCondo) {
|
||||
const list = await CondoService.getCondos();
|
||||
setCondos(list);
|
||||
} else if (isAdmin) {
|
||||
// If multi-condo disabled, just get the one (which acts as active)
|
||||
const list = await CondoService.getCondos();
|
||||
setCondos(list); // Store list anyway, though dropdown will be hidden
|
||||
}
|
||||
} catch(e) { console.error("Error fetching settings", e); }
|
||||
|
||||
const active = await CondoService.getActiveCondo();
|
||||
setActiveCondo(active);
|
||||
|
||||
// Check for notices for User (not admin, to avoid spamming admin managing multiple condos)
|
||||
// Check for notices for User
|
||||
// ONLY if notices feature is enabled (which we check inside logic or rely on settings state)
|
||||
// However, `getSettings` is async. For simplicity, we fetch notices and if feature disabled at backend/UI level, it's fine.
|
||||
// Ideally we check `settings?.features.notices` but `settings` might not be set yet.
|
||||
// We'll rely on the UI hiding it, but fetching it doesn't hurt.
|
||||
if (!isAdmin && active && user) {
|
||||
const unread = await CondoService.getUnreadNoticesForUser(user.id, active.id);
|
||||
if (unread.length > 0) {
|
||||
// Show the most recent unread notice
|
||||
setActiveNotice(unread[0]);
|
||||
}
|
||||
try {
|
||||
const unread = await CondoService.getUnreadNoticesForUser(user.id, active.id);
|
||||
if (unread.length > 0) {
|
||||
// Show the most recent unread notice
|
||||
setActiveNotice(unread[0]);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -76,11 +94,14 @@ export const Layout: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Check if notices are actually enabled before showing modal
|
||||
const showNotice = activeNotice && settings?.features.notices;
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-slate-50 overflow-hidden">
|
||||
|
||||
{/* Active Notice Modal */}
|
||||
{activeNotice && (
|
||||
{showNotice && activeNotice && (
|
||||
<div className="fixed inset-0 bg-black/60 z-[100] flex items-center justify-center p-4 backdrop-blur-sm animate-in fade-in duration-300">
|
||||
<div className="bg-white rounded-2xl shadow-2xl max-w-md w-full overflow-hidden transform transition-all scale-100">
|
||||
<div className={`p-6 ${activeNotice.type === 'warning' ? 'bg-amber-50' : 'bg-blue-50'} border-b border-slate-100 flex items-start gap-4`}>
|
||||
@@ -147,8 +168,8 @@ export const Layout: React.FC = () => {
|
||||
<h1 className="font-bold text-xl text-slate-800 tracking-tight">CondoPay</h1>
|
||||
</div>
|
||||
|
||||
{/* Condo Switcher (Admin Only) */}
|
||||
{isAdmin && (
|
||||
{/* Condo Switcher (Admin Only & MultiCondo Enabled) */}
|
||||
{isAdmin && settings?.features.multiCondo && (
|
||||
<div className="relative mt-2">
|
||||
<div className="flex items-center gap-1.5 mb-2 text-xs font-bold text-slate-400 uppercase tracking-wider">
|
||||
<LayoutDashboard className="w-3 h-3" />
|
||||
@@ -178,7 +199,8 @@ export const Layout: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isAdmin && activeCondo && (
|
||||
{/* Static info if not multi-condo or not admin */}
|
||||
{(!isAdmin || (isAdmin && !settings?.features.multiCondo)) && activeCondo && (
|
||||
<div className="px-3 py-2 bg-slate-50 border border-slate-100 rounded-lg text-sm text-slate-500 truncate">
|
||||
{activeCondo.name}
|
||||
</div>
|
||||
@@ -192,7 +214,7 @@ export const Layout: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Mobile Condo Switcher */}
|
||||
{isAdmin && (
|
||||
{isAdmin && settings?.features.multiCondo && (
|
||||
<div className="lg:hidden px-4 py-2 border-b border-slate-100">
|
||||
<p className="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-2">
|
||||
<LayoutDashboard className="w-3 h-3" />
|
||||
@@ -218,10 +240,13 @@ export const Layout: React.FC = () => {
|
||||
<span className="font-medium">Famiglie</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink to="/tickets" className={navClass} onClick={closeMenu}>
|
||||
<MessageSquareWarning className="w-5 h-5" />
|
||||
<span className="font-medium">Segnalazioni</span>
|
||||
</NavLink>
|
||||
{/* Hide Tickets if disabled */}
|
||||
{settings?.features.tickets && (
|
||||
<NavLink to="/tickets" className={navClass} onClick={closeMenu}>
|
||||
<MessageSquareWarning className="w-5 h-5" />
|
||||
<span className="font-medium">Segnalazioni</span>
|
||||
</NavLink>
|
||||
)}
|
||||
|
||||
<NavLink to="/settings" className={navClass} onClick={closeMenu}>
|
||||
<Settings className="w-5 h-5" />
|
||||
|
||||
Reference in New Issue
Block a user