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:
@@ -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} />
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
* Seed script for Port Nimara CRM.
|
||||
*
|
||||
* Top-level orchestrator:
|
||||
* 1. Create 3 ports (idempotent):
|
||||
* - Port Nimara
|
||||
* - Marina Azzurra
|
||||
* - Harbor Royale
|
||||
* 1. Create the operational ports (idempotent):
|
||||
* - Port Nimara (primary install — the real marina)
|
||||
* - Port Amador (secondary, kept for multi-tenant isolation tests
|
||||
* and as scaffolding for a future Panama install)
|
||||
* 2. Create 5 system roles with full permission maps
|
||||
* 3. Create the super admin user profile placeholder (matt@portnimara.com)
|
||||
* 4. For each port, call `seedPortData(portId, portSlug)` from seed-data.ts
|
||||
* to produce the realistic multi-cardinality fixture
|
||||
* (berths, clients, companies, yachts, memberships, interests,
|
||||
* reservations, ownership-transfer history).
|
||||
* (117 berths from the NocoDB snapshot, plus clients, companies, yachts,
|
||||
* memberships, interests, reservations, ownership-transfer history).
|
||||
* 5. Print a summary.
|
||||
*
|
||||
* Run with: pnpm db:seed
|
||||
@@ -413,19 +413,15 @@ const PORT_DEFINITIONS: Array<{
|
||||
defaultCurrency: 'USD',
|
||||
timezone: 'America/Anguilla',
|
||||
},
|
||||
// Second port kept for multi-tenant isolation tests (cross-port scoping,
|
||||
// permission boundaries). Drop or rename if the production install is
|
||||
// single-port.
|
||||
{
|
||||
name: 'Marina Azzurra',
|
||||
slug: 'marina-azzurra',
|
||||
primaryColor: '#2E86AB',
|
||||
defaultCurrency: 'EUR',
|
||||
timezone: 'Europe/Rome',
|
||||
},
|
||||
{
|
||||
name: 'Harbor Royale',
|
||||
slug: 'harbor-royale',
|
||||
primaryColor: '#8B1E3F',
|
||||
defaultCurrency: 'GBP',
|
||||
timezone: 'Europe/London',
|
||||
name: 'Port Amador',
|
||||
slug: 'port-amador',
|
||||
primaryColor: '#D97706',
|
||||
defaultCurrency: 'USD',
|
||||
timezone: 'America/Panama',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user