update kpi report to include summary for daily report and end cycle

This commit is contained in:
Alberto-Audrix committed 2026-09-07 10:08:07 +07:00
1 parent 6c21af745d
commit 4439e3b7b1
5 files changed
+454 -43

No files matched your search

+62 -23
View File
@@ -17,11 +17,8 @@ import {
formatNumber,
formatRatio,
} from '../../utils/format.ts';
import type {
DashboardReportFcrRow,
DashboardReportPayload,
DashboardReportType,
} from '../../types/dashboardReport.ts';
import { buildReportSummary, formatSummaryValue } from '../../utils/reportSummary.ts';
import type { DashboardReportPayload, DashboardReportType } from '../../types/dashboardReport.ts';
import { Banner } from '../ui/Feedback.tsx';
type Props = {
@@ -55,17 +52,6 @@ const harvestKgCell = (grams: number | null | undefined): string => {
return `${formatNumber(Number(grams) / 1000, 1)} kg`;
};
const latestValidRatio = (
rows: Array<DashboardReportFcrRow | { iotEef: number | null }>,
key: 'iotFcr' | 'iotEef'
): number | null => {
for (let i = rows.length - 1; i >= 0; i -= 1) {
const value = (rows[i] as Record<string, number | null>)[key];
if (value != null && Number.isFinite(value)) return value;
}
return null;
};
const ReportPreviewOverlay: React.FC<Props> = ({ open, reportType, onClose }) => {
const { selectedCycle, selectedKandang, selectedSite } = useFarm();
const isEndCycle = reportType === 'end_cycle';
@@ -138,6 +124,11 @@ const ReportPreviewOverlay: React.FC<Props> = ({ open, reportType, onClose }) =>
await exportElementToPdf(printRef.current, filename);
}, [payload, isEndCycle]);
const summary = useMemo(
() => (payload ? buildReportSummary(payload) : null),
[payload]
);
if (!open) return null;
const title = isEndCycle ? 'Laporan Akhir Siklus' : 'Laporan Harian';
@@ -151,7 +142,7 @@ const ReportPreviewOverlay: React.FC<Props> = ({ open, reportType, onClose }) =>
const endDateLabel = selectedCycle ? formatDateId(selectedCycle.start_date) : '';
const dataDates =
selectedCycle && payload
? `${endDateLabel} – ${formatDateId(payload.counting[payload.counting.length - 1]?.date ?? selectedCycle.start_date)}`
? `(${endDateLabel} - ${formatDateId(payload.counting[payload.counting.length - 1]?.date ?? selectedCycle.start_date)})`
: '';
const cycleRange = selectedCycle
? formatDateRange(selectedCycle.start_date, selectedCycle.end_date)
@@ -263,14 +254,62 @@ const ReportPreviewOverlay: React.FC<Props> = ({ open, reportType, onClose }) =>
<div style={{ fontSize: 12, color: '#6b7280', marginTop: 2 }}>
Dicetak: {printedDate}
</div>
{isEndCycle && (
<div style={{ fontSize: 22, color: '#111827', marginTop: 12, fontWeight: 700 }}>
FCR akhir: {ratioCell(latestValidRatio(payload.fcr, 'iotFcr'))} · EEF akhir:{' '}
{ratioCell(latestValidRatio(payload.eef, 'iotEef'))}
</div>
)}
</div>
{summary && (
<div
data-pdf-block
style={{
display: 'grid',
gridTemplateColumns: 'repeat(4, minmax(0, 1fr))',
gap: 8,
marginBottom: 24,
}}
>
{summary.metrics.map((metric) => {
const formattedValue = formatSummaryValue(metric);
return (
<div
key={metric.key}
style={{
border: '1px solid #e9d5ff',
borderTop: '3px solid #7e22ce',
borderRadius: 8,
padding: '8px 10px',
background: '#faf5ff',
}}
>
<div style={{ fontSize: 10, color: '#6b7280', fontWeight: 600 }}>
{metric.label}
</div>
<div
style={{
fontSize: 16,
color: '#111827',
fontWeight: 700,
marginTop: 2,
}}
>
{formattedValue}
{metric.unit && formattedValue !== '—' ? (
<span
style={{
fontSize: 10,
fontWeight: 500,
color: '#6b7280',
marginLeft: 4,
}}
>
{metric.unit}
</span>
) : null}
</div>
</div>
);
})}
</div>
)}
<ReportTable title="Hitung Ayam harian">
<thead>
<tr>