import { Family, Payment, AppSettings, User, AuthResponse, AlertDefinition } from '../types'; // --- CONFIGURATION TOGGLE --- // TRUE = WORK MODE (Localstorage, no backend required) // FALSE = COMMIT MODE (Real API calls) const FORCE_LOCAL_DB = false; const API_URL = '/api'; const STORAGE_KEYS = { SETTINGS: 'condo_settings', FAMILIES: 'condo_families', PAYMENTS: 'condo_payments', TOKEN: 'condo_auth_token', USER: 'condo_user_info', USERS_LIST: 'condo_users_list', ALERTS: 'condo_alerts_def' }; 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)); }; // --- AUTH HELPERS --- const getAuthHeaders = () => { const token = localStorage.getItem(STORAGE_KEYS.TOKEN); return token ? { 'Authorization': `Bearer ${token}` } : {}; }; // --- SERVICE IMPLEMENTATION --- export const CondoService = { login: async (email, password) => { if (FORCE_LOCAL_DB) { // MOCK LOGIN for Preview await new Promise(resolve => setTimeout(resolve, 600)); // Fake delay // Allow any login, but give admin rights to specific email or generic 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: null, receiveAlerts: true }; localStorage.setItem(STORAGE_KEYS.TOKEN, 'mock-local-token-' + Date.now()); localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(mockUser)); return { token: localStorage.getItem(STORAGE_KEYS.TOKEN)!, user: mockUser }; } try { const res = await fetch(`${API_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); if (!res.ok) throw new Error('Login fallito'); const data = await res.json(); localStorage.setItem(STORAGE_KEYS.TOKEN, data.token); localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user)); return data; } catch (e) { console.warn("Backend unavailable or login failed"); throw e; } }, logout: () => { localStorage.removeItem(STORAGE_KEYS.TOKEN); localStorage.removeItem(STORAGE_KEYS.USER); window.location.href = '#/login'; }, getCurrentUser: (): User | null => { return getLocal(STORAGE_KEYS.USER, null); }, updateProfile: async (data: Partial & { password?: string }) => { if (FORCE_LOCAL_DB) { const currentUser = getLocal(STORAGE_KEYS.USER, null); if (!currentUser) throw new Error("Not logged in"); // Update current user session const updatedUser = { ...currentUser, ...data }; delete (updatedUser as any).password; setLocal(STORAGE_KEYS.USER, updatedUser); // Update in users list if it exists const users = getLocal(STORAGE_KEYS.USERS_LIST, []); const userIndex = users.findIndex(u => u.id === currentUser.id || u.email === currentUser.email); if (userIndex >= 0) { users[userIndex] = { ...users[userIndex], ...data }; delete (users[userIndex] as any).password; // mock logic: don't store pw setLocal(STORAGE_KEYS.USERS_LIST, users); } return { success: true, user: updatedUser }; } const res = await fetch(`${API_URL}/profile`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(data) }); if (!res.ok) throw new Error('Failed to update profile'); const response = await res.json(); // Update local session localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(response.user)); return response; }, getSettings: async (): Promise => { if (FORCE_LOCAL_DB) { return getLocal(STORAGE_KEYS.SETTINGS, { defaultMonthlyQuota: 100, condoName: 'Condominio (Anteprima)', currentYear: new Date().getFullYear(), smtpConfig: { host: '', port: 587, user: '', pass: '', secure: false, fromEmail: '' } }); } try { const res = await fetch(`${API_URL}/settings`, { headers: getAuthHeaders() }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { return getLocal(STORAGE_KEYS.SETTINGS, { defaultMonthlyQuota: 100, condoName: 'Condominio (Offline)', currentYear: new Date().getFullYear() }); } }, updateSettings: async (settings: AppSettings): Promise => { if (FORCE_LOCAL_DB) { setLocal(STORAGE_KEYS.SETTINGS, settings); return; } try { const res = await fetch(`${API_URL}/settings`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(settings) }); if (!res.ok) throw new Error('API Error'); } catch (e) { setLocal(STORAGE_KEYS.SETTINGS, settings); } }, getAvailableYears: async (): Promise => { // Shared logic works for both because it falls back to calculating from payments if (FORCE_LOCAL_DB) { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); const settings = getLocal(STORAGE_KEYS.SETTINGS, { currentYear: new Date().getFullYear() } as AppSettings); const years = new Set(payments.map(p => p.forYear)); years.add(settings.currentYear); return Array.from(years).sort((a, b) => b - a); } try { const res = await fetch(`${API_URL}/years`, { headers: getAuthHeaders() }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); const settings = getLocal(STORAGE_KEYS.SETTINGS, { currentYear: new Date().getFullYear() } as AppSettings); const years = new Set(payments.map(p => p.forYear)); years.add(settings.currentYear); return Array.from(years).sort((a, b) => b - a); } }, getFamilies: async (): Promise => { if (FORCE_LOCAL_DB) { return getLocal(STORAGE_KEYS.FAMILIES, []); } try { const res = await fetch(`${API_URL}/families`, { headers: getAuthHeaders() }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { return getLocal(STORAGE_KEYS.FAMILIES, []); } }, addFamily: async (familyData: Omit): Promise => { if (FORCE_LOCAL_DB) { const families = getLocal(STORAGE_KEYS.FAMILIES, []); const newFamily = { ...familyData, id: crypto.randomUUID(), balance: 0 }; setLocal(STORAGE_KEYS.FAMILIES, [...families, newFamily]); return newFamily; } try { const res = await fetch(`${API_URL}/families`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(familyData) }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { throw e; } }, updateFamily: async (family: Family): Promise => { if (FORCE_LOCAL_DB) { const families = getLocal(STORAGE_KEYS.FAMILIES, []); const updated = families.map(f => f.id === family.id ? family : f); setLocal(STORAGE_KEYS.FAMILIES, updated); return family; } try { const res = await fetch(`${API_URL}/families/${family.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(family) }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { throw e; } }, deleteFamily: async (familyId: string): Promise => { if (FORCE_LOCAL_DB) { const families = getLocal(STORAGE_KEYS.FAMILIES, []); setLocal(STORAGE_KEYS.FAMILIES, families.filter(f => f.id !== familyId)); return; } try { const res = await fetch(`${API_URL}/families/${familyId}`, { method: 'DELETE', headers: getAuthHeaders() }); if (!res.ok) throw new Error('API Error'); } catch (e) { throw e; } }, getPaymentsByFamily: async (familyId: string): Promise => { if (FORCE_LOCAL_DB) { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); return payments.filter(p => p.familyId === familyId); } try { const res = await fetch(`${API_URL}/payments?familyId=${familyId}`, { headers: getAuthHeaders() }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); return payments.filter(p => p.familyId === familyId); } }, addPayment: async (payment: Omit): Promise => { if (FORCE_LOCAL_DB) { const payments = getLocal(STORAGE_KEYS.PAYMENTS, []); const newPayment = { ...payment, id: crypto.randomUUID() }; setLocal(STORAGE_KEYS.PAYMENTS, [...payments, newPayment]); return newPayment; } try { const res = await fetch(`${API_URL}/payments`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(payment) }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { throw e; } }, getUsers: async (): Promise => { if (FORCE_LOCAL_DB) { return getLocal(STORAGE_KEYS.USERS_LIST, []); } try { const res = await fetch(`${API_URL}/users`, { headers: getAuthHeaders() }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { return []; } }, createUser: async (userData: any) => { if (FORCE_LOCAL_DB) { const users = getLocal(STORAGE_KEYS.USERS_LIST, []); const newUser = { ...userData, id: crypto.randomUUID() }; if (newUser.receiveAlerts === undefined) newUser.receiveAlerts = true; // Don't save password in local mock delete newUser.password; setLocal(STORAGE_KEYS.USERS_LIST, [...users, newUser]); return { success: true, id: newUser.id }; } const res = await fetch(`${API_URL}/users`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(userData) }); if (!res.ok) throw new Error('Failed to create user'); return res.json(); }, updateUser: async (id: string, userData: any) => { if (FORCE_LOCAL_DB) { 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 }; } const res = await fetch(`${API_URL}/users/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(userData) }); if (!res.ok) throw new Error('Failed to update user'); return res.json(); }, deleteUser: async (id: string) => { if (FORCE_LOCAL_DB) { const users = getLocal(STORAGE_KEYS.USERS_LIST, []); setLocal(STORAGE_KEYS.USERS_LIST, users.filter(u => u.id !== id)); return; } const res = await fetch(`${API_URL}/users/${id}`, { method: 'DELETE', headers: getAuthHeaders() }); if (!res.ok) throw new Error('Failed to delete user'); }, // --- ALERTS SERVICE --- getAlerts: async (): Promise => { if (FORCE_LOCAL_DB) { return getLocal(STORAGE_KEYS.ALERTS, []); } try { const res = await fetch(`${API_URL}/alerts`, { headers: getAuthHeaders() }); if (!res.ok) throw new Error('API Error'); return res.json(); } catch (e) { return []; } }, saveAlert: async (alert: AlertDefinition): Promise => { if (FORCE_LOCAL_DB) { 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; } const method = alert.id ? 'PUT' : 'POST'; const url = alert.id ? `${API_URL}/alerts/${alert.id}` : `${API_URL}/alerts`; const res = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, body: JSON.stringify(alert) }); if (!res.ok) throw new Error('Failed to save alert'); return res.json(); }, deleteAlert: async (id: string) => { if (FORCE_LOCAL_DB) { const alerts = getLocal(STORAGE_KEYS.ALERTS, []); setLocal(STORAGE_KEYS.ALERTS, alerts.filter(a => a.id !== id)); return; } const res = await fetch(`${API_URL}/alerts/${id}`, { method: 'DELETE', headers: getAuthHeaders() }); if (!res.ok) throw new Error('Failed to delete alert'); }, seedPayments: () => { if (!FORCE_LOCAL_DB) return; // Don't seed if connected to DB const families = getLocal(STORAGE_KEYS.FAMILIES, []); if (families.length === 0) { // Seed only if completely empty const demoFamilies: Family[] = [ { id: 'f1', name: 'Rossi Mario', unitNumber: 'A1', contactEmail: 'rossi@email.com', balance: 0 }, { id: 'f2', name: 'Bianchi Luigi', unitNumber: 'A2', contactEmail: 'bianchi@email.com', balance: 0 }, { id: 'f3', name: 'Verdi Anna', unitNumber: 'B1', contactEmail: 'verdi@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); } } };