@fluid-app/portal-sdk 0.1.84 → 0.1.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AppDownloadScreen-B-JdFulZ.cjs → AppDownloadScreen-BcpYbAZo.cjs} +4 -4
- package/dist/{AppDownloadScreen-CaTNCtzb.cjs → AppDownloadScreen-DQOgNOQY.cjs} +2 -2
- package/dist/{AppDownloadScreen-CaTNCtzb.cjs.map → AppDownloadScreen-DQOgNOQY.cjs.map} +1 -1
- package/dist/{AppDownloadScreen-BE6M8WxN.mjs → AppDownloadScreen-DsKIUmDP.mjs} +2 -2
- package/dist/{AppDownloadScreen-BE6M8WxN.mjs.map → AppDownloadScreen-DsKIUmDP.mjs.map} +1 -1
- package/dist/{CarouselWidget-v1NWYsQe.mjs → CarouselWidget-BSUwgJQ3.mjs} +3 -3
- package/dist/CarouselWidget-BSUwgJQ3.mjs.map +1 -0
- package/dist/{CarouselWidget-A4_pvjCL.cjs → CarouselWidget-BWHWH48S.cjs} +1 -1
- package/dist/{CarouselWidget-DDW8GqoX.cjs → CarouselWidget-srEpxcKU.cjs} +3 -3
- package/dist/CarouselWidget-srEpxcKU.cjs.map +1 -0
- package/dist/{ContactsScreen-DvmqkXOu.cjs → ContactsScreen-1DC328FQ.cjs} +2 -2
- package/dist/{ContactsScreen-DvmqkXOu.cjs.map → ContactsScreen-1DC328FQ.cjs.map} +1 -1
- package/dist/{ContactsScreen-DrkHWPMm.mjs → ContactsScreen-Bs3DtjXw.mjs} +2 -2
- package/dist/{ContactsScreen-DrkHWPMm.mjs.map → ContactsScreen-Bs3DtjXw.mjs.map} +1 -1
- package/dist/{ContactsScreen-Du9M90zi.cjs → ContactsScreen-C-v5Yrxn.cjs} +4 -4
- package/dist/{FluidProvider-BSBLJp15.cjs → FluidProvider-DGm9zaDs.cjs} +5 -5
- package/dist/{FluidProvider-BSBLJp15.cjs.map → FluidProvider-DGm9zaDs.cjs.map} +1 -1
- package/dist/{FluidProvider-CwkMUOoy.mjs → FluidProvider-DpiMY5TW.mjs} +5 -5
- package/dist/{FluidProvider-CwkMUOoy.mjs.map → FluidProvider-DpiMY5TW.mjs.map} +1 -1
- package/dist/{MessagingScreen-Cg2XvQZF.mjs → MessagingScreen-BI8BzJKm.mjs} +3 -3
- package/dist/{MessagingScreen-Cg2XvQZF.mjs.map → MessagingScreen-BI8BzJKm.mjs.map} +1 -1
- package/dist/{MessagingScreen-C4-8_bvB.mjs → MessagingScreen-CSkPf1fv.mjs} +4 -4
- package/dist/{MessagingScreen-BlN6KjV-.cjs → MessagingScreen-CVAFDLsZ.cjs} +4 -4
- package/dist/{MessagingScreen-Lc81OXIN.cjs → MessagingScreen-GijHV-DQ.cjs} +3 -3
- package/dist/{MessagingScreen-Lc81OXIN.cjs.map → MessagingScreen-GijHV-DQ.cjs.map} +1 -1
- package/dist/{MySiteScreen-BedF13nt.mjs → MySiteScreen-Bg6LNzsn.mjs} +2 -2
- package/dist/{MySiteScreen-BedF13nt.mjs.map → MySiteScreen-Bg6LNzsn.mjs.map} +1 -1
- package/dist/{MySiteScreen-DEOKI32H.cjs → MySiteScreen-CrLr2D4S.cjs} +4 -4
- package/dist/{MySiteScreen-B-I4V3Cc.cjs → MySiteScreen-D8HSLknT.cjs} +2 -2
- package/dist/{MySiteScreen-B-I4V3Cc.cjs.map → MySiteScreen-D8HSLknT.cjs.map} +1 -1
- package/dist/{OrdersScreen-BE_E1QGL.cjs → OrdersScreen-DiQTxyIy.cjs} +4 -4
- package/dist/{OrdersScreen-CfAmUUN3.mjs → OrdersScreen-Dn0fEtdh.mjs} +3 -3
- package/dist/{OrdersScreen-CfAmUUN3.mjs.map → OrdersScreen-Dn0fEtdh.mjs.map} +1 -1
- package/dist/{OrdersScreen-C-rU9cx6.cjs → OrdersScreen-DuGMu--7.cjs} +3 -3
- package/dist/{OrdersScreen-C-rU9cx6.cjs.map → OrdersScreen-DuGMu--7.cjs.map} +1 -1
- package/dist/{PointsWidget-D8U2k-_A.cjs → PointsWidget-Bgl95R-6.cjs} +2 -2
- package/dist/PointsWidget-Bgl95R-6.cjs.map +1 -0
- package/dist/{PointsWidget-CYesuDDG.mjs → PointsWidget-C05iCMne.mjs} +2 -2
- package/dist/PointsWidget-C05iCMne.mjs.map +1 -0
- package/dist/{ProductsScreen-Csw96ete.cjs → ProductsScreen--1YW_UgE.cjs} +3 -3
- package/dist/{ProductsScreen-Csw96ete.cjs.map → ProductsScreen--1YW_UgE.cjs.map} +1 -1
- package/dist/{ProductsScreen-DUPhUJ7p.cjs → ProductsScreen-C6vvURBQ.cjs} +4 -4
- package/dist/{ProductsScreen-CgG79XW9.mjs → ProductsScreen-COd2v64K.mjs} +5 -4
- package/dist/{ProductsScreen-CGo9KQ9L.mjs → ProductsScreen-Cp0Ca5fO.mjs} +3 -3
- package/dist/{ProductsScreen-CGo9KQ9L.mjs.map → ProductsScreen-Cp0Ca5fO.mjs.map} +1 -1
- package/dist/{ProfileScreen-CANmoFsP.cjs → ProfileScreen-BbMzMV7N.cjs} +3 -3
- package/dist/{ProfileScreen-CANmoFsP.cjs.map → ProfileScreen-BbMzMV7N.cjs.map} +1 -1
- package/dist/{ProfileScreen-BSZLS9hY.cjs → ProfileScreen-CuCgYLYg.cjs} +4 -4
- package/dist/{ProfileScreen-C4g7ZGPo.mjs → ProfileScreen-WigtmiOv.mjs} +3 -3
- package/dist/{ProfileScreen-C4g7ZGPo.mjs.map → ProfileScreen-WigtmiOv.mjs.map} +1 -1
- package/dist/{ShareablesScreen-loETsu9v.cjs → ShareablesScreen-CECBa_sH.cjs} +4 -4
- package/dist/{ShareablesScreen-BK0H5fb2.mjs → ShareablesScreen-Ceb5wUmc.mjs} +4 -4
- package/dist/{ShareablesScreen-BK0H5fb2.mjs.map → ShareablesScreen-Ceb5wUmc.mjs.map} +1 -1
- package/dist/{ShareablesScreen-D0kIiW8S.cjs → ShareablesScreen-D5_P3VVk.cjs} +4 -4
- package/dist/{ShareablesScreen-D0kIiW8S.cjs.map → ShareablesScreen-D5_P3VVk.cjs.map} +1 -1
- package/dist/{ShareablesScreen-BmKYYRZq.mjs → ShareablesScreen-Ds-a4dSK.mjs} +5 -4
- package/dist/{ShopScreen-CDwCW5Go.cjs → ShopScreen-BG2O9grs.cjs} +93 -74
- package/dist/ShopScreen-BG2O9grs.cjs.map +1 -0
- package/dist/{ShopScreen-CaTpU5go.cjs → ShopScreen-CfriRC8H.cjs} +4 -4
- package/dist/{ShopScreen-B0Pr0CTu.mjs → ShopScreen-DjXzkEHA.mjs} +94 -75
- package/dist/{ShopScreen-B0Pr0CTu.mjs.map → ShopScreen-DjXzkEHA.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-D3LAx25s.mjs → SubscriptionsScreen-Bc1L9w_R.mjs} +3 -3
- package/dist/{SubscriptionsScreen-D3LAx25s.mjs.map → SubscriptionsScreen-Bc1L9w_R.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-CfvaEuTS.cjs → SubscriptionsScreen-CntycOyp.cjs} +3 -3
- package/dist/{SubscriptionsScreen-CfvaEuTS.cjs.map → SubscriptionsScreen-CntycOyp.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-Dpn0YuDb.cjs → SubscriptionsScreen-DCUFhSbh.cjs} +4 -4
- package/dist/index.cjs +36 -36
- package/dist/index.d.cts +1 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +36 -36
- package/dist/{use-account-clients-62dZrh9E.cjs → use-account-clients-C9UjVcyo.cjs} +2 -2
- package/dist/{use-account-clients-62dZrh9E.cjs.map → use-account-clients-C9UjVcyo.cjs.map} +1 -1
- package/dist/{use-account-clients-Cf_6sxeC.mjs → use-account-clients-oKk8PvPK.mjs} +2 -2
- package/dist/{use-account-clients-Cf_6sxeC.mjs.map → use-account-clients-oKk8PvPK.mjs.map} +1 -1
- package/dist/{use-current-user-Bl9nPNFt.mjs → use-current-user-CoGu5DT7.mjs} +3 -3
- package/dist/{use-current-user-Bl9nPNFt.mjs.map → use-current-user-CoGu5DT7.mjs.map} +1 -1
- package/dist/{use-current-user-SFCzJt0q.cjs → use-current-user-D2YUKSpk.cjs} +3 -3
- package/dist/{use-current-user-SFCzJt0q.cjs.map → use-current-user-D2YUKSpk.cjs.map} +1 -1
- package/dist/{use-customer-account-BoobPvSQ.cjs → use-customer-account-B-eDvs2m.cjs} +3 -3
- package/dist/{use-customer-account-BoobPvSQ.cjs.map → use-customer-account-B-eDvs2m.cjs.map} +1 -1
- package/dist/{use-customer-account-Dt3kO7ti.mjs → use-customer-account-CQr2JJOY.mjs} +3 -3
- package/dist/{use-customer-account-Dt3kO7ti.mjs.map → use-customer-account-CQr2JJOY.mjs.map} +1 -1
- package/dist/{use-fluid-api-DPhr-9Jv.mjs → use-fluid-api-B6740M-8.mjs} +2 -2
- package/dist/{use-fluid-api-DPhr-9Jv.mjs.map → use-fluid-api-B6740M-8.mjs.map} +1 -1
- package/dist/{use-fluid-api-DwRQk00z.cjs → use-fluid-api-LVdptjW2.cjs} +2 -2
- package/dist/{use-fluid-api-DwRQk00z.cjs.map → use-fluid-api-LVdptjW2.cjs.map} +1 -1
- package/package.json +10 -10
- package/dist/CarouselWidget-DDW8GqoX.cjs.map +0 -1
- package/dist/CarouselWidget-v1NWYsQe.mjs.map +0 -1
- package/dist/PointsWidget-CYesuDDG.mjs.map +0 -1
- package/dist/PointsWidget-D8U2k-_A.cjs.map +0 -1
- package/dist/ShopScreen-CDwCW5Go.cjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PointsWidget-D8U2k-_A.cjs","names":["DataSourceProvider","useDataSourceConfig","useWidgetPreviewContext","borderWidthClasses","borderColorClasses","ErrorState","Coins","ChevronDown","getFontSizeField","getColorField","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField"],"sources":["../../core/src/data-sources/fetchers/api.ts","../../core/src/data-sources/fetchers/custom.ts","../../core/src/data-sources/fetchers/static.ts","../../core/src/data-sources/transformers.ts","../../core/src/data-sources/registry.ts","../../react/src/data-sources/registry-context.tsx","../../widgets/src/hooks/use-points-ledger.preview.ts","../../widgets/src/hooks/use-points-ledger.ts","../../widgets/src/widgets/PointsWidget.tsx"],"sourcesContent":["import type { ApiDataSource, DataSourceContext } from \"../types\";\n\n/**\n * Extracts a value from an object using dot notation path\n * e.g., getByPath({ data: { items: [1,2,3] } }, 'data.items') => [1,2,3]\n */\nfunction getByPath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce((current, key) => {\n if (current && typeof current === \"object\" && key in current) {\n return (current as Record<string, unknown>)[key];\n }\n return undefined;\n }, obj);\n}\n\n/**\n * Replaces {variable} placeholders in an endpoint path with values from the\n * variables map. E.g., \"/reps/{rep_id}/most_shared\" with { rep_id: \"42\" }\n * becomes \"/reps/42/most_shared\".\n */\nfunction interpolateVariables(\n endpoint: string,\n variables?: Record<string, string>,\n): string {\n if (!variables) return endpoint;\n const resolved = endpoint.replace(\n /\\{(\\w+)\\}/g,\n (match, key: string) => variables[key] ?? match,\n );\n const unresolved = resolved.match(/\\{(\\w+)\\}/g);\n if (unresolved) {\n console.warn(\n `[DataSource] Unresolved variable placeholders in endpoint: ${unresolved.join(\", \")}. ` +\n `Endpoint: \"${endpoint}\". Available variables: ${Object.keys(variables).join(\", \") || \"(none)\"}`,\n );\n }\n return stripAllQueryParams(resolved);\n}\n\n/**\n * Removes query parameters whose value is \"all\" from a URL.\n *\n * Convention: \"all\" is a reserved no-op value for data source preset\n * config fields. Preset authors should use \"all\" as the default option\n * for \"show everything\" filters. This function strips those params so\n * APIs that don't recognise \"all\" fall back to their default (return\n * everything) behaviour. Do not use \"all\" as a meaningful filter value\n * in preset endpoints.\n */\nfunction stripAllQueryParams(url: string): string {\n const qIndex = url.indexOf(\"?\");\n if (qIndex === -1) return url;\n\n const base = url.slice(0, qIndex);\n const query = url.slice(qIndex + 1);\n const kept = query\n .split(\"&\")\n .filter((pair) => {\n const eqIndex = pair.indexOf(\"=\");\n if (eqIndex === -1) return true;\n return pair.slice(eqIndex + 1) !== \"all\";\n })\n .join(\"&\");\n\n return kept ? `${base}?${kept}` : base;\n}\n\n/**\n * Resolves the full URL for an endpoint.\n * - Substitutes {variable} placeholders from context variables\n * - Absolute URLs (starting with http:// or https://) are used as-is\n * - Relative paths are prefixed with the context's baseUrl\n */\nfunction resolveEndpointUrl(\n endpoint: string,\n baseUrl?: string,\n variables?: Record<string, string>,\n): string {\n const resolved = interpolateVariables(endpoint, variables);\n\n // If endpoint is already absolute, use it directly\n if (resolved.startsWith(\"http://\") || resolved.startsWith(\"https://\")) {\n return resolved;\n }\n\n // If we have a baseUrl, prepend it to the relative endpoint\n if (baseUrl) {\n // Ensure proper joining (no double slashes)\n const base = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n const path = resolved.startsWith(\"/\") ? resolved : `/${resolved}`;\n return `${base}${path}`;\n }\n\n // No baseUrl provided, return endpoint as-is (will likely fail for relative paths)\n return resolved;\n}\n\n/**\n * Default API fetcher implementation\n */\nexport async function apiFetcher(\n source: ApiDataSource,\n context: DataSourceContext,\n): Promise<unknown> {\n const { endpoint, method = \"GET\", headers = {}, body, resultPath } = source;\n\n // Merge context variables with per-source variables (source overrides context)\n const mergedVariables = { ...context.variables, ...source.variables };\n\n // Resolve the full URL, substituting variables and using baseUrl for relative endpoints\n const url = resolveEndpointUrl(endpoint, context.baseUrl, mergedVariables);\n\n // Skip the fetch if the URL still contains unresolved {variable} placeholders\n // (e.g. customer_id not yet loaded). Returning undefined avoids a bad API request.\n if (/\\{\\w+\\}/.test(url)) {\n return undefined;\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n \"content-type\": \"application/json\",\n ...context.getApiHeaders?.(),\n ...headers,\n },\n signal: context.signal,\n };\n\n if (body && (method === \"POST\" || method === \"PUT\")) {\n fetchOptions.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(\n `API request failed: ${response.status} ${response.statusText}`,\n );\n }\n\n const data = await response.json();\n\n // Extract data at resultPath if specified\n if (resultPath) {\n return getByPath(data, resultPath);\n }\n\n return data;\n}\n","import type {\n CustomDataSource,\n DataSourceContext,\n SelectedItem,\n ShareableType,\n} from \"../types\";\n\n/**\n * Individual resource endpoints for each shareable type.\n * These fetch single items by ID from the company API.\n */\nconst SHAREABLE_ENDPOINTS: Record<\n ShareableType,\n (id: string | number) => string\n> = {\n Medium: (id) => `/company/media/${id}`,\n Page: (id) => `/company/pages/${id}`,\n Library: (id) => `/company/libraries/${id}`,\n Product: (id) => `/company/v1/products/${id}`,\n EnrollmentPack: (id) => `/enrollment_packs/${id}`,\n};\n\n/**\n * Extract the actual resource data from various API response structures.\n * Different endpoints wrap their data differently:\n * - Product: { status, resource: { product: {...} } }\n * - EnrollmentPack: { status, resource: { enrollment_pack: {...} } }\n * - Medium/Page/Library: Direct root or { medium: {...} }\n */\nfunction extractResourceData(\n json: Record<string, unknown>,\n shareableType: ShareableType,\n): Record<string, unknown> {\n // Try nested resource structure first (Product, EnrollmentPack)\n const resource = json.resource as Record<string, unknown> | undefined;\n if (resource) {\n // Check for type-specific key within resource\n const typeKey = shareableType.toLowerCase().replace(\"pack\", \"_pack\");\n if (resource[typeKey]) {\n return resource[typeKey] as Record<string, unknown>;\n }\n // Check common keys\n if (resource.product) return resource.product as Record<string, unknown>;\n if (resource.enrollment_pack)\n return resource.enrollment_pack as Record<string, unknown>;\n }\n\n // Try direct type key (e.g., { medium: {...} })\n const directKey = shareableType.toLowerCase().replace(\"pack\", \"_pack\");\n if (json[directKey]) {\n return json[directKey] as Record<string, unknown>;\n }\n\n // Try common response shapes\n if (json.data) return json.data as Record<string, unknown>;\n if (json.medium) return json.medium as Record<string, unknown>;\n if (json.page) return json.page as Record<string, unknown>;\n if (json.library) return json.library as Record<string, unknown>;\n if (json.product) return json.product as Record<string, unknown>;\n if (json.enrollment_pack)\n return json.enrollment_pack as Record<string, unknown>;\n\n // Return as-is if no wrapper found (Library uses direct serialization)\n return json;\n}\n\n/**\n * Result from fetching a single shareable item\n */\ninterface FetchResult {\n item: SelectedItem;\n data: unknown;\n error?: Error;\n}\n\n/**\n * Fetch a single shareable item by ID from the individual resource endpoint.\n */\nasync function fetchSingleItem(\n item: SelectedItem,\n signal: AbortSignal,\n baseUrl: string = \"\",\n getApiHeaders?: () => Record<string, string>,\n): Promise<FetchResult> {\n const getEndpoint = SHAREABLE_ENDPOINTS[item.shareableType];\n if (!getEndpoint) {\n return {\n item,\n data: null,\n error: new Error(`Unknown shareable type: ${item.shareableType}`),\n };\n }\n\n const endpoint = `${baseUrl}${getEndpoint(item.id)}`;\n\n try {\n const response = await fetch(endpoint, {\n method: \"GET\",\n headers: {\n ...getApiHeaders?.(),\n \"Content-Type\": \"application/json\",\n },\n signal,\n });\n\n if (!response.ok) {\n return {\n item,\n data: null,\n error: new Error(\n `Failed to fetch ${item.shareableType} with ID ${item.id}: ${response.status}`,\n ),\n };\n }\n\n const json = await response.json();\n\n // Extract the actual resource data from the response wrapper\n const resourceData = extractResourceData(json, item.shareableType);\n\n // Add shareable_type to the response for transformer compatibility\n if (resourceData && typeof resourceData === \"object\") {\n (resourceData as Record<string, unknown>).shareable_type =\n item.shareableType;\n }\n\n return { item, data: resourceData };\n } catch (error) {\n return {\n item,\n data: null,\n error:\n error instanceof Error\n ? error\n : new Error(\n `Unknown error fetching ${item.shareableType} ${item.id}`,\n ),\n };\n }\n}\n\n/**\n * Custom fetcher that fetches selected items.\n *\n * Fetches items from individual resource endpoints using the configured\n * getApiHeaders for authentication.\n *\n * Fetches all items in parallel for performance.\n * Returns an array of successfully fetched items, preserving order.\n * Logs warnings for failed fetches but doesn't throw.\n */\nexport async function customFetcher(\n source: CustomDataSource,\n context: DataSourceContext,\n): Promise<unknown[]> {\n const { selectedItems } = source;\n if (!selectedItems || selectedItems.length === 0) {\n return [];\n }\n\n // Fallback: fetch from individual endpoints (no auth)\n const results = await Promise.all(\n selectedItems.map((item) =>\n fetchSingleItem(\n item,\n context.signal,\n context.baseUrl,\n context.getApiHeaders,\n ),\n ),\n );\n\n // Process results, collecting successful data and tracking errors\n const successfulData: unknown[] = [];\n const errors: Error[] = [];\n\n for (const result of results) {\n if (result.error) {\n console.warn(\n `[CustomFetcher] Failed to fetch ${result.item.shareableType} #${result.item.id}:`,\n result.error.message,\n );\n errors.push(result.error);\n continue;\n }\n\n if (result.data) {\n successfulData.push(result.data);\n }\n }\n\n // Throw error if all fetches failed\n if (successfulData.length === 0 && errors.length > 0) {\n throw new Error(\n `Failed to fetch all ${errors.length} item(s): ${errors[0]?.message || \"Unknown error\"}`,\n );\n }\n\n return successfulData;\n}\n","import type {\n StaticDataSource,\n DataSourceContext,\n StaticSourceType,\n} from \"../types\";\n\n/**\n * Endpoint patterns for fetching products by static source type.\n * Tags require backend API support (not yet available).\n */\nconst STATIC_ENDPOINTS: Record<\"collections\" | \"categories\", string> = {\n collections: \"/company/v1/products\",\n categories: \"/company/v1/products\",\n};\n\n/**\n * Query parameter names for each static source type.\n */\nconst STATIC_QUERY_PARAMS: Record<\"collections\" | \"categories\", string> = {\n collections: \"collection_id\",\n categories: \"category_id\",\n};\n\n/**\n * Extract products array from API response.\n * The /company/v1/products endpoint returns { products: [...], meta: {...} }\n */\nfunction extractProducts(json: Record<string, unknown>): unknown[] {\n if (Array.isArray(json.products)) {\n return json.products;\n }\n if (Array.isArray(json.data)) {\n return json.data;\n }\n if (Array.isArray(json)) {\n return json;\n }\n console.warn(\n \"[extractProducts] Could not find products array. Available keys:\",\n Object.keys(json),\n );\n return [];\n}\n\n/**\n * Static data source fetcher.\n *\n * Fetches products filtered by collection or category ID.\n * Tags are not yet supported (requires backend API changes).\n *\n * @param source - The static data source configuration\n * @param context - The data source context with baseUrl and headers\n * @returns Array of products matching the filter criteria\n */\nexport async function staticFetcher(\n source: StaticDataSource,\n context: DataSourceContext,\n): Promise<unknown[]> {\n const { staticType, selectedId } = source;\n\n // Tags require backend API support - not yet implemented\n if (staticType === \"tags\") {\n throw new Error(\n \"Tags data source is not yet supported. Please select Collections or Categories instead.\",\n );\n }\n\n const endpoint = STATIC_ENDPOINTS[staticType];\n const queryParam = STATIC_QUERY_PARAMS[staticType];\n\n if (!endpoint || !queryParam) {\n throw new Error(\n `Invalid data source configuration: \"${staticType}\" is not a supported static source type.`,\n );\n }\n\n const url = new URL(\n `${context.baseUrl ?? \"\"}${endpoint}`,\n context.baseUrl ? undefined : window.location.origin,\n );\n url.searchParams.set(queryParam, String(selectedId));\n\n try {\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n ...context.getApiHeaders?.(),\n \"Content-Type\": \"application/json\",\n },\n signal: context.signal,\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch products for ${staticType} ${selectedId}: ${response.status} ${response.statusText}`,\n );\n }\n\n const json = await response.json();\n return extractProducts(json);\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n // Request was cancelled, don't log as error\n return [];\n }\n console.error(\n `[staticFetcher] Error fetching ${staticType} ${selectedId}:`,\n error,\n );\n throw error;\n }\n}\n\n/**\n * Endpoints for listing static source items (collections, categories, tags).\n */\nexport const LIST_ENDPOINTS: Record<\n StaticSourceType,\n { endpoint: string; resultKey: string }\n> = {\n collections: {\n endpoint: \"/company/v1/collections\",\n resultKey: \"collections\",\n },\n categories: { endpoint: \"/company/v1/categories\", resultKey: \"categories\" },\n tags: { endpoint: \"/company/v1/tags\", resultKey: \"tags\" },\n};\n","/**\n * Widget Transformers\n *\n * Transform functions that map API response data to widget-specific prop shapes.\n * Each widget has one transformer that handles all data structure variants:\n * - Standard: Direct field name match\n * - Legacy: Different field names that need mapping\n * - Minimal: Bare minimum fields that need defaults\n *\n * Usage:\n * 1. Transformers are registered in the DataSourceRegistry\n * 2. Reference by name in ApiDataSource.transform\n * 3. Applied after resultPath extraction, before targetProps assignment\n */\n\nimport type { DataTransformer, DataSource } from \"./types\";\n\n/**\n * Helper to extract image URL from various API response structures.\n * Handles:\n * - Flat fields (Medium, Page, Product): image_url, imageUrl, thumbnail_url, src\n * - Nested images array (EnrollmentPack): images[0].image_url\n * - Nested library_items (Library): library_items[0].relateable.image_url\n */\nfunction extractImageUrl(d: Record<string, unknown>): string {\n // Try flat fields first (Medium, Page, Product)\n if (d.image_url) return d.image_url as string;\n if (d.imageUrl) return d.imageUrl as string;\n if (d.thumbnail_url) return d.thumbnail_url as string;\n if (d.src) return d.src as string;\n\n // Try nested images array (EnrollmentPack)\n const images = d.images as Array<Record<string, unknown>> | undefined;\n if (images && images.length > 0) {\n const firstImage = images[0];\n if (firstImage?.image_url) return firstImage.image_url as string;\n if (firstImage?.url) return firstImage.url as string;\n }\n\n // Try nested library_items (Library/Playlist)\n const libraryItems = d.library_items as\n | Array<Record<string, unknown>>\n | undefined;\n if (libraryItems && libraryItems.length > 0) {\n const firstItem = libraryItems[0];\n const relateable = firstItem?.relateable as\n | Record<string, unknown>\n | undefined;\n if (relateable?.image_url) return relateable.image_url as string;\n if (relateable?.imageUrl) return relateable.imageUrl as string;\n if (relateable?.thumbnail_url) return relateable.thumbnail_url as string;\n }\n\n return \"\";\n}\n\n/**\n * Helper to extract description from various API response structures.\n * Handles different field names across endpoints.\n */\nfunction extractDescription(d: Record<string, unknown>): string {\n // Try various description field names\n const desc =\n d.description ?? d.stripped ?? d.stripped_description ?? d.body ?? \"\";\n\n // Handle nested description object (some APIs return { body: \"...\" })\n if (typeof desc === \"object\" && desc !== null && \"body\" in desc) {\n return ((desc as Record<string, unknown>).body as string) ?? \"\";\n }\n\n return desc as string;\n}\n\n/**\n * Helper to extract custom widget config from a selected item.\n */\nfunction extractCustomWidgetConfig(\n d: Record<string, unknown>,\n source: DataSource | undefined,\n): Record<string, unknown> {\n if (source === undefined || source.type !== \"custom\") return {};\n\n const selectedItem = source.selectedItems.find(\n (s) => String(s.id) === String(d.id),\n );\n return selectedItem?.widgetConfig ?? {};\n}\n\n/**\n * ImageWidget transformer from shareable data\n */\nconst toImagePropsFromShareable: DataTransformer = (data) => {\n const item = Array.isArray(data) ? data[0] : data;\n if (!item || typeof item !== \"object\") {\n return { src: \"\", alt: \"Image\", objectFit: \"cover\" };\n }\n\n const d = item as Record<string, unknown>;\n return {\n src: extractImageUrl(d),\n alt: (d.title ?? d.name ?? d.alt ?? \"Image\") as string,\n objectFit: \"cover\" as const,\n };\n};\n\n/**\n * VideoWidget transformer from shareable data\n */\nconst toVideoPropsFromShareable: DataTransformer = (data) => {\n const item = Array.isArray(data) ? data[0] : data;\n if (!item || typeof item !== \"object\") {\n return { src: \"\", poster: \"\", caption: \"\" };\n }\n\n const d = item as Record<string, unknown>;\n return {\n src: ((d.video_url ?? d.videoUrl ?? d.src) as string) || \"\",\n poster: extractImageUrl(d),\n caption: (d.title ?? d.name ?? d.caption ?? \"\") as string,\n };\n};\n\n/**\n * Shareable content transformer\n * Normalizes shareable API responses to a consistent format\n */\nconst toShareableProps: DataTransformer = (data, source) => {\n if (!Array.isArray(data)) return [];\n\n // Derive a fallback shareable type from the data source configuration.\n // API presets pass shareable_type in their variables (e.g., \"products\").\n // Custom sources store it per-item (handled below).\n const sourceTypeHint =\n source?.type === \"api\" ? source.variables?.shareable_type : undefined;\n\n return data.map((item: unknown) => {\n const d = item as Record<string, unknown>;\n\n const widgetConfig = extractCustomWidgetConfig(d, source);\n\n // Resolve shareable type from the item itself, then fall back to source hint\n const shareableType = (d.type ??\n d.relateable_type ??\n d.shareable_type ??\n d.shareableType ??\n sourceTypeHint ??\n \"\") as string;\n\n return {\n ...d,\n id: d.id,\n title: (d.title ?? d.name ?? \"\") as string,\n description: extractDescription(d),\n imageUrl: extractImageUrl(d),\n videoUrl: ((d.video_url ?? d.videoUrl) as string) || null,\n shareableType,\n kind: (d.kind ?? \"image\") as string,\n status: (d.status ?? \"active\") as string,\n wholesalePrice: d.display_wholesale_price ?? d.wholesale_price ?? null,\n subscriptionPrice: d.subscription_price ?? null,\n outOfStock: d.out_of_stock ?? false,\n lowInStock: d.low_in_stock ?? false,\n ...widgetConfig,\n };\n });\n};\n\n/**\n * Carousel slides from shareables transformer\n */\nconst toCarouselSlidesFromShareables: DataTransformer = (data, source) => {\n if (!Array.isArray(data)) return [];\n\n return data.map((item: unknown, index: number) => {\n const d = item as Record<string, unknown>;\n\n const widgetConfig = extractCustomWidgetConfig(d, source);\n\n const imageUrl = extractImageUrl(d);\n const isVideo = d.kind === \"video\" || d.videoUrl || d.video_url;\n const title = (d.title ?? d.name ?? \"\") as string;\n\n const content = isVideo\n ? {\n type: \"VideoWidget\",\n props: {\n src: ((d.videoUrl ?? d.video_url) as string) || \"\",\n poster: imageUrl,\n caption: title,\n autoplay: true,\n loop: true,\n muted: true,\n controls: false,\n },\n }\n : {\n type: \"ImageWidget\",\n props: {\n src: imageUrl,\n alt: title || \"Slide image\",\n objectFit: \"cover\",\n },\n };\n\n const baseSlide = {\n id: String(d.id ?? `slide-${index}`),\n content,\n title,\n description: extractDescription(d),\n };\n\n return {\n ...baseSlide,\n ...widgetConfig,\n };\n });\n};\n\n/**\n * Orders table transformer\n * Normalizes order list data into ShareableItem rows + column definitions.\n * Returns { data, columns } so multi-targetProp mapping sends each to the widget.\n */\nconst toOrderTableProps: DataTransformer = (data) => {\n const orders = Array.isArray(data) ? data : [];\n\n const columns = [\n { key: \"imageUrl\", label: \"Image\", sortable: false },\n { key: \"order_number\", label: \"Order #\", sortable: true },\n { key: \"price\", label: \"Total\", sortable: true },\n { key: \"status\", label: \"Status\", sortable: true },\n ];\n\n const rows = orders.map((item: unknown) => {\n const d = item as Record<string, unknown>;\n const firstItem = d.first_item as Record<string, unknown> | undefined;\n const amount = parseFloat(String(d.amount ?? \"0\")) || 0;\n\n return {\n ...d,\n id: d.id ?? d.token,\n shareableType: \"Order\",\n imageUrl: (firstItem?.image_url as string) || \"\",\n title: (firstItem?.title as string) || \"\",\n order_number: d.order_number ?? \"\",\n price: amount,\n display_price: (d.total_display_amount as string) ?? d.amount ?? \"\",\n status: (d.status as string) ?? \"unknown\",\n created_at: d.created_at,\n };\n });\n\n return { data: rows, columns };\n};\n\n/**\n * Subscriptions table transformer\n * Normalizes subscription list data into ShareableItem rows + column definitions.\n */\nconst toSubscriptionTableProps: DataTransformer = (data) => {\n const subscriptions = Array.isArray(data) ? data : [];\n\n const columns = [\n { key: \"imageUrl\", label: \"Image\", sortable: false },\n { key: \"title\", label: \"Product\", sortable: true },\n { key: \"price\", label: \"Price\", sortable: true },\n { key: \"status\", label: \"Status\", sortable: true },\n ];\n\n const rows = subscriptions.map((item: unknown) => {\n const d = item as Record<string, unknown>;\n const variant = d.variant as Record<string, unknown> | undefined;\n const product = variant?.product as Record<string, unknown> | undefined;\n\n return {\n ...d,\n id: d.id ?? d.subscription_token,\n shareableType: \"Subscription\",\n imageUrl: (product?.image_url as string) || \"\",\n title: (product?.title as string) || \"\",\n price: parseFloat(String(d.price ?? \"0\")) || 0,\n display_price:\n (product?.price_in_currency as string) ?? String(d.price ?? \"\"),\n status: (d.status as string) ?? \"unknown\",\n next_bill_date: d.next_bill_date,\n };\n });\n\n return { data: rows, columns };\n};\n\n/**\n * All widget transformers bundled for registration\n */\nexport const WIDGET_TRANSFORMERS: Record<string, DataTransformer> = {\n toShareableProps,\n toCarouselSlidesFromShareables,\n toImagePropsFromShareable,\n toVideoPropsFromShareable,\n toOrderTableProps,\n toSubscriptionTableProps,\n};\n","import type {\n DataSourceRegistry,\n DataFetcher,\n DataTransformer,\n DataSourceType,\n} from \"./types\";\nimport { apiFetcher } from \"./fetchers/api\";\nimport { customFetcher } from \"./fetchers/custom\";\nimport { staticFetcher } from \"./fetchers/static\";\nimport { WIDGET_TRANSFORMERS } from \"./transformers\";\n\nexport interface CreateDataSourceRegistryOptions {\n /** Custom fetchers to add or override */\n fetchers?: Partial<Record<DataSourceType, DataFetcher>>;\n /** Custom transform functions */\n transformers?: Record<string, DataTransformer>;\n}\n\n/**\n * Creates a data source registry with default fetchers.\n * Users can extend this with custom fetchers and transformers.\n */\nexport function createDataSourceRegistry(\n options?: CreateDataSourceRegistryOptions,\n): DataSourceRegistry {\n return {\n fetchers: {\n api: apiFetcher,\n custom: customFetcher,\n static: staticFetcher,\n ...options?.fetchers,\n } as Record<DataSourceType, DataFetcher>,\n transformers: {\n ...WIDGET_TRANSFORMERS,\n ...options?.transformers,\n },\n };\n}\n\n/** Default registry instance */\nexport const DEFAULT_DATA_SOURCE_REGISTRY: DataSourceRegistry =\n createDataSourceRegistry();\n","import type React from \"react\";\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { DataSourceRegistry } from \"@fluid-app/portal-core/data-sources/types\";\nimport { DEFAULT_DATA_SOURCE_REGISTRY } from \"@fluid-app/portal-core/data-sources/registry\";\nimport { DataSourceProvider } from \"./context\";\n\ninterface DataSourceRegistryContextValue {\n registry: DataSourceRegistry;\n baseUrl?: string | undefined;\n /** Get API headers function */\n getApiHeaders?: (() => Record<string, string>) | undefined;\n /** Dynamic variables for endpoint path substitution (e.g., { rep_id: \"123\" }) */\n variables?: Record<string, string> | undefined;\n}\n\nconst DataSourceRegistryContext = createContext<DataSourceRegistryContextValue>(\n {\n registry: DEFAULT_DATA_SOURCE_REGISTRY,\n },\n);\n\nexport interface DataSourceRegistryProviderProps {\n registry?: DataSourceRegistry | undefined;\n /** Base URL for API calls (e.g., \"https://api.fluid.app/api\") */\n baseUrl?: string | undefined;\n /**\n * Get API headers function\n */\n getApiHeaders?: (() => Record<string, string>) | undefined;\n /** Dynamic variables for endpoint path substitution (e.g., { rep_id: \"123\" }) */\n variables?: Record<string, string> | undefined;\n children: ReactNode;\n}\n\n/**\n * Provides data source registry and configuration to all descendants.\n * If no registry is provided, uses the default.\n * Also provides the shared DataSourceProvider from portal-core so that\n * portal-widgets hooks can access baseUrl and getApiHeaders.\n */\nexport function DataSourceRegistryProvider({\n registry,\n baseUrl,\n getApiHeaders,\n variables,\n children,\n}: DataSourceRegistryProviderProps): React.JSX.Element {\n const value = useMemo(\n () => ({\n registry: registry ?? DEFAULT_DATA_SOURCE_REGISTRY,\n baseUrl,\n getApiHeaders,\n variables,\n }),\n [registry, baseUrl, getApiHeaders, variables],\n );\n\n return (\n <DataSourceRegistryContext.Provider value={value}>\n <DataSourceProvider baseUrl={baseUrl} getApiHeaders={getApiHeaders}>\n {children}\n </DataSourceProvider>\n </DataSourceRegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the data source registry.\n */\nexport function useDataSourceRegistry(): DataSourceRegistry {\n return useContext(DataSourceRegistryContext).registry;\n}\n\n/**\n * Hook to access the full data source registry context (registry + config).\n */\nexport function useDataSourceRegistryConfig(): DataSourceRegistryContextValue {\n return useContext(DataSourceRegistryContext);\n}\n","import type { PointsData } from \"./use-points-ledger.types\";\n\nconst now = new Date();\n\nfunction daysAgo(days: number): string {\n return new Date(now.getTime() - days * 86_400_000).toISOString();\n}\n\nexport const PREVIEW_DATA: PointsData = {\n balance: 25,\n entries: [\n {\n id: 1,\n description: \"Purchase March 3rd\",\n amount: 20,\n createdAt: daysAgo(3),\n },\n {\n id: 2,\n description: \"Referral Bonus\",\n amount: 50,\n createdAt: daysAgo(14),\n },\n {\n id: 3,\n description: \"Points Redemption\",\n amount: -75,\n createdAt: daysAgo(60),\n },\n {\n id: 4,\n description: \"Welcome Reward\",\n amount: 30,\n createdAt: daysAgo(90),\n },\n ],\n};\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useDataSourceConfig } from \"@fluid-app/portal-react/data-sources/context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-points-ledger.preview\";\nimport type { PointsData, PointsEntry } from \"./use-points-ledger.types\";\n\nexport type { PointsData, PointsEntry } from \"./use-points-ledger.types\";\n\nexport const POINTS_LEDGER_QUERY_KEY = \"points-ledger\" as const;\n\ntype ApiPointsLedger = {\n id: number;\n amount: number;\n company_id: number;\n created_at: string;\n customer_id: number;\n metadata: {\n transaction_type?: string;\n source?: { name?: string; reason?: string };\n };\n total_balance: number;\n updated_at: string;\n};\n\ntype ApiResponse = {\n points_ledgers: ApiPointsLedger[];\n meta: {\n request_id: string;\n timestamp: string;\n pagination: {\n current_page: number;\n total_pages: number;\n total_count: number;\n per_page: number;\n };\n };\n};\n\nfunction formatDescription(ledger: ApiPointsLedger): string {\n if (ledger.metadata?.transaction_type) {\n return ledger.metadata.transaction_type\n .split(\"_\")\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n }\n if (ledger.metadata?.source?.reason) {\n return ledger.metadata.source.reason;\n }\n const date = new Date(ledger.created_at);\n return `Transaction ${date.toLocaleDateString(\"en-US\", { month: \"long\", day: \"numeric\" })}`;\n}\n\nfunction transformResponse(data: ApiResponse): PointsData {\n const ledgers = data.points_ledgers;\n\n // Sort newest-first to get current balance from the most recent entry\n const sorted = [...ledgers].sort(\n (a, b) =>\n new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),\n );\n const balance = sorted[0]?.total_balance ?? 0;\n\n const entries: PointsEntry[] = sorted.map((ledger) => ({\n id: ledger.id,\n description: formatDescription(ledger),\n amount: ledger.amount,\n createdAt: ledger.created_at,\n }));\n\n return { balance, entries };\n}\n\nexport function usePointsLedger(): UseQueryResult<PointsData, Error> {\n const { baseUrl, getApiHeaders } = useDataSourceConfig();\n const { isPreview } = useWidgetPreviewContext();\n const registryConfig = useDataSourceRegistryConfig();\n const customerId = registryConfig.variables?.customer_id;\n\n return useQuery({\n queryKey: [\n \"portal-widget-use\",\n POINTS_LEDGER_QUERY_KEY,\n isPreview ? \"preview\" : baseUrl,\n customerId,\n ] as const,\n queryFn: async ({ signal }): Promise<PointsData> => {\n const base = baseUrl ?? \"\";\n const url = `${base}/v202506/customers/${customerId}/points_ledgers?page=1&per_page=100&sort_by=created_at&sort_dir=desc`;\n const response = await fetch(url, {\n headers: {\n \"content-type\": \"application/json\",\n ...getApiHeaders?.(),\n },\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch points ledger: ${response.status}`);\n }\n\n const data: ApiResponse = await response.json();\n return transformResponse(data);\n },\n enabled: !isPreview && !!customerId,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n","import { useId, useState, type ComponentProps } from \"react\";\nimport type React from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n borderWidthClasses,\n borderColorClasses,\n} from \"../core/fields\";\nimport { usePointsLedger, type PointsEntry } from \"../hooks/use-points-ledger\";\nimport { ErrorState } from \"../components/error-state\";\nimport { ChevronDown, Coins } from \"lucide-react\";\n\nconst formatRelativeTime = (dateString: string): string => {\n const now = new Date();\n const date = new Date(dateString);\n const diffMs = now.getTime() - date.getTime();\n const diffDays = Math.floor(diffMs / 86_400_000);\n\n if (diffDays < 1) return \"Today\";\n if (diffDays === 1) return \"1 day ago\";\n if (diffDays < 7) return `${diffDays} days ago`;\n if (diffDays < 14) return \"1 week ago\";\n if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;\n if (diffDays < 60) return \"1 month ago\";\n return `${Math.floor(diffDays / 30)} months ago`;\n};\n\nconst formatBalance = (balance: number): string => {\n return balance.toLocaleString(\"en-US\");\n};\n\ntype PointsWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n title?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Balance\n balanceColor?: ColorOptions;\n\n // History\n historyEnabled?: boolean;\n historyTitle?: string;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n};\n\nfunction PointsEntryRow({\n entry,\n textColor,\n accentColor,\n}: {\n entry: PointsEntry;\n textColor: ColorOptions;\n accentColor: ColorOptions;\n}) {\n const sign = entry.amount >= 0 ? \"+\" : \"-\";\n return (\n <div className=\"flex w-full items-center gap-3\">\n <div className=\"min-w-0 flex-1\">\n <p className={`text-sm text-${textColor} truncate`}>\n {entry.description}\n </p>\n <p className={`text-[10px] text-${textColor} leading-tight opacity-70`}>\n {formatRelativeTime(entry.createdAt)}\n </p>\n </div>\n <div className={`bg-${accentColor}/20 shrink-0 rounded-md px-1.5`}>\n <span className={`text-xs font-medium text-${accentColor} opacity-70`}>\n {sign}\n {Math.abs(entry.amount).toLocaleString(\"en-US\")}pts\n </span>\n </div>\n </div>\n );\n}\n\nexport function PointsWidget({\n // Title\n titleEnabled = true,\n title = \"Points\",\n titleFontSize = \"md\",\n titleColor = \"foreground\",\n\n // Balance\n balanceColor = \"primary\",\n\n // History\n historyEnabled = true,\n historyTitle = \"History\",\n\n // Styling\n background = { type: \"solid\", color: \"background\" },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n\n className,\n ...props\n}: PointsWidgetProps): React.JSX.Element {\n const [historyOpen, setHistoryOpen] = useState(false);\n const historyPanelId = useId();\n\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n const { data, isLoading, isError } = usePointsLedger();\n\n return (\n <div\n className={`@container overflow-hidden rounded-${borderRadius} bg-${backgroundColor} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${className ?? \"\"}`}\n style={{ backgroundImage }}\n {...props}\n >\n <div className={`p-${padding} flex flex-col gap-2.5`}>\n {/* Header: Title + Balance */}\n {titleEnabled && (\n <h2\n className={`text-${titleFontSize} font-header font-bold text-${titleColor}`}\n >\n {title}\n </h2>\n )}\n {!isLoading && data && (\n <span\n className={`text-3xl font-semibold text-${balanceColor} font-header leading-snug`}\n >\n {formatBalance(data.balance)}\n </span>\n )}\n\n {/* Loading */}\n {isLoading ? (\n <div className=\"flex min-h-[60px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n <ErrorState />\n ) : historyEnabled && (!data || data.entries.length === 0) ? (\n /* Empty state — only shown when history is enabled but no entries */\n <div className=\"flex min-h-[60px] flex-col items-center justify-center gap-2\">\n <Coins className={`size-10 text-${textColor} opacity-30`} />\n <p className={`text-sm font-semibold text-${textColor} opacity-50`}>\n No Points Activity\n </p>\n </div>\n ) : historyEnabled && data && data.entries.length > 0 ? (\n /* History dropdown */\n <div className=\"flex flex-col gap-2\">\n {/* Dropdown toggle */}\n <button\n type=\"button\"\n aria-expanded={historyOpen}\n aria-controls={historyPanelId}\n onClick={() => setHistoryOpen((prev) => !prev)}\n className={`flex w-full items-center text-xs font-semibold text-${textColor} cursor-pointer`}\n >\n <span className=\"flex-1 text-left\">{historyTitle}</span>\n <ChevronDown\n className={`size-4 transition-transform duration-200 ${historyOpen ? \"rotate-180\" : \"\"}`}\n />\n </button>\n <div className={`bg-${textColor}/20 h-px w-full`} />\n\n {/* Collapsible entries */}\n <div\n id={historyPanelId}\n className={`flex flex-col gap-3 ${!historyOpen ? \"hidden\" : \"\"}`}\n >\n {data.entries.map((entry) => (\n <PointsEntryRow\n key={entry.id}\n entry={entry}\n textColor={textColor}\n accentColor={accentColor}\n />\n ))}\n </div>\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n\nexport const pointsWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"PointsWidget\",\n displayName: \"Points\",\n fields: [\n // Title group\n {\n key: \"titleEnabled\",\n label: \"Show Title\",\n type: \"boolean\",\n description: \"Toggle title visibility\",\n defaultValue: true,\n\n group: \"Title\",\n },\n {\n key: \"title\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the balance\",\n defaultValue: \"Points\",\n\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the title\",\n defaultValue: \"md\",\n\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the title\",\n defaultValue: \"foreground\",\n\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Balance group\n getColorField({\n key: \"balanceColor\",\n label: \"Balance Color\",\n description: \"Color for the points balance number\",\n defaultValue: \"primary\",\n\n group: \"Balance\",\n }),\n\n // History group\n {\n key: \"historyEnabled\",\n label: \"Show History\",\n type: \"boolean\",\n description: \"Show a collapsible history dropdown below the balance\",\n defaultValue: true,\n\n group: \"History\",\n },\n {\n key: \"historyTitle\",\n label: \"History Title\",\n type: \"text\",\n description: \"Title for the history dropdown\",\n defaultValue: \"History\",\n\n group: \"History\",\n requiresKeyToBeTrue: \"historyEnabled\",\n },\n\n // Design group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the widget\",\n defaultValue: \"background\",\n\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color\",\n defaultValue: \"foreground\",\n\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color for points badges and highlights\",\n defaultValue: \"primary\",\n\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Widget padding\",\n defaultValue: 4,\n\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Widget border radius\",\n defaultValue: \"md\",\n\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Widget border width\",\n defaultValue: \"none\",\n\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Widget border color\",\n defaultValue: \"muted\",\n\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ShopScreen-CDwCW5Go.cjs","names":["getProduct","listProducts","defaultRenderImage","CirclePlay","Card","ChevronLeft","ChevronRight","RadioGroup","RadioGroupItem","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Button","Skeleton","Search","Input","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Button","ArrowLeft","ShoppingCart","React","Button","ShoppingCart","useFluidContext","useSdkClient","useFluidAuth","useCurrentUser","useAppNavigation","Breadcrumb","BreadcrumbList","BreadcrumbItem","BreadcrumbPage"],"sources":["../../../products/api-client/src/utils/product-price.ts","../../../products/core/src/context.tsx","../../../products/core/src/query-keys.ts","../../../products/core/src/hooks/use-products.ts","../../../products/core/src/hooks/use-debounce.ts","../../../products/core/src/hooks/use-product-detail.ts","../../../products/core/src/hooks/use-product-catalog.ts","../../../products/core/src/utils/subscription-plans.ts","../../../products/core/src/stores/use-product-store.ts","../../../products/core/src/stores/use-draft-store.ts","../../../products/core/src/utils/product-helpers.ts","../../../shop/ui/src/utils/media-helpers.ts","../../../shop/ui/src/components/product-card.tsx","../../../shop/ui/src/components/image-gallery.tsx","../../../shop/ui/src/components/purchase-options.tsx","../../../shop/ui/src/components/quantity-selector.tsx","../../../shop/ui/src/components/shop-app.tsx","../../../cart/ui/src/components/cart-script.tsx","../../../cart/ui/src/components/cart-widget.tsx","../../../cart/ui/src/components/cart-button.tsx","../../../cart/ui/src/components/shop-container.tsx","../src/screens/ShopScreen.tsx"],"sourcesContent":["import type { products } from \"../custom/products\";\n\ntype ProductPriceInput = products.Product | products.ShopProduct;\n\nfunction stripParentheticalText(text: string | undefined): string | null {\n if (!text) return null;\n return text.replace(/\\s*\\([^)]*\\)/g, \"\").trim();\n}\n\nfunction isShopVariantCountry(\n vc: products.VariantCountry | products.ShopVariantCountry | undefined,\n): vc is products.ShopVariantCountry {\n return vc !== undefined && \"display_wholesale_subscription_price\" in vc;\n}\n\nfunction isAdminProduct(\n product: ProductPriceInput,\n): product is products.Product {\n return \"display_price\" in product;\n}\n\nfunction isVariantCountriesRecord(\n vc: unknown,\n): vc is Record<string, products.VariantCountry> {\n return vc !== null && typeof vc === \"object\" && !Array.isArray(vc);\n}\n\nexport function determineProductPrice(\n product: ProductPriceInput,\n countryIso: string,\n): { repPrice: string | null | undefined; price?: string | null } {\n const { variants } = product;\n\n // Get the first active variant for the country, or fall back to first variant\n const selectedVariant =\n variants?.find((v) => {\n if (isVariantCountriesRecord(v.variant_countries)) {\n return v.variant_countries[countryIso]?.active;\n }\n return false;\n }) ||\n variants?.[0] ||\n null;\n\n let variantCountry:\n | products.VariantCountry\n | products.ShopVariantCountry\n | undefined;\n if (countryIso && selectedVariant?.variant_countries) {\n const variantCountries = selectedVariant.variant_countries;\n\n if (Array.isArray(variantCountries)) {\n variantCountry = variantCountries.find(\n (v: products.ShopVariantCountry) => v?.country?.iso === countryIso,\n );\n } else if (isVariantCountriesRecord(variantCountries)) {\n variantCountry = variantCountries[countryIso];\n }\n }\n\n if (selectedVariant?.subscription_only)\n return {\n repPrice: isShopVariantCountry(variantCountry)\n ? variantCountry.display_wholesale_subscription_price\n : undefined,\n };\n\n const price = isShopVariantCountry(variantCountry)\n ? variantCountry.display_price\n : isAdminProduct(product)\n ? product.display_price\n : undefined;\n\n const repPrice = isShopVariantCountry(variantCountry)\n ? variantCountry.display_wholesale\n : undefined;\n return {\n repPrice: stripParentheticalText(repPrice),\n price: price === repPrice ? null : stripParentheticalText(price),\n };\n}\n\nexport function extractPriceFromString(priceString: string): number | null {\n if (!priceString) return null;\n const strippedString = priceString.replace(/[^\\d.]/g, \"\");\n return parseFloat(strippedString);\n}\n","import { createContext, useContext, type JSX, type ReactNode } from \"react\";\nimport type { FetchClient } from \"@fluid-app/products-api-client\";\n\ninterface ProductsCoreConfig {\n client: FetchClient;\n}\n\nconst ProductsCoreContext = createContext<ProductsCoreConfig | null>(null);\n\nexport function ProductsCoreProvider({\n client,\n children,\n}: ProductsCoreConfig & { children: ReactNode }): JSX.Element {\n return (\n <ProductsCoreContext.Provider value={{ client }}>\n {children}\n </ProductsCoreContext.Provider>\n );\n}\n\nexport function useProductsClient(): FetchClient {\n const ctx = useContext(ProductsCoreContext);\n if (!ctx) {\n throw new Error(\n \"useProductsClient must be used within a <ProductsCoreProvider>\",\n );\n }\n return ctx.client;\n}\n","import type { products } from \"@fluid-app/products-api-client\";\n\nexport const productKeys = {\n products: {\n all: [\"products\"] as const,\n list: (params?: products.ListProductsParams) =>\n [...productKeys.products.all, \"list\", params] as const,\n listV2: (params?: products.ListProductsV2Params) =>\n [...productKeys.products.all, \"listV2\", params] as const,\n detail: (\n id: number | string,\n options?: { lang?: string; country_code?: string },\n ) =>\n [\n ...productKeys.products.all,\n \"detail\",\n String(id),\n options?.lang,\n options?.country_code,\n ] as const,\n search: (query: string, countryCodes: string[]) =>\n [...productKeys.products.all, \"search\", query, countryCodes] as const,\n },\n variants: {\n all: [\"variants\"] as const,\n detail: (id: number) =>\n [...productKeys.variants.all, \"detail\", id] as const,\n },\n} as const;\n","import { useQuery } from \"@tanstack/react-query\";\nimport {\n listProducts,\n listProductsV2,\n getProduct,\n type products,\n} from \"@fluid-app/products-api-client\";\nimport { useProductsClient } from \"../context\";\nimport { productKeys } from \"../query-keys\";\n\nexport function useGetProducts(params?: products.ListProductsParams) {\n const client = useProductsClient();\n return useQuery({\n queryKey: productKeys.products.list(params),\n queryFn: () => listProducts(client, params),\n });\n}\n\nexport function useGetProductsV2(params?: products.ListProductsV2Params) {\n const client = useProductsClient();\n return useQuery({\n queryKey: productKeys.products.listV2(params),\n queryFn: () => listProductsV2(client, params),\n });\n}\n\nexport function useGetProduct(\n id: string | number,\n options?: {\n lang?: string;\n country_code?: string;\n enabled?: boolean;\n },\n) {\n const client = useProductsClient();\n return useQuery({\n queryKey: productKeys.products.detail(id, {\n lang: options?.lang,\n country_code: options?.country_code,\n }),\n queryFn: () =>\n getProduct(client, id, {\n lang: options?.lang,\n country_code: options?.country_code,\n }),\n enabled: options?.enabled,\n });\n}\n","import { useState, useEffect } from \"react\";\n\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(timer);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n","import { useState, useMemo, useCallback } from \"react\";\nimport type { products } from \"@fluid-app/products-api-client\";\nimport { useGetProduct } from \"./use-products\";\n\ntype SelectedOptionsState = Record<string, string>;\n\ninterface ProductOption {\n id: string;\n name: string;\n values: string[];\n index: number;\n}\n\nexport interface UseProductDetailParams {\n productId: string;\n countryCode?: string;\n lang?: string;\n}\n\nexport function useProductDetail({\n productId,\n countryCode,\n lang = \"en\",\n}: UseProductDetailParams) {\n const [userSelectedSubscribe, setUserSelectedSubscribe] = useState(false);\n const [quantity, setQuantity] = useState(1);\n const [selectedOptions, setSelectedOptions] = useState<SelectedOptionsState>(\n {},\n );\n const [selectedSubscriptionPlan, setSelectedSubscriptionPlan] = useState<\n products.ProductSubscriptionPlan | undefined\n >(undefined);\n\n const {\n data: productResponse,\n isLoading,\n error,\n } = useGetProduct(productId, {\n lang,\n country_code: countryCode,\n });\n\n const product = productResponse?.product;\n\n // Initialize subscription plan when product loads\n const resolvedSubscriptionPlan = useMemo(():\n | products.ProductSubscriptionPlan\n | undefined => {\n if (selectedSubscriptionPlan) return selectedSubscriptionPlan;\n return product?.product_subscription_plans?.find((plan) => plan.default);\n }, [selectedSubscriptionPlan, product?.product_subscription_plans]);\n\n // Extract options from product structure (option_attrs + variants)\n const productOptions: ProductOption[] = useMemo(() => {\n if (!product?.option_attrs || !product?.variants) return [];\n\n return product.option_attrs.map((optionName: string, index: number) => {\n const uniqueValues = new Set<string>();\n\n product.variants?.forEach((variant) => {\n if (variant.option_attrs && variant.option_attrs[index]) {\n uniqueValues.add(variant.option_attrs[index]);\n }\n });\n\n return {\n id: `option-${index}`,\n name: optionName,\n values: Array.from(uniqueValues),\n index,\n };\n });\n }, [product]);\n\n // Find the first active variant for the user's country, or fall back to first variant\n const findFirstActiveVariant = useCallback(\n (variants: readonly products.Variant[]) =>\n (countryCode &&\n variants.find((v) => v.variant_countries?.[countryCode]?.active)) ||\n variants[0],\n [countryCode],\n );\n\n // Find variant based on selected option values\n const findMatchingVariant = useCallback(\n (opts: SelectedOptionsState) => {\n if (!product?.variants || product.variants.length === 0) return null;\n\n // If no options selected, return first active variant\n if (Object.keys(opts).length === 0) {\n return findFirstActiveVariant(product.variants);\n }\n\n // Find variant with matching option_attrs\n const matchingVariant = product.variants.find((variant) => {\n if (!variant.option_attrs || !product.option_attrs) return false;\n\n return product.option_attrs.every((_: string, index: number) => {\n const optionKey = `option-${index}`;\n const selectedValue = opts[optionKey];\n if (!selectedValue) return false;\n return variant?.option_attrs?.[index] === selectedValue;\n });\n });\n\n return matchingVariant || findFirstActiveVariant(product.variants);\n },\n [product, findFirstActiveVariant],\n );\n\n // Find default variant (first active variant, or fallback to first variant)\n const defaultVariant = useMemo(\n () => (product?.variants ? findFirstActiveVariant(product.variants) : null),\n [product, findFirstActiveVariant],\n );\n\n // Compute effective selected options (user selections + defaults)\n const effectiveSelectedOptions = useMemo(() => {\n const defaultOptions: SelectedOptionsState = {};\n if (defaultVariant && product?.option_attrs) {\n product.option_attrs.forEach((_: string, index: number) => {\n if (defaultVariant.option_attrs?.[index]) {\n defaultOptions[`option-${index}`] =\n defaultVariant.option_attrs[index];\n }\n });\n }\n\n return {\n ...defaultOptions,\n ...selectedOptions,\n };\n }, [selectedOptions, defaultVariant, product?.option_attrs]);\n\n // Get current selected variant\n const currentVariant = useMemo(() => {\n const matchingVariant = findMatchingVariant(effectiveSelectedOptions);\n return matchingVariant || defaultVariant;\n }, [effectiveSelectedOptions, findMatchingVariant, defaultVariant]);\n\n // Derive isSubscribe from currentVariant and user preference\n const isSubscribe =\n currentVariant?.subscription_only || userSelectedSubscribe;\n\n // Variant display name (display_name takes precedence over title)\n const variantDisplayName =\n currentVariant?.display_name || currentVariant?.title || null;\n\n // Get current variant's country data (for the user's country)\n const currentVariantCountry = useMemo(() => {\n if (!currentVariant?.variant_countries) return null;\n\n const variantCountries = currentVariant.variant_countries;\n const countryEntries = Object.entries(variantCountries);\n\n if (countryCode && variantCountries[countryCode]) {\n return variantCountries[countryCode];\n }\n\n return countryEntries[0]?.[1] || null;\n }, [currentVariant?.variant_countries, countryCode]);\n\n // Price display\n const displayPrice =\n currentVariantCountry?.display_price ||\n product?.display_price ||\n `$${product?.price}`;\n const displayWholesalePrice =\n currentVariantCountry?.display_wholesale ||\n product?.tax_inclusive_wholesale ||\n `$${product?.price}`;\n const displayWholesaleSubscriptionPrice =\n currentVariantCountry?.display_wholesale_subscription_price ||\n product?.tax_inclusive_wholesale_subscription_price ||\n `$${product?.subscription_price}`;\n\n // CV/QV display\n const wholesaleCv = currentVariantCountry?.cv ?? 0;\n const wholesaleQv = currentVariantCountry?.qv ?? 0;\n\n // Purchase options\n const showBuyOnce = !currentVariant?.subscription_only;\n const showSubscribe = currentVariant?.allow_subscription || false;\n\n const handleOptionChange = useCallback((optionKey: string, value: string) => {\n setSelectedOptions((prev) => ({\n ...prev,\n [optionKey]: value,\n }));\n }, []);\n\n return {\n product,\n isLoading,\n error,\n currentVariant,\n productOptions,\n selectedOptions,\n effectiveSelectedOptions,\n handleOptionChange,\n quantity,\n setQuantity,\n isSubscribe,\n userSelectedSubscribe,\n setUserSelectedSubscribe,\n showBuyOnce,\n showSubscribe,\n selectedSubscriptionPlan: resolvedSubscriptionPlan,\n setSelectedSubscriptionPlan,\n displayPrice,\n displayWholesalePrice,\n displayWholesaleSubscriptionPrice,\n wholesaleCv,\n wholesaleQv,\n currentVariantCountry,\n variantDisplayName,\n };\n}\n","import { useState, useMemo, useCallback } from \"react\";\nimport { listProducts, type products } from \"@fluid-app/products-api-client\";\nimport { useProductsClient } from \"../context\";\nimport { useDebounce } from \"./use-debounce\";\n\nexport interface UseProductCatalogParams {\n initialCountryCode?: string | null;\n perPage?: number;\n publishedStores?: readonly string[];\n statusFilter?: readonly products.ProductStatusFilter[];\n}\n\nexport function useProductCatalog({\n initialCountryCode = null,\n perPage = 25,\n publishedStores = [\"rep\"],\n statusFilter = [\"active\"],\n}: UseProductCatalogParams = {}) {\n const client = useProductsClient();\n const [searchTerm, setSearchTerm] = useState(\"\");\n const debouncedSearchTerm = useDebounce(searchTerm, 300);\n const [countryCode, setCountryCode] = useState<string | null>(\n initialCountryCode,\n );\n const [currentSort, setCurrentSort] = useState<{ desc: boolean; id: string }>(\n {\n desc: false,\n id: \"title\",\n },\n );\n\n const queryParams = useMemo(\n () => ({\n per_page: perPage,\n status: [...statusFilter],\n country_code: countryCode ? [countryCode] : [],\n published_stores: [...publishedStores],\n }),\n [countryCode, perPage, statusFilter, publishedStores],\n );\n\n const fetchProducts = useCallback(\n async (page: number): Promise<products.Product[]> => {\n const response = await listProducts(client, {\n ...queryParams,\n page,\n search_query: debouncedSearchTerm || \"\",\n sorting: currentSort,\n });\n return response.products;\n },\n [client, queryParams, debouncedSearchTerm, currentSort],\n );\n\n const queryKey = useMemo(\n () => [\n \"product-catalog\",\n countryCode ?? \"\",\n `${currentSort.id}-${currentSort.desc ? \"desc\" : \"asc\"}`,\n debouncedSearchTerm || \"\",\n ],\n [countryCode, currentSort, debouncedSearchTerm],\n );\n\n return {\n searchTerm,\n setSearchTerm,\n debouncedSearchTerm,\n currentSort,\n setCurrentSort,\n countryCode,\n setCountryCode,\n fetchProducts,\n queryKey,\n };\n}\n","import type { products } from \"@fluid-app/products-api-client\";\n\nexport function ensureDefaultSubscriptionPlan(\n plans: readonly products.ProductSubscriptionPlan[],\n): products.ProductSubscriptionPlan[] {\n const activePlans = plans.filter((plan) => plan.active !== false);\n\n if (activePlans.length === 0) {\n return plans.map((plan) => ({ ...plan, default: false }));\n }\n\n const hasActiveDefault = activePlans.some((plan) => plan.default === true);\n\n if (!hasActiveDefault) {\n const planWithLowestId = activePlans.reduce((lowest, current) => {\n const lowestId = lowest.subscription_plan?.id || Infinity;\n const currentId = current.subscription_plan?.id || Infinity;\n return currentId < lowestId ? current : lowest;\n });\n\n return plans.map((plan) => ({\n ...plan,\n default:\n plan.subscription_plan?.id === planWithLowestId.subscription_plan?.id &&\n plan.active !== false,\n }));\n }\n\n return plans.map((plan) => ({\n ...plan,\n default: plan.active !== false ? plan.default : false,\n }));\n}\n\nexport function plansToAttributes(\n plans: readonly products.ProductSubscriptionPlan[],\n): products.ProductSubscriptionPlanAttribute[] {\n return plans.map((plan) => ({\n id: plan.id ?? undefined,\n subscription_plan_id: plan.subscription_plan.id,\n default: plan.default || false,\n active: plan.active !== false,\n }));\n}\n","import { create, type StateCreator } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport type { products } from \"@fluid-app/products-api-client\";\nimport {\n ensureDefaultSubscriptionPlan,\n plansToAttributes,\n} from \"../utils/subscription-plans\";\n\n// Default state\nconst defaultState = {\n id: undefined,\n title: \"\",\n description: \"\",\n introduction: \"\",\n stripped: \"\",\n feature_text: \"\",\n sku: \"\",\n slug: \"\",\n canonical_url: null as string | null,\n image_url: \"\",\n image_path: undefined,\n status: \"draft\",\n publish_at: null,\n price: \"0\",\n commission: 0,\n public: true,\n no_index: false,\n show_reviews: true,\n publish_to_retail_store: true,\n publish_to_rep_store: true,\n publish_to_share_tab: true,\n collection_ids: [],\n tag_ids: [],\n images_attributes: [],\n product_subscription_plans_attributes: [],\n product_subscription_plans: [],\n variants_attributes: [],\n bundle: false,\n track_inventory_on_bundle_items: false,\n product_bundles_attributes: [],\n option_attrs: [],\n options: [],\n metafields_attributes: [],\n metadata: {},\n search_engine_optimizer_attributes: {\n title: \"\",\n description: \"\",\n image_url: \"\",\n image_path: \"\",\n block_crawler: false,\n },\n};\n\ntype ValidationErrors = Partial<Record<string, string>>;\n\nexport type TranslationData = {\n title?: string;\n introduction?: string;\n description?: string;\n feature_text?: string;\n};\n\ntype LanguageTranslations = Record<string, TranslationData>;\ntype TranslationLoadingState = Record<string, boolean>;\n\ninterface UpdateFieldOptions {\n shouldValidate?: boolean;\n shouldClearError?: boolean;\n markDirty?: boolean;\n}\n\ntype ArrayItemType<T> = T extends (infer U)[] ? U : unknown;\n\ntype ProductStoreFields = products.UpdateProduct & {\n product_subscription_plans: products.ProductSubscriptionPlan[];\n track_inventory_on_bundle_items: boolean;\n canonical_url: string | null;\n publish_at: string | null;\n};\n\nexport type ProductStoreState = ProductStoreFields & {\n errors: ValidationErrors;\n isValid: boolean;\n isDirty: boolean;\n translations: LanguageTranslations;\n editedTranslations: LanguageTranslations;\n translationErrors: Record<string, Record<string, string | undefined>>;\n translationLoading: TranslationLoadingState;\n translationsFetched: boolean;\n\n setProduct: (productData: products.Product) => void;\n updateField: <K extends keyof ProductStoreFields>(\n key: K,\n value: ProductStoreFields[K],\n options?: UpdateFieldOptions,\n ) => void;\n updatePartial: (updates: Partial<ProductStoreFields>) => void;\n updateSlug: (slug: string, isManual?: boolean) => void;\n updateSEO: (seo: {\n title?: string;\n description?: string;\n image_url?: string;\n image_path?: string;\n block_crawler?: boolean;\n }) => void;\n updateArrayItem: <K extends keyof ProductStoreFields>(\n arrayKey: K,\n itemId: number | string,\n updatedItem: ArrayItemType<ProductStoreFields[K]>,\n idField?: string,\n ) => void;\n reset: () => void;\n validateField: (field: string) => void;\n validateRequired: () => boolean;\n clearErrors: () => void;\n clearFieldError: (field: string) => void;\n markClean: () => void;\n\n setTranslationLoading: (languageIso: string, loading: boolean) => void;\n setTranslationData: (languageIso: string, data: TranslationData) => void;\n updateTranslationField: (\n languageIso: string,\n field: keyof TranslationData,\n value: string,\n ) => void;\n getOriginalTranslation: (\n languageIso: string,\n field: keyof TranslationData,\n ) => string | undefined;\n getEditedTranslation: (\n languageIso: string,\n field: keyof TranslationData,\n ) => string | undefined;\n setTranslationError: (\n languageIso: string,\n field: keyof TranslationData,\n error?: string,\n ) => void;\n getTranslationError: (\n languageIso: string,\n field: keyof TranslationData,\n ) => string | undefined;\n getTranslation: (\n languageIso: string,\n field: keyof TranslationData,\n ) => string | undefined;\n isTranslationLoading: (languageIso: string) => boolean;\n resetTranslations: () => void;\n setTranslationsFetched: (fetched: boolean) => void;\n};\n\nfunction syncSubscriptionPlans(plans: products.ProductSubscriptionPlan[]): {\n product_subscription_plans: products.ProductSubscriptionPlan[];\n product_subscription_plans_attributes: products.ProductSubscriptionPlanAttribute[];\n} {\n const activePlans = plans.filter((plan) => plan.active !== false);\n const plansWithDefault =\n activePlans.length > 0 ? ensureDefaultSubscriptionPlan(plans) : plans;\n return {\n product_subscription_plans: plansWithDefault,\n product_subscription_plans_attributes: plansToAttributes(plansWithDefault),\n };\n}\n\nconst extractFilenameFromUrl = (imageUrl: string): string => {\n try {\n const url = new URL(imageUrl);\n return url.pathname.split(\"/\").pop() || \"\";\n } catch {\n return imageUrl.split(\"/\").pop()?.split(\"?\")[0] || \"\";\n }\n};\n\nconst createProductStore: StateCreator<ProductStoreState> = (set, get) => ({\n ...defaultState,\n track_inventory_on_bundle_items: false,\n translations: {},\n editedTranslations: {},\n translationErrors: {},\n translationLoading: {},\n translationsFetched: false,\n errors: {},\n isValid: false,\n isDirty: false,\n\n setProduct: (productData: products.Product) => {\n const transformedData: ProductStoreFields = {\n id: productData.id || undefined,\n title: productData.title || \"\",\n description: productData.description || \"\",\n introduction: productData.introduction || \"\",\n stripped: productData.stripped || \"\",\n feature_text: productData.feature_text || \"\",\n sku: productData.sku || \"\",\n slug: productData.slug || \"\",\n canonical_url: productData.canonical_url || null,\n custom_slug: productData.custom_slug || false,\n image_url: productData.image_url || \"\",\n image_path:\n productData.image_path && !productData.image_path.includes(\"undefined\")\n ? productData.image_path\n : productData.image_url\n ? extractFilenameFromUrl(productData.image_url)\n : undefined,\n status: productData.status || \"draft\",\n publish_at: productData.publish_at || null,\n commission:\n typeof productData.commission === \"string\"\n ? parseFloat(productData.commission)\n : productData.commission || 0,\n public: productData.public ?? true,\n no_index: productData.no_index ?? false,\n show_reviews: productData.show_reviews ?? true,\n publish_to_retail_store: productData.publish_to_retail_store,\n publish_to_rep_store: productData.publish_to_rep_store,\n publish_to_share_tab: productData.publish_to_share_tab,\n ...(productData.tax_category_id && {\n tax_category_id: productData.tax_category_id,\n }),\n international_tax_type: productData.international_tax_type || undefined,\n category_id: productData.category_id\n ? parseInt(productData.category_id)\n : productData.category?.id\n ? productData.category.id\n : undefined,\n application_theme_template_id:\n productData.application_theme_template_id || undefined,\n collection_ids: (\n productData.collections as Array<{ id: number }> | undefined\n )?.map((collection) => collection.id),\n tag_ids: Array.isArray(productData.tags)\n ? (productData.tags as Array<{ id: number } | number>)\n .map((tag) => (typeof tag === \"number\" ? tag : tag?.id))\n .filter(Boolean)\n : [],\n search_engine_optimizer_attributes: {\n id: productData.search_engine_optimizer?.id,\n title:\n productData.search_engine_optimizer?.title ||\n productData.title ||\n undefined,\n description: productData.search_engine_optimizer?.description || \"\",\n image_url:\n productData.search_engine_optimizer?.image_url ||\n productData.image_url ||\n \"\",\n image_path:\n productData.search_engine_optimizer?.image_path ||\n productData.image_path ||\n \"\",\n block_crawler:\n productData.search_engine_optimizer?.block_crawler ?? false,\n },\n images_attributes:\n (productData.images as products.ImageAttribute[] | undefined)?.map(\n (img) => ({\n id: img.id,\n position: img.position || 0,\n image_url: img.image_url,\n image_path:\n img.image_path && !img.image_path.includes(\"undefined\")\n ? img.image_path\n : img.image_url\n ? extractFilenameFromUrl(img.image_url)\n : undefined,\n _destroy: false,\n }),\n ) || [],\n ...(productData.images &&\n productData.images.length > 0 && {\n image_path: (() => {\n if (\n productData.image_path &&\n typeof productData.image_path === \"string\" &&\n !productData.image_path.includes(\"undefined\")\n ) {\n return productData.image_path;\n }\n const firstImage = (\n productData.images as products.ImageAttribute[]\n ).find((img) => img.position === 0);\n if (\n firstImage?.image_path &&\n typeof firstImage.image_path === \"string\" &&\n !firstImage.image_path.includes(\"undefined\")\n ) {\n return firstImage.image_path;\n }\n return productData.image_url\n ? extractFilenameFromUrl(productData.image_url)\n : undefined;\n })(),\n }),\n product_subscription_plans: (() => {\n const plans =\n productData?.product_subscription_plans?.map(\n (plan: products.ProductSubscriptionPlan) => ({\n ...plan,\n active: plan?.active,\n }),\n ) || [];\n return plans.length > 0 ? ensureDefaultSubscriptionPlan(plans) : plans;\n })(),\n product_subscription_plans_attributes: (() => {\n const plans =\n productData?.product_subscription_plans?.map(\n (plan: products.ProductSubscriptionPlan) => ({\n ...plan,\n active: plan?.active,\n }),\n ) || [];\n const finalPlans =\n plans.length > 0 ? ensureDefaultSubscriptionPlan(plans) : plans;\n return plansToAttributes(finalPlans);\n })(),\n variants_attributes: productData?.variants\n ?.filter(\n (\n variant: products.Variant,\n ): variant is products.Variant & { id: number } =>\n variant.id !== null && variant.id !== undefined,\n )\n .map((variant) => ({\n id: variant.id,\n title: variant.title ?? productData?.title ?? \"Untitled Variant\",\n option_attrs: variant.option_attrs || [],\n sku: variant.sku || undefined,\n price: variant.price,\n track_quantity: variant.track_quantity ?? false,\n keep_selling: variant.keep_selling ?? false,\n bar_code: variant.bar_code ?? \"\",\n limit_subscription: variant.limit_subscription,\n subscription_max_qty: variant.subscription_max_qty ?? 0,\n customer_limit: variant.customer_limit ?? 0,\n is_master: variant.is_master,\n _destroy: false,\n images_attributes: variant?.images?.map(\n (image: { id?: number; position: number; image_url: string }) => ({\n id: image.id,\n position: image.position || 0,\n image_url: image.image_url,\n _destroy: false,\n }),\n ),\n inventory_levels_attributes: variant?.inventory_levels\n ?.filter(\n (level: products.InventoryLevel) =>\n (level.warehouse_id ?? level.warehouse?.id) != null,\n )\n .map((level: products.InventoryLevel) => ({\n id: level.id,\n available: level.available,\n committed: level.committed,\n on_hand: level.on_hand,\n unavailable: level.unavailable,\n warehouse_id: level.warehouse?.id || 0,\n _destroy: false,\n })),\n variant_countries_attributes: variant?.variant_countries\n ? Object.entries(\n variant.variant_countries as Record<\n string,\n products.VariantCountry\n >,\n ).map(([iso, country]) => ({\n id: country.id ?? 0,\n active: country.active ?? true,\n country_id: country.country_id,\n country_name: country.country_name ?? \"\",\n country_iso: iso,\n price: Number(country.price) || 0,\n subscription_price: Number(country.subscription_price) || 0,\n wholesale: Number(country.wholesale) || 0,\n wholesale_subscription_price:\n Number(country.wholesale_subscription_price) || 0,\n compare_price: Number(country.compare_price) || 0,\n cv: Number(country.cv) || 0,\n qv: Number(country.qv) || 0,\n pc_cv: Number(country.pc_cv) || 0,\n pc_qv: Number(country.pc_qv) || 0,\n cost_of_goods_sold: Number(country.cost_of_goods_sold) || 0,\n currency_code: country.currency_code || null,\n shipping: Number(country.shipping) || 0,\n points: country.points,\n }))\n : [],\n })),\n bundle:\n productData.product_bundles && productData.product_bundles.length > 0,\n track_inventory_on_bundle_items:\n productData.track_inventory_on_bundle_items ?? false,\n product_bundles_attributes: (productData.product_bundles || []).map(\n (bundle: products.ProductBundle) => ({\n id: bundle.id,\n bundled_variant_id: bundle.bundled_variant?.id || 0,\n bundled_variant: {\n title: bundle.bundled_variant?.title || \"\",\n sku: bundle.bundled_variant?.sku || null,\n price: String(bundle.bundled_variant?.price || \"0\"),\n price_in_currency: bundle.bundled_variant?.price_in_currency || \"\",\n currency_code: bundle.bundled_variant?.currency_code,\n product: {\n id: bundle.bundled_variant?.product.id || 0,\n title: bundle.bundled_variant?.product.title || \"\",\n image_url: bundle.bundled_variant?.product.image_url || \"\",\n price: bundle.bundled_variant?.product.price || \"0\",\n price_in_currency:\n bundle.bundled_variant?.product.price_in_currency || \"\",\n cv: bundle.bundled_variant?.product.cv || 0,\n qv: bundle.bundled_variant?.product.qv || 0,\n },\n },\n cv: bundle.cv || 0,\n qv: bundle.qv || 0,\n quantity: bundle.quantity,\n display_externally: bundle.display_externally ?? true,\n _destroy: false,\n }),\n ),\n option_attrs: productData.option_attrs || [],\n options: productData.options || [],\n metafields_attributes: (productData.metafields || []).map(\n (metafield: products.Metafield) => ({\n id: metafield.id,\n namespace: metafield.namespace,\n key: metafield.key,\n value: metafield.value,\n value_type: metafield.value_type,\n _destroy: false,\n }),\n ),\n metadata: productData.metadata || {},\n };\n\n set({\n ...transformedData,\n errors: {},\n isValid: false,\n isDirty: false,\n });\n },\n\n updateSlug: (slug, isManual = true) => {\n set((state: ProductStoreState) => ({\n slug,\n custom_slug: isManual,\n search_engine_optimizer_attributes: {\n ...state.search_engine_optimizer_attributes,\n title: state.search_engine_optimizer_attributes?.title || \"\",\n description:\n state.search_engine_optimizer_attributes?.description || \"\",\n image_url: state.search_engine_optimizer_attributes?.image_url || \"\",\n image_path: state.search_engine_optimizer_attributes?.image_path || \"\",\n block_crawler:\n state.search_engine_optimizer_attributes?.block_crawler ?? false,\n },\n isDirty: true,\n }));\n },\n\n updateSEO: (seo) => {\n set((state: ProductStoreState) => ({\n search_engine_optimizer_attributes: {\n ...state.search_engine_optimizer_attributes,\n title:\n seo.title !== undefined\n ? seo.title\n : state.search_engine_optimizer_attributes?.title || \"\",\n description:\n seo.description !== undefined\n ? seo.description\n : state.search_engine_optimizer_attributes?.description || \"\",\n image_url:\n seo.image_url !== undefined\n ? seo.image_url\n : state.search_engine_optimizer_attributes?.image_url || \"\",\n image_path:\n seo.image_path !== undefined\n ? seo.image_path\n : state.search_engine_optimizer_attributes?.image_path || \"\",\n block_crawler:\n seo.block_crawler !== undefined\n ? seo.block_crawler\n : (state.search_engine_optimizer_attributes?.block_crawler ??\n false),\n },\n isDirty: true,\n }));\n },\n\n updateField: <K extends keyof ProductStoreFields>(\n key: K,\n value: ProductStoreFields[K],\n options?: UpdateFieldOptions,\n ) => {\n const {\n shouldValidate = false,\n shouldClearError = true,\n markDirty = true,\n } = options || {};\n\n set((state: ProductStoreState) => {\n if (key === \"product_subscription_plans\") {\n return {\n ...state,\n ...syncSubscriptionPlans(value as products.ProductSubscriptionPlan[]),\n errors: shouldClearError\n ? { ...state.errors, [key]: undefined }\n : state.errors,\n isDirty: markDirty ? true : state.isDirty,\n };\n }\n\n return {\n ...state,\n [key]: value,\n errors: shouldClearError\n ? { ...state.errors, [key]: undefined }\n : state.errors,\n isDirty: markDirty ? true : state.isDirty,\n };\n });\n\n if (shouldValidate) {\n get().validateField(key as string);\n }\n },\n\n updatePartial: (updates: Partial<ProductStoreFields>) => {\n set((state: ProductStoreState) => {\n if (updates.product_subscription_plans) {\n return {\n ...state,\n ...updates,\n ...syncSubscriptionPlans(updates.product_subscription_plans),\n errors: {\n ...state.errors,\n ...Object.keys(updates).reduce(\n (acc, key) => {\n acc[key] = undefined;\n return acc;\n },\n {} as Record<string, undefined>,\n ),\n },\n isDirty: true,\n };\n }\n\n return {\n ...state,\n ...updates,\n errors: {\n ...state.errors,\n ...Object.keys(updates).reduce(\n (acc, key) => {\n acc[key] = undefined;\n return acc;\n },\n {} as Record<string, undefined>,\n ),\n },\n isDirty: true,\n };\n });\n },\n\n updateArrayItem: <K extends keyof ProductStoreFields>(\n arrayKey: K,\n itemId: number | string,\n updatedItem: ArrayItemType<ProductStoreFields[K]>,\n idField: string = \"id\",\n ) => {\n set((state: ProductStoreState) => {\n const currentArray = Array.isArray(state[arrayKey])\n ? (state[arrayKey] as unknown[])\n : [];\n\n const updatedArray = currentArray.map((item) => {\n if (\n typeof item === \"object\" &&\n item !== null &&\n idField in item &&\n (item as Record<string, unknown>)[idField] === itemId\n ) {\n return updatedItem;\n }\n return item;\n });\n\n if (arrayKey === \"product_subscription_plans\") {\n return {\n ...state,\n ...syncSubscriptionPlans(\n updatedArray as products.ProductSubscriptionPlan[],\n ),\n errors: { ...state.errors, [arrayKey]: undefined },\n isDirty: true,\n };\n }\n\n return {\n ...state,\n [arrayKey]: updatedArray,\n errors: { ...state.errors, [arrayKey]: undefined },\n isDirty: true,\n };\n });\n },\n\n reset: () => {\n set({\n ...defaultState,\n track_inventory_on_bundle_items: false,\n custom_slug: false,\n errors: {},\n isValid: false,\n isDirty: false,\n });\n },\n\n // Validation methods — these provide basic field-level validation.\n // For full schema validation (e.g. Zod), the consumer should call\n // their own validation function against the store state.\n validateField: (field: string) => {\n const fieldValue = get()[field as keyof ProductStoreState];\n const hasValue =\n fieldValue !== undefined && fieldValue !== null && fieldValue !== \"\";\n\n set((state: ProductStoreState) => ({\n errors: {\n ...state.errors,\n [field]: hasValue ? undefined : `${field} is required`,\n },\n isValid:\n hasValue &&\n Object.keys(state.errors).every(\n (key) => key === field || !state.errors[key],\n ),\n }));\n },\n\n validateRequired: () => {\n // Basic validation: check that title exists\n const state = get();\n const errors: ValidationErrors = {};\n\n if (!state.title) {\n errors.title = \"Title is required\";\n }\n\n if (Object.keys(errors).length > 0) {\n set({ errors, isValid: false });\n return false;\n }\n\n set({ errors: {}, isValid: true });\n return true;\n },\n\n clearErrors: () => {\n set({ errors: {}, isValid: false });\n },\n\n clearFieldError: (field: string) => {\n set((state: ProductStoreState) => ({\n errors: { ...state.errors, [field]: undefined },\n }));\n },\n\n markClean: () => {\n set({ isDirty: false });\n },\n\n // Translation methods\n setTranslationLoading: (languageIso: string, loading: boolean) => {\n set((state: ProductStoreState) => ({\n translationLoading: {\n ...state.translationLoading,\n [languageIso]: loading,\n },\n }));\n },\n\n setTranslationData: (languageIso: string, data: TranslationData) => {\n set((state: ProductStoreState) => ({\n translations: {\n ...state.translations,\n [languageIso]: data,\n },\n translationLoading: {\n ...state.translationLoading,\n [languageIso]: false,\n },\n }));\n },\n\n updateTranslationField: (\n languageIso: string,\n field: keyof TranslationData,\n value: string,\n ) => {\n set((state: ProductStoreState) => {\n const originalValue = state.translations[languageIso]?.[field];\n let error: string | undefined;\n\n if (\n field === \"title\" &&\n value === \"\" &&\n originalValue &&\n originalValue.trim() !== \"\"\n ) {\n error = \"Title is required\";\n }\n\n return {\n editedTranslations: {\n ...state.editedTranslations,\n [languageIso]: {\n ...state.editedTranslations[languageIso],\n [field]: value,\n },\n },\n translationErrors: {\n ...state.translationErrors,\n [languageIso]: {\n ...state.translationErrors[languageIso],\n [field]: error,\n },\n },\n };\n });\n },\n\n getTranslation: (languageIso: string, field: keyof TranslationData) => {\n const state = get();\n return (\n state.editedTranslations[languageIso]?.[field] ??\n state.translations[languageIso]?.[field]\n );\n },\n\n getOriginalTranslation: (\n languageIso: string,\n field: keyof TranslationData,\n ) => {\n return get().translations[languageIso]?.[field];\n },\n\n getEditedTranslation: (languageIso: string, field: keyof TranslationData) => {\n return get().editedTranslations[languageIso]?.[field];\n },\n\n setTranslationError: (\n languageIso: string,\n field: keyof TranslationData,\n error?: string,\n ) => {\n set((state: ProductStoreState) => ({\n translationErrors: {\n ...state.translationErrors,\n [languageIso]: {\n ...state.translationErrors[languageIso],\n [field]: error,\n },\n },\n }));\n },\n\n getTranslationError: (languageIso: string, field: keyof TranslationData) => {\n return get().translationErrors[languageIso]?.[field];\n },\n\n isTranslationLoading: (languageIso: string) => {\n return get().translationLoading[languageIso] ?? false;\n },\n\n resetTranslations: () => {\n set({\n translations: {},\n editedTranslations: {},\n translationErrors: {},\n translationLoading: {},\n translationsFetched: false,\n });\n },\n\n setTranslationsFetched: (fetched: boolean) => {\n set({ translationsFetched: fetched });\n },\n});\n\nexport const useProductStore =\n process.env.NODE_ENV === \"development\"\n ? create<ProductStoreState>()(\n devtools(createProductStore, { name: \"product-store\" }),\n )\n : create<ProductStoreState>()(createProductStore);\n","import { create } from \"zustand\";\n\ninterface DraftStore {\n draftData: unknown | null;\n isFromSettings: boolean;\n navigationTarget: string | null;\n\n saveDraft: (data: unknown) => void;\n getDraft: () => unknown | null;\n clearDraft: () => void;\n setFromSettings: (value: boolean) => void;\n setNavigationTarget: (target: string | null) => void;\n reset: () => void;\n}\n\nexport const useDraftStore = create<DraftStore>()((set, get) => ({\n draftData: null,\n isFromSettings: false,\n navigationTarget: null,\n\n saveDraft: (data: unknown) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const formData = data as any;\n const hasContent =\n formData?.title ||\n formData?.description ||\n formData?.sku ||\n (formData?.product_subscription_plans &&\n formData.product_subscription_plans.length > 0);\n\n if (hasContent) {\n set({ draftData: data });\n }\n },\n\n getDraft: () => {\n const { draftData, isFromSettings, navigationTarget } = get();\n\n if (!draftData) {\n return null;\n }\n\n if (isFromSettings && navigationTarget) {\n return draftData;\n }\n\n set({ draftData: null });\n return null;\n },\n\n clearDraft: () => {\n const { draftData } = get();\n if (draftData) {\n set({ draftData: null });\n }\n },\n\n setFromSettings: (value: boolean) => {\n set({ isFromSettings: value });\n },\n\n setNavigationTarget: (target: string | null) => {\n set({ navigationTarget: target });\n },\n\n reset: () => {\n set({ draftData: null, isFromSettings: false, navigationTarget: null });\n },\n}));\n","import type { products } from \"@fluid-app/products-api-client\";\n\nexport function createSlug(title: string): string {\n return title\n .trim()\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport function stripHtmlTags(html: string): string {\n return html\n .replace(/<[^>]*>/g, \"\")\n .replace(/&[^;]+;/g, \" \")\n .trim();\n}\n\nexport function getVariantImageUrl(\n variant?: {\n primary_image?: string | null;\n image_url?: string | null;\n images?: Array<{ image_url: string }> | null;\n } | null,\n product?: { image_url?: string | null } | null,\n): string {\n if (!variant) return product?.image_url || \"\";\n if (variant.primary_image) return variant.primary_image;\n if (variant.image_url) return variant.image_url;\n if (Array.isArray(variant.images) && variant.images.length > 0) {\n const firstImage = variant.images[0];\n if (firstImage?.image_url) return firstImage.image_url;\n }\n return product?.image_url || \"\";\n}\n\nexport function getProductImageUrl(\n product?: {\n image_url?: string | null;\n images?: Array<{ image_url: string; position?: number }> | null;\n } | null,\n): string | null {\n if (!product) return null;\n if (Array.isArray(product.images) && product.images.length > 0) {\n const sortedImages = [...product.images].sort(\n (a, b) => (a.position ?? 0) - (b.position ?? 0),\n );\n const primaryImage = sortedImages[0];\n if (primaryImage?.image_url) return primaryImage.image_url;\n }\n return product.image_url ?? null;\n}\n\nexport function sanitizeBundleData<\n T extends products.CreateProduct | products.UpdateProduct,\n>(productData: T): T {\n const activeVariants = (productData.variants_attributes || []).filter(\n (variant) => !variant._destroy,\n );\n const hasMultipleVariants = activeVariants.length > 1;\n\n const activeOptions = (productData.options || []).filter(\n (opt) => !opt._destroy,\n );\n const derivedOptionAttrs = [\n ...new Set(activeOptions.map((opt) => opt.title.toLowerCase())),\n ];\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { options, option_attrs, ...restProductData } = productData;\n\n // We spread the original data and override specific fields, preserving the\n // runtime shape. The cast to T is safe because we only narrow/replace fields\n // that exist on the base type.\n if (hasMultipleVariants) {\n return {\n ...productData,\n options: undefined,\n option_attrs: derivedOptionAttrs,\n bundle: false,\n product_bundles_attributes: [],\n track_inventory_on_bundle_items: false,\n } as T;\n }\n\n return {\n ...productData,\n options: undefined,\n option_attrs: derivedOptionAttrs,\n } as T;\n}\n\nexport interface ApiErrorShape {\n message?: string;\n error_message?: string;\n status?: number;\n data?: unknown;\n errors?: Record<string, string[]>;\n}\n\nfunction getErrorsFromApiError(\n error: ApiErrorShape,\n): Record<string, string[]> | undefined {\n if (error?.errors && typeof error.errors === \"object\") {\n return error.errors;\n }\n if (error?.data && typeof error.data === \"object\") {\n const data = error.data as Record<string, unknown>;\n const firstKey = Object.keys(data)[0];\n if (firstKey && Array.isArray(data[firstKey])) {\n return data as Record<string, string[]>;\n }\n }\n return undefined;\n}\n\nfunction formatFieldName(fieldName: string): string {\n const SEO_FIELD_LABELS: Record<string, string> = {\n \"search_engine_optimizer.title\": \"SEO Title\",\n \"search_engine_optimizer.description\": \"SEO Description\",\n \"search_engine_optimizer.image_url\": \"SEO Image\",\n };\n\n if (SEO_FIELD_LABELS[fieldName]) return SEO_FIELD_LABELS[fieldName];\n\n const parts = fieldName.split(\".\");\n if (parts.length > 1) {\n return parts\n .map((part, index) =>\n part\n .split(\"_\")\n .map((word, wordIndex) => {\n if (\n (index === 0 && wordIndex === 0) ||\n (index > 0 && wordIndex > 0)\n )\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();\n return word.toLowerCase();\n })\n .join(\" \"),\n )\n .join(\" \");\n }\n\n return fieldName\n .split(\"_\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\" \");\n}\n\nexport function getErrorMessage(\n error: ApiErrorShape,\n fallbackMessage: string,\n): string {\n if (error?.message && error.message !== \"unprocessable entity\") {\n return error.message;\n }\n\n const errorsObj = getErrorsFromApiError(error);\n if (errorsObj) {\n const firstField = Object.keys(errorsObj)[0];\n if (firstField && errorsObj[firstField]) {\n const fieldErrors = errorsObj[firstField];\n if (\n Array.isArray(fieldErrors) &&\n fieldErrors.length > 0 &&\n fieldErrors[0]\n ) {\n return `${formatFieldName(firstField)} ${fieldErrors[0]}`;\n }\n }\n }\n\n return fallbackMessage;\n}\n\nexport function isSeoError(fieldName: string): boolean {\n return (\n fieldName.startsWith(\"search_engine_optimizer\") || fieldName === \"slug\"\n );\n}\n\nexport function extractSeoErrors(\n error: ApiErrorShape,\n): Array<{ field: string; message: string }> {\n const seoErrors: Array<{ field: string; message: string }> = [];\n const errorsObj = getErrorsFromApiError(error);\n if (errorsObj) {\n for (const fieldName of Object.keys(errorsObj)) {\n if (isSeoError(fieldName) && errorsObj[fieldName]) {\n const fieldErrors = errorsObj[fieldName];\n if (\n Array.isArray(fieldErrors) &&\n fieldErrors.length > 0 &&\n fieldErrors[0]\n ) {\n seoErrors.push({\n field: fieldName,\n message: `${formatFieldName(fieldName)} ${fieldErrors[0].charAt(0).toUpperCase() + fieldErrors[0].slice(1)}`,\n });\n }\n }\n }\n }\n return seoErrors;\n}\n","const VIDEO_EXTENSIONS = [\n \".mp4\",\n \".webm\",\n \".mov\",\n \".avi\",\n \".m4v\",\n \".mkv\",\n \".ogv\",\n \".ogg\",\n \".wmv\",\n \".flv\",\n \".3gp\",\n];\n\nconst IMAGE_EXTENSIONS = [\n \".jpg\",\n \".jpeg\",\n \".png\",\n \".gif\",\n \".webp\",\n \".svg\",\n \".bmp\",\n \".ico\",\n \".tiff\",\n \".tif\",\n];\n\nexport function isVideoUrl(url: string | undefined | null): boolean {\n if (!url) return false;\n const lowerUrl = url.toLowerCase();\n return VIDEO_EXTENSIONS.some((ext) => lowerUrl.includes(ext));\n}\n\nexport function isImageUrl(url: string | undefined | null): boolean {\n if (!url) return false;\n const lowerUrl = url.toLowerCase();\n return IMAGE_EXTENSIONS.some((ext) => lowerUrl.includes(ext));\n}\n\nexport function getMediaTypeFromUrl(\n url: string | undefined | null,\n): \"video\" | \"image\" | \"unknown\" {\n if (isVideoUrl(url)) return \"video\";\n if (isImageUrl(url)) return \"image\";\n return \"unknown\";\n}\n\nexport function getVideoThumbnailUrl(videoUrl: string): string {\n if (videoUrl.includes(\"ik.imagekit.io\")) {\n return `${videoUrl}/ik-thumbnail.jpg`;\n }\n return videoUrl;\n}\n\nexport function ensureImageIsFeatured<\n T extends { image_url: string; position?: number },\n>(items: T[]): T[] {\n if (items.length === 0) return items;\n\n const firstItem = items[0];\n if (!firstItem || !isVideoUrl(firstItem.image_url)) {\n return items;\n }\n\n const firstImageIndex = items.findIndex(\n (item) => !isVideoUrl(item.image_url),\n );\n\n if (firstImageIndex === -1) {\n return items;\n }\n\n const reordered = [...items];\n const [imageToMove] = reordered.splice(firstImageIndex, 1);\n if (imageToMove) {\n reordered.unshift(imageToMove);\n }\n\n return reordered;\n}\n","import type React from \"react\";\nimport { useState, type ReactNode } from \"react\";\nimport type { products } from \"@fluid-app/products-api-client\";\nimport { determineProductPrice } from \"@fluid-app/products-api-client\";\nimport { getProductImageUrl } from \"@fluid-app/products-core\";\nimport { Card } from \"@fluid-app/ui-primitives\";\nimport { CirclePlay } from \"lucide-react\";\nimport { getVideoThumbnailUrl, isVideoUrl } from \"../utils/media-helpers\";\n\ntype ProductCardProduct = (products.Product | products.ShopProduct) & {\n kind?: string;\n video_url?: string;\n};\n\nexport interface RenderImageProps {\n src: string;\n alt: string;\n fill?: boolean;\n className?: string;\n onError?: (e: React.SyntheticEvent<HTMLImageElement>) => void;\n unoptimized?: boolean;\n}\n\ninterface ProductCardProps {\n product: ProductCardProduct;\n countryIso: string;\n companyLogoUrl?: string | null;\n showShareModal?: boolean;\n setShareModalOpen?: (open: boolean) => void;\n setSelectedProduct?: (product: ProductCardProduct) => void;\n renderLink?: (props: { href: string; children: ReactNode }) => ReactNode;\n renderImage?: (props: RenderImageProps) => ReactNode;\n onClick?: () => void;\n}\n\nfunction getSelectedVariant(\n product: ProductCardProduct,\n): products.Variant | products.ShopVariant | null {\n if (!product.variants || product.variants.length === 0) return null;\n\n const masterVariant = product.variants.find(\n (v: products.Variant | products.ShopVariant) => {\n return \"is_master\" in v && v.is_master;\n },\n );\n if (masterVariant) return masterVariant;\n\n return product.variants[0] || null;\n}\n\nfunction getVariantCountryValue(\n variant: products.Variant | products.ShopVariant | null,\n countryIso: string,\n field: \"cv\" | \"qv\",\n): number | null {\n if (!variant || !variant.variant_countries) return null;\n\n if (\n typeof variant.variant_countries === \"object\" &&\n !Array.isArray(variant.variant_countries)\n ) {\n const countryData = variant.variant_countries[countryIso] as\n | products.VariantCountry\n | undefined;\n return countryData?.[field] ?? null;\n }\n\n if (Array.isArray(variant.variant_countries)) {\n const countryData = variant.variant_countries.find(\n (vc: products.ShopVariantCountry) => vc.country_iso === countryIso,\n );\n return countryData?.[field] ?? null;\n }\n\n return null;\n}\n\nfunction defaultRenderImage({\n src,\n alt,\n fill,\n className,\n onError,\n}: RenderImageProps): ReactNode {\n return (\n <img\n src={src}\n alt={alt}\n className={`${fill ? \"absolute inset-0 h-full w-full\" : \"\"} ${className ?? \"\"}`}\n onError={onError}\n />\n );\n}\n\nfunction ProductCardContent({\n product,\n repPrice,\n price,\n countryIso,\n companyLogoUrl,\n renderImage = defaultRenderImage,\n}: {\n product: ProductCardProduct;\n repPrice: string;\n price?: string;\n countryIso: string;\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n}) {\n const [isHovered, setIsHovered] = useState(false);\n const coverImage = getProductImageUrl(\n product as Parameters<typeof getProductImageUrl>[0],\n );\n const isVideo = isVideoUrl(coverImage);\n const selectedVariant = getSelectedVariant(product);\n\n return (\n <>\n {/* Image/Video container */}\n <div\n className=\"bg-muted relative aspect-square overflow-hidden\"\n onMouseEnter={() => isVideo && setIsHovered(true)}\n onMouseLeave={() => isVideo && setIsHovered(false)}\n >\n {isVideo && isHovered ? (\n <video\n src={coverImage || \"\"}\n className=\"absolute inset-0 h-full w-full object-cover\"\n autoPlay\n muted\n loop\n playsInline\n />\n ) : (\n renderImage({\n src:\n isVideo && coverImage\n ? getVideoThumbnailUrl(coverImage)\n : coverImage ||\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\",\n alt: product.title || \"\",\n fill: true,\n className:\n \"object-cover transition-transform group-hover:scale-105\",\n onError: (e) => {\n e.currentTarget.src =\n companyLogoUrl ||\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n },\n unoptimized: true,\n })\n )}\n\n {/* Video play indicator */}\n {isVideo && !isHovered && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm\">\n <CirclePlay className=\"h-12 w-12 text-white\" />\n </div>\n </div>\n )}\n </div>\n\n {/* Product info */}\n <div className=\"px-3 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-1 text-sm font-bold\">\n {product.title || \"No title available\"}\n </h3>\n\n <div className=\"flex items-center gap-2\">\n <span className=\"text-foreground text-sm font-bold\">{repPrice}</span>\n <span className=\"text-muted-foreground text-sm line-through\">\n {price}\n </span>\n </div>\n\n <div className=\"text-muted-foreground text-sm\">\n CV {getVariantCountryValue(selectedVariant, countryIso, \"cv\") || \"-\"}{\" \"}\n | QV{\" \"}\n {getVariantCountryValue(selectedVariant, countryIso, \"qv\") || \"-\"}\n </div>\n </div>\n </>\n );\n}\n\nexport default function ProductCard({\n product,\n countryIso,\n companyLogoUrl,\n showShareModal = false,\n setShareModalOpen,\n setSelectedProduct,\n renderLink,\n renderImage,\n onClick,\n}: ProductCardProps): React.JSX.Element {\n const { repPrice, price } = determineProductPrice(product, countryIso);\n const handleShareClick = () => {\n if (setSelectedProduct && setShareModalOpen) {\n setSelectedProduct(product);\n setShareModalOpen(true);\n }\n };\n\n const cardContent = (\n <ProductCardContent\n product={product}\n repPrice={repPrice || \"\"}\n price={price || undefined}\n countryIso={countryIso}\n companyLogoUrl={companyLogoUrl}\n renderImage={renderImage}\n />\n );\n\n const cardClassName = \"overflow-hidden border-0 shadow-none pt-0 gap-0\";\n\n if (showShareModal) {\n return (\n <Card className={cardClassName}>\n <button\n onClick={handleShareClick}\n className=\"group block w-full cursor-pointer text-left\"\n >\n {cardContent}\n </button>\n </Card>\n );\n }\n\n if (onClick) {\n return (\n <Card className={cardClassName}>\n <button\n onClick={onClick}\n className=\"group block w-full cursor-pointer text-left\"\n >\n {cardContent}\n </button>\n </Card>\n );\n }\n\n const href = `/portal/shop/${product.id}`;\n\n if (renderLink) {\n return (\n <Card className={cardClassName}>\n {renderLink({ href, children: cardContent })}\n </Card>\n );\n }\n\n return (\n <Card className={cardClassName}>\n <a href={href} className=\"group block cursor-pointer\">\n {cardContent}\n </a>\n </Card>\n );\n}\n","import type React from \"react\";\nimport { useState, useEffect, useMemo, type ReactNode } from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { isVideoUrl } from \"../utils/media-helpers\";\nimport type { RenderImageProps } from \"./product-card\";\n\ninterface ImageGalleryProps {\n images: Array<{\n id: number;\n image_url: string;\n image_path: string | null;\n position: number;\n }>;\n fallbackImageUrl: string;\n productTitle: string;\n renderImage?: (props: RenderImageProps) => ReactNode;\n}\n\nfunction defaultRenderImage({\n src,\n alt,\n fill,\n className,\n onError,\n}: RenderImageProps): ReactNode {\n return (\n <img\n src={src}\n alt={alt}\n className={`${fill ? \"absolute inset-0 h-full w-full\" : \"\"} ${className ?? \"\"}`}\n onError={onError}\n />\n );\n}\n\nexport default function ImageGallery({\n images,\n fallbackImageUrl,\n productTitle,\n renderImage = defaultRenderImage,\n}: ImageGalleryProps): React.JSX.Element {\n const [currentImageIndex, setCurrentImageIndex] = useState(0);\n const [isImageHovered, setIsImageHovered] = useState(false);\n const [hoverSide, setHoverSide] = useState<\"left\" | \"right\" | null>(null);\n\n const hasMultipleImages = images && images.length > 0;\n const displayImages = useMemo(\n () =>\n hasMultipleImages\n ? [...images].sort((a, b) => a.position - b.position)\n : [{ id: 0, image_url: fallbackImageUrl, position: 0 }],\n [images, hasMultipleImages, fallbackImageUrl],\n );\n\n // Reset to first image when the images array changes (e.g. variant switch)\n useEffect(() => {\n setCurrentImageIndex(0);\n }, [displayImages]);\n\n const nextImage = () => {\n if (displayImages.length > 1) {\n setCurrentImageIndex((prev) => (prev + 1) % displayImages.length);\n }\n };\n\n const prevImage = () => {\n if (displayImages.length > 1) {\n setCurrentImageIndex(\n (prev) => (prev - 1 + displayImages.length) % displayImages.length,\n );\n }\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Main Image */}\n <div\n className=\"relative aspect-square overflow-hidden rounded-sm bg-gray-100\"\n onMouseEnter={() => setIsImageHovered(true)}\n onMouseLeave={() => {\n setIsImageHovered(false);\n setHoverSide(null);\n }}\n onMouseMove={(e) => {\n const bounds = e.currentTarget.getBoundingClientRect();\n const x = e.clientX - bounds.left;\n if (x < bounds.width / 4) {\n setHoverSide(\"left\");\n } else if (x > (bounds.width * 3) / 4) {\n setHoverSide(\"right\");\n } else {\n setHoverSide(null);\n }\n }}\n >\n {isVideoUrl(displayImages[currentImageIndex]?.image_url) ? (\n <video\n key={displayImages[currentImageIndex]?.id}\n src={displayImages[currentImageIndex]?.image_url}\n className=\"absolute inset-0 h-full w-full object-cover\"\n controls\n loop\n playsInline\n />\n ) : (\n renderImage({\n src:\n displayImages[currentImageIndex]?.image_url || fallbackImageUrl,\n alt: productTitle,\n fill: true,\n className: \"object-cover group-hover:scale-105\",\n onError: (e) => {\n e.currentTarget.src =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n },\n unoptimized: true,\n })\n )}\n\n {/* Navigation click areas and icons */}\n {displayImages.length > 1 && isImageHovered && (\n <>\n {/* Left quarter clickable area and icon */}\n <div\n className=\"absolute top-0 left-0 z-10 h-full w-1/4 cursor-pointer\"\n onClick={prevImage}\n style={{ pointerEvents: \"auto\" }}\n >\n {hoverSide === \"left\" && (\n <span className=\"absolute top-1/2 left-8 z-20 -translate-y-1/2 select-none\">\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-white\">\n <ChevronLeft className=\"h-5 w-5 text-black\" />\n </span>\n </span>\n )}\n </div>\n {/* Right quarter clickable area and icon */}\n <div\n className=\"absolute top-0 right-0 z-10 h-full w-1/4 cursor-pointer\"\n onClick={nextImage}\n style={{ pointerEvents: \"auto\" }}\n >\n {hoverSide === \"right\" && (\n <span className=\"absolute top-1/2 right-8 z-20 -translate-y-1/2 select-none\">\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-white\">\n <ChevronRight className=\"h-5 w-5 text-black\" />\n </span>\n </span>\n )}\n </div>\n </>\n )}\n\n {/* Page Indicators - Bottom Center */}\n {displayImages.length > 1 && (\n <div className=\"absolute bottom-3 left-1/2 flex -translate-x-1/2 gap-3\">\n {displayImages.map((_, index) => (\n <div\n key={index}\n className={`h-1.5 w-6 rounded-lg transition-colors ${\n index === currentImageIndex ? \"bg-gray-800\" : \"bg-gray-400\"\n }`}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport { useMemo } from \"react\";\nimport type { products } from \"@fluid-app/products-api-client\";\nimport {\n RadioGroup,\n RadioGroupItem,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@fluid-app/ui-primitives\";\n\ninterface PurchaseOptionsProps {\n showBuyOnce: boolean;\n showSubscribe: boolean;\n isSubscribe: boolean;\n onSubscribeChange: (subscribe: boolean) => void;\n product_subscription_plans: products.ProductSubscriptionPlan[];\n selectedSubscriptionPlan?: products.ProductSubscriptionPlan;\n onSubscriptionPlanChange?: (plan: products.ProductSubscriptionPlan) => void;\n userSelectedSubscribe: boolean;\n wholesalePrice?: number;\n wholesaleSubscriptionPrice?: number;\n}\n\nexport default function PurchaseOptions({\n showBuyOnce,\n showSubscribe,\n isSubscribe,\n onSubscribeChange,\n product_subscription_plans,\n selectedSubscriptionPlan,\n onSubscriptionPlanChange,\n userSelectedSubscribe,\n wholesalePrice,\n wholesaleSubscriptionPrice,\n}: PurchaseOptionsProps): React.JSX.Element | null {\n // Find default subscription plan or use first one\n const defaultSubscriptionPlan = useMemo(() => {\n if (!product_subscription_plans?.length) return null;\n return (\n product_subscription_plans.find((plan) => plan.default) ||\n product_subscription_plans[0]\n );\n }, [product_subscription_plans]);\n\n // Use selected plan or default to the default/first plan\n const currentSubscriptionPlan =\n selectedSubscriptionPlan || defaultSubscriptionPlan;\n\n // Calculate savings percentage from wholesale prices\n const savingsText = useMemo(() => {\n if (\n wholesalePrice === undefined ||\n wholesaleSubscriptionPrice === undefined ||\n wholesalePrice <= 0\n ) {\n return null;\n }\n\n const savings = wholesalePrice - wholesaleSubscriptionPrice;\n if (savings <= 0) return null;\n\n const savingsPercentage = Math.round((savings / wholesalePrice) * 100);\n if (savingsPercentage <= 0) return null;\n\n return `Save ${savingsPercentage}%`;\n }, [wholesalePrice, wholesaleSubscriptionPrice]);\n\n // Format subscription plan display name\n const formatSubscriptionPlan = (plan: products.ProductSubscriptionPlan) => {\n const interval = plan.subscription_plan.billing_interval;\n const unit = plan.subscription_plan.billing_interval_unit;\n return `${interval} ${unit} (${plan.subscription_plan.name})`;\n };\n\n // Prepare subscription plan options for Select component\n const subscriptionPlanOptions = useMemo(() => {\n if (!product_subscription_plans?.length) return [];\n\n return product_subscription_plans.map((plan) => ({\n value: plan.subscription_plan.id.toString(),\n label: formatSubscriptionPlan(plan),\n }));\n }, [product_subscription_plans]);\n\n // Don't render if only buy once is shown and subscribe is false\n if (showBuyOnce && !showSubscribe) {\n return null;\n }\n const handleSubscriptionPlanChange = (planId: string) => {\n const selectedPlan = product_subscription_plans?.find(\n (plan) => plan.subscription_plan.id.toString() === planId,\n );\n if (selectedPlan && onSubscriptionPlanChange) {\n onSubscriptionPlanChange(selectedPlan);\n }\n };\n\n const purchaseType = isSubscribe ? \"subscribe\" : \"once\";\n\n return (\n <div className=\"mb-3 flex flex-col\">\n <RadioGroup\n value={purchaseType}\n onValueChange={(value) => onSubscribeChange(value === \"subscribe\")}\n className=\"gap-0 space-y-0\"\n >\n {showSubscribe && (\n <div\n className={`cursor-pointer rounded-t-lg p-4 text-left transition-all duration-200 ${showBuyOnce ? \"border border-b-0\" : \"rounded-b-lg\"} ${userSelectedSubscribe ? \"bg-muted\" : \"bg-background\"}`}\n >\n <div className=\"flex items-start space-x-3\">\n <RadioGroupItem\n value=\"subscribe\"\n className={`flex items-center justify-center [&_svg]:h-1.5 [&_svg]:w-1.5 [&_svg]:fill-white ${\n isSubscribe\n ? \"border-contrast bg-contrast text-foreground\"\n : \"border-border bg-background text-muted-foreground\"\n }`}\n />\n <div className=\"flex-1\">\n <div className=\"text-foreground mb-1 text-sm leading-tight font-medium\">\n Subscribe{savingsText ? ` & ${savingsText}` : \"\"}\n </div>\n\n {/* Subscription Plan Dropdown*/}\n {product_subscription_plans?.length > 0 && (\n <div\n className=\"bg-muted mt-3 rounded-lg p-2\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"text-foreground mb-1 text-xs font-medium\">\n Delivery Frequency:\n </div>\n <Select\n value={\n currentSubscriptionPlan?.subscription_plan.id.toString() ||\n \"\"\n }\n onValueChange={handleSubscriptionPlanChange}\n disabled={product_subscription_plans?.length === 1}\n >\n <SelectTrigger className=\"bg-background text-foreground! w-full text-xs\">\n <SelectValue placeholder=\"Select delivery schedule\" />\n </SelectTrigger>\n <SelectContent>\n {subscriptionPlanOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n {showBuyOnce && (\n <div\n className={`cursor-pointer rounded-b-lg border p-4 text-left transition-all duration-200 ${showSubscribe ? \"border-t-0\" : \"rounded-t-lg\"} ${!userSelectedSubscribe ? \"bg-muted\" : \"bg-background\"}`}\n >\n <div className=\"flex items-center space-x-3\">\n <RadioGroupItem\n value=\"once\"\n className={`flex items-center justify-center [&_svg]:h-1.5 [&_svg]:w-1.5 [&_svg]:fill-white ${\n !isSubscribe\n ? \"border-contrast bg-contrast text-foreground\"\n : \"border-border bg-background text-muted-foreground\"\n }`}\n />\n <div className=\"text-foreground mb-1 text-sm leading-tight font-medium\">\n One-Time Purchase\n </div>\n </div>\n </div>\n )}\n </RadioGroup>\n </div>\n );\n}\n","import type React from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\n\ninterface QuantitySelectorProps {\n quantity: number;\n setQuantity: (quantity: number) => void;\n}\n\nexport default function QuantitySelector({\n quantity,\n setQuantity,\n}: QuantitySelectorProps): React.JSX.Element {\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center rounded-lg\">\n <Button\n variant=\"default\"\n onClick={() => setQuantity(Math.max(1, quantity - 1))}\n className=\"border-0 px-3 py-2 shadow-none\"\n >\n −\n </Button>\n <span className=\"text-foreground min-w-8 px-4 py-2 text-center text-sm font-medium\">\n {quantity}\n </span>\n <Button\n variant=\"default\"\n onClick={() => setQuantity(quantity + 1)}\n className=\"border-0 px-3 py-2 shadow-none\"\n >\n +\n </Button>\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport {\n useState,\n useMemo,\n useEffect,\n useRef,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport type { products } from \"@fluid-app/products-api-client\";\nimport { useProductCatalog, useProductDetail } from \"@fluid-app/products-core\";\nimport { getProductImageUrl } from \"@fluid-app/products-core\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport {\n Button,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { ArrowLeft, Search, ShoppingCart } from \"lucide-react\";\nimport ProductCard, { type RenderImageProps } from \"./product-card\";\nimport ImageGallery from \"./image-gallery\";\nimport PurchaseOptions from \"./purchase-options\";\nimport QuantitySelector from \"./quantity-selector\";\n\ninterface ShopAppProps {\n countryCode: string;\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n onAddToCart?: (\n variantId: number,\n quantity: number,\n subscribe: boolean,\n subscriptionPlanId?: string,\n ) => void;\n /** When provided, controls which product detail to show (URL-driven routing) */\n productId?: string | null;\n /** Called when a product is selected from the listing */\n onSelectProduct?: (productId: string) => void;\n /** Called when user navigates back from product detail */\n onBack?: () => void;\n /** Optional cart button to render in the header area */\n cartButton?: ReactNode;\n}\n\nconst PAGE_SIZE = 25;\n\nfunction sanitizeHtml(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n for (const el of doc.querySelectorAll(\n \"script, iframe, object, embed, form, base, meta, link, style\",\n )) {\n el.remove();\n }\n for (const el of doc.querySelectorAll(\"*\")) {\n for (const attr of [...el.attributes]) {\n if (\n attr.name.toLowerCase().startsWith(\"on\") ||\n attr.value.toLowerCase().trim().startsWith(\"javascript:\")\n ) {\n el.removeAttribute(attr.name);\n }\n }\n }\n return doc.body.innerHTML;\n}\n\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4\";\n\nfunction SkeletonGrid({ count = 8 }: { count?: number }) {\n return (\n <div className={GRID_CLASS}>\n {Array.from({ length: count }, (_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n ))}\n </div>\n );\n}\n\nfunction ProductListing({\n countryCode,\n companyLogoUrl,\n renderImage,\n onSelectProduct,\n cartButton,\n}: {\n countryCode: string;\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n onSelectProduct: (productId: string) => void;\n cartButton?: ReactNode;\n}) {\n const observerTarget = useRef<HTMLDivElement>(null);\n\n const catalog = useProductCatalog({\n initialCountryCode: countryCode,\n publishedStores: [\"rep\"],\n statusFilter: [\"active\"],\n });\n\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 = 1 }) => catalog.fetchProducts(pageParam),\n getNextPageParam: (lastPage, allPages) =>\n lastPage.length === PAGE_SIZE ? allPages.length + 1 : undefined,\n initialPageParam: 1,\n });\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n\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 allProducts = data?.pages.flatMap((page) => page) ?? [];\n\n return (\n <div className=\"h-full overflow-auto\">\n <div className=\"mx-auto px-2 md:px-10\">\n {/* Search and Sort */}\n <div className=\"flex flex-col gap-4 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative flex-1 sm:max-w-96\">\n <Search className=\"text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2\" />\n <Input\n placeholder=\"Search products...\"\n value={catalog.searchTerm}\n onChange={(e) => catalog.setSearchTerm(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n <div className=\"flex shrink-0 items-center gap-3\">\n <Select\n value={`${catalog.currentSort.id}-${catalog.currentSort.desc ? \"desc\" : \"asc\"}`}\n onValueChange={(value) => {\n const lastDash = value.lastIndexOf(\"-\");\n const id = value.slice(0, lastDash);\n const dir = value.slice(lastDash + 1);\n if (id) {\n catalog.setCurrentSort({ id, desc: dir === \"desc\" });\n }\n }}\n >\n <SelectTrigger className=\"w-48\">\n <SelectValue placeholder=\"Sort by\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"title-asc\">Name A-Z</SelectItem>\n <SelectItem value=\"title-desc\">Name Z-A</SelectItem>\n <SelectItem value=\"price-asc\">Price Low-High</SelectItem>\n <SelectItem value=\"price-desc\">Price High-Low</SelectItem>\n </SelectContent>\n </Select>\n {cartButton}\n </div>\n </div>\n </div>\n\n {/* Product Grid */}\n <div className=\"mx-auto space-y-8 px-2 md:px-10 md:py-8\">\n {isLoading ? (\n <SkeletonGrid />\n ) : error ? (\n <p className=\"mx-auto my-6 rounded-lg bg-red-100 px-3 py-2 text-red-500\">\n Error: {error.message}\n </p>\n ) : isFetched && allProducts.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n {catalog.searchTerm\n ? `No products match \"${catalog.searchTerm}\". Try a different search term.`\n : \"There are no products available at the moment.\"}\n </p>\n </div>\n ) : (\n <>\n <div className={GRID_CLASS}>\n {allProducts.map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n countryIso={countryCode}\n companyLogoUrl={companyLogoUrl}\n renderImage={renderImage}\n onClick={() => onSelectProduct(String(product.id))}\n />\n ))}\n </div>\n <div ref={observerTarget} />\n {isFetchingNextPage && <SkeletonGrid count={4} />}\n </>\n )}\n </div>\n </div>\n );\n}\n\nfunction ProductDetail({\n productId,\n countryCode,\n renderImage,\n onBack,\n onAddToCart,\n cartButton,\n}: {\n productId: string;\n countryCode: string;\n renderImage?: (props: RenderImageProps) => ReactNode;\n onBack: () => void;\n onAddToCart?: ShopAppProps[\"onAddToCart\"];\n cartButton?: ReactNode;\n}) {\n const {\n product,\n isLoading,\n error,\n currentVariant,\n productOptions,\n effectiveSelectedOptions,\n handleOptionChange,\n quantity,\n setQuantity,\n isSubscribe,\n userSelectedSubscribe,\n setUserSelectedSubscribe,\n showBuyOnce,\n showSubscribe,\n selectedSubscriptionPlan,\n setSelectedSubscriptionPlan,\n displayPrice,\n displayWholesalePrice,\n displayWholesaleSubscriptionPrice,\n wholesaleCv,\n wholesaleQv,\n currentVariantCountry,\n } = useProductDetail({\n productId,\n countryCode,\n });\n\n const coverImage = product\n ? getProductImageUrl(product as Parameters<typeof getProductImageUrl>[0])\n : null;\n\n const galleryImages = useMemo(\n () =>\n product\n ? (\n ((currentVariant?.images?.length\n ? currentVariant.images\n : product.images) || []) as Array<{\n id?: number;\n image_url?: string;\n image_path?: string | null;\n position?: number;\n }>\n ).map((img, idx) => ({\n id: img.id ?? idx,\n image_url: img.image_url ?? coverImage ?? \"\",\n image_path: img.image_path ?? null,\n position: img.position ?? idx,\n }))\n : [],\n [product, currentVariant?.images, coverImage],\n );\n\n const handleAddToCart = () => {\n if (!onAddToCart || !currentVariant?.id) return;\n onAddToCart(\n currentVariant.id,\n quantity,\n isSubscribe,\n selectedSubscriptionPlan?.subscription_plan?.id?.toString(),\n );\n };\n\n if (isLoading) {\n return (\n <div className=\"mx-auto max-w-7xl py-8 pr-4 pl-0 md:pr-6 lg:pr-8 lg:pl-0\">\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <div className=\"space-y-4 pl-20\">\n <Skeleton className=\"h-8 w-3/4\" />\n <Skeleton className=\"h-5 w-1/4\" />\n <Skeleton className=\"h-20 w-full\" />\n <Skeleton className=\"h-10 w-1/2\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"text-center\">\n <h3 className=\"text-foreground mb-2 text-lg font-medium\">\n Unable to load product\n </h3>\n <p className=\"text-muted-foreground\">\n Something went wrong. Please try again later.\n </p>\n </div>\n </div>\n );\n }\n\n if (!product) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"text-center\">\n <h3 className=\"text-foreground mb-2 text-lg font-medium\">\n Product not found\n </h3>\n <p className=\"text-muted-foreground\">\n The product you're looking for doesn't exist.\n </p>\n </div>\n </div>\n );\n }\n\n const title = product.title || \"Product\";\n\n return (\n <div className=\"h-full overflow-auto pb-5 pl-8\">\n {/* Back button and cart */}\n <div className=\"flex items-center justify-between px-2 pt-4 md:px-10\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onBack} className=\"gap-1\">\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Shop\n </Button>\n {cartButton}\n </div>\n\n <div className=\"mx-auto max-w-7xl py-8 pr-4 pl-0 md:pr-6 lg:pr-8 lg:pl-0\">\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-2\">\n {/* Image Gallery */}\n <ImageGallery\n images={galleryImages}\n fallbackImageUrl={\n coverImage ??\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\"\n }\n productTitle={title}\n renderImage={renderImage}\n />\n\n {/* Product Info */}\n <div className=\"max-w-lg pl-20\">\n <h1 className=\"text-foreground text-3xl font-bold\">{title}</h1>\n\n {/* Price */}\n <div className=\"mb-2 flex items-center gap-2\">\n <span className=\"text-foreground text-sm\">\n {isSubscribe\n ? displayWholesaleSubscriptionPrice\n : displayWholesalePrice}\n </span>\n {((isSubscribe &&\n displayWholesaleSubscriptionPrice !== displayWholesalePrice) ||\n (!isSubscribe && displayWholesalePrice !== displayPrice)) && (\n <span className=\"text-muted-foreground text-sm line-through\">\n {isSubscribe ? displayWholesalePrice : displayPrice}\n </span>\n )}\n </div>\n\n {/* Product Description */}\n <div className=\"pt-2\">\n <h3 className=\"text-foreground mb-1 text-sm font-medium\">\n Product Description\n </h3>\n <div\n className=\"text-foreground mb-3 text-[12px]\"\n dangerouslySetInnerHTML={{\n __html: sanitizeHtml(product.description ?? \"\"),\n }}\n />\n </div>\n\n {/* Purchase Options */}\n <PurchaseOptions\n showBuyOnce={showBuyOnce}\n showSubscribe={showSubscribe}\n isSubscribe={isSubscribe}\n userSelectedSubscribe={userSelectedSubscribe}\n onSubscribeChange={setUserSelectedSubscribe}\n wholesalePrice={currentVariantCountry?.wholesale}\n wholesaleSubscriptionPrice={\n currentVariantCountry?.wholesale_subscription_price\n }\n product_subscription_plans={\n (product.product_subscription_plans ||\n []) as products.ProductSubscriptionPlan[]\n }\n selectedSubscriptionPlan={selectedSubscriptionPlan}\n onSubscriptionPlanChange={setSelectedSubscriptionPlan}\n />\n\n {/* CV/QV */}\n <div className=\"text-muted-foreground mb-3 text-sm\">\n CV {countryCode && wholesaleCv != null ? wholesaleCv : \"-\"} | QV{\" \"}\n {countryCode && wholesaleQv != null ? wholesaleQv : \"-\"}\n </div>\n <div className=\"flex items-center border-t\" />\n\n {/* Product Options */}\n {productOptions.length > 0 && (\n <div className=\"mb-4 pt-4\">\n {productOptions.map((option) => (\n <div key={option.id} className=\"mb-3 flex items-center\">\n <h3 className=\"text-md text-foreground w-24 font-bold\">\n {option.name.charAt(0).toUpperCase() +\n option.name.slice(1)}\n </h3>\n <Select\n value={effectiveSelectedOptions[option.id] || \"\"}\n onValueChange={(value) =>\n handleOptionChange(option.id, value)\n }\n >\n <SelectTrigger className=\"w-48 max-w-full\">\n <SelectValue placeholder={`Select ${option.name}`} />\n </SelectTrigger>\n <SelectContent>\n {option.values.map((value) => (\n <SelectItem key={value} value={value}>\n {value}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ))}\n </div>\n )}\n\n {/* Unavailable state */}\n {currentVariant?.subscription_only &&\n !currentVariant.allow_subscription && (\n <div className=\"text-muted-foreground text-sm\">\n This product is unavailable for purchase.\n </div>\n )}\n\n {/* Quantity and Add to Cart */}\n <div className=\"mb-3\" />\n <div className=\"flex items-center gap-3 pb-3\">\n <QuantitySelector quantity={quantity} setQuantity={setQuantity} />\n\n <Button\n disabled={\n !currentVariant?.id ||\n (currentVariant?.subscription_only &&\n !currentVariant.allow_subscription)\n }\n variant=\"default\"\n className=\"flex-1 gap-2 py-2 text-base font-medium\"\n onClick={onAddToCart ? handleAddToCart : undefined}\n {...(!onAddToCart && {\n \"data-fluid-add-to-cart\": String(currentVariant?.id ?? \"\"),\n \"data-fluid-quantity\": quantity,\n \"data-fluid-subscribe\": isSubscribe,\n \"data-fluid-subscription-plan-id\": isSubscribe\n ? selectedSubscriptionPlan?.subscription_plan?.id?.toString() ||\n (\n product.product_subscription_plans as\n | products.ProductSubscriptionPlan[]\n | undefined\n )\n ?.find((plan) => plan.default)\n ?.subscription_plan.id.toString() ||\n (\n product.product_subscription_plans as\n | products.ProductSubscriptionPlan[]\n | undefined\n )?.[0]?.subscription_plan.id.toString() ||\n \"\"\n : \"\",\n \"data-fluid-open-cart-after-add\": \"false\",\n })}\n >\n <ShoppingCart className=\"h-4 w-4\" />\n {isSubscribe ? \"Subscribe\" : \"Add to Cart\"}\n </Button>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nexport default function ShopApp({\n countryCode,\n companyLogoUrl,\n renderImage,\n onAddToCart,\n productId: controlledProductId,\n onSelectProduct: onSelectProductProp,\n onBack: onBackProp,\n cartButton,\n}: ShopAppProps): React.JSX.Element {\n // Internal state used only when navigation is not controlled externally\n const [internalProductId, setInternalProductId] = useState<string | null>(\n null,\n );\n\n const isControlled = controlledProductId !== undefined;\n const activeProductId = isControlled\n ? controlledProductId\n : internalProductId;\n\n const handleSelectProduct = onSelectProductProp ?? setInternalProductId;\n const handleBack = onBackProp ?? (() => setInternalProductId(null));\n\n if (activeProductId) {\n return (\n <ProductDetail\n productId={activeProductId}\n countryCode={countryCode}\n renderImage={renderImage}\n onBack={handleBack}\n onAddToCart={onAddToCart}\n cartButton={cartButton}\n />\n );\n }\n\n return (\n <ProductListing\n countryCode={countryCode}\n companyLogoUrl={companyLogoUrl}\n renderImage={renderImage}\n onSelectProduct={handleSelectProduct}\n cartButton={cartButton}\n />\n );\n}\n","import { useEffect, useRef } from \"react\";\n\ninterface CartScriptProps {\n subdomain: string;\n authJwt?: string;\n}\n\nconst SCRIPT_ID = \"fluid-cdn-script\";\nconst LEAD_CAPTURE_ID = \"fluid-lead-capture-suppress\";\nconst SCRIPT_SRC =\n \"https://assets.fluid.app/scripts/fluid-sdk/latest/web-widgets/index.js\";\n\nexport default function CartScript({\n subdomain,\n authJwt,\n}: CartScriptProps): React.ReactNode {\n // Use a ref so the script is injected once with the initial JWT value.\n // ES modules are cached by URL — re-inserting the same script won't\n // re-execute it, so changing authJwt after the first load has no effect.\n const authJwtRef = useRef(authJwt);\n authJwtRef.current = authJwt;\n\n useEffect(() => {\n if (!subdomain) return;\n\n // Don't add a duplicate script\n if (document.getElementById(SCRIPT_ID)) return;\n\n const script = document.createElement(\"script\");\n script.id = SCRIPT_ID;\n script.src = SCRIPT_SRC;\n script.type = \"module\";\n script.crossOrigin = \"anonymous\";\n script.dataset.fluidShop = subdomain;\n if (authJwtRef.current) {\n script.dataset.authJwt = authJwtRef.current;\n }\n document.head.appendChild(script);\n\n // Suppress the SDK's auto-injected lead capture widget.\n // The SDK skips injection when it finds an existing element with hide-widget.\n const leadCapture = document.createElement(\"fluid-lead-capture\");\n leadCapture.id = LEAD_CAPTURE_ID;\n leadCapture.setAttribute(\"hide-widget\", \"true\");\n document.body.appendChild(leadCapture);\n\n return () => {\n const existing = document.getElementById(SCRIPT_ID);\n if (existing) existing.remove();\n const existingLeadCapture = document.getElementById(LEAD_CAPTURE_ID);\n if (existingLeadCapture) existingLeadCapture.remove();\n };\n }, [subdomain]);\n\n return null;\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ninterface CartWidgetProps {\n theme?: Record<string, string>;\n}\n\nexport default function CartWidget({\n theme,\n}: CartWidgetProps): React.ReactNode {\n const [mounted, setMounted] = useState(false);\n const widgetRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n useEffect(() => {\n if (!mounted) return;\n const el = widgetRef.current;\n if (!el) return;\n if (theme) {\n el.setAttribute(\"theme\", JSON.stringify(theme));\n } else {\n el.removeAttribute(\"theme\");\n }\n }, [theme, mounted]);\n\n const widget = React.createElement(\"fluid-cart-widget\", {\n ref: (el: HTMLElement | null) => {\n widgetRef.current = el;\n },\n \"data-fluid-widget\": \"true\",\n \"hide-widget\": \"true\",\n \"is-primary\": \"true\",\n });\n\n // Portal to document.body so the cart drawer escapes any\n // overflow-hidden / isolation stacking contexts in the layout.\n if (mounted) {\n return createPortal(widget, document.body);\n }\n\n return null;\n}\n","\"use client\";\n\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { ShoppingCart } from \"lucide-react\";\nimport React, { useEffect, useCallback, useState } from \"react\";\n\ndeclare global {\n interface Window {\n FluidCommerceSDK?: {\n getCheckoutUrl: () => string;\n setOnCheckout: (callback: () => void) => void;\n };\n FairShareSDK?: {\n getCartItemCount: () => number;\n };\n fluidCart?: {\n open: () => void;\n };\n }\n}\n\ninterface CartButtonProps {\n onCheckout?: (checkoutUrl: string) => void;\n}\n\nconst MAX_SDK_POLL_ATTEMPTS = 50; // 5 seconds at 100ms intervals\n\nexport function CartButton({ onCheckout }: CartButtonProps): React.ReactNode {\n const [initialCount, setInitialCount] = useState(0);\n\n const navigateToCheckout = useCallback(() => {\n if (!window.FluidCommerceSDK) {\n console.error(\"FluidCommerceSDK not available\");\n return;\n }\n\n try {\n const checkoutUrl = window.FluidCommerceSDK.getCheckoutUrl();\n if (!checkoutUrl) {\n console.error(\"No checkout URL available\");\n return;\n }\n onCheckout?.(checkoutUrl);\n } catch (error) {\n console.error(\"Error getting checkout URL:\", error);\n }\n }, [onCheckout]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let attempts = 0;\n let cancelled = false;\n\n const setupSDK = () => {\n if (cancelled) return;\n if (window.FluidCommerceSDK) {\n if (onCheckout) {\n window.FluidCommerceSDK.setOnCheckout(navigateToCheckout);\n }\n const count = window.FairShareSDK?.getCartItemCount?.();\n if (count != null) {\n setInitialCount(count);\n }\n } else if (attempts < MAX_SDK_POLL_ATTEMPTS) {\n attempts++;\n timeoutId = setTimeout(setupSDK, 100);\n }\n };\n\n setupSDK();\n return () => {\n cancelled = true;\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [navigateToCheckout, onCheckout]);\n\n return (\n <Button\n className=\"bg-primary text-primary-foreground hover:bg-primary-700 relative flex items-center gap-4 rounded-sm px-4 py-1.5\"\n onClick={() => {\n window.fluidCart?.open();\n }}\n >\n <div className=\"relative\">\n <ShoppingCart className=\"size-5\" />\n <span\n id=\"fluid-cart-count\"\n className=\"bg-primary-foreground text-primary absolute -top-1 -right-2 flex h-4 w-4 items-center justify-center rounded-full text-[8px] font-bold\"\n >\n {initialCount}\n </span>\n </div>\n <span>Cart</span>\n </Button>\n );\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ninterface ShopContainerProps {\n children: React.ReactNode;\n className?: string;\n cartScript?: React.ReactNode;\n cartWidget?: React.ReactNode;\n}\n\nexport default function ShopContainer({\n children,\n className = \"\",\n cartScript,\n cartWidget,\n}: ShopContainerProps): React.ReactNode {\n const containerRef = useRef<HTMLDivElement>(null);\n const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(\n null,\n );\n\n useEffect(() => {\n const currentContainer = containerRef.current;\n if (!currentContainer) return;\n\n const reactContentWrapper = document.createElement(\"div\");\n reactContentWrapper.id = \"react-content-wrapper\";\n reactContentWrapper.style.cssText = `\n position: relative;\n `;\n reactContentWrapper.className = \"min-h-full\";\n\n currentContainer.appendChild(reactContentWrapper);\n\n setPortalContainer(reactContentWrapper);\n\n return () => {\n if (currentContainer && reactContentWrapper) {\n try {\n currentContainer.removeChild(reactContentWrapper);\n } catch (e) {\n console.warn(\"Failed to cleanup isolated container:\", e);\n }\n }\n setPortalContainer(null);\n };\n }, []);\n\n return (\n <>\n <div\n ref={containerRef}\n className={`isolated-shop-wrapper ${className} h-full`}\n >\n {portalContainer &&\n createPortal(\n <>\n {cartScript}\n {cartWidget}\n {children}\n </>,\n portalContainer,\n )}\n </div>\n </>\n );\n}\n","import { type ComponentProps, useMemo } from \"react\";\nimport ShopApp from \"@fluid-app/shop-ui/components/shop-app\";\nimport { ProductsCoreProvider } from \"@fluid-app/products-core\";\nimport {\n CartButton,\n CartScript,\n CartWidget,\n ShopContainer,\n} from \"@fluid-app/cart-ui\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n} from \"@fluid-app/ui-primitives\";\nimport {\n useScreenHeaderBreadcrumbs,\n useScreenHeaderActions,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\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 { useFluidAuth } from \"../hooks/use-fluid-auth\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useSdkClient } from \"../account/use-account-clients\";\n\ntype ShopScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function ShopScreen({\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}: ShopScreenProps): React.JSX.Element {\n const { config } = useFluidContext();\n const domainClient = useSdkClient();\n const { token } = useFluidAuth();\n const { data: userData } = useCurrentUser();\n const { currentSlug, navigate } = useAppNavigation();\n const countryCode = config.countryIso ?? userData?.country?.iso ?? \"US\";\n const subdomain = userData?.company?.subdomain;\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">Shop</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const headerActions = useMemo(\n () => (\n <div className=\"flex items-center gap-4\">\n <CartButton />\n </div>\n ),\n [],\n );\n useScreenHeaderActions(headerActions);\n\n // Parse product ID from slug: \"shop/{productId}\"\n const parts = currentSlug.split(\"/\");\n const productId = parts[1] ?? null;\n\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <ShopContainer\n cartScript={\n subdomain ? (\n <CartScript subdomain={subdomain} authJwt={token ?? undefined} />\n ) : null\n }\n cartWidget={<CartWidget />}\n >\n <ProductsCoreProvider client={domainClient}>\n <ShopApp\n countryCode={countryCode}\n companyLogoUrl={userData?.company?.logo_url}\n productId={productId}\n onSelectProduct={(id) => navigate(`shop/${id}`)}\n onBack={() => navigate(\"shop\")}\n />\n </ProductsCoreProvider>\n </ShopContainer>\n </div>\n );\n}\n\nexport const shopScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShopScreen\",\n displayName: \"Shop Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
|