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

200 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// components/modals/CountStockModal.tsx
import React from 'react'
import axios from 'axios'
import { toast } from 'react-hot-toast'
import { StockBatch, StockEntry } from "@/types"
interface Props {
onClose: () => void
selectedBatch: () => StockBatch
}
const CountStockModal: React.FC<Props> = ({ onClose, selectedBatch }) => {
const [quantity, setQuantity] = React.useState("")
const [isDropdownOpen, setIsDropdownOpen] = React.useState(false)
const [selectedEntry, setSelectedEntry] = React.useState<StockEntry | null>(null)
const handleSelect = (entry: StockEntry) => {
setSelectedEntry(entry)
setIsDropdownOpen(false)
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!selectedEntry) {
toast.error("Please select a product first.")
return
}
try {
const { data } = await axios.post(
"/api/pdaView/countStock",
{
entryId: selectedEntry.id,
quantity,
},
{ withCredentials: true }
)
// If the HTTP status was 200 but success===false,
// inspect data.error to decide which toast to show
if (!data.success) {
switch (data.error) {
case "already_counted":
toast.error("This item has already been counted.")
break
case "validation_failed":
toast.error("Validation failed. Please check your inputs.")
break
case "not_found":
toast.error("Could not find that stock entry.")
break
case "server_error":
default:
// show the message from the server if provided, otherwise fallback
toast.error(
data.message ?? "Something went wrong on the server."
)
break
}
return
}
// success === true:
toast.success("Stock counted!")
onClose()
} catch (err: any) {
// If the request itself failed (e.g. network or HTTP 500 that didn't return JSON):
// You can inspect err.response.status if you want, e.g. 409 → extract JSON, etc.
if (err.response && err.response.data) {
// Attempt to read the servers JSON error payload
const payload = err.response.data
if (payload.error === "already_counted") {
toast.error("This item has already been counted.")
return
}
if (payload.error === "validation_failed") {
toast.error("Validation failed. Please check your inputs.")
return
}
if (payload.error === "not_found") {
toast.error("Could not find that stock entry.")
return
}
// Fallback to any message string
toast.error(payload.message || "Unknown error occurred.")
return
}
// Otherwise, a true “network” or unexpected error:
toast.error("Failed to count stock. Please try again.")
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<h3 className="font-bold text-lg">Count Stock</h3>
{/* Product Dropdown */}
<div className="form-control w-full">
<label className="label">
<span className="label-text">Product</span>
</label>
<div className="dropdown w-full">
<button
type="button"
className={`btn w-full justify-between ${selectedEntry && selectedEntry?.counted ? "pointer-events-none opacity-50" : "pointer-events-auto opacity-100"}`}
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} ({selectedEntry.original_count_invoice})
</span>
</div>
) : (
"Select product ..."
)}
<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"
}`}
>
{selectedBatch.stock_entries.map((entry) => (
<li key={entry.id}>
<button
type="button"
disabled={entry.counted}
className={`flex items-center px-2 py-1 hover:bg-gray-100 rounded ${entry.counted ? "pointer-events-none opacity-50" : "pointer-events-auto opacity-100"}`}
onClick={() => handleSelect(entry)}
>
<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.original_count_invoice}) {entry.counted ? " --- (Already counted)" : ""}
</span>
</button>
</li>
))}
{selectedBatch.stock_entries.length === 0 && (
<li>
<span className="px-2 py-1 text-gray-500">No items available</span>
</li>
)}
</ul>
</div>
</div>
{/* Quantity Input */}
<div className="form-control w-full">
<label className="label">
<span className="label-text">Quantity</span>
</label>
<input
type="number"
value={quantity}
onChange={(e) => setQuantity(+e.target.value)}
className="input input-bordered w-full"
required
/>
</div>
{/* Actions */}
<div className="modal-action flex justify-end space-x-2">
<button type="button" className="btn" onClick={onClose}>
Cancel
</button>
<button
type="submit"
className="btn btn-primary"
disabled={!selectedEntry || quantity < 0}
>
Submit
</button>
</div>
</form>
)
}
export default CountStockModal