@withpotter/components 0.13.0-next.84 → 0.13.0-next.85
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.
- package/dist/dashboard/activity-feed.d.ts +2 -0
- package/dist/dashboard/activity-feed.js +3 -1
- package/dist/dashboard/activity-feed.js.map +1 -1
- package/dist/dashboard/activity-paid.d.ts +10 -0
- package/dist/dashboard/activity-paid.js +7 -0
- package/dist/dashboard/activity-paid.js.map +1 -0
- package/dist/dashboard/home/ask-controls.js +1 -0
- package/dist/dashboard/home/ask-controls.js.map +1 -1
- package/dist/dashboard/home/ask-mode-chip.js +4 -4
- package/dist/dashboard/home/ask-mode-chip.js.map +1 -1
- package/dist/dashboard/home/home-ask.js +3 -3
- package/dist/dashboard/home/home-ask.js.map +1 -1
- package/dist/dashboard/home/home-quick-actions.js +9 -1
- package/dist/dashboard/home/home-quick-actions.js.map +1 -1
- package/dist/dashboard/home/host-quick-actions.d.ts +19 -0
- package/dist/dashboard/home/host-quick-actions.js +12 -0
- package/dist/dashboard/home/host-quick-actions.js.map +1 -0
- package/dist/dashboard/home/widgets/activity-widget.js +7 -1
- package/dist/dashboard/home/widgets/activity-widget.js.map +1 -1
- package/dist/features/gift-card-artwork.js +1 -1
- package/dist/features/gift-card-artwork.js.map +1 -1
- package/dist/features/gift-cards/gift-card-logic.d.ts +41 -0
- package/dist/features/gift-cards/gift-card-logic.js +65 -0
- package/dist/features/gift-cards/gift-card-logic.js.map +1 -0
- package/dist/features/gift-cards/gift-cards-feature.js +656 -518
- package/dist/features/gift-cards/gift-cards-feature.js.map +1 -1
- package/dist/features/gift-cards/hint.d.ts +9 -0
- package/dist/features/gift-cards/hint.js +25 -0
- package/dist/features/gift-cards/hint.js.map +1 -0
- package/dist/features/product-form.js +1 -1
- package/dist/features/product-form.js.map +1 -1
- package/dist/sidebar/nav-renderers.js +2 -0
- package/dist/sidebar/nav-renderers.js.map +1 -1
- package/dist/sidebar/nav-section.js +5 -3
- package/dist/sidebar/nav-section.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/features/gift-card-artwork.tsx"],"sourcesContent":["import { Gift } from 'lucide-react';\nimport type { GiftCardDesignSnapshot } from '@withpotter/lib/api';\n\nexport const GIFT_CARD_PRESETS: GiftCardDesignSnapshot[] = [\n {\n designId: 'gold',\n name: 'Gold',\n imageUrl: null,\n palette: {\n background: '#713f12',\n foreground: '#fffbeb',\n accent: '#fbbf24',\n },\n occasion: 'general',\n },\n {\n designId: 'platinum',\n name: 'Platinum',\n imageUrl: null,\n palette: {\n background: '#334155',\n foreground: '#f8fafc',\n accent: '#cbd5e1',\n },\n occasion: 'general',\n },\n {\n designId: 'bronze',\n name: 'Bronze',\n imageUrl: null,\n palette: {\n background: '#7c2d12',\n foreground: '#fff7ed',\n accent: '#fb923c',\n },\n occasion: 'general',\n },\n {\n designId: 'silver',\n name: 'Silver',\n imageUrl: null,\n palette: {\n background: '#475569',\n foreground: '#f8fafc',\n accent: '#e2e8f0',\n },\n occasion: 'general',\n },\n];\n\ninterface GiftCardArtworkProps {\n design: GiftCardDesignSnapshot;\n merchantName: string;\n amount?: string;\n recipientName?: string;\n senderName?: string;\n code?: string;\n className?: string;\n}\n\nexport interface GiftCardArtworkData\n extends Omit<GiftCardArtworkProps, 'className'> {}\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nexport function createGiftCardSvg({\n design,\n merchantName,\n amount,\n recipientName,\n senderName,\n code,\n}: GiftCardArtworkData): string {\n const foreground = design.palette.foreground;\n const background = design.palette.background;\n const accent = design.palette.accent;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"756\" viewBox=\"0 0 1200 756\">\n <defs><pattern id=\"lines\" width=\"72\" height=\"72\" patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\"><line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"72\" stroke=\"${accent}\" stroke-opacity=\"0.14\" stroke-width=\"3\"/></pattern></defs>\n <rect width=\"1200\" height=\"756\" rx=\"32\" fill=\"${background}\"/>\n <rect width=\"1200\" height=\"756\" rx=\"32\" fill=\"url(#lines)\"/>\n <rect width=\"1200\" height=\"12\" rx=\"6\" fill=\"${accent}\"/>\n <circle cx=\"78\" cy=\"82\" r=\"28\" fill=\"${accent}\"/>\n <text x=\"126\" y=\"72\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"30\" font-weight=\"700\">${escapeXml(merchantName)}</text>\n <text x=\"126\" y=\"104\" fill=\"${foreground}\" fill-opacity=\"0.7\" font-family=\"Arial, sans-serif\" font-size=\"20\">GIFT CARD</text>\n ${amount ? `<text x=\"1120\" y=\"82\" text-anchor=\"end\" fill=\"${foreground}\" fill-opacity=\"0.82\" font-family=\"Arial, sans-serif\" font-size=\"26\" font-weight=\"600\">${escapeXml(amount)}</text>` : ''}\n <text x=\"72\" y=\"330\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"58\" font-weight=\"700\">${escapeXml(design.name)}</text>\n <text x=\"72\" y=\"470\" fill=\"${foreground}\" fill-opacity=\"0.58\" font-family=\"Arial, sans-serif\" font-size=\"18\" font-weight=\"700\">TO</text>\n <text x=\"72\" y=\"515\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"34\" font-weight=\"600\">${escapeXml(recipientName || 'Someone special')}</text>\n <text x=\"600\" y=\"470\" fill=\"${foreground}\" fill-opacity=\"0.58\" font-family=\"Arial, sans-serif\" font-size=\"18\" font-weight=\"700\">FROM</text>\n <text x=\"600\" y=\"515\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"34\" font-weight=\"600\">${escapeXml(senderName || merchantName)}</text>\n ${code ? `<text x=\"72\" y=\"664\" fill=\"${foreground}\" fill-opacity=\"0.58\" font-family=\"Arial, sans-serif\" font-size=\"18\" font-weight=\"700\">REDEEM WITH</text><text x=\"72\" y=\"704\" fill=\"${foreground}\" font-family=\"monospace\" font-size=\"30\" font-weight=\"700\">${escapeXml(code)}</text>` : ''}\n</svg>`;\n}\n\nexport async function downloadGiftCardArtwork(\n data: GiftCardArtworkData,\n): Promise<void> {\n const svgBlob = new Blob([createGiftCardSvg(data)], {\n type: 'image/svg+xml;charset=utf-8',\n });\n const svgUrl = URL.createObjectURL(svgBlob);\n const image = new Image();\n await new Promise<void>((resolve, reject) => {\n image.onload = () => resolve();\n image.onerror = () => reject(new Error('Unable to render gift card image'));\n image.src = svgUrl;\n });\n\n const canvas = document.createElement('canvas');\n canvas.width = 1200;\n canvas.height = 756;\n const context = canvas.getContext('2d');\n if (!context) {\n URL.revokeObjectURL(svgUrl);\n throw new Error('Unable to create gift card image');\n }\n context.drawImage(image, 0, 0, canvas.width, canvas.height);\n URL.revokeObjectURL(svgUrl);\n\n const pngBlob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) =>\n blob\n ? resolve(blob)\n : reject(new Error('Unable to encode gift card image')),\n 'image/png',\n );\n });\n const url = URL.createObjectURL(pngBlob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `gift-card-${data.code || 'preview'}.png`;\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 0);\n}\n\nexport function createGiftCardEmailHtml(data: GiftCardArtworkData): string {\n const { design, merchantName, amount, recipientName, senderName, code } = data;\n return `<div style=\"margin:0;padding:32px 16px;background:#f4f4f5;font-family:Arial,sans-serif;color:#18181b\">\n <div style=\"max-width:620px;margin:0 auto\">\n <p style=\"margin:0 0 16px;font-size:16px\">${escapeXml(senderName || merchantName)} sent you a gift card.</p>\n <div style=\"padding:28px;border-radius:12px;background:${design.palette.background};color:${design.palette.foreground};border-top:6px solid ${design.palette.accent}\">\n <div style=\"font-size:20px;font-weight:700\">${escapeXml(merchantName)}</div>\n <div style=\"margin-top:4px;font-size:12px;opacity:.7\">GIFT CARD</div>\n <div style=\"margin-top:56px;font-size:30px;font-weight:700\">${escapeXml(design.name)}</div>\n <table role=\"presentation\" style=\"width:100%;margin-top:40px;color:inherit\"><tr>\n <td><div style=\"font-size:11px;opacity:.6\">TO</div><div style=\"margin-top:6px;font-size:18px;font-weight:600\">${escapeXml(recipientName || 'Someone special')}</div></td>\n <td><div style=\"font-size:11px;opacity:.6\">FROM</div><div style=\"margin-top:6px;font-size:18px;font-weight:600\">${escapeXml(senderName || merchantName)}</div></td>\n ${amount ? `<td style=\"text-align:right;font-size:16px;font-weight:600\">${escapeXml(amount)}</td>` : ''}\n </tr></table>\n ${code ? `<div style=\"margin-top:36px;font-size:11px;opacity:.6\">REDEEM WITH</div><div style=\"margin-top:6px;font-family:monospace;font-size:20px;font-weight:700\">${escapeXml(code)}</div>` : ''}\n </div>\n </div>\n</div>`;\n}\n\nexport function GiftCardArtwork({\n design,\n merchantName,\n amount,\n recipientName,\n senderName,\n code,\n className = '',\n}: GiftCardArtworkProps) {\n return (\n <div\n className={`relative aspect-[1.586/1] w-full overflow-hidden rounded-md p-5 ${className}`}\n style={{\n color: design.palette.foreground,\n backgroundColor: design.palette.background,\n backgroundImage: `repeating-linear-gradient(135deg, transparent 0 18px, ${design.palette.accent}22 18px 19px)`,\n }}\n >\n {design.imageUrl && (\n <img\n src={design.imageUrl}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n )}\n <div\n className=\"absolute inset-x-0 top-0 h-1\"\n style={{ backgroundColor: design.palette.accent }}\n />\n <div className=\"relative flex h-full flex-col justify-between\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span\n className=\"flex h-7 w-7 items-center justify-center rounded-full\"\n style={{\n color: design.palette.background,\n backgroundColor: design.palette.accent,\n }}\n >\n <Gift className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold\">{merchantName}</p>\n <p className=\"text-[10px] font-medium uppercase opacity-65\">\n Gift card\n </p>\n </div>\n </div>\n {amount && (\n <span className=\"shrink-0 text-sm font-medium opacity-80\">\n {amount}\n </span>\n )}\n </div>\n <div>\n <p className=\"text-lg font-semibold\">{design.name}</p>\n <div className=\"mt-3 grid grid-cols-2 gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-[10px] font-semibold uppercase opacity-55\">To</p>\n <p className=\"truncate text-sm font-medium\">\n {recipientName || 'Someone special'}\n </p>\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-[10px] font-semibold uppercase opacity-55\">\n From\n </p>\n <p className=\"truncate text-sm font-medium\">\n {senderName || merchantName}\n </p>\n </div>\n </div>\n {code && <p className=\"mt-2 truncate font-mono text-xs opacity-70\">{code}</p>}\n </div>\n </div>\n </div>\n );\n}"],"mappings":"AAyLQ,cAsBI,YAtBJ;AAzLR,SAAS,YAAY;AAGd,MAAM,oBAA8C;AAAA,EACzD;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAeA,SAAS,UAAU,OAAuB;AACxC,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,QAAQ;AAC7B;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgC;AAC9B,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,SAAS,OAAO,QAAQ;AAE9B,SAAO;AAAA,4JACmJ,MAAM;AAAA,kDAChH,UAAU;AAAA;AAAA,gDAEZ,MAAM;AAAA,yCACb,MAAM;AAAA,+BAChB,UAAU,sEAAsE,UAAU,YAAY,CAAC;AAAA,gCACtG,UAAU;AAAA,IACtC,SAAS,iDAAiD,UAAU,0FAA0F,UAAU,MAAM,CAAC,YAAY,EAAE;AAAA,+BAClK,UAAU,sEAAsE,UAAU,OAAO,IAAI,CAAC;AAAA,+BACtG,UAAU;AAAA,+BACV,UAAU,sEAAsE,UAAU,iBAAiB,iBAAiB,CAAC;AAAA,gCAC5H,UAAU;AAAA,gCACV,UAAU,sEAAsE,UAAU,cAAc,YAAY,CAAC;AAAA,IACjJ,OAAO,8BAA8B,UAAU,uIAAuI,UAAU,8DAA8D,UAAU,IAAI,CAAC,YAAY,EAAE;AAAA;AAE/R;AAEA,eAAsB,wBACpB,MACe;AACf,QAAM,UAAU,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,GAAG;AAAA,IAClD,MAAM;AAAA,EACR,CAAC;AACD,QAAM,SAAS,IAAI,gBAAgB,OAAO;AAC1C,QAAM,QAAQ,IAAI,MAAM;AACxB,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,SAAS,MAAM,QAAQ;AAC7B,UAAM,UAAU,MAAM,OAAO,IAAI,MAAM,kCAAkC,CAAC;AAC1E,UAAM,MAAM;AAAA,EACd,CAAC;AAED,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,QAAM,UAAU,OAAO,WAAW,IAAI;AACtC,MAAI,CAAC,SAAS;AACZ,QAAI,gBAAgB,MAAM;AAC1B,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AACA,UAAQ,UAAU,OAAO,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AAC1D,MAAI,gBAAgB,MAAM;AAE1B,QAAM,UAAU,MAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,WAAO;AAAA,MACL,CAAC,SACC,OACI,QAAQ,IAAI,IACZ,OAAO,IAAI,MAAM,kCAAkC,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF,CAAC;AACD,QAAM,MAAM,IAAI,gBAAgB,OAAO;AACvC,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,OAAO;AACZ,OAAK,WAAW,aAAa,KAAK,QAAQ,SAAS;AACnD,WAAS,KAAK,YAAY,IAAI;AAC9B,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,aAAW,MAAM,IAAI,gBAAgB,GAAG,GAAG,CAAC;AAC9C;AAEO,SAAS,wBAAwB,MAAmC;AACzE,QAAM,EAAE,QAAQ,cAAc,QAAQ,eAAe,YAAY,KAAK,IAAI;AAC1E,SAAO;AAAA;AAAA,gDAEuC,UAAU,cAAc,YAAY,CAAC;AAAA,6DACxB,OAAO,QAAQ,UAAU,UAAU,OAAO,QAAQ,UAAU,yBAAyB,OAAO,QAAQ,MAAM;AAAA,oDACnH,UAAU,YAAY,CAAC;AAAA;AAAA,oEAEP,UAAU,OAAO,IAAI,CAAC;AAAA;AAAA,wHAE8B,UAAU,iBAAiB,iBAAiB,CAAC;AAAA,0HAC3C,UAAU,cAAc,YAAY,CAAC;AAAA,UACrJ,SAAS,+DAA+D,UAAU,MAAM,CAAC,UAAU,EAAE;AAAA;AAAA,QAEvG,OAAO,4JAA4J,UAAU,IAAI,CAAC,WAAW,EAAE;AAAA;AAAA;AAAA;AAIvM;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AACd,GAAyB;AACvB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,mEAAmE,SAAS;AAAA,MACvF,OAAO;AAAA,QACL,OAAO,OAAO,QAAQ;AAAA,QACtB,iBAAiB,OAAO,QAAQ;AAAA,QAChC,iBAAiB,yDAAyD,OAAO,QAAQ,MAAM;AAAA,MACjG;AAAA,MAEC;AAAA,eAAO,YACN;AAAA,UAAC;AAAA;AAAA,YACC,KAAK,OAAO;AAAA,YACZ,KAAI;AAAA,YACJ,WAAU;AAAA;AAAA,QACZ;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,iBAAiB,OAAO,QAAQ,OAAO;AAAA;AAAA,QAClD;AAAA,QACA,qBAAC,SAAI,WAAU,iDACb;AAAA,+BAAC,SAAI,WAAU,2CACb;AAAA,iCAAC,SAAI,WAAU,mCACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO;AAAA,oBACL,OAAO,OAAO,QAAQ;AAAA,oBACtB,iBAAiB,OAAO,QAAQ;AAAA,kBAClC;AAAA,kBAEA,8BAAC,QAAK,WAAU,WAAU;AAAA;AAAA,cAC5B;AAAA,cACA,qBAAC,SAAI,WAAU,WACb;AAAA,oCAAC,OAAE,WAAU,kCAAkC,wBAAa;AAAA,gBAC5D,oBAAC,OAAE,WAAU,gDAA+C,uBAE5D;AAAA,iBACF;AAAA,eACF;AAAA,YACC,UACC,oBAAC,UAAK,WAAU,2CACb,kBACH;AAAA,aAEJ;AAAA,UACA,qBAAC,SACC;AAAA,gCAAC,OAAE,WAAU,yBAAyB,iBAAO,MAAK;AAAA,YAClD,qBAAC,SAAI,WAAU,+BACb;AAAA,mCAAC,SAAI,WAAU,WACb;AAAA,oCAAC,OAAE,WAAU,kDAAiD,gBAAE;AAAA,gBAChE,oBAAC,OAAE,WAAU,gCACV,2BAAiB,mBACpB;AAAA,iBACF;AAAA,cACA,qBAAC,SAAI,WAAU,WACb;AAAA,oCAAC,OAAE,WAAU,kDAAiD,kBAE9D;AAAA,gBACA,oBAAC,OAAE,WAAU,gCACV,wBAAc,cACjB;AAAA,iBACF;AAAA,eACF;AAAA,YACC,QAAQ,oBAAC,OAAE,WAAU,8CAA8C,gBAAK;AAAA,aAC3E;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/features/gift-card-artwork.tsx"],"sourcesContent":["import { Gift } from 'lucide-react';\nimport type { GiftCardDesignSnapshot } from '@withpotter/lib/api';\n\nexport const GIFT_CARD_PRESETS: GiftCardDesignSnapshot[] = [\n {\n designId: 'gold',\n name: 'Gold',\n imageUrl: null,\n palette: {\n background: '#a16207',\n foreground: '#fffbeb',\n accent: '#fbbf24',\n },\n occasion: 'general',\n },\n {\n designId: 'platinum',\n name: 'Platinum',\n imageUrl: null,\n palette: {\n background: '#334155',\n foreground: '#f8fafc',\n accent: '#cbd5e1',\n },\n occasion: 'general',\n },\n {\n designId: 'bronze',\n name: 'Bronze',\n imageUrl: null,\n palette: {\n background: '#7c2d12',\n foreground: '#fff7ed',\n accent: '#fb923c',\n },\n occasion: 'general',\n },\n {\n designId: 'silver',\n name: 'Silver',\n imageUrl: null,\n palette: {\n background: '#475569',\n foreground: '#f8fafc',\n accent: '#e2e8f0',\n },\n occasion: 'general',\n },\n];\n\ninterface GiftCardArtworkProps {\n design: GiftCardDesignSnapshot;\n merchantName: string;\n amount?: string;\n recipientName?: string;\n senderName?: string;\n code?: string;\n className?: string;\n}\n\nexport interface GiftCardArtworkData\n extends Omit<GiftCardArtworkProps, 'className'> {}\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nexport function createGiftCardSvg({\n design,\n merchantName,\n amount,\n recipientName,\n senderName,\n code,\n}: GiftCardArtworkData): string {\n const foreground = design.palette.foreground;\n const background = design.palette.background;\n const accent = design.palette.accent;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"756\" viewBox=\"0 0 1200 756\">\n <defs><pattern id=\"lines\" width=\"72\" height=\"72\" patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\"><line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"72\" stroke=\"${accent}\" stroke-opacity=\"0.14\" stroke-width=\"3\"/></pattern></defs>\n <rect width=\"1200\" height=\"756\" rx=\"32\" fill=\"${background}\"/>\n <rect width=\"1200\" height=\"756\" rx=\"32\" fill=\"url(#lines)\"/>\n <rect width=\"1200\" height=\"12\" rx=\"6\" fill=\"${accent}\"/>\n <circle cx=\"78\" cy=\"82\" r=\"28\" fill=\"${accent}\"/>\n <text x=\"126\" y=\"72\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"30\" font-weight=\"700\">${escapeXml(merchantName)}</text>\n <text x=\"126\" y=\"104\" fill=\"${foreground}\" fill-opacity=\"0.7\" font-family=\"Arial, sans-serif\" font-size=\"20\">GIFT CARD</text>\n ${amount ? `<text x=\"1120\" y=\"82\" text-anchor=\"end\" fill=\"${foreground}\" fill-opacity=\"0.82\" font-family=\"Arial, sans-serif\" font-size=\"26\" font-weight=\"600\">${escapeXml(amount)}</text>` : ''}\n <text x=\"72\" y=\"330\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"58\" font-weight=\"700\">${escapeXml(design.name)}</text>\n <text x=\"72\" y=\"470\" fill=\"${foreground}\" fill-opacity=\"0.58\" font-family=\"Arial, sans-serif\" font-size=\"18\" font-weight=\"700\">TO</text>\n <text x=\"72\" y=\"515\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"34\" font-weight=\"600\">${escapeXml(recipientName || 'Someone special')}</text>\n <text x=\"600\" y=\"470\" fill=\"${foreground}\" fill-opacity=\"0.58\" font-family=\"Arial, sans-serif\" font-size=\"18\" font-weight=\"700\">FROM</text>\n <text x=\"600\" y=\"515\" fill=\"${foreground}\" font-family=\"Arial, sans-serif\" font-size=\"34\" font-weight=\"600\">${escapeXml(senderName || merchantName)}</text>\n ${code ? `<text x=\"72\" y=\"664\" fill=\"${foreground}\" fill-opacity=\"0.58\" font-family=\"Arial, sans-serif\" font-size=\"18\" font-weight=\"700\">REDEEM WITH</text><text x=\"72\" y=\"704\" fill=\"${foreground}\" font-family=\"monospace\" font-size=\"30\" font-weight=\"700\">${escapeXml(code)}</text>` : ''}\n</svg>`;\n}\n\nexport async function downloadGiftCardArtwork(\n data: GiftCardArtworkData,\n): Promise<void> {\n const svgBlob = new Blob([createGiftCardSvg(data)], {\n type: 'image/svg+xml;charset=utf-8',\n });\n const svgUrl = URL.createObjectURL(svgBlob);\n const image = new Image();\n await new Promise<void>((resolve, reject) => {\n image.onload = () => resolve();\n image.onerror = () => reject(new Error('Unable to render gift card image'));\n image.src = svgUrl;\n });\n\n const canvas = document.createElement('canvas');\n canvas.width = 1200;\n canvas.height = 756;\n const context = canvas.getContext('2d');\n if (!context) {\n URL.revokeObjectURL(svgUrl);\n throw new Error('Unable to create gift card image');\n }\n context.drawImage(image, 0, 0, canvas.width, canvas.height);\n URL.revokeObjectURL(svgUrl);\n\n const pngBlob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) =>\n blob\n ? resolve(blob)\n : reject(new Error('Unable to encode gift card image')),\n 'image/png',\n );\n });\n const url = URL.createObjectURL(pngBlob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `gift-card-${data.code || 'preview'}.png`;\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 0);\n}\n\nexport function createGiftCardEmailHtml(data: GiftCardArtworkData): string {\n const { design, merchantName, amount, recipientName, senderName, code } = data;\n return `<div style=\"margin:0;padding:32px 16px;background:#f4f4f5;font-family:Arial,sans-serif;color:#18181b\">\n <div style=\"max-width:620px;margin:0 auto\">\n <p style=\"margin:0 0 16px;font-size:16px\">${escapeXml(senderName || merchantName)} sent you a gift card.</p>\n <div style=\"padding:28px;border-radius:12px;background:${design.palette.background};color:${design.palette.foreground};border-top:6px solid ${design.palette.accent}\">\n <div style=\"font-size:20px;font-weight:700\">${escapeXml(merchantName)}</div>\n <div style=\"margin-top:4px;font-size:12px;opacity:.7\">GIFT CARD</div>\n <div style=\"margin-top:56px;font-size:30px;font-weight:700\">${escapeXml(design.name)}</div>\n <table role=\"presentation\" style=\"width:100%;margin-top:40px;color:inherit\"><tr>\n <td><div style=\"font-size:11px;opacity:.6\">TO</div><div style=\"margin-top:6px;font-size:18px;font-weight:600\">${escapeXml(recipientName || 'Someone special')}</div></td>\n <td><div style=\"font-size:11px;opacity:.6\">FROM</div><div style=\"margin-top:6px;font-size:18px;font-weight:600\">${escapeXml(senderName || merchantName)}</div></td>\n ${amount ? `<td style=\"text-align:right;font-size:16px;font-weight:600\">${escapeXml(amount)}</td>` : ''}\n </tr></table>\n ${code ? `<div style=\"margin-top:36px;font-size:11px;opacity:.6\">REDEEM WITH</div><div style=\"margin-top:6px;font-family:monospace;font-size:20px;font-weight:700\">${escapeXml(code)}</div>` : ''}\n </div>\n </div>\n</div>`;\n}\n\nexport function GiftCardArtwork({\n design,\n merchantName,\n amount,\n recipientName,\n senderName,\n code,\n className = '',\n}: GiftCardArtworkProps) {\n return (\n <div\n className={`relative aspect-[1.586/1] w-full overflow-hidden rounded-md p-5 ${className}`}\n style={{\n color: design.palette.foreground,\n backgroundColor: design.palette.background,\n backgroundImage: `repeating-linear-gradient(135deg, transparent 0 18px, ${design.palette.accent}22 18px 19px)`,\n }}\n >\n {design.imageUrl && (\n <img\n src={design.imageUrl}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n )}\n <div\n className=\"absolute inset-x-0 top-0 h-1\"\n style={{ backgroundColor: design.palette.accent }}\n />\n <div className=\"relative flex h-full flex-col justify-between\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span\n className=\"flex h-7 w-7 items-center justify-center rounded-full\"\n style={{\n color: design.palette.background,\n backgroundColor: design.palette.accent,\n }}\n >\n <Gift className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold\">{merchantName}</p>\n <p className=\"text-[10px] font-medium uppercase opacity-65\">\n Gift card\n </p>\n </div>\n </div>\n {amount && (\n <span className=\"shrink-0 text-sm font-medium opacity-80\">\n {amount}\n </span>\n )}\n </div>\n <div>\n <p className=\"text-lg font-semibold\">{design.name}</p>\n <div className=\"mt-3 grid grid-cols-2 gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-[10px] font-semibold uppercase opacity-55\">To</p>\n <p className=\"truncate text-sm font-medium\">\n {recipientName || 'Someone special'}\n </p>\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-[10px] font-semibold uppercase opacity-55\">\n From\n </p>\n <p className=\"truncate text-sm font-medium\">\n {senderName || merchantName}\n </p>\n </div>\n </div>\n {code && <p className=\"mt-2 truncate font-mono text-xs opacity-70\">{code}</p>}\n </div>\n </div>\n </div>\n );\n}"],"mappings":"AAyLQ,cAsBI,YAtBJ;AAzLR,SAAS,YAAY;AAGd,MAAM,oBAA8C;AAAA,EACzD;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAeA,SAAS,UAAU,OAAuB;AACxC,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,QAAQ;AAC7B;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgC;AAC9B,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,SAAS,OAAO,QAAQ;AAE9B,SAAO;AAAA,4JACmJ,MAAM;AAAA,kDAChH,UAAU;AAAA;AAAA,gDAEZ,MAAM;AAAA,yCACb,MAAM;AAAA,+BAChB,UAAU,sEAAsE,UAAU,YAAY,CAAC;AAAA,gCACtG,UAAU;AAAA,IACtC,SAAS,iDAAiD,UAAU,0FAA0F,UAAU,MAAM,CAAC,YAAY,EAAE;AAAA,+BAClK,UAAU,sEAAsE,UAAU,OAAO,IAAI,CAAC;AAAA,+BACtG,UAAU;AAAA,+BACV,UAAU,sEAAsE,UAAU,iBAAiB,iBAAiB,CAAC;AAAA,gCAC5H,UAAU;AAAA,gCACV,UAAU,sEAAsE,UAAU,cAAc,YAAY,CAAC;AAAA,IACjJ,OAAO,8BAA8B,UAAU,uIAAuI,UAAU,8DAA8D,UAAU,IAAI,CAAC,YAAY,EAAE;AAAA;AAE/R;AAEA,eAAsB,wBACpB,MACe;AACf,QAAM,UAAU,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,GAAG;AAAA,IAClD,MAAM;AAAA,EACR,CAAC;AACD,QAAM,SAAS,IAAI,gBAAgB,OAAO;AAC1C,QAAM,QAAQ,IAAI,MAAM;AACxB,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,SAAS,MAAM,QAAQ;AAC7B,UAAM,UAAU,MAAM,OAAO,IAAI,MAAM,kCAAkC,CAAC;AAC1E,UAAM,MAAM;AAAA,EACd,CAAC;AAED,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,QAAM,UAAU,OAAO,WAAW,IAAI;AACtC,MAAI,CAAC,SAAS;AACZ,QAAI,gBAAgB,MAAM;AAC1B,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AACA,UAAQ,UAAU,OAAO,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AAC1D,MAAI,gBAAgB,MAAM;AAE1B,QAAM,UAAU,MAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,WAAO;AAAA,MACL,CAAC,SACC,OACI,QAAQ,IAAI,IACZ,OAAO,IAAI,MAAM,kCAAkC,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF,CAAC;AACD,QAAM,MAAM,IAAI,gBAAgB,OAAO;AACvC,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,OAAO;AACZ,OAAK,WAAW,aAAa,KAAK,QAAQ,SAAS;AACnD,WAAS,KAAK,YAAY,IAAI;AAC9B,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,aAAW,MAAM,IAAI,gBAAgB,GAAG,GAAG,CAAC;AAC9C;AAEO,SAAS,wBAAwB,MAAmC;AACzE,QAAM,EAAE,QAAQ,cAAc,QAAQ,eAAe,YAAY,KAAK,IAAI;AAC1E,SAAO;AAAA;AAAA,gDAEuC,UAAU,cAAc,YAAY,CAAC;AAAA,6DACxB,OAAO,QAAQ,UAAU,UAAU,OAAO,QAAQ,UAAU,yBAAyB,OAAO,QAAQ,MAAM;AAAA,oDACnH,UAAU,YAAY,CAAC;AAAA;AAAA,oEAEP,UAAU,OAAO,IAAI,CAAC;AAAA;AAAA,wHAE8B,UAAU,iBAAiB,iBAAiB,CAAC;AAAA,0HAC3C,UAAU,cAAc,YAAY,CAAC;AAAA,UACrJ,SAAS,+DAA+D,UAAU,MAAM,CAAC,UAAU,EAAE;AAAA;AAAA,QAEvG,OAAO,4JAA4J,UAAU,IAAI,CAAC,WAAW,EAAE;AAAA;AAAA;AAAA;AAIvM;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AACd,GAAyB;AACvB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,mEAAmE,SAAS;AAAA,MACvF,OAAO;AAAA,QACL,OAAO,OAAO,QAAQ;AAAA,QACtB,iBAAiB,OAAO,QAAQ;AAAA,QAChC,iBAAiB,yDAAyD,OAAO,QAAQ,MAAM;AAAA,MACjG;AAAA,MAEC;AAAA,eAAO,YACN;AAAA,UAAC;AAAA;AAAA,YACC,KAAK,OAAO;AAAA,YACZ,KAAI;AAAA,YACJ,WAAU;AAAA;AAAA,QACZ;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,iBAAiB,OAAO,QAAQ,OAAO;AAAA;AAAA,QAClD;AAAA,QACA,qBAAC,SAAI,WAAU,iDACb;AAAA,+BAAC,SAAI,WAAU,2CACb;AAAA,iCAAC,SAAI,WAAU,mCACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO;AAAA,oBACL,OAAO,OAAO,QAAQ;AAAA,oBACtB,iBAAiB,OAAO,QAAQ;AAAA,kBAClC;AAAA,kBAEA,8BAAC,QAAK,WAAU,WAAU;AAAA;AAAA,cAC5B;AAAA,cACA,qBAAC,SAAI,WAAU,WACb;AAAA,oCAAC,OAAE,WAAU,kCAAkC,wBAAa;AAAA,gBAC5D,oBAAC,OAAE,WAAU,gDAA+C,uBAE5D;AAAA,iBACF;AAAA,eACF;AAAA,YACC,UACC,oBAAC,UAAK,WAAU,2CACb,kBACH;AAAA,aAEJ;AAAA,UACA,qBAAC,SACC;AAAA,gCAAC,OAAE,WAAU,yBAAyB,iBAAO,MAAK;AAAA,YAClD,qBAAC,SAAI,WAAU,+BACb;AAAA,mCAAC,SAAI,WAAU,WACb;AAAA,oCAAC,OAAE,WAAU,kDAAiD,gBAAE;AAAA,gBAChE,oBAAC,OAAE,WAAU,gCACV,2BAAiB,mBACpB;AAAA,iBACF;AAAA,cACA,qBAAC,SAAI,WAAU,WACb;AAAA,oCAAC,OAAE,WAAU,kDAAiD,kBAE9D;AAAA,gBACA,oBAAC,OAAE,WAAU,gCACV,wBAAc,cACjB;AAAA,iBACF;AAAA,eACF;AAAA,YACC,QAAQ,oBAAC,OAAE,WAAU,8CAA8C,gBAAK;AAAA,aAC3E;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { GiftCardStatus } from '@withpotter/lib/api';
|
|
2
|
+
/**
|
|
3
|
+
* One vocabulary for a card's state, used by the filter chips, the status badge
|
|
4
|
+
* in the list and the hints. "Used" and "Redeemed" used to describe two
|
|
5
|
+
* different things with nearly the same word: a card whose balance is all
|
|
6
|
+
* spent, and the amount spent across every card. The state is now "Fully
|
|
7
|
+
* spent" and the amount is "Spent".
|
|
8
|
+
*/
|
|
9
|
+
export declare const STATUS_LABEL: Record<GiftCardStatus, string>;
|
|
10
|
+
export declare const STATUS_HINT: Record<GiftCardStatus, string>;
|
|
11
|
+
export declare const ALL_HINT = "Every card, whatever its state.";
|
|
12
|
+
export interface StatusFilter {
|
|
13
|
+
value: GiftCardStatus | '';
|
|
14
|
+
label: string;
|
|
15
|
+
hint: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const STATUS_FILTERS: StatusFilter[];
|
|
18
|
+
/** The two lines behind the info icon beside the page title. */
|
|
19
|
+
export declare const PAGE_HINT_LINES: string[];
|
|
20
|
+
/**
|
|
21
|
+
* True when there is nothing to show and nothing narrowing the list: no cards
|
|
22
|
+
* have ever been issued. A filtered list that comes back empty is not first
|
|
23
|
+
* run, and neither is a page still loading.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isFirstRun(input: {
|
|
26
|
+
loading: boolean;
|
|
27
|
+
search: string;
|
|
28
|
+
status: GiftCardStatus | '';
|
|
29
|
+
count: number;
|
|
30
|
+
totalIssued: number | null | undefined;
|
|
31
|
+
}): boolean;
|
|
32
|
+
/** The symbol a currency is written with, falling back to its code. */
|
|
33
|
+
export declare function currencySymbol(currency: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Shares one request between callers asking for the same thing at the same
|
|
36
|
+
* time. A key is forgotten the moment its request settles, so a later call
|
|
37
|
+
* (a reload, a poll) still goes to the network.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createInFlightDeduper(): {
|
|
40
|
+
run<T>(key: string, start: () => Promise<T>): Promise<T>;
|
|
41
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const STATUS_LABEL = {
|
|
2
|
+
active: "Active",
|
|
3
|
+
used: "Fully spent",
|
|
4
|
+
expired: "Expired",
|
|
5
|
+
cancelled: "Cancelled"
|
|
6
|
+
};
|
|
7
|
+
const STATUS_HINT = {
|
|
8
|
+
active: "Can still be spent.",
|
|
9
|
+
used: "The whole balance has been spent.",
|
|
10
|
+
expired: "Passed its expiry date.",
|
|
11
|
+
cancelled: "Voided by you. The record is kept."
|
|
12
|
+
};
|
|
13
|
+
const ALL_HINT = "Every card, whatever its state.";
|
|
14
|
+
const STATUS_FILTERS = [
|
|
15
|
+
{ value: "", label: "All", hint: ALL_HINT },
|
|
16
|
+
...["active", "used", "expired", "cancelled"].map((value) => ({
|
|
17
|
+
value,
|
|
18
|
+
label: STATUS_LABEL[value],
|
|
19
|
+
hint: STATUS_HINT[value]
|
|
20
|
+
}))
|
|
21
|
+
];
|
|
22
|
+
const PAGE_HINT_LINES = [
|
|
23
|
+
"Credit a customer pays for now and spends later. Share the code with them.",
|
|
24
|
+
"Open a card to email it or redeem some of its balance. Check balance looks up a code."
|
|
25
|
+
];
|
|
26
|
+
function isFirstRun(input) {
|
|
27
|
+
return !input.loading && !input.search.trim() && !input.status && input.count === 0 && (input.totalIssued ?? 0) === 0;
|
|
28
|
+
}
|
|
29
|
+
function currencySymbol(currency) {
|
|
30
|
+
try {
|
|
31
|
+
const part = new Intl.NumberFormat("en", {
|
|
32
|
+
style: "currency",
|
|
33
|
+
currency,
|
|
34
|
+
currencyDisplay: "narrowSymbol"
|
|
35
|
+
}).formatToParts(0).find((p) => p.type === "currency");
|
|
36
|
+
return part?.value ?? currency;
|
|
37
|
+
} catch {
|
|
38
|
+
return currency;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function createInFlightDeduper() {
|
|
42
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
43
|
+
return {
|
|
44
|
+
run(key, start) {
|
|
45
|
+
const existing = inFlight.get(key);
|
|
46
|
+
if (existing) return existing;
|
|
47
|
+
const promise = start().finally(() => {
|
|
48
|
+
if (inFlight.get(key) === promise) inFlight.delete(key);
|
|
49
|
+
});
|
|
50
|
+
inFlight.set(key, promise);
|
|
51
|
+
return promise;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
ALL_HINT,
|
|
57
|
+
PAGE_HINT_LINES,
|
|
58
|
+
STATUS_FILTERS,
|
|
59
|
+
STATUS_HINT,
|
|
60
|
+
STATUS_LABEL,
|
|
61
|
+
createInFlightDeduper,
|
|
62
|
+
currencySymbol,
|
|
63
|
+
isFirstRun
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=gift-card-logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/features/gift-cards/gift-card-logic.ts"],"sourcesContent":["import type { GiftCardStatus } from '@withpotter/lib/api';\n\n/**\n * One vocabulary for a card's state, used by the filter chips, the status badge\n * in the list and the hints. \"Used\" and \"Redeemed\" used to describe two\n * different things with nearly the same word: a card whose balance is all\n * spent, and the amount spent across every card. The state is now \"Fully\n * spent\" and the amount is \"Spent\".\n */\nexport const STATUS_LABEL: Record<GiftCardStatus, string> = {\n active: 'Active',\n used: 'Fully spent',\n expired: 'Expired',\n cancelled: 'Cancelled',\n};\n\nexport const STATUS_HINT: Record<GiftCardStatus, string> = {\n active: 'Can still be spent.',\n used: 'The whole balance has been spent.',\n expired: 'Passed its expiry date.',\n cancelled: 'Voided by you. The record is kept.',\n};\n\nexport const ALL_HINT = 'Every card, whatever its state.';\n\nexport interface StatusFilter {\n value: GiftCardStatus | '';\n label: string;\n hint: string;\n}\n\nexport const STATUS_FILTERS: StatusFilter[] = [\n { value: '', label: 'All', hint: ALL_HINT },\n ...(['active', 'used', 'expired', 'cancelled'] as const).map((value) => ({\n value,\n label: STATUS_LABEL[value],\n hint: STATUS_HINT[value],\n })),\n];\n\n/** The two lines behind the info icon beside the page title. */\nexport const PAGE_HINT_LINES = [\n 'Credit a customer pays for now and spends later. Share the code with them.',\n 'Open a card to email it or redeem some of its balance. Check balance looks up a code.',\n];\n\n/**\n * True when there is nothing to show and nothing narrowing the list: no cards\n * have ever been issued. A filtered list that comes back empty is not first\n * run, and neither is a page still loading.\n */\nexport function isFirstRun(input: {\n loading: boolean;\n search: string;\n status: GiftCardStatus | '';\n count: number;\n totalIssued: number | null | undefined;\n}): boolean {\n return (\n !input.loading &&\n !input.search.trim() &&\n !input.status &&\n input.count === 0 &&\n (input.totalIssued ?? 0) === 0\n );\n}\n\n/** The symbol a currency is written with, falling back to its code. */\nexport function currencySymbol(currency: string): string {\n try {\n const part = new Intl.NumberFormat('en', {\n style: 'currency',\n currency,\n currencyDisplay: 'narrowSymbol',\n })\n .formatToParts(0)\n .find((p) => p.type === 'currency');\n return part?.value ?? currency;\n } catch {\n return currency;\n }\n}\n\n/**\n * Shares one request between callers asking for the same thing at the same\n * time. A key is forgotten the moment its request settles, so a later call\n * (a reload, a poll) still goes to the network.\n */\nexport function createInFlightDeduper() {\n const inFlight = new Map<string, Promise<unknown>>();\n return {\n run<T>(key: string, start: () => Promise<T>): Promise<T> {\n const existing = inFlight.get(key);\n if (existing) return existing as Promise<T>;\n const promise = start().finally(() => {\n if (inFlight.get(key) === promise) inFlight.delete(key);\n });\n inFlight.set(key, promise);\n return promise;\n },\n };\n}\n"],"mappings":"AASO,MAAM,eAA+C;AAAA,EAC1D,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AACb;AAEO,MAAM,cAA8C;AAAA,EACzD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AACb;AAEO,MAAM,WAAW;AAQjB,MAAM,iBAAiC;AAAA,EAC5C,EAAE,OAAO,IAAI,OAAO,OAAO,MAAM,SAAS;AAAA,EAC1C,GAAI,CAAC,UAAU,QAAQ,WAAW,WAAW,EAAY,IAAI,CAAC,WAAW;AAAA,IACvE;AAAA,IACA,OAAO,aAAa,KAAK;AAAA,IACzB,MAAM,YAAY,KAAK;AAAA,EACzB,EAAE;AACJ;AAGO,MAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AACF;AAOO,SAAS,WAAW,OAMf;AACV,SACE,CAAC,MAAM,WACP,CAAC,MAAM,OAAO,KAAK,KACnB,CAAC,MAAM,UACP,MAAM,UAAU,MACf,MAAM,eAAe,OAAO;AAEjC;AAGO,SAAS,eAAe,UAA0B;AACvD,MAAI;AACF,UAAM,OAAO,IAAI,KAAK,aAAa,MAAM;AAAA,MACvC,OAAO;AAAA,MACP;AAAA,MACA,iBAAiB;AAAA,IACnB,CAAC,EACE,cAAc,CAAC,EACf,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AACpC,WAAO,MAAM,SAAS;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,wBAAwB;AACtC,QAAM,WAAW,oBAAI,IAA8B;AACnD,SAAO;AAAA,IACL,IAAO,KAAa,OAAqC;AACvD,YAAM,WAAW,SAAS,IAAI,GAAG;AACjC,UAAI,SAAU,QAAO;AACrB,YAAM,UAAU,MAAM,EAAE,QAAQ,MAAM;AACpC,YAAI,SAAS,IAAI,GAAG,MAAM,QAAS,UAAS,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,eAAS,IAAI,KAAK,OAAO;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|