@classytic/commerce-ui
v0.4.0
Published
Commerce admin UI — the dashboard feature screens (WMS, inventory, procurement, sales, finance…) organized by Flow kernel bounded contexts. Commerce-owned: imports @classytic/commerce-sdk directly, composes @classytic/fluid + host shadcn, mounts inside @c
Readme
@classytic/commerce-ui
The commerce admin feature UI — the dashboard screens (WMS, inventory, procurement, sales, finance…) that sit inside the neutral dashboard chrome. It's the domain tier of the front-end stack:
@classytic/fluid UI atoms (DataTable, dialogs, document primitives)
@classytic/erp-shell neutral dashboard chrome (shell, auth, branch, guard) ← peer
@classytic/commerce-ui THIS — commerce feature screens + widgets + manifest
<commerce customer app> thin: route re-exports + theme + brand + configWhat it is (and is NOT)
- Commerce-owned, not vertical-neutral. It imports
@classytic/commerce-sdkdirectly — every commerce customer runs the same SDK, so there's no data injection and no port indirection. (A different vertical — hotel, rental — would build its own UI package; the only genuinely shared pieces areerp-shelland, if it ever materialises, a catalog package with a real second consumer. Flow/WMS is commerce-only, so paying a DI tax for hotel reuse it will never use would be premature.) - The reuse axis is N commerce customers, not N verticals. Fix a screen once, bump the version, it lands for every customer deployment. That's the win.
- Ships no UI atoms. It composes the host's shadcn primitives via the
@/alias (external, never bundled) and@classytic/fluid. The host owns the look.
Organized by the Flow kernel's bounded contexts
Not a flat screens/ folder — the ~28 WMS/inventory screens map onto the same
bounded contexts as the @classytic/flow kernel, one subpath each:
| Subpath | Context | Screens |
|---|---|---|
| /storage | warehouse topology | nodes · locations · slotting · classification · uom-groups |
| /inventory | quant layer | stock · lots · movements · low-stock · adjustments |
| /procurement | inbound | procurement · purchase-orders · requests · receiving · landed-cost |
| /outbound | fulfillment | waves · packages · dispatch · transfers |
| /operations | work mgmt | tasks · labor · replenishment |
| /costing | valuation | standard-costs · valuation · cost |
| /quality | quality & reverse | quality · returns · scrap |
| /analytics | observability | trace · audit · reports |
| /widgets | shared domain widgets | StockLevelBadge, LocationPicker, … |
| /manifest | nav fragments (one per context) | — |
Each context owns its screens + a manifest fragment. The app merges the
fragments it mounts. (Shipped so far: partners (SuppliersScreen, the reference
Tier-1 config), storage (LocationsScreen), widgets (StockLevelBadge),
manifest (createStorageFragment). The rest are mechanical replication.)
Routes are host config — the package is route-agnostic
The package owns no absolute URLs (same rule as branding — the host owns its
route tree). Screens and fragments are factories the host feeds its own routes
into. Routes are passed as plain strings so a Server-Component route file
can hand them straight to a Client screen (functions aren't serializable across
the RSC boundary — a detail: (id) => … override only works from a client mount;
by default detail derives as ${root}/${id}). typedRoutes verification stays in
the host's route registry — Route values are assignable to string.
Mounting (three seams)
1. Screens — inject the host's routes
// app/dashboard/suppliers/page.tsx (Server Component)
import { SuppliersScreen } from "@classytic/commerce-ui/partners";
const SUPPLIER_ROUTES = { root: "/dashboard/suppliers" }; // serializable → RSC-safe
export default function Page() {
return <SuppliersScreen routes={SUPPLIER_ROUTES} />;
}2. Manifest — call the fragment factory, merge, attach gates
import { createStorageFragment } from "@classytic/commerce-ui/manifest";
import { canWarehouse } from "@classytic/commerce-sdk/dashboard";
const commerceManifest = {
coreSidebarCategories: [
{
...createStorageFragment({
nodes: "/dashboard/warehouse/nodes",
locations: "/dashboard/warehouse/locations",
slotting: "/dashboard/warehouse/slotting",
classification: "/dashboard/warehouse/classification",
uomGroups: "/dashboard/warehouse/uom-groups",
}),
canAccess: canWarehouse,
},
/* ...other context fragments */
],
// ...
};3. Widgets
import { StockLevelBadge } from "@classytic/commerce-ui/widgets";
<StockLevelBadge onHand={40} reserved={12} />; // → "28 avail"Host requirements
- Add
@classytic/commerce-ui(and@classytic/fluid,@classytic/erp-shell) totranspilePackages— it ships pre-built ESM carrying"use client". - Provide the shadcn primitives commerce-ui composes under
@/components/ui/*(badge,button,input,label,skeleton, … — stubbed for type-check insrc/types/host.d.ts; the host provides the real ones). - Wrap the dashboard in a React Query provider and mount
<Toaster/>(sonner).
Local dev (cp-dist)
@classytic/erp-shell and @classytic/commerce-sdk are sibling packages consumed
via the cp-dist loop, not fetched from npm during local iteration: build the
source and copy dist/ + package.json into node_modules/@classytic/<pkg>/.
They are peers here (never bundled).
