UIPackage
Menu

Framework

Change language

Boilerplate repo

Commercial Invoice Generator

blockdashboard

B2B cross-border commercial invoice generator with HTS tariff codes, EORI tax identifiers, and CIF valuation totals.

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/commercial-invoice-generator.json
Named registry:npx shadcn-vue@latest add @uipkge/commercial-invoice-generatorInstalls to:app/components/blocks/commercial-invoice-generator/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
invoiceNumberstring'INV-EXP-2026-9042'optional
incotermsstring'DAP (Delivered at Place)'optional
currencystring'USD'optional

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

InvoiceItem
interface InvoiceItem {
  id: string
  description: string
  htsCode: string
  countryOfOrigin: string
  qty: number
  unitPrice: number
  weightKg: number
}

Files installed (2)

  • app/components/blocks/commercial-invoice-generator/CommercialInvoiceGenerator.vue11.1 kB
    <script setup lang="ts">
    import { ref, computed } from 'vue'
    import { Download, Plus, Printer, ShieldCheck, Trash2 } from 'lucide-vue-next'
    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
    import { Button } from '@/components/ui/button'
    import { Input } from '@/components/ui/input'
    import { Badge } from '@/components/ui/badge'
    import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
    
    export interface InvoiceItem {
      id: string
      description: string
      htsCode: string
      countryOfOrigin: string
      qty: number
      unitPrice: number
      weightKg: number
    }
    
    export interface CommercialInvoiceProps {
      invoiceNumber?: string
      incoterms?: string
      currency?: string
    }
    
    const props = withDefaults(defineProps<CommercialInvoiceProps>(), {
      invoiceNumber: 'INV-EXP-2026-9042',
      incoterms: 'DAP (Delivered at Place)',
      currency: 'USD',
    })
    
    const isSigned = ref(false)
    
    const items = ref<InvoiceItem[]>([
      {
        id: 'inv-1',
        description: 'Precision CNC Machined Aluminum Actuator Housing',
        htsCode: '8479.90.94',
        countryOfOrigin: 'US',
        qty: 50,
        unitPrice: 185.0,
        weightKg: 24.5,
      },
      {
        id: 'inv-2',
        description: 'Brushless DC High-Torque Servo Motor 48V',
        htsCode: '8501.31.20',
        countryOfOrigin: 'DE',
        qty: 25,
        unitPrice: 320.0,
        weightKg: 31.0,
      },
      {
        id: 'inv-3',
        description: 'Industrial Polyurethane Seal Gasket Kit',
        htsCode: '3926.90.99',
        countryOfOrigin: 'JP',
        qty: 100,
        unitPrice: 14.5,
        weightKg: 4.2,
      },
    ])
    
    const subtotal = computed(() => items.value.reduce((sum, item) => sum + item.qty * item.unitPrice, 0))
    const totalWeightKg = computed(() => items.value.reduce((sum, item) => sum + item.weightKg, 0))
    const freightCharges = ref(450.0)
    const insuranceCharges = ref(85.0)
    const grandTotal = computed(() => subtotal.value + freightCharges.value + insuranceCharges.value)
    
    function addItem() {
      items.value.push({
        id: `inv-${Date.now()}`,
        description: 'General Industrial Hardware / Fasteners',
        htsCode: '7318.15.20',
        countryOfOrigin: 'US',
        qty: 10,
        unitPrice: 25.0,
        weightKg: 2.0,
      })
    }
    
    function removeItem(id: string) {
      items.value = items.value.filter((i) => i.id !== id)
    }
    </script>
    
    <template>
      <div data-slot="commercial-invoice-generator" class="w-full space-y-6">
        <!-- Top Action Card -->
        <Card>
          <CardHeader class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
            <div class="space-y-1">
              <div class="flex items-center gap-2">
                <span class="text-foreground font-mono text-sm font-semibold">{{ invoiceNumber }}</span>
                <Badge variant="outline">{{ incoterms }}</Badge>
                <Badge :variant="isSigned ? 'default' : 'secondary'">
                  {{ isSigned ? 'Customs Declared & Sealed' : 'Draft Declaration' }}
                </Badge>
              </div>
              <CardTitle class="text-xl">International Commercial Customs Invoice</CardTitle>
              <CardDescription>
                Harmonized Tariff Schedule (HTS) compliance declaration for international customs clearance.
              </CardDescription>
            </div>
    
            <div class="flex items-center gap-2">
              <Button variant="outline" size="sm" class="gap-1.5">
                <Printer class="size-3.5" />
                Print Form
              </Button>
              <Button variant="outline" size="sm" class="gap-1.5">
                <Download class="size-3.5" />
                Export EDI / PDF
              </Button>
              <Button v-if="!isSigned" variant="default" size="sm" class="gap-1.5" @click="isSigned = true">
                <ShieldCheck class="size-3.5" />
                Sign Customs Declaration
              </Button>
              <Button v-else variant="secondary" size="sm" class="gap-1.5" @click="isSigned = false"> Unlock Edit </Button>
            </div>
          </CardHeader>
    
          <CardContent class="border-border grid grid-cols-1 gap-6 border-t pt-4 sm:grid-cols-2">
            <!-- Exporter / Shipper -->
            <div class="border-border space-y-1 rounded-lg border p-3">
              <div class="text-muted-foreground text-xs font-semibold">EXPORTER / SHIPPER (CONSIGNOR)</div>
              <div class="text-foreground text-sm font-medium">Global Aerospace Components Inc.</div>
              <div class="text-muted-foreground text-xs">9400 Aeronautics Way, Dock 4</div>
              <div class="text-muted-foreground text-xs">Seattle, WA 98108, United States</div>
              <div class="text-muted-foreground pt-1 font-mono text-xs">EORI / Tax ID: US-EIN-98441029</div>
            </div>
    
            <!-- Importer / Consignee -->
            <div class="border-border space-y-1 rounded-lg border p-3">
              <div class="text-muted-foreground text-xs font-semibold">IMPORTER / BUYER (CONSIGNEE)</div>
              <div class="text-foreground text-sm font-medium">Nordic Robotics Systems AB</div>
              <div class="text-muted-foreground text-xs">Industrigatan 18, Byggnad B</div>
              <div class="text-muted-foreground text-xs">SE-417 56 Göteborg, Sweden</div>
              <div class="text-muted-foreground pt-1 font-mono text-xs">VAT / EORI: SE556012345601</div>
            </div>
          </CardContent>
        </Card>
    
        <!-- Line Items Table -->
        <Card>
          <CardHeader class="flex flex-col gap-3 pb-3 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <CardTitle class="text-base font-semibold">Customs Line Item Breakdown</CardTitle>
              <CardDescription>
                Total Gross Net Weight: <span class="font-mono">{{ totalWeightKg.toFixed(1) }} kg</span> · Currency:
                {{ currency }}
              </CardDescription>
            </div>
    
            <Button v-if="!isSigned" variant="outline" size="sm" class="gap-1.5" @click="addItem">
              <Plus class="size-3.5" />
              Add Line Item
            </Button>
          </CardHeader>
    
          <CardContent class="p-0">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead class="w-[300px]">Goods Description</TableHead>
                  <TableHead>HTS / Tariff Code</TableHead>
                  <TableHead>Origin (COO)</TableHead>
                  <TableHead class="text-right">Net Wt (kg)</TableHead>
                  <TableHead class="w-[90px] text-right">Qty</TableHead>
                  <TableHead class="w-[120px] text-right">Unit Price</TableHead>
                  <TableHead class="text-right">Customs Total</TableHead>
                  <TableHead v-if="!isSigned" class="w-[40px]"></TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                <TableRow v-for="item in items" :key="item.id">
                  <!-- Description -->
                  <TableCell>
                    <Input v-if="!isSigned" v-model="item.description" class="h-8 text-xs" />
                    <span v-else class="text-foreground font-medium">{{ item.description }}</span>
                  </TableCell>
    
                  <!-- HTS Code -->
                  <TableCell>
                    <Input v-if="!isSigned" v-model="item.htsCode" class="h-8 w-28 font-mono text-xs" />
                    <span v-else class="font-mono text-xs font-semibold">{{ item.htsCode }}</span>
                  </TableCell>
    
                  <!-- Country of Origin -->
                  <TableCell>
                    <Input
                      v-if="!isSigned"
                      v-model="item.countryOfOrigin"
                      class="h-8 w-16 text-center font-mono text-xs uppercase"
                    />
                    <span v-else class="bg-muted rounded px-1.5 py-0.5 font-mono text-xs font-medium">{{
                      item.countryOfOrigin
                    }}</span>
                  </TableCell>
    
                  <!-- Net Weight -->
                  <TableCell class="text-right">
                    <Input
                      v-if="!isSigned"
                      v-model.number="item.weightKg"
                      type="number"
                      step="0.1"
                      class="h-8 text-right font-mono text-xs"
                    />
                    <span v-else class="text-muted-foreground font-mono text-xs">{{ item.weightKg.toFixed(1) }}</span>
                  </TableCell>
    
                  <!-- Qty -->
                  <TableCell class="text-right">
                    <Input
                      v-if="!isSigned"
                      v-model.number="item.qty"
                      type="number"
                      min="1"
                      class="h-8 text-right font-mono text-xs"
                    />
                    <span v-else class="font-mono text-sm">{{ item.qty }}</span>
                  </TableCell>
    
                  <!-- Unit Price -->
                  <TableCell class="text-right">
                    <Input
                      v-if="!isSigned"
                      v-model.number="item.unitPrice"
                      type="number"
                      step="0.01"
                      class="h-8 text-right font-mono text-xs"
                    />
                    <span v-else class="font-mono text-sm">${{ item.unitPrice.toFixed(2) }}</span>
                  </TableCell>
    
                  <!-- Line Total -->
                  <TableCell class="text-foreground text-right font-mono text-sm font-semibold">
                    ${{ (item.qty * item.unitPrice).toFixed(2) }}
                  </TableCell>
    
                  <!-- Delete -->
                  <TableCell v-if="!isSigned">
                    <Button
                      variant="ghost"
                      size="icon"
                      class="text-muted-foreground hover:text-destructive size-8"
                      @click="removeItem(item.id)"
                    >
                      <Trash2 class="size-3.5" />
                    </Button>
                  </TableCell>
                </TableRow>
              </TableBody>
            </Table>
          </CardContent>
    
          <!-- Totals & Tariff Summary Footer -->
          <CardFooter class="border-border flex flex-col gap-4 border-t pt-4 sm:flex-row sm:items-start sm:justify-between">
            <div class="text-muted-foreground space-y-1 text-xs sm:max-w-md">
              <p class="text-foreground font-medium">Exporter Declaration & Legal Certification:</p>
              <p>
                I hereby certify that the information on this invoice is true and correct and that the contents of this
                shipment are as stated above.
              </p>
            </div>
    
            <div class="w-full space-y-1.5 sm:w-72">
              <div class="text-muted-foreground flex justify-between text-xs">
                <span>FOB Line Subtotal:</span>
                <span class="text-foreground font-mono font-medium">${{ subtotal.toFixed(2) }}</span>
              </div>
              <div class="text-muted-foreground flex justify-between text-xs">
                <span>International Freight (Air):</span>
                <span class="text-foreground font-mono font-medium">${{ freightCharges.toFixed(2) }}</span>
              </div>
              <div class="text-muted-foreground flex justify-between text-xs">
                <span>Cargo Marine Insurance:</span>
                <span class="text-foreground font-mono font-medium">${{ insuranceCharges.toFixed(2) }}</span>
              </div>
              <div class="border-border text-foreground flex justify-between border-t pt-2 text-sm font-semibold">
                <span>Declared CIF Value:</span>
                <span class="font-mono text-base font-bold">${{ grandTotal.toFixed(2) }} USD</span>
              </div>
            </div>
          </CardFooter>
        </Card>
      </div>
    </template>
    
  • app/components/blocks/commercial-invoice-generator/index.ts0.2 kB

Raw manifest:https://uipkge.dev/r/vue/commercial-invoice-generator.json