import { Form, Head, Link, usePage } from '@inertiajs/react'; import { ArrowLeft, Check, ClipboardPaste, Copy, Plus, Trash2, } from 'lucide-react'; import { useCallback, useRef, useState } from 'react'; import { toast } from 'sonner'; import { ConfirmDialog } from '@/components/dialogs'; import { FileUpload } from '@/components/inputs'; import { InputError } from '@/components/ui'; import { RupiahInput } from '@/components/inputs'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { useRawMaterialDraftSave } from '@/hooks/use-raw-material-draft'; import { UNITS } from '@/lib/constants'; import { clearRawMaterialDraft } from '@/lib/raw-material-draft'; import { getTemporaryUrl } from '@/lib/upload'; import { index as rawMaterialIndex, update, } from '@/routes/admin/master/raw-materials'; import type { RawMaterialForEdit, RawMaterialVariantForEdit } from './columns'; type Props = { rawMaterial: RawMaterialForEdit; }; type VariantState = { id: number | null; variant: string; price: number; stock: string; photo: string | null; photoUrl: string | null; uploading: boolean; }; export default function RawMaterialEdit({ rawMaterial }: Props) { const { auth } = usePage().props as { auth: { user?: { id?: number } } }; const userId = auth.user?.id; clearRawMaterialDraft('edit', userId, rawMaterial.id); const [name, setName] = useState(rawMaterial.name); const [unit, setUnit] = useState(rawMaterial.unit); const [isActive, setIsActive] = useState(rawMaterial.is_active); const serverVariants: VariantState[] = rawMaterial.raw_material_prices.map( (v: RawMaterialVariantForEdit) => ({ id: v.id, variant: v.variant, price: v.price, stock: String(v.stock), photo: v.photo_key, photoUrl: v.photo_url, uploading: false, }), ); const [variants, setVariants] = useState(() => { return serverVariants.length > 0 ? serverVariants : [ { id: null, variant: '', price: 0, stock: '0', photo: null, photoUrl: null, uploading: false, }, ]; }); const variantsRef = useRef(variants); variantsRef.current = variants; const draftData = { name, unit, isActive, variants: variants.map((v) => ({ id: v.id, variant: v.variant, price: v.price, stock: v.stock, photo: v.photo ?? undefined, })), }; useRawMaterialDraftSave('edit', draftData, userId, rawMaterial.id); const addVariant = useCallback(() => { setVariants((prev) => [ ...prev, { id: null, variant: '', price: 0, stock: '0', photo: null, photoUrl: null, uploading: false, }, ]); }, []); const removeVariant = useCallback((index: number) => { setVariants((prev) => prev.filter((_, i) => i !== index)); }, []); const updateVariant = useCallback( (index: number, field: keyof VariantState, value: unknown) => { setVariants((prev) => { const updated = [...prev]; (updated[index] as Record)[field] = value; return updated; }); }, [], ); const [copiedIndex, setCopiedIndex] = useState(null); const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); const [deleteVariantIndex, setDeleteVariantIndex] = useState( null, ); const confirmRemoveVariant = useCallback((index: number) => { setDeleteVariantIndex(index); setDeleteConfirmOpen(true); }, []); const copyPrice = useCallback((variantIndex: number) => { setVariants((prev) => { const price = prev[variantIndex].price; navigator.clipboard.writeText(String(price)); setCopiedIndex(variantIndex); setTimeout(() => setCopiedIndex(null), 1500); return prev; }); }, []); const pastePrice = useCallback((variantIndex: number) => { navigator.clipboard.readText().then((text) => { try { const price = Number(text); if (!isNaN(price)) { setVariants((prev) => { const updated = [...prev]; updated[variantIndex] = { ...updated[variantIndex], price, }; return updated; }); } } catch { // invalid clipboard data } }); }, []); const applyToAll = useCallback((variantIndex: number) => { setVariants((prev) => { const sourcePrice = prev[variantIndex].price; return prev.map((v, i) => i === variantIndex ? v : { ...v, price: sourcePrice }, ); }); }, []); function getPayload() { return { name, unit, is_active: isActive, variants: variantsRef.current.map((v) => ({ id: v.id, variant: v.variant, price: Number(v.price), stock: parseFloat(String(v.stock)) || 0, photo_key: v.photo, })), }; } return ( <>

Edit Bahan Baku

({ ...data, ...getPayload(), })} onError={() => { toast.error('Ada data yang belum sesuai, silakan periksa kembali input Anda.'); }} > {({ errors, processing }) => ( <>
Informasi Bahan Baku
setName(e.target.value) } placeholder="Masukkan nama bahan baku" />
{UNITS.map((u) => (
))}
Varian Bahan Baku {variants.map( (variant, variantIndex) => (

Varian{' '} {variantIndex + 1}

{variants.length > 1 && ( )}
updateVariant( variantIndex, 'variant', e.target .value, ) } placeholder="Contoh: Ukuran L, Warna Merah" />
updateVariant( variantIndex, 'price', val, ) } />
updateVariant( variantIndex, 'stock', e.target.value, ) } />
{ updateVariant( variantIndex, 'photo', key, ); updateVariant( variantIndex, 'photoUrl', key ? getTemporaryUrl( key, ) : null, ); }} folder="raw-material-variant" existingUrl={ variant.photoUrl } onUploadingChange={( uploading, ) => updateVariant( variantIndex, 'uploading', uploading, ) } />
), )}
)}
{ if (!open) { setDeleteConfirmOpen(false); setDeleteVariantIndex(null); } }} title="Hapus Varian" description="Apakah Anda yakin ingin menghapus varian ini?" confirmLabel="Hapus" onConfirm={() => { if (deleteVariantIndex !== null) { removeVariant(deleteVariantIndex); } setDeleteConfirmOpen(false); setDeleteVariantIndex(null); }} />
); }