vat_wms/resources/js/Components/modals/ChangeCountModal.tsx

211 lines
8.6 KiB
TypeScript

import React from 'react'
import axios from 'axios'
import { toast } from 'react-hot-toast'
import { StockPosition, StockEntry } from '@/types'
interface Props {
onClose: () => void
selectedPosition: StockPosition
}
const ChangeCountModal: React.FC<Props> = ({ onClose, selectedPosition }) => {
const [selectedEntry, setSelectedEntry] = React.useState<StockEntry | null>(null)
const [isDropdownOpen, setIsDropdownOpen] = React.useState(false)
const [newCount, setNewCount] = React.useState<number | null>(null)
const [loading, setLoading] = React.useState(false)
const handleChangeCount = async () => {
setLoading(true)
const toastId = toast.loading('Changing count…')
try {
const { data } = await axios.post(
'/api/pdaView/changeCount',
{
entry_id: selectedEntry.id,
section_id: selectedEntry.pivot.section_id,
count: selectedEntry.pivot.count,
new_count: newCount,
},
{ withCredentials: true }
)
if (!data.success) {
toast.dismiss(toastId)
switch (data.error) {
case 'validation_failed':
toast.error('Validation failed. Check inputs.')
break
case 'not_found':
toast.error('Entry or section not found.')
break
case 'section_occupied':
toast.error('Target section is already occupied.')
break
case 'insufficient_capacity':
toast.error('Not enough capacity in target section.')
break
default:
toast.error(data.message ?? 'Server error during count change.')
break
}
setLoading(false)
return
}
toast.dismiss(toastId)
toast.success('Count changed on position.')
resetAndClose()
} catch (err: any) {
toast.dismiss()
if (err.response && err.response.data) {
const payload = err.response.data
if (payload.error === 'validation_failed') {
toast.error('Validation failed. Check inputs.')
} else if (payload.error === 'not_found') {
toast.error('Entry or section not found.')
} else if (payload.error === 'section_occupied') {
toast.error('Target section is already occupied.')
} else if (payload.error === 'insufficient_capacity') {
toast.error('Not enough capacity in target section.')
} else {
toast.error(payload.message || 'Unknown error occurred.')
}
} else {
toast.error('Network error. Please try again.')
}
setLoading(false)
}
}
const resetAndClose = () => {
setSelectedEntry(null)
setNewCount(null)
setLoading(false)
setIsDropdownOpen(false)
onClose()
}
// Build flat list of entries grouped by section
const entriesList = React.useMemo<StockEntry[]>(() => {
if (!selectedPosition.sections) return []
return selectedPosition.sections.flatMap((section) =>
section.entries.map((entry) => ({ ...entry, pivot: entry.pivot, _section: section }))
)
}, [selectedPosition])
console.log(entriesList);
return (
<div className="space-y-4">
<h3 className="font-bold text-lg">Change item count</h3>
{/* Entry Dropdown */}
<div className="form-control w-full">
<label className="label">
<span className="label-text">Select Item</span>
</label>
<div className="dropdown w-full">
<button
type="button"
className="btn w-full justify-between"
onClick={() => setIsDropdownOpen((prev) => !prev)}
>
{selectedEntry ? (
<div className="flex items-center space-x-2">
<img
src={selectedEntry?.physical_item?.image_url}
alt={selectedEntry?.physical_item?.name}
className="w-6 h-6 rounded-full"
/>
<span>
{selectedEntry?.physical_item?.name} (Count: {selectedEntry?.pivot.count}) in Sect.{' '}
{entriesList.find(e => e.id === selectedEntry.id)._section.section_symbol}
</span>
</div>
) : (
'Select item...'
)}
<svg
className="fill-current w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path d="M5.516 7.548a.75.75 0 0 1 1.06 0L10 10.972l3.424-3.424a.75.75 0 1 1 1.06 1.06l-4 4a.75.75 0 0 1-1.06 0l-4-4a.75.75 0 0 1 0-1.06z"/>
</svg>
</button>
<ul
tabIndex={0}
className={`dropdown-content menu p-2 shadow bg-base-100 rounded-box w-full mt-1 ${
isDropdownOpen ? 'block' : 'hidden'
}`}
>
{entriesList.map((entry) => (
<li key={`${entry.id}-${entry.pivot.section_id}`}>
<button
type="button"
className="flex items-center px-2 py-1 hover:bg-gray-100 rounded"
onClick={() => {
setSelectedEntry(entry)
setIsDropdownOpen(false)
}}
>
<img
src={entry.physical_item?.image_url}
alt={entry.physical_item?.name}
className="w-6 h-6 rounded-full mr-2"
/>
<span>
{entry.physical_item?.name} ({entry.pivot?.count} ks) Section {entry._section?.section_symbol}
</span>
</button>
</li>
))}
{entriesList.length === 0 && (
<li>
<span className="px-2 py-1 text-gray-500">No items in this position</span>
</li>
)}
</ul>
</div>
{selectedEntry ? (
<label className="input">
<input type="number" className="grow" placeholder="123" onChange={(e) => setNewCount(parseInt(e.target.value))} />
<kbd className="kbd kbd-sm">123</kbd>
</label>
): ""}
</div>
{/* Simulate Scan Button */}
<div className="flex space-x-2">
<button
type="button"
className="btn btn-primary"
onClick={handleChangeCount}
disabled={!selectedEntry || loading}
>
Confirm
</button>
</div>
{/* Waiting for scan indicator */}
{selectedEntry && newCount === null && !loading && (
<div className="flex items-center space-x-2 mt-2">
<span>Waiting for section scan...</span>
<span className="loading loading-spinner text-primary"></span>
</div>
)}
{/* Cancel Button */}
<div className="modal-action flex justify-end pt-2">
<button type="button" className="btn" onClick={resetAndClose}>
Cancel
</button>
</div>
</div>
)
}
export default ChangeCountModal