{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "wave-picking-console",
  "title": "Wave Picking Console",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/wave-picking-console/wave-picking-console.component.ts",
      "content": "import { Component, Input, signal, computed, ChangeDetectionStrategy } from '@angular/core'\nimport { Barcode, Check, LucideAngularModule, MapPin } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent, type BadgeVariant } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport interface PickItem {\n  id: string\n  sequence: number\n  location: string\n  sku: string\n  name: string\n  barcode: string\n  requiredQty: number\n  pickedQty: number\n  toteNumber: string\n  orderNumber: string\n  status: 'pending' | 'picking' | 'picked' | 'shortage'\n}\n\nexport interface WavePickingConsoleProps {\n  waveId?: string\n  zoneName?: string\n  pickerName?: string\n  className?: string\n}\n\n/**\n * Angular port of the React WavePickingConsole block: a wave header with pick progress and\n * the active pick target (barcode scan, confirm pick, flag shortage) above the optimized\n * pick route table. Confirming a pick adds one unit to the active item and moves on to the\n * next pending item when it is complete. Edit the sample pick items below.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-wave-picking-console, [ui-wave-picking-console]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"wave-picking-console\"',\n    '[class]': 'hostClass',\n  },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiInputComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <!-- Top Wave Header Card -->\n    <div ui-card>\n      <div ui-card-header class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <div class=\"flex items-center gap-2\">\n            <span class=\"text-foreground font-mono text-sm font-semibold\">{{ waveId }}</span>\n            <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">{{ zoneName }}</span>\n            <span ui-badge [variant]=\"progressPercentage() === 100 ? 'default' : 'secondary'\"\n              >{{ progressPercentage() }}% Picked</span\n            >\n          </div>\n          <h3 ui-card-title class=\"text-xl\">Batch Order Wave Picking Console</h3>\n          <p ui-card-description>\n            Assigned Picker: <span class=\"text-foreground font-medium\">{{ pickerName }}</span> · Route Optimized\n          </p>\n        </div>\n\n        <div class=\"flex items-center gap-3\">\n          <div class=\"text-right\">\n            <div class=\"text-muted-foreground text-xs\">Pick Completion</div>\n            <div class=\"font-mono text-lg font-bold\">{{ totalUnitsPicked() }} / {{ totalUnitsToPick() }} Units</div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Active Pick Target Banner -->\n      @if (activeItem(); as active) {\n        <div ui-card-content class=\"border-border border-t pt-4\">\n          <div class=\"border-border bg-muted/40 rounded-lg border p-4\">\n            <div class=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n              <div class=\"space-y-1\">\n                <div class=\"flex items-center gap-2\">\n                  <span class=\"bg-foreground text-background rounded px-2 py-0.5 font-mono text-xs font-semibold\"\n                    >NEXT TARGET: {{ active.location }}</span\n                  >\n                  <span class=\"text-muted-foreground font-mono text-xs\">TOTE: {{ active.toteNumber }}</span>\n                  <span class=\"text-muted-foreground font-mono text-xs\">({{ active.orderNumber }})</span>\n                </div>\n                <div class=\"text-foreground text-lg font-semibold\">{{ active.name }}</div>\n                <div class=\"text-muted-foreground font-mono text-xs\">\n                  SKU: {{ active.sku }} · Scan Barcode: {{ active.barcode }}\n                </div>\n              </div>\n\n              <!-- Fast Scan & Action Button Group -->\n              <div class=\"flex items-center gap-2\">\n                <div class=\"relative w-48\">\n                  <lucide-icon\n                    [img]=\"Barcode\"\n                    class=\"text-muted-foreground pointer-events-none absolute top-2.5 left-2.5 size-4\"\n                  />\n                  <ui-input\n                    [value]=\"activeBarcode()\"\n                    (valueChange)=\"activeBarcode.set($event)\"\n                    placeholder=\"Scan SKU barcode...\"\n                    aria-label=\"Scan SKU barcode\"\n                    class=\"h-9 pl-8\"\n                    (keydown.enter)=\"scanItem()\"\n                  />\n                </div>\n                <button ui-button size=\"sm\" class=\"gap-1.5\" (click)=\"scanItem()\">\n                  <lucide-icon [img]=\"Check\" class=\"size-4\" />\n                  Confirm Pick\n                </button>\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"text-destructive hover:bg-destructive/10\"\n                  (click)=\"flagShortage(active.id)\"\n                >\n                  Flag Shortage\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n      }\n    </div>\n\n    <!-- Optimized Route Table -->\n    <div ui-card>\n      <div ui-card-header class=\"pb-3\">\n        <h3 ui-card-title class=\"text-base font-semibold\">Optimized Pick Route Sequence</h3>\n        <p ui-card-description>\n          Pick items strictly in sequence to minimize travel distance across fulfillment aisles.\n        </p>\n      </div>\n\n      <div ui-card-content class=\"p-0\">\n        <table ui-table>\n          <thead ui-table-header>\n            <tr ui-table-row>\n              <th ui-table-head class=\"w-[60px]\">Seq</th>\n              <th ui-table-head>Location Bin</th>\n              <th ui-table-head class=\"w-[300px]\">Product / SKU</th>\n              <th ui-table-head>Tote &amp; Order</th>\n              <th ui-table-head class=\"text-right\">Qty</th>\n              <th ui-table-head>Status</th>\n            </tr>\n          </thead>\n          <tbody ui-table-body>\n            @for (item of pickItems(); track item.id) {\n              <tr ui-table-row [class]=\"item.status === 'picking' ? 'bg-muted/50 font-medium' : ''\">\n                <!-- Sequence Number -->\n                <td ui-table-cell class=\"text-muted-foreground font-mono text-xs\">#{{ item.sequence }}</td>\n\n                <!-- Location Bin -->\n                <td ui-table-cell>\n                  <div class=\"text-foreground flex items-center gap-1.5 font-mono text-xs font-semibold\">\n                    <lucide-icon [img]=\"MapPin\" class=\"text-muted-foreground size-3.5\" />\n                    <span>{{ item.location }}</span>\n                  </div>\n                </td>\n\n                <!-- Product -->\n                <td ui-table-cell>\n                  <div class=\"font-medium\">{{ item.name }}</div>\n                  <div class=\"text-muted-foreground font-mono text-xs\">{{ item.sku }}</div>\n                </td>\n\n                <!-- Tote & Order -->\n                <td ui-table-cell>\n                  <div class=\"flex items-center gap-1.5 font-mono text-xs\">\n                    <span ui-badge variant=\"outline\">{{ item.toteNumber }}</span>\n                    <span class=\"text-muted-foreground\">{{ item.orderNumber }}</span>\n                  </div>\n                </td>\n\n                <!-- Qty Progress -->\n                <td ui-table-cell class=\"text-right font-mono text-sm\">\n                  {{ item.pickedQty }} / {{ item.requiredQty }}\n                </td>\n\n                <!-- Status Badge -->\n                <td ui-table-cell>\n                  <span ui-badge [variant]=\"statusVariant(item.status)\" class=\"capitalize\">{{ item.status }}</span>\n                </td>\n              </tr>\n            }\n          </tbody>\n        </table>\n      </div>\n\n      <div\n        ui-card-footer\n        class=\"border-border text-muted-foreground flex items-center justify-between border-t py-3 text-xs\"\n      >\n        <div>Completed totes are automatically routed to Conveyor Pack Station 04.</div>\n        <div class=\"text-foreground font-medium\">\n          Wave Target: <span class=\"font-mono\">{{ pickItems().length }}</span> pick positions\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiWavePickingConsoleComponent {\n  @Input() waveId = 'WAVE-B2C-904'\n  @Input() zoneName = 'Zone 02 · Fast Mover Aisle'\n  @Input() pickerName = 'Alex Mercer (ID: PK-41)'\n  @Input('class') className?: string\n\n  protected readonly Barcode = Barcode\n  protected readonly Check = Check\n  protected readonly MapPin = MapPin\n\n  readonly activeBarcode = signal('')\n\n  readonly pickItems = signal<PickItem[]>([\n    {\n      id: 'pk-1',\n      sequence: 1,\n      location: 'A02-S1-B04',\n      sku: 'SKU-AUDIO-ANC',\n      name: 'Active Noise Canceling Headphones Matte Black',\n      barcode: '079357319901',\n      requiredQty: 2,\n      pickedQty: 2,\n      toteNumber: 'TOTE-01',\n      orderNumber: 'ORD-99120',\n      status: 'picked',\n    },\n    {\n      id: 'pk-2',\n      sequence: 2,\n      location: 'A02-S3-B11',\n      sku: 'SKU-CABLE-BRAID',\n      name: 'Braided Type-C Thunderbolt 4 Cable (2m)',\n      barcode: '079357319902',\n      requiredQty: 4,\n      pickedQty: 1,\n      toteNumber: 'TOTE-02',\n      orderNumber: 'ORD-99124',\n      status: 'picking',\n    },\n    {\n      id: 'pk-3',\n      sequence: 3,\n      location: 'A03-S2-B08',\n      sku: 'SKU-DESK-PAD',\n      name: 'Top-Grain Leather Desk Mat Midnight Gray',\n      barcode: '079357319903',\n      requiredQty: 1,\n      pickedQty: 0,\n      toteNumber: 'TOTE-01',\n      orderNumber: 'ORD-99120',\n      status: 'pending',\n    },\n    {\n      id: 'pk-4',\n      sequence: 4,\n      location: 'A04-S1-B02',\n      sku: 'SKU-HUB-10IN1',\n      name: 'USB-C Aluminum Desktop Docking Station',\n      barcode: '079357319904',\n      requiredQty: 3,\n      pickedQty: 0,\n      toteNumber: 'TOTE-03',\n      orderNumber: 'ORD-99131',\n      status: 'pending',\n    },\n  ])\n\n  readonly totalUnitsToPick = computed(() => this.pickItems().reduce((acc, i) => acc + i.requiredQty, 0))\n  readonly totalUnitsPicked = computed(() => this.pickItems().reduce((acc, i) => acc + i.pickedQty, 0))\n  readonly progressPercentage = computed(() => Math.round((this.totalUnitsPicked() / this.totalUnitsToPick()) * 100))\n\n  readonly activeItem = computed(\n    () => this.pickItems().find((i) => i.status === 'picking') || this.pickItems().find((i) => i.status === 'pending'),\n  )\n\n  get hostClass(): string {\n    return cn('block w-full space-y-6', this.className)\n  }\n\n  statusVariant(status: PickItem['status']): BadgeVariant {\n    return status === 'picked'\n      ? 'default'\n      : status === 'picking'\n        ? 'secondary'\n        : status === 'shortage'\n          ? 'destructive'\n          : 'outline'\n  }\n\n  scanItem(): void {\n    const activeItem = this.activeItem()\n    if (!activeItem) return\n    this.pickItems.update((prev) => {\n      let movedToNext = false\n      return prev\n        .map((item) => {\n          if (item.id === activeItem.id && item.pickedQty < item.requiredQty) {\n            const nextQty = item.pickedQty + 1\n            const isDone = nextQty === item.requiredQty\n            if (isDone) movedToNext = true\n            return { ...item, pickedQty: nextQty, status: (isDone ? 'picked' : 'picking') as PickItem['status'] }\n          }\n          return item\n        })\n        .map((item, _idx, arr) => {\n          if (movedToNext && item.status === 'pending') {\n            // make the first pending active\n            const firstPending = arr.find((x) => x.status === 'pending')\n            if (item.id === firstPending?.id) return { ...item, status: 'picking' as const }\n          }\n          return item\n        })\n    })\n    this.activeBarcode.set('')\n  }\n\n  flagShortage(id: string): void {\n    this.pickItems.update((prev) => {\n      let foundNext = false\n      return prev.map((item) => {\n        if (item.id === id) {\n          foundNext = true\n          return { ...item, status: 'shortage' as const }\n        }\n        if (foundNext && item.status === 'pending') {\n          foundNext = false\n          return { ...item, status: 'picking' as const }\n        }\n        return item\n      })\n    })\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/wave-picking-console/wave-picking-console.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/wave-picking-console/index.ts",
      "content": "export {\n  UiWavePickingConsoleComponent,\n  type PickItem,\n  type WavePickingConsoleProps,\n} from './wave-picking-console.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/wave-picking-console/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Batch wave picking console with multi-order tote assignment, optimized pick route, and barcode verification.",
  "categories": [
    "dashboard",
    "data-display"
  ]
}