@withpotter/components 0.13.0-next.84 → 0.13.0-next.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/billing/overview-tab.js +4 -3
  2. package/dist/billing/overview-tab.js.map +1 -1
  3. package/dist/billing/plans-tab.js +6 -12
  4. package/dist/billing/plans-tab.js.map +1 -1
  5. package/dist/dashboard/activity-feed.d.ts +2 -0
  6. package/dist/dashboard/activity-feed.js +3 -1
  7. package/dist/dashboard/activity-feed.js.map +1 -1
  8. package/dist/dashboard/activity-paid.d.ts +10 -0
  9. package/dist/dashboard/activity-paid.js +7 -0
  10. package/dist/dashboard/activity-paid.js.map +1 -0
  11. package/dist/dashboard/home/ask-controls.js +1 -0
  12. package/dist/dashboard/home/ask-controls.js.map +1 -1
  13. package/dist/dashboard/home/ask-mode-chip.js +4 -4
  14. package/dist/dashboard/home/ask-mode-chip.js.map +1 -1
  15. package/dist/dashboard/home/home-ask.js +3 -3
  16. package/dist/dashboard/home/home-ask.js.map +1 -1
  17. package/dist/dashboard/home/home-quick-actions.js +9 -1
  18. package/dist/dashboard/home/home-quick-actions.js.map +1 -1
  19. package/dist/dashboard/home/host-quick-actions.d.ts +19 -0
  20. package/dist/dashboard/home/host-quick-actions.js +12 -0
  21. package/dist/dashboard/home/host-quick-actions.js.map +1 -0
  22. package/dist/dashboard/home/widgets/activity-widget.js +7 -1
  23. package/dist/dashboard/home/widgets/activity-widget.js.map +1 -1
  24. package/dist/features/gift-card-artwork.js +1 -1
  25. package/dist/features/gift-card-artwork.js.map +1 -1
  26. package/dist/features/gift-cards/gift-card-logic.d.ts +41 -0
  27. package/dist/features/gift-cards/gift-card-logic.js +65 -0
  28. package/dist/features/gift-cards/gift-card-logic.js.map +1 -0
  29. package/dist/features/gift-cards/gift-cards-feature.js +656 -518
  30. package/dist/features/gift-cards/gift-cards-feature.js.map +1 -1
  31. package/dist/features/gift-cards/hint.d.ts +9 -0
  32. package/dist/features/gift-cards/hint.js +25 -0
  33. package/dist/features/gift-cards/hint.js.map +1 -0
  34. package/dist/features/product-form.js +1 -1
  35. package/dist/features/product-form.js.map +1 -1
  36. package/dist/mobile/screens/billing-flows.js +7 -6
  37. package/dist/mobile/screens/billing-flows.js.map +1 -1
  38. package/dist/mobile/screens/billing.js +2 -2
  39. package/dist/mobile/screens/billing.js.map +1 -1
  40. package/dist/sidebar/nav-renderers.js +2 -0
  41. package/dist/sidebar/nav-renderers.js.map +1 -1
  42. package/dist/sidebar/nav-section.js +5 -3
  43. package/dist/sidebar/nav-section.js.map +1 -1
  44. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/features/gift-cards/gift-cards-feature.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n Copy,\n Download,\n Gift,\n History,\n Mail,\n Plus,\n Search,\n X,\n} from 'lucide-react';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { normalizeCurrencyCode } from '@withpotter/lib/utils';\nimport { InlineDeleteButton } from '@withpotter/ui-kit/inline-delete-button';\nimport {\n giftCardsApi,\n communicationsApi,\n type CreateGiftCardData,\n type GiftCard,\n type GiftCardStats,\n type GiftCardStatus,\n} from '@withpotter/lib/api';\nimport { useFormatCurrency } from '@withpotter/hooks/use-format-currency';\nimport { useQuietPoll } from '@withpotter/hooks/use-quiet-poll';\nimport { InlineListSkeleton } from '@withpotter/ui-kit/page-skeletons';\nimport { RefreshButton } from '@withpotter/ui-kit/refresh-button';\nimport { TableEmptyState } from '@withpotter/ui-kit/table-empty-state';\nimport { TablePagination } from '@withpotter/ui-kit/table-pagination';\nimport { useToast } from '@withpotter/ui-kit/use-toast';\nimport { MobilePageHeader } from '../../mobile/mobile-page-header';\nimport { useRegisterRefresh } from '../../mobile/pull-to-refresh-context';\nimport { MetricRow, MetricTile } from '@withpotter/ui-kit/metric-row';\nimport {\n createGiftCardEmailHtml,\n downloadGiftCardArtwork,\n GiftCardArtwork,\n GIFT_CARD_PRESETS,\n} from '../gift-card-artwork';\n\nconst PAGE_SIZE = 20;\n\n/**\n * `NEXT_PUBLIC_BOX_GIFT_CARDS_LIVE=true`: the list keeps itself current, re-read\n * every two seconds while the tab is visible, and the Reload button goes.\n * Unset, the page reads once and keeps its Reload button.\n */\nconst LIVE = process.env.NEXT_PUBLIC_BOX_GIFT_CARDS_LIVE === 'true';\nconst LIVE_REFRESH_MS = 2_000;\n\nfunction minimumExpirationDate(): string {\n const tomorrow = new Date();\n tomorrow.setDate(tomorrow.getDate() + 1);\n return tomorrow.toISOString().slice(0, 10);\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : 'Something went wrong';\n}\n\nfunction dateLabel(value: string | null): string {\n if (!value) return 'Never';\n return new Date(value).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n}\n\nfunction statusClass(status: GiftCardStatus): string {\n switch (status) {\n case 'active':\n return 'bg-[var(--success-bg)] text-[var(--success)]';\n case 'used':\n return 'bg-[var(--info-bg)] text-[var(--info)]';\n case 'expired':\n return 'bg-[var(--error-bg)] text-[var(--error)]';\n case 'cancelled':\n return 'bg-[var(--bg-muted)] text-[var(--text-tertiary)]';\n }\n}\n\ninterface ModalProps {\n title: string;\n onClose: () => void;\n children: React.ReactNode;\n}\n\nfunction Modal({ title, onClose, children }: ModalProps) {\n return (\n <div className=\"fixed inset-0 z-50 flex items-end justify-center bg-black/40 sm:items-center sm:p-4\">\n <div className=\"max-h-[92vh] w-full overflow-y-auto bg-[var(--bg-base)] sm:max-w-xl sm:rounded-md sm:border sm:border-[var(--border-default)]\">\n <div className=\"sticky top-0 z-10 flex items-center justify-between border-b border-[var(--border-default)] bg-[var(--bg-base)] px-4 py-3\">\n <h2 className=\"text-base font-semibold text-[var(--text-primary)]\">\n {title}\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-md p-2 text-[var(--text-tertiary)] hover:bg-[var(--interactive-hover)]\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n {children}\n </div>\n </div>\n );\n}\n\ninterface IssueGiftCardModalProps {\n currency: string;\n merchantName: string;\n onClose: () => void;\n onCreated: (giftCard: GiftCard) => void;\n}\n\nfunction IssueGiftCardModal({\n currency,\n merchantName,\n onClose,\n onCreated,\n}: IssueGiftCardModalProps) {\n const { tenant } = useAuth();\n const [form, setForm] = useState<CreateGiftCardData>({\n originalAmount: 0,\n currency,\n design: GIFT_CARD_PRESETS[0],\n });\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const setField = <Key extends keyof CreateGiftCardData>(\n key: Key,\n value: CreateGiftCardData[Key],\n ) => setForm((current) => ({ ...current, [key]: value }));\n\n const submit = async (event: React.FormEvent) => {\n event.preventDefault();\n if (!tenant?.slug || form.originalAmount <= 0) return;\n setSaving(true);\n setError(null);\n try {\n const payload = Object.fromEntries(\n Object.entries(form).filter(([, value]) => value !== ''),\n ) as unknown as CreateGiftCardData;\n const giftCard = await giftCardsApi.create(tenant.slug, payload);\n onCreated(giftCard);\n } catch (submitError) {\n setError(errorMessage(submitError));\n } finally {\n setSaving(false);\n }\n };\n\n const inputClass =\n 'w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm text-[var(--text-primary)] focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20';\n\n return (\n <Modal title=\"Issue gift card\" onClose={onClose}>\n <form onSubmit={submit} className=\"space-y-5 p-4\">\n <div className=\"grid gap-4 sm:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] sm:items-start\">\n <GiftCardArtwork\n design={form.design ?? GIFT_CARD_PRESETS[0]}\n merchantName={merchantName}\n amount={\n form.originalAmount > 0\n ? new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: form.currency || currency,\n }).format(form.originalAmount)\n : undefined\n }\n recipientName={form.recipientName}\n senderName={form.purchaserName}\n />\n <fieldset>\n <legend className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Design\n </legend>\n <div className=\"mt-2 grid grid-cols-4 gap-2 sm:grid-cols-1\">\n {GIFT_CARD_PRESETS.map((design) => {\n const selected = form.design?.designId === design.designId;\n return (\n <button\n key={design.designId}\n type=\"button\"\n onClick={() => setField('design', design)}\n className={`flex min-w-0 items-center gap-2 rounded-md border p-2 text-left text-xs font-medium ${\n selected\n ? 'border-brand-500 bg-brand-500/10 text-[var(--text-primary)]'\n : 'border-[var(--border-default)] text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]'\n }`}\n aria-label={design.name}\n aria-pressed={selected}\n >\n <span\n className=\"h-5 w-5 shrink-0 rounded-sm border border-black/10\"\n style={{ backgroundColor: design.palette.background }}\n />\n <span className=\"hidden truncate sm:block\">{design.name}</span>\n </button>\n );\n })}\n </div>\n </fieldset>\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <label className=\"space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Amount\n </span>\n <input\n className={inputClass}\n type=\"number\"\n min=\"0.01\"\n step=\"0.01\"\n required\n value={form.originalAmount || ''}\n onChange={(event) =>\n setField('originalAmount', Number(event.target.value))\n }\n />\n </label>\n <label className=\"space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Currency\n </span>\n <input\n className={inputClass}\n readOnly\n required\n value={form.currency}\n />\n </label>\n <label className=\"space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Recipient name\n </span>\n <input\n className={inputClass}\n value={form.recipientName ?? ''}\n onChange={(event) => setField('recipientName', event.target.value)}\n />\n <span className=\"block text-xs text-[var(--text-muted)]\">\n The person who receives the gift card and spends it.\n </span>\n </label>\n <label className=\"space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Recipient email\n </span>\n <input\n className={inputClass}\n type=\"email\"\n value={form.recipientEmail ?? ''}\n onChange={(event) =>\n setField('recipientEmail', event.target.value)\n }\n />\n </label>\n <label className=\"space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Purchaser name\n </span>\n <input\n className={inputClass}\n value={form.purchaserName ?? ''}\n onChange={(event) => setField('purchaserName', event.target.value)}\n />\n <span className=\"block text-xs text-[var(--text-muted)]\">\n The person paying for the gift card.\n </span>\n </label>\n <label className=\"space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Purchaser email\n </span>\n <input\n className={inputClass}\n type=\"email\"\n value={form.purchaserEmail ?? ''}\n onChange={(event) =>\n setField('purchaserEmail', event.target.value)\n }\n />\n </label>\n </div>\n <label className=\"block space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Personal message\n </span>\n <textarea\n className={`${inputClass} min-h-24 resize-y`}\n maxLength={500}\n value={form.personalMessage ?? ''}\n onChange={(event) => setField('personalMessage', event.target.value)}\n />\n </label>\n <label className=\"block space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Expiration date\n </span>\n <input\n className={inputClass}\n type=\"date\"\n min={minimumExpirationDate()}\n value={form.expiresAt ?? ''}\n onChange={(event) => setField('expiresAt', event.target.value)}\n />\n </label>\n {error && (\n <p className=\"rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]\">\n {error}\n </p>\n )}\n <div className=\"flex justify-end gap-2 border-t border-[var(--border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-md border border-[var(--border-default)] px-4 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={saving || form.originalAmount <= 0}\n className=\"rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:opacity-50\"\n >\n {saving ? 'Issuing...' : 'Issue gift card'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\ninterface BalanceModalProps {\n onClose: () => void;\n initialCode?: string;\n}\n\nfunction BalanceModal({ onClose, initialCode = '' }: BalanceModalProps) {\n const { tenant } = useAuth();\n const formatCurrency = useFormatCurrency();\n const [code, setCode] = useState(initialCode);\n const [checking, setChecking] = useState(false);\n const [result, setResult] = useState<{\n valid: boolean;\n balance?: number;\n error?: string;\n } | null>(null);\n\n const submit = async (event: React.FormEvent) => {\n event.preventDefault();\n if (!tenant?.slug || !code.trim()) return;\n setChecking(true);\n try {\n setResult(\n await giftCardsApi.checkBalance(tenant.slug, code.trim().toUpperCase()),\n );\n } catch (checkError) {\n setResult({ valid: false, error: errorMessage(checkError) });\n } finally {\n setChecking(false);\n }\n };\n\n return (\n <Modal title=\"Check gift card balance\" onClose={onClose}>\n <form onSubmit={submit} className=\"space-y-4 p-4\">\n <label className=\"block space-y-1.5\">\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Gift card code\n </span>\n <input\n autoFocus\n className=\"w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 font-mono text-sm uppercase text-[var(--text-primary)] focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20\"\n value={code}\n onChange={(event) => {\n setCode(event.target.value.toUpperCase());\n setResult(null);\n }}\n />\n </label>\n {result && (\n <div\n className={`rounded-md p-4 ${\n result.valid\n ? 'bg-[var(--success-bg)] text-[var(--success)]'\n : 'bg-[var(--error-bg)] text-[var(--error)]'\n }`}\n >\n {result.valid\n ? `Available balance: ${formatCurrency(result.balance ?? 0)}`\n : result.error || 'This gift card is not valid.'}\n </div>\n )}\n <button\n type=\"submit\"\n disabled={checking || !code.trim()}\n className=\"w-full rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:opacity-50\"\n >\n {checking ? 'Checking...' : 'Check balance'}\n </button>\n </form>\n </Modal>\n );\n}\n\ninterface GiftCardDetailModalProps {\n giftCard: GiftCard;\n onClose: () => void;\n onChanged: () => void;\n}\n\nfunction GiftCardDetailModal({\n giftCard,\n onClose,\n onChanged,\n}: GiftCardDetailModalProps) {\n const { tenant } = useAuth();\n const formatCurrency = useFormatCurrency();\n const { toast } = useToast();\n const [amount, setAmount] = useState('');\n const [referenceId, setReferenceId] = useState('');\n const [note, setNote] = useState('');\n const [saving, setSaving] = useState(false);\n const [sharing, setSharing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const copyCode = async () => {\n await navigator.clipboard.writeText(giftCard.code);\n toast({ title: 'Gift card code copied', description: giftCard.code });\n };\n\n const artworkData = {\n design: giftCard.design ?? GIFT_CARD_PRESETS[0],\n merchantName: tenant?.businessName || 'Gift card',\n amount: formatCurrency(Number(giftCard.originalAmount), giftCard.currency),\n recipientName: giftCard.recipientName ?? undefined,\n senderName: giftCard.purchaserName ?? undefined,\n code: giftCard.code,\n };\n\n const sendByEmail = async () => {\n if (!tenant?.slug || !giftCard.recipientEmail) return;\n setSharing(true);\n setError(null);\n try {\n const result = await communicationsApi.sendCustomEmail(tenant.slug, {\n recipientEmail: giftCard.recipientEmail,\n recipientName: giftCard.recipientName ?? undefined,\n subject: `${giftCard.purchaserName || tenant.businessName} sent you a gift card`,\n htmlContent: createGiftCardEmailHtml(artworkData),\n metadata: { giftCardId: giftCard.id, kind: 'gift_card_delivery' },\n });\n if (!result.success) throw new Error(result.error || 'Email delivery failed');\n toast({ title: 'Gift card email sent', description: giftCard.recipientEmail });\n } catch (sendError) {\n setError(errorMessage(sendError));\n } finally {\n setSharing(false);\n }\n };\n\n const redeem = async (event: React.FormEvent) => {\n event.preventDefault();\n if (!tenant?.slug) return;\n setSaving(true);\n setError(null);\n try {\n const result = await giftCardsApi.redeem(tenant.slug, giftCard.code, {\n amount: Number(amount),\n ...(referenceId ? { referenceId } : {}),\n ...(note ? { note } : {}),\n });\n if (!result.success) throw new Error(result.error || 'Redemption failed');\n toast({ title: 'Gift card redeemed' });\n onChanged();\n onClose();\n } catch (redeemError) {\n setError(errorMessage(redeemError));\n } finally {\n setSaving(false);\n }\n };\n\n const remove = async () => {\n if (!tenant?.slug) return;\n setSaving(true);\n try {\n await giftCardsApi.delete(tenant.slug, giftCard.id);\n toast({ title: 'Gift card cancelled' });\n onChanged();\n onClose();\n } catch (deleteError) {\n setError(errorMessage(deleteError));\n setSaving(false);\n }\n };\n\n return (\n <Modal title=\"Gift card details\" onClose={onClose}>\n <div className=\"space-y-5 p-4\">\n <GiftCardArtwork {...artworkData} />\n <div className=\"grid grid-cols-2 gap-2\">\n <button\n type=\"button\"\n onClick={() => downloadGiftCardArtwork(artworkData)}\n className=\"flex items-center justify-center gap-2 rounded-md border border-[var(--border-default)] px-3 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]\"\n >\n <Download className=\"h-4 w-4\" />\n Download\n </button>\n <button\n type=\"button\"\n onClick={sendByEmail}\n disabled={sharing || !giftCard.recipientEmail}\n title={giftCard.recipientEmail ? undefined : 'Add a recipient email to send this card'}\n className=\"flex items-center justify-center gap-2 rounded-md bg-brand-500 px-3 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Mail className=\"h-4 w-4\" />\n {sharing ? 'Sending...' : 'Send by email'}\n </button>\n </div>\n <div className=\"rounded-md border border-[var(--border-default)] bg-[var(--bg-subtle)] p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <p className=\"text-xs font-medium uppercase text-[var(--text-muted)]\">\n Code\n </p>\n <p className=\"mt-1 font-mono text-xl font-semibold text-[var(--text-primary)]\">\n {giftCard.code}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={copyCode}\n className=\"rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] p-2 text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]\"\n title=\"Copy code\"\n >\n <Copy className=\"h-4 w-4\" />\n </button>\n </div>\n <div className=\"mt-4 grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Balance</p>\n <p className=\"font-semibold text-[var(--text-primary)]\">\n {formatCurrency(Number(giftCard.balance), giftCard.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Issued value</p>\n <p className=\"font-semibold text-[var(--text-primary)]\">\n {formatCurrency(\n Number(giftCard.originalAmount),\n giftCard.currency,\n )}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Recipient</p>\n <p className=\"text-sm text-[var(--text-primary)]\">\n {giftCard.recipientName || giftCard.recipientEmail || 'Walk-in'}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Expires</p>\n <p className=\"text-sm text-[var(--text-primary)]\">\n {dateLabel(giftCard.expiresAt)}\n </p>\n </div>\n </div>\n </div>\n\n {giftCard.status === 'active' && Number(giftCard.balance) > 0 && (\n <form onSubmit={redeem} className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)]\">\n Redeem value\n </h3>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <input\n type=\"number\"\n min=\"0.01\"\n max={Number(giftCard.balance)}\n step=\"0.01\"\n required\n placeholder=\"Amount\"\n value={amount}\n onChange={(event) => setAmount(event.target.value)}\n className=\"rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm focus:border-brand-500 focus:outline-none\"\n />\n <input\n placeholder=\"Order or reference ID\"\n value={referenceId}\n onChange={(event) => setReferenceId(event.target.value)}\n className=\"rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm focus:border-brand-500 focus:outline-none\"\n />\n </div>\n <input\n placeholder=\"Note (optional)\"\n value={note}\n onChange={(event) => setNote(event.target.value)}\n className=\"w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm focus:border-brand-500 focus:outline-none\"\n />\n <button\n type=\"submit\"\n disabled={saving || !amount || Number(amount) <= 0}\n className=\"rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:opacity-50\"\n >\n {saving ? 'Redeeming...' : 'Redeem gift card'}\n </button>\n </form>\n )}\n\n <div>\n <div className=\"mb-2 flex items-center gap-2\">\n <History className=\"h-4 w-4 text-[var(--text-tertiary)]\" />\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)]\">\n Redemption history\n </h3>\n </div>\n {giftCard.redemptions.length === 0 ? (\n <p className=\"text-sm text-[var(--text-muted)]\">No redemptions yet.</p>\n ) : (\n <div className=\"divide-y divide-[var(--border-default)] rounded-md border border-[var(--border-default)]\">\n {giftCard.redemptions.map((redemption) => (\n <div\n key={redemption.id}\n className=\"flex items-center justify-between gap-3 px-3 py-2.5\"\n >\n <div>\n <p className=\"text-sm text-[var(--text-primary)]\">\n {redemption.orderId || 'Manual redemption'}\n </p>\n <p className=\"text-xs text-[var(--text-muted)]\">\n {dateLabel(redemption.redeemedAt)}\n </p>\n </div>\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">\n -{formatCurrency(Number(redemption.amount), giftCard.currency)}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {error && (\n <p className=\"rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]\">\n {error}\n </p>\n )}\n <div className=\"flex justify-end border-t border-[var(--border-default)] pt-4\">\n <InlineDeleteButton\n onDelete={remove}\n disabled={saving}\n title=\"Cancel gift card\"\n confirmText=\"Cancel this gift card?\"\n className=\"text-[var(--error)]\"\n />\n </div>\n </div>\n </Modal>\n );\n}\n\nexport function GiftCardsFeature() {\n const { tenant } = useAuth();\n const formatCurrency = useFormatCurrency();\n const { toast } = useToast();\n const currency = normalizeCurrencyCode(\n (tenant?.settings as Record<string, unknown> | undefined)?.currency,\n );\n const [giftCards, setGiftCards] = useState<GiftCard[]>([]);\n const [stats, setStats] = useState<GiftCardStats | null>(null);\n const [loading, setLoading] = useState(true);\n const [search, setSearch] = useState('');\n const [status, setStatus] = useState<GiftCardStatus | ''>('');\n const [page, setPage] = useState(1);\n const [count, setCount] = useState(0);\n const [showIssue, setShowIssue] = useState(false);\n const [showBalance, setShowBalance] = useState(false);\n const [selected, setSelected] = useState<GiftCard | null>(null);\n\n // Only the newest read may land: a quiet re-read started before the filters\n // changed must not put the old filter's rows back on screen.\n const latestRead = useRef(0);\n const readGiftCards = useCallback(\n async (quiet: boolean) => {\n if (!tenant?.slug) return;\n // A page's own read moves the marker; a quiet re-read only checks it.\n const read = quiet ? latestRead.current : ++latestRead.current;\n if (!quiet) setLoading(true);\n try {\n const [list, giftCardStats] = await Promise.all([\n giftCardsApi.list(tenant.slug, {\n ...(status ? { status } : {}),\n ...(search.trim() ? { search: search.trim() } : {}),\n page,\n limit: PAGE_SIZE,\n }),\n giftCardsApi.stats(tenant.slug),\n ]);\n if (read !== latestRead.current) return;\n setGiftCards(list.rows);\n setCount(list.count);\n setStats(giftCardStats);\n } catch (loadError) {\n // A quiet re-read keeps what is on screen and says nothing; the poll\n // counts the failure and slows down.\n if (quiet) throw loadError;\n toast({\n title: 'Unable to load gift cards',\n description: errorMessage(loadError),\n });\n } finally {\n if (!quiet) setLoading(false);\n }\n },\n [page, search, status, tenant?.slug, toast],\n );\n const loadGiftCards = useCallback(() => readGiftCards(false), [readGiftCards]);\n\n useEffect(() => {\n loadGiftCards();\n }, [loadGiftCards]);\n\n useQuietPoll(\n useCallback(() => readGiftCards(true), [readGiftCards]),\n LIVE_REFRESH_MS,\n LIVE,\n );\n\n useRegisterRefresh(loadGiftCards);\n\n const copyCode = async (event: React.MouseEvent, code: string) => {\n event.stopPropagation();\n await navigator.clipboard.writeText(code);\n toast({ title: 'Gift card code copied', description: code });\n };\n\n const totalPages = Math.max(1, Math.ceil(count / PAGE_SIZE));\n const filters: Array<{ value: GiftCardStatus | ''; label: string }> = [\n { value: '', label: 'All' },\n { value: 'active', label: 'Active' },\n { value: 'used', label: 'Used' },\n { value: 'expired', label: 'Expired' },\n { value: 'cancelled', label: 'Cancelled' },\n ];\n\n return (\n <div className=\"mx-auto max-w-7xl space-y-5 lg:space-y-6\">\n <MobilePageHeader\n title=\"Gift cards\"\n action={{ label: 'Issue gift card', onClick: () => setShowIssue(true) }}\n />\n <div className=\"hidden items-center justify-between gap-3 lg:flex\">\n <div>\n <h1 className=\"text-2xl font-semibold text-[var(--text-primary)]\">\n Gift cards\n </h1>\n <p className=\"mt-1 text-sm text-[var(--text-tertiary)]\">\n Credit a customer pays for now and spends later. Discounts and\n loyalty points cost the customer nothing.\n </p>\n </div>\n <div className=\"flex gap-2\">\n {!LIVE && (\n <RefreshButton onClick={loadGiftCards} refreshing={loading} />\n )}\n <button\n type=\"button\"\n onClick={() => setShowBalance(true)}\n className=\"flex items-center gap-2 rounded-md border border-[var(--border-default)] px-3 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]\"\n >\n <Search className=\"h-4 w-4\" />\n Check balance\n </button>\n <button\n type=\"button\"\n onClick={() => setShowIssue(true)}\n className=\"flex items-center gap-2 rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600\"\n >\n <Plus className=\"h-4 w-4\" />\n Issue gift card\n </button>\n </div>\n </div>\n\n <div className=\"hidden lg:block\">\n <MetricRow>\n <MetricTile\n label=\"Issued value\"\n value={formatCurrency(stats?.totalIssued ?? 0, currency)}\n />\n <MetricTile\n label=\"Outstanding\"\n value={formatCurrency(stats?.outstanding ?? 0, currency)}\n />\n <MetricTile\n label=\"Redeemed\"\n value={formatCurrency(stats?.redeemed ?? 0, currency)}\n />\n <MetricTile label=\"Active cards\" value={stats?.activeCards ?? 0} />\n </MetricRow>\n </div>\n\n <div className=\"grid grid-cols-2 gap-px overflow-hidden rounded-md bg-[var(--border-default)] lg:hidden\">\n <div className=\"bg-[var(--bg-subtle)] p-4\">\n <p className=\"text-xs text-[var(--text-muted)]\">Outstanding</p>\n <p className=\"mt-1 text-lg font-semibold text-[var(--text-primary)]\">\n {formatCurrency(stats?.outstanding ?? 0, currency)}\n </p>\n </div>\n <div className=\"bg-[var(--bg-subtle)] p-4\">\n <p className=\"text-xs text-[var(--text-muted)]\">Active cards</p>\n <p className=\"mt-1 text-lg font-semibold text-[var(--text-primary)]\">\n {stats?.activeCards ?? 0}\n </p>\n </div>\n </div>\n\n <div className=\"flex gap-2 overflow-x-auto lg:hidden\">\n <button\n type=\"button\"\n onClick={() => setShowIssue(true)}\n className=\"flex shrink-0 items-center gap-1.5 rounded-full bg-brand-500 px-3.5 py-2 text-sm font-medium text-[var(--text-inverse)]\"\n >\n <Plus className=\"h-4 w-4\" />\n Issue gift card\n </button>\n <button\n type=\"button\"\n onClick={() => setShowBalance(true)}\n className=\"flex shrink-0 items-center gap-1.5 rounded-full bg-[var(--bg-subtle)] px-3.5 py-2 text-sm font-medium text-[var(--text-secondary)]\"\n >\n <Search className=\"h-4 w-4\" />\n Check balance\n </button>\n </div>\n\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[var(--text-muted)]\" />\n <input\n type=\"search\"\n placeholder=\"Search code, recipient, or email\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setPage(1);\n }}\n className=\"w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] py-2 pl-10 pr-3 text-sm focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20\"\n />\n </div>\n <div className=\"flex gap-2 overflow-x-auto\">\n {filters.map((filter) => (\n <button\n key={filter.value}\n type=\"button\"\n onClick={() => {\n setStatus(filter.value);\n setPage(1);\n }}\n className={`shrink-0 rounded-md px-3 py-2 text-sm font-medium ${\n status === filter.value\n ? 'bg-brand-500 text-[var(--text-inverse)]'\n : 'border border-[var(--border-default)] bg-[var(--bg-base)] text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]'\n }`}\n >\n {filter.label}\n </button>\n ))}\n </div>\n </div>\n\n {loading && giftCards.length === 0 && <InlineListSkeleton />}\n\n {!loading && giftCards.length === 0 && (\n <TableEmptyState\n icon={Gift}\n title={search || status ? 'No gift cards found' : 'Issue your first gift card'}\n description={\n search || status\n ? 'Try changing the search or status filter'\n : 'Create a stored-value card for a customer or walk-in purchase'\n }\n />\n )}\n\n {giftCards.length > 0 && (\n <>\n <div className=\"space-y-2 lg:hidden\">\n {giftCards.map((giftCard) => (\n <button\n key={giftCard.id}\n type=\"button\"\n onClick={() => setSelected(giftCard)}\n className=\"flex w-full items-center justify-between gap-3 rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] p-4 text-left\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate font-mono text-sm font-semibold text-[var(--text-primary)]\">\n {giftCard.code}\n </p>\n <p className=\"truncate text-sm text-[var(--text-tertiary)]\">\n {giftCard.recipientName || giftCard.recipientEmail || 'Walk-in'}\n </p>\n </div>\n <div className=\"shrink-0 text-right\">\n <p className=\"text-sm font-semibold text-[var(--text-primary)]\">\n {formatCurrency(Number(giftCard.balance), giftCard.currency)}\n </p>\n <span className={`text-xs capitalize ${statusClass(giftCard.status)}`}>\n {giftCard.status}\n </span>\n </div>\n </button>\n ))}\n </div>\n\n <div className=\"hidden overflow-x-auto rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] lg:block\">\n <table className=\"w-full min-w-[760px]\">\n <thead className=\"border-b border-[var(--border-default)] bg-[var(--bg-subtle)]\">\n <tr>\n {['Code', 'Recipient', 'Issued', 'Balance', 'Expires', 'Status'].map(\n (heading) => (\n <th\n key={heading}\n className=\"px-4 py-3 text-left text-xs font-medium uppercase text-[var(--text-tertiary)]\"\n >\n {heading}\n </th>\n ),\n )}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--border-default)]\">\n {giftCards.map((giftCard) => (\n <tr\n key={giftCard.id}\n onClick={() => setSelected(giftCard)}\n className=\"cursor-pointer hover:bg-[var(--interactive-hover)]\"\n >\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-mono text-sm font-medium text-[var(--text-primary)]\">\n {giftCard.code}\n </span>\n <button\n type=\"button\"\n onClick={(event) => copyCode(event, giftCard.code)}\n className=\"rounded p-1 text-[var(--text-muted)] hover:text-[var(--text-primary)]\"\n title=\"Copy code\"\n >\n <Copy className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <p className=\"text-sm text-[var(--text-primary)]\">\n {giftCard.recipientName || 'Walk-in'}\n </p>\n <p className=\"text-xs text-[var(--text-muted)]\">\n {giftCard.recipientEmail}\n </p>\n </td>\n <td className=\"px-4 py-3 text-sm text-[var(--text-secondary)]\">\n {formatCurrency(\n Number(giftCard.originalAmount),\n giftCard.currency,\n )}\n </td>\n <td className=\"px-4 py-3 text-sm font-medium text-[var(--text-primary)]\">\n {formatCurrency(Number(giftCard.balance), giftCard.currency)}\n </td>\n <td className=\"px-4 py-3 text-sm text-[var(--text-secondary)]\">\n {dateLabel(giftCard.expiresAt)}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex rounded-full px-2.5 py-1 text-xs font-medium capitalize ${statusClass(giftCard.status)}`}\n >\n {giftCard.status}\n </span>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n <TablePagination\n page={page}\n totalPages={totalPages}\n pageSize={PAGE_SIZE}\n totalItems={count}\n entityName=\"gift cards\"\n onPageChange={setPage}\n />\n </>\n )}\n\n {showIssue && (\n <IssueGiftCardModal\n currency={currency}\n merchantName={tenant?.businessName || 'Gift card'}\n onClose={() => setShowIssue(false)}\n onCreated={(giftCard) => {\n setShowIssue(false);\n setSelected(giftCard);\n toast({\n title: 'Gift card issued',\n description: giftCard.code,\n });\n loadGiftCards();\n }}\n />\n )}\n {showBalance && <BalanceModal onClose={() => setShowBalance(false)} />}\n {selected && (\n <GiftCardDetailModal\n giftCard={selected}\n onClose={() => setSelected(null)}\n onChanged={loadGiftCards}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/features/gift-cards/gift-cards-feature.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { Copy, Download, Gift, History, Mail, Plus, Search } from 'lucide-react';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { normalizeCurrencyCode } from '@withpotter/lib/utils';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { Field } from '@withpotter/ui-kit/field';\nimport { FormDialog } from '@withpotter/ui-kit/form-dialog';\nimport { InlineDeleteButton } from '@withpotter/ui-kit/inline-delete-button';\nimport { Input } from '@withpotter/ui-kit/input';\nimport { ListPage } from '@withpotter/ui-kit/list-page';\nimport { Textarea } from '@withpotter/ui-kit/textarea';\nimport { Tooltip } from '@withpotter/ui-kit/tooltip';\nimport {\n giftCardsApi,\n giftcardsApi,\n communicationsApi,\n type CreateGiftCardData,\n type GiftCard,\n type GiftCardStats,\n type GiftCardStatus,\n} from '@withpotter/lib/api';\nimport { useFormatCurrency } from '@withpotter/hooks/use-format-currency';\nimport { useQuietPoll } from '@withpotter/hooks/use-quiet-poll';\nimport { InlineListSkeleton } from '@withpotter/ui-kit/page-skeletons';\nimport { RefreshButton } from '@withpotter/ui-kit/refresh-button';\nimport { TableEmptyState } from '@withpotter/ui-kit/table-empty-state';\nimport { TablePagination } from '@withpotter/ui-kit/table-pagination';\nimport { useToast } from '@withpotter/ui-kit/use-toast';\nimport { MobilePageHeader } from '../../mobile/mobile-page-header';\nimport { useRegisterRefresh } from '../../mobile/pull-to-refresh-context';\nimport { MetricRow, MetricTile } from '@withpotter/ui-kit/metric-row';\nimport {\n createGiftCardEmailHtml,\n downloadGiftCardArtwork,\n GiftCardArtwork,\n GIFT_CARD_PRESETS,\n} from '../gift-card-artwork';\nimport {\n createInFlightDeduper,\n currencySymbol,\n isFirstRun,\n PAGE_HINT_LINES,\n STATUS_FILTERS,\n STATUS_LABEL,\n} from './gift-card-logic';\nimport { Hint } from './hint';\n\nconst PAGE_SIZE = 20;\n\n/**\n * `NEXT_PUBLIC_BOX_GIFT_CARDS_LIVE=true`: the list keeps itself current, re-read\n * every two seconds while the tab is visible, and the Reload button goes.\n * Unset, the page reads once and keeps its Reload button.\n */\nconst LIVE = process.env.NEXT_PUBLIC_BOX_GIFT_CARDS_LIVE === 'true';\nconst LIVE_REFRESH_MS = 2_000;\n\nfunction minimumExpirationDate(): string {\n const tomorrow = new Date();\n tomorrow.setDate(tomorrow.getDate() + 1);\n return tomorrow.toISOString().slice(0, 10);\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : 'Something went wrong';\n}\n\nfunction dateLabel(value: string | null): string {\n if (!value) return 'Never';\n return new Date(value).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n}\n\nfunction statusClass(status: GiftCardStatus): string {\n switch (status) {\n case 'active':\n return 'bg-[var(--success-bg)] text-[var(--success)]';\n case 'used':\n return 'bg-[var(--info-bg)] text-[var(--info)]';\n case 'expired':\n return 'bg-[var(--error-bg)] text-[var(--error)]';\n case 'cancelled':\n return 'bg-[var(--bg-muted)] text-[var(--text-tertiary)]';\n }\n}\n\ninterface IssueGiftCardModalProps {\n currency: string;\n merchantName: string;\n onClose: () => void;\n onCreated: (giftCard: GiftCard) => void;\n}\n\nfunction IssueGiftCardModal({\n currency,\n merchantName,\n onClose,\n onCreated,\n}: IssueGiftCardModalProps) {\n const { tenant } = useAuth();\n const formId = useId();\n const ids = {\n amount: `${formId}-amount`,\n recipientName: `${formId}-recipient-name`,\n recipientEmail: `${formId}-recipient-email`,\n purchaserName: `${formId}-purchaser-name`,\n purchaserEmail: `${formId}-purchaser-email`,\n message: `${formId}-message`,\n expiresAt: `${formId}-expires-at`,\n };\n const [form, setForm] = useState<CreateGiftCardData>({\n originalAmount: 0,\n currency,\n design: GIFT_CARD_PRESETS[0],\n });\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const setField = <Key extends keyof CreateGiftCardData>(\n key: Key,\n value: CreateGiftCardData[Key],\n ) => setForm((current) => ({ ...current, [key]: value }));\n\n const submit = async (event: React.FormEvent) => {\n event.preventDefault();\n if (!tenant?.slug || form.originalAmount <= 0) return;\n setSaving(true);\n setError(null);\n try {\n const payload = Object.fromEntries(\n Object.entries(form).filter(([, value]) => value !== ''),\n ) as unknown as CreateGiftCardData;\n const giftCard = await giftCardsApi.create(tenant.slug, payload);\n onCreated(giftCard);\n } catch (submitError) {\n setError(errorMessage(submitError));\n } finally {\n setSaving(false);\n }\n };\n\n const symbol = currencySymbol(form.currency || currency);\n\n return (\n <FormDialog\n title=\"Issue gift card\"\n description=\"We create the code for you. Fields marked * are required.\"\n size=\"lg\"\n className=\"max-w-4xl\"\n onClose={onClose}\n footer={\n <div className=\"flex justify-end gap-2\">\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n form={formId}\n disabled={saving || form.originalAmount <= 0}\n >\n {saving ? 'Issuing...' : 'Issue gift card'}\n </Button>\n </div>\n }\n >\n <form\n id={formId}\n onSubmit={submit}\n className=\"grid gap-6 md:grid-cols-[17rem_minmax(0,1fr)]\"\n >\n <div className=\"space-y-4 md:self-start\">\n <GiftCardArtwork\n design={form.design ?? GIFT_CARD_PRESETS[0]}\n merchantName={merchantName}\n amount={\n form.originalAmount > 0\n ? new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: form.currency || currency,\n }).format(form.originalAmount)\n : undefined\n }\n recipientName={form.recipientName}\n senderName={form.purchaserName}\n />\n <fieldset>\n <legend className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Design\n </legend>\n <div className=\"mt-2 grid grid-cols-4 gap-2 md:grid-cols-2\">\n {GIFT_CARD_PRESETS.map((design) => {\n const selected = form.design?.designId === design.designId;\n return (\n <button\n key={design.designId}\n type=\"button\"\n onClick={() => setField('design', design)}\n className={`flex min-w-0 items-center gap-2 rounded-md border p-2 text-left text-xs font-medium ${\n selected\n ? 'border-brand-500 bg-brand-500/10 text-[var(--text-primary)]'\n : 'border-[var(--border-default)] text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]'\n }`}\n aria-label={design.name}\n aria-pressed={selected}\n >\n <span\n className=\"h-5 w-5 shrink-0 rounded-sm border border-black/10\"\n style={{ backgroundColor: design.palette.background }}\n />\n <span className=\"hidden truncate md:block\">{design.name}</span>\n </button>\n );\n })}\n </div>\n </fieldset>\n </div>\n\n <div className=\"space-y-4\">\n <Field\n label=\"Amount\"\n htmlFor={ids.amount}\n required\n hint={`In ${form.currency || currency}, your business currency. It cannot be changed here.`}\n >\n <div className=\"relative\">\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-y-0 left-3 flex items-center text-sm text-[var(--text-tertiary)]\"\n >\n {symbol}\n </span>\n <Input\n id={ids.amount}\n type=\"number\"\n min=\"0.01\"\n step=\"0.01\"\n required\n aria-required\n className={symbol.length > 2 ? 'pl-14' : 'pl-8'}\n value={form.originalAmount || ''}\n onChange={(event) =>\n setField('originalAmount', Number(event.target.value))\n }\n />\n </div>\n </Field>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Field\n label=\"Recipient name\"\n htmlFor={ids.recipientName}\n hint=\"Optional. Who receives and spends the card.\"\n >\n <Input\n id={ids.recipientName}\n value={form.recipientName ?? ''}\n onChange={(event) => setField('recipientName', event.target.value)}\n />\n </Field>\n <Field\n label=\"Recipient email\"\n htmlFor={ids.recipientEmail}\n hint=\"Optional. Nothing is emailed when you issue. Send it from the card's details.\"\n >\n <Input\n id={ids.recipientEmail}\n type=\"email\"\n value={form.recipientEmail ?? ''}\n onChange={(event) =>\n setField('recipientEmail', event.target.value)\n }\n />\n </Field>\n <Field\n label=\"Purchaser name\"\n htmlFor={ids.purchaserName}\n hint=\"Optional. Who is paying for the card.\"\n >\n <Input\n id={ids.purchaserName}\n value={form.purchaserName ?? ''}\n onChange={(event) => setField('purchaserName', event.target.value)}\n />\n </Field>\n <Field\n label=\"Purchaser email\"\n htmlFor={ids.purchaserEmail}\n hint=\"Optional.\"\n >\n <Input\n id={ids.purchaserEmail}\n type=\"email\"\n value={form.purchaserEmail ?? ''}\n onChange={(event) =>\n setField('purchaserEmail', event.target.value)\n }\n />\n </Field>\n </div>\n <Field\n label=\"Personal message\"\n htmlFor={ids.message}\n hint=\"Optional. Up to 500 characters.\"\n >\n <Textarea\n id={ids.message}\n rows={3}\n maxLength={500}\n value={form.personalMessage ?? ''}\n onChange={(event) => setField('personalMessage', event.target.value)}\n />\n </Field>\n <Field\n label=\"Expiration date\"\n htmlFor={ids.expiresAt}\n hint=\"Optional. Leave it empty and the card has no expiry date.\"\n >\n <Input\n id={ids.expiresAt}\n type=\"date\"\n min={minimumExpirationDate()}\n value={form.expiresAt ?? ''}\n onChange={(event) => setField('expiresAt', event.target.value)}\n />\n </Field>\n {error && (\n <p\n role=\"alert\"\n className=\"rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]\"\n >\n {error}\n </p>\n )}\n </div>\n </form>\n </FormDialog>\n );\n}\n\ninterface BalanceModalProps {\n onClose: () => void;\n initialCode?: string;\n}\n\nfunction BalanceModal({ onClose, initialCode = '' }: BalanceModalProps) {\n const { tenant } = useAuth();\n const formatCurrency = useFormatCurrency();\n const codeId = useId();\n const [code, setCode] = useState(initialCode);\n const [checking, setChecking] = useState(false);\n const [result, setResult] = useState<{\n valid: boolean;\n balance?: number;\n error?: string;\n } | null>(null);\n\n const submit = async (event: React.FormEvent) => {\n event.preventDefault();\n if (!tenant?.slug || !code.trim()) return;\n setChecking(true);\n try {\n setResult(\n await giftCardsApi.checkBalance(tenant.slug, code.trim().toUpperCase()),\n );\n } catch (checkError) {\n setResult({ valid: false, error: errorMessage(checkError) });\n } finally {\n setChecking(false);\n }\n };\n\n return (\n <FormDialog title=\"Check gift card balance\" size=\"sm\" onClose={onClose}>\n <form onSubmit={submit} className=\"space-y-4\">\n <Field label=\"Gift card code\" htmlFor={codeId} required>\n <Input\n id={codeId}\n required\n aria-required\n autoComplete=\"off\"\n spellCheck={false}\n className=\"font-mono uppercase\"\n value={code}\n onChange={(event) => {\n setCode(event.target.value.toUpperCase());\n setResult(null);\n }}\n />\n </Field>\n <div role=\"status\" aria-live=\"polite\">\n {result && (\n <div\n className={`rounded-md p-4 ${\n result.valid\n ? 'bg-[var(--success-bg)] text-[var(--success)]'\n : 'bg-[var(--error-bg)] text-[var(--error)]'\n }`}\n >\n {result.valid\n ? `Available balance: ${formatCurrency(result.balance ?? 0)}`\n : result.error || 'This gift card is not valid.'}\n </div>\n )}\n </div>\n <Button\n type=\"submit\"\n className=\"w-full\"\n disabled={checking || !code.trim()}\n >\n {checking ? 'Checking...' : 'Check balance'}\n </Button>\n </form>\n </FormDialog>\n );\n}\n\ninterface GiftCardDetailModalProps {\n giftCard: GiftCard;\n onClose: () => void;\n onChanged: () => void;\n}\n\nfunction GiftCardDetailModal({\n giftCard,\n onClose,\n onChanged,\n}: GiftCardDetailModalProps) {\n const { tenant } = useAuth();\n const formatCurrency = useFormatCurrency();\n const { toast } = useToast();\n const [amount, setAmount] = useState('');\n const [referenceId, setReferenceId] = useState('');\n const [note, setNote] = useState('');\n const [saving, setSaving] = useState(false);\n const [sharing, setSharing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const copyCode = async () => {\n await navigator.clipboard.writeText(giftCard.code);\n toast({ title: 'Gift card code copied', description: giftCard.code });\n };\n\n const artworkData = {\n design: giftCard.design ?? GIFT_CARD_PRESETS[0],\n merchantName: tenant?.businessName || 'Gift card',\n amount: formatCurrency(Number(giftCard.originalAmount), giftCard.currency),\n recipientName: giftCard.recipientName ?? undefined,\n senderName: giftCard.purchaserName ?? undefined,\n code: giftCard.code,\n };\n\n const sendByEmail = async () => {\n if (!tenant?.slug || !giftCard.recipientEmail) return;\n setSharing(true);\n setError(null);\n try {\n const result = await communicationsApi.sendCustomEmail(tenant.slug, {\n recipientEmail: giftCard.recipientEmail,\n recipientName: giftCard.recipientName ?? undefined,\n subject: `${giftCard.purchaserName || tenant.businessName} sent you a gift card`,\n htmlContent: createGiftCardEmailHtml(artworkData),\n metadata: { giftCardId: giftCard.id, kind: 'gift_card_delivery' },\n });\n if (!result.success) throw new Error(result.error || 'Email delivery failed');\n toast({ title: 'Gift card email sent', description: giftCard.recipientEmail });\n } catch (sendError) {\n setError(errorMessage(sendError));\n } finally {\n setSharing(false);\n }\n };\n\n const redeem = async (event: React.FormEvent) => {\n event.preventDefault();\n if (!tenant?.slug) return;\n setSaving(true);\n setError(null);\n try {\n const result = await giftCardsApi.redeem(tenant.slug, giftCard.code, {\n amount: Number(amount),\n ...(referenceId ? { referenceId } : {}),\n ...(note ? { note } : {}),\n });\n if (!result.success) throw new Error(result.error || 'Redemption failed');\n toast({ title: 'Gift card redeemed' });\n onChanged();\n onClose();\n } catch (redeemError) {\n setError(errorMessage(redeemError));\n } finally {\n setSaving(false);\n }\n };\n\n // Voids the card and keeps its record and history. The older DELETE\n // endpoint destroyed the row, and with it the balance a customer had paid for.\n const remove = async () => {\n if (!tenant?.slug) return;\n setSaving(true);\n try {\n await giftcardsApi.cancel(tenant.slug, giftCard.id);\n toast({ title: 'Gift card cancelled' });\n onChanged();\n onClose();\n } catch (deleteError) {\n setError(errorMessage(deleteError));\n setSaving(false);\n }\n };\n\n return (\n <FormDialog title=\"Gift card details\" onClose={onClose}>\n <div className=\"space-y-5\">\n <GiftCardArtwork {...artworkData} />\n <div className=\"grid grid-cols-2 gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n icon={<Download />}\n onClick={() => downloadGiftCardArtwork(artworkData)}\n >\n Download\n </Button>\n <Tooltip\n label={\n giftCard.recipientEmail\n ? ''\n : 'Add a recipient email to send this card'\n }\n >\n <span className=\"block\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n icon={<Mail />}\n className=\"w-full\"\n onClick={sendByEmail}\n disabled={sharing || !giftCard.recipientEmail}\n >\n {sharing ? 'Sending...' : 'Send by email'}\n </Button>\n </span>\n </Tooltip>\n </div>\n <div className=\"rounded-md border border-[var(--border-default)] bg-[var(--bg-subtle)] p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <p className=\"text-xs font-medium text-[var(--text-muted)]\">Code</p>\n <p className=\"mt-1 font-mono text-xl font-semibold text-[var(--text-primary)]\">\n {giftCard.code}\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"icon-sm\"\n aria-label=\"Copy code\"\n onClick={copyCode}\n >\n <Copy />\n </Button>\n </div>\n <div className=\"mt-4 grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Balance left</p>\n <p className=\"font-semibold text-[var(--text-primary)]\">\n {formatCurrency(Number(giftCard.balance), giftCard.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Original value</p>\n <p className=\"font-semibold text-[var(--text-primary)]\">\n {formatCurrency(\n Number(giftCard.originalAmount),\n giftCard.currency,\n )}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Recipient</p>\n <p className=\"text-sm text-[var(--text-primary)]\">\n {giftCard.recipientName || giftCard.recipientEmail || 'Walk-in'}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-[var(--text-muted)]\">Expires</p>\n <p className=\"text-sm text-[var(--text-primary)]\">\n {dateLabel(giftCard.expiresAt)}\n </p>\n </div>\n </div>\n </div>\n\n {giftCard.status === 'active' && Number(giftCard.balance) > 0 && (\n <form onSubmit={redeem} className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)]\">\n Redeem value\n </h3>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <Input\n label=\"Amount\"\n type=\"number\"\n min=\"0.01\"\n max={Number(giftCard.balance)}\n step=\"0.01\"\n required\n aria-required\n value={amount}\n onChange={(event) => setAmount(event.target.value)}\n />\n <Input\n label=\"Order or reference ID (optional)\"\n value={referenceId}\n onChange={(event) => setReferenceId(event.target.value)}\n />\n </div>\n <Input\n label=\"Note (optional)\"\n value={note}\n onChange={(event) => setNote(event.target.value)}\n />\n <Button\n type=\"submit\"\n disabled={saving || !amount || Number(amount) <= 0}\n >\n {saving ? 'Redeeming...' : 'Redeem gift card'}\n </Button>\n </form>\n )}\n\n <div>\n <div className=\"mb-2 flex items-center gap-2\">\n <History className=\"h-4 w-4 text-[var(--text-tertiary)]\" />\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)]\">\n Redemption history\n </h3>\n </div>\n {giftCard.redemptions.length === 0 ? (\n <p className=\"text-sm text-[var(--text-muted)]\">No redemptions yet.</p>\n ) : (\n <div className=\"divide-y divide-[var(--border-default)] rounded-md border border-[var(--border-default)]\">\n {giftCard.redemptions.map((redemption) => (\n <div\n key={redemption.id}\n className=\"flex items-center justify-between gap-3 px-3 py-2.5\"\n >\n <div>\n <p className=\"text-sm text-[var(--text-primary)]\">\n {redemption.orderId || 'Manual redemption'}\n </p>\n <p className=\"text-xs text-[var(--text-muted)]\">\n {dateLabel(redemption.redeemedAt)}\n </p>\n </div>\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">\n -{formatCurrency(Number(redemption.amount), giftCard.currency)}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {error && (\n <p\n role=\"alert\"\n className=\"rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]\"\n >\n {error}\n </p>\n )}\n {giftCard.status !== 'cancelled' && (\n <div className=\"flex justify-end border-t border-[var(--border-default)] pt-4\">\n <InlineDeleteButton\n onDelete={remove}\n disabled={saving}\n title=\"Cancel gift card\"\n confirmText=\"Cancel this gift card?\"\n className=\"text-[var(--error)]\"\n />\n </div>\n )}\n </div>\n </FormDialog>\n );\n}\n\nexport function GiftCardsFeature() {\n const { tenant } = useAuth();\n const formatCurrency = useFormatCurrency();\n const { toast } = useToast();\n const slug = tenant?.slug;\n const currency = normalizeCurrencyCode(\n (tenant?.settings as Record<string, unknown> | undefined)?.currency,\n );\n const [giftCards, setGiftCards] = useState<GiftCard[]>([]);\n const [stats, setStats] = useState<GiftCardStats | null>(null);\n const [loading, setLoading] = useState(true);\n const [loaded, setLoaded] = useState(false);\n const [search, setSearch] = useState('');\n const [status, setStatus] = useState<GiftCardStatus | ''>('');\n const [page, setPage] = useState(1);\n const [count, setCount] = useState(0);\n const [showIssue, setShowIssue] = useState(false);\n const [showBalance, setShowBalance] = useState(false);\n const [selected, setSelected] = useState<GiftCard | null>(null);\n\n // Two effects, an unstable callback and a pull-to-refresh registration all\n // used to start the same reads on load, so every request went out twice.\n // Identical reads in flight together now share one request.\n const [dedupe] = useState(createInFlightDeduper);\n const toastRef = useRef(toast);\n useEffect(() => {\n toastRef.current = toast;\n });\n\n // Only the newest read may land: a quiet re-read started before the filters\n // changed must not put the old filter's rows back on screen.\n const latestRead = useRef(0);\n const readGiftCards = useCallback(\n async (quiet: boolean) => {\n if (!slug) return;\n // A page's own read moves the marker; a quiet re-read only checks it.\n const read = quiet ? latestRead.current : ++latestRead.current;\n if (!quiet) setLoading(true);\n const query = {\n ...(status ? { status } : {}),\n ...(search.trim() ? { search: search.trim() } : {}),\n page,\n limit: PAGE_SIZE,\n };\n try {\n const list = await dedupe.run(\n `list:${slug}:${JSON.stringify(query)}`,\n () => giftCardsApi.list(slug, query),\n );\n if (read !== latestRead.current) return;\n setGiftCards(list.rows);\n setCount(list.count);\n } catch (loadError) {\n // A quiet re-read keeps what is on screen and says nothing; the poll\n // counts the failure and slows down.\n if (quiet) throw loadError;\n toastRef.current({\n title: 'Unable to load gift cards',\n description: errorMessage(loadError),\n });\n } finally {\n if (!quiet && read === latestRead.current) {\n setLoading(false);\n setLoaded(true);\n }\n }\n },\n [dedupe, page, search, slug, status],\n );\n\n // The totals do not depend on the filters, so they are read on their own and\n // not again each time the search box or a filter changes.\n const readStats = useCallback(\n async (quiet: boolean) => {\n if (!slug) return;\n try {\n setStats(await dedupe.run(`stats:${slug}`, () => giftCardsApi.stats(slug)));\n } catch (statsError) {\n if (quiet) throw statsError;\n }\n },\n [dedupe, slug],\n );\n\n const reloadAll = useCallback(\n () => Promise.all([readGiftCards(false), readStats(false)]),\n [readGiftCards, readStats],\n );\n\n useEffect(() => {\n readGiftCards(false);\n }, [readGiftCards]);\n\n useEffect(() => {\n readStats(false);\n }, [readStats]);\n\n useQuietPoll(\n useCallback(\n () => Promise.all([readGiftCards(true), readStats(true)]),\n [readGiftCards, readStats],\n ),\n LIVE_REFRESH_MS,\n LIVE,\n );\n\n useRegisterRefresh(reloadAll);\n\n const copyCode = async (event: React.MouseEvent, code: string) => {\n event.stopPropagation();\n await navigator.clipboard.writeText(code);\n toast({ title: 'Gift card code copied', description: code });\n };\n\n const totalPages = Math.max(1, Math.ceil(count / PAGE_SIZE));\n const hasFilters = Boolean(search.trim() || status);\n const firstRun =\n loaded &&\n isFirstRun({\n loading,\n search,\n status,\n count,\n totalIssued: stats?.totalIssued,\n });\n const hasCards = loaded && !firstRun;\n const showStats = hasCards && stats !== null;\n\n const openIssue = () => setShowIssue(true);\n const openBalance = () => setShowBalance(true);\n\n return (\n <ListPage\n largeTitle\n title=\"Gift cards\"\n mobileHeader={\n <MobilePageHeader\n title=\"Gift cards\"\n action={\n hasCards ? { label: 'Issue gift card', onClick: openIssue } : undefined\n }\n />\n }\n actions={\n <>\n <Hint lines={PAGE_HINT_LINES} topic=\"gift cards\" />\n {!LIVE && <RefreshButton onClick={reloadAll} refreshing={loading} />}\n {hasCards && (\n <>\n <Button\n type=\"button\"\n variant=\"secondary\"\n icon={<Search />}\n onClick={openBalance}\n >\n Check balance\n </Button>\n <Button type=\"button\" icon={<Plus />} onClick={openIssue}>\n Issue gift card\n </Button>\n </>\n )}\n </>\n }\n >\n {showStats && (\n <>\n <div className=\"hidden lg:block\">\n <MetricRow>\n <MetricTile\n label=\"Total sold\"\n value={formatCurrency(stats?.totalIssued ?? 0, currency)}\n />\n <MetricTile\n label=\"Still to spend\"\n value={formatCurrency(stats?.outstanding ?? 0, currency)}\n />\n <MetricTile\n label=\"Spent\"\n value={formatCurrency(stats?.redeemed ?? 0, currency)}\n />\n <MetricTile label=\"Cards in use\" value={stats?.activeCards ?? 0} />\n </MetricRow>\n </div>\n\n <div className=\"grid grid-cols-2 gap-px overflow-hidden rounded-md bg-[var(--border-default)] lg:hidden\">\n <div className=\"bg-[var(--bg-subtle)] p-4\">\n <p className=\"text-xs text-[var(--text-muted)]\">Still to spend</p>\n <p className=\"mt-1 text-lg font-semibold text-[var(--text-primary)]\">\n {formatCurrency(stats?.outstanding ?? 0, currency)}\n </p>\n </div>\n <div className=\"bg-[var(--bg-subtle)] p-4\">\n <p className=\"text-xs text-[var(--text-muted)]\">Cards in use</p>\n <p className=\"mt-1 text-lg font-semibold text-[var(--text-primary)]\">\n {stats?.activeCards ?? 0}\n </p>\n </div>\n </div>\n </>\n )}\n\n {hasCards && (\n <div className=\"flex gap-2 overflow-x-auto lg:hidden\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n shape=\"pill\"\n icon={<Search />}\n onClick={openBalance}\n >\n Check balance\n </Button>\n </div>\n )}\n\n {!firstRun && (\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center\">\n <div className=\"relative flex-1\">\n <Search\n aria-hidden\n className=\"pointer-events-none absolute left-3 top-1/2 z-10 h-4 w-4 -translate-y-1/2 text-[var(--text-muted)]\"\n />\n <Input\n type=\"search\"\n aria-label=\"Search gift cards\"\n placeholder=\"Search code, recipient, or email\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setPage(1);\n }}\n className=\"pl-10\"\n />\n </div>\n <div\n role=\"group\"\n aria-label=\"Filter by status\"\n className=\"flex gap-1 overflow-x-auto\"\n >\n {STATUS_FILTERS.map((filter) => {\n const pressed = status === filter.value;\n return (\n <Tooltip key={filter.value} label={filter.hint}>\n <Button\n type=\"button\"\n size=\"sm\"\n variant={pressed ? 'secondary' : 'ghost'}\n aria-pressed={pressed}\n className=\"shrink-0\"\n onClick={() => {\n setStatus(filter.value);\n setPage(1);\n }}\n >\n {filter.label}\n </Button>\n </Tooltip>\n );\n })}\n </div>\n </div>\n )}\n\n {/* The page's second heading level, so the empty state's own title below\n follows the h1 in order. */}\n <h2 className=\"sr-only\">Issued gift cards</h2>\n\n {!loaded && <InlineListSkeleton />}\n\n {loading && loaded && giftCards.length === 0 && <InlineListSkeleton />}\n\n {firstRun && (\n <div>\n <TableEmptyState\n icon={Gift}\n title=\"Issue your first gift card\"\n description=\"Create a stored-value card for a customer or walk-in purchase\"\n action={{ label: 'Issue gift card', onClick: openIssue }}\n />\n <div className=\"flex justify-center\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n icon={<Search />}\n onClick={openBalance}\n >\n Check balance\n </Button>\n </div>\n </div>\n )}\n\n {loaded && !loading && !firstRun && giftCards.length === 0 && (\n <TableEmptyState\n icon={Gift}\n title=\"No gift cards found\"\n description=\"Try changing the search or status filter\"\n action={\n hasFilters\n ? {\n label: 'Clear search and filters',\n onClick: () => {\n setSearch('');\n setStatus('');\n setPage(1);\n },\n }\n : undefined\n }\n />\n )}\n\n {giftCards.length > 0 && (\n <>\n <div className=\"space-y-2 lg:hidden\">\n {giftCards.map((giftCard) => (\n <button\n key={giftCard.id}\n type=\"button\"\n onClick={() => setSelected(giftCard)}\n className=\"flex w-full items-center justify-between gap-3 rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] p-4 text-left\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate font-mono text-sm font-semibold text-[var(--text-primary)]\">\n {giftCard.code}\n </p>\n <p className=\"truncate text-sm text-[var(--text-tertiary)]\">\n {giftCard.recipientName || giftCard.recipientEmail || 'Walk-in'}\n </p>\n </div>\n <div className=\"shrink-0 text-right\">\n <p className=\"text-sm font-semibold text-[var(--text-primary)]\">\n {formatCurrency(Number(giftCard.balance), giftCard.currency)}\n </p>\n <span className={`text-xs ${statusClass(giftCard.status)}`}>\n {STATUS_LABEL[giftCard.status]}\n </span>\n </div>\n </button>\n ))}\n </div>\n\n <div className=\"hidden overflow-x-auto rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] lg:block\">\n <table className=\"w-full min-w-[760px]\">\n <thead className=\"border-b border-[var(--border-default)] bg-[var(--bg-subtle)]\">\n <tr>\n {['Code', 'Recipient', 'Issued', 'Balance', 'Expires', 'Status'].map(\n (heading) => (\n <th\n key={heading}\n scope=\"col\"\n className=\"px-4 py-3 text-left text-xs font-medium uppercase text-[var(--text-tertiary)]\"\n >\n {heading}\n </th>\n ),\n )}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--border-default)]\">\n {giftCards.map((giftCard) => (\n <tr\n key={giftCard.id}\n tabIndex={0}\n aria-label={`Open gift card ${giftCard.code}`}\n onClick={() => setSelected(giftCard)}\n onKeyDown={(event) => {\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n setSelected(giftCard);\n }\n }}\n className=\"cursor-pointer hover:bg-[var(--interactive-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--focus-ring)]\"\n >\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-mono text-sm font-medium text-[var(--text-primary)]\">\n {giftCard.code}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`Copy code ${giftCard.code}`}\n onClick={(event) => copyCode(event, giftCard.code)}\n >\n <Copy />\n </Button>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <p className=\"text-sm text-[var(--text-primary)]\">\n {giftCard.recipientName || 'Walk-in'}\n </p>\n <p className=\"text-xs text-[var(--text-muted)]\">\n {giftCard.recipientEmail}\n </p>\n </td>\n <td className=\"px-4 py-3 text-sm text-[var(--text-secondary)]\">\n {formatCurrency(\n Number(giftCard.originalAmount),\n giftCard.currency,\n )}\n </td>\n <td className=\"px-4 py-3 text-sm font-medium text-[var(--text-primary)]\">\n {formatCurrency(Number(giftCard.balance), giftCard.currency)}\n </td>\n <td className=\"px-4 py-3 text-sm text-[var(--text-secondary)]\">\n {dateLabel(giftCard.expiresAt)}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${statusClass(giftCard.status)}`}\n >\n {STATUS_LABEL[giftCard.status]}\n </span>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n <TablePagination\n page={page}\n totalPages={totalPages}\n pageSize={PAGE_SIZE}\n totalItems={count}\n entityName=\"gift cards\"\n onPageChange={setPage}\n />\n </>\n )}\n\n {showIssue && (\n <IssueGiftCardModal\n currency={currency}\n merchantName={tenant?.businessName || 'Gift card'}\n onClose={() => setShowIssue(false)}\n onCreated={(giftCard) => {\n setShowIssue(false);\n setSelected(giftCard);\n toast({\n title: 'Gift card issued',\n description: giftCard.code,\n });\n reloadAll();\n }}\n />\n )}\n {showBalance && <BalanceModal onClose={() => setShowBalance(false)} />}\n {selected && (\n <GiftCardDetailModal\n giftCard={selected}\n onClose={() => setSelected(null)}\n onChanged={reloadAll}\n />\n )}\n </ListPage>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The small info icon that holds a page's helper text, so the sentence is one
3
+ * hover or focus away instead of sitting on the page. `lines` render one under
4
+ * the other; screen readers get them joined as the button's name.
5
+ */
6
+ export declare function Hint({ lines, topic }: {
7
+ lines: string[];
8
+ topic: string;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Info } from "lucide-react";
4
+ import { Tooltip } from "@withpotter/ui-kit/tooltip";
5
+ function Hint({ lines, topic }) {
6
+ return /* @__PURE__ */ jsx(
7
+ Tooltip,
8
+ {
9
+ label: /* @__PURE__ */ jsx("span", { className: "block space-y-1", children: lines.map((line) => /* @__PURE__ */ jsx("span", { className: "block", children: line }, line)) }),
10
+ children: /* @__PURE__ */ jsx(
11
+ "button",
12
+ {
13
+ type: "button",
14
+ "aria-label": `About ${topic}. ${lines.join(" ")}`,
15
+ className: "inline-flex items-center rounded-md p-1 text-[var(--text-muted)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)]",
16
+ children: /* @__PURE__ */ jsx(Info, { className: "size-4", "aria-hidden": true })
17
+ }
18
+ )
19
+ }
20
+ );
21
+ }
22
+ export {
23
+ Hint
24
+ };
25
+ //# sourceMappingURL=hint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/features/gift-cards/hint.tsx"],"sourcesContent":["'use client';\n\nimport { Info } from 'lucide-react';\nimport { Tooltip } from '@withpotter/ui-kit/tooltip';\n\n/**\n * The small info icon that holds a page's helper text, so the sentence is one\n * hover or focus away instead of sitting on the page. `lines` render one under\n * the other; screen readers get them joined as the button's name.\n */\nexport function Hint({ lines, topic }: { lines: string[]; topic: string }) {\n return (\n <Tooltip\n label={\n <span className=\"block space-y-1\">\n {lines.map((line) => (\n <span key={line} className=\"block\">\n {line}\n </span>\n ))}\n </span>\n }\n >\n <button\n type=\"button\"\n aria-label={`About ${topic}. ${lines.join(' ')}`}\n className=\"inline-flex items-center rounded-md p-1 text-[var(--text-muted)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)]\"\n >\n <Info className=\"size-4\" aria-hidden />\n </button>\n </Tooltip>\n );\n}\n"],"mappings":";AAgBY;AAdZ,SAAS,YAAY;AACrB,SAAS,eAAe;AAOjB,SAAS,KAAK,EAAE,OAAO,MAAM,GAAuC;AACzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OACE,oBAAC,UAAK,WAAU,mBACb,gBAAM,IAAI,CAAC,SACV,oBAAC,UAAgB,WAAU,SACxB,kBADQ,IAEX,CACD,GACH;AAAA,MAGF;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAY,SAAS,KAAK,KAAK,MAAM,KAAK,GAAG,CAAC;AAAA,UAC9C,WAAU;AAAA,UAEV,8BAAC,QAAK,WAAU,UAAS,eAAW,MAAC;AAAA;AAAA,MACvC;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -154,7 +154,7 @@ function ProductForm({ product, onSaved, onClose }) {
154
154
  const isEditing = !!product;
155
155
  const tenantCurrency = useMemo(() => {
156
156
  const settings = tenant?.settings;
157
- return settings?.currency || "USD";
157
+ return settings?.currency || "NGN";
158
158
  }, [tenant?.settings]);
159
159
  const tenantCategories = useMemo(() => {
160
160
  const saved = tenant?.settings?.categories || {};