import type { InventoryItemDetailDto, InventoryReservationInput, InventoryTransactionInput, InventoryTransferInput, WarehouseLocationOptionDto, } from "@mrp/shared/dist/inventory/types.js"; import { permissions } from "@mrp/shared"; import { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { useAuth } from "../../auth/AuthProvider"; import { api, ApiError } from "../../lib/api"; import { emptyInventoryTransactionInput, inventoryTransactionOptions } from "./config"; import { InventoryAttachmentsPanel } from "./InventoryAttachmentsPanel"; import { InventoryStatusBadge } from "./InventoryStatusBadge"; import { InventoryTransactionTypeBadge } from "./InventoryTransactionTypeBadge"; import { InventoryTypeBadge } from "./InventoryTypeBadge"; const emptyTransferInput: InventoryTransferInput = { quantity: 1, fromWarehouseId: "", fromLocationId: "", toWarehouseId: "", toLocationId: "", notes: "", }; const emptyReservationInput: InventoryReservationInput = { quantity: 1, warehouseId: null, locationId: null, notes: "", }; export function InventoryDetailPage() { const { token, user } = useAuth(); const { itemId } = useParams(); const [item, setItem] = useState(null); const [locationOptions, setLocationOptions] = useState([]); const [transactionForm, setTransactionForm] = useState(emptyInventoryTransactionInput); const [transferForm, setTransferForm] = useState(emptyTransferInput); const [reservationForm, setReservationForm] = useState(emptyReservationInput); const [transactionStatus, setTransactionStatus] = useState("Record receipts, issues, and adjustments against this item."); const [transferStatus, setTransferStatus] = useState("Move physical stock between warehouses or locations without manual paired entries."); const [reservationStatus, setReservationStatus] = useState("Reserve stock manually while active work orders reserve component demand automatically."); const [isSavingTransaction, setIsSavingTransaction] = useState(false); const [isSavingTransfer, setIsSavingTransfer] = useState(false); const [isSavingReservation, setIsSavingReservation] = useState(false); const [status, setStatus] = useState("Loading inventory item..."); const canManage = user?.permissions.includes(permissions.inventoryWrite) ?? false; useEffect(() => { if (!token || !itemId) { return; } api .getInventoryItem(token, itemId) .then((nextItem) => { setItem(nextItem); setStatus("Inventory item loaded."); }) .catch((error: unknown) => { const message = error instanceof ApiError ? error.message : "Unable to load inventory item."; setStatus(message); }); api .getWarehouseLocationOptions(token) .then((options) => { setLocationOptions(options); const firstOption = options[0]; if (!firstOption) { return; } setTransactionForm((current) => ({ ...current, warehouseId: current.warehouseId || firstOption.warehouseId, locationId: current.locationId || firstOption.locationId, })); setTransferForm((current) => ({ ...current, fromWarehouseId: current.fromWarehouseId || firstOption.warehouseId, fromLocationId: current.fromLocationId || firstOption.locationId, toWarehouseId: current.toWarehouseId || firstOption.warehouseId, toLocationId: current.toLocationId || firstOption.locationId, })); }) .catch(() => setLocationOptions([])); }, [itemId, token]); function updateTransactionField(key: Key, value: InventoryTransactionInput[Key]) { setTransactionForm((current) => ({ ...current, [key]: value })); } function updateTransferField(key: Key, value: InventoryTransferInput[Key]) { setTransferForm((current) => ({ ...current, [key]: value })); } async function handleTransactionSubmit(event: React.FormEvent) { event.preventDefault(); if (!token || !itemId) { return; } setIsSavingTransaction(true); setTransactionStatus("Saving stock transaction..."); try { const nextItem = await api.createInventoryTransaction(token, itemId, transactionForm); setItem(nextItem); setTransactionStatus("Stock transaction recorded."); setTransactionForm((current) => ({ ...emptyInventoryTransactionInput, transactionType: current.transactionType, warehouseId: current.warehouseId, locationId: current.locationId, })); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to save stock transaction."; setTransactionStatus(message); } finally { setIsSavingTransaction(false); } } async function handleTransferSubmit(event: React.FormEvent) { event.preventDefault(); if (!token || !itemId) { return; } setIsSavingTransfer(true); setTransferStatus("Saving transfer..."); try { const nextItem = await api.createInventoryTransfer(token, itemId, transferForm); setItem(nextItem); setTransferStatus("Transfer recorded."); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to save transfer."; setTransferStatus(message); } finally { setIsSavingTransfer(false); } } async function handleReservationSubmit(event: React.FormEvent) { event.preventDefault(); if (!token || !itemId) { return; } setIsSavingReservation(true); setReservationStatus("Saving reservation..."); try { const nextItem = await api.createInventoryReservation(token, itemId, reservationForm); setItem(nextItem); setReservationStatus("Reservation recorded."); setReservationForm((current) => ({ ...current, quantity: 1, notes: "" })); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to save reservation."; setReservationStatus(message); } finally { setIsSavingReservation(false); } } if (!item) { return
{status}
; } return (

Inventory Detail

{item.sku}

{item.name}

Last updated {new Date(item.updatedAt).toLocaleString()}.

Back to items {canManage ? ( Edit item ) : null}

On Hand

{item.onHandQuantity}

Reserved

{item.reservedQuantity}

Available

{item.availableQuantity}

Stock Locations

{item.stockBalances.length}

Transactions

{item.recentTransactions.length}

Transfers

{item.transfers.length}

Reservations

{item.reservations.length}

Item Definition

Description
{item.description || "No description provided."}
Unit of measure
{item.unitOfMeasure}
Default cost
{item.defaultCost == null ? "Not set" : `$${item.defaultCost.toFixed(2)}`}
Default price
{item.defaultPrice == null ? "Not set" : `$${item.defaultPrice.toFixed(2)}`}
Flags
{item.isSellable ? "Sellable" : "Not sellable"} / {item.isPurchasable ? "Purchasable" : "Not purchasable"}

Stock By Location

{item.stockBalances.length === 0 ? (

No stock or reservation balances have been posted for this item yet.

) : (
{item.stockBalances.map((balance) => (
{balance.warehouseCode} / {balance.locationCode}
{balance.warehouseName} / {balance.locationName}
{balance.quantityOnHand} on hand
{balance.quantityReserved} reserved / {balance.quantityAvailable} available
))}
)}
{canManage ? (

Stock Transactions