File size: 3,044 Bytes
98fbc6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { assignmentSchema } from '../assignment/assignment.schema';

import { z } from 'zod';

/**
 * Day + day-note API contract — single source of truth for the
 * /api/trips/:tripId/days and /api/trips/:tripId/days/:dayId/notes endpoints.
 *
 * Trip-scoped, both gated by the 'day_edit' permission. The legacy routes
 * (server/src/routes/days.ts + routes/dayNotes.ts) wrap dayService /
 * dayNoteService. Day rows (with their assignments) are wide and DB-derived, so
 * list responses stay open. Day notes cap text at 500 and time at 150 chars
 * (the legacy validateStringLengths middleware) — reproduced in the controller.
 */

/**
 * Day note entity (server day_notes table / dayNoteService). `sort_order` is
 * SQLite REAL; `icon` defaults to a note emoji.
 */
export const dayNoteSchema = z.object({
  id: z.number(),
  day_id: z.number(),
  trip_id: z.number().optional(),
  text: z.string(),
  time: z.string().nullable().optional(),
  icon: z.string().nullable().optional(),
  sort_order: z.number().optional(),
  created_at: z.string().optional(),
});
export type DayNote = z.infer<typeof dayNoteSchema>;

/**
 * Day entity as returned by the day list/get endpoints
 * (server/src/services/dayService.ts -> listDays). Columns of the `days` table
 * plus the embedded `assignments` and `notes_items` arrays.
 */
export const daySchema = z.object({
  id: z.number(),
  trip_id: z.number(),
  day_number: z.number().optional(),
  date: z.string().nullable().optional(),
  title: z.string().nullable().optional(),
  notes: z.string().nullable().optional(),
  assignments: z.array(assignmentSchema).optional(),
  notes_items: z.array(dayNoteSchema).optional(),
});
export type Day = z.infer<typeof daySchema>;

export const dayCreateRequestSchema = z.object({
  date: z.string().optional(),
  notes: z.string().optional(),
  // 1-based slot to insert a new empty day at (omit to append at the end).
  position: z.number().int().positive().optional(),
});
export type DayCreateRequest = z.infer<typeof dayCreateRequestSchema>;

/** Reorder whole days: the desired full sequence of this trip's day ids. */
export const dayReorderRequestSchema = z.object({
  orderedIds: z.array(z.number()),
});
export type DayReorderRequest = z.infer<typeof dayReorderRequestSchema>;

export const dayUpdateRequestSchema = z.object({
  notes: z.string().optional(),
  title: z.string().nullable().optional(),
});
export type DayUpdateRequest = z.infer<typeof dayUpdateRequestSchema>;

export const dayNoteCreateRequestSchema = z.object({
  text: z.string().min(1).max(500),
  time: z.string().max(250).optional(),
  icon: z.string().optional(),
  sort_order: z.number().optional(),
});
export type DayNoteCreateRequest = z.infer<typeof dayNoteCreateRequestSchema>;

export const dayNoteUpdateRequestSchema = z.object({
  text: z.string().max(500).optional(),
  time: z.string().max(250).optional(),
  icon: z.string().optional(),
  sort_order: z.number().optional(),
});
export type DayNoteUpdateRequest = z.infer<typeof dayNoteUpdateRequestSchema>;