UIPackage
Menu

Framework

Change language

Boilerplate repo

Gift Card Balance Checker

blockcommerce

Customer storefront gift card balance lookup, live digital card passbook, quick fund reloading, and complete redemption audit history ledger.

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/gift-card-balance-checker.json
Named registry:npx shadcn-vue@latest add @uipkge/gift-card-balance-checkerInstalls to:app/components/blocks/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
classHTMLAttributes['class']optional

Schema

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

TransactionRecord
interface TransactionRecord {
  id: string
  date: string
  reference: string
  title: string
  subtitle?: string
  type: 'activation' | 'redemption' | 'reload'
  amount: number
  balanceAfter: number
  status: 'Completed' | 'Pending'
}

Files installed (1)

  • app/components/blocks/GiftCardBalanceChecker.vue27.3 kB
    <script setup lang="ts">
    import { computed, ref } from 'vue'
    import type { HTMLAttributes } from 'vue'
    import {
      ArrowDownLeft,
      ArrowUpRight,
      Check,
      CheckCircle2,
      Copy,
      CreditCard,
      Gift,
      HelpCircle,
      Lock,
      Plus,
      RefreshCw,
      ShieldCheck,
      ShoppingBag,
      Wallet,
    } from 'lucide-vue-next'
    import { cn } from '@/lib/utils'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
    import { Input } from '@/components/ui/input'
    import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
    import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
    
    interface Props {
      class?: HTMLAttributes['class']
    }
    
    const props = defineProps<Props>()
    
    interface TransactionRecord {
      id: string
      date: string
      reference: string
      title: string
      subtitle?: string
      type: 'activation' | 'redemption' | 'reload'
      amount: number
      balanceAfter: number
      status: 'Completed' | 'Pending'
    }
    
    const initialTransactions: TransactionRecord[] = [
      {
        id: 'tx-1',
        date: 'Dec 19, 2024',
        reference: '#ORD-84112',
        title: 'Store Checkout Redemption',
        subtitle: 'Order #ORD-84112 · Leather Travel Wallet',
        type: 'redemption',
        amount: -15.0,
        balanceAfter: 150.0,
        status: 'Completed',
      },
      {
        id: 'tx-2',
        date: 'Nov 04, 2024',
        reference: '#ORD-84920',
        title: 'Store Checkout Redemption',
        subtitle: 'Order #ORD-84920 · Merino Knit Sweater',
        type: 'redemption',
        amount: -35.0,
        balanceAfter: 165.0,
        status: 'Completed',
      },
      {
        id: 'tx-3',
        date: 'Oct 12, 2024',
        reference: '#ACT-99014',
        title: 'Initial Activation & Fund Issue',
        subtitle: 'Digital Storefront Pass Issued',
        type: 'activation',
        amount: 200.0,
        balanceAfter: 200.0,
        status: 'Completed',
      },
    ]
    
    const cardNumber = ref('7482 - 9104 - 6382 - 8492')
    const securityPin = ref('4829')
    const isChecking = ref(false)
    const isVerified = ref(true)
    const copied = ref(false)
    const applied = ref(false)
    const feedbackMessage = ref<string | null>(null)
    const filterType = ref<'all' | 'redemptions' | 'loads'>('all')
    const transactions = ref<TransactionRecord[]>(initialTransactions)
    
    function formatCardInput(raw: string): string {
      const digits = raw.replace(/\D/g, '').slice(0, 16)
      return digits.replace(/(.{4})(?=.)/g, '$1 - ')
    }
    
    function handleCardNumberChange(e: Event) {
      const target = e.target as HTMLInputElement
      const formatted = formatCardInput(target.value)
      cardNumber.value = formatted
      target.value = formatted
    }
    
    function handlePinChange(e: Event) {
      const target = e.target as HTMLInputElement
      const digits = target.value.replace(/\D/g, '').slice(0, 4)
      securityPin.value = digits
      target.value = digits
    }
    
    const currentBalance = computed(() => {
      if (transactions.value.length === 0) return 0
      return transactions.value[0].balanceAfter
    })
    
    const totalLoaded = computed(() => {
      return transactions.value.filter((t) => t.amount > 0).reduce((sum, t) => sum + t.amount, 0)
    })
    
    const totalSpent = computed(() => {
      return Math.abs(transactions.value.filter((t) => t.amount < 0).reduce((sum, t) => sum + t.amount, 0))
    })
    
    const lastFourDigits = computed(() => {
      const digits = cardNumber.value.replace(/\D/g, '')
      return digits.length >= 4 ? digits.slice(-4) : '8492'
    })
    
    const filteredTransactions = computed(() => {
      if (filterType.value === 'redemptions') {
        return transactions.value.filter((t) => t.type === 'redemption')
      }
      if (filterType.value === 'loads') {
        return transactions.value.filter((t) => t.type === 'activation' || t.type === 'reload')
      }
      return transactions.value
    })
    
    function checkBalance() {
      isChecking.value = true
      feedbackMessage.value = null
      setTimeout(() => {
        isChecking.value = false
        isVerified.value = true
        feedbackMessage.value = `Gift card verified. Current active balance is $${currentBalance.value.toFixed(2)}.`
        setTimeout(() => {
          feedbackMessage.value = null
        }, 4000)
      }, 500)
    }
    
    function copyCardNumber() {
      const raw = cardNumber.value.replace(/\s+/g, '')
      if (navigator?.clipboard) {
        navigator.clipboard.writeText(raw)
      }
      copied.value = true
      setTimeout(() => {
        copied.value = false
      }, 2000)
    }
    
    function applyToOrder() {
      copyCardNumber()
      applied.value = true
      feedbackMessage.value = `Card ending in ${lastFourDigits.value} copied and applied to your checkout session!`
      setTimeout(() => {
        applied.value = false
        feedbackMessage.value = null
      }, 4000)
    }
    
    function reloadFunds(amount: number) {
      const newBalance = currentBalance.value + amount
      const newRecord: TransactionRecord = {
        id: `tx-${Date.now()}`,
        date: 'Today',
        reference: `#RLD-${Math.floor(10000 + Math.random() * 90000)}`,
        title: 'Manual Online Fund Reload',
        subtitle: `Instant Online Top-Up (+ $${amount.toFixed(2)})`,
        type: 'reload',
        amount: amount,
        balanceAfter: newBalance,
        status: 'Completed',
      }
      transactions.value = [newRecord, ...transactions.value]
      feedbackMessage.value = `Successfully reloaded +$${amount.toFixed(2)}! New card balance: $${newBalance.toFixed(2)}.`
      setTimeout(() => {
        feedbackMessage.value = null
      }, 4000)
    }
    </script>
    
    <template>
      <div data-slot="gift-card-balance-checker" :class="cn('w-full space-y-6', props.class)">
        <!-- Header Section -->
        <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
          <div class="space-y-1">
            <div class="flex items-center gap-2">
              <h2 class="text-foreground text-2xl font-bold tracking-tight">Check Gift Card Balance</h2>
              <Badge variant="outline" class="bg-primary/5 text-primary border-primary/20 gap-1">
                <CheckCircle2 class="text-primary size-3" />
                Storefront Pass
              </Badge>
            </div>
            <p class="text-muted-foreground text-sm">
              Check your remaining card balance, review past order redemptions, or reload funds.
            </p>
          </div>
    
          <div class="flex items-center gap-2">
            <TooltipProvider :delay-duration="150">
              <Tooltip>
                <TooltipTrigger as-child>
                  <div
                    class="border-border bg-card text-muted-foreground hover:text-foreground flex cursor-pointer items-center gap-1.5 rounded-md border px-3 py-1.5 text-xs shadow-xs"
                  >
                    <HelpCircle class="text-primary size-3.5" />
                    <span>PIN & Card Guide</span>
                  </div>
                </TooltipTrigger>
                <TooltipContent side="bottom" class="max-w-xs text-xs">
                  Locate the 16-digit card code on the back of your physical card or inside your digital gift email. The
                  4-digit security PIN is under the scratch-off foil.
                </TooltipContent>
              </Tooltip>
            </TooltipProvider>
          </div>
        </div>
    
        <!-- Feedback Banner if active -->
        <div
          v-if="feedbackMessage"
          class="border-success/20 bg-success/10 text-success flex items-center justify-between rounded-lg border px-4 py-2.5 text-sm"
        >
          <div class="flex items-center gap-2">
            <CheckCircle2 class="text-success size-4 shrink-0" />
            <span>{{ feedbackMessage }}</span>
          </div>
        </div>
    
        <!-- 2-Column Storefront Layout -->
        <div class="grid grid-cols-1 gap-6 lg:grid-cols-12">
          <!-- Left Column: Lookup Form & Digital Card Hero -->
          <div class="space-y-6 lg:col-span-5">
            <!-- Lookup Card Form -->
            <Card class="border-border shadow-xs">
              <CardHeader class="pb-4">
                <div class="flex items-center justify-between">
                  <CardTitle class="text-base font-semibold">Card Lookup</CardTitle>
                  <Badge
                    v-if="isVerified"
                    variant="outline"
                    class="border-success/30 bg-success/10 text-success gap-1 text-xs"
                  >
                    <Check class="size-3" />
                    Active Card
                  </Badge>
                </div>
                <CardDescription class="text-xs">
                  Enter your gift card credentials to view live balance and activity.
                </CardDescription>
              </CardHeader>
    
              <CardContent class="space-y-4 pb-4">
                <!-- 16-Digit Card Number Input -->
                <div class="space-y-1.5">
                  <div class="flex items-center justify-between">
                    <label for="gc-number-input" class="text-foreground text-xs font-medium"> 16-Digit Card Number </label>
                    <TooltipProvider :delay-duration="150">
                      <Tooltip>
                        <TooltipTrigger as-child>
                          <button
                            type="button"
                            class="text-muted-foreground hover:text-foreground inline-flex items-center focus-visible:outline-none"
                            aria-label="Gift card number info"
                          >
                            <HelpCircle class="size-3.5" />
                          </button>
                        </TooltipTrigger>
                        <TooltipContent side="top" class="max-w-xs text-xs">
                          Enter the 16 digits on the back of your card or in your receipt email.
                        </TooltipContent>
                      </Tooltip>
                    </TooltipProvider>
                  </div>
    
                  <Input
                    id="gc-number-input"
                    :model-value="cardNumber"
                    placeholder="XXXX - XXXX - XXXX - XXXX"
                    maxlength="25"
                    class="font-mono text-sm"
                    :prefix-icon="CreditCard"
                    @input="handleCardNumberChange"
                  />
                </div>
    
                <!-- 4-Digit Security PIN Input -->
                <div class="space-y-1.5">
                  <div class="flex items-center justify-between">
                    <label for="gc-pin-input" class="text-foreground text-xs font-medium"> Security PIN (4 digits) </label>
                    <TooltipProvider :delay-duration="150">
                      <Tooltip>
                        <TooltipTrigger as-child>
                          <button
                            type="button"
                            class="text-muted-foreground hover:text-foreground inline-flex items-center focus-visible:outline-none"
                            aria-label="Security PIN info"
                          >
                            <HelpCircle class="size-3.5" />
                          </button>
                        </TooltipTrigger>
                        <TooltipContent side="top" class="max-w-xs text-xs">
                          The 4-digit PIN is revealed by scratching the silver foil or listed in your digital claim email.
                        </TooltipContent>
                      </Tooltip>
                    </TooltipProvider>
                  </div>
    
                  <Input
                    id="gc-pin-input"
                    :model-value="securityPin"
                    type="password"
                    placeholder="••••"
                    maxlength="4"
                    show-password-toggle
                    class="font-mono text-sm"
                    :prefix-icon="Lock"
                    @input="handlePinChange"
                  />
                </div>
    
                <Button type="button" class="w-full font-medium" :disabled="isChecking" @click="checkBalance">
                  <RefreshCw v-if="isChecking" class="mr-2 size-4 animate-spin" />
                  <ShieldCheck v-else class="mr-2 size-4" />
                  {{ isChecking ? 'Verifying Balance...' : 'Check Balance' }}
                </Button>
              </CardContent>
            </Card>
    
            <!-- Stylized Digital Gift Card Hero Display -->
            <div
              class="relative overflow-hidden rounded-2xl border border-white/10 bg-gradient-to-br from-zinc-950 via-neutral-900 to-zinc-900 p-6 text-white shadow-lg"
            >
              <div class="relative flex flex-col justify-between space-y-6">
                <!-- Card Top Bar: Brand & Pass Badge -->
                <div class="flex items-start justify-between">
                  <div class="flex items-center gap-2.5">
                    <div
                      class="flex size-9 items-center justify-center rounded-xl border border-white/10 bg-white/10 text-white shadow-inner backdrop-blur-md"
                    >
                      <Gift class="text-success size-5" />
                    </div>
                    <div>
                      <p class="text-xs font-bold tracking-wider text-neutral-200 uppercase">Lumen Store</p>
                      <p class="text-xs text-neutral-400">Digital Gift Pass</p>
                    </div>
                  </div>
    
                  <Badge
                    variant="outline"
                    class="gap-1.5 border-white/20 bg-white/10 px-2.5 py-0.5 text-xs text-white backdrop-blur-md"
                  >
                    <span class="bg-success size-1.5 animate-pulse rounded-full" />
                    Verified Active
                  </Badge>
                </div>
    
                <!-- Card Center: Chip Icon & Balance Display -->
                <div class="space-y-1 pt-1">
                  <div class="flex items-center justify-between">
                    <span class="text-xs font-medium tracking-wider text-neutral-400 uppercase">Available Balance</span>
                    <!-- Chip graphic representation -->
                    <div class="flex items-center gap-1 opacity-70">
                      <div
                        class="border-warning/20/40 h-5 w-7 rounded-sm border bg-gradient-to-tr from-amber-400/20 to-amber-200/40"
                      />
                      <span class="font-mono text-xs tracking-tighter text-neutral-400">NFC</span>
                    </div>
                  </div>
    
                  <div class="flex items-baseline gap-2">
                    <span class="text-3xl font-bold tracking-tight text-white tabular-nums sm:text-4xl">
                      ${{ currentBalance.toFixed(2) }}
                    </span>
                    <span class="text-success text-xs font-medium">USD</span>
                  </div>
                </div>
    
                <!-- Card Footer: Card Number & Expiry terms -->
                <div class="space-y-2 border-t border-white/10 pt-4">
                  <div class="flex items-center justify-between">
                    <div class="flex items-center gap-2">
                      <span class="font-mono text-sm tracking-widest text-neutral-200">
                        •••• •••• •••• {{ lastFourDigits }}
                      </span>
                      <button
                        type="button"
                        class="rounded p-1 text-neutral-400 transition-colors hover:text-white focus-visible:ring-1 focus-visible:ring-white/40 focus-visible:outline-none"
                        aria-label="Copy card number"
                        @click="copyCardNumber"
                      >
                        <Check v-if="copied" class="text-success size-3.5" />
                        <Copy v-else class="size-3.5" />
                      </button>
                    </div>
                    <span v-if="copied" class="animate-in fade-in text-success text-xs font-medium"> Copied! </span>
                  </div>
    
                  <div class="flex items-center justify-between text-xs text-neutral-400">
                    <span>Never Expires · No Inactivity Fees</span>
                    <span class="font-mono text-neutral-500">PIN: ••••</span>
                  </div>
                </div>
              </div>
            </div>
    
            <!-- Quick Action Buttons -->
            <div class="space-y-3">
              <Button variant="default" class="w-full font-medium" @click="applyToOrder">
                <Check v-if="applied" class="text-success mr-2 size-4" />
                <ShoppingBag v-else class="mr-2 size-4" />
                {{ applied ? 'Applied to Checkout!' : 'Apply to Next Order' }}
              </Button>
    
              <!-- Quick Reload Card -->
              <Card class="border-border shadow-xs">
                <CardHeader class="p-4 pb-2">
                  <div class="flex items-center justify-between">
                    <CardTitle class="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
                      Quick Reload Funds
                    </CardTitle>
                    <Plus class="text-muted-foreground size-3.5" />
                  </div>
                </CardHeader>
                <CardContent class="p-4 pt-0">
                  <p class="text-muted-foreground mb-3 text-xs">
                    Top up your gift card instantly with a saved payment method.
                  </p>
                  <div class="grid grid-cols-3 gap-2">
                    <Button
                      variant="outline"
                      size="sm"
                      class="hover:bg-primary/5 hover:text-primary hover:border-primary/30 w-full font-medium tabular-nums"
                      @click="reloadFunds(25)"
                    >
                      + $25.00
                    </Button>
                    <Button
                      variant="outline"
                      size="sm"
                      class="hover:bg-primary/5 hover:text-primary hover:border-primary/30 w-full font-medium tabular-nums"
                      @click="reloadFunds(50)"
                    >
                      + $50.00
                    </Button>
                    <Button
                      variant="outline"
                      size="sm"
                      class="hover:bg-primary/5 hover:text-primary hover:border-primary/30 w-full font-medium tabular-nums"
                      @click="reloadFunds(100)"
                    >
                      + $100.00
                    </Button>
                  </div>
                </CardContent>
              </Card>
            </div>
          </div>
    
          <!-- Right Column: Balance Stats & Redemption History Table -->
          <div class="space-y-6 lg:col-span-7">
            <!-- Passbook Summary Metrics -->
            <div class="grid grid-cols-1 gap-3 sm:grid-cols-3">
              <div class="border-border bg-card rounded-xl border p-4 shadow-xs">
                <div class="text-muted-foreground mb-1 flex items-center justify-between text-xs">
                  <span>Available Balance</span>
                  <Wallet class="text-primary size-3.5" />
                </div>
                <p class="text-foreground text-2xl font-bold tracking-tight tabular-nums">
                  ${{ currentBalance.toFixed(2) }}
                </p>
                <p class="text-success mt-1 flex items-center gap-1 text-xs font-medium">
                  <Check class="size-3" />
                  Ready to spend
                </p>
              </div>
    
              <div class="border-border bg-card rounded-xl border p-4 shadow-xs">
                <div class="text-muted-foreground mb-1 flex items-center justify-between text-xs">
                  <span>Total Value Loaded</span>
                  <ArrowDownLeft class="text-success size-3.5" />
                </div>
                <p class="text-foreground text-2xl font-bold tracking-tight tabular-nums">${{ totalLoaded.toFixed(2) }}</p>
                <p class="text-muted-foreground mt-1 text-xs">All top-ups & issue</p>
              </div>
    
              <div class="border-border bg-card rounded-xl border p-4 shadow-xs">
                <div class="text-muted-foreground mb-1 flex items-center justify-between text-xs">
                  <span>Total Redeemed</span>
                  <ArrowUpRight class="text-muted-foreground size-3.5" />
                </div>
                <p class="text-foreground text-2xl font-bold tracking-tight tabular-nums">-${{ totalSpent.toFixed(2) }}</p>
                <p class="text-muted-foreground mt-1 text-xs">Past store orders</p>
              </div>
            </div>
    
            <!-- Redemption & Activity History Card -->
            <Card class="border-border shadow-xs">
              <CardHeader class="pb-3">
                <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                  <div>
                    <CardTitle class="text-base font-semibold">Redemption & Activity History</CardTitle>
                    <CardDescription class="text-xs">
                      Detailed ledger of card issuance, promotional top-ups, and store checkouts.
                    </CardDescription>
                  </div>
    
                  <!-- Filter Pills -->
                  <div class="border-border bg-muted/40 flex items-center gap-1.5 rounded-lg border p-1 text-xs">
                    <button
                      type="button"
                      :class="
                        cn(
                          'rounded px-2.5 py-1 font-medium transition-colors',
                          filterType === 'all'
                            ? 'bg-background text-foreground shadow-xs'
                            : 'text-muted-foreground hover:text-foreground',
                        )
                      "
                      @click="filterType = 'all'"
                    >
                      All ({{ transactions.length }})
                    </button>
                    <button
                      type="button"
                      :class="
                        cn(
                          'rounded px-2.5 py-1 font-medium transition-colors',
                          filterType === 'redemptions'
                            ? 'bg-background text-foreground shadow-xs'
                            : 'text-muted-foreground hover:text-foreground',
                        )
                      "
                      @click="filterType = 'redemptions'"
                    >
                      Redemptions
                    </button>
                    <button
                      type="button"
                      :class="
                        cn(
                          'rounded px-2.5 py-1 font-medium transition-colors',
                          filterType === 'loads'
                            ? 'bg-background text-foreground shadow-xs'
                            : 'text-muted-foreground hover:text-foreground',
                        )
                      "
                      @click="filterType = 'loads'"
                    >
                      Loads
                    </button>
                  </div>
                </div>
              </CardHeader>
    
              <CardContent class="p-0">
                <div class="overflow-x-auto">
                  <Table>
                    <TableHeader>
                      <TableRow>
                        <TableHead class="w-[110px]">Date</TableHead>
                        <TableHead>Activity & Reference</TableHead>
                        <TableHead class="w-[90px] text-center">Type</TableHead>
                        <TableHead class="w-[100px] text-right">Amount</TableHead>
                        <TableHead class="w-[110px] text-right">Resulting Balance</TableHead>
                      </TableRow>
                    </TableHeader>
                    <TableBody>
                      <TableRow v-for="record in filteredTransactions" :key="record.id">
                        <TableCell class="text-muted-foreground text-xs font-medium">
                          {{ record.date }}
                        </TableCell>
                        <TableCell>
                          <div class="space-y-0.5">
                            <p class="text-foreground text-sm leading-none font-medium">
                              {{ record.title }}
                            </p>
                            <p v-if="record.subtitle" class="text-muted-foreground text-xs">
                              {{ record.subtitle }}
                            </p>
                          </div>
                        </TableCell>
                        <TableCell class="text-center">
                          <Badge
                            v-if="record.type === 'activation'"
                            variant="outline"
                            class="border-success/30 bg-success/10 text-success px-2 py-0.5 text-xs font-normal"
                          >
                            Activation
                          </Badge>
                          <Badge
                            v-else-if="record.type === 'reload'"
                            variant="outline"
                            class="border-info/30 bg-info/10 text-info px-2 py-0.5 text-xs font-normal"
                          >
                            Reload
                          </Badge>
                          <Badge v-else variant="secondary" class="px-2 py-0.5 text-xs font-normal"> Redemption </Badge>
                        </TableCell>
                        <TableCell
                          :class="
                            cn(
                              'text-right text-sm font-medium tabular-nums',
                              record.amount > 0 ? 'text-success' : 'text-foreground',
                            )
                          "
                        >
                          {{
                            record.amount > 0 ? `+$${record.amount.toFixed(2)}` : `-$${Math.abs(record.amount).toFixed(2)}`
                          }}
                        </TableCell>
                        <TableCell class="text-muted-foreground text-right font-mono text-sm tabular-nums">
                          ${{ record.balanceAfter.toFixed(2) }}
                        </TableCell>
                      </TableRow>
                    </TableBody>
                  </Table>
                </div>
              </CardContent>
    
              <CardFooter
                class="border-border text-muted-foreground flex flex-col gap-3 border-t p-4 text-xs sm:flex-row sm:items-center sm:justify-between"
              >
                <div class="flex items-center gap-1.5">
                  <ShieldCheck class="text-primary size-4 shrink-0" />
                  <span>Protected by 256-bit encryption · Zero liability policy</span>
                </div>
                <div class="flex items-center gap-3">
                  <button
                    type="button"
                    class="hover:text-foreground flex min-h-6 items-center gap-1 underline-offset-4 hover:underline"
                    @click="checkBalance"
                  >
                    <RefreshCw class="size-3" />
                    Refresh Ledger
                  </button>
                </div>
              </CardFooter>
            </Card>
    
            <!-- Digital Passbook Barcode Card -->
            <Card class="border-border bg-muted/20 shadow-xs">
              <CardContent class="flex flex-col items-center justify-between gap-4 p-4 sm:flex-row sm:p-5">
                <div class="space-y-1 text-center sm:text-left">
                  <h4 class="text-foreground flex items-center justify-center gap-2 text-sm font-semibold sm:justify-start">
                    <Wallet class="text-primary size-4" />
                    In-Store Digital Pass
                  </h4>
                  <p class="text-muted-foreground max-w-sm text-xs">
                    Present this barcode at checkout in any physical store location to scan and redeem card funds.
                  </p>
                </div>
    
                <!-- Barcode Simulation -->
                <div class="border-border bg-card flex flex-col items-center gap-1.5 rounded-lg border p-3 shadow-xs">
                  <div class="flex h-10 items-end gap-[3px] px-2" aria-hidden="true">
                    <div class="bg-foreground h-full w-[2px]" />
                    <div class="bg-foreground h-full w-[4px]" />
                    <div class="bg-foreground h-full w-[1px]" />
                    <div class="bg-foreground h-full w-[3px]" />
                    <div class="bg-foreground h-full w-[2px]" />
                    <div class="bg-foreground h-full w-[5px]" />
                    <div class="bg-foreground h-full w-[1px]" />
                    <div class="bg-foreground h-full w-[3px]" />
                    <div class="bg-foreground h-full w-[2px]" />
                    <div class="bg-foreground h-full w-[4px]" />
                    <div class="bg-foreground h-full w-[2px]" />
                    <div class="bg-foreground h-full w-[1px]" />
                    <div class="bg-foreground h-full w-[4px]" />
                    <div class="bg-foreground h-full w-[2px]" />
                    <div class="bg-foreground h-full w-[3px]" />
                  </div>
                  <span class="text-muted-foreground font-mono text-xs tracking-widest">
                    {{ cardNumber }}
                  </span>
                </div>
              </CardContent>
            </Card>
          </div>
        </div>
      </div>
    </template>
    

Raw manifest:https://uipkge.dev/r/vue/gift-card-balance-checker.json