diff --git a/src/components/berths/berth-tabs.tsx b/src/components/berths/berth-tabs.tsx index f057419..b2c4747 100644 --- a/src/components/berths/berth-tabs.tsx +++ b/src/components/berths/berth-tabs.tsx @@ -1,9 +1,13 @@ 'use client'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; + import { type DetailTab } from '@/components/shared/detail-layout'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { TagBadge } from '@/components/shared/tag-badge'; import { EntityActivityFeed } from '@/components/shared/entity-activity-feed'; +import { InlineEditableField } from '@/components/shared/inline-editable-field'; +import { InlineTagEditor } from '@/components/shared/inline-tag-editor'; +import { apiFetch } from '@/lib/api/client'; import { BerthReservationsTab } from './berth-reservations-tab'; import { BerthInterestsTab } from './berth-interests-tab'; import { BerthInterestPulse } from './berth-interest-pulse'; @@ -61,7 +65,73 @@ function SpecRow({ label, value }: { label: string; value: React.ReactNode }) { ); } +function useBerthPatch(berthId: string) { + const qc = useQueryClient(); + return useMutation({ + mutationFn: async (patch: Record) => + apiFetch(`/api/v1/berths/${berthId}`, { + method: 'PATCH', + body: patch, + }), + onSuccess: () => { + qc.invalidateQueries({ queryKey: ['berths', berthId] }); + qc.invalidateQueries({ queryKey: ['berths'] }); + }, + }); +} + +/** + * Editable spec row. Wraps SpecRow with InlineEditableField for fields + * the operator commonly tweaks (length, width, draft, side pontoon, etc). + * Read-only fields (mooringNumber, area) keep using plain SpecRow. + * + * Numeric fields are stored as strings in the schema (postgres NUMERIC); + * the `numeric` flag tells us to parse before sending and display "-" when + * blank. + */ +function EditableSpec({ + label, + value, + field, + patch, + numeric = false, + suffix, +}: { + label: string; + value: string | null; + field: string; + patch: ReturnType; + numeric?: boolean; + suffix?: string; +}) { + return ( +
+ {label} + + { + if (numeric) { + if (next === null || next.trim() === '') { + await patch.mutateAsync({ [field]: null }); + return; + } + const n = Number.parseFloat(next); + if (Number.isNaN(n)) throw new Error('Must be a number'); + await patch.mutateAsync({ [field]: n }); + return; + } + await patch.mutateAsync({ [field]: next }); + }} + placeholder={suffix ? `e.g. 25${suffix ? ` ${suffix}` : ''}` : undefined} + /> + +
+ ); +} + function OverviewTab({ berth }: { berth: BerthData }) { + const patch = useBerthPatch(berth.id); // Round to at most 2 decimals; trim trailing zeros so "5.00" -> "5". const fmt = (v: string | null, fractionDigits = 2): string | null => { if (v == null || v === '') return null; @@ -73,15 +143,9 @@ function OverviewTab({ berth }: { berth: BerthData }) { }); }; - const formatDim = (ft: string | null, m: string | null) => { - const parts = []; - const ftFmt = fmt(ft); - const mFmt = fmt(m); - if (ftFmt) parts.push(`${ftFmt} ft`); - if (mFmt) parts.push(`${mFmt} m`); - return parts.length > 0 ? parts.join(' / ') : null; - }; - + // Read-only display helper for the metric column on dimensions — + // mirrors the pre-edit "X ft / Y m" rendering for fields where only + // the foot value is editable today. const formatNominalBoatSize = (ft: string | null, m: string | null): string | null => { const ftFmt = fmt(ft, 0); const mFmt = fmt(m); @@ -91,24 +155,6 @@ function OverviewTab({ berth }: { berth: BerthData }) { return parts.length > 0 ? parts.join(' / ') : null; }; - const formatPower = (kw: string | null) => { - const v = fmt(kw, 0); - return v ? `${v} kW` : null; - }; - - const formatVoltage = (v: string | null) => { - const fv = fmt(v, 0); - return fv ? `${fv} V` : null; - }; - - const price = berth.price - ? new Intl.NumberFormat('en-US', { - style: 'currency', - currency: berth.priceCurrency || 'USD', - maximumFractionDigits: 0, - }).format(Number(berth.price)) - : null; - return (
{/* Sales pulse - top-of-page so reps doing berth-level triage can see @@ -122,32 +168,61 @@ function OverviewTab({ berth }: { berth: BerthData }) { Specifications - - + + - - - - - - + + + + @@ -159,12 +234,46 @@ function OverviewTab({ berth }: { berth: BerthData }) { Infrastructure - - - - - - + + + + + + @@ -184,24 +293,28 @@ function OverviewTab({ berth }: { berth: BerthData }) { )} - + - {berth.tags.length > 0 && ( - - - Tags - - -
- {berth.tags.map((tag) => ( - - ))} -
-
-
- )} + + + Tags + + + + +