import React, { useEffect, useState } from 'react'; import { api, errorMessage } from '../../services/apiClient.ts'; import type { ManualInput, ManualInputWrite } from '../../types/api.ts'; import { Banner } from '../ui/Feedback.tsx'; import { formatNumber, todayIso } from '../../utils/format.ts'; const inputClass = 'border border-gray-300 rounded-md px-3 py-2 text-sm w-full focus:ring-red-500 focus:border-red-500'; type Props = { cycleId: number; currentDay: number; readOnly?: boolean; }; type FormState = { date: string; age_manual: number; mortality_manual: number; feed_in_manual: number; feed_use_manual: number; feed_out_manual: number; harvest_manual: number; harvest_weight_manual: number; manual_weight: number; }; const emptyForm = (currentDay: number): FormState => ({ date: todayIso(), age_manual: currentDay, mortality_manual: 0, feed_in_manual: 0, feed_use_manual: 0, feed_out_manual: 0, harvest_manual: 0, harvest_weight_manual: 0, manual_weight: 0, }); function toForm(row: ManualInput): FormState { return { date: row.date, age_manual: row.age_manual, mortality_manual: row.mortality_manual, feed_in_manual: row.feed_in_manual, feed_use_manual: row.feed_use_manual, feed_out_manual: row.feed_out_manual, harvest_manual: row.harvest_manual, // Database dalam gram, form selalu dalam kg. harvest_weight_manual: row.harvest_weight_manual / 1000, manual_weight: row.manual_weight, }; } const DashboardManualInputForm: React.FC = ({ cycleId, currentDay, readOnly = false }) => { const [form, setForm] = useState(() => emptyForm(currentDay)); const [saved, setSaved] = useState(null); const [rows, setRows] = useState([]); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [notice, setNotice] = useState(null); const load = async (date: string) => { const list = await api.manualInputs.list({ cycle_id: cycleId }); setRows(list); const match = list.find((row) => row.date === date) ?? null; setSaved(match); if (match) setForm(toForm(match)); else { setForm((prev) => ({ ...emptyForm(currentDay), date, age_manual: prev.date === date ? prev.age_manual : currentDay, })); } }; useEffect(() => { let cancelled = false; (async () => { try { const list = await api.manualInputs.list({ cycle_id: cycleId }); if (cancelled) return; setRows(list); const today = todayIso(); const match = list.find((row) => row.date === today) ?? (readOnly ? (list[0] ?? null) : null); setSaved(match); setForm(match ? toForm(match) : emptyForm(currentDay)); } catch (err) { if (!cancelled) setError(errorMessage(err)); } })(); return () => { cancelled = true; }; }, [cycleId, currentDay, readOnly]); const onDateChange = async (date: string) => { setError(null); setNotice(null); try { await load(date); } catch (err) { setError(errorMessage(err)); } }; return (
{ e.preventDefault(); if (readOnly) return; void (async () => { setSaving(true); setError(null); setNotice(null); try { const body: ManualInputWrite = { cycle: cycleId, date: form.date, age_manual: form.age_manual, mortality_manual: form.mortality_manual, feed_in_manual: form.feed_in_manual, feed_use_manual: form.feed_use_manual, feed_out_manual: form.feed_out_manual, harvest_manual: form.harvest_manual, // Form dalam kg, disimpan ke database dalam gram. harvest_weight_manual: form.harvest_weight_manual * 1000, manual_weight: form.manual_weight, }; const row = await api.manualInputs.create(body); setSaved(row); setForm(toForm(row)); setRows((prev) => { const others = prev.filter((item) => item.date !== row.date); return [row, ...others]; }); setNotice('Input manual tersimpan.'); } catch (err) { setError(errorMessage(err)); } finally { setSaving(false); } })(); }} className="bg-white rounded-xl shadow-sm p-5 space-y-4" >

Input manual

{readOnly ? 'Siklus ini sudah ditutup. Data di bawah hanya untuk ditinjau.' : 'Isi data harian di sini. Total, FCR, dan EEF dihitung otomatis setelah data sumber lengkap.'}

{error && {error}} {notice && {notice}}
{saved && (

Total mortalitas {formatNumber(saved.mortality_manual_total, 0)} · karung masuk{' '} {formatNumber(saved.feed_in_manual_total, 0)} · karung tuang{' '} {formatNumber(saved.feed_use_manual_total, 0)} · karung keluar{' '} {formatNumber(saved.feed_out_manual_total, 0)} · panen{' '} {formatNumber(saved.harvest_manual_total, 0)} · berat panen{' '} {formatNumber(saved.harvest_weight_manual_total / 1000, 2)} kg

)} {!saved && rows.length > 0 && (

Belum ada input untuk tanggal ini.

)} {!readOnly && ( )}
); }; export default DashboardManualInputForm;