mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
3977a5ecba
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.
95 lines
3.7 KiB
TypeScript
95 lines
3.7 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
/**
|
|
* Budget API contract — single source of truth for the /api/trips/:tripId/budget
|
|
* endpoints (expense items, per-member splits, paid toggles, settlement).
|
|
*
|
|
* Trip-scoped: every endpoint verifies trip access (404 "Trip not found") and
|
|
* mutations check the 'budget_edit' permission (403 "No permission"). The legacy
|
|
* route (server/src/routes/budget.ts) wraps services/budgetService.ts; rows are
|
|
* DB-shaped and kept open. Mutations broadcast over WebSocket with the forwarded
|
|
* X-Socket-Id. Updating a linked item's total_price also syncs the price into the
|
|
* linked reservation's metadata (and broadcasts reservation:updated).
|
|
*/
|
|
|
|
/**
|
|
* Budget item member as embedded on a budget item
|
|
* (server/src/services/budgetService.ts -> loadItemMembers). `paid` is the raw
|
|
* SQLite INTEGER (0/1); `avatar_url` is the resolved avatar (avatarUrl()).
|
|
*/
|
|
export const budgetItemMemberSchema = z.object({
|
|
user_id: z.number(),
|
|
paid: z.number(),
|
|
username: z.string(),
|
|
avatar_url: z.string().nullable().optional(),
|
|
avatar: z.string().nullable().optional(),
|
|
budget_item_id: z.number().optional(),
|
|
});
|
|
export type BudgetItemMember = z.infer<typeof budgetItemMemberSchema>;
|
|
|
|
/**
|
|
* Budget item entity as returned by the budget list/create/update endpoints
|
|
* (server/src/services/budgetService.ts). Columns of the `budget_items` table
|
|
* plus the embedded `members` array. total_price is SQLite REAL.
|
|
*/
|
|
export const budgetItemSchema = z.object({
|
|
id: z.number(),
|
|
trip_id: z.number(),
|
|
category: z.string(),
|
|
name: z.string(),
|
|
total_price: z.number(),
|
|
persons: z.number().nullable().optional(),
|
|
days: z.number().nullable().optional(),
|
|
note: z.string().nullable().optional(),
|
|
reservation_id: z.number().nullable().optional(),
|
|
paid_by_user_id: z.number().nullable().optional(),
|
|
expense_date: z.string().nullable().optional(),
|
|
sort_order: z.number().optional(),
|
|
created_at: z.string().optional(),
|
|
members: z.array(budgetItemMemberSchema).optional(),
|
|
});
|
|
export type BudgetItem = z.infer<typeof budgetItemSchema>;
|
|
|
|
export const budgetCreateItemRequestSchema = z.object({
|
|
name: z.string().min(1),
|
|
category: z.string().optional(),
|
|
total_price: z.number().optional(),
|
|
persons: z.number().nullable().optional(),
|
|
days: z.number().nullable().optional(),
|
|
note: z.string().nullable().optional(),
|
|
expense_date: z.string().nullable().optional(),
|
|
});
|
|
export type BudgetCreateItemRequest = z.infer<typeof budgetCreateItemRequestSchema>;
|
|
|
|
/** Update accepts the same fields plus total_price changes; all optional. */
|
|
export const budgetUpdateItemRequestSchema = z.object({
|
|
name: z.string().optional(),
|
|
category: z.string().optional(),
|
|
total_price: z.number().optional(),
|
|
persons: z.number().nullable().optional(),
|
|
days: z.number().nullable().optional(),
|
|
note: z.string().nullable().optional(),
|
|
expense_date: z.string().nullable().optional(),
|
|
});
|
|
export type BudgetUpdateItemRequest = z.infer<typeof budgetUpdateItemRequestSchema>;
|
|
|
|
export const budgetUpdateMembersRequestSchema = z.object({
|
|
user_ids: z.array(z.number()),
|
|
});
|
|
export type BudgetUpdateMembersRequest = z.infer<typeof budgetUpdateMembersRequestSchema>;
|
|
|
|
export const budgetToggleMemberPaidRequestSchema = z.object({
|
|
paid: z.boolean(),
|
|
});
|
|
export type BudgetToggleMemberPaidRequest = z.infer<typeof budgetToggleMemberPaidRequestSchema>;
|
|
|
|
export const budgetReorderItemsRequestSchema = z.object({
|
|
orderedIds: z.array(z.number()),
|
|
});
|
|
export type BudgetReorderItemsRequest = z.infer<typeof budgetReorderItemsRequestSchema>;
|
|
|
|
export const budgetReorderCategoriesRequestSchema = z.object({
|
|
orderedCategories: z.array(z.string()),
|
|
});
|
|
export type BudgetReorderCategoriesRequest = z.infer<typeof budgetReorderCategoriesRequestSchema>;
|