feat(reports): financial report-level empty state
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,9 @@ import { rangeToBounds, type DateRange } from '@/lib/analytics/range';
|
|||||||
import { apiFetch } from '@/lib/api/client';
|
import { apiFetch } from '@/lib/api/client';
|
||||||
import { formatMoney, formatMoneyCompact, formatNumber } from '@/lib/reports/format-currency';
|
import { formatMoney, formatMoneyCompact, formatNumber } from '@/lib/reports/format-currency';
|
||||||
import type { ReportPayload } from '@/lib/reports/types';
|
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) ───────────
|
// ─── Payload types (mirror the /api/v1/reports/financial response) ───────────
|
||||||
|
|
||||||
@@ -119,6 +122,7 @@ interface FinancialPayload {
|
|||||||
refundLog: RefundRow[];
|
refundLog: RefundRow[];
|
||||||
expenseLedger: ExpenseLedgerRow[];
|
expenseLedger: ExpenseLedgerRow[];
|
||||||
range: { from: string; to: string };
|
range: { from: string; to: string };
|
||||||
|
hasData: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +142,7 @@ const DONUT_COLORS = [
|
|||||||
'hsl(var(--chart-6))',
|
'hsl(var(--chart-6))',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function FinancialReportClient({ portSlug: _portSlug }: { portSlug: string }) {
|
export function FinancialReportClient({ portSlug }: { portSlug: string }) {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const initialTemplateId = searchParams?.get('templateId') ?? null;
|
const initialTemplateId = searchParams?.get('templateId') ?? null;
|
||||||
|
|
||||||
@@ -271,6 +275,25 @@ export function FinancialReportClient({ portSlug: _portSlug }: { portSlug: strin
|
|||||||
|
|
||||||
const isLoading = query.isLoading || !kpis;
|
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 (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { apiFetch } from '@/lib/api/client';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useUIStore } from '@/stores/ui-store';
|
import { useUIStore } from '@/stores/ui-store';
|
||||||
import type { ReportPayload } from '@/lib/reports/types';
|
import type { ReportPayload } from '@/lib/reports/types';
|
||||||
|
import { ReportEmptyState } from '@/components/reports/shared/report-empty-state';
|
||||||
|
|
||||||
import { OperationalHeatmap } from './operational-heatmap';
|
import { OperationalHeatmap } from './operational-heatmap';
|
||||||
import { OperationalSigningBoxPlot } from './operational-signing-box-plot';
|
import { OperationalSigningBoxPlot } from './operational-signing-box-plot';
|
||||||
@@ -162,6 +163,8 @@ interface OperationalReportPayload {
|
|||||||
stuckSigning: StuckSigningRow[];
|
stuckSigning: StuckSigningRow[];
|
||||||
highestValueVacant: HighestValueVacantRow[];
|
highestValueVacant: HighestValueVacantRow[];
|
||||||
range: { from: string; to: string };
|
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 (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
|||||||
Reference in New Issue
Block a user