// resources/js/Pages/BatchCounting.tsx import React, { useMemo, useState } from 'react' import { Head, usePage } from '@inertiajs/react' import AppLayout from '@/Layouts/AppLayout' import axios from 'axios' import { toast, Toaster } from 'react-hot-toast' import { MaterialReactTable, type MRT_ColumnDef, } from 'material-react-table' import { StockEntry, StockBatch } from '@/types' const statusMapping = { CORRECT: 2, // NEW_GOODS_COUNTED MISSING_ITEMS: 7, // NEW_GOODS_MISSING BROKEN_ITEMS: 6, // NEW_GOODS_DAMAGED } export default function BatchCounting() { const { selectedBatch } = usePage<{ selectedBatch: StockBatch }>().props const [entries, setEntries] = useState(selectedBatch.stock_entries) // pin items without any of (2,6,7) to top const data = useMemo(() => { const noStatus: StockEntry[] = [] const withStatus: StockEntry[] = [] selectedBatch.stock_entries.forEach((e) => { const has = e.status_history.some(h => [2, 6, 7].includes(h.stock_entries_status_id) ) if (has) withStatus.push(e) else noStatus.push(e) }) return [...noStatus, ...withStatus] }, [selectedBatch.stock_entries]) const getLatestRelevant = (history: any[]) => { const relevant = history .filter(h => [2, 6, 7].includes(h.stock_entries_status_id)) .sort( (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime() ); return relevant.length ? relevant[relevant.length - 1] : null; }; const columns = useMemo[]>(() => [ { accessorKey: 'status', header: 'Status', size: 60, Cell: ({ row }) => { const latest = getLatestRelevant(row.original.status_history); if (!latest) return null; switch (latest.stock_entries_status_id) { case 2: return ; case 6: return ⚠️; case 7: return ✖️; default: return null; } }, }, { accessorFn: e => e.id, header: 'ID' }, { accessorFn: e => e.physical_item.name, header: 'Item' }, { accessorFn: e => e.supplier.name, header: 'Supplier' }, { accessorFn: e => e.quantity, header: 'Qty' }, ], []) // modal state const [modalOpen, setModalOpen] = useState(false) const [selectedEntry, setSelectedEntry] = useState(null) const [selectedStatus, setSelectedStatus] = useState(null) const [count, setCount] = useState(0) const openModal = (entry: StockEntry) => { setSelectedEntry(entry) setSelectedStatus(null) setCount(0) setModalOpen(true) } const submitStatus = async () => { if (!selectedEntry || !selectedStatus) return try { const response = await axios.post(`/api/stockActions/${selectedEntry.id}/status`, { status: statusMapping[selectedStatus], count: selectedStatus === 'CORRECT' ? undefined : count, }) const newHist: typeof selectedEntry.status_history[0] = response.data.history // 2. Append the new history into our entries state setEntries(prev => prev.map((e) => e.id === selectedEntry.id ? { ...e, status_history: [...e.status_history, newHist] } : e ) ) toast.success('Status updated successfully!') setModalOpen(false) } catch (error: any) { console.error('Failed to update status:', error) toast.error( error.response?.data?.message || 'Failed to update status. Please try again.' ) } } return ( (

Stock Entries

)} >

Batch: {selectedBatch.name}

!e.status_history.some(h => [2,6,7].includes(h.stock_entries_status_id))), ...entries.filter(e => e.status_history.some(h => [2,6,7].includes(h.stock_entries_status_id))), ]} enableRowSelection={false} muiTableBodyRowProps={({ row }) => { const latest = getLatestRelevant(row.original.status_history); let bgColor: string | undefined; if (latest) { switch (latest.stock_entries_status_id) { case 2: bgColor = 'rgba(220, 253, 213, 0.5)'; // green-50 break; case 6: bgColor = 'rgba(255, 247, 237, 0.5)'; // orange-50 break; case 7: bgColor = 'rgba(254, 226, 226, 0.5)'; // red-50 break; } } return { onClick: () => openModal(row.original), sx: { cursor: 'pointer', backgroundColor: bgColor, }, }; }} /> {modalOpen && selectedEntry && (

Update "{selectedEntry.physical_item.name}"

{selectedStatus && selectedStatus !== 'CORRECT' && (
setCount(Number(e.target.value))} />
)}
)}
) }