137 lines
4.9 KiB
TypeScript
137 lines
4.9 KiB
TypeScript
import React, { useState } from 'react'
|
|
import axios from 'axios'
|
|
import { toast } from 'react-hot-toast'
|
|
import { StockPosition, StockSection } from '@/types'
|
|
|
|
interface Props {
|
|
onClose: () => void
|
|
fromPosition?: StockPosition | null
|
|
fromSection?: StockSection | null
|
|
toPosition?: StockPosition | null
|
|
toSection?: StockSection | null
|
|
}
|
|
|
|
const MoveSectionModal: React.FC<Props> = ({
|
|
onClose,
|
|
fromSection,
|
|
toSection,
|
|
}) => {
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const handleMove = async () => {
|
|
if (!fromSection || !toSection) return
|
|
setLoading(true)
|
|
const toastId = toast.loading('Moving…')
|
|
|
|
try {
|
|
const { data } = await axios.post(
|
|
'/api/pdaView/moveStockSection',
|
|
{
|
|
entry_id: fromSection.entries[0].id,
|
|
section_id: fromSection.section_id,
|
|
new_section_id: toSection.section_id,
|
|
count: fromSection.entries[0].pivot.count,
|
|
},
|
|
{ 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 moving.')
|
|
break
|
|
}
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
toast.dismiss(toastId)
|
|
toast.success('Moved successfully.')
|
|
onClose()
|
|
} 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 {
|
|
if(fromSection.entries?.length <= 0) {
|
|
toast.error('No items in the FROM section.');
|
|
}
|
|
else {
|
|
toast.error('Network error. Please try again.');
|
|
}
|
|
}
|
|
console.error('MoveSectionModal error:', err)
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<h3 className="font-bold text-lg">Move Stock Item</h3>
|
|
|
|
{!fromSection && (
|
|
<div className="alert alert-info">
|
|
Please scan the <strong>TO</strong> section…
|
|
</div>
|
|
)}
|
|
|
|
{fromSection && !toSection && (
|
|
<div className="alert alert-info">
|
|
Scanned <strong>FROM:</strong> {fromSection.section_id}. Now scan the <strong>TO</strong> section…
|
|
</div>
|
|
)}
|
|
|
|
{fromSection && toSection && (
|
|
<div className="alert alert-success">
|
|
Ready to move from {fromSection.section_id} → {toSection.section_id}
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex space-x-2">
|
|
<button
|
|
type="button"
|
|
className="btn btn-primary"
|
|
onClick={handleMove}
|
|
disabled={!fromSection || !toSection || loading}
|
|
>
|
|
{loading ? 'Moving…' : 'Confirm Move'}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="btn"
|
|
onClick={onClose}
|
|
disabled={loading}
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default MoveSectionModal
|