165 lines
5.8 KiB
TypeScript
165 lines
5.8 KiB
TypeScript
import { type FormEvent, useEffect, useState } from 'react'
|
|
import { Link, useLocation, useParams, useSearchParams } from 'react-router-dom'
|
|
import { ApiHttpError } from '../../api/http'
|
|
import {
|
|
fetchInventoryCreateForm,
|
|
fetchInventoryItem,
|
|
postInventoryItem,
|
|
putInventoryItem,
|
|
} from '../../api/inventory'
|
|
|
|
/** CI `inventory/kitchen/form.php` */
|
|
export function InventoryKitchenFormPage() {
|
|
const { itemId } = useParams()
|
|
const { pathname } = useLocation()
|
|
const [searchParams] = useSearchParams()
|
|
const isCreate = pathname.endsWith('/create')
|
|
|
|
const [categories, setCategories] = useState<Record<string, unknown>[]>([])
|
|
const [name, setName] = useState('')
|
|
const [categoryId, setCategoryId] = useState('')
|
|
const [quantity, setQuantity] = useState('0')
|
|
const [unit, setUnit] = useState('')
|
|
const [sku, setSku] = useState('')
|
|
const [description, setDescription] = useState('')
|
|
const [loading, setLoading] = useState(true)
|
|
const [saving, setSaving] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
let c = false
|
|
setLoading(true)
|
|
const run = isCreate
|
|
? fetchInventoryCreateForm('kitchen', searchParams)
|
|
: fetchInventoryItem(itemId ?? '')
|
|
run
|
|
.then((data) => {
|
|
if (c) return
|
|
if (data && typeof data === 'object') {
|
|
const o = data as Record<string, unknown>
|
|
const cats = Array.isArray(o.categories)
|
|
? (o.categories as Record<string, unknown>[])
|
|
: []
|
|
setCategories(cats)
|
|
const item = (o.item ?? o) as Record<string, unknown>
|
|
if (!isCreate && item?.id != null) {
|
|
setName(String(item.name ?? ''))
|
|
setCategoryId(item.category_id != null ? String(item.category_id) : '')
|
|
setQuantity(String(item.quantity ?? 0))
|
|
setUnit(String(item.unit ?? ''))
|
|
setSku(String(item.sku ?? ''))
|
|
setDescription(String(item.description ?? ''))
|
|
}
|
|
}
|
|
setError(null)
|
|
})
|
|
.catch((e: unknown) => {
|
|
if (!c) setError(e instanceof ApiHttpError ? e.message : 'Unable to load form.')
|
|
})
|
|
.finally(() => {
|
|
if (!c) setLoading(false)
|
|
})
|
|
return () => {
|
|
c = true
|
|
}
|
|
}, [isCreate, itemId, searchParams])
|
|
|
|
async function onSubmit(e: FormEvent<HTMLFormElement>) {
|
|
e.preventDefault()
|
|
setSaving(true)
|
|
setError(null)
|
|
const body: Record<string, unknown> = {
|
|
type: 'kitchen',
|
|
name,
|
|
category_id: categoryId === '' ? null : Number(categoryId),
|
|
quantity: quantity === '' ? 0 : Number(quantity),
|
|
unit: unit || undefined,
|
|
sku: sku || undefined,
|
|
description: description || undefined,
|
|
}
|
|
try {
|
|
if (isCreate) await postInventoryItem(body)
|
|
else await putInventoryItem(itemId ?? '', body)
|
|
window.location.href = '/app/administrator/inventory/kitchen'
|
|
} catch (err: unknown) {
|
|
setError(err instanceof ApiHttpError ? err.message : 'Save failed.')
|
|
} finally {
|
|
setSaving(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="container my-4">
|
|
<h3 className="mb-3">{isCreate ? 'Add' : 'Edit'} Kitchen Supply</h3>
|
|
{error ? <div className="alert alert-danger">{error}</div> : null}
|
|
{loading ? <p className="text-muted">Loading…</p> : null}
|
|
|
|
{!loading ? (
|
|
<form onSubmit={(ev) => void onSubmit(ev)}>
|
|
<div className="row g-3">
|
|
<div className="col-md-6">
|
|
<label className="form-label">Name</label>
|
|
<input
|
|
className="form-control"
|
|
required
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="col-md-6">
|
|
<label className="form-label">Category</label>
|
|
<select
|
|
className="form-select"
|
|
value={categoryId}
|
|
onChange={(e) => setCategoryId(e.target.value)}
|
|
>
|
|
<option value="">— None —</option>
|
|
{categories.map((c) => (
|
|
<option key={String(c.id)} value={String(c.id)}>
|
|
{String(c.name ?? '')}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div className="col-md-2">
|
|
<label className="form-label">Quantity</label>
|
|
<input
|
|
type="number"
|
|
min={0}
|
|
className="form-control"
|
|
value={quantity}
|
|
onChange={(e) => setQuantity(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="col-md-2">
|
|
<label className="form-label">Unit</label>
|
|
<input className="form-control" value={unit} onChange={(e) => setUnit(e.target.value)} />
|
|
</div>
|
|
<div className="col-md-4">
|
|
<label className="form-label">SKU</label>
|
|
<input className="form-control" value={sku} onChange={(e) => setSku(e.target.value)} />
|
|
</div>
|
|
<div className="col-12">
|
|
<label className="form-label">Description / Notes</label>
|
|
<textarea
|
|
className="form-control"
|
|
rows={3}
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="mt-3 d-flex gap-2">
|
|
<button type="submit" className="btn btn-primary" disabled={saving}>
|
|
{saving ? 'Saving…' : isCreate ? 'Save' : 'Update'}
|
|
</button>
|
|
<Link className="btn btn-outline-secondary" to="/app/administrator/inventory/kitchen">
|
|
Cancel
|
|
</Link>
|
|
</div>
|
|
</form>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|