'use client';
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 { BerthReservationsTab } from './berth-reservations-tab';
import { BerthInterestsTab } from './berth-interests-tab';
import { BerthInterestPulse } from './berth-interest-pulse';
type BerthData = {
id: string;
mooringNumber: string;
area: string | null;
status: string;
lengthFt: string | null;
lengthM: string | null;
widthFt: string | null;
widthM: string | null;
draftFt: string | null;
draftM: string | null;
widthIsMinimum: boolean | null;
nominalBoatSize: string | null;
nominalBoatSizeM: string | null;
waterDepth: string | null;
waterDepthM: string | null;
waterDepthIsMinimum: boolean | null;
sidePontoon: string | null;
powerCapacity: string | null;
voltage: string | null;
mooringType: string | null;
cleatType: string | null;
cleatCapacity: string | null;
bollardType: string | null;
bollardCapacity: string | null;
access: string | null;
price: string | null;
priceCurrency: string;
bowFacing: string | null;
berthApproved: boolean | null;
tenureType: string;
tenureYears: number | null;
tenureStartDate: string | null;
tenureEndDate: string | null;
statusLastChangedReason: string | null;
statusLastModified: string | null;
tags: Array<{ id: string; name: string; color: string }>;
};
function SpecRow({ label, value }: { label: string; value: React.ReactNode }) {
if (!value && value !== 0 && value !== false) return null;
// Mobile-first: stack vertically with label on top so long values
// (e.g. "206.69 ft / 62.99 m") never clip at the right edge.
// From `sm` (>=640px) up: switch to the original two-column layout.
return (
{label}
{value}
);
}
function OverviewTab({ berth }: { berth: BerthData }) {
// 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;
const n = Number(v);
if (Number.isNaN(n)) return v;
return n.toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: fractionDigits,
});
};
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;
};
const formatNominalBoatSize = (ft: string | null, m: string | null): string | null => {
const ftFmt = fmt(ft, 0);
const mFmt = fmt(m);
const parts: string[] = [];
if (ftFmt) parts.push(`${ftFmt} ft`);
if (mFmt) parts.push(`${mFmt} m`);
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
who's interested + how warm without clicking into the Interests tab. */}
{/* Specifications */}
Specifications
{/* Infrastructure & Pricing */}
Infrastructure
Tenure & Pricing
{berth.tenureType === 'fixed_term' && (
<>
>
)}
{berth.tags.length > 0 && (
Tags
{berth.tags.map((tag) => (
))}
)}
);
}
function StubTab({ label }: { label: string }) {
return (
);
}
export function buildBerthTabs(berth: BerthData): DetailTab[] {
return [
{
id: 'overview',
label: 'Overview',
content: ,
},
{
id: 'interests',
label: 'Interests',
content: ,
},
{
id: 'reservations',
label: 'Reservations',
content: ,
},
{
id: 'waiting-list',
label: 'Waiting List',
content: ,
},
{
id: 'maintenance',
label: 'Maintenance Log',
content: ,
},
{
id: 'activity',
label: 'Activity',
content: ,
},
];
}