ai insight rework #2
This commit is contained in:
1 parent
9d76f54d33
commit
e1987d6999
39 files changed
+2311
-830
No files matched your search
@@ -24,7 +24,12 @@ import {
|
||||
} from '../utils/insightContextBuilders.ts';
|
||||
import { getCobbStandardForDay } from '../utils/insightHelpers.ts';
|
||||
import { formatRatio } from '../utils/format.ts';
|
||||
import { HeroFigure, MutedStatRow, StatTile, type InsightStatus } from './shared/InsightStatTiles.tsx';
|
||||
import {
|
||||
HeroFigure,
|
||||
MutedStatRow,
|
||||
StatTile,
|
||||
type InsightStatus,
|
||||
} from './shared/InsightStatTiles.tsx';
|
||||
import InsightTrendChart from './shared/InsightTrendChart.tsx';
|
||||
|
||||
const CLIENT_TIMEOUT_MS = 1_200_000; // 20 minutes
|
||||
@@ -265,8 +270,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
? contextProp.hari_ke
|
||||
: null;
|
||||
// Prefer context/data day over cycle calendar day (can be ahead of KPI/weight data).
|
||||
const currentDayVal =
|
||||
contextDay ?? dashboardMetrics?.hari_ke ?? selectedCycle?.current_day ?? 0;
|
||||
const currentDayVal = contextDay ?? dashboardMetrics?.hari_ke ?? selectedCycle?.current_day ?? 0;
|
||||
const reportPeriod =
|
||||
reportType === 'daily'
|
||||
? (dayPeriod ?? (currentDayVal > 0 ? `Hari ${currentDayVal}` : null))
|
||||
@@ -354,6 +358,13 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
contextPayload.tonase_panen_kg = scopedMetrics.tonase_panen_kg;
|
||||
contextPayload.uniformity_persen = scopedMetrics.uniformity_persen;
|
||||
contextPayload.pakan_kumulatif_karung = scopedMetrics.pakan_kumulatif_karung;
|
||||
|
||||
// EEF series for trend computation (eef, dashboard, end_cycle topics)
|
||||
if (topic === 'eef' || topic === 'dashboard' || reportType === 'end_cycle') {
|
||||
contextPayload.eef_seri = scopedMetrics.kpiSeries
|
||||
.filter((row) => row.eef != null && row.hari > 0)
|
||||
.map((row) => ({ hari: row.hari, eef: row.eef }));
|
||||
}
|
||||
}
|
||||
|
||||
if (reportType === 'end_cycle' && scopedMetrics) {
|
||||
@@ -529,7 +540,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
const detailed = structuredInsight?.detailed;
|
||||
|
||||
const gradeDay = scopedMetrics?.hari_ke ?? (currentDayVal > 0 ? currentDayVal : null);
|
||||
const fcrStandard = gradeDay ? getCobbStandardForDay(gradeDay)?.fcr ?? null : null;
|
||||
const fcrStandard = gradeDay ? (getCobbStandardForDay(gradeDay)?.fcr ?? null) : null;
|
||||
|
||||
const fcrStatus = ((): InsightStatus => {
|
||||
const actual = scopedMetrics?.fcr ?? null;
|
||||
@@ -558,8 +569,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
|
||||
const weightTarget =
|
||||
scopedMetrics?.weightSeries.find((row) => row.hari === gradeDay)?.target ??
|
||||
[...(scopedMetrics?.weightSeries ?? [])].reverse().find((row) => row.target != null)
|
||||
?.target ??
|
||||
[...(scopedMetrics?.weightSeries ?? [])].reverse().find((row) => row.target != null)?.target ??
|
||||
null;
|
||||
|
||||
const weightStatus = ((): InsightStatus => {
|
||||
@@ -658,9 +668,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
value={bobotVal == null ? 'N/A' : Math.round(bobotVal).toLocaleString('id-ID')}
|
||||
unit={bobotVal == null ? undefined : 'g'}
|
||||
status={weightStatus}
|
||||
deltaLabel={
|
||||
weightGapPct == null ? null : `${weightGapPct.toFixed(0)}% vs target`
|
||||
}
|
||||
deltaLabel={weightGapPct == null ? null : `${weightGapPct.toFixed(0)}% vs target`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -834,7 +842,10 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
<h4 className="text-sm font-bold text-gray-800 mb-3 flex items-center gap-2">
|
||||
<i className="fa-solid fa-triangle-exclamation text-amber-600" /> Masalah
|
||||
</h4>
|
||||
{renderBulletList(endCycleInsight.masalah, 'Tidak ada masalah kritis dari graded facts')}
|
||||
{renderBulletList(
|
||||
endCycleInsight.masalah,
|
||||
'Tidak ada masalah kritis dari graded facts'
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
|
||||
@@ -842,7 +853,9 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
<i className="fa-solid fa-magnifying-glass-chart text-red-600" /> Akar penyebab
|
||||
</h4>
|
||||
{endCycleInsight.akar_penyebab.length === 0 ? (
|
||||
<p className="text-sm text-gray-500">Belum ada kandidat akar penyebab berbukti.</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
Belum ada kandidat akar penyebab berbukti.
|
||||
</p>
|
||||
) : (
|
||||
<ul className="space-y-3">
|
||||
{endCycleInsight.akar_penyebab.map((item, index) => (
|
||||
@@ -901,9 +914,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-gray-800">{item.aksi}</p>
|
||||
{item.metrik_pantau ? (
|
||||
<p className="mt-1 text-xs text-gray-500">
|
||||
Pantau: {item.metrik_pantau}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-gray-500">Pantau: {item.metrik_pantau}</p>
|
||||
) : null}
|
||||
</li>
|
||||
))}
|
||||
@@ -929,11 +940,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
<i className="fa-solid fa-brain text-red-600" /> AI Insight
|
||||
</h4>
|
||||
<div className="text-sm text-gray-700 space-y-2 leading-relaxed">
|
||||
{renderBulletList([
|
||||
...summary.bullets,
|
||||
...summary.risks,
|
||||
...summary.actions,
|
||||
])}
|
||||
{renderBulletList([...summary.bullets, ...summary.risks, ...summary.actions])}
|
||||
</div>
|
||||
</div>
|
||||
<CitationsBlock citations={citations} />
|
||||
|
||||
Reference in new issue
Block a user