add collapse button for ai insight

This commit is contained in:
Alberto-Audrix committed 2026-10-08 10:24:08 +07:00
1 parent 6bad900cf0
commit 67df3eeeb6
2 files changed
+676 -593

No files matched your search

+378 -341
View File
@@ -169,6 +169,7 @@ export const PageAiInsight: React.FC<PageAiInsightProps> = ({
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [stage, setStage] = useState<InsightJobStage | null>(null);
const [isCollapsed, setIsCollapsed] = useState(false);
const supportsTimeframe = Boolean(scalarScope);
const timeframe: 'harian' | 'mingguan' = 'harian';
@@ -673,14 +674,27 @@ export const PageAiInsight: React.FC<PageAiInsightProps> = ({
}, [scopeKey]);
return (
<div className="rounded-xl border border-gray-100 bg-white shadow-sm overflow-hidden mb-6">
<div className="rounded-xl border border-gray-100 bg-white shadow-sm overflow-hidden mb-6 transition-all">
<div className="flex items-center justify-between px-5 py-3.5 border-b border-gray-100 bg-gray-50/80">
<div className="flex items-center gap-2.5 min-w-0">
<div className="p-1.5 bg-red-50 rounded-lg">
<div
className="flex items-center gap-2.5 min-w-0 cursor-pointer select-none group"
onClick={() => setIsCollapsed((prev) => !prev)}
title={isCollapsed ? 'Klik untuk membuka AI Insight' : 'Klik untuk menutup AI Insight'}
>
<div className="p-1.5 bg-red-50 rounded-lg group-hover:bg-red-100 transition-colors">
<i className={`fa-solid ${meta.icon} text-red-600 text-sm`} />
</div>
<div className="min-w-0">
<span className="text-sm font-bold text-gray-900">{meta.title}</span>
<div className="flex items-center gap-2">
<span className="text-sm font-bold text-gray-900 group-hover:text-red-700 transition-colors">
{meta.title}
</span>
{isCollapsed && result && (
<span className="text-[10px] font-semibold text-emerald-700 bg-emerald-50 border border-emerald-200 px-2 py-0.5 rounded-full">
Tersedia
</span>
)}
</div>
<p className="text-xs text-gray-400 truncate">
{selectedKandang?.kandang_name ?? 'Kandang'}
{selectedSite?.site_name ? ` · ${selectedSite.site_name}` : ''}
@@ -693,20 +707,23 @@ export const PageAiInsight: React.FC<PageAiInsightProps> = ({
</div>
</div>
<div className="flex items-center gap-2 shrink-0">
{supportsTimeframe && timeframe === 'harian' && availableDays.length > 1 && (
<select
value={effectiveDay ?? availableDays[availableDays.length - 1]}
onChange={(e) => setSelectedDay(parseInt(e.target.value, 10))}
className="text-[11px] font-semibold border border-gray-200 bg-white text-gray-700 rounded-lg px-2 py-1 shadow-sm"
title="Pilih hari siklus"
>
{availableDays.map((d) => (
<option key={d} value={d}>
Hari {d}
</option>
))}
</select>
)}
{!isCollapsed &&
supportsTimeframe &&
timeframe === 'harian' &&
availableDays.length > 1 && (
<select
value={effectiveDay ?? availableDays[availableDays.length - 1]}
onChange={(e) => setSelectedDay(parseInt(e.target.value, 10))}
className="text-[11px] font-semibold border border-gray-200 bg-white text-gray-700 rounded-lg px-2 py-1 shadow-sm"
title="Pilih hari siklus"
>
{availableDays.map((d) => (
<option key={d} value={d}>
Hari {d}
</option>
))}
</select>
)}
<button
onClick={() => generate(true)}
disabled={loading || selectedKandangId == null}
@@ -717,357 +734,377 @@ export const PageAiInsight: React.FC<PageAiInsightProps> = ({
className={`fa-solid fa-rotate-right text-gray-600 text-sm ${loading ? 'fa-spin' : ''}`}
/>
</button>
<button
type="button"
onClick={() => setIsCollapsed((prev) => !prev)}
title={isCollapsed ? 'Buka AI Insight' : 'Tutup AI Insight'}
aria-label={isCollapsed ? 'Buka AI Insight' : 'Tutup AI Insight'}
className="p-1.5 rounded-lg border border-gray-200 bg-white hover:bg-gray-50 transition-all text-gray-600 hover:text-gray-900"
>
<i
className={`fa-solid ${isCollapsed ? 'fa-chevron-down' : 'fa-chevron-up'} text-sm`}
/>
</button>
</div>
</div>
<div className="p-4">
{selectedKandangId == null && (
<div className="flex items-start gap-2 bg-amber-50 border border-amber-100 rounded-xl p-3">
<i className="fa-solid fa-triangle-exclamation text-amber-500 mt-0.5" />
<p className="text-xs text-amber-800">
Pilih satu kandang untuk menghasilkan AI Insight. Generate untuk semua kandang tidak
diizinkan.
</p>
</div>
)}
{!isCollapsed && (
<div className="p-4">
{selectedKandangId == null && (
<div className="flex items-start gap-2 bg-amber-50 border border-amber-100 rounded-xl p-3">
<i className="fa-solid fa-triangle-exclamation text-amber-500 mt-0.5" />
<p className="text-xs text-amber-800">
Pilih satu kandang untuk menghasilkan AI Insight. Generate untuk semua kandang tidak
diizinkan.
</p>
</div>
)}
{loading && !result && (
<div className="flex flex-col items-center gap-3 py-8 justify-center">
<div className="w-8 h-8 rounded-full border-4 border-gray-100 border-t-red-600 animate-spin" />
<InsightProgressSteps stage={stage} />
<p className="text-sm text-gray-500 text-center max-w-sm">
Menganalisis data dengan model AI… proses ini bisa memakan waktu beberapa menit.
</p>
</div>
)}
{loading && !result && (
<div className="flex flex-col items-center gap-3 py-8 justify-center">
<div className="w-8 h-8 rounded-full border-4 border-gray-100 border-t-red-600 animate-spin" />
<InsightProgressSteps stage={stage} />
<p className="text-sm text-gray-500 text-center max-w-sm">
Menganalisis data dengan model AI… proses ini bisa memakan waktu beberapa menit.
</p>
</div>
)}
{error && !result && (
<div className="flex items-start gap-2 bg-red-50 border border-red-100 rounded-xl p-3">
<i className="fa-solid fa-triangle-exclamation text-red-400 shrink-0 mt-0.5" />
<div>
<p className="text-xs font-semibold text-red-600">Gagal memuat insight</p>
<p className="text-xs text-red-500 mt-0.5">{error}</p>
{error && !result && (
<div className="flex items-start gap-2 bg-red-50 border border-red-100 rounded-xl p-3">
<i className="fa-solid fa-triangle-exclamation text-red-400 shrink-0 mt-0.5" />
<div>
<p className="text-xs font-semibold text-red-600">Gagal memuat insight</p>
<p className="text-xs text-red-500 mt-0.5">{error}</p>
<button
onClick={() => generate(true)}
className="text-xs text-red-600 underline mt-1 hover:text-red-800"
>
Coba lagi
</button>
</div>
</div>
)}
{!loading && !error && !result && selectedKandangId != null && (
<div className="flex flex-col items-center justify-center gap-2 py-8 text-center">
<i className="fa-solid fa-brain text-gray-300 text-2xl" />
<p className="text-sm text-gray-500">Belum ada AI Insight untuk periode ini.</p>
<p className="text-xs text-gray-400">Klik tombol refresh untuk generate insight.</p>
<button
type="button"
onClick={() => generate(true)}
className="text-xs text-red-600 underline mt-1 hover:text-red-800"
className="mt-2 inline-flex items-center gap-2 rounded-lg bg-red-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-red-700"
>
Coba lagi
<i className="fa-solid fa-wand-magic-sparkles" />
Generate Insight
</button>
</div>
</div>
)}
)}
{!loading && !error && !result && selectedKandangId != null && (
<div className="flex flex-col items-center justify-center gap-2 py-8 text-center">
<i className="fa-solid fa-brain text-gray-300 text-2xl" />
<p className="text-sm text-gray-500">Belum ada AI Insight untuk periode ini.</p>
<p className="text-xs text-gray-400">Klik tombol refresh untuk generate insight.</p>
<button
type="button"
onClick={() => generate(true)}
className="mt-2 inline-flex items-center gap-2 rounded-lg bg-red-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-red-700"
>
<i className="fa-solid fa-wand-magic-sparkles" />
Generate Insight
</button>
</div>
)}
{result && (
<div className="space-y-4">
{heroSpec && (
<HeroFigure
label={heroSpec.label}
value={heroSpec.value}
unit={heroSpec.unit}
context={heroSpec.context}
status={heroSpec.status}
deltaLabel={heroSpec.deltaLabel}
/>
)}
<div className="bg-white rounded-xl border border-gray-100 shadow-sm p-5">
{periodeLabel && (
<p className="mb-3 text-xs font-medium text-gray-500">
Angka di bawah dihitung dari periode{' '}
<span className="text-gray-700">{periodeLabel}</span>
</p>
{result && (
<div className="space-y-4">
{heroSpec && (
<HeroFigure
label={heroSpec.label}
value={heroSpec.value}
unit={heroSpec.unit}
context={heroSpec.context}
status={heroSpec.status}
deltaLabel={heroSpec.deltaLabel}
/>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-y-3 gap-x-6">
{topic === 'fcr' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Konsumsi Pakan Kumulatif
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.pakan_karung_terakhir, 'Karung')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Rata-rata Bobot</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.bobot_iot_gram_terakhir, 'g')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Populasi Hidup</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.ayam_hidup_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Total Bobot Panen</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.beratPanen_kg, 'kg')}
</span>
</div>
</>
<div className="bg-white rounded-xl border border-gray-100 shadow-sm p-5">
{periodeLabel && (
<p className="mb-3 text-xs font-medium text-gray-500">
Angka di bawah dihitung dari periode{' '}
<span className="text-gray-700">{periodeLabel}</span>
</p>
)}
{topic === 'berat_ayam' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Target Bobot</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.targetWeight, 'g')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Uniformity</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.uniformity, '%')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Average Daily Gain (ADG)
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.adg, 'g/hari')}
</span>
</div>
</>
)}
{topic === 'eef' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">FCR Aktual</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.fcr_terakhir === 'number'
? formatRatio(displayContext.fcr_terakhir, 3)
: renderValue(displayContext.fcr_terakhir)}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Persentase Hidup</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.persen_hidup === 'number'
? `${displayContext.persen_hidup.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} %`
: renderValue(displayContext.persen_hidup, '%')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">BW Rata-rata Panen</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.bw_panen_gram === 'number'
? `${displayContext.bw_panen_gram.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} g`
: renderValue(displayContext.bw_panen_gram, 'g')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Umur Rata-rata Panen
</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.umur_panen_rata_hari === 'number'
? `${displayContext.umur_panen_rata_hari.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} hari`
: renderValue(displayContext.umur_panen_rata_hari, 'hari')}
</span>
</div>
</>
)}
{topic === 'iot_panel' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Experience Suhu</span>
<span className="text-sm font-bold text-gray-900">
{renderDayRange(
displayContext.experience_suhu_min_C ?? displayContext.experience_suhu_C,
displayContext.experience_suhu_max_C ?? displayContext.experience_suhu_C,
'°C'
)}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Kelembapan</span>
<span className="text-sm font-bold text-gray-900">
{renderDayRange(
displayContext.kelembapan_min_persen ?? displayContext.kelembapan_persen,
displayContext.kelembapan_max_persen ?? displayContext.kelembapan_persen,
'%'
)}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Kecepatan Angin</span>
<span className="text-sm font-bold text-gray-900">
{renderDayRange(
displayContext.kecepatan_angin_min_mPerDetik ??
displayContext.kecepatan_angin_mPerDetik,
displayContext.kecepatan_angin_max_mPerDetik ??
displayContext.kecepatan_angin_mPerDetik,
'm/s'
)}
</span>
</div>
</>
)}
{topic === 'hitung_karung' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<div>
<span className="text-sm text-gray-600 font-medium block">
Total Karung Masuk
<div className="grid grid-cols-1 md:grid-cols-2 gap-y-3 gap-x-6">
{topic === 'fcr' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Konsumsi Pakan Kumulatif
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.pakan_karung_terakhir, 'Karung')}
</span>
{(typeof displayContext.saldo_awal_karung === 'number' ||
typeof displayContext.total_karung_masuk_iot === 'number') && (
<span className="text-xs text-gray-400">
{[
typeof displayContext.saldo_awal_karung === 'number'
? `Saldo Awal: ${displayContext.saldo_awal_karung.toLocaleString('id-ID')}`
: null,
typeof displayContext.total_karung_masuk_iot === 'number'
? `Masuk IoT: ${displayContext.total_karung_masuk_iot.toLocaleString('id-ID')}`
: null,
]
.filter(Boolean)
.join(' + ')}
</span>
)}
</div>
<span className="text-sm font-bold text-gray-900">
{renderValue(feedMasukTotal(displayContext), 'Karung')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Total Karung Pakan Dituang
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.total_karung_dituang_iot, 'Karung')}
</span>
</div>
</>
)}
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Rata-rata Bobot</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.bobot_iot_gram_terakhir, 'g')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Populasi Hidup</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.ayam_hidup_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Total Bobot Panen</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.beratPanen_kg, 'kg')}
</span>
</div>
</>
)}
{topic === 'hitung_ayam' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Populasi Awal</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.populasi_awal_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Populasi Kini</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.populasi_kini_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Mortalitas Kumulatif
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.mortalitas_kumulatif_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Mortalitas</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.mortalitas_persen === 'number'
? `${displayContext.mortalitas_persen.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} %`
: renderValue(displayContext.mortalitas_persen, '%')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Panen Kumulatif</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.panen_kumulatif_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Berat Panen</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.berat_panen_kumulatif_kg, 'kg')}
</span>
</div>
</>
)}
</div>
{topic === 'berat_ayam' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Target Bobot</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.targetWeight, 'g')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Uniformity</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.uniformity, '%')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Average Daily Gain (ADG)
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.adg, 'g/hari')}
</span>
</div>
</>
)}
{chartSpec && (
<div className="mt-6 pt-4 border-t border-gray-100">
{chartSpec.type === 'trend' ? (
<InsightTrendChart
rows={trendChartRows}
series={chartSpec.series}
unit={chartSpec.unit}
title={chartSpec.title}
highlight={chartHighlight}
gap={chartSpec.gap ?? null}
/>
) : (
<InsightConditionChart bars={chartSpec.bars} title={chartSpec.title} />
{topic === 'eef' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">FCR Aktual</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.fcr_terakhir === 'number'
? formatRatio(displayContext.fcr_terakhir, 3)
: renderValue(displayContext.fcr_terakhir)}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Persentase Hidup</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.persen_hidup === 'number'
? `${displayContext.persen_hidup.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} %`
: renderValue(displayContext.persen_hidup, '%')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
BW Rata-rata Panen
</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.bw_panen_gram === 'number'
? `${displayContext.bw_panen_gram.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} g`
: renderValue(displayContext.bw_panen_gram, 'g')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Umur Rata-rata Panen
</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.umur_panen_rata_hari === 'number'
? `${displayContext.umur_panen_rata_hari.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} hari`
: renderValue(displayContext.umur_panen_rata_hari, 'hari')}
</span>
</div>
</>
)}
{topic === 'iot_panel' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Experience Suhu</span>
<span className="text-sm font-bold text-gray-900">
{renderDayRange(
displayContext.experience_suhu_min_C ??
displayContext.experience_suhu_C,
displayContext.experience_suhu_max_C ??
displayContext.experience_suhu_C,
'°C'
)}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Kelembapan</span>
<span className="text-sm font-bold text-gray-900">
{renderDayRange(
displayContext.kelembapan_min_persen ??
displayContext.kelembapan_persen,
displayContext.kelembapan_max_persen ??
displayContext.kelembapan_persen,
'%'
)}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Kecepatan Angin</span>
<span className="text-sm font-bold text-gray-900">
{renderDayRange(
displayContext.kecepatan_angin_min_mPerDetik ??
displayContext.kecepatan_angin_mPerDetik,
displayContext.kecepatan_angin_max_mPerDetik ??
displayContext.kecepatan_angin_mPerDetik,
'm/s'
)}
</span>
</div>
</>
)}
{topic === 'hitung_karung' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<div>
<span className="text-sm text-gray-600 font-medium block">
Total Karung Masuk
</span>
{(typeof displayContext.saldo_awal_karung === 'number' ||
typeof displayContext.total_karung_masuk_iot === 'number') && (
<span className="text-xs text-gray-400">
{[
typeof displayContext.saldo_awal_karung === 'number'
? `Saldo Awal: ${displayContext.saldo_awal_karung.toLocaleString('id-ID')}`
: null,
typeof displayContext.total_karung_masuk_iot === 'number'
? `Masuk IoT: ${displayContext.total_karung_masuk_iot.toLocaleString('id-ID')}`
: null,
]
.filter(Boolean)
.join(' + ')}
</span>
)}
</div>
<span className="text-sm font-bold text-gray-900">
{renderValue(feedMasukTotal(displayContext), 'Karung')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Total Karung Pakan Dituang
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.total_karung_dituang_iot, 'Karung')}
</span>
</div>
</>
)}
{topic === 'hitung_ayam' && (
<>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Populasi Awal</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.populasi_awal_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Populasi Kini</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.populasi_kini_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">
Mortalitas Kumulatif
</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.mortalitas_kumulatif_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Mortalitas</span>
<span className="text-sm font-bold text-gray-900">
{typeof displayContext.mortalitas_persen === 'number'
? `${displayContext.mortalitas_persen.toLocaleString('id-ID', {
maximumFractionDigits: 2,
})} %`
: renderValue(displayContext.mortalitas_persen, '%')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Panen Kumulatif</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.panen_kumulatif_ekor, 'Ekor')}
</span>
</div>
<div className="flex justify-between items-center py-1 border-b border-gray-50">
<span className="text-sm text-gray-600 font-medium">Berat Panen</span>
<span className="text-sm font-bold text-gray-900">
{renderValue(displayContext.berat_panen_kumulatif_kg, 'kg')}
</span>
</div>
</>
)}
</div>
)}
<div className="mt-6 pt-4 border-t border-gray-100">
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
<i className="fa-solid fa-circle-check text-green-600" /> Kesimpulan
</h4>
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg border border-gray-100">
{result.summary}
</p>
</div>
{chartSpec && (
<div className="mt-6 pt-4 border-t border-gray-100">
{chartSpec.type === 'trend' ? (
<InsightTrendChart
rows={trendChartRows}
series={chartSpec.series}
unit={chartSpec.unit}
title={chartSpec.title}
highlight={chartHighlight}
gap={chartSpec.gap ?? null}
/>
) : (
<InsightConditionChart bars={chartSpec.bars} title={chartSpec.title} />
)}
</div>
)}
{result.akar_masalah && (
<div className="mt-4">
<div className="mt-6 pt-4 border-t border-gray-100">
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
<i className="fa-solid fa-magnifying-glass-chart text-amber-600" /> Akar Masalah
<i className="fa-solid fa-circle-check text-green-600" /> Kesimpulan
</h4>
<p className="text-sm text-gray-700 bg-amber-50/50 p-3 rounded-lg border border-amber-100/80">
{result.akar_masalah}
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg border border-gray-100">
{result.summary}
</p>
</div>
)}
<div className="mt-4">
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
<i className="fa-solid fa-brain text-red-600" /> AI Insight & Rekomendasi
</h4>
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg border border-gray-100 whitespace-pre-wrap">
{result.insight}
</p>
{result.akar_masalah && (
<div className="mt-4">
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
<i className="fa-solid fa-magnifying-glass-chart text-amber-600" /> Akar
Masalah
</h4>
<p className="text-sm text-gray-700 bg-amber-50/50 p-3 rounded-lg border border-amber-100/80">
{result.akar_masalah}
</p>
</div>
)}
<div className="mt-4">
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
<i className="fa-solid fa-brain text-red-600" /> AI Insight & Rekomendasi
</h4>
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg border border-gray-100 whitespace-pre-wrap">
{result.insight}
</p>
</div>
<CitationsBlock citations={result.citations} />
</div>
<CitationsBlock citations={result.citations} />
</div>
</div>
)}
</div>
)}
</div>
)}
</div>
);
};