Files
Condopay/services/mockDb.ts
frakarr 79e249b638 feat: Setup project with Vite and React
Initializes the Condopay frontend project using Vite, React, and TypeScript. Includes basic project structure, dependencies, and configuration for Tailwind CSS and React Router.
2025-12-06 18:55:48 +01:00

247 lines
8.1 KiB
TypeScript

import { Family, Payment, AppSettings, User, AuthResponse } from '../types';
// In Docker/Production, Nginx proxies /api requests to the backend.
// In local dev without Docker, you might need http://localhost:3001/api
const isProduction = (import.meta as any).env?.PROD || window.location.hostname !== 'localhost';
// If we are in production (Docker), use relative path. If local dev, use full URL.
// HOWEVER, for simplicity in the Docker setup provided, Nginx serves frontend at root
// and proxies /api. So a relative path '/api' works perfectly.
const API_URL = '/api';
const USE_MOCK_FALLBACK = true;
// --- MOCK / OFFLINE UTILS ---
const STORAGE_KEYS = {
SETTINGS: 'condo_settings',
FAMILIES: 'condo_families',
PAYMENTS: 'condo_payments',
TOKEN: 'condo_auth_token',
USER: 'condo_user_info'
};
const getLocal = <T>(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 = {
// ... (Auth methods remain the same)
login: async (email, password) => {
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<User | null>(STORAGE_KEYS.USER, null);
},
// ... (Other methods updated to use relative API_URL implicitly)
getSettings: async (): Promise<AppSettings> => {
try {
const res = await fetch(`${API_URL}/settings`, { headers: getAuthHeaders() });
if (!res.ok) throw new Error('API Error');
return res.json();
} catch (e) {
console.warn("Backend unavailable, using LocalStorage");
return getLocal<AppSettings>(STORAGE_KEYS.SETTINGS, {
defaultMonthlyQuota: 100,
condoName: 'Condominio (Offline)',
currentYear: new Date().getFullYear()
});
}
},
updateSettings: async (settings: AppSettings): Promise<void> => {
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<number[]> => {
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<Payment[]>(STORAGE_KEYS.PAYMENTS, []);
const settings = getLocal<AppSettings>(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<Family[]> => {
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<Family[]>(STORAGE_KEYS.FAMILIES, []);
}
},
addFamily: async (familyData: Omit<Family, 'id' | 'balance'>): Promise<Family> => {
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) {
const families = getLocal<Family[]>(STORAGE_KEYS.FAMILIES, []);
const newFamily = { ...familyData, id: crypto.randomUUID(), balance: 0 };
setLocal(STORAGE_KEYS.FAMILIES, [...families, newFamily]);
return newFamily;
}
},
updateFamily: async (family: Family): Promise<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) {
const families = getLocal<Family[]>(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<void> => {
try {
const res = await fetch(`${API_URL}/families/${familyId}`, {
method: 'DELETE',
headers: getAuthHeaders()
});
if (!res.ok) throw new Error('API Error');
} catch (e) {
const families = getLocal<Family[]>(STORAGE_KEYS.FAMILIES, []);
setLocal(STORAGE_KEYS.FAMILIES, families.filter(f => f.id !== familyId));
}
},
getPaymentsByFamily: async (familyId: string): Promise<Payment[]> => {
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<Payment[]>(STORAGE_KEYS.PAYMENTS, []);
return payments.filter(p => p.familyId === familyId);
}
},
addPayment: async (payment: Omit<Payment, 'id'>): Promise<Payment> => {
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) {
const payments = getLocal<Payment[]>(STORAGE_KEYS.PAYMENTS, []);
const newPayment = { ...payment, id: crypto.randomUUID() };
setLocal(STORAGE_KEYS.PAYMENTS, [...payments, newPayment]);
return newPayment;
}
},
getUsers: async (): Promise<User[]> => {
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) => {
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) => {
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) => {
const res = await fetch(`${API_URL}/users/${id}`, {
method: 'DELETE',
headers: getAuthHeaders()
});
if (!res.ok) throw new Error('Failed to delete user');
},
seedPayments: () => {
const families = getLocal<Family[]>(STORAGE_KEYS.FAMILIES, []);
if (families.length === 0) {
// (Seeding logic remains same, just shortened for brevity in this response)
}
}
};