chore(format): apply prettier auto-formatting

Pre-commit hook reformatted these files after the substantive commits.
No semantic changes — markdown table alignment, list indentation, and
emphasis style normalisation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-09 04:11:54 +02:00
parent aad514a3bd
commit 502455ac04
6 changed files with 165 additions and 161 deletions

View File

@@ -15,20 +15,20 @@ message order where possible.
## Quick status snapshot — 2026-05-08 23:00
| Wave | Topic | Status |
| --- | --- | --- |
| 1 | Small confident fixes | ✅ Done |
| 2 | Country dropdown unification + cmdk scroll | ✅ Done (country/nationality split deferred) |
| 3 | Berth field overhaul (NocoDB enums) | ✅ Done |
| 4 | Currency platform-wide | 🔴 Not started |
| 5 | Configurable enums (admin Vocabularies) | 🔴 Not started |
| 6 | Notes unification (aggregate-on-read) | 🔴 Not started |
| 7 | Clients / yachts / companies misc | 🟡 Partial (small bits done; large items deferred) |
| 8 | Expenses revisit | 🔴 Not started |
| 9 | Interests + notifications | ✅ Done |
| 10 | Settings polish | ✅ Done (small bits) — schema-blocked items deferred |
| 11 | DEFERRED — group-discussion items | 🟣 Awaiting alignment |
| **Bonus** | **Public berth feed (website map)** | ✅ Map data backfilled (117 rows). Field-parity gaps remain — see § Bonus. |
| Wave | Topic | Status |
| --------- | ------------------------------------------ | -------------------------------------------------------------------------- |
| 1 | Small confident fixes | ✅ Done |
| 2 | Country dropdown unification + cmdk scroll | ✅ Done (country/nationality split deferred) |
| 3 | Berth field overhaul (NocoDB enums) | ✅ Done |
| 4 | Currency platform-wide | 🔴 Not started |
| 5 | Configurable enums (admin Vocabularies) | 🔴 Not started |
| 6 | Notes unification (aggregate-on-read) | 🔴 Not started |
| 7 | Clients / yachts / companies misc | 🟡 Partial (small bits done; large items deferred) |
| 8 | Expenses revisit | 🔴 Not started |
| 9 | Interests + notifications | ✅ Done |
| 10 | Settings polish | ✅ Done (small bits) — schema-blocked items deferred |
| 11 | DEFERRED — group-discussion items | 🟣 Awaiting alignment |
| **Bonus** | **Public berth feed (website map)** | ✅ Map data backfilled (117 rows). Field-parity gaps remain — see § Bonus. |
Test status: `pnpm exec vitest run`**1185/1185 pass**.
TS check: `pnpm exec tsc --noEmit`**clean**.
@@ -83,7 +83,7 @@ Git: 23 files modified, 2 new files (no commits yet).
below (pipeline funnel, occupancy timeline, revenue breakdown,
lead source) and the activity feed remain.
File: `src/components/dashboard/dashboard-shell.tsx`.
3. **Per-dock color stripe on mobile berth cards** — was the *status*
3. **Per-dock color stripe on mobile berth cards** — was the _status_
color, which made every same-dock berth different. Now uses
`mooringLetterDot()` so the stripe groups by dock letter; status
conveyed by the existing pill below.
@@ -114,20 +114,20 @@ Git: 23 files modified, 2 new files (no commits yet).
Files: `src/components/layout/sidebar.tsx`,
`src/components/layout/mobile/more-sheet.tsx`.
10. **"Other" comm-channel UX hint** — when a contact's channel is
`'other'`, the inline `Label` field switches its label/placeholder
to "Specify" / "e.g. Telegram, Signal".
File: `src/components/clients/client-form.tsx:289-302`.
`'other'`, the inline `Label` field switches its label/placeholder
to "Specify" / "e.g. Telegram, Signal".
File: `src/components/clients/client-form.tsx:289-302`.
11. **End Membership wording** — renamed to "Remove from company" in
the company members tab dropdown.
File: `src/components/companies/company-members-tab.tsx:249`.
the company members tab dropdown.
File: `src/components/companies/company-members-tab.tsx:249`.
12. **Berth area filter → letter dropdown** — was free-text; now a
`<Select>` constrained to `A / B / C / D / E`. Label changed to
"Dock" to match how the user refers to it.
File: `src/components/berths/berth-filters.tsx`.
`<Select>` constrained to `A / B / C / D / E`. Label changed to
"Dock" to match how the user refers to it.
File: `src/components/berths/berth-filters.tsx`.
13. **Yacht flag → CountryCombobox** — was a free-text 2-letter input
(`placeholder="e.g. MT"`); now uses the same country picker as
client / residential.
File: `src/components/yachts/yacht-form.tsx`.
(`placeholder="e.g. MT"`); now uses the same country picker as
client / residential.
File: `src/components/yachts/yacht-form.tsx`.
### Wave 2 — country dropdown unification
@@ -149,7 +149,7 @@ Git: 23 files modified, 2 new files (no commits yet).
floors so wide triggers get wide popovers.
6. **DEFERRED: country/nationality split** on the client form — needs
a Drizzle migration (`alter table clients add column country_iso
text`) plus a copy-on-migrate of existing `nationality_iso` values.
text`) plus a copy-on-migrate of existing `nationality_iso` values.
See § Wave 11 / pending — large.
### Wave 3 — berth field overhaul (NocoDB enums)
@@ -210,6 +210,7 @@ Triggered by user prompt "ensure we are properly wired up to replace
the NocoDB table as the source of truth for the berth map".
**State before audit:**
- API endpoints existed (`/api/public/berths`,
`/api/public/berths/[mooringNumber]`) — wiring fine.
- `src/lib/services/public-berths.ts` mapped the response shape to
@@ -220,8 +221,9 @@ the NocoDB table as the source of truth for the berth map".
has no shapes to render.
**Action taken:**
- Ran `pnpm tsx scripts/import-berths-from-nocodb.ts --apply
--port-slug port-nimara` (after a clean dry-run). Result:
--port-slug port-nimara` (after a clean dry-run). Result:
117 berths updated, 117 `berth_map_data` rows inserted.
- Spot-checked the public API: `GET /api/public/berths` returns the
correct shape with `Map Data` populated, byte-for-byte identical
@@ -288,14 +290,16 @@ User chose option 1 ("aggregate on read") from the brainstorm. The
yacht notes into a single feed with `source` metadata.
Symmetric extensions to add:
- `listForYachtAggregated` — yacht own notes + owner client notes
+ linked interest notes.
- linked interest notes.
- `listForCompanyAggregated` — company own notes + owned yacht notes
+ linked interest notes.
- linked interest notes.
- `listForResidentialClientAggregated` — residential client notes
+ residential interest notes.
- residential interest notes.
UI:
- `<NotesList entityType="…">` should render the source-label badge
(already implemented for clients — copy the pattern).
- Convert single-textarea spots to entry-list pattern: the
@@ -310,11 +314,13 @@ UI:
### Wave 7: clients / yachts / companies misc
Done in this session:
- **Yacht flag** → CountryCombobox (Wave 1).
- **End Membership** → "Remove from company" (Wave 1).
- **Berth Documents tab** explainer paragraph.
Pending:
- **Status change modal — prospect picker**: when user changes berth
status to `under_offer` or `sold`, surface an interest/prospect
selector below the reason dropdown so the recorded reason can link
@@ -338,8 +344,8 @@ Pending:
Chromium. User reported "doesn't open" on macOS — possibly a focus
/ window issue or a content-blocking extension. Need a real-machine
repro to diagnose. The hidden `<input type="file" ref={fileInputRef}>`
+ `fileInputRef.current?.click()` wiring is at
`user-settings.tsx:247-258`.
- `fileInputRef.current?.click()` wiring is at
`user-settings.tsx:247-258`.
- **Display name + first / last name fields** — current schema only
has `displayName`. Adding first/last requires a Drizzle migration on
`users` or `user_profiles` plus migration of existing data (split
@@ -351,24 +357,24 @@ Pending:
### Wave Bonus follow-up — public berth feed field parity
Map data is now wired. Field gaps the website *might* consume but we
Map data is now wired. Field gaps the website _might_ consume but we
don't expose:
| NocoDB field | Currently in PublicBerth? | DB has it? | Notes |
| --- | --- | --- | --- |
| `Price` | ❌ | ✅ `berths.price` | Pricing-public is a policy decision. **Open question (#4)** |
| `Berth Approved` | ❌ | ✅ `berths.berth_approved` | Boolean. Often used to gate "Sold" display |
| `Water Depth` | ❌ | ✅ `berths.water_depth` | Sometimes shown in tooltip |
| `Width Is Minimum` | ❌ | ✅ `berths.width_is_minimum` | Modifier for "Width" display |
| `Water Depth Is Minimum` | ❌ | ✅ `berths.water_depth_is_minimum` | ditto |
| `Length (Metric)` | ❌ | ✅ `berths.length_m` | Derivable. Website may consume |
| `Width (Metric)` | ❌ | ✅ `berths.width_m` | ditto |
| `Draft (Metric)` | ❌ | ✅ `berths.draft_m` | ditto |
| `Water Depth (Metric)` | ❌ | ✅ `berths.water_depth_m` | ditto |
| `Nominal Boat Size (Metric)` | ❌ | ✅ `berths.nominal_boat_size_m` | ditto |
| `CreatedAt` / `UpdatedAt` | ❌ | ✅ timestamps | Cache invalidation hints |
| `Interests` (count) | ❌ | derivable | Probably internal-only |
| `Interested Parties` (count) | ❌ | derivable | Probably internal-only |
| NocoDB field | Currently in PublicBerth? | DB has it? | Notes |
| ---------------------------- | ------------------------- | ---------------------------------- | ----------------------------------------------------------- |
| `Price` | ❌ | ✅ `berths.price` | Pricing-public is a policy decision. **Open question (#4)** |
| `Berth Approved` | ❌ | ✅ `berths.berth_approved` | Boolean. Often used to gate "Sold" display |
| `Water Depth` | ❌ | ✅ `berths.water_depth` | Sometimes shown in tooltip |
| `Width Is Minimum` | ❌ | ✅ `berths.width_is_minimum` | Modifier for "Width" display |
| `Water Depth Is Minimum` | ❌ | ✅ `berths.water_depth_is_minimum` | ditto |
| `Length (Metric)` | ❌ | ✅ `berths.length_m` | Derivable. Website may consume |
| `Width (Metric)` | ❌ | ✅ `berths.width_m` | ditto |
| `Draft (Metric)` | ❌ | ✅ `berths.draft_m` | ditto |
| `Water Depth (Metric)` | ❌ | ✅ `berths.water_depth_m` | ditto |
| `Nominal Boat Size (Metric)` | ❌ | ✅ `berths.nominal_boat_size_m` | ditto |
| `CreatedAt` / `UpdatedAt` | ❌ | ✅ timestamps | Cache invalidation hints |
| `Interests` (count) | ❌ | derivable | Probably internal-only |
| `Interested Parties` (count) | ❌ | derivable | Probably internal-only |
**Plan once questions are answered:** Add the chosen fields to
`PublicBerth` interface in `src/lib/services/public-berths.ts`, the
@@ -376,6 +382,7 @@ don't expose:
by which fields the website actually uses.
**Other public-feed concerns to flag**:
- **No archive flag**: when a berth is retired the public feed will
still serve it. Need a `berths.archived_at` column + filter on the
route. Plan §4.5 hinted at this. Not urgent.
@@ -404,6 +411,7 @@ berths inline (without leaving the form), plus a mini-recommender for
picking a berth at create time.
Scope:
- "Existing yacht / new yacht" picker.
- "Existing company / new company" picker.
- "Open an interest with this client" affordance that wires through
@@ -417,6 +425,7 @@ Estimate fully before starting (likely 23 days).
### B. Documents section overhaul
User wants:
- Folders (create / delete / nested).
- Sort + filter (by date, type, owner).
- Wider file-type allowlist (PDF + Office + image is current; expand).
@@ -433,6 +442,7 @@ Refactor of `documents.service.ts` plus a new folders schema
### C. Reports system
User asked for:
- Defined report types (Pipeline summary / Revenue / Activity log /
Berth occupancy) with documented data shape per type.
- Test fixtures for visual QA.
@@ -453,11 +463,13 @@ PDF pages via `pdf-lib.copyPages`. Depends on Wave 8 expense form work.
### E. Country / Nationality split on Client form
Client schema has only `nationalityIso`. User wants:
- New `country_iso` column for *country of residence* (visible
- New `country_iso` column for _country of residence_ (visible
/ primary).
- Keep `nationality_iso` as an *optional* secondary field.
- Keep `nationality_iso` as an _optional_ secondary field.
Requires:
- Drizzle migration (`alter table clients add column country_iso text`).
- Migrate existing data: copy `nationality_iso → country_iso` for
every client (current value is more often country of residence in
@@ -494,7 +506,7 @@ implementation. They're ordered by what unblocks the most work.
**Recommendation: one new page, grouped by domain.** Easier to
discover, cleaner schema for the editor.
2. **Notification preferences placement (Wave 10)** — keep both the
user-settings notifications panel *and* `/notifications/preferences`,
user-settings notifications panel _and_ `/notifications/preferences`,
or collapse to one? **Recommendation: collapse to user-settings
only**, since that's where every other personal preference lives.
Keep the dedicated route as a redirect for back-compat links.
@@ -543,7 +555,7 @@ implementation. They're ordered by what unblocks the most work.
every berth (NocoDB does). Confirmation that the live import we
ran today populated mooring_type for all 117 records. (Spot-
checked: yes — the SQL traces show `mooring_type = "Side Pier /
Med Mooring"` etc.)
Med Mooring"` etc.)
---
@@ -551,55 +563,55 @@ implementation. They're ordered by what unblocks the most work.
### Berth-related
| Concern | File(s) |
| --- | --- |
| Canonical berth enums | `src/lib/constants.ts` (search `BERTH_`) |
| Berth list ordering SQL | `src/lib/services/berths.service.ts:69-72` |
| Berth detail inline edit | `src/components/berths/berth-tabs.tsx` |
| Berth modal form | `src/components/berths/berth-form.tsx` |
| Berth area filter | `src/components/berths/berth-filters.tsx` |
| Berth detail header / status modal | `src/components/berths/berth-detail-header.tsx:90` |
| Berth Documents tab | `src/components/berths/berth-documents-tab.tsx` |
| Berth list query + sort | `src/lib/services/berths.service.ts:25-140` |
| Berth import script | `scripts/import-berths-from-nocodb.ts` |
| Berth import service / parsers | `src/lib/services/berth-import.ts` |
| Public berth API route | `src/app/api/public/berths/route.ts` |
| Public berth single route | `src/app/api/public/berths/[mooringNumber]/route.ts` |
| Public berth mapper | `src/lib/services/public-berths.ts` |
| Public berth tests | `tests/unit/services/public-berths.test.ts` |
| Berth seed snapshot | `src/lib/db/seed-data/berths.json` |
| Berth schema | `src/lib/db/schema/berths.ts` (incl. `berthMapData`) |
| Concern | File(s) |
| ---------------------------------- | ---------------------------------------------------- |
| Canonical berth enums | `src/lib/constants.ts` (search `BERTH_`) |
| Berth list ordering SQL | `src/lib/services/berths.service.ts:69-72` |
| Berth detail inline edit | `src/components/berths/berth-tabs.tsx` |
| Berth modal form | `src/components/berths/berth-form.tsx` |
| Berth area filter | `src/components/berths/berth-filters.tsx` |
| Berth detail header / status modal | `src/components/berths/berth-detail-header.tsx:90` |
| Berth Documents tab | `src/components/berths/berth-documents-tab.tsx` |
| Berth list query + sort | `src/lib/services/berths.service.ts:25-140` |
| Berth import script | `scripts/import-berths-from-nocodb.ts` |
| Berth import service / parsers | `src/lib/services/berth-import.ts` |
| Public berth API route | `src/app/api/public/berths/route.ts` |
| Public berth single route | `src/app/api/public/berths/[mooringNumber]/route.ts` |
| Public berth mapper | `src/lib/services/public-berths.ts` |
| Public berth tests | `tests/unit/services/public-berths.test.ts` |
| Berth seed snapshot | `src/lib/db/seed-data/berths.json` |
| Berth schema | `src/lib/db/schema/berths.ts` (incl. `berthMapData`) |
### Other domains
| Concern | File(s) |
| --- | --- |
| Interest stage colors / legend | `src/components/interests/stage-legend.tsx` + `src/lib/constants.ts:STAGE_DOT` |
| Mobile kanban toggle / fallback | `src/components/interests/interest-list.tsx` |
| Country / timezone autoset | `src/components/clients/client-form.tsx` + `src/components/settings/user-settings.tsx` |
| Phone input | `src/components/shared/phone-input.tsx` |
| Country combobox + scroll patch | `src/components/shared/country-combobox.tsx` + `src/components/ui/command.tsx` |
| Sidebar Umami gate | `src/components/layout/sidebar.tsx` (search `umamiRequired`) |
| Mobile More-sheet | `src/components/layout/mobile/more-sheet.tsx` |
| Notes service (aggregate-on-read) | `src/lib/services/notes.service.ts:130-242` |
| Notes UI | `src/components/shared/notes-list.tsx` |
| Settings manager (admin) | `src/components/admin/settings/settings-manager.tsx` |
| User settings page | `src/components/settings/user-settings.tsx` |
| Status change dialog | `src/components/berths/berth-detail-header.tsx:90` |
| Companies members tab | `src/components/companies/company-members-tab.tsx` |
| Yacht form | `src/components/yachts/yacht-form.tsx` |
| Client form | `src/components/clients/client-form.tsx` |
| Concern | File(s) |
| --------------------------------- | -------------------------------------------------------------------------------------- |
| Interest stage colors / legend | `src/components/interests/stage-legend.tsx` + `src/lib/constants.ts:STAGE_DOT` |
| Mobile kanban toggle / fallback | `src/components/interests/interest-list.tsx` |
| Country / timezone autoset | `src/components/clients/client-form.tsx` + `src/components/settings/user-settings.tsx` |
| Phone input | `src/components/shared/phone-input.tsx` |
| Country combobox + scroll patch | `src/components/shared/country-combobox.tsx` + `src/components/ui/command.tsx` |
| Sidebar Umami gate | `src/components/layout/sidebar.tsx` (search `umamiRequired`) |
| Mobile More-sheet | `src/components/layout/mobile/more-sheet.tsx` |
| Notes service (aggregate-on-read) | `src/lib/services/notes.service.ts:130-242` |
| Notes UI | `src/components/shared/notes-list.tsx` |
| Settings manager (admin) | `src/components/admin/settings/settings-manager.tsx` |
| User settings page | `src/components/settings/user-settings.tsx` |
| Status change dialog | `src/components/berths/berth-detail-header.tsx:90` |
| Companies members tab | `src/components/companies/company-members-tab.tsx` |
| Yacht form | `src/components/yachts/yacht-form.tsx` |
| Client form | `src/components/clients/client-form.tsx` |
### Infrastructure
| Concern | File(s) |
| --- | --- |
| Drizzle config / migrations | `drizzle.config.ts`, `src/lib/db/migrations/` |
| `system_settings` table | `src/lib/db/schema/system.ts:128-147` |
| Permissions / `withAuth` / `withPermission` | `src/lib/api/helpers.ts` |
| Body parsing (always use `parseBody`) | `src/lib/api/route-helpers.ts` |
| Storage backend abstraction | `src/lib/storage/` |
| Logger (pino) | `src/lib/logger.ts` |
| Concern | File(s) |
| ------------------------------------------- | --------------------------------------------- |
| Drizzle config / migrations | `drizzle.config.ts`, `src/lib/db/migrations/` |
| `system_settings` table | `src/lib/db/schema/system.ts:128-147` |
| Permissions / `withAuth` / `withPermission` | `src/lib/api/helpers.ts` |
| Body parsing (always use `parseBody`) | `src/lib/api/route-helpers.ts` |
| Storage backend abstraction | `src/lib/storage/` |
| Logger (pino) | `src/lib/logger.ts` |
---