import React, { useState, useEffect, useMemo, useRef } from 'react'; import { Head } from '@inertiajs/react'; import AppLayout from '@/Layouts/AppLayout'; import { MaterialReactTable, type MRT_ColumnDef, type MRT_PaginationState, type MRT_SortingState, } from 'material-react-table'; import axios from 'axios'; import { toast } from 'react-hot-toast'; import { Combobox } from '@headlessui/react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronDown, faCheck } from '@fortawesome/free-solid-svg-icons'; // --- Interfaces --- interface DropdownOption { id: number; name: string; } interface StockEntry { id: number; physical_item_id: number | null; supplier_id: number | null; count: number; price: number | null; bought: string | null; description: string | null; note: string | null; stock_position_id: number | null; country_of_origin_id: number | null; on_the_way: boolean; stock_batch_id: number | null; created_by?: number; updated_by?: number; // Related objects physical_item?: DropdownOption; supplier?: DropdownOption; stock_position?: { id: number; line: string; rack: string; shelf: string; position: string }; } // Form data mirrors the model fields interface StockEntryFormData { physical_item_id: number | null; supplier_id: number | null; count: number; price: number | null; bought: string | null; description: string | null; note: string | null; stock_position_id: number | null; country_of_origin_id: number | null; on_the_way: boolean; stock_batch_id: number | null; } export default function StockEntries() { // Table state const [data, setData] = useState([]); const [isError, setIsError] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isRefetching, setIsRefetching] = useState(false); const [rowCount, setRowCount] = useState(0); // Options for dropdowns const [stockPositions, setStockPositions] = useState([]); const [suppliers, setSuppliers] = useState([]); const [physicalItems, setPhysicalItems] = useState([]); const [originCountries, setOriginCountries] = useState([]); // Modal & form state const [editingEntry, setEditingEntry] = useState(null); const [formData, setFormData] = useState({ physical_item_id: null, supplier_id: null, count: 0, price: null, bought: null, description: null, note: null, stock_position_id: null, country_of_origin_id: null, on_the_way: false, stock_batch_id: null, }); // Combobox search state const [itemQuery, setItemQuery] = useState(''); const filteredItems = useMemo( () => itemQuery === '' ? physicalItems : physicalItems.filter(item => item.name.toLowerCase().includes(itemQuery.toLowerCase()), ), [itemQuery, physicalItems], ); // Pagination, sorting, filtering const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); const [sorting, setSorting] = useState([{ id: 'updated_at', desc: true }]); const [globalFilter, setGlobalFilter] = useState(''); // Modal ref const dialogRef = useRef(null); const openModal = () => dialogRef.current?.showModal(); const closeModal = () => dialogRef.current?.close(); // Column definitions const columns = useMemo[]>( () => [ { accessorKey: 'id', header: 'ID', size: 80 }, { accessorKey: 'physical_item.name', header: 'Physical Item', size: 200 }, { accessorKey: 'supplier.name', header: 'Supplier', size: 150 }, { accessorKey: 'physical_item.manufacturer.name', header: 'Brand', size: 150 }, { accessorKey: 'count', header: 'Count', size: 100 }, { accessorKey: 'price', header: 'Price', size: 100, Cell: ({ cell }) => cell.getValue()?.toFixed(2) || '-' }, { accessorKey: 'bought', header: 'Bought Date', size: 120 }, { accessorKey: 'on_the_way', header: 'On The Way', size: 100, Cell: ({ cell }) => cell.getValue() ? 'Yes' : 'No' }, { accessorKey: 'stock_position', header: 'Position', size: 150, Cell: ({ row }) => { const pos = row.original.stock_position; return pos ? `${pos.line}-${pos.rack}-${pos.shelf}-${pos.position}` : '-'; } }, { accessorKey: 'updated_at', header: 'Last Updated', size: 150 }, ], [], ); // Load dropdown options (except items) const fetchOptions = async () => { try { const response = await axios.get('/api/stockData/options'); setStockPositions(response.data.stockPositions); setSuppliers(response.data.suppliers); setOriginCountries(response.data.countriesOrigin); console.log("data all"); console.log(response.data); } catch (error) { console.error('Error fetching options', error); toast.error('Failed to load form options'); } }; // Fetch filtered items const fetchPhysicalItems = async (query: string) => { if (!query) return; try { const response = await axios.get('/api/stockData/options/items', { params: { item_name: query } }); setPhysicalItems(response.data.physicalItems); console.log("physical items"); console.log(response.data); } catch (error) { console.error('Error fetching items', error); toast.error('Failed to load form items'); } }; // Debounce itemQuery changes useEffect(() => { const delayDebounce = setTimeout(() => { fetchPhysicalItems(itemQuery); }, 500); return () => clearTimeout(delayDebounce); }, [itemQuery]); // Fetch table data const fetchData = async () => { if (!data.length) setIsLoading(true); else setIsRefetching(true); try { const response = await axios.get('/api/stockData'); setData(response.data.data); console.log(response.data.data); setRowCount(response.data.meta.total); } catch (error) { setIsError(true); console.error(error); toast.error('Failed to fetch stock entries'); } finally { setIsLoading(false); setIsRefetching(false); } }; // Handle form input changes const handleInputChange = ( e: React.ChangeEvent ) => { const { name, value, type } = e.target; if (type === 'checkbox') { setFormData(prev => ({ ...prev, [name]: (e.target as HTMLInputElement).checked })); } else if (type === 'number') { setFormData(prev => ({ ...prev, [name]: value ? parseFloat(value) : null })); } else { setFormData(prev => ({ ...prev, [name]: value || null })); } }; // Submit form const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingEntry) { await axios.put(`/api/stockData/${editingEntry.id}`, formData); toast.success('Stock entry updated successfully'); } else { await axios.post('/api/stockData', formData); toast.success('Stock entry created successfully'); } closeModal(); fetchData(); } catch (error) { console.error('Error submitting form', error); toast.error('Failed to save stock entry'); } }; // Edit/Delete handlers const handleEdit = (entry: StockEntry) => { setEditingEntry(entry); setFormData({ physical_item_id: entry.physical_item_id, supplier_id: entry.supplier_id, count: entry.count, price: entry.price, bought: entry.bought, description: entry.description, note: entry.note, stock_position_id: entry.stock_position_id, country_of_origin_id: entry.country_of_origin_id, on_the_way: entry.on_the_way, stock_batch_id: entry.stock_batch_id, }); openModal(); }; const handleDelete = async (id: number) => { if (!confirm('Are you sure you want to delete this entry?')) return; try { await axios.delete(`/api/stock-entries/${id}`); toast.success('Stock entry deleted successfully'); fetchData(); } catch (error) { console.error('Error deleting entry', error); toast.error('Failed to delete stock entry'); } }; // Add new const handleAdd = () => { setEditingEntry(null); setFormData({ physical_item_id: null, supplier_id: null, count: 0, price: null, bought: null, description: null, note: null, stock_position_id: null, country_of_origin_id: null, on_the_way: false, stock_batch_id: null, }); openModal(); }; // handleNewBatch const handleNewBatch = () => { }; useEffect(() => { fetchData(); }, [pagination.pageIndex, pagination.pageSize, sorting, globalFilter]); useEffect(() => { fetchOptions(); }, []); return ( (

Stock Entries

)}>

Stock Entries

[ , , ]} />

{editingEntry ? 'Edit Stock Entry' : 'New Stock Entry'}

{/* Physical Item */}
setFormData(prev => ({ ...prev, physical_item_id: val }))}> setItemQuery(e.target.value)} displayValue={id => physicalItems.find(i => i.id === id)?.name || ''} placeholder="Select item..." className="input" /> {filteredItems.map(item => (
{item.name} {formData.physical_item_id === item.id && }
))}
{/* Supplier */}
{/* Count, Price, Bought Date */}
{/* Description & Note */}