From 477a95bec2b64d0ab186fecf4015a33906445fe4 Mon Sep 17 00:00:00 2001 From: Aleksei Vesnin Date: Mon, 21 Sep 2026 08:00:20 +0300 Subject: [PATCH 1/2] docs: a trip is a sequence of parts, each with its own place and dates Co-Authored-By: Claude Opus 5 --- README.md | 3 ++- src/components/courses/course-dialog.tsx | 4 ++-- src/hooks/useInfiniteResource.ts | 11 ++++++----- src/lib/api/courses.ts | 12 ++++++------ src/lib/date-time.ts | 9 +++++---- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 083cfc4f..4adeb34b 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,8 @@ one. one. - **Air consumption** — SAC and RMV are derived automatically, including a **per-tank breakdown** across recorded gas switches on multi-tank dives, with a consumption trend chart on the dashboard. -- **Trips** — group dives into a liveaboard or a holiday week, with location and dates. +- **Trips** — group dives into a liveaboard or a holiday week, a part at a time: each part carries + its own place and its own dates, and the trip spans them all. - **Dive sites** — your personal site list, with every dive you've logged at each site. - **Gear tracking** — your equipment with per-item dive counts, groupable into gear sets you can attach to a dive in one click, plus **service schedules** (annual service, visual inspection, diff --git a/src/components/courses/course-dialog.tsx b/src/components/courses/course-dialog.tsx index e2a85ca0..39f121a5 100644 --- a/src/components/courses/course-dialog.tsx +++ b/src/components/courses/course-dialog.tsx @@ -329,8 +329,8 @@ export function CourseDialog({ name="end_date" render={({ field }) => ( - {/* Neither date is required, unlike a trip's start date: a - course that is only booked has no dates yet. */} + {/* Neither date is required: a course that is only + booked has no dates yet. */} End date ( * The cursor steps back a page for the same reason `removeItem` re-derives * it, and is re-derived rather than decremented for the same reason too. * Every list here is ordered by a column the edit dialog can change - dive - * sites by name, trips and courses by start date, certifications by the date - * certified - so a rename or a re-dated trip *moves* the row in the server's - * order. Move it later than the loaded window and everything after its old - * slot shifts up one offset, so asking for the page after the last one fetched - * skips whichever row slid across the boundary, permanently. Re-reading the + * sites by name, courses by start date, trips by the earliest date across + * their parts, certifications by the date certified - so a rename or a + * re-dated trip *moves* the row in the server's order. Move it later than the + * loaded window and everything after its old slot shifts up one offset, so + * asking for the page after the last one fetched skips whichever row slid + * across the boundary, permanently. Re-reading the * previous page covers a shift of one row in either direction and the dedup in * `load` absorbs the repeats; the cost is one overlapping request on the next * scroll, however many edits it follows. diff --git a/src/lib/api/courses.ts b/src/lib/api/courses.ts index 31f5a0bf..dcf3bcf8 100644 --- a/src/lib/api/courses.ts +++ b/src/lib/api/courses.ts @@ -35,10 +35,10 @@ export const DEFAULT_COURSE_STATUS: CourseStatus = "completed"; /** * A training course: a group of dives and the certifications they produced. * - * Works like a trip without a location. The instructor/training-center trio - * duplicates the same fields on `Certification` deliberately - a certification - * has to stand alone, because imported history arrives certification-first with - * no course to hang them on. + * Works like a trip, but with dates of its own and no place. The + * instructor/training-center trio duplicates the same fields on `Certification` + * deliberately - a certification has to stand alone, because imported history + * arrives certification-first with no course to hang them on. */ export interface Course { uuid: string; @@ -50,8 +50,8 @@ export interface Course { // Only set when `agency` is `other` - the name of the training body. agency_other?: string | null; status: CourseStatus; - // Both nullable, unlike a trip's start date: a `planned` course has no dates - // yet, and a referral spans months with fuzzy edges. Bare "YYYY-MM-DD". + // Both nullable: a `planned` course has no dates yet, and a referral spans + // months with fuzzy edges. Bare "YYYY-MM-DD". start_date?: string | null; end_date?: string | null; instructor_name?: string | null; diff --git a/src/lib/date-time.ts b/src/lib/date-time.ts index 11a59e67..f4172625 100644 --- a/src/lib/date-time.ts +++ b/src/lib/date-time.ts @@ -311,9 +311,9 @@ export function formatDiveStartTime(startTime: string): string { return `${date} at ${formatDiveTimeOnly(startTime)}${zone}`; } -// Formats a plain "YYYY-MM-DD" date (no time component, e.g. a trip's start -// or end date) without going through timezone-sensitive UTC parsing - using -// `new Date(dateString)` directly can shift the displayed day by one in +// Formats a plain "YYYY-MM-DD" date (no time component, e.g. a trip part's +// start or end date) without going through timezone-sensitive UTC parsing - +// using `new Date(dateString)` directly can shift the displayed day by one in // negative-UTC-offset timezones since bare date strings parse as UTC midnight. export function formatDateOnly( dateString: string, @@ -410,7 +410,8 @@ export function formatDurationHoursMinutes(durationSeconds: number): string { return minutes > 0 ? `${hours}h ${minutes}min` : `${hours}h`; } -// Formats a trip's start/end date range for display, e.g. "Jun 1 - Jun 8, 2024". +// Formats a date range - a trip's span, a course's dates - for display, e.g. +// "Jun 1 - Jun 8, 2024". // Returns `undefined` if neither date is set. export function formatTripDateRange( startDate?: string, From dac6ce27118957fa6cd69f54d6ac0dc7ca601b7c Mon Sep 17 00:00:00 2001 From: Aleksei Vesnin Date: Mon, 21 Sep 2026 09:07:00 +0300 Subject: [PATCH 2/2] docs: the list-reorder test names a trip's part dates Co-Authored-By: Claude Opus 5 --- src/hooks/useInfiniteResource.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/useInfiniteResource.test.tsx b/src/hooks/useInfiniteResource.test.tsx index 771a6b97..b0afccbd 100644 --- a/src/hooks/useInfiniteResource.test.tsx +++ b/src/hooks/useInfiniteResource.test.tsx @@ -42,7 +42,8 @@ function ledger(total = 30, perPage = 10) { /** * Simulates an edit that changes the column the server sorts by, moving the * row to `to` in the order. Every list here is sorted by something an edit - * dialog can change - a dive site's name, a trip's start date. + * dialog can change - a dive site's name, the earliest date across a trip's + * parts. */ moveServerSide: (uuid: string, to: number) => { const row = rows.find((one) => one.uuid === uuid)!;