Files
Condopay/services/mockDb.ts
frakarr 2a6da489aa 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.
2025-12-09 23:12:47 +01:00

345 lines
10 KiB
TypeScript

import {
Condo, Family, Payment, AppSettings, User, AuthResponse,
Ticket, TicketComment, ExtraordinaryExpense, Notice,
AlertDefinition, NoticeRead
} from '../types';
const API_URL = '/api';
async function request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = localStorage.getItem('condo_token');
const headers: HeadersInit = {
'Content-Type': 'application/json',
...options.headers as any,
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(`${API_URL}${endpoint}`, {
...options,
headers,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText || response.statusText);
}
// Handle empty responses
const text = await response.text();
return text ? JSON.parse(text) : undefined;
}
export const CondoService = {
// Auth & User
login: async (email: string, password: string): Promise<void> => {
const data = await request<AuthResponse>('/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password })
});
localStorage.setItem('condo_token', data.token);
localStorage.setItem('condo_user', JSON.stringify(data.user));
},
logout: () => {
localStorage.removeItem('condo_token');
localStorage.removeItem('condo_user');
window.location.href = '/#/login';
},
getCurrentUser: (): User | null => {
const u = localStorage.getItem('condo_user');
return u ? JSON.parse(u) : null;
},
updateProfile: async (data: any): Promise<void> => {
const res = await request<{success: boolean, user: User}>('/profile', {
method: 'PUT',
body: JSON.stringify(data)
});
if (res.user) {
localStorage.setItem('condo_user', JSON.stringify(res.user));
}
},
// Settings
getSettings: async (): Promise<AppSettings> => {
return request<AppSettings>('/settings');
},
updateSettings: async (settings: AppSettings): Promise<void> => {
return request('/settings', {
method: 'PUT',
body: JSON.stringify(settings)
});
},
testSmtpConfig: async (config: any): Promise<void> => {
return request('/settings/smtp-test', {
method: 'POST',
body: JSON.stringify(config)
});
},
getAvailableYears: async (): Promise<number[]> => {
return request<number[]>('/years');
},
// Condos
getCondos: async (): Promise<Condo[]> => {
return request<Condo[]>('/condos');
},
getActiveCondoId: (): string | undefined => {
return localStorage.getItem('active_condo_id') || undefined;
},
getActiveCondo: async (): Promise<Condo | undefined> => {
const id = localStorage.getItem('active_condo_id');
const condos = await CondoService.getCondos();
if (id) {
return condos.find(c => c.id === id);
}
return condos.length > 0 ? condos[0] : undefined;
},
setActiveCondo: (id: string) => {
localStorage.setItem('active_condo_id', id);
window.dispatchEvent(new Event('condo-updated'));
window.location.reload();
},
saveCondo: async (condo: Condo): Promise<Condo> => {
if (condo.id) {
await request(`/condos/${condo.id}`, {
method: 'PUT',
body: JSON.stringify(condo)
});
return condo;
} else {
return request<Condo>('/condos', {
method: 'POST',
body: JSON.stringify(condo)
});
}
},
deleteCondo: async (id: string): Promise<void> => {
return request(`/condos/${id}`, { method: 'DELETE' });
},
// Families
getFamilies: async (condoId?: string): Promise<Family[]> => {
let url = '/families';
const activeId = condoId || CondoService.getActiveCondoId();
if (activeId) url += `?condoId=${activeId}`;
return request<Family[]>(url);
},
addFamily: async (family: any): Promise<Family> => {
const activeId = CondoService.getActiveCondoId();
return request<Family>('/families', {
method: 'POST',
body: JSON.stringify({ ...family, condoId: activeId })
});
},
updateFamily: async (family: Family): Promise<void> => {
return request(`/families/${family.id}`, {
method: 'PUT',
body: JSON.stringify(family)
});
},
deleteFamily: async (id: string): Promise<void> => {
return request(`/families/${id}`, { method: 'DELETE' });
},
// Payments
seedPayments: () => { /* No-op for real backend */ },
getPaymentsByFamily: async (familyId: string): Promise<Payment[]> => {
return request<Payment[]>(`/payments?familyId=${familyId}`);
},
getCondoPayments: async (condoId: string): Promise<Payment[]> => {
return request<Payment[]>(`/payments?condoId=${condoId}`);
},
addPayment: async (payment: any): Promise<Payment> => {
return request<Payment>('/payments', {
method: 'POST',
body: JSON.stringify(payment)
});
},
// Users
getUsers: async (condoId?: string): Promise<User[]> => {
let url = '/users';
if (condoId) url += `?condoId=${condoId}`;
return request<User[]>(url);
},
createUser: async (user: any): Promise<void> => {
return request('/users', {
method: 'POST',
body: JSON.stringify(user)
});
},
updateUser: async (id: string, user: any): Promise<void> => {
return request(`/users/${id}`, {
method: 'PUT',
body: JSON.stringify(user)
});
},
deleteUser: async (id: string): Promise<void> => {
return request(`/users/${id}`, { method: 'DELETE' });
},
// Alerts
getAlerts: async (condoId?: string): Promise<AlertDefinition[]> => {
let url = '/alerts';
if (condoId) url += `?condoId=${condoId}`;
return request<AlertDefinition[]>(url);
},
saveAlert: async (alert: AlertDefinition): Promise<AlertDefinition> => {
const activeId = CondoService.getActiveCondoId();
if (alert.id) {
await request(`/alerts/${alert.id}`, { method: 'PUT', body: JSON.stringify(alert) });
return alert;
} else {
return request('/alerts', {
method: 'POST',
body: JSON.stringify({ ...alert, condoId: activeId })
});
}
},
deleteAlert: async (id: string): Promise<void> => {
return request(`/alerts/${id}`, { method: 'DELETE' });
},
// Notices
getNotices: async (condoId?: string): Promise<Notice[]> => {
let url = '/notices';
const activeId = condoId || CondoService.getActiveCondoId();
if (activeId) url += `?condoId=${activeId}`;
return request<Notice[]>(url);
},
getUnreadNoticesForUser: async (userId: string, condoId: string): Promise<Notice[]> => {
return request<Notice[]>(`/notices/unread?userId=${userId}&condoId=${condoId}`);
},
getNoticeReadStatus: async (noticeId: string): Promise<NoticeRead[]> => {
return request<NoticeRead[]>(`/notices/${noticeId}/read-status`);
},
markNoticeAsRead: async (noticeId: string, userId: string): Promise<void> => {
return request(`/notices/${noticeId}/read`, {
method: 'POST',
body: JSON.stringify({ userId })
});
},
saveNotice: async (notice: Notice): Promise<void> => {
if (notice.id) {
return request(`/notices/${notice.id}`, { method: 'PUT', body: JSON.stringify(notice) });
} else {
return request('/notices', { method: 'POST', body: JSON.stringify(notice) });
}
},
deleteNotice: async (id: string): Promise<void> => {
return request(`/notices/${id}`, { method: 'DELETE' });
},
// Tickets
getTickets: async (): Promise<Ticket[]> => {
const activeId = CondoService.getActiveCondoId();
return request<Ticket[]>(`/tickets?condoId=${activeId}`);
},
createTicket: async (data: any): Promise<void> => {
const activeId = CondoService.getActiveCondoId();
return request('/tickets', {
method: 'POST',
body: JSON.stringify({ ...data, condoId: activeId })
});
},
updateTicket: async (id: string, data: any): Promise<void> => {
return request(`/tickets/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
});
},
deleteTicket: async (id: string): Promise<void> => {
return request(`/tickets/${id}`, { method: 'DELETE' });
},
getTicketComments: async (ticketId: string): Promise<TicketComment[]> => {
return request<TicketComment[]>(`/tickets/${ticketId}/comments`);
},
addTicketComment: async (ticketId: string, text: string): Promise<void> => {
return request(`/tickets/${ticketId}/comments`, {
method: 'POST',
body: JSON.stringify({ text })
});
},
getTicketAttachment: async (ticketId: string, attachmentId: string): Promise<any> => {
return request(`/tickets/${ticketId}/attachments/${attachmentId}`);
},
// Extraordinary Expenses
getExpenses: async (condoId?: string): Promise<ExtraordinaryExpense[]> => {
let url = '/expenses';
const activeId = condoId || CondoService.getActiveCondoId();
if (activeId) url += `?condoId=${activeId}`;
return request<ExtraordinaryExpense[]>(url);
},
getExpenseDetails: async (id: string): Promise<ExtraordinaryExpense> => {
return request<ExtraordinaryExpense>(`/expenses/${id}`);
},
createExpense: async (data: any): Promise<void> => {
const activeId = CondoService.getActiveCondoId();
if (!activeId) throw new Error("No active condo");
return request('/expenses', {
method: 'POST',
body: JSON.stringify({ ...data, condoId: activeId })
});
},
updateExpense: async (id: string, data: any): Promise<void> => {
return request(`/expenses/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
});
},
getExpenseAttachment: async (expenseId: string, attachmentId: string): Promise<any> => {
return request(`/expenses/${expenseId}/attachments/${attachmentId}`);
},
getMyExpenses: async (): Promise<any[]> => {
const activeId = CondoService.getActiveCondoId();
return request(`/my-expenses?condoId=${activeId}`);
},
payExpense: async (expenseId: string, amount: number): Promise<void> => {
return request(`/expenses/${expenseId}/pay`, {
method: 'POST',
body: JSON.stringify({ amount, notes: 'PayPal Payment' })
});
}
};