import { permissions } from "@mrp/shared"; import type { InventorySkuCatalogTreeDto, InventorySkuFamilyInput, InventorySkuNodeDto, InventorySkuNodeInput } from "@mrp/shared/dist/inventory/types.js"; import type { ReactNode } from "react"; import { useEffect, useMemo, useState } from "react"; import { Link } from "react-router-dom"; import { useAuth } from "../../auth/AuthProvider"; import { api, ApiError } from "../../lib/api"; const emptyFamilyForm: InventorySkuFamilyInput = { code: "", sequenceCode: "", name: "", description: "", isActive: true, }; const emptyNodeForm: InventorySkuNodeInput = { familyId: "", parentNodeId: null, code: "", label: "", description: "", sortOrder: 10, isActive: true, }; export function InventorySkuMasterPage() { const { token, user } = useAuth(); const [catalog, setCatalog] = useState({ families: [], nodes: [] }); const [familyForm, setFamilyForm] = useState(emptyFamilyForm); const [nodeForm, setNodeForm] = useState(emptyNodeForm); const [selectedFamilyId, setSelectedFamilyId] = useState(""); const [status, setStatus] = useState("Loading SKU master..."); const canManage = user?.permissions.includes(permissions.inventoryWrite) ?? false; useEffect(() => { if (!token) { return; } api .getInventorySkuCatalog(token) .then((nextCatalog) => { setCatalog(nextCatalog); const firstFamilyId = nextCatalog.families[0]?.id ?? ""; setSelectedFamilyId((current) => current || firstFamilyId); setNodeForm((current) => ({ ...current, familyId: current.familyId || firstFamilyId, })); setStatus(`${nextCatalog.families.length} family branch(es) loaded.`); }) .catch((error: unknown) => { setStatus(error instanceof ApiError ? error.message : "Unable to load SKU master."); }); }, [token]); const familyNodes = useMemo( () => catalog.nodes .filter((node) => node.familyId === selectedFamilyId) .sort((left, right) => left.level - right.level || left.sortOrder - right.sortOrder || left.code.localeCompare(right.code)), [catalog.nodes, selectedFamilyId] ); const parentOptions = useMemo( () => familyNodes.filter((node) => node.level < 6), [familyNodes] ); function renderNodes(parentNodeId: string | null, depth = 0): ReactNode { const branchNodes = familyNodes.filter((node) => node.parentNodeId === parentNodeId); if (!branchNodes.length) { return null; } return branchNodes.map((node) => (
{node.code} - {node.label}
Level {node.level} • {node.childCount} child branch(es)
{node.description ?
{node.description}
: null}
{renderNodes(node.id, depth + 1)}
)); } async function reloadCatalog() { if (!token) { return; } const nextCatalog = await api.getInventorySkuCatalog(token); setCatalog(nextCatalog); if (!selectedFamilyId && nextCatalog.families[0]) { setSelectedFamilyId(nextCatalog.families[0].id); } } async function handleCreateFamily(event: React.FormEvent) { event.preventDefault(); if (!token || !canManage) { return; } try { const created = await api.createInventorySkuFamily(token, familyForm); setFamilyForm(emptyFamilyForm); setSelectedFamilyId(created.id); setNodeForm((current) => ({ ...current, familyId: created.id, parentNodeId: null })); await reloadCatalog(); setStatus(`Created SKU family ${created.code}.`); } catch (error: unknown) { setStatus(error instanceof ApiError ? error.message : "Unable to create SKU family."); } } async function handleCreateNode(event: React.FormEvent) { event.preventDefault(); if (!token || !canManage || !nodeForm.familyId) { return; } try { const created = await api.createInventorySkuNode(token, nodeForm); setNodeForm((current) => ({ ...emptyNodeForm, familyId: current.familyId, parentNodeId: created.parentNodeId, })); await reloadCatalog(); setStatus(`Created SKU branch ${created.code}.`); } catch (error: unknown) { setStatus(error instanceof ApiError ? error.message : "Unable to create SKU branch."); } } return (

Inventory Master Data

SKU Master Builder

Define family roots, branch-specific child codes, and the family-scoped short-code suffix that finishes each generated SKU.

Back to items
Families
{catalog.families.length === 0 ? (
No SKU families defined yet.
) : ( catalog.families.map((family) => ( )) )}
{canManage ? (
Add family