feat: Refactor API services and UI components

This commit refactors the API service to use a consistent `fetch` wrapper for all requests, improving error handling and authorization logic. It also updates UI components to reflect changes in API endpoints and data structures, particularly around notifications and extraordinary expenses. Docker configurations are removed as they are no longer relevant for this stage of development.
This commit is contained in:
2025-12-09 23:12:47 +01:00
parent 38a3402deb
commit 2a6da489aa
9 changed files with 449 additions and 920 deletions

View File

@@ -15,8 +15,9 @@ export const Layout: React.FC = () => {
const [showCondoDropdown, setShowCondoDropdown] = useState(false);
const [settings, setSettings] = useState<AppSettings | null>(null);
// Notice Modal State
// Notifications
const [activeNotice, setActiveNotice] = useState<Notice | null>(null);
const [hasNewExpenses, setHasNewExpenses] = useState(false);
const fetchContext = async () => {
// Fetch global settings to check features
@@ -28,23 +29,32 @@ export const Layout: React.FC = () => {
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
setCondos(list);
}
} catch(e) { console.error("Error fetching settings", e); }
const active = await CondoService.getActiveCondo();
setActiveCondo(active);
// Check for notices for User
// Check for notices & expenses for User
if (!isAdmin && active && user) {
try {
// 1. Check Notices
const unread = await CondoService.getUnreadNoticesForUser(user.id, active.id);
if (unread.length > 0) {
// Show the most recent unread notice
setActiveNotice(unread[0]);
}
// 2. Check New Extraordinary Expenses
const myExpenses = await CondoService.getMyExpenses();
const lastViewed = localStorage.getItem('lastViewedExpensesTime');
const lastViewedTime = lastViewed ? parseInt(lastViewed) : 0;
// Check if any expense was created AFTER the last visit
const hasNew = myExpenses.some((e: any) => new Date(e.createdAt).getTime() > lastViewedTime);
setHasNewExpenses(hasNew);
} catch(e) {}
}
};
@@ -52,10 +62,14 @@ export const Layout: React.FC = () => {
useEffect(() => {
fetchContext();
// Listen for updates from Settings
const handleCondoUpdate = () => fetchContext();
window.addEventListener('condo-updated', handleCondoUpdate);
return () => window.removeEventListener('condo-updated', handleCondoUpdate);
// Listen for updates from Settings or Expense views
const handleUpdate = () => fetchContext();
window.addEventListener('condo-updated', handleUpdate);
window.addEventListener('expenses-viewed', handleUpdate); // Listen for manual trigger when user views page
return () => {
window.removeEventListener('condo-updated', handleUpdate);
window.removeEventListener('expenses-viewed', handleUpdate);
};
}, [isAdmin]);
const handleCondoSwitch = (condoId: string) => {
@@ -239,8 +253,15 @@ export const Layout: React.FC = () => {
{/* New Extraordinary Expenses Link - Conditional */}
{settings?.features.extraordinaryExpenses && (
<NavLink to="/extraordinary" className={navClass} onClick={closeMenu}>
<Briefcase className="w-5 h-5" />
<span className="font-medium">{isAdmin ? 'Spese Straordinarie' : 'Le Mie Spese Extra'}</span>
<div className="flex items-center justify-between w-full">
<div className="flex items-center gap-3">
<Briefcase className="w-5 h-5" />
<span className="font-medium">{isAdmin ? 'Spese Straordinarie' : 'Le Mie Spese Extra'}</span>
</div>
{hasNewExpenses && (
<span className="bg-red-500 w-2.5 h-2.5 rounded-full animate-pulse"></span>
)}
</div>
</NavLink>
)}