feat: Add tickets module and PayPal integration

Introduces a new 'Tickets' module for users to submit and manage issues within their condominium. This includes defining ticket types, statuses, priorities, and categories.

Additionally, this commit integrates PayPal as a payment option for family fee payments, enabling users to pay directly via PayPal using their client ID.

Key changes:
- Added `Ticket` related types and enums.
- Implemented `TicketService` functions for CRUD operations.
- Integrated `@paypal/react-paypal-js` library.
- Added `paypalClientId` to `AppSettings` and `Condo` types.
- Updated `FamilyDetail` page to include PayPal payment option.
- Added 'Segnalazioni' navigation link to `Layout`.
This commit is contained in:
2025-12-07 19:49:59 +01:00
parent 2566b406e1
commit 5311400615
14 changed files with 977 additions and 146 deletions

View File

@@ -1,5 +1,5 @@
import { Family, Payment, AppSettings, User, AlertDefinition, Condo, Notice, NoticeRead } from '../types';
import { Family, Payment, AppSettings, User, AlertDefinition, Condo, Notice, NoticeRead, Ticket, TicketAttachment } from '../types';
// --- CONFIGURATION TOGGLE ---
const FORCE_LOCAL_DB = false;
@@ -288,8 +288,41 @@ export const CondoService = {
await request(`/alerts/${id}`, { method: 'DELETE' });
},
// --- TICKETS ---
getTickets: async (condoId?: string): Promise<Ticket[]> => {
let url = '/tickets';
const activeId = condoId || CondoService.getActiveCondoId();
if (activeId) url += `?condoId=${activeId}`;
return request<Ticket[]>(url);
},
createTicket: async (data: Partial<Ticket> & { attachments?: { fileName: string, fileType: string, data: string }[] }) => {
const activeId = CondoService.getActiveCondoId();
if(!activeId) throw new Error("No active condo");
return request('/tickets', {
method: 'POST',
body: JSON.stringify({ ...data, condoId: activeId })
});
},
updateTicket: async (id: string, data: { status: string, priority: string }) => {
return request(`/tickets/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
});
},
deleteTicket: async (id: string) => {
await request(`/tickets/${id}`, { method: 'DELETE' });
},
getTicketAttachment: async (ticketId: string, attachmentId: string): Promise<TicketAttachment> => {
return request<TicketAttachment>(`/tickets/${ticketId}/attachments/${attachmentId}`);
},
// --- SEEDING ---
seedPayments: () => {
// No-op in remote mode
}
};
};