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:
@@ -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 2–3 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` |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -163,10 +163,9 @@ export function BerthDocumentsTab({ berthId }: { berthId: string }) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Berth-spec PDF: the dimensional drawing or surveyor sheet for this slip.
|
||||
Versioned so a misparse can be rolled back. For documents tied to a
|
||||
prospect on this berth (EOI, contract, etc.), open the matching deal
|
||||
in the Interests tab.
|
||||
Berth-spec PDF: the dimensional drawing or surveyor sheet for this slip. Versioned so a
|
||||
misparse can be rolled back. For documents tied to a prospect on this berth (EOI, contract,
|
||||
etc.), open the matching deal in the Interests tab.
|
||||
</p>
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-3">
|
||||
|
||||
@@ -245,7 +245,11 @@ function OverviewTab({ berth }: { berth: BerthData }) {
|
||||
/>
|
||||
<SpecRow
|
||||
label="Nominal Boat Size (m)"
|
||||
value={formatNominalBoatSize(berth.nominalBoatSize, berth.nominalBoatSizeM)?.split(' / ')[1] ?? null}
|
||||
value={
|
||||
formatNominalBoatSize(berth.nominalBoatSize, berth.nominalBoatSizeM)?.split(
|
||||
' / ',
|
||||
)[1] ?? null
|
||||
}
|
||||
/>
|
||||
<EditableSpec
|
||||
label="Water Depth (ft)"
|
||||
|
||||
@@ -4,12 +4,7 @@ import { Info } from 'lucide-react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import {
|
||||
PIPELINE_STAGES,
|
||||
STAGE_LABELS,
|
||||
stageDotClass,
|
||||
type PipelineStage,
|
||||
} from '@/lib/constants';
|
||||
import { PIPELINE_STAGES, STAGE_LABELS, stageDotClass, type PipelineStage } from '@/lib/constants';
|
||||
|
||||
/**
|
||||
* Small popover that decodes the colored stripe on each interest card to
|
||||
|
||||
@@ -276,7 +276,11 @@ export function UserSettings() {
|
||||
<Label htmlFor="settings-phone">Phone</Label>
|
||||
<PhoneInput
|
||||
id="settings-phone"
|
||||
value={phone ? ({ e164: phone, country: (country as never) ?? 'US' } as PhoneInputValue) : null}
|
||||
value={
|
||||
phone
|
||||
? ({ e164: phone, country: (country as never) ?? 'US' } as PhoneInputValue)
|
||||
: null
|
||||
}
|
||||
onChange={(next) => setPhone(next.e164 ?? '')}
|
||||
placeholder="555 0123"
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import { type DialogProps } from "@radix-ui/react-dialog"
|
||||
import { Command as CommandPrimitive } from "cmdk"
|
||||
import { Search } from "lucide-react"
|
||||
import * as React from 'react';
|
||||
import { type DialogProps } from '@radix-ui/react-dialog';
|
||||
import { Command as CommandPrimitive } from 'cmdk';
|
||||
import { Search } from 'lucide-react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog"
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
||||
|
||||
const Command = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive>,
|
||||
@@ -15,13 +15,13 @@ const Command = React.forwardRef<
|
||||
<CommandPrimitive
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
||||
className
|
||||
'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Command.displayName = CommandPrimitive.displayName
|
||||
));
|
||||
Command.displayName = CommandPrimitive.displayName;
|
||||
|
||||
const CommandDialog = ({ children, ...props }: DialogProps) => {
|
||||
return (
|
||||
@@ -32,8 +32,8 @@ const CommandDialog = ({ children, ...props }: DialogProps) => {
|
||||
</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const CommandInput = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Input>,
|
||||
@@ -44,15 +44,15 @@ const CommandInput = React.forwardRef<
|
||||
<CommandPrimitive.Input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
));
|
||||
|
||||
CommandInput.displayName = CommandPrimitive.Input.displayName
|
||||
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
||||
|
||||
const CommandList = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.List>,
|
||||
@@ -64,30 +64,26 @@ const CommandList = React.forwardRef<
|
||||
// event ourselves so the list scrolls regardless of focus state.
|
||||
<CommandPrimitive.List
|
||||
ref={ref}
|
||||
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden overscroll-contain", className)}
|
||||
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden overscroll-contain', className)}
|
||||
onWheel={(event) => {
|
||||
onWheel?.(event)
|
||||
if (event.defaultPrevented) return
|
||||
event.currentTarget.scrollTop += event.deltaY
|
||||
onWheel?.(event);
|
||||
if (event.defaultPrevented) return;
|
||||
event.currentTarget.scrollTop += event.deltaY;
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
));
|
||||
|
||||
CommandList.displayName = CommandPrimitive.List.displayName
|
||||
CommandList.displayName = CommandPrimitive.List.displayName;
|
||||
|
||||
const CommandEmpty = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Empty>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
|
||||
>((props, ref) => (
|
||||
<CommandPrimitive.Empty
|
||||
ref={ref}
|
||||
className="py-6 text-center text-sm"
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
<CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />
|
||||
));
|
||||
|
||||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
|
||||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
||||
|
||||
const CommandGroup = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Group>,
|
||||
@@ -96,14 +92,14 @@ const CommandGroup = React.forwardRef<
|
||||
<CommandPrimitive.Group
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
||||
className
|
||||
'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
));
|
||||
|
||||
CommandGroup.displayName = CommandPrimitive.Group.displayName
|
||||
CommandGroup.displayName = CommandPrimitive.Group.displayName;
|
||||
|
||||
const CommandSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Separator>,
|
||||
@@ -111,11 +107,11 @@ const CommandSeparator = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 h-px bg-border", className)}
|
||||
className={cn('-mx-1 h-px bg-border', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
|
||||
));
|
||||
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
|
||||
|
||||
const CommandItem = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Item>,
|
||||
@@ -124,30 +120,24 @@ const CommandItem = React.forwardRef<
|
||||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
className
|
||||
'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
));
|
||||
|
||||
CommandItem.displayName = CommandPrimitive.Item.displayName
|
||||
CommandItem.displayName = CommandPrimitive.Item.displayName;
|
||||
|
||||
const CommandShortcut = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"ml-auto text-xs tracking-widest text-muted-foreground",
|
||||
className
|
||||
)}
|
||||
className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
CommandShortcut.displayName = "CommandShortcut"
|
||||
);
|
||||
};
|
||||
CommandShortcut.displayName = 'CommandShortcut';
|
||||
|
||||
export {
|
||||
Command,
|
||||
@@ -159,4 +149,4 @@ export {
|
||||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user