{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "commercial-invoice-generator",
  "title": "Commercial Invoice Generator",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/commercial-invoice-generator/commercial-invoice-generator.component.ts",
      "content": "import { Component, Input, computed, signal, ChangeDetectionStrategy } from '@angular/core'\nimport { Download, LucideAngularModule, Plus, Printer, ShieldCheck, Trash2 } 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  UiCardFooterComponent,\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'\n\nexport interface InvoiceItem {\n  id: string\n  description: string\n  htsCode: string\n  countryOfOrigin: string\n  qty: number\n  unitPrice: number\n  weightKg: number\n}\n\nexport interface CommercialInvoiceProps {\n  invoiceNumber?: string\n  incoterms?: string\n  currency?: string\n  className?: string\n}\n\n/**\n * CommercialInvoiceGenerator -- the React block, 1:1: an HTS customs invoice with\n * exporter / importer parties, editable line items (description, tariff code, origin,\n * weight, qty, unit price) and FOB + freight + insurance = CIF totals. Signing the\n * declaration locks every row into read-only text; \"Unlock Edit\" reopens it.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-commercial-invoice-generator, [ui-commercial-invoice-generator]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiCardDescriptionComponent,\n    UiCardContentComponent,\n    UiCardFooterComponent,\n    UiInputComponent,\n    UiTableComponent,\n    UiTableHeaderComponent,\n    UiTableBodyComponent,\n    UiTableRowComponent,\n    UiTableHeadComponent,\n    UiTableCellComponent,\n  ],\n  template: `\n    <div data-slot=\"commercial-invoice-generator\" [class]=\"rootClass\">\n      <!-- Top Action 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\">{{ invoiceNumber }}</span>\n              <span ui-badge variant=\"outline\">{{ incoterms }}</span>\n              <span ui-badge [variant]=\"isSigned() ? 'default' : 'secondary'\">\n                {{ isSigned() ? 'Customs Declared & Sealed' : 'Draft Declaration' }}\n              </span>\n            </div>\n            <h3 ui-card-title class=\"text-xl\">International Commercial Customs Invoice</h3>\n            <p ui-card-description>\n              Harmonized Tariff Schedule (HTS) compliance declaration for international customs clearance.\n            </p>\n          </div>\n\n          <div class=\"flex items-center gap-2\">\n            <button ui-button variant=\"outline\" size=\"sm\" class=\"gap-1.5\">\n              <lucide-icon [img]=\"Printer\" class=\"size-3.5\" aria-hidden=\"true\" />\n              Print Form\n            </button>\n            <button ui-button variant=\"outline\" size=\"sm\" class=\"gap-1.5\">\n              <lucide-icon [img]=\"Download\" class=\"size-3.5\" aria-hidden=\"true\" />\n              Export EDI / PDF\n            </button>\n            @if (!isSigned()) {\n              <button ui-button variant=\"default\" size=\"sm\" class=\"gap-1.5\" (click)=\"isSigned.set(true)\">\n                <lucide-icon [img]=\"ShieldCheck\" class=\"size-3.5\" aria-hidden=\"true\" />\n                Sign Customs Declaration\n              </button>\n            } @else {\n              <button ui-button variant=\"secondary\" size=\"sm\" class=\"gap-1.5\" (click)=\"isSigned.set(false)\">\n                Unlock Edit\n              </button>\n            }\n          </div>\n        </div>\n\n        <div ui-card-content class=\"border-border grid grid-cols-1 gap-6 border-t pt-4 sm:grid-cols-2\">\n          <!-- Exporter / Shipper -->\n          <div class=\"border-border space-y-1 rounded-lg border p-3\">\n            <div class=\"text-muted-foreground text-xs font-semibold\">EXPORTER / SHIPPER (CONSIGNOR)</div>\n            <div class=\"text-foreground text-sm font-medium\">Global Aerospace Components Inc.</div>\n            <div class=\"text-muted-foreground text-xs\">9400 Aeronautics Way, Dock 4</div>\n            <div class=\"text-muted-foreground text-xs\">Seattle, WA 98108, United States</div>\n            <div class=\"text-muted-foreground pt-1 font-mono text-xs\">EORI / Tax ID: US-EIN-98441029</div>\n          </div>\n\n          <!-- Importer / Consignee -->\n          <div class=\"border-border space-y-1 rounded-lg border p-3\">\n            <div class=\"text-muted-foreground text-xs font-semibold\">IMPORTER / BUYER (CONSIGNEE)</div>\n            <div class=\"text-foreground text-sm font-medium\">Nordic Robotics Systems AB</div>\n            <div class=\"text-muted-foreground text-xs\">Industrigatan 18, Byggnad B</div>\n            <div class=\"text-muted-foreground text-xs\">SE-417 56 Göteborg, Sweden</div>\n            <div class=\"text-muted-foreground pt-1 font-mono text-xs\">VAT / EORI: SE556012345601</div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Line Items Table -->\n      <div ui-card>\n        <div ui-card-header class=\"flex flex-col gap-3 pb-3 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <h3 ui-card-title class=\"text-base font-semibold\">Customs Line Item Breakdown</h3>\n            <p ui-card-description>\n              Total Gross Net Weight: <span class=\"font-mono\">{{ totalWeightKg().toFixed(1) }} kg</span> · Currency:\n              {{ currency }}\n            </p>\n          </div>\n\n          @if (!isSigned()) {\n            <button ui-button variant=\"outline\" size=\"sm\" class=\"gap-1.5\" (click)=\"addItem()\">\n              <lucide-icon [img]=\"Plus\" class=\"size-3.5\" aria-hidden=\"true\" />\n              Add Line Item\n            </button>\n          }\n        </div>\n\n        <div ui-card-content class=\"p-0\">\n          <div ui-table>\n            <thead ui-table-header>\n              <tr ui-table-row>\n                <th ui-table-head class=\"w-[300px]\">Goods Description</th>\n                <th ui-table-head>HTS / Tariff Code</th>\n                <th ui-table-head>Origin (COO)</th>\n                <th ui-table-head class=\"text-right\">Net Wt (kg)</th>\n                <th ui-table-head class=\"w-[90px] text-right\">Qty</th>\n                <th ui-table-head class=\"w-[120px] text-right\">Unit Price</th>\n                <th ui-table-head class=\"text-right\">Customs Total</th>\n                @if (!isSigned()) {\n                  <th ui-table-head class=\"w-[40px]\"></th>\n                }\n              </tr>\n            </thead>\n            <tbody ui-table-body>\n              @for (item of items(); track item.id) {\n                <tr ui-table-row>\n                  <!-- Description -->\n                  <td ui-table-cell>\n                    @if (!isSigned()) {\n                      <ui-input\n                        [value]=\"item.description\"\n                        (valueChange)=\"updateItem(item.id, { description: $event })\"\n                        class=\"h-8 text-xs\"\n                      />\n                    } @else {\n                      <span class=\"text-foreground font-medium\">{{ item.description }}</span>\n                    }\n                  </td>\n\n                  <!-- HTS Code -->\n                  <td ui-table-cell>\n                    @if (!isSigned()) {\n                      <ui-input\n                        [value]=\"item.htsCode\"\n                        (valueChange)=\"updateItem(item.id, { htsCode: $event })\"\n                        class=\"h-8 w-28 font-mono text-xs\"\n                      />\n                    } @else {\n                      <span class=\"font-mono text-xs font-semibold\">{{ item.htsCode }}</span>\n                    }\n                  </td>\n\n                  <!-- Country of Origin -->\n                  <td ui-table-cell>\n                    @if (!isSigned()) {\n                      <ui-input\n                        [value]=\"item.countryOfOrigin\"\n                        (valueChange)=\"updateItem(item.id, { countryOfOrigin: $event })\"\n                        class=\"h-8 w-16 text-center font-mono text-xs uppercase\"\n                      />\n                    } @else {\n                      <span class=\"bg-muted rounded px-1.5 py-0.5 font-mono text-xs font-medium\">{{\n                        item.countryOfOrigin\n                      }}</span>\n                    }\n                  </td>\n\n                  <!-- Net Weight -->\n                  <td ui-table-cell class=\"text-right\">\n                    @if (!isSigned()) {\n                      <ui-input\n                        type=\"number\"\n                        step=\"0.1\"\n                        [value]=\"item.weightKg\"\n                        (valueChange)=\"updateItem(item.id, { weightKg: toNumber($event) })\"\n                        class=\"h-8 text-right font-mono text-xs\"\n                      />\n                    } @else {\n                      <span class=\"text-muted-foreground font-mono text-xs\">{{ item.weightKg.toFixed(1) }}</span>\n                    }\n                  </td>\n\n                  <!-- Qty -->\n                  <td ui-table-cell class=\"text-right\">\n                    @if (!isSigned()) {\n                      <ui-input\n                        type=\"number\"\n                        min=\"1\"\n                        [value]=\"item.qty\"\n                        (valueChange)=\"updateItem(item.id, { qty: toNumber($event) })\"\n                        class=\"h-8 text-right font-mono text-xs\"\n                      />\n                    } @else {\n                      <span class=\"font-mono text-sm\">{{ item.qty }}</span>\n                    }\n                  </td>\n\n                  <!-- Unit Price -->\n                  <td ui-table-cell class=\"text-right\">\n                    @if (!isSigned()) {\n                      <ui-input\n                        type=\"number\"\n                        step=\"0.01\"\n                        [value]=\"item.unitPrice\"\n                        (valueChange)=\"updateItem(item.id, { unitPrice: toNumber($event) })\"\n                        class=\"h-8 text-right font-mono text-xs\"\n                      />\n                    } @else {\n                      <span class=\"font-mono text-sm\">{{ money(item.unitPrice) }}</span>\n                    }\n                  </td>\n\n                  <!-- Line Total -->\n                  <td ui-table-cell class=\"text-foreground text-right font-mono text-sm font-semibold\">\n                    {{ money(item.qty * item.unitPrice) }}\n                  </td>\n\n                  <!-- Delete -->\n                  @if (!isSigned()) {\n                    <td ui-table-cell>\n                      <button\n                        ui-button\n                        variant=\"ghost\"\n                        size=\"icon\"\n                        class=\"text-muted-foreground hover:text-destructive size-8\"\n                        [attr.aria-label]=\"'Remove line item ' + item.description\"\n                        (click)=\"removeItem(item.id)\"\n                      >\n                        <lucide-icon [img]=\"Trash2\" class=\"size-3.5\" aria-hidden=\"true\" />\n                      </button>\n                    </td>\n                  }\n                </tr>\n              }\n            </tbody>\n          </div>\n        </div>\n\n        <!-- Totals & Tariff Summary Footer -->\n        <div\n          ui-card-footer\n          class=\"border-border flex flex-col gap-4 border-t pt-4 sm:flex-row sm:items-start sm:justify-between\"\n        >\n          <div class=\"text-muted-foreground space-y-1 text-xs sm:max-w-md\">\n            <p class=\"text-foreground font-medium\">Exporter Declaration &amp; Legal Certification:</p>\n            <p>\n              I hereby certify that the information on this invoice is true and correct and that the contents of this\n              shipment are as stated above.\n            </p>\n          </div>\n\n          <div class=\"w-full space-y-1.5 sm:w-72\">\n            <div class=\"text-muted-foreground flex justify-between text-xs\">\n              <span>FOB Line Subtotal:</span>\n              <span class=\"text-foreground font-mono font-medium\">{{ money(subtotal()) }}</span>\n            </div>\n            <div class=\"text-muted-foreground flex justify-between text-xs\">\n              <span>International Freight (Air):</span>\n              <span class=\"text-foreground font-mono font-medium\">{{ money(freightCharges()) }}</span>\n            </div>\n            <div class=\"text-muted-foreground flex justify-between text-xs\">\n              <span>Cargo Marine Insurance:</span>\n              <span class=\"text-foreground font-mono font-medium\">{{ money(insuranceCharges()) }}</span>\n            </div>\n            <div class=\"border-border text-foreground flex justify-between border-t pt-2 text-sm font-semibold\">\n              <span>Declared CIF Value:</span>\n              <span class=\"font-mono text-base font-bold\">{{ money(grandTotal()) }} USD</span>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiCommercialInvoiceGeneratorComponent {\n  @Input() invoiceNumber = 'INV-EXP-2026-9042'\n  @Input() incoterms = 'DAP (Delivered at Place)'\n  @Input() currency = 'USD'\n  @Input('class') className?: string\n\n  protected readonly Printer = Printer\n  protected readonly Download = Download\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Plus = Plus\n  protected readonly Trash2 = Trash2\n\n  readonly isSigned = signal(false)\n  readonly freightCharges = signal(450.0)\n  readonly insuranceCharges = signal(85.0)\n\n  readonly items = signal<InvoiceItem[]>([\n    {\n      id: 'inv-1',\n      description: 'Precision CNC Machined Aluminum Actuator Housing',\n      htsCode: '8479.90.94',\n      countryOfOrigin: 'US',\n      qty: 50,\n      unitPrice: 185.0,\n      weightKg: 24.5,\n    },\n    {\n      id: 'inv-2',\n      description: 'Brushless DC High-Torque Servo Motor 48V',\n      htsCode: '8501.31.20',\n      countryOfOrigin: 'DE',\n      qty: 25,\n      unitPrice: 320.0,\n      weightKg: 31.0,\n    },\n    {\n      id: 'inv-3',\n      description: 'Industrial Polyurethane Seal Gasket Kit',\n      htsCode: '3926.90.99',\n      countryOfOrigin: 'JP',\n      qty: 100,\n      unitPrice: 14.5,\n      weightKg: 4.2,\n    },\n  ])\n\n  readonly subtotal = computed(() => this.items().reduce((sum, item) => sum + item.qty * item.unitPrice, 0))\n  readonly totalWeightKg = computed(() => this.items().reduce((sum, item) => sum + item.weightKg, 0))\n  readonly grandTotal = computed(() => this.subtotal() + this.freightCharges() + this.insuranceCharges())\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  addItem(): void {\n    this.items.update((prev) => [\n      ...prev,\n      {\n        id: `inv-${Date.now()}`,\n        description: 'General Industrial Hardware / Fasteners',\n        htsCode: '7318.15.20',\n        countryOfOrigin: 'US',\n        qty: 10,\n        unitPrice: 25.0,\n        weightKg: 2.0,\n      },\n    ])\n  }\n\n  removeItem(id: string): void {\n    this.items.update((prev) => prev.filter((i) => i.id !== id))\n  }\n\n  updateItem(id: string, updates: Partial<InvoiceItem>): void {\n    this.items.update((prev) => prev.map((item) => (item.id === id ? { ...item, ...updates } : item)))\n  }\n\n  /** React's `Number(e.target.value)` for the numeric cells. */\n  toNumber(value: string): number {\n    return Number(value)\n  }\n\n  /** React's `$${n.toFixed(2)}`. */\n  money(value: number): string {\n    return `$${value.toFixed(2)}`\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/commercial-invoice-generator/commercial-invoice-generator.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/commercial-invoice-generator/index.ts",
      "content": "export {\n  UiCommercialInvoiceGeneratorComponent,\n  type InvoiceItem,\n  type CommercialInvoiceProps,\n} from './commercial-invoice-generator.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/commercial-invoice-generator/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": "B2B cross-border commercial invoice generator with HTS tariff codes, EORI tax identifiers, and CIF valuation totals.",
  "categories": [
    "dashboard",
    "data-display"
  ]
}