mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 14:21:46 +00:00
Derive client domain types from the shared schema contracts
Add entity/response Zod schemas to @trek/shared (place, trip, assignment, day, budget, packing, reservation), each matched against the producing server service, and re-export them from client types.ts instead of the hand-written duplicates that had drifted (name/title, amount/total_price, owner_id/user_id, cover_url/cover_image, ...). Updates the call sites and test fixtures the corrected types surfaced; type-only, no runtime behaviour change.
This commit is contained in:
@@ -66,14 +66,15 @@ export function buildTrip(overrides: Partial<Trip> = {}): Trip {
|
||||
const id = next();
|
||||
return {
|
||||
id,
|
||||
name: `Trip ${id}`,
|
||||
user_id: 1,
|
||||
title: `Trip ${id}`,
|
||||
description: null,
|
||||
start_date: '2025-06-01',
|
||||
end_date: '2025-06-05',
|
||||
cover_url: null,
|
||||
is_archived: false,
|
||||
currency: 'EUR',
|
||||
cover_image: null,
|
||||
is_archived: 0,
|
||||
reminder_days: 7,
|
||||
owner_id: 1,
|
||||
created_at: '2025-01-01T00:00:00.000Z',
|
||||
updated_at: '2025-01-01T00:00:00.000Z',
|
||||
...overrides,
|
||||
@@ -105,14 +106,19 @@ export function buildPlace(overrides: Partial<Place> = {}): Place {
|
||||
lng: 2.3522,
|
||||
address: null,
|
||||
category_id: null,
|
||||
icon: null,
|
||||
price: null,
|
||||
currency: null,
|
||||
image_url: null,
|
||||
google_place_id: null,
|
||||
osm_id: null,
|
||||
route_geometry: null,
|
||||
place_time: null,
|
||||
end_time: null,
|
||||
duration_minutes: 60,
|
||||
notes: null,
|
||||
transport_mode: 'walking',
|
||||
website: null,
|
||||
phone: null,
|
||||
created_at: '2025-01-01T00:00:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
@@ -154,6 +160,7 @@ export function buildPackingItem(overrides: Partial<PackingItem> = {}): PackingI
|
||||
name: `Packing item ${id}`,
|
||||
category: null,
|
||||
checked: 0,
|
||||
sort_order: 0,
|
||||
quantity: 1,
|
||||
...overrides,
|
||||
};
|
||||
@@ -181,14 +188,16 @@ export function buildBudgetItem(overrides: Partial<BudgetItem> = {}): BudgetItem
|
||||
return {
|
||||
id,
|
||||
trip_id: 1,
|
||||
category: 'Other',
|
||||
name: `Budget item ${id}`,
|
||||
amount: 100,
|
||||
currency: 'EUR',
|
||||
category: null,
|
||||
paid_by: null,
|
||||
total_price: 100,
|
||||
persons: 1,
|
||||
days: null,
|
||||
note: null,
|
||||
sort_order: 0,
|
||||
members: [],
|
||||
expense_date: null,
|
||||
created_at: '2025-01-01T00:00:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
@@ -198,14 +207,14 @@ export function buildReservation(overrides: Partial<Reservation> = {}): Reservat
|
||||
return {
|
||||
id,
|
||||
trip_id: 1,
|
||||
name: `Reservation ${id}`,
|
||||
title: `Reservation ${id}`,
|
||||
type: 'restaurant',
|
||||
status: 'confirmed',
|
||||
date: null,
|
||||
time: null,
|
||||
reservation_time: null,
|
||||
reservation_end_time: null,
|
||||
location: null,
|
||||
confirmation_number: null,
|
||||
notes: null,
|
||||
url: null,
|
||||
created_at: '2025-01-01T00:00:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
@@ -219,6 +228,7 @@ export function buildTripFile(overrides: Partial<TripFile> = {}): TripFile {
|
||||
filename: 'test.pdf',
|
||||
original_name: 'test.pdf',
|
||||
mime_type: 'application/pdf',
|
||||
url: `/api/trips/1/files/${id}/download`,
|
||||
created_at: '2025-01-01T00:00:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
@@ -240,6 +250,7 @@ export function buildCategory(overrides: Partial<Category> = {}): Category {
|
||||
return {
|
||||
id,
|
||||
name: `Category ${id}`,
|
||||
color: '#6366f1',
|
||||
icon: 'restaurant',
|
||||
user_id: 1,
|
||||
...overrides,
|
||||
|
||||
@@ -25,9 +25,17 @@ export function resetAllStores(): void {
|
||||
usePermissionsStore.setState(initialPermsState, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests routinely seed a store with a partially-populated slice of state,
|
||||
* including partial nested objects (e.g. only `settings.time_format`). The
|
||||
* store's own setState wants the exact field types, so seeding accepts a
|
||||
* deep-partial view and casts at the boundary.
|
||||
*/
|
||||
type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;
|
||||
|
||||
export function seedStore<T extends object>(
|
||||
store: { setState: (partial: Partial<T>, replace?: boolean) => void },
|
||||
state: Partial<T>,
|
||||
state: DeepPartial<T>,
|
||||
): void {
|
||||
store.setState(state);
|
||||
store.setState(state as Partial<T>);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user