feat(reports): financial report-level empty state

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 10:19:57 +02:00
parent 24e88ae32e
commit 91d8ee226b
2 changed files with 46 additions and 1 deletions

View File

@@ -30,6 +30,9 @@ import { rangeToBounds, type DateRange } from '@/lib/analytics/range';
import { apiFetch } from '@/lib/api/client';
import { formatMoney, formatMoneyCompact, formatNumber } from '@/lib/reports/format-currency';
import type { ReportPayload } from '@/lib/reports/types';
import { ReportEmptyState } from '@/components/reports/shared/report-empty-state';
import type { Route } from 'next';
import { Wallet } from 'lucide-react';
// ─── Payload types (mirror the /api/v1/reports/financial response) ───────────
@@ -119,6 +122,7 @@ interface FinancialPayload {
refundLog: RefundRow[];
expenseLedger: ExpenseLedgerRow[];
range: { from: string; to: string };
hasData: boolean;
};
}
@@ -138,7 +142,7 @@ const DONUT_COLORS = [
'hsl(var(--chart-6))',
];
export function FinancialReportClient({ portSlug: _portSlug }: { portSlug: string }) {
export function FinancialReportClient({ portSlug }: { portSlug: string }) {
const searchParams = useSearchParams();
const initialTemplateId = searchParams?.get('templateId') ?? null;
@@ -271,6 +275,25 @@ export function FinancialReportClient({ portSlug: _portSlug }: { portSlug: strin
const isLoading = query.isLoading || !kpis;
if (!query.isLoading && d && !d.hasData) {
return (
<div className="space-y-6">
<PageHeader
eyebrow="Reports"
title="Financial"
description="Revenue collected, deposits, outstanding balances, cash flow, and expense breakdown."
/>
<ReportEmptyState
icon={Wallet}
title="No financial activity yet"
body="Record a payment on a deal or log an expense to see revenue, deposits, and cash flow."
actionLabel="Go to expenses"
actionHref={`/${portSlug}/expenses` as Route}
/>
</div>
);
}
return (
<div className="space-y-6">
<PageHeader

View File

@@ -33,6 +33,7 @@ import { apiFetch } from '@/lib/api/client';
import { cn } from '@/lib/utils';
import { useUIStore } from '@/stores/ui-store';
import type { ReportPayload } from '@/lib/reports/types';
import { ReportEmptyState } from '@/components/reports/shared/report-empty-state';
import { OperationalHeatmap } from './operational-heatmap';
import { OperationalSigningBoxPlot } from './operational-signing-box-plot';
@@ -162,6 +163,8 @@ interface OperationalReportPayload {
stuckSigning: StuckSigningRow[];
highestValueVacant: HighestValueVacantRow[];
range: { from: string; to: string };
hasData: boolean;
areaOptions: string[];
};
}
@@ -312,6 +315,25 @@ export function OperationalReportClient({ portSlug }: { portSlug: string }) {
};
}
if (!query.isLoading && data && !data.hasData) {
return (
<div className="space-y-6">
<PageHeader
eyebrow="Reports"
title="Operational"
description="Berth utilisation, tenancy lifecycle, signing turnaround, operational bottlenecks."
/>
<ReportEmptyState
icon={Anchor}
title="No berths yet"
body="Add berths to see utilisation, occupancy, and signing turnaround."
actionLabel="Add berths"
actionHref={`/${portSlug}/berths` as Route}
/>
</div>
);
}
return (
<div className="space-y-6">
<PageHeader