Files
pn-new-crm/src/components/residential/residential-interests-list.tsx

207 lines
7.7 KiB
TypeScript
Raw Normal View History

feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
'use client';
import { useState } from 'react';
import Link from 'next/link';
feat(uat-batch): Group A quick-fixes — 7 items shipped, 5 verified pre-shipped Sweeps Group A of the 2026-05-21 remaining-plan. Several items the plan listed as open turned out to already be shipped (annotation gap in the master doc) — those are confirmed in the commit notes. Shipped now: A1 Documenso settings: collapsed `V2_FEATURE_FIELDS` + `CONTRACT_RESERVATION_FIELDS` (legacy SettingsFormCard) into `RegistryDrivenForm` sections (`documenso.behavior` + `documenso.templates`). Every Documenso setting now flows through the registry path that surfaces the env-fallback / port / global source badge per field. EOI generation card retitled to "Templates & signing pathway" since it now covers EOI + reservation + contract template IDs (registry already had all three under `documenso.templates`). A2 WatchersCard empty state: bumped `mb-3 → mb-4 pb-1` so the "No one is watching yet" line has breathing room above the "Add a watcher…" select. A4 /invoices/upload-receipts guide copy: terse luxury-CRM tone. Drop "Snap a photo", "fancy phone camera", "No typing. No spreadsheets." Tighten OCR explainer to one sentence; action-oriented step + best-practices headers. A5 Pageviews chart X-axis: added `interval="preserveStartEnd"` + `minTickGap={52}` so multi-week ranges thin out the middle ticks instead of overlapping. The MM-DD formatter was already in place from an earlier session. A7 Inbox doc comment: was stale ("Alerts first, Reminders second") but the JSX already had Reminders before Alerts. Fixed the docstring. A9 CommandList scroll-cap: `max-h-[300px]` now `max-h-[min(300px, var(--radix-popover-content-available-height,300px))]` so the cmdk list never extends past the host Popover's available area. Non-Popover hosts fall through to the 300px static cap. A10 DropdownMenuContent: `max-h-96` now `max-h-[min(24rem,var(--radix-dropdown-menu-content- available-height,24rem))]` for the same available-space behaviour on long menus near the viewport edge. A11 Residential InterestsTab (list page): row gets an onClick → `router.push`; first-cell Link stops propagation so middle- click / Cmd-click "open in new tab" still works. A12 StageStepper: gained a stage-name row below the bar showing every reached stage's short label inline (muted for future stages). `size="xs"` variant keeps the cramped table-cell footprint intact (no labels). Already shipped (just annotation gap in master doc): A3 EOI "Mark as signed without file" button — line 599 of interest-eoi-tab.tsx, parent passes onMarkSigned. Master doc already has `SHIPPED in 52342ee` annotation. A6 Pageviews vs Sessions explainer — Info popover at line 157-181 of website-analytics-shell.tsx. A8 BulkAddBerthsWizard CurrencySelect — line 376 (apply-to-all) + line 456 (per-row). Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:34:20 +02:00
import { useParams, useRouter } from 'next/navigation';
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
import { useQuery } from '@tanstack/react-query';
import { Input } from '@/components/ui/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { PageHeader } from '@/components/shared/page-header';
import { useRealtimeInvalidation } from '@/hooks/use-realtime-invalidation';
import { apiFetch } from '@/lib/api/client';
import { PIPELINE_STAGES } from '@/lib/validators/residential';
interface ResidentialInterestRow {
id: string;
residentialClientId: string;
pipelineStage: string;
source: string | null;
notes: string | null;
preferences: string | null;
assignedTo: string | null;
updatedAt: string;
}
interface ListResponse {
data: ResidentialInterestRow[];
pagination: { total: number };
}
const STAGE_LABELS: Record<string, string> = {
new: 'New',
contacted: 'Contacted',
viewing_scheduled: 'Viewing scheduled',
offer_made: 'Offer made',
offer_accepted: 'Offer accepted',
closed_won: 'Closed - won',
closed_lost: 'Closed - lost',
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
};
export function ResidentialInterestsList() {
const params = useParams<{ portSlug: string }>();
feat(uat-batch): Group A quick-fixes — 7 items shipped, 5 verified pre-shipped Sweeps Group A of the 2026-05-21 remaining-plan. Several items the plan listed as open turned out to already be shipped (annotation gap in the master doc) — those are confirmed in the commit notes. Shipped now: A1 Documenso settings: collapsed `V2_FEATURE_FIELDS` + `CONTRACT_RESERVATION_FIELDS` (legacy SettingsFormCard) into `RegistryDrivenForm` sections (`documenso.behavior` + `documenso.templates`). Every Documenso setting now flows through the registry path that surfaces the env-fallback / port / global source badge per field. EOI generation card retitled to "Templates & signing pathway" since it now covers EOI + reservation + contract template IDs (registry already had all three under `documenso.templates`). A2 WatchersCard empty state: bumped `mb-3 → mb-4 pb-1` so the "No one is watching yet" line has breathing room above the "Add a watcher…" select. A4 /invoices/upload-receipts guide copy: terse luxury-CRM tone. Drop "Snap a photo", "fancy phone camera", "No typing. No spreadsheets." Tighten OCR explainer to one sentence; action-oriented step + best-practices headers. A5 Pageviews chart X-axis: added `interval="preserveStartEnd"` + `minTickGap={52}` so multi-week ranges thin out the middle ticks instead of overlapping. The MM-DD formatter was already in place from an earlier session. A7 Inbox doc comment: was stale ("Alerts first, Reminders second") but the JSX already had Reminders before Alerts. Fixed the docstring. A9 CommandList scroll-cap: `max-h-[300px]` now `max-h-[min(300px, var(--radix-popover-content-available-height,300px))]` so the cmdk list never extends past the host Popover's available area. Non-Popover hosts fall through to the 300px static cap. A10 DropdownMenuContent: `max-h-96` now `max-h-[min(24rem,var(--radix-dropdown-menu-content- available-height,24rem))]` for the same available-space behaviour on long menus near the viewport edge. A11 Residential InterestsTab (list page): row gets an onClick → `router.push`; first-cell Link stops propagation so middle- click / Cmd-click "open in new tab" still works. A12 StageStepper: gained a stage-name row below the bar showing every reached stage's short label inline (muted for future stages). `size="xs"` variant keeps the cramped table-cell footprint intact (no labels). Already shipped (just annotation gap in master doc): A3 EOI "Mark as signed without file" button — line 599 of interest-eoi-tab.tsx, parent passes onMarkSigned. Master doc already has `SHIPPED in 52342ee` annotation. A6 Pageviews vs Sessions explainer — Info popover at line 157-181 of website-analytics-shell.tsx. A8 BulkAddBerthsWizard CurrencySelect — line 376 (apply-to-all) + line 456 (per-row). Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:34:20 +02:00
const router = useRouter();
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
const portSlug = params?.portSlug ?? '';
const [search, setSearch] = useState('');
const [stage, setStage] = useState<string>('all');
const { data, isLoading } = useQuery<ListResponse>({
queryKey: ['residential-interests', { search, stage }],
queryFn: () => {
const qs = new URLSearchParams({ search, limit: '50' });
if (stage !== 'all') qs.set('pipelineStage', stage);
return apiFetch(`/api/v1/residential/interests?${qs.toString()}`);
},
});
useRealtimeInvalidation({
'residential_interest:created': [['residential-interests']],
'residential_interest:updated': [['residential-interests']],
'residential_interest:archived': [['residential-interests']],
});
return (
<div className="space-y-4">
<PageHeader
title="Residential Interests"
description="Inquiries flowing through the residential pipeline"
/>
<div className="flex items-center gap-2">
<Input
placeholder="Search notes / preferences…"
value={search}
onChange={(e) => setSearch(e.target.value)}
feat: autonomous backlog push — admin UX overhaul + storage parity + residential parity + Documenso Phase 1 Massive multi-area push driven by docs/admin-ux-backlog.md. Every byte path now goes through getStorageBackend() so signed EOIs, contracts, brochures, berth PDFs, files, avatars, branding logos, and DB backups all work identically on S3 and filesystem backends. USER SETTINGS (rebuild) - Country + Timezone selectors with cross-defaulting - Browser-detected timezone banner ("Looks like you're in Europe/Paris…") - Email change with verification flow (user_email_changes table, OLD-address cancel link + NEW-address confirm link) + EMAIL_CHANGE_INSTANT=true dev shortcut - Password reset triggered via better-auth requestPasswordReset - Profile photo upload + crop (square 256×256) via shared <ImageCropperDialog> + /api/v1/me/avatar BRANDING - Shared <ImageCropperDialog> using react-easy-crop - Logo upload + crop in /admin/branding (writes via /api/v1/admin/settings/image -> storage backend) - Email header/footer HTML defaults injectable via "Insert default" - SettingsFormCard new field types: timezone (combobox), image-upload STORAGE ADMIN OVERHAUL - S3 config form FIRST, swap action SECOND - Test connection before any switch - Two-button switch: "Switch + migrate" vs "Switch only" with warning modals - runMigration() honours skipMigration flag - /api/ready + system-monitoring health check use the active storage backend instead of always probing MinIO - Filesystem backend already had full feature parity — verified BACKUP MANAGEMENT (real) - New backup_jobs table (id / status / trigger / size / storage_path) - runBackup() service spawns pg_dump --format=custom, streams to active storage backend via getStorageBackend().put() - /admin/backup page: trigger, history, download .dump for restore - Super-admin gated AI ADMIN PANEL - /admin/ai consolidates master switch + monthly token cap + provider credentials - Per-feature settings (OCR, berth-PDF parser, recommender) linked from the same page ONBOARDING WIZARD - /admin/onboarding now real with auto-checked steps - Reads each setting key + lists endpoint (roles/users/tags) to decide completion - Manual checkboxes for steps without an auto-detect signal - Progress bar + Mark done/Mark incomplete buttons - State persisted in system_settings.onboarding_manual_status RESIDENTIAL PARITY (full) - New residential_client_notes + residential_interest_notes tables (mirror marina-side shape) - Polymorphic notes.service.ts extended (verifyParent, listForEntity, create, update, delete) for residential_clients/_interests - <NotesList> component accepts the new entity types - 4 new note endpoints (GET/POST/PATCH/DELETE for clients + interests) - 2 new activity endpoints (residential clients + interests) - residential-client-tabs.tsx + residential-interest-tabs.tsx use DetailLayout (Overview / Interests / Notes / Activity) - residential-client-detail-header.tsx mirrors marina-side strip - useBreadcrumbHint wired into both detail components - Configurable Assigned-to dropdown (residential_interests.view perm) CONFIGURABLE RESIDENTIAL STAGES - residential-stages.service.ts with list / save / orphan-check - /api/v1/residential/stages GET/PUT - /admin/residential-stages admin UI with reassign-on-remove modal - Validators relaxed from z.enum to z.string DOCUMENSO PHASE 1 - Schema: document_signers.invited_at / opened_at / last_reminder_sent_at / signing_token (+ idx_ds_signing_token) - Schema: documents.completion_cc_emails (text[]) + auto_reminder_interval_days (int) - transformSigningUrl() now maps SignerRole -> URL segment via ROLE_TO_URL_SEGMENT (approver->cc, witness->witness) — fixes Risk #5 where approver invites landed on /sign/error - POST /api/v1/documents/[id]/send-invitation with auto-pick of next pending signer - Per-port settings: documenso_developer_label / _approver_label + documenso_developer_user_id / _approver_user_id (Phase 7 Project Director RBAC binding fields) ADMIN UX RAPID-FIRE - Sidebar collapse removed (always-expanded design) - Audit log: input sizes (h-9), date pickers w-44, action cell sub-label so single-row entries aren't blank - Sales email config: token list <details> + tooltips on threshold + body fields - Custom Settings card: long-form description - Reminder digest timezone uses TimezoneCombobox - Port form: currency dropdown (10 common currencies) + timezone combobox + brand color picker - Permissions count badge opens modal with granted/denied per resource - Role names display-normalized via prettifyRoleName - Tag form: native input type=color - Custom Fields page: amber heads-up about non-integration - Settings manager: select field type + fallthrough_policy as dropdown - Storage admin S3 fields ship as proper password + boolean LIST PAGES - Residential client list: clickable email/phone (mailto/tel/wa.me) - Residential interests + Documents Hub search inputs sized h-9 CURRENCY API - scripts/test-currency-api.ts verifies live Frankfurter fetch -> DB upsert -> getRate -> convert. Inverse-rate drift <=0.001 TESTS - 1185/1185 vitest passing - tsc clean - eslint 0 errors (16 pre-existing warnings) Note: WEBSITE_INTAKE_SECRET added to .env.example but committed separately due to pre-commit hook policy on .env* files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:02:12 +02:00
className="max-w-sm h-9"
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
/>
<Select value={stage} onValueChange={setStage}>
<SelectTrigger className="w-52">
<SelectValue placeholder="All stages" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All stages</SelectItem>
{PIPELINE_STAGES.map((s) => (
<SelectItem key={s} value={s}>
{STAGE_LABELS[s] ?? s}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Desktop: table layout. Hidden below lg; mobile renders cards. */}
<div className="hidden lg:block rounded-lg border bg-card overflow-hidden">
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
<table className="w-full text-sm">
<thead className="bg-muted/40 text-xs text-muted-foreground">
<tr>
<th className="text-left font-medium px-3 py-2">Stage</th>
<th className="text-left font-medium px-3 py-2">Preferences</th>
<th className="text-left font-medium px-3 py-2">Notes</th>
<th className="text-left font-medium px-3 py-2">Source</th>
<th className="text-left font-medium px-3 py-2">Updated</th>
</tr>
</thead>
<tbody>
{isLoading && (
<tr>
<td colSpan={5} className="px-3 py-8 text-center text-muted-foreground">
Loading
</td>
</tr>
)}
{!isLoading && data?.data.length === 0 && (
<tr>
<td colSpan={5} className="px-3 py-8 text-center text-muted-foreground">
No interests match.
</td>
</tr>
)}
{data?.data.map((i) => (
<tr
key={i.id}
className="border-t hover:bg-muted/30 transition-colors cursor-pointer"
feat(uat-batch): Group A quick-fixes — 7 items shipped, 5 verified pre-shipped Sweeps Group A of the 2026-05-21 remaining-plan. Several items the plan listed as open turned out to already be shipped (annotation gap in the master doc) — those are confirmed in the commit notes. Shipped now: A1 Documenso settings: collapsed `V2_FEATURE_FIELDS` + `CONTRACT_RESERVATION_FIELDS` (legacy SettingsFormCard) into `RegistryDrivenForm` sections (`documenso.behavior` + `documenso.templates`). Every Documenso setting now flows through the registry path that surfaces the env-fallback / port / global source badge per field. EOI generation card retitled to "Templates & signing pathway" since it now covers EOI + reservation + contract template IDs (registry already had all three under `documenso.templates`). A2 WatchersCard empty state: bumped `mb-3 → mb-4 pb-1` so the "No one is watching yet" line has breathing room above the "Add a watcher…" select. A4 /invoices/upload-receipts guide copy: terse luxury-CRM tone. Drop "Snap a photo", "fancy phone camera", "No typing. No spreadsheets." Tighten OCR explainer to one sentence; action-oriented step + best-practices headers. A5 Pageviews chart X-axis: added `interval="preserveStartEnd"` + `minTickGap={52}` so multi-week ranges thin out the middle ticks instead of overlapping. The MM-DD formatter was already in place from an earlier session. A7 Inbox doc comment: was stale ("Alerts first, Reminders second") but the JSX already had Reminders before Alerts. Fixed the docstring. A9 CommandList scroll-cap: `max-h-[300px]` now `max-h-[min(300px, var(--radix-popover-content-available-height,300px))]` so the cmdk list never extends past the host Popover's available area. Non-Popover hosts fall through to the 300px static cap. A10 DropdownMenuContent: `max-h-96` now `max-h-[min(24rem,var(--radix-dropdown-menu-content- available-height,24rem))]` for the same available-space behaviour on long menus near the viewport edge. A11 Residential InterestsTab (list page): row gets an onClick → `router.push`; first-cell Link stops propagation so middle- click / Cmd-click "open in new tab" still works. A12 StageStepper: gained a stage-name row below the bar showing every reached stage's short label inline (muted for future stages). `size="xs"` variant keeps the cramped table-cell footprint intact (no labels). Already shipped (just annotation gap in master doc): A3 EOI "Mark as signed without file" button — line 599 of interest-eoi-tab.tsx, parent passes onMarkSigned. Master doc already has `SHIPPED in 52342ee` annotation. A6 Pageviews vs Sessions explainer — Info popover at line 157-181 of website-analytics-shell.tsx. A8 BulkAddBerthsWizard CurrencySelect — line 376 (apply-to-all) + line 456 (per-row). Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:34:20 +02:00
// Whole-row navigation — clicking anywhere on the row opens
// the interest detail. The first-cell Link still works for
// middle-click / Cmd+click "open in new tab" + keyboard
// navigation; the row onClick covers the common case where
// reps click on Preferences / Notes columns.
onClick={() => router.push(`/${portSlug}/residential/interests/${i.id}` as never)}
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
>
<td className="px-3 py-2">
<Link
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
href={`/${portSlug}/residential/interests/${i.id}` as any}
className="font-medium hover:underline"
feat(uat-batch): Group A quick-fixes — 7 items shipped, 5 verified pre-shipped Sweeps Group A of the 2026-05-21 remaining-plan. Several items the plan listed as open turned out to already be shipped (annotation gap in the master doc) — those are confirmed in the commit notes. Shipped now: A1 Documenso settings: collapsed `V2_FEATURE_FIELDS` + `CONTRACT_RESERVATION_FIELDS` (legacy SettingsFormCard) into `RegistryDrivenForm` sections (`documenso.behavior` + `documenso.templates`). Every Documenso setting now flows through the registry path that surfaces the env-fallback / port / global source badge per field. EOI generation card retitled to "Templates & signing pathway" since it now covers EOI + reservation + contract template IDs (registry already had all three under `documenso.templates`). A2 WatchersCard empty state: bumped `mb-3 → mb-4 pb-1` so the "No one is watching yet" line has breathing room above the "Add a watcher…" select. A4 /invoices/upload-receipts guide copy: terse luxury-CRM tone. Drop "Snap a photo", "fancy phone camera", "No typing. No spreadsheets." Tighten OCR explainer to one sentence; action-oriented step + best-practices headers. A5 Pageviews chart X-axis: added `interval="preserveStartEnd"` + `minTickGap={52}` so multi-week ranges thin out the middle ticks instead of overlapping. The MM-DD formatter was already in place from an earlier session. A7 Inbox doc comment: was stale ("Alerts first, Reminders second") but the JSX already had Reminders before Alerts. Fixed the docstring. A9 CommandList scroll-cap: `max-h-[300px]` now `max-h-[min(300px, var(--radix-popover-content-available-height,300px))]` so the cmdk list never extends past the host Popover's available area. Non-Popover hosts fall through to the 300px static cap. A10 DropdownMenuContent: `max-h-96` now `max-h-[min(24rem,var(--radix-dropdown-menu-content- available-height,24rem))]` for the same available-space behaviour on long menus near the viewport edge. A11 Residential InterestsTab (list page): row gets an onClick → `router.push`; first-cell Link stops propagation so middle- click / Cmd-click "open in new tab" still works. A12 StageStepper: gained a stage-name row below the bar showing every reached stage's short label inline (muted for future stages). `size="xs"` variant keeps the cramped table-cell footprint intact (no labels). Already shipped (just annotation gap in master doc): A3 EOI "Mark as signed without file" button — line 599 of interest-eoi-tab.tsx, parent passes onMarkSigned. Master doc already has `SHIPPED in 52342ee` annotation. A6 Pageviews vs Sessions explainer — Info popover at line 157-181 of website-analytics-shell.tsx. A8 BulkAddBerthsWizard CurrencySelect — line 376 (apply-to-all) + line 456 (per-row). Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:34:20 +02:00
// Don't fire the row's onClick when the rep middle-clicks
// the link — the Link's native handler covers that path.
onClick={(e) => e.stopPropagation()}
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
>
{STAGE_LABELS[i.pipelineStage] ?? i.pipelineStage}
</Link>
</td>
<td className="px-3 py-2 text-muted-foreground truncate max-w-xs">
{i.preferences ?? '-'}
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
</td>
<td className="px-3 py-2 text-muted-foreground truncate max-w-xs">
{i.notes ?? '-'}
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
</td>
<td className="px-3 py-2 capitalize text-muted-foreground">{i.source ?? '-'}</td>
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
<td className="px-3 py-2 text-muted-foreground text-xs">
{new Date(i.updatedAt).toLocaleDateString()}
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Mobile: card list. Stage as the headline (it's the most actionable
field for triage), preferences/notes truncated below. */}
<div className="lg:hidden space-y-2">
{isLoading && (
<div className="rounded-lg border bg-card px-3 py-8 text-center text-sm text-muted-foreground">
Loading
</div>
)}
{!isLoading && data?.data.length === 0 && (
<div className="rounded-lg border bg-card px-3 py-8 text-center text-sm text-muted-foreground">
No interests match.
</div>
)}
{data?.data.map((i) => (
<Link
key={i.id}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
href={`/${portSlug}/residential/interests/${i.id}` as any}
className="block rounded-lg border bg-card p-3 transition-colors hover:bg-muted/30"
>
<div className="flex items-start justify-between gap-2">
<p className="font-medium text-sm">
{STAGE_LABELS[i.pipelineStage] ?? i.pipelineStage}
</p>
<span className="shrink-0 text-[11px] text-muted-foreground">
{new Date(i.updatedAt).toLocaleDateString()}
</span>
</div>
{i.preferences ? (
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">{i.preferences}</p>
) : null}
{i.notes ? (
<p className="mt-1 line-clamp-1 text-xs text-muted-foreground/80">{i.notes}</p>
) : null}
{i.source ? (
<p className="mt-1 text-[11px] capitalize text-muted-foreground">{i.source}</p>
) : null}
</Link>
))}
</div>
feat(platform): residential module + admin UI + reliability fixes Residential platform - New schema: residentialClients, residentialInterests (separate from marina/yacht clients) with migration 0010 - Service layer with CRUD + audit + sockets + per-port portal toggle - v1 + public API routes (/api/v1/residential/*, /api/public/residential-inquiries) - List + detail pages with inline editing for clients and interests - Per-user residentialAccess toggle on userPortRoles (migration 0011) - Permission keys: residential_clients, residential_interests - Sidebar nav + role form integration - Smoke spec covering page loads, UI create flow, public endpoint Admin & shared UI - Admin → Forms (form templates CRUD) with validators + service - Notification preferences page (in-app + email per type) - Email composition + accounts list + threads view - Branded auth shell shared across CRM + portal auth surfaces - Inline editing extended to yacht/company/interest detail pages - InlineTagEditor + per-entity tags endpoints (yachts, companies) - Notes service polymorphic across clients/interests/yachts/companies - Client list columns: yachtCount + companyCount badges - Reservation file-download via presigned URL (replaces stale <a href>) Route handler refactor - Extracted yachts/companies/berths reservation handlers to sibling handlers.ts files (Next.js 15 route.ts only allows specific exports) Reliability fixes - apiFetch double-stringify bug fixed across 13 components (apiFetch already JSON.stringifies its body; passing a stringified body produced double-encoded JSON which failed zod validation) - SocketProvider gated behind useSyncExternalStore-based mount check to avoid useSession() SSR crashes under React 19 + Next 15 - apiFetch falls back to URL-pathname → port-id resolution when the Zustand store hasn't hydrated yet (fresh contexts, e2e tests) - CRM invite flow (schema, service, route, email, dev script) - Dashboard route → [portSlug]/dashboard/page.tsx + redirect - Document the dev-server restart-after-migration gotcha in CLAUDE.md Tests - 5-case residential smoke spec - Integration test updates for new service signatures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:54:32 +02:00
</div>
);
}