{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "wholesale-b2b-quick-order",
  "title": "Wholesale B2b Quick Order",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/wholesale-b2b-quick-order/wholesale-b2b-quick-order.component.ts",
      "content": "import {\n  Component,\n  DestroyRef,\n  EventEmitter,\n  Input,\n  type OnInit,\n  Output,\n  computed,\n  inject,\n  signal,\n  ChangeDetectionStrategy,\n} from '@angular/core'\nimport {\n  AlertCircle,\n  Bookmark,\n  Building2,\n  CheckCircle2,\n  Download,\n  FileSpreadsheet,\n  Layers,\n  LucideAngularModule,\n  Minus,\n  Package,\n  Plus,\n  RefreshCw,\n  Send,\n  Trash2,\n  TrendingDown,\n  Truck,\n  Upload,\n  X,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\nimport { UiTextareaComponent } from '@/ui/textarea/textarea.component'\n\nexport interface WholesaleProduct {\n  id: string\n  sku: string\n  name: string\n  variant: string\n  casePack: number\n  msrpUnitPrice: number\n  wholesaleUnitPrice: number\n  cases: number\n  availableCases: number\n  image: string\n  category: string\n}\n\nexport interface WholesaleOrderPayload {\n  poNumber: string\n  items: WholesaleProduct[]\n  subtotal: number\n  totalUnits: number\n  totalCases: number\n  savings: number\n}\n\nexport interface WholesalePasteFeedback {\n  type: 'success' | 'error'\n  message: string\n}\n\nexport const DEFAULT_WHOLESALE_PRODUCTS: WholesaleProduct[] = [\n  {\n    id: 'prod-1',\n    sku: 'SKU-84920',\n    name: 'Pro Studio Headphones',\n    variant: 'Matte Black · Pro Series',\n    casePack: 12,\n    msrpUnitPrice: 299.0,\n    wholesaleUnitPrice: 194.35,\n    cases: 4,\n    availableCases: 450,\n    image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=300&auto=format&fit=crop&q=80',\n    category: 'Audio & Acoustics',\n  },\n  {\n    id: 'prod-2',\n    sku: 'SKU-49102',\n    name: 'Aero Minimalist Runner',\n    variant: 'Arctic White · US 10-12 Assorted',\n    casePack: 10,\n    msrpUnitPrice: 140.0,\n    wholesaleUnitPrice: 91.0,\n    cases: 3,\n    availableCases: 180,\n    image: 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=300&auto=format&fit=crop&q=80',\n    category: 'Footwear',\n  },\n  {\n    id: 'prod-3',\n    sku: 'SKU-77215',\n    name: 'Technical Shell Parka',\n    variant: 'Mineral Gray · Waterproof 3L',\n    casePack: 8,\n    msrpUnitPrice: 220.0,\n    wholesaleUnitPrice: 143.0,\n    cases: 2,\n    availableCases: 95,\n    image: 'https://images.unsplash.com/photo-1591047139829-d91aecb6caea?w=300&auto=format&fit=crop&q=80',\n    category: 'Outerwear',\n  },\n  {\n    id: 'prod-4',\n    sku: 'SKU-10934',\n    name: 'Braided USB-C Cable',\n    variant: 'Space Gray · 2m 240W EPR',\n    casePack: 24,\n    msrpUnitPrice: 25.0,\n    wholesaleUnitPrice: 16.25,\n    cases: 2,\n    availableCases: 620,\n    image: 'https://images.unsplash.com/photo-1583863788434-e58a36330cf0?w=300&auto=format&fit=crop&q=80',\n    category: 'Hardware & Cables',\n  },\n  {\n    id: 'prod-5',\n    sku: 'SKU-63821',\n    name: 'Leather Cardholder',\n    variant: 'Saddle Brown · Full-Grain Veg-Tan',\n    casePack: 20,\n    msrpUnitPrice: 50.0,\n    wholesaleUnitPrice: 32.5,\n    cases: 1,\n    availableCases: 24,\n    image: 'https://images.unsplash.com/photo-1627123424574-724758594e93?w=300&auto=format&fit=crop&q=80',\n    category: 'Leather Goods',\n  },\n]\n\nconst currencyFormatter = new Intl.NumberFormat('en-US', {\n  style: 'currency',\n  currency: 'USD',\n  minimumFractionDigits: 2,\n  maximumFractionDigits: 2,\n})\nconst numberFormatter = new Intl.NumberFormat('en-US')\n\nconst clone = (items: WholesaleProduct[]): WholesaleProduct[] => items.map((p) => ({ ...p }))\n\nconst SAMPLE_CSV = `SKU-84920, 8\\nSKU-49102, 5\\nSKU-77215, 4\\nSKU-10934, 12\\nSKU-63821, 6`\n\n/**\n * Angular port of the React WholesaleB2bQuickOrder block: a B2B bulk order matrix with\n * tier pricing, case pack multipliers, a CSV SKU paste box and a sticky order summary.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-wholesale-b2b-quick-order, [ui-wholesale-b2b-quick-order]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"wholesale-b2b-quick-order\"',\n    '[class]': 'hostClass',\n  },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n    UiTextareaComponent,\n  ],\n  template: `\n    <!-- Header & Account Info -->\n    <div ui-card class=\"border-border bg-card shadow-xs\">\n      <div ui-card-header class=\"pb-4\">\n        <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n          <div class=\"space-y-1.5\">\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs font-medium\">\n                <lucide-icon [img]=\"Building2\" class=\"size-3.5\" />\n                <span>{{ accountName }}</span>\n                <span>·</span>\n                <span>{{ accountTier }}</span>\n              </div>\n              <span ui-badge variant=\"outline\" class=\"border-success/30 bg-success/10 text-success font-medium\">\n                <!-- [class]: a static class on lucide-icon lands on the host and the svg, doubling mr-1. -->\n                <lucide-icon [img]=\"Layers\" [class]=\"'mr-1 size-3'\" />{{ tierDiscountPercent }}% Off MSRP Wholesale Tier\n              </span>\n            </div>\n            <h3 ui-card-title class=\"text-xl font-bold tracking-tight sm:text-2xl\">\n              Wholesale &amp; B2B Bulk Order Matrix\n            </h3>\n            <p ui-card-description class=\"text-muted-foreground text-xs sm:text-sm\">\n              Build your purchase order by entering case quantities below or pasting bulk SKU manifests with automated\n              case multipliers.\n            </p>\n          </div>\n\n          <!-- Header Action Buttons -->\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <button\n              ui-button\n              aria-label=\"Close CSV paste\"\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"gap-1.5 text-xs font-medium\"\n              (click)=\"showCsvBox.set(!showCsvBox())\"\n            >\n              <lucide-icon [img]=\"FileSpreadsheet\" class=\"size-3.5\" />\n              <span>{{ showCsvBox() ? 'Hide CSV Paste' : 'Upload CSV Order' }}</span>\n            </button>\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"gap-1.5 text-xs font-medium\"\n              (click)=\"handleSaveTemplate()\"\n            >\n              <lucide-icon [img]=\"Bookmark\" class=\"size-3.5\" />\n              <span>Save Order Template</span>\n            </button>\n          </div>\n        </div>\n      </div>\n\n      <div ui-card-content class=\"pt-0\">\n        <!-- Account Terms & Partner Meta Strip -->\n        <div class=\"bg-muted/40 border-border grid grid-cols-2 gap-3 rounded-lg border p-3 sm:grid-cols-4 sm:gap-4\">\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">PO Reference</div>\n            <div class=\"font-mono text-sm font-semibold\">{{ poRef() }}</div>\n          </div>\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">Payment Terms</div>\n            <div class=\"text-foreground flex items-center gap-1 text-sm font-semibold\">\n              <lucide-icon [img]=\"CheckCircle2\" class=\"text-success size-3.5\" />\n              <span>{{ paymentTerms }}</span>\n            </div>\n          </div>\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">Freight Status</div>\n            <div [class]=\"cn('text-sm font-semibold tabular-nums', isFreeFreight ? 'text-success' : 'text-foreground')\">\n              {{ isFreeFreight ? 'Free Ground Freight' : '$250 Standard Freight' }}\n            </div>\n          </div>\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">Volume Tier Status</div>\n            <div class=\"text-foreground text-sm font-semibold\">\n              Tier 3 <span class=\"text-muted-foreground font-normal\">({{ tierDiscountPercent }}% Margin)</span>\n            </div>\n          </div>\n        </div>\n\n        <!-- Volume Tier Margin Incentive Banner -->\n        <div class=\"border-border bg-card/60 mt-3 rounded-lg border p-3\">\n          <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n            <div class=\"flex items-center gap-2\">\n              <lucide-icon [img]=\"TrendingDown\" class=\"text-success size-4 shrink-0\" />\n              <div class=\"text-xs\">\n                <span class=\"text-foreground font-semibold\">Tier 4 Milestone ($50,000):</span>\n                <span class=\"text-muted-foreground ml-1\">{{\n                  nextTierRemaining() > 0\n                    ? 'Add ' + formatCurrency(nextTierRemaining()) + ' more to unlock 42% Tier 4 Enterprise Margin.'\n                    : 'Tier 4 Enterprise Margin (42%) Unlocked!'\n                }}</span>\n              </div>\n            </div>\n            <div class=\"text-muted-foreground flex items-center gap-2 text-xs\">\n              <span>{{ tierProgressPercent() }}% to Tier 4</span>\n              <div class=\"bg-muted border-border h-2 w-24 overflow-hidden rounded-full border\">\n                <div\n                  class=\"bg-success h-full transition-[width] duration-300\"\n                  [style.width.%]=\"tierProgressPercent()\"\n                ></div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Template Saved Alert -->\n    @if (templateSaved()) {\n      <div\n        class=\"border-success/30 bg-success/10 text-success text-success flex items-center justify-between rounded-lg border p-3 text-xs sm:text-sm\"\n      >\n        <div class=\"flex items-center gap-2\">\n          <lucide-icon [img]=\"Bookmark\" class=\"text-success size-4 shrink-0\" />\n          <span>Order matrix saved as your primary template. You can re-populate this draft at any time.</span>\n        </div>\n        <button\n          ui-button\n          variant=\"ghost\"\n          size=\"xs\"\n          class=\"text-success hover:text-success\"\n          aria-label=\"Dismiss notification\"\n          (click)=\"templateSaved.set(false)\"\n        >\n          <lucide-icon [img]=\"X\" class=\"size-3.5\" />\n        </button>\n      </div>\n    }\n\n    <!-- Order Submitted Success Alert -->\n    @if (orderSubmitted()) {\n      <div class=\"border-success/30 bg-success/10 text-success dark:text-foreground rounded-xl border p-4 sm:p-5\">\n        <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n          <div class=\"flex items-start gap-3\">\n            <lucide-icon [img]=\"CheckCircle2\" [class]=\"'text-success mt-0.5 size-5 shrink-0'\" />\n            <div class=\"space-y-1\">\n              <h3 class=\"text-sm font-semibold sm:text-base\">Purchase Order {{ poRef() }} Submitted Successfully!</h3>\n              <p class=\"text-muted-foreground text-xs sm:text-sm\">\n                Your bulk order of\n                <strong class=\"text-foreground\">{{ totalCases() }} cases ({{ totalUnits() }} units)</strong>\n                totaling <strong class=\"text-foreground\">{{ formatCurrency(grandTotal) }}</strong> has been queued for\n                warehouse dispatch under {{ paymentTerms }}.\n              </p>\n            </div>\n          </div>\n          <div class=\"flex items-center gap-2\">\n            <button\n              ui-button\n              aria-label=\"Download attachment\"\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"text-xs\"\n              (click)=\"handleExportCsv()\"\n            >\n              <lucide-icon [img]=\"Download\" [class]=\"'mr-1.5 size-3.5'\" />\n              Download PO PDF\n            </button>\n            <button ui-button size=\"sm\" class=\"text-xs\" (click)=\"orderSubmitted.set(false)\">Start New Order</button>\n          </div>\n        </div>\n      </div>\n    }\n\n    <!-- Quick SKU & Quantity CSV Paste Box -->\n    @if (showCsvBox()) {\n      <div ui-card class=\"border-border bg-card shadow-xs transition-colors duration-200\">\n        <div ui-card-header class=\"pb-3\">\n          <div class=\"flex items-center justify-between\">\n            <div class=\"flex items-center gap-2\">\n              <lucide-icon [img]=\"FileSpreadsheet\" class=\"text-primary size-4\" />\n              <h3 ui-card-title class=\"text-sm font-semibold sm:text-base\">Quick Paste SKU &amp; Quantity Manifest</h3>\n            </div>\n            <button\n              ui-button\n              aria-label=\"Close CSV paste\"\n              variant=\"ghost\"\n              size=\"xs\"\n              class=\"text-muted-foreground hover:text-foreground\"\n              (click)=\"showCsvBox.set(false)\"\n            >\n              <lucide-icon [img]=\"X\" class=\"size-4\" />\n            </button>\n          </div>\n          <p ui-card-description class=\"text-muted-foreground text-xs\">\n            Paste CSV rows with SKU identifier and case quantity (one item per line, e.g.\n            <code class=\"bg-muted rounded px-1 font-mono\">SKU-84920, 10</code>) to auto-populate matrix.\n          </p>\n        </div>\n\n        <div ui-card-content class=\"space-y-3\">\n          <ui-textarea\n            [value]=\"csvPasteText()\"\n            (valueChange)=\"csvPasteText.set($event)\"\n            [placeholder]=\"csvPlaceholder\"\n            [rows]=\"4\"\n            class=\"font-mono text-xs\"\n          />\n\n          <div class=\"flex flex-wrap items-center justify-between gap-2\">\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <button ui-button size=\"sm\" class=\"text-xs font-medium\" (click)=\"applyCsvPaste()\">\n                <lucide-icon [img]=\"Upload\" [class]=\"'mr-1.5 size-3.5'\" />\n                Apply SKU List to Matrix\n              </button>\n              <button ui-button variant=\"outline\" size=\"sm\" class=\"text-xs font-medium\" (click)=\"loadSampleCsv()\">\n                Load Sample Data\n              </button>\n              @if (csvPasteText()) {\n                <button\n                  ui-button\n                  variant=\"ghost\"\n                  size=\"sm\"\n                  class=\"text-muted-foreground text-xs\"\n                  (click)=\"csvPasteText.set('')\"\n                >\n                  Clear Input\n                </button>\n              }\n            </div>\n            <div class=\"text-muted-foreground text-xs\">Supports comma, tab, and semicolon delimited lines</div>\n          </div>\n\n          <!-- Parse Feedback Alert -->\n          @if (pasteFeedback(); as feedback) {\n            <div\n              [class]=\"\n                cn(\n                  'flex items-center gap-2 rounded-lg border p-2.5 text-xs',\n                  feedback.type === 'success'\n                    ? 'border-success/30 bg-success/10 text-success'\n                    : 'border-destructive/30 bg-destructive/10 text-destructive'\n                )\n              \"\n            >\n              @if (feedback.type === 'success') {\n                <lucide-icon [img]=\"CheckCircle2\" class=\"text-success size-4 shrink-0\" />\n              } @else {\n                <lucide-icon [img]=\"AlertCircle\" class=\"size-4 shrink-0\" />\n              }\n              <span>{{ feedback.message }}</span>\n            </div>\n          }\n        </div>\n      </div>\n    }\n\n    <!-- Bulk SKU Matrix Table Card -->\n    <div ui-card class=\"border-border bg-card shadow-xs\">\n      <div ui-card-header class=\"border-border border-b pb-3\">\n        <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <h3 ui-card-title class=\"text-base font-semibold\">Bulk SKU Order Matrix</h3>\n            <p ui-card-description class=\"text-muted-foreground text-xs\">\n              {{ products().length }} wholesale catalog items available with active case multipliers.\n            </p>\n          </div>\n          <div class=\"flex items-center gap-2\">\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"xs\"\n              class=\"text-muted-foreground text-xs\"\n              (click)=\"restoreDefaultCases()\"\n            >\n              <lucide-icon [img]=\"RefreshCw\" [class]=\"'mr-1 size-3'\" />\n              Reset Defaults\n            </button>\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"xs\"\n              class=\"text-muted-foreground text-xs\"\n              (click)=\"resetAllCases()\"\n            >\n              <lucide-icon [img]=\"Trash2\" [class]=\"'mr-1 size-3'\" />\n              Zero All\n            </button>\n          </div>\n        </div>\n      </div>\n\n      <div ui-card-content class=\"p-0\">\n        <div class=\"overflow-x-auto\">\n          <div ui-table class=\"w-full max-w-[760px] min-w-full\">\n            <thead ui-table-header>\n              <tr ui-table-row class=\"bg-muted/30\">\n                <th ui-table-head class=\"w-[300px] text-xs font-semibold\">Product &amp; SKU Identifier</th>\n                <th ui-table-head class=\"text-xs font-semibold\">Case Multiplier</th>\n                <th ui-table-head class=\"text-xs font-semibold\">Unit Price</th>\n                <th ui-table-head class=\"text-xs font-semibold\">Case Quantity</th>\n                <th ui-table-head class=\"text-xs font-semibold\">Availability</th>\n                <th ui-table-head class=\"text-right text-xs font-semibold\">Line Total</th>\n                <th ui-table-head class=\"w-[50px]\">\n                  <span class=\"sr-only\">Actions</span>\n                </th>\n              </tr>\n            </thead>\n            <tbody ui-table-body>\n              @for (product of products(); track product.id) {\n                <tr\n                  ui-table-row\n                  [class]=\"cn('transition-colors', product.cases > 0 && 'bg-primary/5 dark:bg-primary/5')\"\n                >\n                  <!-- Product Thumbnail & Details -->\n                  <td ui-table-cell class=\"py-3.5\">\n                    <div class=\"flex items-center gap-3\">\n                      <img\n                        [src]=\"product.image\"\n                        [alt]=\"product.name\"\n                        class=\"border-border bg-muted/40 size-12 shrink-0 rounded-lg border object-cover\"\n                        loading=\"lazy\"\n                      />\n                      <div class=\"min-w-0 space-y-0.5\">\n                        <div class=\"text-foreground truncate text-sm font-medium\">{{ product.name }}</div>\n                        <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                          <span class=\"font-mono\">{{ product.sku }}</span>\n                          <span>·</span>\n                          <span class=\"truncate\">{{ product.variant }}</span>\n                        </div>\n                      </div>\n                    </div>\n                  </td>\n\n                  <!-- Case Pack Multiplier -->\n                  <td ui-table-cell class=\"py-3.5\">\n                    <div class=\"flex items-center gap-1.5\">\n                      <lucide-icon [img]=\"Package\" class=\"text-muted-foreground size-3.5\" />\n                      <span class=\"text-foreground font-mono text-xs font-medium tabular-nums\"\n                        >{{ product.casePack }} units / case</span\n                      >\n                    </div>\n                  </td>\n\n                  <!-- Pricing (MSRP vs Wholesale) -->\n                  <td ui-table-cell class=\"py-3.5\">\n                    <div class=\"space-y-0.5\">\n                      <div class=\"text-foreground text-sm font-semibold tabular-nums\">\n                        {{ formatCurrency(product.wholesaleUnitPrice)\n                        }}<span class=\"text-muted-foreground text-xs font-normal\"> wholesale</span>\n                      </div>\n                      <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                        <span class=\"tabular-nums line-through\">{{ formatCurrency(product.msrpUnitPrice) }} MSRP</span>\n                        <span class=\"text-success font-medium\">-{{ discountPercent(product) }}%</span>\n                      </div>\n                    </div>\n                  </td>\n\n                  <!-- Quantity Stepper Input -->\n                  <td ui-table-cell class=\"py-3.5\">\n                    <div class=\"space-y-1\">\n                      <div class=\"flex items-center gap-1\">\n                        <button\n                          ui-button\n                          aria-label=\"Decrease cases\"\n                          variant=\"outline\"\n                          size=\"xs\"\n                          class=\"size-7 p-0\"\n                          [disabled]=\"product.cases <= 0\"\n                          (click)=\"updateCases(product.id, -1)\"\n                        >\n                          <lucide-icon [img]=\"Minus\" class=\"size-3\" />\n                        </button>\n                        <ui-input\n                          [value]=\"product.cases\"\n                          type=\"number\"\n                          [min]=\"0\"\n                          [max]=\"product.availableCases\"\n                          class=\"h-7 w-16 text-center font-mono text-xs tabular-nums\"\n                          (valueChange)=\"setCases(product.id, $event)\"\n                        />\n                        <button\n                          ui-button\n                          aria-label=\"Increase cases\"\n                          variant=\"outline\"\n                          size=\"xs\"\n                          class=\"size-7 p-0\"\n                          [disabled]=\"product.cases >= product.availableCases\"\n                          (click)=\"updateCases(product.id, 1)\"\n                        >\n                          <lucide-icon [img]=\"Plus\" class=\"size-3\" />\n                        </button>\n                      </div>\n                      <div class=\"text-muted-foreground font-mono text-xs tabular-nums\">\n                        {{ product.cases }} {{ product.cases === 1 ? 'case' : 'cases' }} =\n                        <strong class=\"text-foreground\">{{ product.cases * product.casePack }}</strong> units\n                      </div>\n                    </div>\n                  </td>\n\n                  <!-- Stock Status -->\n                  <td ui-table-cell class=\"py-3.5\">\n                    @if (product.availableCases > 50) {\n                      <span\n                        ui-badge\n                        variant=\"outline\"\n                        class=\"border-success/30 bg-success/10 text-success text-xs font-medium\"\n                      >\n                        In Stock · {{ formatNumber(product.availableCases) }} cases\n                      </span>\n                    } @else {\n                      <span\n                        ui-badge\n                        variant=\"outline\"\n                        class=\"border-warning/30 bg-warning/10 text-warning text-xs font-medium\"\n                      >\n                        Low Stock · {{ product.availableCases }} cases\n                      </span>\n                    }\n                  </td>\n\n                  <!-- Line Total -->\n                  <td ui-table-cell class=\"py-3.5 text-right\">\n                    <div class=\"space-y-0.5\">\n                      <div class=\"text-foreground font-mono text-sm font-bold tabular-nums\">\n                        {{ formatCurrency(product.cases * product.casePack * product.wholesaleUnitPrice) }}\n                      </div>\n                      @if (product.cases > 0) {\n                        <div class=\"text-success text-success font-mono text-xs tabular-nums\">\n                          Save\n                          {{\n                            formatCurrency(\n                              product.cases * product.casePack * (product.msrpUnitPrice - product.wholesaleUnitPrice)\n                            )\n                          }}\n                        </div>\n                      } @else {\n                        <div class=\"text-muted-foreground text-xs\">0 units</div>\n                      }\n                    </div>\n                  </td>\n\n                  <!-- Row Clear Action -->\n                  <td ui-table-cell class=\"py-3.5 text-center\">\n                    @if (product.cases > 0) {\n                      <button\n                        ui-button\n                        variant=\"ghost\"\n                        size=\"xs\"\n                        class=\"text-muted-foreground hover:text-destructive size-7 p-0\"\n                        title=\"Clear row quantity\"\n                        (click)=\"clearLine(product.id)\"\n                      >\n                        <lucide-icon [img]=\"Trash2\" class=\"size-3.5\" />\n                      </button>\n                    }\n                  </td>\n                </tr>\n              }\n            </tbody>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Sticky Order Summary Footer -->\n    <div\n      class=\"bg-card/95 border-border sticky bottom-4 z-20 rounded-xl border p-4 shadow-lg backdrop-blur-md transition-colors sm:p-5\"\n    >\n      <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n        <!-- Summary Totals Metrics -->\n        <div class=\"grid grid-cols-2 gap-3 sm:grid-cols-4 sm:gap-6\">\n          <!-- Total Units -->\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">Total Units</div>\n            <div class=\"font-mono text-base font-bold tabular-nums sm:text-lg\">\n              {{ formatNumber(totalUnits()) }} Units\n            </div>\n            <div class=\"text-muted-foreground font-mono text-xs tabular-nums\">{{ totalCases() }} Cases ordered</div>\n          </div>\n\n          <!-- Wholesale Subtotal -->\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">Wholesale Subtotal</div>\n            <div class=\"font-mono text-base font-bold tabular-nums sm:text-lg\">\n              {{ formatCurrency(wholesaleSubtotal()) }}\n            </div>\n            <div class=\"text-muted-foreground text-xs\">MSRP {{ formatCurrency(msrpTotal()) }}</div>\n          </div>\n\n          <!-- Tier Savings -->\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">Tier Volume Savings</div>\n            <div class=\"text-success text-success font-mono text-base font-bold tabular-nums sm:text-lg\">\n              -{{ formatCurrency(totalSavings()) }}\n            </div>\n            <div class=\"text-success text-xs font-medium\">{{ savingsPercent }}% Margin Savings</div>\n          </div>\n\n          <!-- Freight & Terms -->\n          <div class=\"space-y-0.5\">\n            <div class=\"text-muted-foreground text-xs font-medium\">Freight &amp; Terms</div>\n            <div class=\"text-foreground flex items-center gap-1.5 text-xs font-semibold\">\n              <lucide-icon [img]=\"Truck\" class=\"text-primary size-3.5 shrink-0\" />\n              <span>{{ isFreeFreight ? 'Free Freight (>$10k)' : '$250 Ground' }}</span>\n            </div>\n            <div class=\"text-muted-foreground text-xs\">{{ paymentTerms }}</div>\n          </div>\n        </div>\n\n        <!-- Submit & Secondary Actions -->\n        <div class=\"flex flex-wrap items-center gap-2.5 pt-2 lg:pt-0\">\n          <button\n            ui-button\n            aria-label=\"Download attachment\"\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"text-xs font-medium\"\n            [disabled]=\"totalCases() === 0\"\n            (click)=\"handleExportCsv()\"\n          >\n            <lucide-icon [img]=\"Download\" [class]=\"'mr-1.5 size-3.5'\" />\n            Export CSV\n          </button>\n\n          <button\n            ui-button\n            size=\"default\"\n            class=\"gap-2 text-xs font-semibold sm:text-sm\"\n            [disabled]=\"totalCases() === 0 || isSubmitting()\"\n            (click)=\"handleSubmitOrder()\"\n          >\n            @if (!isSubmitting()) {\n              <lucide-icon [img]=\"Send\" class=\"size-4\" />\n            } @else {\n              <lucide-icon [img]=\"RefreshCw\" class=\"size-4 animate-spin\" />\n            }\n            <span>{{ isSubmitting() ? 'Processing PO...' : 'Submit Wholesale Purchase Order' }}</span>\n          </button>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiWholesaleB2bQuickOrderComponent implements OnInit {\n  @Input() poNumber = 'PO-2026-8841B'\n  @Input() accountName = 'Northwind Retailers'\n  @Input() accountTier = 'Tier 3 Wholesale Partner'\n  @Input() tierDiscountPercent = 35\n  @Input() paymentTerms = 'Net 30 Invoicing'\n  @Input() freeFreightThreshold = 10000\n  @Input() initialProducts?: WholesaleProduct[]\n  @Input('class') className?: string\n\n  @Output() submitOrder = new EventEmitter<WholesaleOrderPayload>()\n  @Output() saveTemplate = new EventEmitter<WholesaleProduct[]>()\n  @Output() exportCsv = new EventEmitter<WholesaleProduct[]>()\n\n  protected readonly AlertCircle = AlertCircle\n  protected readonly Bookmark = Bookmark\n  protected readonly Building2 = Building2\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Download = Download\n  protected readonly FileSpreadsheet = FileSpreadsheet\n  protected readonly Layers = Layers\n  protected readonly Minus = Minus\n  protected readonly Package = Package\n  protected readonly Plus = Plus\n  protected readonly RefreshCw = RefreshCw\n  protected readonly Send = Send\n  protected readonly Trash2 = Trash2\n  protected readonly TrendingDown = TrendingDown\n  protected readonly Truck = Truck\n  protected readonly Upload = Upload\n  protected readonly X = X\n  protected readonly cn = cn\n\n  readonly csvPlaceholder = SAMPLE_CSV\n\n  readonly products = signal<WholesaleProduct[]>(clone(DEFAULT_WHOLESALE_PRODUCTS))\n  readonly poRef = signal('PO-2026-8841B')\n  readonly showCsvBox = signal(false)\n  readonly csvPasteText = signal('')\n  readonly pasteFeedback = signal<WholesalePasteFeedback | null>(null)\n  readonly templateSaved = signal(false)\n  readonly orderSubmitted = signal(false)\n  readonly isSubmitting = signal(false)\n\n  private readonly timers = new Set<ReturnType<typeof setTimeout>>()\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => {\n      for (const t of this.timers) clearTimeout(t)\n      this.timers.clear()\n    })\n  }\n\n  /** React seeds its state once from the props; inputs are set by now. */\n  ngOnInit(): void {\n    this.products.set(clone(this.initialProducts ?? DEFAULT_WHOLESALE_PRODUCTS))\n    this.poRef.set(this.poNumber)\n  }\n\n  get hostClass(): string {\n    return cn('block bg-background text-foreground w-full space-y-6', this.className)\n  }\n\n  // Computations\n  readonly totalCases = computed(() => this.products().reduce((acc, p) => acc + (Number(p.cases) || 0), 0))\n  readonly totalUnits = computed(() => this.products().reduce((acc, p) => acc + (Number(p.cases) || 0) * p.casePack, 0))\n  readonly wholesaleSubtotal = computed(() =>\n    this.products().reduce((acc, p) => acc + (Number(p.cases) || 0) * p.casePack * p.wholesaleUnitPrice, 0),\n  )\n  readonly msrpTotal = computed(() =>\n    this.products().reduce((acc, p) => acc + (Number(p.cases) || 0) * p.casePack * p.msrpUnitPrice, 0),\n  )\n  readonly totalSavings = computed(() => Math.max(0, this.msrpTotal() - this.wholesaleSubtotal()))\n\n  get savingsPercent(): number {\n    if (this.msrpTotal() <= 0) return this.tierDiscountPercent\n    return Math.round((this.totalSavings() / this.msrpTotal()) * 100)\n  }\n\n  get isFreeFreight(): boolean {\n    return this.wholesaleSubtotal() >= this.freeFreightThreshold\n  }\n\n  get grandTotal(): number {\n    return this.wholesaleSubtotal() + (this.isFreeFreight ? 0 : 250)\n  }\n\n  private readonly nextTierTarget = 50000\n  readonly nextTierRemaining = computed(() => Math.max(0, this.nextTierTarget - this.wholesaleSubtotal()))\n  readonly tierProgressPercent = computed(() =>\n    Math.min(100, Math.round((this.wholesaleSubtotal() / this.nextTierTarget) * 100)),\n  )\n\n  formatCurrency(val: number): string {\n    return currencyFormatter.format(val)\n  }\n\n  formatNumber(val: number): string {\n    return numberFormatter.format(val)\n  }\n\n  discountPercent(p: WholesaleProduct): number {\n    return Math.round(((p.msrpUnitPrice - p.wholesaleUnitPrice) / p.msrpUnitPrice) * 100)\n  }\n\n  // Actions\n  updateCases(id: string, delta: number): void {\n    this.products.update((prev) =>\n      prev.map((p) => {\n        if (p.id !== id) return p\n        const current = Number(p.cases) || 0\n        return { ...p, cases: Math.max(0, Math.min(p.availableCases, current + delta)) }\n      }),\n    )\n  }\n\n  setCases(id: string, val: string | number): void {\n    this.products.update((prev) =>\n      prev.map((p) => {\n        if (p.id !== id) return p\n        const parsed = parseInt(String(val), 10)\n        return { ...p, cases: isNaN(parsed) || parsed < 0 ? 0 : Math.min(p.availableCases, parsed) }\n      }),\n    )\n  }\n\n  clearLine(id: string): void {\n    this.products.update((prev) => prev.map((p) => (p.id === id ? { ...p, cases: 0 } : p)))\n  }\n\n  resetAllCases(): void {\n    this.products.update((prev) => prev.map((p) => ({ ...p, cases: 0 })))\n    this.pasteFeedback.set(null)\n  }\n\n  restoreDefaultCases(): void {\n    this.products.set(clone(DEFAULT_WHOLESALE_PRODUCTS))\n    this.pasteFeedback.set(null)\n  }\n\n  applyCsvPaste(): void {\n    this.pasteFeedback.set(null)\n    const text = this.csvPasteText()\n    if (!text.trim()) {\n      this.pasteFeedback.set({ type: 'error', message: 'Please paste SKU and case quantity rows to parse.' })\n      return\n    }\n\n    let updatedCount = 0\n    const unknownSkus: string[] = []\n    const cloned = clone(this.products())\n    for (const rawLine of text.split(/\\r?\\n/)) {\n      const line = rawLine.trim()\n      if (!line || line.startsWith('#') || line.toLowerCase().startsWith('sku')) continue\n\n      const parts = line.split(/[,;\\t]+/).map((s) => s.trim())\n      if (parts.length < 2) continue\n      const skuQuery = parts[0]!.toUpperCase()\n      const qty = parseInt(parts[1]!, 10)\n      if (isNaN(qty)) continue\n\n      const product = cloned.find((p) => p.sku.toUpperCase() === skuQuery || p.sku.toUpperCase().includes(skuQuery))\n      if (product) {\n        product.cases = Math.max(0, Math.min(product.availableCases, qty))\n        updatedCount++\n      } else {\n        unknownSkus.push(parts[0]!)\n      }\n    }\n    this.products.set(cloned)\n\n    if (updatedCount > 0) {\n      this.pasteFeedback.set({\n        type: 'success',\n        message: `Successfully updated ${updatedCount} SKU line items in bulk matrix.${\n          unknownSkus.length > 0\n            ? ` Note: ${unknownSkus.length} SKU(s) not found in catalog (${unknownSkus.slice(0, 3).join(', ')}).`\n            : ''\n        }`,\n      })\n    } else {\n      this.pasteFeedback.set({\n        type: 'error',\n        message: 'No matching catalog SKUs found. Verify format: SKU-84920, 10',\n      })\n    }\n  }\n\n  loadSampleCsv(): void {\n    this.csvPasteText.set(SAMPLE_CSV)\n    const cloned = clone(this.products())\n    for (const rawLine of SAMPLE_CSV.split(/\\r?\\n/)) {\n      const parts = rawLine.split(/[,;\\t]+/).map((s) => s.trim())\n      if (parts.length < 2) continue\n      const skuQuery = parts[0]!.toUpperCase()\n      const qty = parseInt(parts[1]!, 10)\n      const product = cloned.find((p) => p.sku.toUpperCase() === skuQuery)\n      if (product && !isNaN(qty)) product.cases = Math.max(0, Math.min(product.availableCases, qty))\n    }\n    this.products.set(cloned)\n    this.pasteFeedback.set({\n      type: 'success',\n      message: 'Sample manifest loaded and applied to matrix (5 SKUs updated).',\n    })\n  }\n\n  handleSaveTemplate(): void {\n    this.templateSaved.set(true)\n    this.saveTemplate.emit(this.products())\n    this.later(() => this.templateSaved.set(false), 4000)\n  }\n\n  handleSubmitOrder(): void {\n    if (this.totalCases() === 0) return\n    this.isSubmitting.set(true)\n    this.later(() => {\n      this.isSubmitting.set(false)\n      this.orderSubmitted.set(true)\n      this.submitOrder.emit({\n        poNumber: this.poRef(),\n        items: this.products().filter((p) => p.cases > 0),\n        subtotal: this.wholesaleSubtotal(),\n        totalUnits: this.totalUnits(),\n        totalCases: this.totalCases(),\n        savings: this.totalSavings(),\n      })\n    }, 600)\n  }\n\n  handleExportCsv(): void {\n    const products = this.products()\n    this.exportCsv.emit(products)\n    const header =\n      'SKU,Product Name,Variant,Case Pack Multiplier,Cases Ordered,Total Units,Wholesale Unit Price,Line Total\\n'\n    const rows = products\n      .filter((p) => p.cases > 0)\n      .map(\n        (p) =>\n          `\"${p.sku}\",\"${p.name}\",\"${p.variant}\",${p.casePack},${p.cases},${p.cases * p.casePack},${p.wholesaleUnitPrice},${(p.cases * p.casePack * p.wholesaleUnitPrice).toFixed(2)}`,\n      )\n      .join('\\n')\n    const csvContent = 'data:text/csv;charset=utf-8,' + encodeURIComponent(header + rows)\n    const downloadLink = document.createElement('a')\n    downloadLink.setAttribute('href', csvContent)\n    downloadLink.setAttribute('download', `${this.poRef()}-order-matrix.csv`)\n    document.body.appendChild(downloadLink)\n    downloadLink.click()\n    document.body.removeChild(downloadLink)\n  }\n\n  private later(fn: () => void, ms: number): void {\n    const t = setTimeout(() => {\n      this.timers.delete(t)\n      fn()\n    }, ms)\n    this.timers.add(t)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/wholesale-b2b-quick-order/wholesale-b2b-quick-order.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/wholesale-b2b-quick-order/index.ts",
      "content": "export {\n  UiWholesaleB2bQuickOrderComponent,\n  DEFAULT_WHOLESALE_PRODUCTS,\n  type WholesaleOrderPayload,\n  type WholesalePasteFeedback,\n  type WholesaleProduct,\n} from './wholesale-b2b-quick-order.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/wholesale-b2b-quick-order/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/table.json",
    "https://uipkge.dev/r/angular/textarea.json"
  ],
  "description": "B2B wholesale bulk order matrix with volume tier pricing, case pack multipliers, and CSV SKU paste input.",
  "categories": [
    "commerce",
    "ecommerce"
  ]
}