{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "movement-timeline",
  "title": "Movement Timeline",
  "description": "The nx-ui Movement Timeline block.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/stepper",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/domain/movement-timeline/index.ts",
      "content": "export {\n  MovementTimeline,\n  type MovementTimelineProps,\n  type MovementTimelineStatus,\n  type MovementTimelineStep,\n} from \"./movement-timeline\"\n",
      "type": "registry:component",
      "target": "components/blocks/domain/movement-timeline/index.ts"
    },
    {
      "path": "components/blocks/domain/movement-timeline/movement-timeline.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  CheckCircle,\n  ClipboardCheck,\n  File02,\n  PackageCheck,\n  Truck01,\n  XCircle,\n} from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Stepper } from \"@/components/ui/stepper\"\n\n/**\n * MovementTimeline — the waste-movement lifecycle as a stepper.\n *\n * Where ChainOfCustody is the audit trail of custody events, this is the\n * status lifecycle: proposed → consented → in-transit → received →\n * reconciled (with cancelled as a terminal branch). Each step carries an\n * optional timestamp and note; the current step is the last one with a\n * timestamp unless `currentIndex` is given.\n *\n * Built on the ui Stepper — when every step is inert it delegates to it\n * directly; when any step has an `onClick`, the same layout is rendered with\n * each step's label as a button.\n */\n\nexport type MovementTimelineStatus =\n  | \"proposed\"\n  | \"consented\"\n  | \"in-transit\"\n  | \"received\"\n  | \"reconciled\"\n  | \"cancelled\"\n\nconst STATUS_META: Record<\n  MovementTimelineStatus,\n  { Icon: React.ComponentType<{ className?: string }>; label: string }\n> = {\n  proposed: { Icon: File02, label: \"Proposed\" },\n  consented: { Icon: ClipboardCheck, label: \"Consented\" },\n  \"in-transit\": { Icon: Truck01, label: \"In transit\" },\n  received: { Icon: PackageCheck, label: \"Received\" },\n  reconciled: { Icon: CheckCircle, label: \"Reconciled\" },\n  cancelled: { Icon: XCircle, label: \"Cancelled\" },\n}\n\nexport interface MovementTimelineStep {\n  status: MovementTimelineStatus\n  /** Override the default status label. */\n  label?: string\n  /** Pre-formatted timestamp, e.g. \"14 Jul 2026, 09:12\". */\n  timestamp?: string\n  /** A supporting line, e.g. \"Consignment NX-2041 raised\". */\n  note?: string\n  /** Makes the step's label a button. */\n  onClick?: () => void\n}\n\nexport interface MovementTimelineProps {\n  steps: MovementTimelineStep[]\n  /** Zero-based index of the current step; derived from timestamps when omitted. */\n  currentIndex?: number\n  orientation?: \"horizontal\" | \"vertical\"\n  className?: string\n}\n\n/** The current step is the last one with a timestamp (cancelled counts), so a\n * partially completed lifecycle lights up correctly without bookkeeping. */\nfunction deriveCurrentIndex(steps: MovementTimelineStep[]): number {\n  for (let i = steps.length - 1; i >= 0; i--) {\n    if (steps[i].timestamp) return i\n  }\n  return 0\n}\n\nexport function MovementTimeline({\n  steps,\n  currentIndex,\n  orientation = \"horizontal\",\n  className,\n}: MovementTimelineProps) {\n  const activeStep = currentIndex ?? deriveCurrentIndex(steps)\n  const interactive = steps.some((step) => step.onClick)\n\n  if (!interactive) {\n    return (\n      <Stepper\n        data-slot=\"movement-timeline\"\n        steps={steps.map((step) => ({\n          title: step.label ?? STATUS_META[step.status].label,\n          description: [step.timestamp, step.note].filter(Boolean).join(\" · \") || undefined,\n          icon: STATUS_META[step.status].Icon,\n        }))}\n        activeStep={activeStep}\n        orientation={orientation}\n        marker=\"icon\"\n        className={className}\n      />\n    )\n  }\n\n  const vertical = orientation === \"vertical\"\n\n  return (\n    <ol\n      data-slot=\"movement-timeline\"\n      data-orientation={orientation}\n      className={cn(\"flex\", vertical ? \"flex-col\" : \"w-full items-start\", className)}\n    >\n      {steps.map((step, index) => {\n        const meta = STATUS_META[step.status]\n        const state =\n          index < activeStep ? \"complete\" : index === activeStep ? \"current\" : \"upcoming\"\n        const isLast = index === steps.length - 1\n        const connectorFilled = index < activeStep\n\n        const text = (\n          <>\n            <p\n              className={cn(\n                \"text-sm font-semibold\",\n                state === \"upcoming\" ? \"text-text-quaternary\" : \"text-secondary-foreground\"\n              )}\n            >\n              {step.label ?? meta.label}\n            </p>\n            {step.timestamp ? (\n              <p className=\"text-sm text-text-tertiary\">{step.timestamp}</p>\n            ) : null}\n            {step.note ? <p className=\"text-sm text-text-tertiary\">{step.note}</p> : null}\n          </>\n        )\n\n        return (\n          <li\n            key={index}\n            aria-current={state === \"current\" ? \"step\" : undefined}\n            className={cn(\n              \"flex\",\n              vertical ? \"gap-4\" : \"flex-1 flex-col items-center last:flex-none\"\n            )}\n          >\n            {vertical ? (\n              <div className=\"flex flex-col items-center self-stretch\">\n                <span\n                  data-slot=\"stepper-marker\"\n                  className={cn(\n                    \"flex size-8 shrink-0 items-center justify-center rounded-full text-sm font-semibold transition\",\n                    state === \"complete\" && \"bg-primary text-primary-foreground\",\n                    state === \"current\" &&\n                      \"bg-bg-brand-primary text-text-brand-primary ring-2 ring-ring/60 ring-offset-2 ring-offset-background\",\n                    state === \"upcoming\" && \"text-text-quaternary ring-1 ring-border ring-inset\"\n                  )}\n                >\n                  <meta.Icon className=\"size-4\" aria-hidden=\"true\" />\n                </span>\n                {!isLast && (\n                  <span\n                    aria-hidden=\"true\"\n                    className={cn(\n                      \"my-1 w-0.5 flex-1 rounded-full\",\n                      connectorFilled ? \"bg-primary\" : \"bg-border\"\n                    )}\n                  />\n                )}\n              </div>\n            ) : (\n              <div className=\"flex w-full items-center\">\n                <span\n                  data-slot=\"stepper-marker\"\n                  className={cn(\n                    \"flex size-8 shrink-0 items-center justify-center rounded-full text-sm font-semibold transition\",\n                    state === \"complete\" && \"bg-primary text-primary-foreground\",\n                    state === \"current\" &&\n                      \"bg-bg-brand-primary text-text-brand-primary ring-2 ring-ring/60 ring-offset-2 ring-offset-background\",\n                    state === \"upcoming\" && \"text-text-quaternary ring-1 ring-border ring-inset\"\n                  )}\n                >\n                  <meta.Icon className=\"size-4\" aria-hidden=\"true\" />\n                </span>\n                {!isLast && (\n                  <span\n                    aria-hidden=\"true\"\n                    className={cn(\n                      \"mx-2 h-0.5 flex-1 rounded-full\",\n                      connectorFilled ? \"bg-primary\" : \"bg-border\"\n                    )}\n                  />\n                )}\n              </div>\n            )}\n\n            {step.onClick ? (\n              <button\n                type=\"button\"\n                onClick={step.onClick}\n                className={cn(\n                  \"cursor-pointer rounded-lg transition focus-visible:ring-2 focus-visible:ring-ring/25 focus-visible:outline-none\",\n                  vertical ? \"pb-8 text-left last:pb-0\" : \"mt-3 text-center\"\n                )}\n              >\n                {text}\n              </button>\n            ) : (\n              <div className={cn(vertical ? \"pb-8 last:pb-0\" : \"mt-3 text-center\")}>\n                {text}\n              </div>\n            )}\n          </li>\n        )\n      })}\n    </ol>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/domain/movement-timeline/movement-timeline.tsx"
    }
  ],
  "type": "registry:block"
}