import { Family, Payment, AppSettings, User, AlertDefinition, Condo, Notice, NoticeRead } from '../types'; // --- CONFIGURATION TOGGLE --- const FORCE_LOCAL_DB = true; const API_URL = '/api'; const STORAGE_KEYS = { SETTINGS: 'condo_settings', CONDOS: 'condo_list', ACTIVE_CONDO_ID: 'condo_active_id', FAMILIES: 'condo_families', PAYMENTS: 'condo_payments', TOKEN: 'condo_auth_token', USER: 'condo_user_info', USERS_LIST: 'condo_users_list', ALERTS: 'condo_alerts_def', NOTICES: 'condo_notices', NOTICES_READ: 'condo_notices_read' }; const getLocal = (key: string, defaultVal: T): T => { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : defaultVal; } catch { return defaultVal; } }; const setLocal = (key: string, val: any) => { localStorage.setItem(key, JSON.stringify(val)); }; const getAuthHeaders = () => { const token = localStorage.getItem(STORAGE_KEYS.TOKEN); return token ? { 'Authorization': `Bearer ${token}` } : {}; }; export const CondoService = { // --- CONDO CONTEXT MANAGEMENT --- getActiveCondoId: (): string | null => { return localStorage.getItem(STORAGE_KEYS.ACTIVE_CONDO_ID); }, setActiveCondo: (condoId: string) => { localStorage.setItem(STORAGE_KEYS.ACTIVE_CONDO_ID, condoId); window.location.reload(); // Simple way to refresh context }, getCondos: async (): Promise => { if (FORCE_LOCAL_DB) { return getLocal(STORAGE_KEYS.CONDOS, []); } return getLocal(STORAGE_KEYS.CONDOS, []); }, getActiveCondo: async (): Promise => { const condos = await CondoService.getCondos(); const activeId = CondoService.getActiveCondoId(); if (!activeId && condos.length > 0) { CondoService.setActiveCondo(condos[0].id); return condos[0]; } return condos.find(c => c.id === activeId); }, saveCondo: async (condo: Condo): Promise => { if (FORCE_LOCAL_DB) { const condos = getLocal(STORAGE_KEYS.CONDOS, []); const index = condos.findIndex(c => c.id === condo.id); let newCondos; if (index >= 0) { newCondos = condos.map(c => c.id === condo.id ? condo : c); } else { newCondos = [...condos, { ...condo, id: condo.id || crypto.randomUUID() }]; } setLocal(STORAGE_KEYS.CONDOS, newCondos); if (newCondos.length === 1) { localStorage.setItem(STORAGE_KEYS.ACTIVE_CONDO_ID, newCondos[0].id); } return condo; } return condo; }, deleteCondo: async (id: string) => { if (FORCE_LOCAL_DB) { const condos = getLocal(STORAGE_KEYS.CONDOS, []); setLocal(STORAGE_KEYS.CONDOS, condos.filter(c => c.id !== id)); if (localStorage.getItem(STORAGE_KEYS.ACTIVE_CONDO_ID) === id) { localStorage.removeItem(STORAGE_KEYS.ACTIVE_CONDO_ID); window.location.reload(); } } }, // --- NOTICES (BACHECA) --- getNotices: async (condoId?: string): Promise => { const allNotices = getLocal(STORAGE_KEYS.NOTICES, []); if (!condoId) return allNotices; return allNotices.filter(n => n.condoId === condoId).sort((a,b) => new Date(b.date).getTime() - new Date(a.date).getTime()); }, saveNotice: async (notice: Notice): Promise => { const notices = getLocal(STORAGE_KEYS.NOTICES, []); const index = notices.findIndex(n => n.id === notice.id); let newNotices; if (index >= 0) { newNotices = notices.map(n => n.id === notice.id ? notice : n); } else { newNotices = [...notices, { ...notice, id: notice.id || crypto.randomUUID(), date: notice.date || new Date().toISOString() }]; } setLocal(STORAGE_KEYS.NOTICES, newNotices); return notice; }, deleteNotice: async (id: string) => { const notices = getLocal(STORAGE_KEYS.NOTICES, []); setLocal(STORAGE_KEYS.NOTICES, notices.filter(n => n.id !== id)); }, markNoticeAsRead: async (noticeId: string, userId: string) => { const reads = getLocal(STORAGE_KEYS.NOTICES_READ, []); if (!reads.find(r => r.noticeId === noticeId && r.userId === userId)) { reads.push({ noticeId, userId, readAt: new Date().toISOString() }); setLocal(STORAGE_KEYS.NOTICES_READ, reads); } }, getNoticeReadStatus: async (noticeId: string): Promise => { const reads = getLocal(STORAGE_KEYS.NOTICES_READ, []); return reads.filter(r => r.noticeId === noticeId); }, getUnreadNoticesForUser: async (userId: string, condoId: string): Promise => { const notices = await CondoService.getNotices(condoId); const reads = getLocal(STORAGE_KEYS.NOTICES_READ, []); const userReadIds = reads.filter(r => r.userId === userId).map(r => r.noticeId); return notices.filter(n => n.active && !userReadIds.includes(n.id)); }, // --- AUTH --- login: async (email, password) => { if (FORCE_LOCAL_DB) { await new Promise(resolve => setTimeout(resolve, 600)); const role = email.includes('admin') || email === 'fcarra79@gmail.com' ? 'admin' : 'user'; const mockUser: User = { id: 'local-user-' + Math.random().toString(36).substr(2, 9), email, name: email.split('@')[0], role: role as any, familyId: role === 'admin' ? null : 'f1', // simple logic receiveAlerts: true }; localStorage.setItem(STORAGE_KEYS.TOKEN, 'mock-local-token-' + Date.now()); localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(mockUser)); // Post-login check: if user has a family, set active condo to that family's condo if (mockUser.familyId) { const families = getLocal(STORAGE_KEYS.FAMILIES, []); const fam = families.find(f => f.id === mockUser.familyId); if (fam) { localStorage.setItem(STORAGE_KEYS.ACTIVE_CONDO_ID, fam.condoId); } } return { token: localStorage.getItem(STORAGE_KEYS.TOKEN)!, user: mockUser }; } throw new Error("Remote login not implemented in this snippet update"); }, logout: () => { localStorage.removeItem(STORAGE_KEYS.TOKEN); localStorage.removeItem(STORAGE_KEYS.USER); // Do NOT clear active condo ID, nice for UX to remember where admin was window.location.href = '#/login'; }, getCurrentUser: (): User | null => { return getLocal(STORAGE_KEYS.USER, null); }, updateProfile: async (data: Partial & { password?: string }) => { const currentUser = getLocal(STORAGE_KEYS.USER, null); if (!currentUser) throw new Error("Not logged in"); const updatedUser = { ...currentUser, ...data }; delete (updatedUser as any).password; setLocal(STORAGE_KEYS.USER, updatedUser); return { success: true, user: updatedUser }; }, // --- SETTINGS (Global) --- getSettings: async (): Promise => { return getLocal(STORAGE_KEYS.SETTINGS, { currentYear: new Date().getFullYear(), smtpConfig: { host: '', port: 587, user: '', pass: '', secure: false, fromEmail: '' } }); }, updateSettings: async (settings: AppSettings): Promise => { setLocal(STORAGE_KEYS.SETTINGS, settings); }, getAvailableYears: async (): Promise => { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); const settings = getLocal(STORAGE_KEYS.SETTINGS, { currentYear: new Date().getFullYear() } as AppSettings); const activeCondoId = CondoService.getActiveCondoId(); const families = getLocal(STORAGE_KEYS.FAMILIES, []); const condoFamilyIds = families.filter(f => f.condoId === activeCondoId).map(f => f.id); const relevantPayments = payments.filter(p => condoFamilyIds.includes(p.familyId)); const years = new Set(relevantPayments.map(p => p.forYear)); years.add(settings.currentYear); return Array.from(years).sort((a, b) => b - a); }, // --- FAMILIES --- getFamilies: async (): Promise => { const activeCondoId = CondoService.getActiveCondoId(); const allFamilies = getLocal(STORAGE_KEYS.FAMILIES, []); if (!activeCondoId) return []; return allFamilies.filter(f => f.condoId === activeCondoId); }, addFamily: async (familyData: Omit): Promise => { const families = getLocal(STORAGE_KEYS.FAMILIES, []); const activeCondoId = CondoService.getActiveCondoId(); if (!activeCondoId) throw new Error("Nessun condominio selezionato"); const newFamily = { ...familyData, id: crypto.randomUUID(), balance: 0, condoId: activeCondoId }; setLocal(STORAGE_KEYS.FAMILIES, [...families, newFamily]); return newFamily; }, updateFamily: async (family: Family): Promise => { const families = getLocal(STORAGE_KEYS.FAMILIES, []); const updated = families.map(f => f.id === family.id ? family : f); setLocal(STORAGE_KEYS.FAMILIES, updated); return family; }, deleteFamily: async (familyId: string): Promise => { const families = getLocal(STORAGE_KEYS.FAMILIES, []); setLocal(STORAGE_KEYS.FAMILIES, families.filter(f => f.id !== familyId)); }, // --- PAYMENTS --- getPaymentsByFamily: async (familyId: string): Promise => { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); return payments.filter(p => p.familyId === familyId); }, addPayment: async (payment: Omit): Promise => { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); const newPayment = { ...payment, id: crypto.randomUUID() }; setLocal(STORAGE_KEYS.PAYMENTS, [...payments, newPayment]); return newPayment; }, // --- USERS --- getUsers: async (): Promise => { return getLocal(STORAGE_KEYS.USERS_LIST, []); }, createUser: async (userData: any) => { const users = getLocal(STORAGE_KEYS.USERS_LIST, []); const newUser = { ...userData, id: crypto.randomUUID() }; delete newUser.password; setLocal(STORAGE_KEYS.USERS_LIST, [...users, newUser]); return { success: true, id: newUser.id }; }, updateUser: async (id: string, userData: any) => { const users = getLocal(STORAGE_KEYS.USERS_LIST, []); const updatedUsers = users.map(u => u.id === id ? { ...u, ...userData, id } : u); setLocal(STORAGE_KEYS.USERS_LIST, updatedUsers); return { success: true }; }, deleteUser: async (id: string) => { const users = getLocal(STORAGE_KEYS.USERS_LIST, []); setLocal(STORAGE_KEYS.USERS_LIST, users.filter(u => u.id !== id)); }, // --- ALERTS --- getAlerts: async (): Promise => { return getLocal(STORAGE_KEYS.ALERTS, []); }, saveAlert: async (alert: AlertDefinition): Promise => { const alerts = getLocal(STORAGE_KEYS.ALERTS, []); const existingIndex = alerts.findIndex(a => a.id === alert.id); let newAlerts; if (existingIndex >= 0) { newAlerts = alerts.map(a => a.id === alert.id ? alert : a); } else { newAlerts = [...alerts, { ...alert, id: alert.id || crypto.randomUUID() }]; } setLocal(STORAGE_KEYS.ALERTS, newAlerts); return alert; }, deleteAlert: async (id: string) => { const alerts = getLocal(STORAGE_KEYS.ALERTS, []); setLocal(STORAGE_KEYS.ALERTS, alerts.filter(a => a.id !== id)); }, // --- SEEDING --- seedPayments: () => { if (!FORCE_LOCAL_DB) return; const condos = getLocal(STORAGE_KEYS.CONDOS, []); if (condos.length === 0) { const demoCondos: Condo[] = [ { id: 'c1', name: 'Residenza i Pini', address: 'Via Roma 10, Milano', defaultMonthlyQuota: 100 }, { id: 'c2', name: 'Condominio Parco Vittoria', address: 'Corso Italia 50, Torino', defaultMonthlyQuota: 85 } ]; setLocal(STORAGE_KEYS.CONDOS, demoCondos); localStorage.setItem(STORAGE_KEYS.ACTIVE_CONDO_ID, 'c1'); } const families = getLocal(STORAGE_KEYS.FAMILIES, []); if (families.length === 0) { const demoFamilies: Family[] = [ { id: 'f1', condoId: 'c1', name: 'Rossi Mario', unitNumber: 'A1', contactEmail: 'rossi@email.com', balance: 0 }, { id: 'f2', condoId: 'c1', name: 'Bianchi Luigi', unitNumber: 'A2', contactEmail: 'bianchi@email.com', balance: 0 }, { id: 'f3', condoId: 'c2', name: 'Verdi Anna', unitNumber: 'B1', contactEmail: 'verdi@email.com', balance: 0 }, { id: 'f4', condoId: 'c2', name: 'Neri Paolo', unitNumber: 'B2', contactEmail: 'neri@email.com', balance: 0 }, ]; setLocal(STORAGE_KEYS.FAMILIES, demoFamilies); const demoUsers: User[] = [ { id: 'u1', email: 'admin@condo.it', name: 'Amministratore', role: 'admin', phone: '3331234567', familyId: null, receiveAlerts: true }, { id: 'u2', email: 'rossi@email.com', name: 'Mario Rossi', role: 'user', phone: '', familyId: 'f1', receiveAlerts: true } ]; setLocal(STORAGE_KEYS.USERS_LIST, demoUsers); } } };