diff --git a/components/AiInsightCard.tsx b/components/AiInsightCard.tsx index 134f6ed..270fa0c 100644 --- a/components/AiInsightCard.tsx +++ b/components/AiInsightCard.tsx @@ -264,6 +264,7 @@ export const AiInsightCard: React.FC = ({ const [detailsOpen, setDetailsOpen] = useState(false); const [reportType, setReportType] = useState('daily'); const [dayPeriod, setDayPeriod] = useState(null); + const [isCollapsed, setIsCollapsed] = useState(false); const insightInFlightRef = useRef(false); const effectiveKandangId = kandangId ?? selectedKandangId; @@ -781,13 +782,28 @@ export const AiInsightCard: React.FC = ({ }; return ( -
+
-
-
- +
+
setIsCollapsed((prev) => !prev)} + title={isCollapsed ? 'Klik untuk membuka AI Insight' : 'Klik untuk menutup AI Insight'} + > +
-

AI Insight

+
+

+ AI Insight +

+ {isCollapsed && hasInsight && ( + + Tersedia + + )} +

{selectedKandang?.kandang_name ?? `Kandang ${effectiveKandangId}`} {selectedSite?.site_name ? ` · ${selectedSite.site_name}` : ''} @@ -795,7 +811,7 @@ export const AiInsightCard: React.FC = ({

- {(page === 'dashboard' || !page) && ( + {!isCollapsed && (page === 'dashboard' || !page) && ( <> 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) => ( - - ))} - - )} + {!isCollapsed && + supportsTimeframe && + timeframe === 'harian' && + availableDays.length > 1 && ( + + )} +
-
- {selectedKandangId == null && ( -
- -

- Pilih satu kandang untuk menghasilkan AI Insight. Generate untuk semua kandang tidak - diizinkan. -

-
- )} + {!isCollapsed && ( +
+ {selectedKandangId == null && ( +
+ +

+ Pilih satu kandang untuk menghasilkan AI Insight. Generate untuk semua kandang tidak + diizinkan. +

+
+ )} - {loading && !result && ( -
-
- -

- Menganalisis data dengan model AI… proses ini bisa memakan waktu beberapa menit. -

-
- )} + {loading && !result && ( +
+
+ +

+ Menganalisis data dengan model AI… proses ini bisa memakan waktu beberapa menit. +

+
+ )} - {error && !result && ( -
- -
-

Gagal memuat insight

-

{error}

+ {error && !result && ( +
+ +
+

Gagal memuat insight

+

{error}

+ +
+
+ )} + + {!loading && !error && !result && selectedKandangId != null && ( +
+ +

Belum ada AI Insight untuk periode ini.

+

Klik tombol refresh untuk generate insight.

-
- )} + )} - {!loading && !error && !result && selectedKandangId != null && ( -
- -

Belum ada AI Insight untuk periode ini.

-

Klik tombol refresh untuk generate insight.

- -
- )} - - {result && ( -
- {heroSpec && ( - - )} -
- {periodeLabel && ( -

- Angka di bawah dihitung dari periode{' '} - {periodeLabel} -

+ {result && ( +
+ {heroSpec && ( + )} -
- {topic === 'fcr' && ( - <> -
- - Konsumsi Pakan Kumulatif - - - {renderValue(displayContext.pakan_karung_terakhir, 'Karung')} - -
-
- Rata-rata Bobot - - {renderValue(displayContext.bobot_iot_gram_terakhir, 'g')} - -
-
- Populasi Hidup - - {renderValue(displayContext.ayam_hidup_ekor, 'Ekor')} - -
-
- Total Bobot Panen - - {renderValue(displayContext.beratPanen_kg, 'kg')} - -
- +
+ {periodeLabel && ( +

+ Angka di bawah dihitung dari periode{' '} + {periodeLabel} +

)} - - {topic === 'berat_ayam' && ( - <> -
- Target Bobot - - {renderValue(displayContext.targetWeight, 'g')} - -
-
- Uniformity - - {renderValue(displayContext.uniformity, '%')} - -
-
- - Average Daily Gain (ADG) - - - {renderValue(displayContext.adg, 'g/hari')} - -
- - )} - - {topic === 'eef' && ( - <> -
- FCR Aktual - - {typeof displayContext.fcr_terakhir === 'number' - ? formatRatio(displayContext.fcr_terakhir, 3) - : renderValue(displayContext.fcr_terakhir)} - -
-
- Persentase Hidup - - {typeof displayContext.persen_hidup === 'number' - ? `${displayContext.persen_hidup.toLocaleString('id-ID', { - maximumFractionDigits: 2, - })} %` - : renderValue(displayContext.persen_hidup, '%')} - -
-
- BW Rata-rata Panen - - {typeof displayContext.bw_panen_gram === 'number' - ? `${displayContext.bw_panen_gram.toLocaleString('id-ID', { - maximumFractionDigits: 2, - })} g` - : renderValue(displayContext.bw_panen_gram, 'g')} - -
-
- - Umur Rata-rata Panen - - - {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')} - -
- - )} - - {topic === 'iot_panel' && ( - <> -
- Experience Suhu - - {renderDayRange( - displayContext.experience_suhu_min_C ?? displayContext.experience_suhu_C, - displayContext.experience_suhu_max_C ?? displayContext.experience_suhu_C, - '°C' - )} - -
-
- Kelembapan - - {renderDayRange( - displayContext.kelembapan_min_persen ?? displayContext.kelembapan_persen, - displayContext.kelembapan_max_persen ?? displayContext.kelembapan_persen, - '%' - )} - -
-
- Kecepatan Angin - - {renderDayRange( - displayContext.kecepatan_angin_min_mPerDetik ?? - displayContext.kecepatan_angin_mPerDetik, - displayContext.kecepatan_angin_max_mPerDetik ?? - displayContext.kecepatan_angin_mPerDetik, - 'm/s' - )} - -
- - )} - - {topic === 'hitung_karung' && ( - <> -
-
- - Total Karung Masuk +
+ {topic === 'fcr' && ( + <> +
+ + Konsumsi Pakan Kumulatif + + + {renderValue(displayContext.pakan_karung_terakhir, 'Karung')} - {(typeof displayContext.saldo_awal_karung === 'number' || - typeof displayContext.total_karung_masuk_iot === 'number') && ( - - {[ - 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(' + ')} - - )}
- - {renderValue(feedMasukTotal(displayContext), 'Karung')} - -
-
- - Total Karung Pakan Dituang - - - {renderValue(displayContext.total_karung_dituang_iot, 'Karung')} - -
- - )} +
+ Rata-rata Bobot + + {renderValue(displayContext.bobot_iot_gram_terakhir, 'g')} + +
+
+ Populasi Hidup + + {renderValue(displayContext.ayam_hidup_ekor, 'Ekor')} + +
+
+ Total Bobot Panen + + {renderValue(displayContext.beratPanen_kg, 'kg')} + +
+ + )} - {topic === 'hitung_ayam' && ( - <> -
- Populasi Awal - - {renderValue(displayContext.populasi_awal_ekor, 'Ekor')} - -
-
- Populasi Kini - - {renderValue(displayContext.populasi_kini_ekor, 'Ekor')} - -
-
- - Mortalitas Kumulatif - - - {renderValue(displayContext.mortalitas_kumulatif_ekor, 'Ekor')} - -
-
- Mortalitas - - {typeof displayContext.mortalitas_persen === 'number' - ? `${displayContext.mortalitas_persen.toLocaleString('id-ID', { - maximumFractionDigits: 2, - })} %` - : renderValue(displayContext.mortalitas_persen, '%')} - -
-
- Panen Kumulatif - - {renderValue(displayContext.panen_kumulatif_ekor, 'Ekor')} - -
-
- Berat Panen - - {renderValue(displayContext.berat_panen_kumulatif_kg, 'kg')} - -
- - )} -
+ {topic === 'berat_ayam' && ( + <> +
+ Target Bobot + + {renderValue(displayContext.targetWeight, 'g')} + +
+
+ Uniformity + + {renderValue(displayContext.uniformity, '%')} + +
+
+ + Average Daily Gain (ADG) + + + {renderValue(displayContext.adg, 'g/hari')} + +
+ + )} - {chartSpec && ( -
- {chartSpec.type === 'trend' ? ( - - ) : ( - + {topic === 'eef' && ( + <> +
+ FCR Aktual + + {typeof displayContext.fcr_terakhir === 'number' + ? formatRatio(displayContext.fcr_terakhir, 3) + : renderValue(displayContext.fcr_terakhir)} + +
+
+ Persentase Hidup + + {typeof displayContext.persen_hidup === 'number' + ? `${displayContext.persen_hidup.toLocaleString('id-ID', { + maximumFractionDigits: 2, + })} %` + : renderValue(displayContext.persen_hidup, '%')} + +
+
+ + BW Rata-rata Panen + + + {typeof displayContext.bw_panen_gram === 'number' + ? `${displayContext.bw_panen_gram.toLocaleString('id-ID', { + maximumFractionDigits: 2, + })} g` + : renderValue(displayContext.bw_panen_gram, 'g')} + +
+
+ + Umur Rata-rata Panen + + + {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')} + +
+ + )} + + {topic === 'iot_panel' && ( + <> +
+ Experience Suhu + + {renderDayRange( + displayContext.experience_suhu_min_C ?? + displayContext.experience_suhu_C, + displayContext.experience_suhu_max_C ?? + displayContext.experience_suhu_C, + '°C' + )} + +
+
+ Kelembapan + + {renderDayRange( + displayContext.kelembapan_min_persen ?? + displayContext.kelembapan_persen, + displayContext.kelembapan_max_persen ?? + displayContext.kelembapan_persen, + '%' + )} + +
+
+ Kecepatan Angin + + {renderDayRange( + displayContext.kecepatan_angin_min_mPerDetik ?? + displayContext.kecepatan_angin_mPerDetik, + displayContext.kecepatan_angin_max_mPerDetik ?? + displayContext.kecepatan_angin_mPerDetik, + 'm/s' + )} + +
+ + )} + + {topic === 'hitung_karung' && ( + <> +
+
+ + Total Karung Masuk + + {(typeof displayContext.saldo_awal_karung === 'number' || + typeof displayContext.total_karung_masuk_iot === 'number') && ( + + {[ + 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(' + ')} + + )} +
+ + {renderValue(feedMasukTotal(displayContext), 'Karung')} + +
+
+ + Total Karung Pakan Dituang + + + {renderValue(displayContext.total_karung_dituang_iot, 'Karung')} + +
+ + )} + + {topic === 'hitung_ayam' && ( + <> +
+ Populasi Awal + + {renderValue(displayContext.populasi_awal_ekor, 'Ekor')} + +
+
+ Populasi Kini + + {renderValue(displayContext.populasi_kini_ekor, 'Ekor')} + +
+
+ + Mortalitas Kumulatif + + + {renderValue(displayContext.mortalitas_kumulatif_ekor, 'Ekor')} + +
+
+ Mortalitas + + {typeof displayContext.mortalitas_persen === 'number' + ? `${displayContext.mortalitas_persen.toLocaleString('id-ID', { + maximumFractionDigits: 2, + })} %` + : renderValue(displayContext.mortalitas_persen, '%')} + +
+
+ Panen Kumulatif + + {renderValue(displayContext.panen_kumulatif_ekor, 'Ekor')} + +
+
+ Berat Panen + + {renderValue(displayContext.berat_panen_kumulatif_kg, 'kg')} + +
+ )}
- )} -
-

- Kesimpulan -

-

- {result.summary} -

-
+ {chartSpec && ( +
+ {chartSpec.type === 'trend' ? ( + + ) : ( + + )} +
+ )} - {result.akar_masalah && ( -
+

- Akar Masalah + Kesimpulan

-

- {result.akar_masalah} +

+ {result.summary}

- )} -
-

- AI Insight & Rekomendasi -

-

- {result.insight} -

+ {result.akar_masalah && ( +
+

+ Akar + Masalah +

+

+ {result.akar_masalah} +

+
+ )} + +
+

+ AI Insight & Rekomendasi +

+

+ {result.insight} +

+
+ +
- -
-
- )} -
+ )} +
+ )}
); };