@fluid-app/portal-sdk 0.1.469 → 0.1.471
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/authoring/portal-api/api.md +227 -0
- package/authoring/widget-api/api.md +6 -0
- package/authoring/widget-api/core/src/registries/property-schema-types.md +3769 -0
- package/authoring/widget-api/sdk/src/widgets/worker.md +3683 -0
- package/dist/{CarouselWidget-CnjImV4p.cjs → CarouselWidget-B9_YaBRd.cjs} +7 -3
- package/dist/{CarouselWidget-CnjImV4p.cjs.map → CarouselWidget-B9_YaBRd.cjs.map} +1 -1
- package/dist/{CarouselWidget-xVj0Nl_1.cjs → CarouselWidget-BEPWgTCi.cjs} +1 -1
- package/dist/{CarouselWidget-DcoqH7YF.mjs → CarouselWidget-u58GTUD2.mjs} +7 -3
- package/dist/{CarouselWidget-DcoqH7YF.mjs.map → CarouselWidget-u58GTUD2.mjs.map} +1 -1
- package/dist/{ChartWidget-DhH_Q0e9.cjs → ChartWidget-C6hsftO9.cjs} +1 -1
- package/dist/{ChartWidget-CFyWkbfl.mjs → ChartWidget-J_JLCsug.mjs} +9 -2
- package/dist/{ChartWidget-CFyWkbfl.mjs.map → ChartWidget-J_JLCsug.mjs.map} +1 -1
- package/dist/{ChartWidget-DF-KE_5t.cjs → ChartWidget-q9yjk-79.cjs} +9 -2
- package/dist/{ChartWidget-DF-KE_5t.cjs.map → ChartWidget-q9yjk-79.cjs.map} +1 -1
- package/dist/{FluidProvider-CHo5kVRs.cjs → FluidProvider-DD1pE8b6.cjs} +2 -2
- package/dist/{FluidProvider-CHo5kVRs.cjs.map → FluidProvider-DD1pE8b6.cjs.map} +1 -1
- package/dist/{FluidProvider-BUV8QY8z.mjs → FluidProvider-DoJ-j6H-.mjs} +2 -2
- package/dist/{FluidProvider-BUV8QY8z.mjs.map → FluidProvider-DoJ-j6H-.mjs.map} +1 -1
- package/dist/{ImageWidget-BK_f_L0T.mjs → ImageWidget-B2SfenSC.mjs} +2 -2
- package/dist/{ImageWidget-BK_f_L0T.mjs.map → ImageWidget-B2SfenSC.mjs.map} +1 -1
- package/dist/{ImageWidget-8WfRzR60.cjs → ImageWidget-BFyfpxLg.cjs} +1 -1
- package/dist/{ImageWidget-C9PCTFpZ.cjs → ImageWidget-C7U9t7a3.cjs} +2 -2
- package/dist/{ImageWidget-C9PCTFpZ.cjs.map → ImageWidget-C7U9t7a3.cjs.map} +1 -1
- package/dist/{LinkWidget-DRMzQhQz.cjs → LinkWidget-Cjzrpnzd.cjs} +4 -1
- package/dist/LinkWidget-Cjzrpnzd.cjs.map +1 -0
- package/dist/{LinkWidget-BqLCMzfY.cjs → LinkWidget-D3acO4R8.cjs} +1 -1
- package/dist/{LinkWidget-C3HRFueg.mjs → LinkWidget-Dlw2rzK6.mjs} +4 -1
- package/dist/LinkWidget-Dlw2rzK6.mjs.map +1 -0
- package/dist/{ListWidget-BiBx02S8.cjs → ListWidget-DWAtSTK6.cjs} +1 -1
- package/dist/{ListWidget-D7Ws1bMu.mjs → ListWidget-W5Tm4mWy.mjs} +7 -3
- package/dist/{ListWidget-D7Ws1bMu.mjs.map → ListWidget-W5Tm4mWy.mjs.map} +1 -1
- package/dist/{ListWidget-Ge83X2-d.cjs → ListWidget-YrcH5_Ez.cjs} +7 -3
- package/dist/{ListWidget-Ge83X2-d.cjs.map → ListWidget-YrcH5_Ez.cjs.map} +1 -1
- package/dist/{MessagingScreen-KYbF9U2N.cjs → MessagingScreen-B8Bcy-2V.cjs} +2 -2
- package/dist/{MessagingScreen-KYbF9U2N.cjs.map → MessagingScreen-B8Bcy-2V.cjs.map} +1 -1
- package/dist/{MessagingScreen-r40mXBJR.mjs → MessagingScreen-BBoYyLPj.mjs} +11 -11
- package/dist/{MessagingScreen-CC2FSdcv.cjs → MessagingScreen-BJ7NTczB.cjs} +11 -11
- package/dist/{MessagingScreen-B9LmuGG4.mjs → MessagingScreen-vaNfWYeM.mjs} +2 -2
- package/dist/{MessagingScreen-B9LmuGG4.mjs.map → MessagingScreen-vaNfWYeM.mjs.map} +1 -1
- package/dist/{NestedWidget-nWK5H1NG.cjs → NestedWidget-B5iyFylx.cjs} +6 -2
- package/dist/{NestedWidget-nWK5H1NG.cjs.map → NestedWidget-B5iyFylx.cjs.map} +1 -1
- package/dist/{NestedWidget-J8fPrBNA.mjs → NestedWidget-Bdll8TLQ.mjs} +6 -2
- package/dist/{NestedWidget-J8fPrBNA.mjs.map → NestedWidget-Bdll8TLQ.mjs.map} +1 -1
- package/dist/{NestedWidget-Bhltm3Db.cjs → NestedWidget-CBYJ_Qw7.cjs} +1 -1
- package/dist/{OrdersScreen-jhkTEp5y.cjs → OrdersScreen-BdtVN1wl.cjs} +11 -11
- package/dist/{OrdersScreen-BMslkiOK.mjs → OrdersScreen-ClB_Yor-.mjs} +2 -2
- package/dist/{OrdersScreen-BMslkiOK.mjs.map → OrdersScreen-ClB_Yor-.mjs.map} +1 -1
- package/dist/{OrdersScreen-CHt2QwB7.mjs → OrdersScreen-D2cymJtp.mjs} +11 -11
- package/dist/{OrdersScreen-DkRte66f.cjs → OrdersScreen-DnJa329w.cjs} +2 -2
- package/dist/{OrdersScreen-DkRte66f.cjs.map → OrdersScreen-DnJa329w.cjs.map} +1 -1
- package/dist/{ProfileScreen-BP5KEyW0.mjs → ProfileScreen-CIjBpDIq.mjs} +12 -12
- package/dist/{ProfileScreen-Ba8B0_Bm.cjs → ProfileScreen-CpI9XiZQ.cjs} +9 -4
- package/dist/ProfileScreen-CpI9XiZQ.cjs.map +1 -0
- package/dist/{ProfileScreen-BAbxbpdQ.cjs → ProfileScreen-DUEVFZqo.cjs} +12 -12
- package/dist/{ProfileScreen-Cw1hajhU.mjs → ProfileScreen-TbmOka5D.mjs} +9 -4
- package/dist/ProfileScreen-TbmOka5D.mjs.map +1 -0
- package/dist/{ShopWidget-jQ-osGyn.mjs → ShopWidget-BH0Pcepg.mjs} +6 -2
- package/dist/{ShopWidget-jQ-osGyn.mjs.map → ShopWidget-BH0Pcepg.mjs.map} +1 -1
- package/dist/{ShopWidget-Dc3fH_tP.cjs → ShopWidget-RfF5ZRv2.cjs} +6 -2
- package/dist/{ShopWidget-Dc3fH_tP.cjs.map → ShopWidget-RfF5ZRv2.cjs.map} +1 -1
- package/dist/{ShopWidget-gc_BbyrJ.cjs → ShopWidget-ecp3h54J.cjs} +1 -1
- package/dist/{SubscriptionsScreen-BscwpAw8.cjs → SubscriptionsScreen-BiNmEFLP.cjs} +4 -4
- package/dist/{SubscriptionsScreen-BscwpAw8.cjs.map → SubscriptionsScreen-BiNmEFLP.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-D2uyJGIE.mjs → SubscriptionsScreen-NCU2I079.mjs} +4 -4
- package/dist/{SubscriptionsScreen-D2uyJGIE.mjs.map → SubscriptionsScreen-NCU2I079.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-Nfbt2u5r.cjs → SubscriptionsScreen-cuw6yk56.cjs} +12 -12
- package/dist/{SubscriptionsScreen-DU5AKlfR.mjs → SubscriptionsScreen-fv_xxA9j.mjs} +12 -12
- package/dist/{TableWidget-B4rGem0e.cjs → TableWidget-B8axD206.cjs} +1 -1
- package/dist/{TableWidget-LolaiYXA.mjs → TableWidget-C861T_Ac.mjs} +8 -1
- package/dist/{TableWidget-LolaiYXA.mjs.map → TableWidget-C861T_Ac.mjs.map} +1 -1
- package/dist/{TableWidget-Dfzx5jaI.cjs → TableWidget-CAgARK2Y.cjs} +8 -1
- package/dist/{TableWidget-Dfzx5jaI.cjs.map → TableWidget-CAgARK2Y.cjs.map} +1 -1
- package/dist/build-remote-widget-plugin-manifests-BDGS5t6k.d.mts +87 -0
- package/dist/build-remote-widget-plugin-manifests-BDGS5t6k.d.mts.map +1 -0
- package/dist/build-remote-widget-plugin-manifests-BS2FjZRV.d.cts +87 -0
- package/dist/build-remote-widget-plugin-manifests-BS2FjZRV.d.cts.map +1 -0
- package/dist/{build-widget-registry-BwiPIMcu.d.mts → build-widget-registry-ByUJk3-B.d.mts} +2 -2
- package/dist/{build-widget-registry-BwiPIMcu.d.mts.map → build-widget-registry-ByUJk3-B.d.mts.map} +1 -1
- package/dist/{build-widget-registry-BmU54VJb.cjs → build-widget-registry-CCmtBo8w.cjs} +17 -17
- package/dist/build-widget-registry-CCmtBo8w.cjs.map +1 -0
- package/dist/{build-widget-registry-DSE7CmVe.d.cts → build-widget-registry-Dsp9P8gq.d.cts} +2 -2
- package/dist/{build-widget-registry-DSE7CmVe.d.cts.map → build-widget-registry-Dsp9P8gq.d.cts.map} +1 -1
- package/dist/{build-widget-registry-Bimd6Pxq.mjs → build-widget-registry-DvCtfn9d.mjs} +17 -17
- package/dist/build-widget-registry-DvCtfn9d.mjs.map +1 -0
- package/dist/index.cjs +29 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +40 -13
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +29 -24
- package/dist/index.mjs.map +1 -1
- package/dist/{link-type-schema-fields-DJBFdG7t.mjs → link-type-schema-fields-CncR6GKa.mjs} +3 -1
- package/dist/link-type-schema-fields-CncR6GKa.mjs.map +1 -0
- package/dist/{link-type-schema-fields-CQXXgSqn.cjs → link-type-schema-fields-DvSzjbwD.cjs} +3 -1
- package/dist/link-type-schema-fields-DvSzjbwD.cjs.map +1 -0
- package/dist/{build-remote-widget-plugin-manifests-CFmLot7p.d.cts → portal-function-DYWFgeRx.d.cts} +826 -796
- package/dist/portal-function-DYWFgeRx.d.cts.map +1 -0
- package/dist/{build-remote-widget-plugin-manifests-BsgLYqar.d.mts → portal-function-DgqXwF13.d.mts} +932 -902
- package/dist/portal-function-DgqXwF13.d.mts.map +1 -0
- package/dist/registries-DgzzKX8_.cjs.map +1 -1
- package/dist/registries-Dvq4M6wO.mjs.map +1 -1
- package/dist/{use-portal-card-add-3ds-flow-BE4p2zYl.cjs → use-portal-card-add-3ds-flow-CRiGY6e0.cjs} +3 -3
- package/dist/{use-portal-card-add-3ds-flow-BE4p2zYl.cjs.map → use-portal-card-add-3ds-flow-CRiGY6e0.cjs.map} +1 -1
- package/dist/{use-portal-card-add-3ds-flow-DzhPOYGn.mjs → use-portal-card-add-3ds-flow-DjeKiZvQ.mjs} +3 -3
- package/dist/{use-portal-card-add-3ds-flow-DzhPOYGn.mjs.map → use-portal-card-add-3ds-flow-DjeKiZvQ.mjs.map} +1 -1
- package/dist/utils/build-remote-widget-plugin-manifests.cjs +9 -9
- package/dist/utils/build-remote-widget-plugin-manifests.d.cts +1 -1
- package/dist/utils/build-remote-widget-plugin-manifests.d.mts +1 -1
- package/dist/utils/build-remote-widget-plugin-manifests.mjs +9 -9
- package/dist/utils/build-widget-registry.cjs +9 -9
- package/dist/utils/build-widget-registry.d.cts +1 -1
- package/dist/utils/build-widget-registry.d.mts +1 -1
- package/dist/utils/build-widget-registry.mjs +9 -9
- package/dist/widgets/worker.cjs +20 -1
- package/dist/widgets/worker.cjs.map +1 -1
- package/dist/widgets/worker.d.cts +67 -4
- package/dist/widgets/worker.d.cts.map +1 -1
- package/dist/widgets/worker.d.mts +67 -4
- package/dist/widgets/worker.d.mts.map +1 -1
- package/dist/widgets/worker.mjs +20 -1
- package/dist/widgets/worker.mjs.map +1 -1
- package/package.json +18 -17
- package/dist/LinkWidget-C3HRFueg.mjs.map +0 -1
- package/dist/LinkWidget-DRMzQhQz.cjs.map +0 -1
- package/dist/ProfileScreen-Ba8B0_Bm.cjs.map +0 -1
- package/dist/ProfileScreen-Cw1hajhU.mjs.map +0 -1
- package/dist/build-remote-widget-plugin-manifests-BsgLYqar.d.mts.map +0 -1
- package/dist/build-remote-widget-plugin-manifests-CFmLot7p.d.cts.map +0 -1
- package/dist/build-widget-registry-Bimd6Pxq.mjs.map +0 -1
- package/dist/build-widget-registry-BmU54VJb.cjs.map +0 -1
- package/dist/link-type-schema-fields-CQXXgSqn.cjs.map +0 -1
- package/dist/link-type-schema-fields-DJBFdG7t.mjs.map +0 -1
- package/dist/portal-function-85qUlAVz.d.mts +0 -76
- package/dist/portal-function-85qUlAVz.d.mts.map +0 -1
- package/dist/portal-function-CW3U6P5p.d.cts +0 -76
- package/dist/portal-function-CW3U6P5p.d.cts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProfileScreen-TbmOka5D.mjs","names":[],"sources":["../src/screens/profile/derive-last-name.ts","../src/screens/profile/derive-card-holder-name.ts","../src/screens/profile/ProfileLayout.tsx","../src/screens/profile-card-add-success.ts","../src/screens/ProfileContentScreen.tsx","../src/screens/ProfileScreen.tsx"],"sourcesContent":["export function deriveLastName(firstName: string, fullName: string): string {\n if (!firstName) return \"\";\n const prefix = `${firstName} `;\n return fullName.startsWith(prefix) ? fullName.slice(prefix.length) : \"\";\n}\n","import type { fluidPay } from \"@fluid-app/fluid-pay-core\";\n\n// The edit dialog can only change billing_address.name; card_holder has no\n// update API.\nexport function deriveCardHolderName(\n method: fluidPay.CustomerPaymentMethod,\n): string {\n return (\n method.billing_address?.name?.trim() ||\n method.details.card_holder?.trim() ||\n \"\"\n );\n}\n","import { type JSX, type ReactNode, useState, type ComponentType } from \"react\";\nimport {\n ResponsiveDialog,\n type ResponsiveDialogPresentation,\n} from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport {\n Sparkles,\n Pencil,\n Plus,\n Copy,\n Link2,\n MapPin,\n User,\n Mail,\n Phone,\n Quote,\n Linkedin,\n Facebook,\n Twitter,\n Instagram,\n Youtube,\n Globe,\n LogOut,\n} from \"lucide-react\";\nimport type { fluidPay } from \"@fluid-app/fluid-pay-core\";\nimport type { PointsLedger } from \"@fluid-app/profile-core\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\nimport { EllipsesDropdown } from \"@fluid-app/profile-ui\";\nimport {\n brandIconUrl,\n GENERIC_PAYMENT_ICON_URL,\n} from \"@fluid-app/payments-display/brands\";\nimport { maskCardNumber } from \"@fluid-app/payments-display/display\";\nimport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nimport { titleCaseFromSnake } from \"@fluid-app/portal-core/format/title-case\";\nimport { deriveCardHolderName } from \"./derive-card-holder-name\";\n\ninterface ProfileLayoutProps {\n account: AccountRep;\n pointsBalance: number | undefined;\n pointsLedger?: PointsLedger[];\n pointsLabel: { singular: string; plural: string };\n rewardsEnabled?: boolean;\n addresses: fluidPay.CustomerAddress[];\n paymentMethods: fluidPay.CustomerPaymentMethod[];\n mySiteDisplayUrl: string;\n socialLinks: Partial<Record<SocialKey, string>>;\n onEditPersonalInfo: () => void;\n onAddAddress: () => void;\n onEditAddress: (address: fluidPay.CustomerAddress) => void;\n onDeleteAddress: (address: fluidPay.CustomerAddress) => void;\n onAddPaymentMethod: () => void;\n onEditPaymentMethod: (method: fluidPay.CustomerPaymentMethod) => void;\n onCopyMySiteLink: () => void;\n onEditMySiteLink?: () => void;\n onConnectSocial: (key: SocialKey) => void;\n onSignOut?: () => void;\n presentation?: ResponsiveDialogPresentation;\n}\n\nexport function ProfileLayout(props: ProfileLayoutProps): JSX.Element {\n const { t } = useProfileTranslation();\n const {\n account,\n pointsBalance,\n pointsLedger,\n pointsLabel,\n rewardsEnabled,\n addresses,\n paymentMethods,\n mySiteDisplayUrl,\n socialLinks,\n onEditPersonalInfo,\n onAddAddress,\n onEditAddress,\n onDeleteAddress,\n onAddPaymentMethod,\n onEditPaymentMethod,\n onCopyMySiteLink,\n onEditMySiteLink,\n onConnectSocial,\n onSignOut,\n presentation = \"dialog\",\n } = props;\n\n const fullName =\n `${account.first_name ?? \"\"} ${account.last_name ?? \"\"}`.trim();\n const heroFirstName = (account.first_name || \"\").trim().split(\" \")[0] || \"\";\n const heroDisplay =\n heroFirstName || account.email?.split(\"@\")[0] || t(\"account_fallback\");\n const connectedSocialCount = Object.values(socialLinks).filter(\n (v) => typeof v === \"string\" && v.length > 0,\n ).length;\n const [isPointsHistoryOpen, setIsPointsHistoryOpen] = useState(false);\n const POINTS_PREVIEW_COUNT = 4;\n const ledger = pointsLedger ?? [];\n const ledgerPreview = ledger.slice(0, POINTS_PREVIEW_COUNT);\n const hasMoreLedger = ledger.length > POINTS_PREVIEW_COUNT;\n // Configured points label: capitalized plural for headings, count-aware form\n // (singular at 1) for \"N <label>\" amounts.\n const pointsLabelPluralCap = capitalize(pointsLabel.plural);\n const pointsCountLabel = (n: number): string =>\n n === 1 ? pointsLabel.singular : pointsLabel.plural;\n const sortedAddresses = [...addresses].sort(\n (a, b) => Number(b.default ?? false) - Number(a.default ?? false),\n );\n\n return (\n <div className=\"relative\">\n <div className=\"relative mx-auto max-w-4xl px-5 pt-8 pb-20 md:px-8\">\n {/* Hero */}\n <section className=\"flex items-center gap-3.5\">\n <div className=\"relative size-12 shrink-0 overflow-hidden rounded-full ring-1 ring-black/5 md:size-13\">\n {account.avatar_url ? (\n <img\n src={account.avatar_url}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"bg-primary text-primary-foreground flex h-full w-full items-center justify-center text-lg font-semibold\">\n {heroDisplay.charAt(0).toUpperCase()}\n </div>\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-foreground truncate text-xl font-bold tracking-tight md:text-2xl\">\n {heroDisplay}\n </h1>\n <p className=\"text-muted-foreground mt-0.5 truncate text-sm\">\n {account.email}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {rewardsEnabled && (\n <div className=\"ring-border text-foreground inline-flex items-center gap-1.5 rounded-md bg-transparent px-2.5 py-1 text-xs font-semibold ring-1\">\n <Sparkles className=\"text-primary size-3\" strokeWidth={2.25} />\n {(pointsBalance ?? 0).toLocaleString()}{\" \"}\n {pointsCountLabel(pointsBalance ?? 0)}\n </div>\n )}\n </div>\n </section>\n\n {/* Personal information */}\n <SectionHeader\n title={t(\"personal_information\")}\n action={\n <PillButton\n onClick={onEditPersonalInfo}\n icon={Pencil}\n label={t(\"edit\")}\n />\n }\n />\n <div className=\"ring-border mt-2.5 overflow-hidden rounded-2xl bg-transparent ring-1\">\n <FieldRow\n icon={User}\n label={t(\"name\")}\n value={fullName || account.first_name || \"\"}\n />\n <FieldRow\n icon={Mail}\n label={t(\"email\")}\n value={account.email ?? \"\"}\n />\n <FieldRow\n icon={Phone}\n label={t(\"phone\")}\n value={account.phone ?? \"\"}\n placeholder={t(\"add_phone_number\")}\n />\n <FieldRow\n icon={Quote}\n label={t(\"bio\")}\n value={account.bio ?? \"\"}\n placeholder={t(\"add_a_short_bio\")}\n multiline\n isLast\n />\n </div>\n\n {/* Payment methods */}\n <SectionHeader title={t(\"payment_methods\")} />\n <div className=\"mt-2.5 grid grid-cols-1 gap-2.5 sm:grid-cols-3\">\n {paymentMethods.map((method) => (\n <PaymentCardVisual\n key={method.id}\n method={method}\n onClick={() => onEditPaymentMethod(method)}\n />\n ))}\n <AddPaymentTile onClick={onAddPaymentMethod} />\n </div>\n\n {/* Points history */}\n {rewardsEnabled && (\n <>\n <SectionHeader\n title={t(\"points_history\", { label: pointsLabelPluralCap })}\n action={\n <span className=\"text-muted-foreground/75 text-xs\">\n {t(\"pts_available\", {\n count: String((pointsBalance ?? 0).toLocaleString()),\n label: pointsCountLabel(pointsBalance ?? 0),\n })}\n </span>\n }\n />\n {ledger.length > 0 ? (\n <div className=\"ring-border mt-2.5 overflow-hidden rounded-2xl bg-transparent ring-1\">\n {ledgerPreview.map((entry, i) => (\n <PointsLedgerRow\n key={entry.id}\n entry={entry}\n isLast={!hasMoreLedger && i === ledgerPreview.length - 1}\n pointsLabelPlural={pointsLabelPluralCap}\n />\n ))}\n {hasMoreLedger && (\n <button\n type=\"button\"\n onClick={() => setIsPointsHistoryOpen(true)}\n className=\"text-foreground hover:bg-muted flex w-full items-center justify-center gap-1 px-4 py-3 text-xs font-bold transition-colors\"\n >\n {t(\"see_all_transactions\", { count: ledger.length })}\n </button>\n )}\n </div>\n ) : (\n <div className=\"ring-dashed ring-border mt-2.5 flex flex-col items-center justify-center gap-1 rounded-2xl bg-transparent px-4 py-8 text-center ring-1\">\n <Sparkles\n className=\"text-muted-foreground/75 size-5\"\n strokeWidth={1.75}\n />\n <div className=\"text-foreground text-sm font-bold\">\n {t(\"no_points_yet\", { label: pointsLabel.plural })}\n </div>\n <div className=\"text-muted-foreground text-xs\">\n {t(\"earn_points_hint\", { label: pointsLabel.plural })}\n </div>\n </div>\n )}\n </>\n )}\n\n {/* Your MySite link */}\n <SectionHeader\n title={t(\"your_mysite_link\")}\n action={\n onEditMySiteLink ? (\n <PillButton\n onClick={onEditMySiteLink}\n icon={Pencil}\n label={t(\"edit_link\")}\n />\n ) : undefined\n }\n />\n <div className=\"ring-border mt-2.5 flex items-center gap-3 rounded-2xl bg-transparent p-3.5 ring-1\">\n <div className=\"bg-muted text-foreground flex size-11 shrink-0 items-center justify-center rounded-full\">\n <Link2 className=\"size-[18px]\" strokeWidth={2} />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-muted-foreground/75 text-xs font-bold tracking-widest uppercase\">\n {t(\"public_link\")}\n </div>\n <div className=\"text-foreground mt-0.5 truncate font-mono text-sm font-semibold\">\n {mySiteDisplayUrl || t(\"set_your_mysite_link\")}\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onCopyMySiteLink}\n disabled={!mySiteDisplayUrl}\n className=\"ring-border text-foreground hover:bg-muted inline-flex items-center gap-1 rounded-md bg-transparent px-3 py-1.5 text-xs font-bold ring-1 transition-all disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <Copy className=\"size-3\" strokeWidth={2.25} />\n {t(\"copy\")}\n </button>\n </div>\n\n {/* Shipping addresses */}\n <SectionHeader title={t(\"shipping_addresses\")} />\n <div className=\"mt-2.5 grid grid-cols-1 gap-2.5 sm:grid-cols-2\">\n {sortedAddresses.map((address) => (\n <AddressCard\n key={address.id}\n address={address}\n onEdit={() => onEditAddress(address)}\n onDelete={\n address.default ? undefined : () => onDeleteAddress(address)\n }\n presentation={presentation}\n />\n ))}\n <AddAddressTile onClick={onAddAddress} />\n </div>\n\n {/* Social media */}\n <SectionHeader\n title={t(\"social_media\")}\n action={\n <span className=\"text-muted-foreground/75 text-xs\">\n {t(\"connected_count\", { count: connectedSocialCount })}\n </span>\n }\n />\n <div className=\"ring-border mt-2.5 overflow-hidden rounded-2xl bg-transparent ring-1\">\n {SOCIAL_ENTRIES.map((entry, i) => (\n <SocialRow\n key={entry.key}\n entry={entry}\n handle={socialLinks[entry.key] ?? \"\"}\n isLast={i === SOCIAL_ENTRIES.length - 1}\n onConnect={() => onConnectSocial(entry.key)}\n />\n ))}\n </div>\n\n <ResponsiveDialog\n open={isPointsHistoryOpen}\n onOpenChange={setIsPointsHistoryOpen}\n title={t(\"points_history\", { label: pointsLabelPluralCap })}\n presentation={presentation}\n >\n <div className=\"ring-border overflow-hidden rounded-2xl bg-transparent ring-1\">\n {ledger.map((entry, i) => (\n <PointsLedgerRow\n key={entry.id}\n entry={entry}\n isLast={i === ledger.length - 1}\n pointsLabelPlural={pointsLabelPluralCap}\n />\n ))}\n </div>\n </ResponsiveDialog>\n\n {onSignOut && (\n <div className=\"ring-border mt-8 flex flex-wrap items-center justify-between gap-3 rounded-2xl bg-transparent p-3.5 ring-1\">\n <div className=\"flex items-center gap-3\">\n <div className=\"bg-destructive/10 flex size-9 items-center justify-center rounded-md\">\n <LogOut\n className=\"text-destructive size-4\"\n strokeWidth={2.25}\n />\n </div>\n <div>\n <div className=\"text-foreground text-sm font-bold\">\n {t(\"sign_out\")}\n </div>\n <div className=\"text-muted-foreground text-xs\">\n {t(\"sign_out_description\")}\n </div>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onSignOut}\n className=\"bg-destructive/15 text-destructive hover:bg-destructive/25 rounded-md px-4 py-2 text-xs font-bold transition-colors\"\n >\n {t(\"sign_out\")}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction SectionHeader({\n title,\n action,\n}: {\n title: string;\n action?: ReactNode;\n}): JSX.Element {\n return (\n <div className=\"mt-8 flex items-end justify-between\">\n <h2 className=\"text-foreground text-base font-bold tracking-tight\">\n {title}\n </h2>\n {action ?? null}\n </div>\n );\n}\n\nfunction PillButton({\n onClick,\n icon: Icon,\n label,\n disabled,\n}: {\n onClick: () => void;\n icon: ComponentType<{ className?: string; strokeWidth?: number }>;\n label: string;\n disabled?: boolean;\n}): JSX.Element {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className=\"ring-border text-foreground hover:bg-muted inline-flex items-center gap-1 rounded-md bg-transparent px-3 py-1.5 text-xs font-bold ring-1 transition-all disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <Icon className=\"size-3\" strokeWidth={2.25} />\n {label}\n </button>\n );\n}\n\nfunction PointsLedgerRow({\n entry,\n isLast,\n pointsLabelPlural,\n}: {\n entry: PointsLedger;\n isLast: boolean;\n pointsLabelPlural: string;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const isCredit = entry.amount >= 0;\n const sign = isCredit ? \"+\" : \"−\";\n const magnitude = Math.abs(entry.amount).toLocaleString();\n const amountColor = isCredit ? \"text-emerald-600\" : \"text-destructive\";\n const sourceName = entry.metadata?.source?.name?.trim();\n const transactionType = entry.metadata?.transaction_type;\n const label =\n sourceName ||\n (transactionType\n ? titleCaseFromSnake(transactionType)\n : isCredit\n ? t(\"points_awarded\", { label: pointsLabelPlural })\n : t(\"points_redeemed\", { label: pointsLabelPlural }));\n const dateLabel = formatLedgerDate(entry.created_at);\n return (\n <div\n className={`flex items-center gap-3 px-4 py-3.5 ${isLast ? \"\" : \"border-border border-b\"}`}\n >\n <div className=\"bg-muted flex size-9 shrink-0 items-center justify-center rounded-full\">\n <Sparkles\n className={`size-4 ${isCredit ? \"text-primary\" : \"text-muted-foreground\"}`}\n strokeWidth={2}\n />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-foreground truncate text-sm font-medium\">\n {label}\n </div>\n <div className=\"text-muted-foreground text-xs\">{dateLabel}</div>\n </div>\n <div\n className={`shrink-0 font-mono text-sm font-bold tabular-nums ${amountColor}`}\n >\n {sign}\n {magnitude}\n </div>\n </div>\n );\n}\n\n/** Capitalize the first letter for display in headings / titles. */\nfunction capitalize(value: string): string {\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\nfunction formatLedgerDate(iso: string): string {\n try {\n const d = new Date(iso);\n return d.toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n } catch {\n return iso;\n }\n}\n\nfunction FieldRow({\n icon: Icon,\n label,\n value,\n placeholder,\n isLast,\n multiline,\n}: {\n icon: ComponentType<{ className?: string; strokeWidth?: number }>;\n label: string;\n value: string;\n placeholder?: string;\n isLast?: boolean;\n multiline?: boolean;\n}): JSX.Element {\n const display = value || placeholder || \"\";\n const isPlaceholder = !value && Boolean(placeholder);\n const valueClasses = multiline ? \"whitespace-pre-line\" : \"truncate\";\n return (\n <div\n className={`flex gap-3 px-4 py-3.5 ${multiline ? \"items-start\" : \"items-center\"} ${isLast ? \"\" : \"border-border border-b\"}`}\n >\n <div className=\"bg-muted text-muted-foreground flex size-9 shrink-0 items-center justify-center rounded-full\">\n <Icon className=\"size-4\" strokeWidth={2} />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-muted-foreground/75 text-xs font-bold tracking-widest uppercase\">\n {label}\n </div>\n <div\n className={`mt-0.5 ${valueClasses} text-sm font-medium ${isPlaceholder ? \"text-muted-foreground/55\" : \"text-foreground\"}`}\n >\n {display}\n </div>\n </div>\n </div>\n );\n}\n\nfunction PaymentCardVisual({\n method,\n onClick,\n}: {\n method: fluidPay.CustomerPaymentMethod;\n onClick: () => void;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const brand = (method.details.card_brand || \"\").toLowerCase();\n // Ordered, de-duplicated fallback chain: the server-provided logo, then the\n // brand-derived CDN icon, then the generic card icon. Each load failure\n // advances one step instead of collapsing straight to generic.\n const logoCandidates = [\n ...new Set(\n [\n method.details.logo_url,\n brandIconUrl(brand),\n GENERIC_PAYMENT_ICON_URL,\n ].filter((url): url is string => Boolean(url)),\n ),\n ];\n const [logoStep, setLogoStep] = useState(0);\n const logoSrc =\n logoCandidates[Math.min(logoStep, logoCandidates.length - 1)] ??\n GENERIC_PAYMENT_ICON_URL;\n const maskedNumber = maskCardNumber(method.details.last_four, brand);\n const expMonth = String(method.details.exp_month ?? \"\").padStart(2, \"0\");\n const expYear = String(method.details.exp_year ?? \"\").slice(-2);\n const exp = expMonth && expYear ? `${expMonth}/${expYear}` : \"\";\n const holder = deriveCardHolderName(method);\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group bg-foreground text-background ring-foreground/10 relative flex aspect-[1.586/1] flex-col justify-between overflow-hidden rounded-xl p-4 text-left shadow-sm ring-1 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg\"\n >\n <div\n aria-hidden\n className=\"absolute inset-0 bg-[repeating-linear-gradient(135deg,transparent_0px,transparent_22px,currentColor_22px,currentColor_24px)] opacity-[0.04]\"\n />\n <div className=\"relative\">\n <div className=\"flex items-start justify-between\">\n <img\n src={logoSrc}\n alt={brand}\n className=\"h-7 w-auto object-contain\"\n onError={() =>\n setLogoStep((step) =>\n Math.min(step + 1, logoCandidates.length - 1),\n )\n }\n />\n {method.default && (\n <span className=\"bg-background/10 text-background/80 rounded-md px-2 py-0.5 text-xs font-bold tracking-wider uppercase\">\n {t(\"default\")}\n </span>\n )}\n </div>\n <div className=\"text-background mt-3 font-mono text-lg font-bold tracking-wider\">\n {maskedNumber}\n </div>\n </div>\n <div className=\"relative flex items-end justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-background/40 text-xs font-semibold tracking-widest uppercase\">\n {t(\"cardholder\")}\n </div>\n <div className=\"text-background truncate text-xs font-semibold\">\n {holder || \" \"}\n </div>\n </div>\n <div>\n <div className=\"text-background/40 text-xs font-semibold tracking-widest uppercase\">\n {t(\"card_expires\")}\n </div>\n <div className=\"text-background font-mono text-xs font-semibold\">\n {exp}\n </div>\n </div>\n </div>\n </button>\n );\n}\n\nfunction AddPaymentTile({ onClick }: { onClick: () => void }): JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group ring-dashed ring-border text-muted-foreground hover:bg-background hover:text-foreground flex aspect-[1.586/1] flex-col items-center justify-center gap-1.5 rounded-xl bg-transparent ring-1 transition-colors\"\n >\n <div className=\"ring-border flex size-9 items-center justify-center rounded-full bg-transparent ring-1\">\n <Plus className=\"size-4\" strokeWidth={2.25} />\n </div>\n <div className=\"text-foreground text-xs font-bold\">\n {t(\"add_payment_method\")}\n </div>\n <div className=\"text-muted-foreground/75 text-xs\">\n {t(\"accepted_cards\")}\n </div>\n </button>\n );\n}\n\nfunction AddressCard({\n address,\n onEdit,\n onDelete,\n presentation,\n}: {\n address: fluidPay.CustomerAddress;\n onEdit: () => void;\n onDelete?: () => void;\n presentation: ResponsiveDialogPresentation;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const label = address.name?.trim() || t(\"address\");\n const cityLine = [address.city, address.state, address.postal_code]\n .filter(Boolean)\n .join(\", \");\n return (\n <div className=\"ring-border hover:bg-muted focus-within:ring-foreground/40 relative flex flex-col rounded-2xl bg-transparent ring-1 transition-all duration-200 focus-within:ring-2 hover:ring-2\">\n <button\n type=\"button\"\n onClick={onEdit}\n className=\"flex w-full flex-col rounded-2xl p-4 pr-12 text-left focus-visible:outline-none\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-2\">\n <div className=\"bg-muted flex size-8 items-center justify-center rounded-full\">\n <MapPin\n className=\"text-muted-foreground size-4\"\n strokeWidth={2}\n />\n </div>\n <div className=\"text-foreground text-sm font-bold\">{label}</div>\n </div>\n {address.default && (\n <span className=\"bg-primary text-primary-foreground rounded-md px-2 py-0.5 text-xs font-bold tracking-wider uppercase\">\n {t(\"default\")}\n </span>\n )}\n </div>\n <div className=\"text-foreground mt-3 space-y-0.5 text-sm leading-snug\">\n {address.address1 && <div>{address.address1}</div>}\n {address.address2 && <div>{address.address2}</div>}\n {cityLine && <div>{cityLine}</div>}\n {address.country_code && (\n <div className=\"text-muted-foreground\">{address.country_code}</div>\n )}\n {address.phone && (\n <div className=\"text-muted-foreground\">{address.phone}</div>\n )}\n </div>\n </button>\n <div className=\"absolute top-2 right-2 z-10\">\n <EllipsesDropdown\n onEdit={onEdit}\n onDelete={onDelete}\n editLabel={t(\"edit\")}\n deleteLabel={t(\"delete\")}\n menuLabel={t(\"address\")}\n presentation={presentation}\n />\n </div>\n </div>\n );\n}\n\nfunction AddAddressTile({ onClick }: { onClick: () => void }): JSX.Element {\n const { t } = useProfileTranslation();\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group ring-dashed ring-border text-muted-foreground hover:bg-background hover:text-foreground flex min-h-37 flex-col items-center justify-center gap-1.5 rounded-2xl bg-transparent ring-1 transition-colors\"\n >\n <div className=\"ring-border flex size-9 items-center justify-center rounded-full bg-transparent ring-1\">\n <Plus className=\"size-4\" strokeWidth={2.25} />\n </div>\n <div className=\"text-foreground text-sm font-bold\">\n {t(\"add_an_address\")}\n </div>\n </button>\n );\n}\n\ntype SocialKey =\n | \"linkedin\"\n | \"facebook\"\n | \"x\"\n | \"instagram\"\n | \"youtube\"\n | \"pinterest\"\n | \"tiktok\";\n\ninterface SocialEntry {\n key: SocialKey;\n label: string;\n icon: ComponentType<{ className?: string; strokeWidth?: number }>;\n placeholder: string;\n}\n\nconst SOCIAL_ENTRIES: readonly SocialEntry[] = [\n {\n key: \"linkedin\",\n label: \"LinkedIn\",\n icon: Linkedin,\n placeholder: \"linkedin.com/in/username\",\n },\n {\n key: \"facebook\",\n label: \"Facebook\",\n icon: Facebook,\n placeholder: \"facebook.com/username\",\n },\n { key: \"x\", label: \"X\", icon: Twitter, placeholder: \"x.com/username\" },\n {\n key: \"instagram\",\n label: \"Instagram\",\n icon: Instagram,\n placeholder: \"instagram.com/username\",\n },\n {\n key: \"youtube\",\n label: \"YouTube\",\n icon: Youtube,\n placeholder: \"youtube.com/@channel\",\n },\n {\n key: \"pinterest\",\n label: \"Pinterest\",\n icon: Globe,\n placeholder: \"pinterest.com/username\",\n },\n {\n key: \"tiktok\",\n label: \"TikTok\",\n icon: Globe,\n placeholder: \"tiktok.com/@username\",\n },\n] as const;\n\nfunction SocialRow({\n entry,\n handle,\n isLast,\n onConnect,\n}: {\n entry: SocialEntry;\n handle: string;\n isLast: boolean;\n onConnect: () => void;\n}): JSX.Element {\n const { t } = useProfileTranslation();\n const Icon = entry.icon;\n return (\n <div\n className={`flex items-center gap-3 px-4 py-3.5 ${isLast ? \"\" : \"border-border border-b\"}`}\n >\n <div className=\"bg-muted text-foreground flex size-9 shrink-0 items-center justify-center rounded-full\">\n <Icon className=\"size-4\" strokeWidth={2} />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-foreground text-sm font-bold\">{entry.label}</div>\n <div\n className={`truncate text-xs ${handle ? \"text-muted-foreground\" : \"text-muted-foreground/55\"}`}\n >\n {handle || entry.placeholder}\n </div>\n </div>\n <PillButton\n onClick={onConnect}\n icon={handle ? Pencil : Plus}\n label={handle ? t(\"edit\") : t(\"connect\")}\n />\n </div>\n );\n}\n\nexport type { SocialKey };\n","import { isThreeDsRequiredCreate } from \"@fluid-app/portal-core/pay-types\";\nimport type {\n CreatePaymentMethodResponse,\n ThreeDsRequiredCreateResult,\n} from \"@fluid-app/portal-core/pay-types\";\n\n/**\n * Build the create-payment-method onSuccess handler. On three_ds_required it\n * hands off to the 3DS flow and leaves the card dialog OPEN (begin() resolves\n * at modal-open, not flow-completion). On a created PM it runs the legacy\n * created path (invalidate + close + toast).\n */\nexport function buildProfileCardAddSuccess(deps: {\n begin: (created: ThreeDsRequiredCreateResult) => Promise<void>;\n onCreated: () => void;\n}) {\n return async (response: CreatePaymentMethodResponse) => {\n if (isThreeDsRequiredCreate(response)) {\n await deps.begin(response);\n return;\n }\n deps.onCreated();\n };\n}\n","import { useCallback, useMemo, useState } from \"react\";\nimport { z } from \"zod\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport {\n Button,\n DialogFooter,\n Input,\n Label,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport { ResponsiveDialog } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\nimport type { Language, PointsLedger } from \"@fluid-app/profile-core\";\nimport {\n AddressFormDialog,\n ConfirmActionDialog,\n CreditCardFormDialog,\n EditPaymentMethodDialog,\n UserInfoDialog,\n socialFields,\n} from \"@fluid-app/profile-ui\";\nimport type {\n UserFormData,\n EditPaymentMethodFormData,\n CreditCardFormSubmitData,\n} from \"@fluid-app/profile-ui\";\nimport {\n FluidPayCoreProvider,\n type fluidPay,\n type State,\n} from \"@fluid-app/fluid-pay-core\";\nimport { getBrowserInfo } from \"@fluid-app/fluid-pay-core/utils/browser-info\";\nimport { TOKEN_PROVIDER_VGS } from \"@fluid-app/portal-core/pay-types\";\nimport type { PayAddress } from \"@fluid-app/portal-core/pay-types\";\nimport {\n createFluidPayApiAdapter,\n mapToFluidPayPaymentMethod,\n} from \"../adapters/fluid-pay-api-adapter\";\nimport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nimport { canAccessRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\nimport { useAccountApi } from \"@fluid-app/portal-core/account-api-context\";\nimport { usePayApi } from \"@fluid-app/portal-core/pay-api-context\";\nimport {\n usePortalMySiteProfile,\n usePortalUpdateSettings,\n} from \"@fluid-app/mysite-ui/portal/hooks/use-mysite-portal\";\nimport { useCountriesApi } from \"@fluid-app/store-core/countries-api-context\";\nimport { useLanguagesApi } from \"@fluid-app/store-core/languages-api-context\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { accountKeys, payKeys, storeKeys } from \"../account/query-keys\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { useStore } from \"../hooks/use-store\";\nimport { deriveLastName } from \"./profile/derive-last-name\";\nimport { ProfileLayout, type SocialKey } from \"./profile/ProfileLayout\";\nimport { usePortalCardAdd3DSFlow } from \"../hooks/use-portal-card-add-3ds-flow\";\nimport { buildProfileCardAddSuccess } from \"./profile-card-add-success\";\n\ninterface ProfileContentScreenProps {\n onToast: (message: string, type: \"success\" | \"error\" | \"warning\") => void;\n countryIso: string;\n}\n\nfunction mapAccountToCustomerAccount(\n raw: AccountRep,\n): fluidPay.CustomerAccount {\n return {\n fluid_pay_account: {\n id: raw.id,\n email: raw.email,\n address_count: 0,\n payment_methods_count: 0,\n language_iso: raw.language_iso,\n },\n customer: {\n id: raw.id,\n active: true,\n active_subscriptions_count: 0,\n display_total_spent: \"0\",\n email: raw.email,\n first_name: raw.first_name,\n full_name: `${raw.first_name} ${raw.last_name}`.trim(),\n inactive_subscriptions_count: 0,\n is_rep: canAccessRepSurfaces(raw),\n phone: raw.phone,\n orders_count: 0,\n },\n };\n}\n\nfunction mapToFluidPayAddress(raw: PayAddress): fluidPay.CustomerAddress {\n return {\n id: raw.id,\n address1: raw.street1,\n address2: raw.street2,\n city: raw.city,\n country_code: raw.country,\n default: raw.default,\n name: raw.name,\n postal_code: raw.zip,\n phone: raw.phone,\n state: raw.state,\n subdivision_code: null,\n };\n}\n\ntype SocialFieldKey = (typeof socialFields)[number][\"name\"];\n\nconst SOCIAL_KEY_TO_FIELD: Record<SocialKey, SocialFieldKey | null> = {\n linkedin: \"linkedin\" as SocialFieldKey,\n facebook: \"facebook\" as SocialFieldKey,\n x: \"twitter\" as SocialFieldKey,\n instagram: \"instagram\" as SocialFieldKey,\n youtube: \"youtube\" as SocialFieldKey,\n pinterest: \"pinterest\" as SocialFieldKey,\n tiktok: \"tiktok\" as SocialFieldKey,\n};\n\nconst SOCIAL_LABELS: Record<SocialKey, string> = {\n linkedin: \"LinkedIn\",\n facebook: \"Facebook\",\n x: \"X\",\n instagram: \"Instagram\",\n youtube: \"YouTube\",\n pinterest: \"Pinterest\",\n tiktok: \"TikTok\",\n};\n\nconst SOCIAL_PLACEHOLDERS: Record<SocialKey, string> = {\n linkedin: \"linkedin.com/in/username\",\n facebook: \"facebook.com/username\",\n x: \"x.com/username\",\n instagram: \"instagram.com/username\",\n youtube: \"youtube.com/@channel\",\n pinterest: \"pinterest.com/username\",\n tiktok: \"tiktok.com/@username\",\n};\n\nexport function ProfileContentScreen({\n onToast,\n countryIso,\n}: ProfileContentScreenProps): React.JSX.Element {\n const { t } = useProfileTranslation();\n const { isMobile } = useSidebar();\n const dialogPresentation = isMobile ? \"bottom-sheet\" : \"dialog\";\n const { locale } = useActiveLocale();\n const payApi = usePayApi();\n const accountApi = useAccountApi();\n const queryClient = useQueryClient();\n const fluidPayShim = useMemo(\n () => createFluidPayApiAdapter(payApi),\n [payApi],\n );\n\n const { data: mySiteProfile } = usePortalMySiteProfile();\n const updateMySiteSettingsMutation = usePortalUpdateSettings();\n\n const mySiteUrl = mySiteProfile?.mysite_url ?? \"\";\n const mySiteDisplayUrl = mySiteUrl\n ? mySiteUrl.replace(/^https?:\\/\\//, \"\")\n : \"\";\n const mySiteLastSlash = mySiteDisplayUrl.lastIndexOf(\"/\");\n const mySiteUrlPrefix =\n mySiteLastSlash >= 0 ? mySiteDisplayUrl.slice(0, mySiteLastSlash + 1) : \"\";\n const mySiteCurrentSlug =\n mySiteLastSlash >= 0\n ? mySiteDisplayUrl.slice(mySiteLastSlash + 1)\n : mySiteDisplayUrl;\n\n // Reuse the app-wide useAccount() cache entry shared with AppShell/PageRouter.\n const {\n data: accountRep,\n isLoading: isLoadingAccount,\n isError: isAccountError,\n } = useAccount();\n\n const accountData = useMemo(\n () => (accountRep ? mapAccountToCustomerAccount(accountRep) : undefined),\n [accountRep],\n );\n\n const { data: addressesData } = useQuery({\n queryKey: payKeys.addresses.list(),\n queryFn: async () => {\n const response = await payApi.fetchAddresses();\n return response.addresses.map(mapToFluidPayAddress);\n },\n enabled: true,\n });\n\n const { data: paymentMethodsData } = useQuery({\n queryKey: payKeys.paymentMethods.list(),\n queryFn: async () => {\n const response = await payApi.fetchPaymentMethods();\n return response.payment_methods.map(mapToFluidPayPaymentMethod);\n },\n enabled: true,\n });\n\n const countriesAdapter = useCountriesApi();\n const languagesAdapter = useLanguagesApi();\n\n const { data: countriesData } = useQuery({\n queryKey: storeKeys.countries(locale),\n queryFn: () => countriesAdapter.listCountries({ languageIso: locale }),\n enabled: true,\n });\n\n const { data: languagesData } = useQuery({\n queryKey: storeKeys.languages(),\n queryFn: () => languagesAdapter.listLanguages(),\n enabled: true,\n });\n\n const { data: pointsLedgerData, isError: isPointsLedgerError } = useQuery({\n queryKey: payKeys.pointsLedgers.list(),\n queryFn: () => payApi.fetchPointsLedgers(),\n enabled: true,\n retry: (failureCount, error) => {\n if (\n error instanceof Error &&\n \"status\" in error &&\n (error as { status: number }).status === 403\n ) {\n return false;\n }\n return failureCount < 1;\n },\n });\n\n const rewardPointsEnabled = !isPointsLedgerError && pointsLedgerData != null;\n\n // Per-company reward-points label (defaults point/points). Drives the\n // configured label across the profile points UI.\n const { data: store } = useStore();\n const pointsLabelSingular = store?.reward_points_label_singular || \"point\";\n const pointsLabelPlural = store?.reward_points_label_plural || \"points\";\n const pointsLabel = useMemo(\n () => ({ singular: pointsLabelSingular, plural: pointsLabelPlural }),\n [pointsLabelSingular, pointsLabelPlural],\n );\n\n const adaptedPointsLedger: PointsLedger[] = useMemo(() => {\n return (pointsLedgerData?.points_ledgers ?? []).map((entry) => {\n const meta = entry.metadata as {\n transaction_type?: string | null;\n source?: {\n name: string;\n email?: string;\n reason?: string;\n user_id?: number;\n } | null;\n } | null;\n return {\n id: entry.id,\n amount: entry.amount,\n company_id: 0,\n created_at: entry.created_at,\n customer_id: 0,\n metadata: {\n transaction_type: meta?.transaction_type ?? undefined,\n source: meta?.source ?? undefined,\n },\n total_balance: entry.total_balance,\n updated_at: entry.created_at,\n };\n });\n }, [pointsLedgerData]);\n\n const updateCustomerMutation = useMutation({\n mutationFn: async (data: UserFormData) => {\n await accountApi.updateAccount({\n account: {\n first_name: data.first_name,\n last_name: data.last_name,\n phone: data.phone_number,\n bio: data.bio,\n language_iso: data.language,\n },\n });\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: accountKeys.all });\n onToast(t(\"profile_updated\"), \"success\");\n },\n onError: () => {\n onToast(t(\"failed_to_update_profile\"), \"error\");\n },\n });\n\n const deletePaymentMethodMutation = useMutation({\n mutationFn: (paymentMethodId: number) =>\n payApi.deletePaymentMethod(paymentMethodId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: payKeys.paymentMethods.all,\n });\n },\n onError: () => {\n onToast(t(\"failed_to_delete_payment_method\"), \"error\");\n },\n });\n\n const updatePaymentMethodMutation = useMutation({\n mutationFn: ({\n paymentMethodId,\n data,\n }: {\n paymentMethodId: number;\n data: EditPaymentMethodFormData;\n }) =>\n payApi.updatePaymentMethod(paymentMethodId, {\n payment_method: {\n default: data.set_as_default,\n billing_address: {\n name: data.billing_address.name,\n street1: data.billing_address.address1,\n street2: data.billing_address.address2 ?? null,\n city: data.billing_address.city,\n state: data.billing_address.state,\n zip: data.billing_address.zip,\n country: data.billing_address.country_code,\n },\n },\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: payKeys.paymentMethods.all,\n });\n },\n onError: () => {\n onToast(t(\"failed_to_update_payment_method\"), \"error\");\n },\n });\n\n const createAddressMutation = useMutation({\n // Detect a server-side dedupe (POST /api/pay/addresses returns the existing\n // record when an identical address already exists, ignoring `name`). Snapshot\n // the current id set before the request and compare against the returned id.\n mutationFn: async (body: fluidPay.CreateAddressBody) => {\n const existingIds = new Set((addressesData ?? []).map((a) => a.id));\n const address = await payApi.createAddress({\n address: {\n name: body.address.name,\n street1: body.address.address1,\n street2: body.address.address2,\n city: body.address.city,\n state: body.address.state,\n zip: body.address.postal_code,\n country: body.address.country_code,\n phone: body.address.phone,\n default: body.address.default,\n },\n });\n return { address, deduped: existingIds.has(address.id) };\n },\n onSuccess: ({ deduped }) => {\n queryClient.invalidateQueries({ queryKey: payKeys.addresses.all });\n if (deduped) {\n onToast(t(\"address_already_exists\"), \"warning\");\n } else {\n onToast(t(\"address_created\"), \"success\");\n }\n },\n onError: () => {\n onToast(t(\"failed_to_create_address\"), \"error\");\n },\n });\n\n const updateAddressMutation = useMutation({\n mutationFn: ({\n addressId,\n body,\n }: {\n addressId: number;\n body: fluidPay.CreateAddressBody;\n }) =>\n payApi.updateAddress(addressId, {\n address: {\n name: body.address.name,\n street1: body.address.address1,\n street2: body.address.address2,\n city: body.address.city,\n state: body.address.state,\n zip: body.address.postal_code,\n country: body.address.country_code,\n phone: body.address.phone,\n default: body.address.default,\n },\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: payKeys.addresses.all });\n onToast(t(\"address_updated\"), \"success\");\n },\n onError: () => {\n onToast(t(\"failed_to_update_address\"), \"error\");\n },\n });\n\n const deleteAddressMutation = useMutation({\n mutationFn: (addressId: number) => payApi.deleteAddress(addressId),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: payKeys.addresses.all });\n onToast(t(\"address_deleted\"), \"success\");\n },\n onError: () => {\n onToast(t(\"failed_to_delete_address\"), \"error\");\n },\n });\n\n // Orchestrates the bank-challenge step when card add returns three_ds_required.\n // begin() resolves the moment the challenge modal opens — so dialog close and\n // the success toast live ONLY here (onApproved) and on the created branch\n // (onCreated below), never on the three_ds_required branch.\n const cardAdd3DS = usePortalCardAdd3DSFlow({\n payApi,\n t: (key: string) => t(key as never),\n onApproved: async () => {\n await queryClient.invalidateQueries({\n queryKey: payKeys.paymentMethods.all,\n });\n setIsAddCardOpen(false);\n onToast(t(\"payment_method_added\"), \"success\");\n },\n onFailed: (message) => {\n setSaveCardError(message);\n },\n onCanceled: () => {},\n });\n\n const cardAddSuccess = buildProfileCardAddSuccess({\n begin: cardAdd3DS.begin,\n onCreated: () => {\n queryClient.invalidateQueries({\n queryKey: payKeys.paymentMethods.all,\n });\n setIsAddCardOpen(false);\n onToast(t(\"payment_method_added\"), \"success\");\n },\n });\n\n const addCreditCardMutation = useMutation({\n mutationFn: (data: CreditCardFormSubmitData) =>\n payApi.createPaymentMethod({\n payment_method: {\n type: \"card\",\n token: data.payment_method.token,\n token_provider: TOKEN_PROVIDER_VGS,\n exp_month: data.payment_method.exp_month,\n exp_year: data.payment_method.exp_year,\n cvv_token: data.payment_method.cvv_token,\n card_holder: data.payment_method.card_holder,\n default: data.set_as_default,\n ...(data.payment_method.bin ? { bin: data.payment_method.bin } : {}),\n ...(data.payment_method.last_four\n ? { last_four: data.payment_method.last_four }\n : {}),\n ...(data.payment_method.card_network\n ? { card_network: data.payment_method.card_network }\n : {}),\n },\n billing_address_id: data.billing_address_id,\n browser_info: getBrowserInfo(),\n }),\n onSuccess: cardAddSuccess,\n onError: () => {\n onToast(t(\"failed_to_add_payment_method\"), \"error\");\n },\n });\n\n const addresses = addressesData ?? [];\n const paymentMethods = paymentMethodsData ?? [];\n\n const adaptedLanguages: Language[] = (languagesData?.languages ?? []).map(\n (l, i) => ({ id: i, name: l.name, iso: l.code }),\n );\n\n const countries = countriesData?.countries;\n\n const countryOptions = useMemo(\n () =>\n [...(countries ?? [])]\n .map((c) => ({ iso: c.code, name: c.name }))\n .sort((a, b) => a.name.localeCompare(b.name)),\n [countries],\n );\n\n const statesByCountry = useMemo(() => {\n const map = new Map<string, State[]>();\n for (const c of countries ?? []) {\n map.set(\n c.code,\n c.states.map((s) => ({ name: s.name, isoCode: s.code })),\n );\n }\n return map;\n }, [countries]);\n\n const fetchStatesFromCountries = useCallback(\n (countryCode: string): Promise<State[]> =>\n Promise.resolve(statesByCountry.get(countryCode) ?? []),\n [statesByCountry],\n );\n\n // Dialog open state\n const [isInfoDialogOpen, setIsInfoDialogOpen] = useState(false);\n const [addressDialog, setAddressDialog] = useState<{\n open: boolean;\n selected: fluidPay.CustomerAddress | null;\n }>({ open: false, selected: null });\n const [deletingAddress, setDeletingAddress] =\n useState<fluidPay.CustomerAddress | null>(null);\n const [isAddCardOpen, setIsAddCardOpen] = useState(false);\n const [saveCardError, setSaveCardError] = useState<string | null>(null);\n const [editPaymentMethod, setEditPaymentMethod] =\n useState<fluidPay.CustomerPaymentMethod | null>(null);\n\n // User-info form (for the existing dialog)\n const userInfoLanguage = useMemo(() => {\n return (\n adaptedLanguages.find(\n (l) => l.iso === accountData?.fluid_pay_account.language_iso,\n )?.iso ?? \"\"\n );\n }, [adaptedLanguages, accountData]);\n\n const derivedLastName = useMemo(() => {\n if (!accountData) return \"\";\n const { first_name, full_name } = accountData.customer;\n return deriveLastName(first_name, full_name);\n }, [accountData]);\n\n const userInfoSchema = useMemo(\n () =>\n z.object({\n first_name: z.string().min(1, t(\"first_name_is_required\")),\n last_name: z.string().min(1, t(\"last_name_is_required\")),\n phone_number: z.string().optional(),\n language: z.string().min(1, t(\"language_is_required\")),\n bio: z.string().optional(),\n }),\n [t],\n );\n\n const userInfoForm = useZodForm<UserFormData>(userInfoSchema, {\n defaultValues: {\n first_name: accountData?.customer.first_name ?? \"\",\n last_name: derivedLastName,\n phone_number: accountData?.customer.phone ?? \"\",\n language: userInfoLanguage,\n bio: accountRep?.bio ?? \"\",\n },\n });\n\n const handleOpenInfoDialog = useCallback(() => {\n userInfoForm.reset({\n first_name: accountData?.customer.first_name ?? \"\",\n last_name: derivedLastName,\n phone_number: accountData?.customer.phone ?? \"\",\n language: userInfoLanguage,\n bio: accountRep?.bio ?? \"\",\n });\n setIsInfoDialogOpen(true);\n }, [\n accountData,\n accountRep,\n derivedLastName,\n userInfoLanguage,\n userInfoForm,\n ]);\n\n const onSubmitUserInfo = userInfoForm.handleSubmit(async (data) => {\n try {\n await updateCustomerMutation.mutateAsync(data);\n setIsInfoDialogOpen(false);\n } catch {\n // toast surfaced via onError\n }\n });\n\n const socialLinks = useMemo<Partial<Record<SocialKey, string>>>(() => {\n const src: Partial<Record<SocialFieldKey, string>> =\n accountRep?.social_links ?? {};\n const result: Partial<Record<SocialKey, string>> = {};\n for (const [key, field] of Object.entries(SOCIAL_KEY_TO_FIELD) as [\n SocialKey,\n SocialFieldKey | null,\n ][]) {\n if (!field) continue;\n const v = src[field];\n if (typeof v === \"string\" && v.length > 0) {\n result[key] = v;\n }\n }\n return result;\n }, [accountRep]);\n\n const handleCopyMySiteLink = useCallback(async () => {\n if (!mySiteUrl) return;\n try {\n await navigator.clipboard.writeText(mySiteUrl);\n onToast(t(\"mysite_link_copied\"), \"success\");\n } catch {\n onToast(t(\"failed_to_copy_link\"), \"error\");\n }\n }, [mySiteUrl, onToast, t]);\n\n const [isEditMySiteLinkOpen, setIsEditMySiteLinkOpen] = useState(false);\n const [mySiteSlugDraft, setMySiteSlugDraft] = useState(\"\");\n\n const handleOpenEditMySiteLink = useCallback(() => {\n setMySiteSlugDraft(mySiteCurrentSlug);\n setIsEditMySiteLinkOpen(true);\n }, [mySiteCurrentSlug]);\n\n const handleSubmitMySiteLink = useCallback(async () => {\n const trimmed = mySiteSlugDraft.trim();\n if (!trimmed) {\n onToast(t(\"mysite_link_empty\"), \"error\");\n return;\n }\n if (trimmed === mySiteCurrentSlug) {\n setIsEditMySiteLinkOpen(false);\n return;\n }\n try {\n await updateMySiteSettingsMutation.mutateAsync({ slug: trimmed });\n onToast(t(\"mysite_link_updated\"), \"success\");\n setIsEditMySiteLinkOpen(false);\n } catch {\n onToast(t(\"failed_to_update_mysite_link\"), \"error\");\n }\n }, [\n mySiteSlugDraft,\n mySiteCurrentSlug,\n updateMySiteSettingsMutation,\n onToast,\n t,\n ]);\n\n const [editingSocial, setEditingSocial] = useState<SocialKey | null>(null);\n const [socialDraft, setSocialDraft] = useState(\"\");\n\n const handleConnectSocial = useCallback(\n (key: SocialKey) => {\n const field = SOCIAL_KEY_TO_FIELD[key];\n if (!field) {\n onToast(t(\"platform_not_editable\"), \"warning\");\n return;\n }\n const current = accountRep?.social_links?.[field] ?? \"\";\n setSocialDraft(current);\n setEditingSocial(key);\n },\n [accountRep, onToast, t],\n );\n\n const updateSocialMutation = useMutation({\n mutationFn: async ({\n field,\n value,\n }: {\n field: SocialFieldKey;\n value: string;\n }) => {\n const current = accountRep?.social_links ?? {};\n await accountApi.updateAccount({\n account: {\n social_links: { ...current, [field]: value },\n },\n });\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: accountKeys.all });\n onToast(t(\"social_link_updated\"), \"success\");\n },\n onError: () => {\n onToast(t(\"failed_to_update_social_link\"), \"error\");\n },\n });\n\n const handleSubmitSocial = useCallback(async () => {\n if (!editingSocial) return;\n const field = SOCIAL_KEY_TO_FIELD[editingSocial];\n if (!field) return;\n await updateSocialMutation.mutateAsync({\n field,\n value: socialDraft.trim(),\n });\n setEditingSocial(null);\n }, [editingSocial, socialDraft, updateSocialMutation]);\n\n if (isAccountError && !isLoadingAccount) {\n return (\n <div className=\"px-4 py-8 sm:px-9\">\n <div className=\"text-muted-foreground text-center text-sm\">\n {t(\"unable_to_load_account\")}\n </div>\n </div>\n );\n }\n\n if (isLoadingAccount || !accountData || !accountRep) {\n return (\n <div className=\"px-4 pt-4 sm:px-9 md:pt-8\">\n <div className=\"space-y-4\">\n <div className=\"bg-muted h-16 animate-pulse rounded\" />\n <div className=\"bg-muted h-32 animate-pulse rounded\" />\n <div className=\"bg-muted h-32 animate-pulse rounded\" />\n </div>\n </div>\n );\n }\n\n const pointsBalance = rewardPointsEnabled\n ? (pointsLedgerData?.points_ledgers?.[0]?.total_balance ?? 0)\n : undefined;\n\n return (\n <FluidPayCoreProvider api={fluidPayShim}>\n <ProfileLayout\n account={accountRep}\n pointsBalance={pointsBalance}\n pointsLedger={adaptedPointsLedger}\n pointsLabel={pointsLabel}\n rewardsEnabled={rewardPointsEnabled}\n addresses={addresses}\n paymentMethods={paymentMethods}\n mySiteDisplayUrl={mySiteDisplayUrl}\n socialLinks={socialLinks}\n onEditPersonalInfo={handleOpenInfoDialog}\n onAddAddress={() => setAddressDialog({ open: true, selected: null })}\n onEditAddress={(address) =>\n setAddressDialog({ open: true, selected: address })\n }\n onDeleteAddress={(address) => setDeletingAddress(address)}\n onAddPaymentMethod={() => {\n setSaveCardError(null);\n setIsAddCardOpen(true);\n }}\n onEditPaymentMethod={(pm) => setEditPaymentMethod(pm)}\n onCopyMySiteLink={handleCopyMySiteLink}\n onEditMySiteLink={handleOpenEditMySiteLink}\n onConnectSocial={handleConnectSocial}\n presentation={dialogPresentation}\n />\n\n <UserInfoDialog\n control={userInfoForm.control}\n isOpen={isInfoDialogOpen}\n onSubmit={onSubmitUserInfo}\n handleClose={() => setIsInfoDialogOpen(false)}\n languageOptions={adaptedLanguages.map((l) => ({\n name: l.name,\n value: l.iso,\n }))}\n errorMsg={undefined}\n isSubmitting={updateCustomerMutation.isPending}\n email={accountRep.email ?? \"\"}\n presentation={dialogPresentation}\n />\n\n <AddressFormDialog\n isOpen={addressDialog.open}\n onClose={() => setAddressDialog({ open: false, selected: null })}\n selectedAddress={addressDialog.selected}\n defaultCountry={\n addresses.find((a) => a.default)?.country_code ?? countryIso\n }\n languageIso={locale}\n t={(key: string) => t(key as never)}\n onSubmit={async (formData) => {\n if (addressDialog.selected) {\n await updateAddressMutation.mutateAsync({\n addressId: addressDialog.selected.id,\n body: formData,\n });\n } else {\n await createAddressMutation.mutateAsync(formData);\n }\n setAddressDialog({ open: false, selected: null });\n }}\n isSubmitting={\n createAddressMutation.isPending || updateAddressMutation.isPending\n }\n onDelete={\n addressDialog.selected && !addressDialog.selected.default\n ? async () => {\n if (!addressDialog.selected) return;\n try {\n await deleteAddressMutation.mutateAsync(\n addressDialog.selected.id,\n );\n setAddressDialog({ open: false, selected: null });\n } catch {\n // toast surfaced via onError\n }\n }\n : undefined\n }\n isDeleting={deleteAddressMutation.isPending}\n countries={countryOptions}\n fetchStates={fetchStatesFromCountries}\n presentation={dialogPresentation}\n />\n\n <ConfirmActionDialog\n title={t(\"delete_address\")}\n description={t(\"delete_address_message\")}\n openDialog={deletingAddress !== null}\n setOpenDialog={(open) => {\n if (!open) setDeletingAddress(null);\n }}\n onAction={async () => {\n if (!deletingAddress) return;\n try {\n await deleteAddressMutation.mutateAsync(deletingAddress.id);\n setDeletingAddress(null);\n } catch {\n // toast surfaced via onError\n }\n }}\n isLoading={deleteAddressMutation.isPending}\n actionText={t(\"delete\")}\n presentation={dialogPresentation}\n />\n\n <CreditCardFormDialog\n isOpen={isAddCardOpen}\n onClose={() => {\n // Don't let the card dialog close while a 3DS challenge is in flight —\n // the bank-verification modal renders behind it and would be stranded.\n if (cardAdd3DS.isActive) return;\n setSaveCardError(null);\n setIsAddCardOpen(false);\n }}\n error={saveCardError ?? undefined}\n t={(key: string) => t(key as never)}\n onSubmit={async (data) => {\n setSaveCardError(null);\n try {\n // Closure happens in cardAddSuccess (created PM) / the 3DS\n // onApproved branch — NOT here, since a three_ds_required response\n // resolves successfully while the bank check is still pending.\n await addCreditCardMutation.mutateAsync(data);\n } catch {\n // toast surfaced via onError\n }\n }}\n isSubmitting={addCreditCardMutation.isPending}\n jwt=\"\"\n savedAddresses={addresses}\n defaultBillingAddressId={addresses.find((a) => a.default)?.id}\n presentation={dialogPresentation}\n renderAddressFormDialog={({ isOpen, onClose, onAddressCreated }) => (\n <AddressFormDialog\n isOpen={isOpen}\n onClose={onClose}\n selectedAddress={null}\n onSubmit={async (data) => {\n try {\n const { address } =\n await createAddressMutation.mutateAsync(data);\n onAddressCreated(mapToFluidPayAddress(address));\n } catch {\n // toast surfaced via onError\n }\n }}\n isSubmitting={createAddressMutation.isPending}\n countries={countryOptions}\n fetchStates={fetchStatesFromCountries}\n languageIso={locale}\n t={(key: string) => t(key as never)}\n presentation={dialogPresentation}\n />\n )}\n />\n\n {cardAdd3DS.modal}\n\n {editPaymentMethod && (\n <EditPaymentMethodDialog\n isOpen\n onClose={() => setEditPaymentMethod(null)}\n paymentMethod={editPaymentMethod}\n billingAddress={editPaymentMethod.billing_address}\n countries={countryOptions}\n fetchStates={fetchStatesFromCountries}\n onSubmit={(data) => {\n updatePaymentMethodMutation.mutate(\n { paymentMethodId: editPaymentMethod.id, data },\n { onSuccess: () => setEditPaymentMethod(null) },\n );\n }}\n isSubmitting={updatePaymentMethodMutation.isPending}\n onDelete={() => {\n deletePaymentMethodMutation.mutate(editPaymentMethod.id, {\n onSuccess: () => setEditPaymentMethod(null),\n });\n }}\n isDeleting={deletePaymentMethodMutation.isPending}\n presentation={dialogPresentation}\n />\n )}\n\n <ResponsiveDialog\n open={isEditMySiteLinkOpen}\n onOpenChange={(open) => {\n if (!open && updateMySiteSettingsMutation.isPending) return;\n setIsEditMySiteLinkOpen(open);\n }}\n title={t(\"edit_link\")}\n presentation={dialogPresentation}\n >\n <div className=\"space-y-2 pt-2\">\n <Label\n htmlFor=\"mysite-slug\"\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {t(\"your_mysite_link\")}\n </Label>\n <div className=\"ring-border focus-within:ring-foreground/40 flex items-center gap-1 rounded-md bg-transparent px-3 py-2 ring-1 transition-all focus-within:ring-2\">\n {mySiteUrlPrefix && (\n <span className=\"text-muted-foreground shrink-0 text-sm\">\n {mySiteUrlPrefix}\n </span>\n )}\n <Input\n id=\"mysite-slug\"\n value={mySiteSlugDraft}\n onChange={(e) => setMySiteSlugDraft(e.target.value)}\n onKeyDown={(e) => {\n if (\n e.key === \"Enter\" &&\n !updateMySiteSettingsMutation.isPending\n ) {\n e.preventDefault();\n void handleSubmitMySiteLink();\n }\n }}\n className=\"h-auto min-w-0 flex-1 border-0 p-0 shadow-none focus-visible:ring-0\"\n />\n </div>\n </div>\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setIsEditMySiteLinkOpen(false)}\n disabled={updateMySiteSettingsMutation.isPending}\n >\n {t(\"cancel\")}\n </Button>\n <Button\n type=\"button\"\n onClick={() => {\n void handleSubmitMySiteLink();\n }}\n disabled={updateMySiteSettingsMutation.isPending}\n >\n {updateMySiteSettingsMutation.isPending ? t(\"saving\") : t(\"save\")}\n </Button>\n </DialogFooter>\n </ResponsiveDialog>\n\n <ResponsiveDialog\n open={editingSocial !== null}\n onOpenChange={(open) => !open && setEditingSocial(null)}\n title={\n editingSocial\n ? t(\"edit_platform_link\", {\n platform: SOCIAL_LABELS[editingSocial] ?? \"\",\n })\n : t(\"edit_social_link\")\n }\n presentation={dialogPresentation}\n >\n <div className=\"space-y-2 pt-2\">\n <Label\n htmlFor=\"social-handle\"\n className=\"mb-1.5 block text-sm font-medium\"\n >\n {t(\"url\")}\n </Label>\n <Input\n id=\"social-handle\"\n value={socialDraft}\n onChange={(e) => setSocialDraft(e.target.value)}\n placeholder={\n editingSocial ? SOCIAL_PLACEHOLDERS[editingSocial] : \"\"\n }\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n void handleSubmitSocial();\n }\n }}\n />\n </div>\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setEditingSocial(null)}\n disabled={updateSocialMutation.isPending}\n >\n {t(\"cancel\")}\n </Button>\n <Button\n type=\"button\"\n onClick={() => {\n void handleSubmitSocial();\n }}\n disabled={updateSocialMutation.isPending}\n >\n {updateSocialMutation.isPending ? t(\"saving\") : t(\"save\")}\n </Button>\n </DialogFooter>\n </ResponsiveDialog>\n </FluidPayCoreProvider>\n );\n}\n","import type { ComponentProps } from \"react\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n fluidToast,\n} from \"@fluid-app/ui-primitives\";\nimport { ScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useProfileTranslation } from \"@fluid-app/profile-core/translation-api-context\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\nimport { ProfileTranslationBridge } from \"../providers/ProfileTranslationBridge\";\nimport { ProfileContentScreen } from \"./ProfileContentScreen\";\n\ntype ProfileScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n onToast?: (message: string, type: \"success\" | \"error\" | \"warning\") => void;\n};\n\nfunction defaultToast(message: string, type: \"success\" | \"error\" | \"warning\") {\n fluidToast({ title: message, type });\n}\n\nexport function ProfileScreen({\n onToast,\n /* eslint-disable @typescript-eslint/no-unused-vars -- destructured to exclude from divProps spread */\n background,\n textColor,\n accentColor,\n padding,\n borderRadius,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n ...divProps\n}: ProfileScreenProps): React.JSX.Element {\n const { config } = useFluidContext();\n const effectiveToast = onToast ?? defaultToast;\n const countryIso = config.countryIso ?? \"US\";\n\n return (\n <ProfileTranslationBridge>\n <ProfileScreenContent\n onToast={effectiveToast}\n countryIso={countryIso}\n divProps={divProps}\n />\n </ProfileTranslationBridge>\n );\n}\n\nfunction ProfileScreenContent({\n onToast,\n countryIso,\n divProps,\n}: {\n onToast: (message: string, type: \"success\" | \"error\" | \"warning\") => void;\n countryIso: string;\n divProps: ComponentProps<\"div\">;\n}): React.JSX.Element {\n const { t } = useProfileTranslation();\n\n return (\n <>\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n <div {...divProps}>\n <ProfileContentScreen onToast={onToast} countryIso={countryIso} />\n </div>\n </>\n );\n}\n\nexport const profileScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ProfileScreen\",\n displayName: \"Profile Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|
|
@@ -681,7 +681,11 @@ const shopWidgetPropertySchema = {
|
|
|
681
681
|
key: "dataSource",
|
|
682
682
|
label: "Data Source",
|
|
683
683
|
type: "dataSource",
|
|
684
|
-
description: "",
|
|
684
|
+
description: "Configure the products rendered in the shop grid.",
|
|
685
|
+
targetProps: [{
|
|
686
|
+
key: "products",
|
|
687
|
+
description: "Products rendered in the shop grid, usually supplied by a data source."
|
|
688
|
+
}],
|
|
685
689
|
tab: "data",
|
|
686
690
|
group: "Data Configuration",
|
|
687
691
|
requiresKeyToBeTrue: "useDataSource"
|
|
@@ -691,4 +695,4 @@ const shopWidgetPropertySchema = {
|
|
|
691
695
|
//#endregion
|
|
692
696
|
export { ShopWidget_exports as n, shopWidgetPropertySchema as r, ShopWidget as t };
|
|
693
697
|
|
|
694
|
-
//# sourceMappingURL=ShopWidget-
|
|
698
|
+
//# sourceMappingURL=ShopWidget-BH0Pcepg.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShopWidget-jQ-osGyn.mjs","names":[],"sources":["../../widgets/src/widgets/ShopWidget.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentProps,\n} from \"react\";\nimport type React from \"react\";\nimport {\n formatPortalPrice,\n formatPortalPriceRange,\n usePortalProductCatalog,\n type PortalProductPageParam,\n type portalProducts,\n} from \"@fluid-app/products-core\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Badge,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport {\n getVideoThumbnailUrl,\n isVideoUrl,\n} from \"@fluid-app/shop-ui/utils/media-helpers\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport { ArrowUpDown, CirclePlay, ImageIcon } from \"lucide-react\";\nimport {\n getBorderRadiusField,\n getColorField,\n getPaddingField,\n} from \"../core/fields\";\nimport { useWidgetInteraction } from \"../contexts/WidgetInteractionContext\";\n\ntype ColumnsKey = 2 | 3 | 4 | 5 | 6;\n\ntype ShopWidgetProps = ComponentProps<\"div\"> & {\n titleEnabled?: boolean;\n title?: string;\n titleColor?: ColorOptions;\n showSearch?: boolean;\n showSort?: boolean;\n columns?: ColumnsKey;\n pageSize?: number;\n useDataSource?: boolean;\n products?: portalProducts.Product[];\n background?: BackgroundValue;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n cardBackground?: ColorOptions;\n cardTextColor?: ColorOptions;\n};\n\nconst GRID_BY_COLUMNS: Record<ColumnsKey, string> = {\n 2: \"grid grid-cols-2 gap-3 @lg:gap-4\",\n 3: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @lg:gap-4\",\n 4: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @lg:grid-cols-4 @lg:gap-4\",\n 5: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @md:grid-cols-4 @xl:grid-cols-5 @lg:gap-4\",\n 6: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @md:grid-cols-4 @xl:grid-cols-5 @2xl:grid-cols-6 @lg:gap-4\",\n};\n\nconst SORT_OPTIONS = [\n { id: \"title_asc\", label: \"Title (A–Z)\" },\n { id: \"title_desc\", label: \"Title (Z–A)\" },\n { id: \"price_asc\", label: \"Price (Low to High)\" },\n { id: \"price_desc\", label: \"Price (High to Low)\" },\n { id: \"created_at_desc\", label: \"Recently added\" },\n { id: \"created_at_asc\", label: \"Oldest\" },\n] as const;\n\nconst PREVIEW_PRODUCTS: ReadonlyArray<{\n id: string;\n name: string;\n price: string;\n}> = [\n { id: \"preview-1\", name: \"Premium Wireless Headphones\", price: \"$249\" },\n { id: \"preview-2\", name: \"Smart Watch Series X\", price: \"$199\" },\n { id: \"preview-3\", name: \"Sunglasses\", price: \"$79\" },\n { id: \"preview-4\", name: \"Trail Running Shoes\", price: \"$129\" },\n { id: \"preview-5\", name: \"Daypack Backpack\", price: \"$89\" },\n { id: \"preview-6\", name: \"Stainless Bottle\", price: \"$32\" },\n { id: \"preview-7\", name: \"Linen Throw\", price: \"$59\" },\n { id: \"preview-8\", name: \"Ceramic Mug\", price: \"$24\" },\n { id: \"preview-9\", name: \"Wool Blanket\", price: \"$148\" },\n { id: \"preview-10\", name: \"Leather Wallet\", price: \"$45\" },\n { id: \"preview-11\", name: \"Pour-Over Kettle\", price: \"$78\" },\n { id: \"preview-12\", name: \"Linen Shirt\", price: \"$95\" },\n];\n\nfunction clampColumns(value: number | undefined): ColumnsKey {\n const n = Math.round(value ?? 4);\n if (n <= 2) return 2;\n if (n >= 6) return 6;\n return n as ColumnsKey;\n}\n\nconst INFINITE_SCROLL_THRESHOLD = 10;\n\nfunction clampPageSize(value: number | undefined): number {\n const n = Math.round(value ?? 25);\n return Math.max(1, n);\n}\n\n// Data sources can deliver products in legacy/shareable shape (`title`,\n// `image_url`/`imageUrl`/`thumbnail_url`) or portal shape (`name`, `images`).\n// Modeled as a typed superset so the probe doesn't lose type-safety.\ntype LegacyProductFields = {\n title?: string;\n image_url?: string;\n imageUrl?: string;\n thumbnail_url?: string;\n};\n\ntype LegacyImageFields = {\n url?: string;\n image_url?: string;\n alt?: string | null;\n};\n\nfunction normalizeDataSourceProduct(\n input: portalProducts.Product,\n): portalProducts.Product {\n const r = input as portalProducts.Product & LegacyProductFields;\n const name = r.name ?? r.title ?? \"\";\n\n let images: portalProducts.ProductImage[];\n if (Array.isArray(r.images) && r.images.length > 0) {\n images = r.images.flatMap<portalProducts.ProductImage>((img) => {\n const i = img as LegacyImageFields;\n const url = i.url ?? i.image_url;\n if (!url) return [];\n return [{ url, alt: i.alt ?? null }];\n });\n } else {\n const url = r.image_url ?? r.imageUrl ?? r.thumbnail_url;\n images = url ? [{ url, alt: null }] : [];\n }\n\n // Strip undefined props from the spread so EOPT accepts the result.\n const cleaned: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(r)) {\n if (v !== undefined) cleaned[k] = v;\n }\n return { ...(cleaned as portalProducts.Product), name, images };\n}\n\nfunction getShopWidgetProductImage(\n product: portalProducts.Product,\n): string | null {\n if (product.images && product.images.length > 0) {\n return product.images[0]?.url ?? null;\n }\n return null;\n}\n\nfunction getShopWidgetProductPrice(\n product: portalProducts.Product,\n): string | null {\n if (product.is_bundle) {\n return (\n formatPortalPriceRange(product.price_range, product.currency) ??\n formatPortalPrice(product.price, product.currency)\n );\n }\n return formatPortalPrice(\n product.wholesale_price ?? product.price,\n product.currency,\n );\n}\n\nfunction getShopWidgetProductRetailPrice(\n product: portalProducts.Product,\n): string | null {\n if (!product.wholesale_price || product.is_bundle) return null;\n\n const wholesalePrice = formatPortalPrice(\n product.wholesale_price,\n product.currency,\n );\n const retailPrice = formatPortalPrice(product.price, product.currency);\n\n return retailPrice !== wholesalePrice ? retailPrice : null;\n}\n\n// Tailwind v4 @theme inline aliases --color-*; remap the underlying --card / --foreground tokens.\nfunction cardScopedStyle(\n cardBackground: ColorOptions | undefined,\n cardTextColor: ColorOptions | undefined,\n): React.CSSProperties {\n const style: Record<string, string> = {};\n if (cardBackground) style[\"--card\"] = `var(--${cardBackground})`;\n if (cardTextColor) style[\"--foreground\"] = `var(--${cardTextColor})`;\n return style as React.CSSProperties;\n}\n\nexport function ShopWidget({\n titleEnabled = true,\n title = \"\",\n titleColor = \"foreground\",\n showSearch = true,\n showSort = true,\n columns = 4,\n pageSize = 25,\n useDataSource = false,\n products,\n\n background = { type: \"solid\", color: \"background\" },\n padding = 6,\n borderRadius = \"lg\",\n\n cardBackground,\n cardTextColor,\n\n className,\n ...props\n}: ShopWidgetProps): React.JSX.Element {\n const { isPreview } = useWidgetPreviewContext();\n const safeColumns = clampColumns(columns);\n const safePageSize = clampPageSize(pageSize);\n\n const backgroundColor = background.color ?? \"background\";\n const backgroundImage =\n background.type === \"image\" &&\n (background.resource?.image_url || background.resource?.imageUrl)\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n return (\n <div\n className={`bg-${backgroundColor} p-${padding} rounded-${borderRadius} @container ${className ?? \"\"}`}\n style={{ backgroundImage }}\n {...props}\n >\n {isPreview ? (\n <PreviewBody\n columns={safeColumns}\n titleEnabled={titleEnabled}\n title={title}\n titleColor={titleColor}\n showSearch={showSearch}\n showSort={showSort}\n cardBackground={cardBackground}\n cardTextColor={cardTextColor}\n />\n ) : (\n <LiveBody\n columns={safeColumns}\n pageSize={safePageSize}\n titleEnabled={titleEnabled}\n title={title}\n titleColor={titleColor}\n showSearch={showSearch}\n showSort={showSort}\n useDataSource={useDataSource}\n products={products}\n cardBackground={cardBackground}\n cardTextColor={cardTextColor}\n />\n )}\n </div>\n );\n}\n\nfunction PreviewBody({\n columns,\n titleEnabled,\n title,\n titleColor,\n showSearch,\n showSort,\n cardBackground,\n cardTextColor,\n}: {\n columns: ColumnsKey;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n showSearch: boolean;\n showSort: boolean;\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n const hasTitle = titleEnabled && !!title;\n const hasHeader = hasTitle || showSearch || showSort;\n return (\n <div>\n {hasHeader && (\n <div className=\"mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-2\">\n {hasTitle && (\n <h2\n className={`text-${titleColor} text-2xl font-semibold tracking-tight`}\n >\n {title}\n </h2>\n )}\n {(showSearch || showSort) && (\n <div className=\"flex items-center gap-2 sm:ml-auto\">\n {showSearch && (\n <div\n aria-hidden\n className=\"bg-muted h-9 w-full max-w-sm rounded-md sm:w-64\"\n />\n )}\n {showSort && (\n <div aria-hidden className=\"bg-muted size-9 rounded-md\" />\n )}\n </div>\n )}\n </div>\n )}\n <div\n className={GRID_BY_COLUMNS[columns]}\n style={cardScopedStyle(cardBackground, cardTextColor)}\n >\n {PREVIEW_PRODUCTS.slice(0, columns * 2).map((p) => (\n <ProductCardCompact\n key={p.id}\n name={p.name}\n imageUrl={null}\n price={p.price}\n retailPrice={null}\n cv={null}\n qv={null}\n isBundle={false}\n />\n ))}\n </div>\n </div>\n );\n}\n\nfunction LiveBody({\n useDataSource,\n products,\n ...rest\n}: {\n columns: ColumnsKey;\n pageSize: number;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n showSearch: boolean;\n showSort: boolean;\n useDataSource: boolean;\n products: portalProducts.Product[] | undefined;\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n // Branch at the component level so the catalog hook (which throws when\n // PortalProductsCoreProvider is missing) is only called in catalog mode.\n if (useDataSource) {\n // DataAwareWidget owns loading state; undefined here just means no data source configured, not \"still loading.\"\n return (\n <DataSourceBody\n columns={rest.columns}\n titleEnabled={rest.titleEnabled}\n title={rest.title}\n titleColor={rest.titleColor}\n products={products ?? []}\n cardBackground={rest.cardBackground}\n cardTextColor={rest.cardTextColor}\n />\n );\n }\n return <CatalogBody {...rest} />;\n}\n\nfunction DataSourceBody({\n columns,\n titleEnabled,\n title,\n titleColor,\n products,\n cardBackground,\n cardTextColor,\n}: {\n columns: ColumnsKey;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n products: portalProducts.Product[];\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n const { onNavigate } = useWidgetInteraction();\n const handleSelect = useCallback(\n (id: string | number) => {\n onNavigate?.(`shop/${id}`);\n },\n [onNavigate],\n );\n\n const hasTitle = titleEnabled && !!title;\n\n return (\n <div>\n {hasTitle && (\n <h2\n className={`text-${titleColor} mb-6 text-2xl font-semibold tracking-tight`}\n >\n {title}\n </h2>\n )}\n {products.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <p className=\"text-muted-foreground text-sm\">No products yet.</p>\n </div>\n ) : (\n <div\n className={GRID_BY_COLUMNS[columns]}\n style={cardScopedStyle(cardBackground, cardTextColor)}\n >\n {products.map((product) => {\n if (product.id == null) return null;\n // Defensive: older widgets may have a data source that returns\n // non-product shareables (Medium, EnrollmentPack, Page, Library).\n // The shop widget only knows how to navigate to product detail,\n // so skip anything that isn't a Product. Items without\n // `shareable_type` (static products / untagged feeds) fall through.\n const shareableType = (product as { shareable_type?: string })\n .shareable_type;\n if (shareableType != null && shareableType !== \"Product\")\n return null;\n const productId = product.id;\n const normalizedProduct = normalizeDataSourceProduct(product);\n return (\n <ProductCardCompact\n key={productId}\n name={normalizedProduct.name ?? \"Product\"}\n imageUrl={getShopWidgetProductImage(normalizedProduct)}\n price={getShopWidgetProductPrice(normalizedProduct)}\n retailPrice={getShopWidgetProductRetailPrice(normalizedProduct)}\n cv={\n normalizedProduct.cv != null\n ? Number(normalizedProduct.cv)\n : null\n }\n qv={\n normalizedProduct.qv != null\n ? Number(normalizedProduct.qv)\n : null\n }\n isBundle={normalizedProduct.is_bundle === true}\n onClick={() => handleSelect(productId)}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nfunction CatalogBody({\n columns,\n pageSize,\n titleEnabled,\n title,\n titleColor,\n showSearch,\n showSort,\n cardBackground,\n cardTextColor,\n}: {\n columns: ColumnsKey;\n pageSize: number;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n showSearch: boolean;\n showSort: boolean;\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n const observerTarget = useRef<HTMLDivElement>(null);\n const { onNavigate } = useWidgetInteraction();\n const isMobile = useIsMobile();\n const [sortOpen, setSortOpen] = useState(false);\n\n // Cap the API batch in infinite mode so the observer has pages left to fetch.\n const isInfinite = pageSize > INFINITE_SCROLL_THRESHOLD;\n const apiPerPage = isInfinite ? INFINITE_SCROLL_THRESHOLD : pageSize;\n\n const catalog = usePortalProductCatalog({ perPage: apiPerPage });\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n isFetched,\n } = useInfiniteQuery({\n queryKey: catalog.queryKey,\n queryFn: ({ pageParam, signal }) =>\n catalog.fetchProducts(pageParam, signal),\n getNextPageParam: catalog.getNextPageParam,\n initialPageParam: undefined as PortalProductPageParam,\n });\n\n const allProducts = useMemo(\n () => data?.pages.flatMap((page) => page.products) ?? [],\n [data?.pages],\n );\n\n const visibleProducts = useMemo(\n () => (isInfinite ? allProducts : allProducts.slice(0, pageSize)),\n [allProducts, pageSize, isInfinite],\n );\n const reachedLimit = !isInfinite && visibleProducts.length >= pageSize;\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (\n entries[0]?.isIntersecting &&\n hasNextPage &&\n !isFetchingNextPage &&\n !reachedLimit\n ) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage, reachedLimit],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n const observer = new IntersectionObserver(handleIntersect, {\n threshold: 0.1,\n rootMargin: \"200px\",\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [handleIntersect]);\n\n const handleSelect = useCallback(\n (id: string | number) => {\n onNavigate?.(`shop/${id}`);\n },\n [onNavigate],\n );\n\n const hasTitle = titleEnabled && !!title;\n const hasHeader = hasTitle || showSearch || showSort;\n\n return (\n <div>\n {hasHeader && (\n <div className=\"mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-2\">\n {hasTitle && (\n <h2\n className={`text-${titleColor} text-2xl font-semibold tracking-tight`}\n >\n {title}\n </h2>\n )}\n {(showSearch || showSort) && (\n <div className=\"flex items-center gap-2 sm:ml-auto\">\n {showSearch && (\n <div className=\"w-full max-w-sm sm:w-64\">\n <SearchSort\n searchValue={catalog.searchTerm}\n onSearchChange={catalog.setSearchTerm}\n placeholder=\"Search products\"\n />\n </div>\n )}\n {showSort &&\n (isMobile ? (\n <>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"border-foreground/10 size-9 shrink-0\"\n aria-label=\"Sort by\"\n onClick={() => setSortOpen(true)}\n >\n <ArrowUpDown className=\"size-3\" />\n </Button>\n <MobileActionSheet\n open={sortOpen}\n onOpenChange={setSortOpen}\n title=\"Sort by\"\n actions={SORT_OPTIONS.map((option) => ({\n id: option.id,\n label: option.label,\n selected: catalog.currentSort === option.id,\n onSelect: () => catalog.setCurrentSort(option.id),\n }))}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"border-foreground/10 size-9 shrink-0\"\n >\n <ArrowUpDown className=\"size-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-60\">\n <DropdownMenuLabel>Sort by</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={catalog.currentSort}\n onValueChange={catalog.setCurrentSort}\n >\n {SORT_OPTIONS.map((opt) => (\n <DropdownMenuRadioItem key={opt.id} value={opt.id}>\n {opt.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n ))}\n </div>\n )}\n </div>\n )}\n\n {isLoading ? (\n // Floor of 8 keeps mobile (always 2 cols) from rendering a stubby grid.\n <SkeletonGrid columns={columns} count={Math.max(columns * 2, 8)} />\n ) : error && visibleProducts.length === 0 ? (\n <div className=\"bg-destructive/10 text-destructive my-6 rounded-lg px-4 py-3 text-sm\">\n Couldn’t load products. Try again later.\n </div>\n ) : isFetched && visibleProducts.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n {catalog.searchTerm\n ? `No products match “${catalog.searchTerm}”.`\n : \"No products yet.\"}\n </p>\n </div>\n ) : (\n <>\n <div\n className={GRID_BY_COLUMNS[columns]}\n style={cardScopedStyle(cardBackground, cardTextColor)}\n >\n {visibleProducts.map((product) => {\n if (product.id == null) return null;\n const productId = product.id;\n return (\n <ProductCardCompact\n key={productId}\n name={product.name ?? \"Product\"}\n imageUrl={getShopWidgetProductImage(product)}\n price={getShopWidgetProductPrice(product)}\n retailPrice={getShopWidgetProductRetailPrice(product)}\n cv={product.cv != null ? Number(product.cv) : null}\n qv={product.qv != null ? Number(product.qv) : null}\n isBundle={product.is_bundle === true}\n onClick={() => handleSelect(productId)}\n />\n );\n })}\n </div>\n {!reachedLimit && <div ref={observerTarget} />}\n {isFetchingNextPage && !reachedLimit && (\n <div className=\"mt-6\">\n <SkeletonGrid columns={columns} count={columns} />\n </div>\n )}\n {error && (\n <div className=\"bg-destructive/10 text-destructive mt-6 rounded-lg px-4 py-3 text-sm\">\n Couldn’t load more products. Try again later.\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nfunction ProductCardCompact({\n name,\n imageUrl,\n price,\n retailPrice,\n cv,\n qv,\n isBundle,\n onClick,\n}: {\n name: string;\n imageUrl: string | null;\n price: string | null;\n retailPrice: string | null;\n cv: number | null;\n qv: number | null;\n isBundle: boolean;\n onClick?: () => void;\n}) {\n const [imageFailed, setImageFailed] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const interactive = !!onClick;\n const isVideo = isVideoUrl(imageUrl);\n const coverImageUrl =\n imageUrl && !imageFailed\n ? isVideo\n ? getVideoThumbnailUrl(imageUrl)\n : imageUrl\n : null;\n const showVolume = cv != null || qv != null;\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={!interactive}\n className={`bg-card text-card-foreground group flex min-w-0 flex-col overflow-hidden rounded-lg text-left ${interactive ? \"cursor-pointer\" : \"cursor-default\"} focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none`}\n >\n <div className=\"bg-muted/40 relative aspect-square w-full overflow-hidden rounded-md @md:rounded-lg\">\n {isVideo && imageUrl && isHovered ? (\n <video\n src={imageUrl}\n className=\"absolute inset-0 h-full w-full object-cover\"\n autoPlay\n muted\n loop\n playsInline\n onMouseLeave={() => setIsHovered(false)}\n />\n ) : coverImageUrl ? (\n <img\n src={coverImageUrl}\n alt={name}\n className=\"h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.02]\"\n loading=\"lazy\"\n decoding=\"async\"\n onError={() => setImageFailed(true)}\n onMouseEnter={() => isVideo && setIsHovered(true)}\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <ImageIcon className=\"text-muted-foreground/40 size-10\" />\n </div>\n )}\n {isVideo && !isHovered && coverImageUrl && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm\">\n <CirclePlay className=\"size-8 text-white\" />\n </div>\n </div>\n )}\n {isBundle && (\n <Badge variant=\"secondary\" className=\"absolute top-2 left-2\">\n Bundle\n </Badge>\n )}\n </div>\n <div className=\"min-w-0 px-0.5 pt-2 pb-0.5 @md:px-1 @md:pt-2.5\">\n <h3 className=\"text-foreground line-clamp-1 text-xs leading-snug font-semibold @md:text-sm\">\n {name}\n </h3>\n <div className=\"mt-1 flex min-w-0 items-center gap-1.5\">\n {price && (\n <span className=\"text-foreground text-sm leading-none font-semibold @md:text-base\">\n {price}\n </span>\n )}\n {retailPrice && (\n <span className=\"text-muted-foreground text-xs leading-none line-through @md:text-sm\">\n {retailPrice}\n </span>\n )}\n </div>\n {showVolume && (\n <p className=\"text-muted-foreground mt-1 truncate text-xs leading-tight\">\n CV {cv ?? \"-\"} | QV {qv ?? \"-\"}\n </p>\n )}\n </div>\n </button>\n );\n}\n\nfunction SkeletonGrid({\n columns,\n count,\n}: {\n columns: ColumnsKey;\n count: number;\n}) {\n return (\n <div className={GRID_BY_COLUMNS[columns]}>\n {Array.from({ length: count }, (_, i) => (\n <div key={i} className=\"space-y-3\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/3\" />\n </div>\n ))}\n </div>\n );\n}\n\nexport const shopWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShopWidget\",\n displayName: \"Shop\",\n tabsConfig: [\n { id: \"styling\", label: \"Styling\" },\n { id: \"data\", label: \"Data\" },\n ],\n dataSourceTargetProps: [\"products\"],\n fields: [\n // Title group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the heading shown above the grid\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"title\",\n label: \"Title\",\n type: \"text\",\n description: \"Heading text shown above the grid\",\n defaultValue: \"\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color of the heading text\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Display group\n {\n key: \"showSearch\",\n label: \"Show Search\",\n type: \"boolean\",\n description: \"Display the search input above the grid\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Display\",\n requiresKeyValue: { key: \"useDataSource\", value: false },\n },\n {\n key: \"showSort\",\n label: \"Show Sort\",\n type: \"boolean\",\n description: \"Display the sort menu above the grid\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Display\",\n requiresKeyValue: { key: \"useDataSource\", value: false },\n },\n {\n key: \"columns\",\n label: \"Columns (desktop)\",\n type: \"select\",\n description:\n \"Max columns at the largest breakpoint. Narrow widgets show 2 columns; wider containers scale up smoothly to the chosen maximum.\",\n options: [\n { label: \"2\", value: 2 },\n { label: \"3\", value: 3 },\n { label: \"4\", value: 4 },\n { label: \"5\", value: 5 },\n { label: \"6\", value: 6 },\n ],\n defaultValue: 4,\n tab: \"styling\",\n group: \"Display\",\n },\n\n // Design group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Container background\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Inner spacing around the grid\",\n defaultValue: 6,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Container corner radius\",\n defaultValue: \"lg\",\n tab: \"styling\",\n group: \"Design\",\n }),\n\n // Card group — optional theme overrides; unset means \"inherit from theme\".\n getColorField({\n key: \"cardBackground\",\n label: \"Card Background\",\n description:\n \"Override the card background color. Leave unset to use the theme's card color.\",\n tab: \"styling\",\n group: \"Card\",\n }),\n getColorField({\n key: \"cardTextColor\",\n label: \"Card Text\",\n description:\n \"Override title + price color inside each card. Leave unset to use the theme's foreground color.\",\n tab: \"styling\",\n group: \"Card\",\n }),\n\n // Data tab\n {\n key: \"useDataSource\",\n label: \"Use Data Source\",\n type: \"boolean\",\n description:\n \"Off shows all products from the catalog. On replaces the catalog with a data source — disables search and sort.\",\n defaultValue: false,\n tab: \"data\",\n group: \"Data Configuration\",\n },\n {\n key: \"pageSize\",\n label: \"Products to show\",\n type: \"number\",\n description:\n \"Up to 10 acts as a hard cap. Above 10 the grid switches to infinite scroll (products load in batches of 10 as you scroll).\",\n defaultValue: 25,\n tab: \"data\",\n group: \"Data Configuration\",\n requiresKeyValue: { key: \"useDataSource\", value: false },\n },\n {\n key: \"dataSource\",\n label: \"Data Source\",\n type: \"dataSource\",\n description: \"\",\n tab: \"data\",\n group: \"Data Configuration\",\n requiresKeyToBeTrue: \"useDataSource\",\n },\n ],\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ShopWidget-BH0Pcepg.mjs","names":[],"sources":["../../widgets/src/widgets/ShopWidget.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentProps,\n} from \"react\";\nimport type React from \"react\";\nimport {\n formatPortalPrice,\n formatPortalPriceRange,\n usePortalProductCatalog,\n type PortalProductPageParam,\n type portalProducts,\n} from \"@fluid-app/products-core\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Badge,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport {\n getVideoThumbnailUrl,\n isVideoUrl,\n} from \"@fluid-app/shop-ui/utils/media-helpers\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport { ArrowUpDown, CirclePlay, ImageIcon } from \"lucide-react\";\nimport {\n getBorderRadiusField,\n getColorField,\n getPaddingField,\n} from \"../core/fields\";\nimport { useWidgetInteraction } from \"../contexts/WidgetInteractionContext\";\n\ntype ColumnsKey = 2 | 3 | 4 | 5 | 6;\n\ntype ShopWidgetProps = ComponentProps<\"div\"> & {\n titleEnabled?: boolean;\n title?: string;\n titleColor?: ColorOptions;\n showSearch?: boolean;\n showSort?: boolean;\n columns?: ColumnsKey;\n pageSize?: number;\n useDataSource?: boolean;\n products?: portalProducts.Product[];\n background?: BackgroundValue;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n cardBackground?: ColorOptions;\n cardTextColor?: ColorOptions;\n};\n\nconst GRID_BY_COLUMNS: Record<ColumnsKey, string> = {\n 2: \"grid grid-cols-2 gap-3 @lg:gap-4\",\n 3: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @lg:gap-4\",\n 4: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @lg:grid-cols-4 @lg:gap-4\",\n 5: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @md:grid-cols-4 @xl:grid-cols-5 @lg:gap-4\",\n 6: \"grid grid-cols-2 gap-3 @sm:grid-cols-3 @md:grid-cols-4 @xl:grid-cols-5 @2xl:grid-cols-6 @lg:gap-4\",\n};\n\nconst SORT_OPTIONS = [\n { id: \"title_asc\", label: \"Title (A–Z)\" },\n { id: \"title_desc\", label: \"Title (Z–A)\" },\n { id: \"price_asc\", label: \"Price (Low to High)\" },\n { id: \"price_desc\", label: \"Price (High to Low)\" },\n { id: \"created_at_desc\", label: \"Recently added\" },\n { id: \"created_at_asc\", label: \"Oldest\" },\n] as const;\n\nconst PREVIEW_PRODUCTS: ReadonlyArray<{\n id: string;\n name: string;\n price: string;\n}> = [\n { id: \"preview-1\", name: \"Premium Wireless Headphones\", price: \"$249\" },\n { id: \"preview-2\", name: \"Smart Watch Series X\", price: \"$199\" },\n { id: \"preview-3\", name: \"Sunglasses\", price: \"$79\" },\n { id: \"preview-4\", name: \"Trail Running Shoes\", price: \"$129\" },\n { id: \"preview-5\", name: \"Daypack Backpack\", price: \"$89\" },\n { id: \"preview-6\", name: \"Stainless Bottle\", price: \"$32\" },\n { id: \"preview-7\", name: \"Linen Throw\", price: \"$59\" },\n { id: \"preview-8\", name: \"Ceramic Mug\", price: \"$24\" },\n { id: \"preview-9\", name: \"Wool Blanket\", price: \"$148\" },\n { id: \"preview-10\", name: \"Leather Wallet\", price: \"$45\" },\n { id: \"preview-11\", name: \"Pour-Over Kettle\", price: \"$78\" },\n { id: \"preview-12\", name: \"Linen Shirt\", price: \"$95\" },\n];\n\nfunction clampColumns(value: number | undefined): ColumnsKey {\n const n = Math.round(value ?? 4);\n if (n <= 2) return 2;\n if (n >= 6) return 6;\n return n as ColumnsKey;\n}\n\nconst INFINITE_SCROLL_THRESHOLD = 10;\n\nfunction clampPageSize(value: number | undefined): number {\n const n = Math.round(value ?? 25);\n return Math.max(1, n);\n}\n\n// Data sources can deliver products in legacy/shareable shape (`title`,\n// `image_url`/`imageUrl`/`thumbnail_url`) or portal shape (`name`, `images`).\n// Modeled as a typed superset so the probe doesn't lose type-safety.\ntype LegacyProductFields = {\n title?: string;\n image_url?: string;\n imageUrl?: string;\n thumbnail_url?: string;\n};\n\ntype LegacyImageFields = {\n url?: string;\n image_url?: string;\n alt?: string | null;\n};\n\nfunction normalizeDataSourceProduct(\n input: portalProducts.Product,\n): portalProducts.Product {\n const r = input as portalProducts.Product & LegacyProductFields;\n const name = r.name ?? r.title ?? \"\";\n\n let images: portalProducts.ProductImage[];\n if (Array.isArray(r.images) && r.images.length > 0) {\n images = r.images.flatMap<portalProducts.ProductImage>((img) => {\n const i = img as LegacyImageFields;\n const url = i.url ?? i.image_url;\n if (!url) return [];\n return [{ url, alt: i.alt ?? null }];\n });\n } else {\n const url = r.image_url ?? r.imageUrl ?? r.thumbnail_url;\n images = url ? [{ url, alt: null }] : [];\n }\n\n // Strip undefined props from the spread so EOPT accepts the result.\n const cleaned: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(r)) {\n if (v !== undefined) cleaned[k] = v;\n }\n return { ...(cleaned as portalProducts.Product), name, images };\n}\n\nfunction getShopWidgetProductImage(\n product: portalProducts.Product,\n): string | null {\n if (product.images && product.images.length > 0) {\n return product.images[0]?.url ?? null;\n }\n return null;\n}\n\nfunction getShopWidgetProductPrice(\n product: portalProducts.Product,\n): string | null {\n if (product.is_bundle) {\n return (\n formatPortalPriceRange(product.price_range, product.currency) ??\n formatPortalPrice(product.price, product.currency)\n );\n }\n return formatPortalPrice(\n product.wholesale_price ?? product.price,\n product.currency,\n );\n}\n\nfunction getShopWidgetProductRetailPrice(\n product: portalProducts.Product,\n): string | null {\n if (!product.wholesale_price || product.is_bundle) return null;\n\n const wholesalePrice = formatPortalPrice(\n product.wholesale_price,\n product.currency,\n );\n const retailPrice = formatPortalPrice(product.price, product.currency);\n\n return retailPrice !== wholesalePrice ? retailPrice : null;\n}\n\n// Tailwind v4 @theme inline aliases --color-*; remap the underlying --card / --foreground tokens.\nfunction cardScopedStyle(\n cardBackground: ColorOptions | undefined,\n cardTextColor: ColorOptions | undefined,\n): React.CSSProperties {\n const style: Record<string, string> = {};\n if (cardBackground) style[\"--card\"] = `var(--${cardBackground})`;\n if (cardTextColor) style[\"--foreground\"] = `var(--${cardTextColor})`;\n return style as React.CSSProperties;\n}\n\nexport function ShopWidget({\n titleEnabled = true,\n title = \"\",\n titleColor = \"foreground\",\n showSearch = true,\n showSort = true,\n columns = 4,\n pageSize = 25,\n useDataSource = false,\n products,\n\n background = { type: \"solid\", color: \"background\" },\n padding = 6,\n borderRadius = \"lg\",\n\n cardBackground,\n cardTextColor,\n\n className,\n ...props\n}: ShopWidgetProps): React.JSX.Element {\n const { isPreview } = useWidgetPreviewContext();\n const safeColumns = clampColumns(columns);\n const safePageSize = clampPageSize(pageSize);\n\n const backgroundColor = background.color ?? \"background\";\n const backgroundImage =\n background.type === \"image\" &&\n (background.resource?.image_url || background.resource?.imageUrl)\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n return (\n <div\n className={`bg-${backgroundColor} p-${padding} rounded-${borderRadius} @container ${className ?? \"\"}`}\n style={{ backgroundImage }}\n {...props}\n >\n {isPreview ? (\n <PreviewBody\n columns={safeColumns}\n titleEnabled={titleEnabled}\n title={title}\n titleColor={titleColor}\n showSearch={showSearch}\n showSort={showSort}\n cardBackground={cardBackground}\n cardTextColor={cardTextColor}\n />\n ) : (\n <LiveBody\n columns={safeColumns}\n pageSize={safePageSize}\n titleEnabled={titleEnabled}\n title={title}\n titleColor={titleColor}\n showSearch={showSearch}\n showSort={showSort}\n useDataSource={useDataSource}\n products={products}\n cardBackground={cardBackground}\n cardTextColor={cardTextColor}\n />\n )}\n </div>\n );\n}\n\nfunction PreviewBody({\n columns,\n titleEnabled,\n title,\n titleColor,\n showSearch,\n showSort,\n cardBackground,\n cardTextColor,\n}: {\n columns: ColumnsKey;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n showSearch: boolean;\n showSort: boolean;\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n const hasTitle = titleEnabled && !!title;\n const hasHeader = hasTitle || showSearch || showSort;\n return (\n <div>\n {hasHeader && (\n <div className=\"mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-2\">\n {hasTitle && (\n <h2\n className={`text-${titleColor} text-2xl font-semibold tracking-tight`}\n >\n {title}\n </h2>\n )}\n {(showSearch || showSort) && (\n <div className=\"flex items-center gap-2 sm:ml-auto\">\n {showSearch && (\n <div\n aria-hidden\n className=\"bg-muted h-9 w-full max-w-sm rounded-md sm:w-64\"\n />\n )}\n {showSort && (\n <div aria-hidden className=\"bg-muted size-9 rounded-md\" />\n )}\n </div>\n )}\n </div>\n )}\n <div\n className={GRID_BY_COLUMNS[columns]}\n style={cardScopedStyle(cardBackground, cardTextColor)}\n >\n {PREVIEW_PRODUCTS.slice(0, columns * 2).map((p) => (\n <ProductCardCompact\n key={p.id}\n name={p.name}\n imageUrl={null}\n price={p.price}\n retailPrice={null}\n cv={null}\n qv={null}\n isBundle={false}\n />\n ))}\n </div>\n </div>\n );\n}\n\nfunction LiveBody({\n useDataSource,\n products,\n ...rest\n}: {\n columns: ColumnsKey;\n pageSize: number;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n showSearch: boolean;\n showSort: boolean;\n useDataSource: boolean;\n products: portalProducts.Product[] | undefined;\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n // Branch at the component level so the catalog hook (which throws when\n // PortalProductsCoreProvider is missing) is only called in catalog mode.\n if (useDataSource) {\n // DataAwareWidget owns loading state; undefined here just means no data source configured, not \"still loading.\"\n return (\n <DataSourceBody\n columns={rest.columns}\n titleEnabled={rest.titleEnabled}\n title={rest.title}\n titleColor={rest.titleColor}\n products={products ?? []}\n cardBackground={rest.cardBackground}\n cardTextColor={rest.cardTextColor}\n />\n );\n }\n return <CatalogBody {...rest} />;\n}\n\nfunction DataSourceBody({\n columns,\n titleEnabled,\n title,\n titleColor,\n products,\n cardBackground,\n cardTextColor,\n}: {\n columns: ColumnsKey;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n products: portalProducts.Product[];\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n const { onNavigate } = useWidgetInteraction();\n const handleSelect = useCallback(\n (id: string | number) => {\n onNavigate?.(`shop/${id}`);\n },\n [onNavigate],\n );\n\n const hasTitle = titleEnabled && !!title;\n\n return (\n <div>\n {hasTitle && (\n <h2\n className={`text-${titleColor} mb-6 text-2xl font-semibold tracking-tight`}\n >\n {title}\n </h2>\n )}\n {products.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <p className=\"text-muted-foreground text-sm\">No products yet.</p>\n </div>\n ) : (\n <div\n className={GRID_BY_COLUMNS[columns]}\n style={cardScopedStyle(cardBackground, cardTextColor)}\n >\n {products.map((product) => {\n if (product.id == null) return null;\n // Defensive: older widgets may have a data source that returns\n // non-product shareables (Medium, EnrollmentPack, Page, Library).\n // The shop widget only knows how to navigate to product detail,\n // so skip anything that isn't a Product. Items without\n // `shareable_type` (static products / untagged feeds) fall through.\n const shareableType = (product as { shareable_type?: string })\n .shareable_type;\n if (shareableType != null && shareableType !== \"Product\")\n return null;\n const productId = product.id;\n const normalizedProduct = normalizeDataSourceProduct(product);\n return (\n <ProductCardCompact\n key={productId}\n name={normalizedProduct.name ?? \"Product\"}\n imageUrl={getShopWidgetProductImage(normalizedProduct)}\n price={getShopWidgetProductPrice(normalizedProduct)}\n retailPrice={getShopWidgetProductRetailPrice(normalizedProduct)}\n cv={\n normalizedProduct.cv != null\n ? Number(normalizedProduct.cv)\n : null\n }\n qv={\n normalizedProduct.qv != null\n ? Number(normalizedProduct.qv)\n : null\n }\n isBundle={normalizedProduct.is_bundle === true}\n onClick={() => handleSelect(productId)}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nfunction CatalogBody({\n columns,\n pageSize,\n titleEnabled,\n title,\n titleColor,\n showSearch,\n showSort,\n cardBackground,\n cardTextColor,\n}: {\n columns: ColumnsKey;\n pageSize: number;\n titleEnabled: boolean;\n title: string | undefined;\n titleColor: ColorOptions;\n showSearch: boolean;\n showSort: boolean;\n cardBackground: ColorOptions | undefined;\n cardTextColor: ColorOptions | undefined;\n}) {\n const observerTarget = useRef<HTMLDivElement>(null);\n const { onNavigate } = useWidgetInteraction();\n const isMobile = useIsMobile();\n const [sortOpen, setSortOpen] = useState(false);\n\n // Cap the API batch in infinite mode so the observer has pages left to fetch.\n const isInfinite = pageSize > INFINITE_SCROLL_THRESHOLD;\n const apiPerPage = isInfinite ? INFINITE_SCROLL_THRESHOLD : pageSize;\n\n const catalog = usePortalProductCatalog({ perPage: apiPerPage });\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n isFetched,\n } = useInfiniteQuery({\n queryKey: catalog.queryKey,\n queryFn: ({ pageParam, signal }) =>\n catalog.fetchProducts(pageParam, signal),\n getNextPageParam: catalog.getNextPageParam,\n initialPageParam: undefined as PortalProductPageParam,\n });\n\n const allProducts = useMemo(\n () => data?.pages.flatMap((page) => page.products) ?? [],\n [data?.pages],\n );\n\n const visibleProducts = useMemo(\n () => (isInfinite ? allProducts : allProducts.slice(0, pageSize)),\n [allProducts, pageSize, isInfinite],\n );\n const reachedLimit = !isInfinite && visibleProducts.length >= pageSize;\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (\n entries[0]?.isIntersecting &&\n hasNextPage &&\n !isFetchingNextPage &&\n !reachedLimit\n ) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage, reachedLimit],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n const observer = new IntersectionObserver(handleIntersect, {\n threshold: 0.1,\n rootMargin: \"200px\",\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [handleIntersect]);\n\n const handleSelect = useCallback(\n (id: string | number) => {\n onNavigate?.(`shop/${id}`);\n },\n [onNavigate],\n );\n\n const hasTitle = titleEnabled && !!title;\n const hasHeader = hasTitle || showSearch || showSort;\n\n return (\n <div>\n {hasHeader && (\n <div className=\"mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-2\">\n {hasTitle && (\n <h2\n className={`text-${titleColor} text-2xl font-semibold tracking-tight`}\n >\n {title}\n </h2>\n )}\n {(showSearch || showSort) && (\n <div className=\"flex items-center gap-2 sm:ml-auto\">\n {showSearch && (\n <div className=\"w-full max-w-sm sm:w-64\">\n <SearchSort\n searchValue={catalog.searchTerm}\n onSearchChange={catalog.setSearchTerm}\n placeholder=\"Search products\"\n />\n </div>\n )}\n {showSort &&\n (isMobile ? (\n <>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"border-foreground/10 size-9 shrink-0\"\n aria-label=\"Sort by\"\n onClick={() => setSortOpen(true)}\n >\n <ArrowUpDown className=\"size-3\" />\n </Button>\n <MobileActionSheet\n open={sortOpen}\n onOpenChange={setSortOpen}\n title=\"Sort by\"\n actions={SORT_OPTIONS.map((option) => ({\n id: option.id,\n label: option.label,\n selected: catalog.currentSort === option.id,\n onSelect: () => catalog.setCurrentSort(option.id),\n }))}\n />\n </>\n ) : (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"border-foreground/10 size-9 shrink-0\"\n >\n <ArrowUpDown className=\"size-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-60\">\n <DropdownMenuLabel>Sort by</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={catalog.currentSort}\n onValueChange={catalog.setCurrentSort}\n >\n {SORT_OPTIONS.map((opt) => (\n <DropdownMenuRadioItem key={opt.id} value={opt.id}>\n {opt.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n ))}\n </div>\n )}\n </div>\n )}\n\n {isLoading ? (\n // Floor of 8 keeps mobile (always 2 cols) from rendering a stubby grid.\n <SkeletonGrid columns={columns} count={Math.max(columns * 2, 8)} />\n ) : error && visibleProducts.length === 0 ? (\n <div className=\"bg-destructive/10 text-destructive my-6 rounded-lg px-4 py-3 text-sm\">\n Couldn’t load products. Try again later.\n </div>\n ) : isFetched && visibleProducts.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n {catalog.searchTerm\n ? `No products match “${catalog.searchTerm}”.`\n : \"No products yet.\"}\n </p>\n </div>\n ) : (\n <>\n <div\n className={GRID_BY_COLUMNS[columns]}\n style={cardScopedStyle(cardBackground, cardTextColor)}\n >\n {visibleProducts.map((product) => {\n if (product.id == null) return null;\n const productId = product.id;\n return (\n <ProductCardCompact\n key={productId}\n name={product.name ?? \"Product\"}\n imageUrl={getShopWidgetProductImage(product)}\n price={getShopWidgetProductPrice(product)}\n retailPrice={getShopWidgetProductRetailPrice(product)}\n cv={product.cv != null ? Number(product.cv) : null}\n qv={product.qv != null ? Number(product.qv) : null}\n isBundle={product.is_bundle === true}\n onClick={() => handleSelect(productId)}\n />\n );\n })}\n </div>\n {!reachedLimit && <div ref={observerTarget} />}\n {isFetchingNextPage && !reachedLimit && (\n <div className=\"mt-6\">\n <SkeletonGrid columns={columns} count={columns} />\n </div>\n )}\n {error && (\n <div className=\"bg-destructive/10 text-destructive mt-6 rounded-lg px-4 py-3 text-sm\">\n Couldn’t load more products. Try again later.\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nfunction ProductCardCompact({\n name,\n imageUrl,\n price,\n retailPrice,\n cv,\n qv,\n isBundle,\n onClick,\n}: {\n name: string;\n imageUrl: string | null;\n price: string | null;\n retailPrice: string | null;\n cv: number | null;\n qv: number | null;\n isBundle: boolean;\n onClick?: () => void;\n}) {\n const [imageFailed, setImageFailed] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const interactive = !!onClick;\n const isVideo = isVideoUrl(imageUrl);\n const coverImageUrl =\n imageUrl && !imageFailed\n ? isVideo\n ? getVideoThumbnailUrl(imageUrl)\n : imageUrl\n : null;\n const showVolume = cv != null || qv != null;\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={!interactive}\n className={`bg-card text-card-foreground group flex min-w-0 flex-col overflow-hidden rounded-lg text-left ${interactive ? \"cursor-pointer\" : \"cursor-default\"} focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none`}\n >\n <div className=\"bg-muted/40 relative aspect-square w-full overflow-hidden rounded-md @md:rounded-lg\">\n {isVideo && imageUrl && isHovered ? (\n <video\n src={imageUrl}\n className=\"absolute inset-0 h-full w-full object-cover\"\n autoPlay\n muted\n loop\n playsInline\n onMouseLeave={() => setIsHovered(false)}\n />\n ) : coverImageUrl ? (\n <img\n src={coverImageUrl}\n alt={name}\n className=\"h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.02]\"\n loading=\"lazy\"\n decoding=\"async\"\n onError={() => setImageFailed(true)}\n onMouseEnter={() => isVideo && setIsHovered(true)}\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <ImageIcon className=\"text-muted-foreground/40 size-10\" />\n </div>\n )}\n {isVideo && !isHovered && coverImageUrl && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"flex size-12 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm\">\n <CirclePlay className=\"size-8 text-white\" />\n </div>\n </div>\n )}\n {isBundle && (\n <Badge variant=\"secondary\" className=\"absolute top-2 left-2\">\n Bundle\n </Badge>\n )}\n </div>\n <div className=\"min-w-0 px-0.5 pt-2 pb-0.5 @md:px-1 @md:pt-2.5\">\n <h3 className=\"text-foreground line-clamp-1 text-xs leading-snug font-semibold @md:text-sm\">\n {name}\n </h3>\n <div className=\"mt-1 flex min-w-0 items-center gap-1.5\">\n {price && (\n <span className=\"text-foreground text-sm leading-none font-semibold @md:text-base\">\n {price}\n </span>\n )}\n {retailPrice && (\n <span className=\"text-muted-foreground text-xs leading-none line-through @md:text-sm\">\n {retailPrice}\n </span>\n )}\n </div>\n {showVolume && (\n <p className=\"text-muted-foreground mt-1 truncate text-xs leading-tight\">\n CV {cv ?? \"-\"} | QV {qv ?? \"-\"}\n </p>\n )}\n </div>\n </button>\n );\n}\n\nfunction SkeletonGrid({\n columns,\n count,\n}: {\n columns: ColumnsKey;\n count: number;\n}) {\n return (\n <div className={GRID_BY_COLUMNS[columns]}>\n {Array.from({ length: count }, (_, i) => (\n <div key={i} className=\"space-y-3\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/3\" />\n </div>\n ))}\n </div>\n );\n}\n\nexport const shopWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShopWidget\",\n displayName: \"Shop\",\n tabsConfig: [\n { id: \"styling\", label: \"Styling\" },\n { id: \"data\", label: \"Data\" },\n ],\n dataSourceTargetProps: [\"products\"],\n fields: [\n // Title group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the heading shown above the grid\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"title\",\n label: \"Title\",\n type: \"text\",\n description: \"Heading text shown above the grid\",\n defaultValue: \"\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color of the heading text\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Display group\n {\n key: \"showSearch\",\n label: \"Show Search\",\n type: \"boolean\",\n description: \"Display the search input above the grid\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Display\",\n requiresKeyValue: { key: \"useDataSource\", value: false },\n },\n {\n key: \"showSort\",\n label: \"Show Sort\",\n type: \"boolean\",\n description: \"Display the sort menu above the grid\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Display\",\n requiresKeyValue: { key: \"useDataSource\", value: false },\n },\n {\n key: \"columns\",\n label: \"Columns (desktop)\",\n type: \"select\",\n description:\n \"Max columns at the largest breakpoint. Narrow widgets show 2 columns; wider containers scale up smoothly to the chosen maximum.\",\n options: [\n { label: \"2\", value: 2 },\n { label: \"3\", value: 3 },\n { label: \"4\", value: 4 },\n { label: \"5\", value: 5 },\n { label: \"6\", value: 6 },\n ],\n defaultValue: 4,\n tab: \"styling\",\n group: \"Display\",\n },\n\n // Design group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Container background\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Inner spacing around the grid\",\n defaultValue: 6,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Container corner radius\",\n defaultValue: \"lg\",\n tab: \"styling\",\n group: \"Design\",\n }),\n\n // Card group — optional theme overrides; unset means \"inherit from theme\".\n getColorField({\n key: \"cardBackground\",\n label: \"Card Background\",\n description:\n \"Override the card background color. Leave unset to use the theme's card color.\",\n tab: \"styling\",\n group: \"Card\",\n }),\n getColorField({\n key: \"cardTextColor\",\n label: \"Card Text\",\n description:\n \"Override title + price color inside each card. Leave unset to use the theme's foreground color.\",\n tab: \"styling\",\n group: \"Card\",\n }),\n\n // Data tab\n {\n key: \"useDataSource\",\n label: \"Use Data Source\",\n type: \"boolean\",\n description:\n \"Off shows all products from the catalog. On replaces the catalog with a data source — disables search and sort.\",\n defaultValue: false,\n tab: \"data\",\n group: \"Data Configuration\",\n },\n {\n key: \"pageSize\",\n label: \"Products to show\",\n type: \"number\",\n description:\n \"Up to 10 acts as a hard cap. Above 10 the grid switches to infinite scroll (products load in batches of 10 as you scroll).\",\n defaultValue: 25,\n tab: \"data\",\n group: \"Data Configuration\",\n requiresKeyValue: { key: \"useDataSource\", value: false },\n },\n {\n key: \"dataSource\",\n label: \"Data Source\",\n type: \"dataSource\",\n description: \"Configure the products rendered in the shop grid.\",\n targetProps: [\n {\n key: \"products\",\n description:\n \"Products rendered in the shop grid, usually supplied by a data source.\",\n },\n ],\n tab: \"data\",\n group: \"Data Configuration\",\n requiresKeyToBeTrue: \"useDataSource\",\n },\n ],\n};\n"],"mappings":"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cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,kBAAkB;IAAE,KAAK;IAAiB,OAAO;IAAO;GACzD;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,kBAAkB;IAAE,KAAK;IAAiB,OAAO;IAAO;GACzD;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,SAAS;IACP;KAAE,OAAO;KAAK,OAAO;KAAG;IACxB;KAAE,OAAO;KAAK,OAAO;KAAG;IACxB;KAAE,OAAO;KAAK,OAAO;KAAG;IACxB;KAAE,OAAO;KAAK,OAAO;KAAG;IACxB;KAAE,OAAO;KAAK,OAAO;KAAG;IACzB;GACD,cAAc;GACd,KAAK;GACL,OAAO;GACR;EAGD;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EAGF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aACE;GACF,KAAK;GACL,OAAO;GACR,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aACE;GACF,KAAK;GACL,OAAO;GACR,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACP,kBAAkB;IAAE,KAAK;IAAiB,OAAO;IAAO;GACzD;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,aAAa,CACX;IACE,KAAK;IACL,aACE;IACH,CACF;GACD,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACF;CACF"}
|
|
@@ -677,7 +677,11 @@ const shopWidgetPropertySchema = {
|
|
|
677
677
|
key: "dataSource",
|
|
678
678
|
label: "Data Source",
|
|
679
679
|
type: "dataSource",
|
|
680
|
-
description: "",
|
|
680
|
+
description: "Configure the products rendered in the shop grid.",
|
|
681
|
+
targetProps: [{
|
|
682
|
+
key: "products",
|
|
683
|
+
description: "Products rendered in the shop grid, usually supplied by a data source."
|
|
684
|
+
}],
|
|
681
685
|
tab: "data",
|
|
682
686
|
group: "Data Configuration",
|
|
683
687
|
requiresKeyToBeTrue: "useDataSource"
|
|
@@ -698,4 +702,4 @@ Object.defineProperty(exports, "shopWidgetPropertySchema", {
|
|
|
698
702
|
}
|
|
699
703
|
});
|
|
700
704
|
|
|
701
|
-
//# sourceMappingURL=ShopWidget-
|
|
705
|
+
//# sourceMappingURL=ShopWidget-RfF5ZRv2.cjs.map
|