feat(berths,seed): polish detail display + prune ports to Port Nimara + Amador

Berth detail (src/components/berths/berth-tabs.tsx):
- Numeric display polish, exposed by the new NocoDB-sourced seed:
  - Power capacity now renders with kW unit (e.g. "330 kW")
  - Voltage now renders with V unit (e.g. "480 V")
  - All metric/imperial values rounded to <= 2 decimals
    (was: "62.999112 m" -> now: "62.99 m")
  - Nominal Boat Size shows full ft + m pair (was: ft only)

Seed ports (src/lib/db/seed.ts):
- Drop Marina Azzurra and Harbor Royale; install now seeds only:
  - Port Nimara  (the real install)
  - Port Amador  (secondary, for multi-tenant isolation tests / Panama
                  scaffolding)
- Existing dev DBs are not touched; this only affects fresh `pnpm db:seed`
  runs. Users wanting to migrate should drop existing rows in the obsolete
  ports manually before re-seeding.

Verification:
- lint clean, tsc unchanged from baseline (36 pre-existing errors), 858/858
  vitest passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-05-03 15:59:36 +02:00
parent c7ab816c99
commit 21868ee5fc
2 changed files with 51 additions and 23 deletions

View File

@@ -57,13 +57,45 @@ function SpecRow({ label, value }: { label: string; value: React.ReactNode }) {
}
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 = [];
if (ft) parts.push(`${ft} ft`);
if (m) parts.push(`${m} m`);
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',
@@ -97,7 +129,7 @@ function OverviewTab({ berth }: { berth: BerthData }) {
<SpecRow label="Draft" value={formatDim(berth.draftFt, berth.draftM)} />
<SpecRow
label="Nominal Boat Size"
value={berth.nominalBoatSize || berth.nominalBoatSizeM}
value={formatNominalBoatSize(berth.nominalBoatSize, berth.nominalBoatSizeM)}
/>
<SpecRow
label="Water Depth"
@@ -122,8 +154,8 @@ function OverviewTab({ berth }: { berth: BerthData }) {
<CardTitle className="text-sm font-medium">Infrastructure</CardTitle>
</CardHeader>
<CardContent className="pt-0 divide-y">
<SpecRow label="Power Capacity" value={berth.powerCapacity} />
<SpecRow label="Voltage" value={berth.voltage} />
<SpecRow label="Power Capacity" value={formatPower(berth.powerCapacity)} />
<SpecRow label="Voltage" value={formatVoltage(berth.voltage)} />
<SpecRow label="Cleat Type" value={berth.cleatType} />
<SpecRow label="Cleat Capacity" value={berth.cleatCapacity} />
<SpecRow label="Bollard Type" value={berth.bollardType} />