mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-20 22:01:45 +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:
@@ -15,6 +15,91 @@ import { z } from 'zod';
|
||||
|
||||
const open = z.record(z.string(), z.unknown());
|
||||
|
||||
/**
|
||||
* A reservation endpoint (flight/train leg terminal) — row of the
|
||||
* reservation_endpoints table (server/src/services/reservationService.ts).
|
||||
*/
|
||||
export const reservationEndpointSchema = z.object({
|
||||
id: z.number().optional(),
|
||||
reservation_id: z.number().optional(),
|
||||
role: z.enum(['from', 'to', 'stop']),
|
||||
sequence: z.number(),
|
||||
name: z.string(),
|
||||
code: z.string().nullable(),
|
||||
lat: z.number(),
|
||||
lng: z.number(),
|
||||
timezone: z.string().nullable(),
|
||||
local_time: z.string().nullable(),
|
||||
local_date: z.string().nullable(),
|
||||
});
|
||||
export type ReservationEndpoint = z.infer<typeof reservationEndpointSchema>;
|
||||
|
||||
/**
|
||||
* Reservation entity as returned by the reservation list endpoint
|
||||
* (server/src/services/reservationService.ts -> listReservations). Columns of
|
||||
* the `reservations` table plus the joined day_number / place_name / linked
|
||||
* accommodation fields and the computed `day_positions` + `endpoints`.
|
||||
* `accommodation_id` is stored as TEXT in the DB.
|
||||
*/
|
||||
export const reservationSchema = z.object({
|
||||
id: z.number(),
|
||||
trip_id: z.number(),
|
||||
day_id: z.number().nullable().optional(),
|
||||
end_day_id: z.number().nullable().optional(),
|
||||
place_id: z.number().nullable().optional(),
|
||||
assignment_id: z.number().nullable().optional(),
|
||||
title: z.string(),
|
||||
reservation_time: z.string().nullable().optional(),
|
||||
reservation_end_time: z.string().nullable().optional(),
|
||||
location: z.string().nullable().optional(),
|
||||
confirmation_number: z.string().nullable().optional(),
|
||||
notes: z.string().nullable().optional(),
|
||||
status: z.string(),
|
||||
type: z.string(),
|
||||
accommodation_id: z.union([z.number(), z.string()]).nullable().optional(),
|
||||
metadata: z.string().nullable().optional(),
|
||||
needs_review: z.number().optional(),
|
||||
day_plan_position: z.number().nullable().optional(),
|
||||
created_at: z.string().optional(),
|
||||
// joined / computed in listReservations
|
||||
day_number: z.number().nullable().optional(),
|
||||
place_name: z.string().nullable().optional(),
|
||||
accommodation_place_id: z.number().nullable().optional(),
|
||||
accommodation_name: z.string().nullable().optional(),
|
||||
accommodation_start_day_id: z.number().nullable().optional(),
|
||||
accommodation_end_day_id: z.number().nullable().optional(),
|
||||
day_positions: z.record(z.string(), z.number()).nullable().optional(),
|
||||
endpoints: z.array(reservationEndpointSchema).optional(),
|
||||
});
|
||||
export type Reservation = z.infer<typeof reservationSchema>;
|
||||
|
||||
/**
|
||||
* Accommodation entity as returned by listAccommodations / getAccommodationWithPlace
|
||||
* (server/src/services/dayService.ts). Columns of the day_accommodations table
|
||||
* plus the joined place fields and (on list) the linked reservation_title.
|
||||
*/
|
||||
export const accommodationSchema = z.object({
|
||||
id: z.number(),
|
||||
trip_id: z.number(),
|
||||
place_id: z.number().nullable().optional(),
|
||||
start_day_id: z.number(),
|
||||
end_day_id: z.number(),
|
||||
check_in: z.string().nullable().optional(),
|
||||
check_in_end: z.string().nullable().optional(),
|
||||
check_out: z.string().nullable().optional(),
|
||||
confirmation: z.string().nullable().optional(),
|
||||
notes: z.string().nullable().optional(),
|
||||
created_at: z.string().optional(),
|
||||
// joined in listAccommodations / getAccommodationWithPlace
|
||||
place_name: z.string().nullable().optional(),
|
||||
place_address: z.string().nullable().optional(),
|
||||
place_image: z.string().nullable().optional(),
|
||||
place_lat: z.number().nullable().optional(),
|
||||
place_lng: z.number().nullable().optional(),
|
||||
reservation_title: z.string().nullable().optional(),
|
||||
});
|
||||
export type Accommodation = z.infer<typeof accommodationSchema>;
|
||||
|
||||
/** Reservation create: title is required; the many optional fields stay open. */
|
||||
export const reservationCreateRequestSchema = open.and(z.object({ title: z.string().min(1) }));
|
||||
export type ReservationCreateRequest = z.infer<typeof reservationCreateRequestSchema>;
|
||||
|
||||
Reference in New Issue
Block a user