UI Lab

    Tree & Flow Variants

    30 dynamic, zero-dependency tree canvases — add, edit, remove nodes and copy the source

    Fulfilment Pipeline

    Branching workflow with stage labels — like the warehouse fulfilment screenshot.

    Hover any node to add a child, rename or remove it. Drag the grip handle on draggable variants to reparent or reorder. Read-only variants disable every affordance.

    13/13

    Search matches node labels. Sort re-orders each parent's children. Tags / smart filter mark which nodes match; Dim fades non-matches in place, Hide removes them from the tree.

    100%
    ORDER POOL

    Order Pool

    FULFILLMENT

    Fulfillment

    PICK

    Pick

    TASK RELEASE

    Task Release

    PICK

    Full Pallet Pull

    AI Recommended

    PACK

    Case → LPN Build

    CONSOLIDATION

    Order Consolidation

    AUDIT

    Order Audit

    VAS

    Pallet Wrap

    LABEL

    Print Labels

    STAGE

    Stage by Carrier

    SHIP

    Load LTL Shipment

    PACK

    Sort & Pack

    Source Code

    Install & set up

    Everything this tree needs: 6 generated files to copy and 7 shared engine files the CLI can install for you.

    1

    Choose how data is fetched

    Two independent choices. The default adds no dependency at all — the command below and the code both follow whatever you pick.

    HTTP client used by services.ts

    Where results live used by hooks.ts

    Adds no extra packages.

    2

    Install it with the CLI

    recommended

    Installs every file below and its packages in one command. The steps after this are only for doing it by hand.

    npx afnoui add tree/tree-fulfilment
    Writes to 3 places
    3

    Or install the packages yourself

    Only needed if you copied the files by hand instead of running the command above.

    Runtime dependencies

    npm install react react-dom lucide-react clsx tailwind-merge

    Dev dependencies

    None — nothing extra is needed at build time.

    • Requires AfnoUI components: badge.
    • Bring `cn` from your `@/lib/utils` (clsx + tailwind-merge).
    • Zero graph-library dependency — TreeCanvas computes layouts + drag itself.
    • The GraphToolbar (search / sort / filter) installs only for variants with `showToolbar: true`.

    Generated files

    Generated files are yours to copy and edit; shared engine files are installed once and reused.

    Generated per tree(6)

    Shared engine — copy once(7)

    Generated per tree

    The composed component — drop into any page. Includes the GraphToolbar search/sort/filter bar.

    src/components/tree-instances/FulfilmentPipeline.tsx
    src/components/tree-instances/FulfilmentPipeline.tsx
    import { useState } from "react";
    import { TreeCanvas } from "@/components/tree/TreeCanvas";
    import type { TreeNode } from "@/components/tree/types";
    import { GraphToolbar, useGraphFilter, defaultGraphFilter } from "@/components/graph";
    import { initialTree } from "./data";
    import { treeConfig } from "./config";
    import { useFulfilmentPipelineHandlers } from "./hooks";
    
    /**
     * FulfilmentPipeline — a fully-typed, self-contained tree with a search / sort /
     * filter toolbar above the canvas.
     *
     * Drop into any page:
     *   <FulfilmentPipeline />
     *
     * To plug into a backend, put requests in `./services.ts` and glue them in `./hooks.ts`. To remove the
     * toolbar, delete the `GraphToolbar`/`useGraphFilter` wiring below and set
     * `showToolbar: false` in `./config.ts`.
     */
    export function FulfilmentPipeline() {
      const [tree, setTree] = useState<TreeNode>(initialTree);
      const [filter, setFilter] = useState(defaultGraphFilter);
      const { handleNodeAdd, handleNodeUpdate, handleNodeRemove, handleNodeMove } = useFulfilmentPipelineHandlers();
      const { tree: filteredTree, matches, stats, allTags } = useGraphFilter(tree, filter);
    
      return (
        <div className="flex flex-col gap-2">
          <GraphToolbar
            filter={filter}
            onChange={setFilter}
            allTags={allTags}
            matched={stats.matched}
            total={stats.total}
          />
          <TreeCanvas
            config={treeConfig}
            tree={filter.mode === "hide" ? filteredTree : tree}
            onTreeChange={setTree}
            highlightIds={filter.mode === "dim" ? matches : undefined}
            onNodeAdd={handleNodeAdd}
            onNodeUpdate={handleNodeUpdate}
            onNodeRemove={handleNodeRemove}
            onNodeMove={handleNodeMove}
          />
        </div>
      );
    }