From c6931d5c5db6ee182abc59a6bb68c21be892afd0 Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 16 Mar 2026 23:34:24 -0500 Subject: [PATCH] sku auto collapse --- .../inventory/InventorySkuMasterPage.tsx | 88 +++++++++++++------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/client/src/modules/inventory/InventorySkuMasterPage.tsx b/client/src/modules/inventory/InventorySkuMasterPage.tsx index 10340b4..05473fd 100644 --- a/client/src/modules/inventory/InventorySkuMasterPage.tsx +++ b/client/src/modules/inventory/InventorySkuMasterPage.tsx @@ -1,5 +1,5 @@ import { permissions } from "@mrp/shared"; -import type { InventorySkuCatalogTreeDto, InventorySkuFamilyInput, InventorySkuNodeDto, InventorySkuNodeInput } from "@mrp/shared/dist/inventory/types.js"; +import type { InventorySkuCatalogTreeDto, InventorySkuFamilyInput, 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"; @@ -31,6 +31,7 @@ export function InventorySkuMasterPage() { const [familyForm, setFamilyForm] = useState(emptyFamilyForm); const [nodeForm, setNodeForm] = useState(emptyNodeForm); const [selectedFamilyId, setSelectedFamilyId] = useState(""); + const [expandedNodeIds, setExpandedNodeIds] = useState([]); const [status, setStatus] = useState("Loading SKU master..."); const canManage = user?.permissions.includes(permissions.inventoryWrite) ?? false; @@ -46,6 +47,7 @@ export function InventorySkuMasterPage() { setCatalog(nextCatalog); const firstFamilyId = nextCatalog.families[0]?.id ?? ""; setSelectedFamilyId((current) => current || firstFamilyId); + setExpandedNodeIds([]); setNodeForm((current) => ({ ...current, familyId: current.familyId || firstFamilyId, @@ -70,39 +72,66 @@ export function InventorySkuMasterPage() { [familyNodes] ); + function toggleNode(nodeId: string) { + setExpandedNodeIds((current) => (current.includes(nodeId) ? current.filter((id) => id !== nodeId) : [...current, nodeId])); + } + 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)
+ return branchNodes.map((node) => { + const isExpanded = expandedNodeIds.includes(node.id); + + return ( +
+
+
+
+
+ {node.childCount > 0 ? ( + + ) : ( + + o + + )} +
+
{node.code} - {node.label}
+
Level {node.level} • {node.childCount} child branch(es)
+
+
+
+
- + {node.description ?
{node.description}
: null}
- {node.description ?
{node.description}
: null} + {isExpanded ? renderNodes(node.id, depth + 1) : null}
- {renderNodes(node.id, depth + 1)} -
- )); + ); + }); } async function reloadCatalog() { @@ -127,6 +156,7 @@ export function InventorySkuMasterPage() { const created = await api.createInventorySkuFamily(token, familyForm); setFamilyForm(emptyFamilyForm); setSelectedFamilyId(created.id); + setExpandedNodeIds([]); setNodeForm((current) => ({ ...current, familyId: created.id, parentNodeId: null })); await reloadCatalog(); setStatus(`Created SKU family ${created.code}.`); @@ -143,6 +173,13 @@ export function InventorySkuMasterPage() { try { const created = await api.createInventorySkuNode(token, nodeForm); + setExpandedNodeIds((current) => { + if (!created.parentNodeId || current.includes(created.parentNodeId)) { + return current; + } + + return [...current, created.parentNodeId]; + }); setNodeForm((current) => ({ ...emptyNodeForm, familyId: current.familyId, @@ -184,6 +221,7 @@ export function InventorySkuMasterPage() { type="button" onClick={() => { setSelectedFamilyId(family.id); + setExpandedNodeIds([]); setNodeForm((current) => ({ ...current, familyId: family.id, parentNodeId: null })); }} className={`block w-full rounded-[18px] border px-3 py-3 text-left transition ${