"use client"; import { useEffect, useMemo, useState } from "react"; import { useLocale } from "@/components/providers/locale-provider"; import { composeGradeLabel } from "@/lib/grade-display"; import { formatQuantity } from "@/lib/formatters"; import type { ApiErrorResponse, DetailResponse } from "@/types/api"; import type { WarehouseLocationRecord, WarehouseRecord } from "@/types/master-data"; import type { PurchaseDetail, PurchaseListItem } from "@/types/purchase"; import type { ReceiptDetail, ReceiptListItem } from "@/types/receipt"; type ReceiptLineForm = { purchase_line_id: string; grade_id: string; grade_name: string; qty_ordered: string; qty_received: string; qty_accepted: string; qty_rejected: string; unit_id: string; unit_code: string; unit_cost: string; warehouse_id: string; warehouse_location_id: string; notes: string; }; type ReceiptForm = { purchase_id: string; receipt_date: string; notes: string; lines: ReceiptLineForm[]; }; const emptyForm = (): ReceiptForm => ({ purchase_id: "", receipt_date: new Date().toISOString().slice(0, 10), notes: "", lines: [] }); export function ReceiptsClient() { const { dict, locale } = useLocale(); const [items, setItems] = useState([]); const [purchases, setPurchases] = useState([]); const [warehouses, setWarehouses] = useState([]); const [locations, setLocations] = useState([]); const [form, setForm] = useState(emptyForm); const [selectedReceipt, setSelectedReceipt] = useState(null); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); async function loadAll() { setLoading(true); setError(null); try { const [receiptsRes, purchasesRes, warehousesRes, locationsRes] = await Promise.all([ fetch("/api/v1/receipts", { cache: "no-store" }), fetch("/api/v1/purchases?purchase_type=REGULAR", { cache: "no-store" }), fetch("/api/v1/warehouses", { cache: "no-store" }), fetch("/api/v1/warehouse-locations", { cache: "no-store" }) ]); const receiptsPayload = (await receiptsRes.json()) as { data: ReceiptListItem[] }; const purchasesPayload = (await purchasesRes.json()) as { data: PurchaseListItem[] }; const warehousesPayload = (await warehousesRes.json()) as { data: WarehouseRecord[] }; const locationsPayload = (await locationsRes.json()) as { data: WarehouseLocationRecord[] }; if (!receiptsRes.ok || !purchasesRes.ok || !warehousesRes.ok || !locationsRes.ok) { throw new Error(dict.receipts.loadingError); } setItems(receiptsPayload.data); setPurchases(purchasesPayload.data); setWarehouses(warehousesPayload.data); setLocations(locationsPayload.data); } catch (err) { setError(err instanceof Error ? err.message : dict.receipts.loadingError); } finally { setLoading(false); } } useEffect(() => { void loadAll(); }, []); async function handlePurchaseChange(purchaseId: string) { setForm((current) => ({ ...current, purchase_id: purchaseId })); if (!purchaseId) { setForm(emptyForm()); return; } try { const response = await fetch(`/api/v1/purchases/${purchaseId}`, { cache: "no-store" }); const payload = (await response.json()) as DetailResponse | ApiErrorResponse; if (!response.ok || !("data" in payload)) { throw new Error(dict.purchases.detailError); } const detail = payload.data; setForm((current) => ({ ...current, purchase_id: detail.id, lines: detail.lines.map((line) => ({ purchase_line_id: line.id, grade_id: line.grade?.id ?? "", grade_name: line.grade?.name ?? "-", qty_ordered: String(line.qty_ordered), qty_received: String(line.qty_ordered), qty_accepted: String(line.qty_ordered), qty_rejected: "0", unit_id: line.unit.id, unit_code: line.unit.code, unit_cost: String(line.unit_price), warehouse_id: "", warehouse_location_id: "", notes: line.notes ?? "" })) })); } catch (err) { setError(err instanceof Error ? err.message : dict.purchases.detailError); } } function resetForm() { setForm(emptyForm()); setSelectedReceipt(null); setError(null); } function updateLine(index: number, patch: Partial) { setForm((current) => ({ ...current, lines: current.lines.map((line, i) => { if (i !== index) return line; const updated = { ...line, ...patch }; if ("qty_received" in patch && patch.qty_received !== undefined) { updated.qty_accepted = patch.qty_received; updated.qty_rejected = "0"; } return updated; }) })); } async function createReceipt() { setSubmitting(true); setError(null); try { const response = await fetch("/api/v1/receipts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ purchase_id: form.purchase_id, receipt_date: form.receipt_date, notes: form.notes, lines: form.lines.map((line) => ({ purchase_line_id: line.purchase_line_id, grade_id: line.grade_id || null, qty_received: Number(line.qty_received), qty_accepted: Number(line.qty_accepted), qty_rejected: Number(line.qty_rejected), unit_id: line.unit_id, unit_cost: Number(line.unit_cost), warehouse_id: line.warehouse_id, warehouse_location_id: line.warehouse_location_id || null, notes: line.notes })) }) }); const payload = (await response.json()) as DetailResponse | ApiErrorResponse; if (!response.ok) { if ("errors" in payload && payload.errors) { const firstError = Object.values(payload.errors)[0]?.[0]; throw new Error(firstError ?? payload.message ?? (locale === "id" ? "Validasi gagal" : "Validation failed")); } throw new Error("message" in payload ? payload.message : dict.common.requestFailed); } if (!("data" in payload)) throw new Error(dict.receipts.createError); resetForm(); await loadAll(); await openReceipt(payload.data.id); } catch (err) { setError(err instanceof Error ? err.message : dict.receipts.createError); } finally { setSubmitting(false); } } async function openReceipt(id: string) { setError(null); try { const response = await fetch(`/api/v1/receipts/${id}`, { cache: "no-store" }); const payload = (await response.json()) as DetailResponse | ApiErrorResponse; if (!response.ok || !("data" in payload)) { throw new Error(dict.receipts.detailError); } setSelectedReceipt(payload.data); } catch (err) { setError(err instanceof Error ? err.message : dict.receipts.detailError); } } async function generateLots(receiptId: string) { setError(null); try { const response = await fetch(`/api/v1/receipts/${receiptId}/generate-lots`, { method: "POST" }); const payload = await response.json(); if (!response.ok) { throw new Error(payload.message ?? dict.receipts.generateError); } await loadAll(); await openReceipt(receiptId); } catch (err) { setError(err instanceof Error ? err.message : dict.receipts.generateError); } } const submittedPurchases = useMemo( () => purchases.filter((purchase) => purchase.status === "SUBMITTED"), [purchases] ); return (

{dict.receipts.overline}

{dict.receipts.createTitle}

{dict.receipts.helper}

setForm((current) => ({ ...current, receipt_date: value }))} />