Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/components/courses/course-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ export function CourseDialog({
name="end_date"
render={({ field }) => (
<FormItem>
{/* 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. */}
<FormLabel>End date</FormLabel>
<FormControl>
<DatePicker
Expand Down
3 changes: 2 additions & 1 deletion src/hooks/useInfiniteResource.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)!;
Expand Down
11 changes: 6 additions & 5 deletions src/hooks/useInfiniteResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,12 @@ export function useInfiniteResource<T>(
* 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.
Expand Down
12 changes: 6 additions & 6 deletions src/lib/api/courses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
9 changes: 5 additions & 4 deletions src/lib/date-time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Loading