Menu

Change language

GitHub repo

Delivery Proof Card

blocklogistics

Last-mile Electronic Proof of Delivery (ePOD) receipt featuring customer signature canvas readout, high-contrast drop-off photo with GPS geostamp HUD overlay, recipient & courier metadata, and itemized package manifest.

Installation

$npx uipkge-ng@latest add delivery-proof-card
Manifest:https://uipkge.dev/r/angular/delivery-proof-card.jsonInstalls to:src/app/components/blocks/delivery-proof-card/

Variants

Loading interactive previews…

Files installed (2)

  • src/app/components/blocks/delivery-proof-card/delivery-proof-card.component.ts33.5 kB
    // Last-mile Electronic Proof of Delivery (ePOD) receipt: customer signature canvas
    // readout, drop-off photo with GPS geostamp HUD overlay, recipient and courier metadata,
    // and an itemized package manifest. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'
    import {
      Camera,
      Check,
      Copy,
      Download,
      FileCheck,
      Fingerprint,
      Key,
      LucideAngularModule,
      MapPin,
      Package,
      Share2,
      ShieldCheck,
      Truck,
    } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    import {
      UiCardComponent,
      UiCardContentComponent,
      UiCardDescriptionComponent,
      UiCardHeaderComponent,
      UiCardTitleComponent,
    } from '@/ui/card/card.component'
    import { UiSeparatorComponent } from '@/ui/separator/separator.component'
    import {
      UiTableBodyComponent,
      UiTableCellComponent,
      UiTableComponent,
      UiTableHeadComponent,
      UiTableHeaderComponent,
      UiTableRowComponent,
    } from '@/ui/table/table.component'
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-delivery-proof-card, [ui-delivery-proof-card]',
      standalone: true,
      host: {
        '[attr.data-slot]': '"delivery-proof-card"',
        '[class]': 'hostClass',
      },
      imports: [
        LucideAngularModule,
        UiAvatarComponent,
        UiAvatarFallbackComponent,
        UiBadgeComponent,
        UiButtonComponent,
        UiCardComponent,
        UiCardContentComponent,
        UiCardDescriptionComponent,
        UiCardHeaderComponent,
        UiCardTitleComponent,
        UiSeparatorComponent,
        UiTableBodyComponent,
        UiTableCellComponent,
        UiTableComponent,
        UiTableHeadComponent,
        UiTableHeaderComponent,
        UiTableRowComponent,
      ],
      template: `
        <!-- Header Card -->
        <div ui-card class="border shadow-xs">
          <div ui-card-header class="flex flex-col gap-4 pb-6 sm:flex-row sm:items-start sm:justify-between">
            <div class="space-y-2">
              <div class="flex flex-wrap items-center gap-2">
                <span ui-badge wrap variant="outline" class="gap-1.5 font-mono text-xs">
                  <lucide-icon [img]="FileCheck" class="text-success size-3.5" />
                  ePOD Receipt
                </span>
                <div
                  class="border-success/20 bg-success/10 text-success inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium"
                >
                  <span class="relative flex size-1.5">
                    <span class="bg-success absolute inline-flex h-full w-full rounded-full opacity-75"></span>
                    <span class="bg-success relative inline-flex size-1.5 rounded-full"></span>
                  </span>
                  Delivered · Signed & Verified
                </div>
              </div>
    
              <div>
                <h1 class="text-foreground text-xl font-semibold tracking-tight sm:text-2xl">
                  Electronic Proof of Delivery (ePOD)
                </h1>
                <div class="text-muted-foreground mt-1 flex flex-wrap items-center gap-2 text-xs sm:text-sm">
                  <span>Tracking Number:</span>
                  <button
                    type="button"
                    (click)="copyTracking()"
                    class="hover:bg-muted focus-visible:ring-ring border-border bg-muted/40 text-foreground inline-flex min-h-6 items-center gap-1.5 rounded border px-2 py-0.5 font-mono text-xs font-semibold transition-colors focus-visible:ring-2 focus-visible:outline-hidden"
                    title="Click to copy tracking number"
                  >
                    #TRK-98421094
                    @if (copiedTracking()) {
                      <lucide-icon [img]="Check" class="text-success size-3" />
                    } @else {
                      <lucide-icon [img]="Copy" class="text-muted-foreground size-3" />
                    }
                  </button>
                  <span class="text-muted-foreground tabular-nums">Aug 21, 2026 at 14:32:08 PST</span>
                </div>
              </div>
            </div>
    
            <!-- Action Buttons -->
            <div class="flex flex-wrap items-center gap-2.5 pt-1">
              <button ui-button aria-label="Download attachment" variant="default" class="gap-2 shadow-xs">
                <lucide-icon [img]="Download" class="size-4" />
                Download ePOD PDF
              </button>
              <button ui-button variant="outline" (click)="handleShare()" class="gap-2 shadow-xs">
                @if (sharedLink()) {
                  <lucide-icon [img]="Check" class="text-success size-4" />
                } @else {
                  <lucide-icon [img]="Share2" class="size-4" />
                }
                {{ sharedLink() ? 'Link Copied' : 'Share Proof Link' }}
              </button>
            </div>
          </div>
        </div>
    
        <!-- 2-Column Delivery Verification Grid -->
        <div class="grid grid-cols-1 gap-6 lg:grid-cols-12">
          <!-- Left Column: Visual Proof & Electronic Signatures (lg:col-span-6) -->
          <div class="space-y-6 lg:col-span-6">
            <!-- Drop-off Photo Card -->
            <div ui-card class="border shadow-xs">
              <div ui-card-header class="pb-3">
                <div class="flex flex-wrap items-center justify-between">
                  <div class="flex items-center gap-2">
                    <lucide-icon [img]="Camera" class="text-primary size-4" />
                    <h3 ui-card-title class="text-base font-semibold">Drop-off Photo Verification</h3>
                  </div>
                  <span ui-badge wrap variant="secondary" class="font-mono text-xs"> Front Porch / Secure Entry </span>
                </div>
                <p ui-card-description>Photographic proof of drop-off captured at consignee doorstep</p>
              </div>
    
              <div ui-card-content class="space-y-4">
                <!-- Delivery Photo Container with Viewfinder and HUD overlay -->
                <div
                  class="group border-border relative aspect-[16/10] w-full overflow-hidden rounded-lg border bg-gradient-to-br from-zinc-800 via-zinc-900 to-black text-white shadow-inner"
                >
                  <!-- Photographic Canvas Composition -->
                  <div class="absolute inset-0 flex items-center justify-center p-6">
                    <!-- Porch Perspective & Doorstep Elements -->
                    <svg
                      class="h-full w-full opacity-90"
                      viewBox="0 0 600 375"
                      fill="none"
                      xmlns="http://www.w3.org/2000/svg"
                      aria-label="Delivery parcel positioned securely on consignee front porch"
                    >
                      <!-- Background Architectural Siding & Wall -->
                      <rect width="600" height="375" fill="#18181b" />
                      <line x1="0" y1="40" x2="600" y2="40" stroke="#27272a" stroke-width="1.5" />
                      <line x1="0" y1="80" x2="600" y2="80" stroke="#27272a" stroke-width="1.5" />
                      <line x1="0" y1="120" x2="600" y2="120" stroke="#27272a" stroke-width="1.5" />
                      <line x1="0" y1="160" x2="600" y2="160" stroke="#27272a" stroke-width="1.5" />
    
                      <!-- Doorframe & Entry Siding -->
                      <rect x="70" y="30" width="160" height="345" fill="#09090b" stroke="#3f3f46" stroke-width="2" />
                      <line x1="150" y1="30" x2="150" y2="375" stroke="#27272a" stroke-width="2" />
                      <circle cx="215" cy="190" r="5" fill="#a1a1aa" />
    
                      <!-- Porch Slate Flooring Horizon -->
                      <polygon points="0,210 600,210 600,375 0,375" fill="#1c1917" />
                      <line x1="0" y1="210" x2="600" y2="210" stroke="#44403c" stroke-width="2" />
                      <line x1="0" y1="270" x2="600" y2="270" stroke="#292524" stroke-width="1" />
                      <line x1="0" y1="330" x2="600" y2="330" stroke="#292524" stroke-width="1" />
    
                      <!-- Floor Plank Angles -->
                      <line x1="120" y1="210" x2="60" y2="375" stroke="#292524" stroke-width="1.5" />
                      <line x1="280" y1="210" x2="240" y2="375" stroke="#292524" stroke-width="1.5" />
                      <line x1="440" y1="210" x2="420" y2="375" stroke="#292524" stroke-width="1.5" />
    
                      <!-- Woven Door Mat -->
                      <rect
                        x="250"
                        y="225"
                        width="230"
                        height="115"
                        rx="4"
                        fill="#451a03"
                        fill-opacity="0.4"
                        stroke="#78350f"
                        stroke-width="1.5"
                      />
                      <line
                        x1="260"
                        y1="235"
                        x2="470"
                        y2="235"
                        stroke="#78350f"
                        stroke-width="0.75"
                        stroke-dasharray="3 3"
                      />
                      <line
                        x1="260"
                        y1="330"
                        x2="470"
                        y2="330"
                        stroke="#78350f"
                        stroke-width="0.75"
                        stroke-dasharray="3 3"
                      />
    
                      <!-- Package Shadow -->
                      <ellipse cx="360" cy="305" rx="75" ry="18" fill="#000000" fill-opacity="0.6" />
    
                      <!-- 3D Cardboard Package Box -->
                      <!-- Box Front Face -->
                      <polygon
                        points="305,230 405,230 405,300 305,300"
                        fill="#b45309"
                        stroke="#78350f"
                        stroke-width="1.5"
                      />
                      <!-- Box Top Face -->
                      <polygon
                        points="305,230 350,195 450,195 405,230"
                        fill="#d97706"
                        stroke="#78350f"
                        stroke-width="1.5"
                      />
                      <!-- Box Right Face -->
                      <polygon
                        points="405,230 450,195 450,265 405,300"
                        fill="#92400e"
                        stroke="#78350f"
                        stroke-width="1.5"
                      />
    
                      <!-- Sealing Tape Across Top & Front -->
                      <polygon points="370,195 385,195 355,230 340,230" fill="#f59e0b" fill-opacity="0.8" />
                      <rect x="348" y="230" width="14" height="70" fill="#f59e0b" fill-opacity="0.8" />
    
                      <!-- White Shipping Label on Box Front -->
                      <rect x="315" y="242" width="48" height="34" rx="1.5" fill="#ffffff" />
                      <line x1="320" y1="248" x2="350" y2="248" stroke="#18181b" stroke-width="1.5" />
                      <line x1="320" y1="252" x2="340" y2="252" stroke="#71717a" stroke-width="1" />
                      <!-- Barcode on Label -->
                      <line x1="320" y1="262" x2="320" y2="272" stroke="#000000" stroke-width="1.5" />
                      <line x1="323" y1="262" x2="323" y2="272" stroke="#000000" stroke-width="1" />
                      <line x1="325" y1="262" x2="325" y2="272" stroke="#000000" stroke-width="2" />
                      <line x1="329" y1="262" x2="329" y2="272" stroke="#000000" stroke-width="1" />
                      <line x1="332" y1="262" x2="332" y2="272" stroke="#000000" stroke-width="1.5" />
                      <line x1="336" y1="262" x2="336" y2="272" stroke="#000000" stroke-width="1" />
                      <line x1="340" y1="262" x2="340" y2="272" stroke="#000000" stroke-width="2" />
                      <line x1="344" y1="262" x2="344" y2="272" stroke="#000000" stroke-width="1" />
                      <line x1="348" y1="262" x2="348" y2="272" stroke="#000000" stroke-width="1.5" />
    
                      <!-- Fragile / Express Stamp -->
                      <rect
                        x="370"
                        y="248"
                        width="26"
                        height="14"
                        rx="1"
                        fill="#ef4444"
                        fill-opacity="0.15"
                        stroke="#ef4444"
                        stroke-width="0.75"
                      />
                      <line x1="373" y1="255" x2="393" y2="255" stroke="#ef4444" stroke-width="1" />
                    </svg>
                  </div>
    
                  <!-- Camera Viewfinder Overlay Reticles -->
                  <div class="pointer-events-none absolute inset-4 border border-white/20">
                    <!-- Top-Left Corner -->
                    <div class="border-success absolute -top-1 -left-1 size-3 border-t-2 border-l-2"></div>
                    <!-- Top-Right Corner -->
                    <div class="border-success absolute -top-1 -right-1 size-3 border-t-2 border-r-2"></div>
                    <!-- Bottom-Left Corner -->
                    <div class="border-success absolute -bottom-1 -left-1 size-3 border-b-2 border-l-2"></div>
                    <!-- Bottom-Right Corner -->
                    <div class="border-success absolute -right-1 -bottom-1 size-3 border-r-2 border-b-2"></div>
                    <!-- Center Target Crosshair -->
                    <div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
                      <div class="relative size-6">
                        <div class="absolute top-0 left-1/2 h-2 w-0.5 -translate-x-1/2 bg-white/40"></div>
                        <div class="absolute bottom-0 left-1/2 h-2 w-0.5 -translate-x-1/2 bg-white/40"></div>
                        <div class="absolute top-1/2 left-0 h-0.5 w-2 -translate-y-1/2 bg-white/40"></div>
                        <div class="absolute top-1/2 right-0 h-0.5 w-2 -translate-y-1/2 bg-white/40"></div>
                        <div
                          class="border-success absolute top-1/2 left-1/2 size-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full border"
                        ></div>
                      </div>
                    </div>
                  </div>
    
                  <!-- Top Telemetry Pill -->
                  <div
                    class="absolute top-3 left-3 flex items-center gap-1.5 rounded-md bg-black/60 px-2 py-1 backdrop-blur-md"
                  >
                    <span class="bg-success size-2 animate-pulse rounded-full"></span>
                    <span class="font-mono text-xs font-medium text-white/90">OPTICAL PROOF CAPTURED</span>
                  </div>
    
                  <!-- Bottom Geostamp HUD Banner -->
                  <div
                    class="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/95 via-black/75 to-transparent p-3 pt-6 sm:p-4"
                  >
                    <div class="flex flex-col gap-1.5 sm:flex-row sm:items-end sm:justify-between">
                      <div class="space-y-0.5">
                        <div class="text-success flex items-center gap-1.5">
                          <lucide-icon [img]="MapPin" class="size-3.5 shrink-0" />
                          <span class="font-mono text-xs font-semibold tracking-tight tabular-nums">
                            34.0522°N, 118.2437°W · Accurate to 3m
                          </span>
                        </div>
                        <p class="font-mono text-xs text-zinc-300">Springfield, OR · Geofence Validated (0.8m delta)</p>
                      </div>
                      <div class="flex items-center gap-2 text-right">
                        <span class="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs text-zinc-300"> Zebra TC57x </span>
                        <span class="text-muted-foreground font-mono text-xs tabular-nums"> 14:32:08 PST </span>
                      </div>
                    </div>
                  </div>
                </div>
    
                <!-- Photo Metadata Breakdown -->
                <div class="grid grid-cols-2 gap-2 text-xs sm:grid-cols-4">
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <p class="text-muted-foreground">Capture Time</p>
                    <p class="text-foreground mt-0.5 font-mono font-medium tabular-nums">14:32:08 PST</p>
                  </div>
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <p class="text-muted-foreground">Camera Sensor</p>
                    <p class="text-foreground mt-0.5 font-mono font-medium">13 MP AutoFocus</p>
                  </div>
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <p class="text-muted-foreground">Geofence Delta</p>
                    <p class="text-success text-success mt-0.5 font-mono font-medium tabular-nums">0.8m radius</p>
                  </div>
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <p class="text-muted-foreground">Integrity Stamp</p>
                    <p class="text-foreground mt-0.5 font-mono font-medium">SHA-256 Valid</p>
                  </div>
                </div>
              </div>
            </div>
    
            <!-- Customer Electronic Signature Card -->
            <div ui-card class="border shadow-xs">
              <div ui-card-header class="pb-3">
                <div class="flex flex-wrap items-center justify-between">
                  <div class="flex items-center gap-2">
                    <lucide-icon [img]="Fingerprint" class="text-primary size-4" />
                    <h3 ui-card-title class="text-base font-semibold">Customer Electronic Signature</h3>
                  </div>
                  <span ui-badge wrap variant="outline" class="border-success/30 bg-success/10 text-success gap-1">
                    <lucide-icon [img]="ShieldCheck" class="size-3" />
                    ID Verified
                  </span>
                </div>
                <p ui-card-description>Legally binding digital acceptance recorded via courier handheld terminal</p>
              </div>
    
              <div ui-card-content class="space-y-4">
                <!-- Digital Signature Lined Canvas Readout -->
                <div class="border-border bg-muted/20 relative rounded-lg border border-dashed p-4 sm:p-6">
                  <!-- Security Watermark Background -->
                  <div
                    class="pointer-events-none absolute inset-0 flex items-center justify-center opacity-[0.03] select-none dark:opacity-[0.06]"
                  >
                    <span class="font-mono text-xl font-semibold tracking-widest uppercase sm:text-2xl">
                      VERIFIED DIGITAL SIGNATURE • EPOD-#98421094
                    </span>
                  </div>
    
                  <!-- Signature SVG Canvas -->
                  <div class="relative z-10 flex h-24 w-full items-center justify-center">
                    <svg
                      class="stroke-foreground h-full w-full max-w-md"
                      viewBox="0 0 460 110"
                      fill="none"
                      xmlns="http://www.w3.org/2000/svg"
                      aria-label="Handwritten signature of Elena Rostova"
                    >
                      <!-- Natural Cursive Handwritten Signature Path: Elena Rostova -->
                      <path
                        d="M 35,68 C 42,32 55,24 64,28 C 74,32 78,78 86,76 C 94,74 98,52 108,52 C 118,52 122,68 134,68 C 142,68 146,56 158,56 C 170,56 174,68 186,68 M 205,38 C 208,62 212,80 220,78 C 228,76 238,44 250,44 C 262,44 266,74 278,74 C 286,74 290,62 300,62 C 310,62 316,74 328,74 C 342,74 358,48 372,44 C 388,40 398,62 414,64 C 424,65 435,52 445,48"
                        stroke="currentColor"
                        stroke-width="2.6"
                        stroke-linecap="round"
                        stroke-linejoin="round"
                      />
                      <!-- Flourish Underline -->
                      <path
                        d="M 50,88 C 110,84 240,88 380,82 C 410,81 430,78 440,74"
                        stroke="currentColor"
                        stroke-width="1.6"
                        stroke-linecap="round"
                        stroke-dasharray="4 2"
                        class="opacity-60"
                      />
                    </svg>
                  </div>
    
                  <!-- Signer Line Marker -->
                  <div
                    class="border-border text-muted-foreground mt-2 flex flex-wrap items-center justify-between border-t pt-2 text-xs"
                  >
                    <span class="font-mono">✕ Signer Baseline</span>
                    <span class="text-success font-mono">● Digitally Encrypted</span>
                  </div>
                </div>
    
                <!-- Signer Audit Metadata -->
                <div class="space-y-2 text-sm">
                  <div class="flex flex-wrap items-center justify-between">
                    <span class="text-muted-foreground text-xs">Received By</span>
                    <span class="text-foreground text-xs font-semibold sm:text-sm">Elena Rostova</span>
                  </div>
                  <ui-separator />
                  <div class="flex flex-wrap items-center justify-between">
                    <span class="text-muted-foreground text-xs">Signer Identity</span>
                    <span class="text-foreground text-xs font-medium">Direct Consignee (Government ID Verified)</span>
                  </div>
                  <ui-separator />
                  <div class="flex flex-wrap items-center justify-between">
                    <span class="text-muted-foreground text-xs">Timestamp</span>
                    <span class="text-foreground font-mono text-xs tabular-nums">Aug 21, 2026 at 14:32:08 PST</span>
                  </div>
                  <ui-separator />
                  <div class="flex flex-wrap items-center justify-between">
                    <span class="text-muted-foreground text-xs">Capture Method</span>
                    <span class="text-foreground font-mono text-xs">Capacitive Stylus · Terminal #SCAN-842</span>
                  </div>
                  <ui-separator />
                  <div class="flex flex-wrap items-center justify-between">
                    <span class="text-muted-foreground text-xs">Cryptographic Seal</span>
                    <button
                      type="button"
                      (click)="copyHash()"
                      class="hover:bg-muted focus-visible:ring-ring text-muted-foreground hover:text-foreground inline-flex min-h-6 items-center gap-1 rounded font-mono text-xs transition-colors focus-visible:ring-2 focus-visible:outline-hidden"
                      title="Click to copy SHA-256 seal hash"
                    >
                      <span>SHA-256: 7f83b1...126d9069</span>
                      @if (copiedHash()) {
                        <lucide-icon [img]="Check" class="text-success size-3" />
                      } @else {
                        <lucide-icon [img]="Copy" class="size-3" />
                      }
                    </button>
                  </div>
                </div>
              </div>
            </div>
          </div>
    
          <!-- Right Column: Delivery Address, Courier Metadata, & Package Manifest (lg:col-span-6) -->
          <div class="space-y-6 lg:col-span-6">
            <!-- Delivery Address & Locker Card -->
            <div ui-card class="border shadow-xs">
              <div ui-card-header class="pb-3">
                <div class="flex flex-wrap items-center justify-between">
                  <div class="flex items-center gap-2">
                    <lucide-icon [img]="MapPin" class="text-primary size-4" />
                    <h3 ui-card-title class="text-base font-semibold">Delivery Destination</h3>
                  </div>
                  <span ui-badge wrap variant="outline" class="text-xs">Residential</span>
                </div>
              </div>
    
              <div ui-card-content class="space-y-3.5 text-sm">
                <div class="space-y-1">
                  <p class="text-foreground font-semibold">Elena Rostova</p>
                  <p class="text-muted-foreground">742 Evergreen Terrace, Apt 4B</p>
                  <p class="text-muted-foreground">Springfield, OR 97477</p>
                  <p class="text-muted-foreground text-xs">United States • +1 (555) 839-2041</p>
                </div>
    
                <!-- Delivery Instructions Callout -->
                <div class="border-warning/30 bg-warning/10 flex items-start gap-3 rounded-lg border p-3 text-xs">
                  <lucide-icon [img]="Key" class="text-warning mt-0.5 size-4 shrink-0" />
                  <div class="space-y-0.5">
                    <p class="text-foreground text-warning font-medium">Delivery Instruction Recorded:</p>
                    <p class="text-warning/90 text-warning/80">"Left inside package locker #4B"</p>
                    <p class="text-muted-foreground pt-0.5 text-xs">Electronic latch verified locked at 14:32 PST.</p>
                  </div>
                </div>
              </div>
            </div>
    
            <!-- Courier & Vehicle Metadata Card -->
            <div ui-card class="border shadow-xs">
              <div ui-card-header class="pb-3">
                <div class="flex flex-wrap items-center justify-between">
                  <div class="flex items-center gap-2">
                    <lucide-icon [img]="Truck" class="text-primary size-4" />
                    <h3 ui-card-title class="text-base font-semibold">Courier & Vehicle Metadata</h3>
                  </div>
                  <span ui-badge wrap variant="secondary" class="bg-primary/10 text-primary text-xs font-medium">
                    FedEx Express Priority
                  </span>
                </div>
              </div>
    
              <div ui-card-content class="space-y-4">
                <!-- Driver Profile Row -->
                <div class="flex flex-wrap items-center justify-between">
                  <div class="flex items-center gap-3">
                    <ui-avatar class="border-border size-10 border">
                      <ui-avatar-fallback class="bg-primary/10 text-primary text-xs font-semibold">DC</ui-avatar-fallback>
                    </ui-avatar>
                    <div>
                      <p class="text-foreground text-sm font-semibold">David Chen</p>
                      <p class="text-muted-foreground text-xs">Van #204 · Ford Transit 350</p>
                    </div>
                  </div>
                  <span ui-badge wrap variant="outline" class="font-mono text-xs">Rating: 4.99 ★</span>
                </div>
    
                <ui-separator />
    
                <!-- Dispatch & Terminal Grid -->
                <div class="grid grid-cols-1 gap-3 text-xs sm:grid-cols-2">
                  <div class="space-y-1">
                    <span class="text-muted-foreground">Carrier Service</span>
                    <p class="text-foreground font-medium">FedEx Express Priority</p>
                  </div>
                  <div class="space-y-1">
                    <span class="text-muted-foreground">Handheld Terminal ID</span>
                    <p class="text-foreground font-mono font-medium">#SCAN-842</p>
                  </div>
                  <div class="space-y-1">
                    <span class="text-muted-foreground">Dispatch Hub</span>
                    <p class="text-foreground font-medium">Pacific NW Hub (PDX-04)</p>
                  </div>
                  <div class="space-y-1">
                    <span class="text-muted-foreground">Route & Sequence</span>
                    <p class="text-foreground font-mono font-medium">RTE-NW-084 · Stop #42</p>
                  </div>
                </div>
              </div>
            </div>
    
            <!-- Package Manifest Table Card -->
            <div ui-card class="border shadow-xs">
              <div ui-card-header class="pb-3">
                <div class="flex flex-wrap items-center justify-between">
                  <div class="flex items-center gap-2">
                    <lucide-icon [img]="Package" class="text-primary size-4" />
                    <h3 ui-card-title class="text-base font-semibold">Package Manifest</h3>
                  </div>
                  <span ui-badge wrap variant="outline" class="font-mono text-xs">2 Items · 1 Parcel</span>
                </div>
                <p ui-card-description>Itemized manifest details and physical cargo specifications</p>
              </div>
    
              <div ui-card-content class="space-y-4">
                <!-- Manifest Table -->
                <div class="border-border overflow-hidden rounded-lg border">
                  <div class="overflow-x-auto">
                    <table ui-table>
                      <thead ui-table-header>
                        <tr ui-table-row class="bg-muted/40">
                          <th ui-table-head class="text-xs font-semibold">Item Description</th>
                          <th ui-table-head class="text-xs font-semibold">SKU / Ref</th>
                          <th ui-table-head class="text-right text-xs font-semibold">Qty</th>
                          <th ui-table-head class="text-right text-xs font-semibold">Weight</th>
                        </tr>
                      </thead>
                      <tbody ui-table-body>
                        <tr ui-table-row>
                          <td ui-table-cell class="text-foreground py-2.5 text-xs font-medium">
                            Pro Wireless Studio Headphones X9
                          </td>
                          <td ui-table-cell class="text-muted-foreground py-2.5 font-mono text-xs">SKU-AUD-9821</td>
                          <td ui-table-cell class="text-foreground py-2.5 text-right font-mono text-xs tabular-nums">1</td>
                          <td ui-table-cell class="text-muted-foreground py-2.5 text-right font-mono text-xs tabular-nums">
                            5.6 lbs
                          </td>
                        </tr>
                        <tr ui-table-row>
                          <td ui-table-cell class="text-foreground py-2.5 text-xs font-medium">
                            Braided Balanced XLR Cable (Pair, 3m)
                          </td>
                          <td ui-table-cell class="text-muted-foreground py-2.5 font-mono text-xs">SKU-CBL-4482</td>
                          <td ui-table-cell class="text-foreground py-2.5 text-right font-mono text-xs tabular-nums">1</td>
                          <td ui-table-cell class="text-muted-foreground py-2.5 text-right font-mono text-xs tabular-nums">
                            2.8 lbs
                          </td>
                        </tr>
                      </tbody>
                    </table>
                  </div>
                </div>
    
                <!-- Physical Specs Summary Grid -->
                <div class="grid grid-cols-2 gap-2 text-xs sm:grid-cols-4">
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <span class="text-muted-foreground">Total Weight</span>
                    <p class="text-foreground mt-0.5 font-mono font-semibold tabular-nums">8.4 lbs (3.81 kg)</p>
                  </div>
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <span class="text-muted-foreground">Dimensions</span>
                    <p class="text-foreground mt-0.5 font-mono font-semibold tabular-nums">14" × 10" × 6"</p>
                  </div>
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <span class="text-muted-foreground">Handling</span>
                    <p class="text-foreground mt-0.5 font-medium">Fragile Express</p>
                  </div>
                  <div class="border-border bg-muted/30 rounded-lg border p-2.5">
                    <span class="text-muted-foreground">Tamper Seal</span>
                    <p class="text-success mt-0.5 font-mono font-medium">#SEAL-9842</p>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
    
        <!-- Security & Chain of Custody Audit Strip -->
        <div ui-card class="border-border bg-muted/20 border shadow-xs">
          <div ui-card-content class="flex flex-col gap-3 p-4 text-xs sm:flex-row sm:items-center sm:justify-between">
            <div class="text-muted-foreground flex items-center gap-2">
              <lucide-icon [img]="ShieldCheck" class="text-success size-4 shrink-0" />
              <span>Chain of Custody Verified · Cryptographically Timestamped & Geofence Locked</span>
            </div>
            <div class="text-muted-foreground flex flex-wrap items-center gap-2 font-mono">
              <span class="text-muted-foreground/80">GPS Lock:</span>
              <button
                type="button"
                (click)="copyGps()"
                class="hover:bg-muted focus-visible:ring-ring bg-muted/60 text-foreground inline-flex min-h-6 items-center gap-1 rounded px-1.5 py-0.5 transition-colors focus-visible:ring-2 focus-visible:outline-hidden"
                title="Click to copy GPS coordinates"
              >
                <span class="tabular-nums">34.0522°N, 118.2437°W</span>
                @if (copiedGps()) {
                  <lucide-icon [img]="Check" class="text-success size-3" />
                } @else {
                  <lucide-icon [img]="Copy" class="size-3" />
                }
              </button>
            </div>
          </div>
        </div>
      `,
    })
    export class UiDeliveryProofCardComponent {
      protected readonly Camera = Camera
      protected readonly Check = Check
      protected readonly Copy = Copy
      protected readonly Download = Download
      protected readonly FileCheck = FileCheck
      protected readonly Fingerprint = Fingerprint
      protected readonly Key = Key
      protected readonly MapPin = MapPin
      protected readonly Package = Package
      protected readonly Share2 = Share2
      protected readonly ShieldCheck = ShieldCheck
      protected readonly Truck = Truck
    
      @Input('class') className?: string
    
      readonly copiedTracking = signal(false)
      readonly copiedGps = signal(false)
      readonly copiedHash = signal(false)
      readonly sharedLink = signal(false)
    
      get hostClass(): string {
        return cn('w-full space-y-6', this.className)
      }
    
      copyTracking(): void {
        try {
          void navigator.clipboard?.writeText('TRK-98421094')
        } catch {
          // Clipboard unavailable (jsdom / insecure context): still show the confirmation.
        }
        this.copiedTracking.set(true)
        setTimeout(() => this.copiedTracking.set(false), 2000)
      }
    
      copyGps(): void {
        try {
          void navigator.clipboard?.writeText('34.0522°N, 118.2437°W')
        } catch {
          // Clipboard unavailable (jsdom / insecure context): still show the confirmation.
        }
        this.copiedGps.set(true)
        setTimeout(() => this.copiedGps.set(false), 2000)
      }
    
      copyHash(): void {
        try {
          void navigator.clipboard?.writeText('7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069')
        } catch {
          // Clipboard unavailable (jsdom / insecure context): still show the confirmation.
        }
        this.copiedHash.set(true)
        setTimeout(() => this.copiedHash.set(false), 2000)
      }
    
      handleShare(): void {
        try {
          void navigator.clipboard?.writeText('https://uipkge.dev/epod/TRK-98421094')
        } catch {
          // Clipboard unavailable (jsdom / insecure context): still show the confirmation.
        }
        this.sharedLink.set(true)
        setTimeout(() => this.sharedLink.set(false), 2000)
      }
    }
    
  • src/app/components/blocks/delivery-proof-card/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/delivery-proof-card.json