@fluid-app/portal-sdk 0.1.162 → 0.1.164

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/{MySiteScreen-g8Esyofu.mjs → MySiteScreen-CG2DnzQH.mjs} +2 -2
  2. package/dist/{MySiteScreen-g8Esyofu.mjs.map → MySiteScreen-CG2DnzQH.mjs.map} +1 -1
  3. package/dist/{MySiteScreen-Cos0jTa_.cjs → MySiteScreen-DRMrEMEI.cjs} +2 -2
  4. package/dist/{MySiteScreen-Xcg07a3M.cjs → MySiteScreen-Lr2SbrXz.cjs} +2 -2
  5. package/dist/{MySiteScreen-Xcg07a3M.cjs.map → MySiteScreen-Lr2SbrXz.cjs.map} +1 -1
  6. package/dist/{OrdersScreen-DZyOBKmU.cjs → OrdersScreen-B6n41CbG.cjs} +1 -1
  7. package/dist/{OrdersScreen-ZGUm8buk.cjs → OrdersScreen-BKCQdz5A.cjs} +55 -56
  8. package/dist/OrdersScreen-BKCQdz5A.cjs.map +1 -0
  9. package/dist/{OrdersScreen-DeLoyVGI.mjs → OrdersScreen-CFRVfzez.mjs} +55 -56
  10. package/dist/OrdersScreen-CFRVfzez.mjs.map +1 -0
  11. package/dist/{use-portal-shareables-api-DRK9Y5dp.mjs → PortalContentApiProvider-CW0ADhPi.mjs} +285 -94
  12. package/dist/PortalContentApiProvider-CW0ADhPi.mjs.map +1 -0
  13. package/dist/{use-portal-shareables-api-DcjYlAOy.cjs → PortalContentApiProvider-Di5emtYd.cjs} +290 -129
  14. package/dist/PortalContentApiProvider-Di5emtYd.cjs.map +1 -0
  15. package/dist/PortalProductsApiProvider-BCXX9NGK.mjs +780 -0
  16. package/dist/PortalProductsApiProvider-BCXX9NGK.mjs.map +1 -0
  17. package/dist/PortalProductsApiProvider-BquMHwvt.cjs +816 -0
  18. package/dist/PortalProductsApiProvider-BquMHwvt.cjs.map +1 -0
  19. package/dist/{ProductsScreen-B2SKzTE4.cjs → ProductsScreen-C6eNgxjP.cjs} +4 -3
  20. package/dist/{ProductsScreen-D6eoU86k.mjs → ProductsScreen-CVNJudq9.mjs} +39 -44
  21. package/dist/ProductsScreen-CVNJudq9.mjs.map +1 -0
  22. package/dist/{ProductsScreen-DbwSCY7G.cjs → ProductsScreen-KjjhlDGo.cjs} +39 -44
  23. package/dist/ProductsScreen-KjjhlDGo.cjs.map +1 -0
  24. package/dist/{ProductsScreen-BGah2tcD.mjs → ProductsScreen-nHmUftQn.mjs} +4 -3
  25. package/dist/{ShareablesScreen-DRUT-yoi.mjs → ShareablesScreen-CdTyyDRO.mjs} +4 -3
  26. package/dist/{ShareablesScreen-Dy04EXe5.cjs → ShareablesScreen-DUzo8kRi.cjs} +4 -3
  27. package/dist/ShareablesScreen-DpEP_6u0.mjs +132 -0
  28. package/dist/ShareablesScreen-DpEP_6u0.mjs.map +1 -0
  29. package/dist/ShareablesScreen-Dz8w2l3e.cjs +144 -0
  30. package/dist/ShareablesScreen-Dz8w2l3e.cjs.map +1 -0
  31. package/dist/{ShopScreen-CQ48b1c8.mjs → ShopScreen-BH6zQndJ.mjs} +10 -724
  32. package/dist/ShopScreen-BH6zQndJ.mjs.map +1 -0
  33. package/dist/{ShopScreen-CFqoT4IC.cjs → ShopScreen-BUXUtEuj.cjs} +13 -727
  34. package/dist/ShopScreen-BUXUtEuj.cjs.map +1 -0
  35. package/dist/{ShopScreen-B7faQx84.cjs → ShopScreen-BbucUNI7.cjs} +2 -1
  36. package/dist/{SubscriptionsScreen-C2iORyT_.cjs → SubscriptionsScreen-BdGF5OLE.cjs} +408 -409
  37. package/dist/SubscriptionsScreen-BdGF5OLE.cjs.map +1 -0
  38. package/dist/{SubscriptionsScreen-ClWrrqPK.cjs → SubscriptionsScreen-Cwa2lR1D.cjs} +1 -1
  39. package/dist/{SubscriptionsScreen-BfdK8067.mjs → SubscriptionsScreen-Dn3AEUJi.mjs} +408 -409
  40. package/dist/SubscriptionsScreen-Dn3AEUJi.mjs.map +1 -0
  41. package/dist/{dist-lO2OG0T5.cjs → dist-BF_4vk1z.cjs} +1 -1
  42. package/dist/{dist-lO2OG0T5.cjs.map → dist-BF_4vk1z.cjs.map} +1 -1
  43. package/dist/index.cjs +21 -20
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts.map +1 -1
  46. package/dist/index.d.mts.map +1 -1
  47. package/dist/index.mjs +21 -20
  48. package/dist/index.mjs.map +1 -1
  49. package/dist/{sortable.esm-DSrWP4x9.mjs → sortable.esm-E6JdQn7I.mjs} +1 -1
  50. package/dist/{sortable.esm-DSrWP4x9.mjs.map → sortable.esm-E6JdQn7I.mjs.map} +1 -1
  51. package/package.json +17 -17
  52. package/dist/OrdersScreen-DeLoyVGI.mjs.map +0 -1
  53. package/dist/OrdersScreen-ZGUm8buk.cjs.map +0 -1
  54. package/dist/ProductsScreen-D6eoU86k.mjs.map +0 -1
  55. package/dist/ProductsScreen-DbwSCY7G.cjs.map +0 -1
  56. package/dist/ShareablesScreen-DPHZMh-V.cjs +0 -391
  57. package/dist/ShareablesScreen-DPHZMh-V.cjs.map +0 -1
  58. package/dist/ShareablesScreen-DrQDQ1-U.mjs +0 -379
  59. package/dist/ShareablesScreen-DrQDQ1-U.mjs.map +0 -1
  60. package/dist/ShopScreen-CFqoT4IC.cjs.map +0 -1
  61. package/dist/ShopScreen-CQ48b1c8.mjs.map +0 -1
  62. package/dist/SubscriptionsScreen-BfdK8067.mjs.map +0 -1
  63. package/dist/SubscriptionsScreen-C2iORyT_.cjs.map +0 -1
  64. package/dist/use-portal-products-client-BUFD20ZY.mjs +0 -65
  65. package/dist/use-portal-products-client-BUFD20ZY.mjs.map +0 -1
  66. package/dist/use-portal-products-client-DTkFvOal.cjs +0 -71
  67. package/dist/use-portal-products-client-DTkFvOal.cjs.map +0 -1
  68. package/dist/use-portal-shareables-api-DRK9Y5dp.mjs.map +0 -1
  69. package/dist/use-portal-shareables-api-DcjYlAOy.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortalProductsApiProvider-BquMHwvt.cjs","names":["usePortalTenantClient"],"sources":["../../../products/core/src/products-api-context.tsx","../../../products/core/src/portal-products-api-context.tsx","../../../products/core/src/hooks/use-debounce.ts","../../../products/core/src/hooks/use-portal-products.ts","../../../products/core/src/hooks/use-portal-product-catalog.ts","../../../products/core/src/hooks/use-portal-product-detail.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","../../../products/core/src/utils/product-price.ts","../src/adapters/products-api-adapter.ts","../src/products/PortalProductsApiProvider.tsx"],"sourcesContent":["import { createContext, useContext, type JSX, type ReactNode } from \"react\";\nimport type { ProductsApi } from \"./products-api\";\n\ninterface ProductsCoreConfig {\n api: ProductsApi;\n}\n\nconst ProductsCoreContext = createContext<ProductsCoreConfig | null>(null);\n\nexport function ProductsCoreProvider({\n api,\n children,\n}: ProductsCoreConfig & { children: ReactNode }): JSX.Element {\n return (\n <ProductsCoreContext.Provider value={{ api }}>\n {children}\n </ProductsCoreContext.Provider>\n );\n}\n\nexport function useProductsApi(): ProductsApi {\n const ctx = useContext(ProductsCoreContext);\n if (!ctx) {\n throw new Error(\n \"useProductsApi must be used within a <ProductsCoreProvider>\",\n );\n }\n return ctx.api;\n}\n","import { createContext, useContext, type JSX, type ReactNode } from \"react\";\nimport type { PortalProductsApi } from \"./portal-products-api\";\n\ninterface PortalProductsCoreConfig {\n api: PortalProductsApi;\n}\n\nconst PortalProductsCoreContext =\n createContext<PortalProductsCoreConfig | null>(null);\n\nexport function PortalProductsCoreProvider({\n api,\n children,\n}: PortalProductsCoreConfig & { children: ReactNode }): JSX.Element {\n return (\n <PortalProductsCoreContext.Provider value={{ api }}>\n {children}\n </PortalProductsCoreContext.Provider>\n );\n}\n\nexport function usePortalProductsApi(): PortalProductsApi {\n const ctx = useContext(PortalProductsCoreContext);\n if (!ctx) {\n throw new Error(\n \"usePortalProductsApi must be used within a <PortalProductsCoreProvider>\",\n );\n }\n return ctx.api;\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 { useQuery } from \"@tanstack/react-query\";\nimport type { portalProducts } from \"../portal-products-api\";\nimport { usePortalProductsApi } from \"../portal-products-api-context\";\n\nconst portalProductKeys = {\n all: [\"portal-products\"] as const,\n list: (params?: portalProducts.CursorPaginationParams) =>\n [...portalProductKeys.all, \"list\", params] as const,\n detail: (id: string | number) =>\n [...portalProductKeys.all, \"detail\", String(id)] as const,\n search: (query: string, params?: portalProducts.CursorPaginationParams) =>\n [...portalProductKeys.all, \"search\", query, params] as const,\n media: (productId: string | number) =>\n [...portalProductKeys.all, \"media\", String(productId)] as const,\n};\n\nexport { portalProductKeys };\n\nexport function usePortalProducts(\n params?: portalProducts.CursorPaginationParams,\n) {\n const api = usePortalProductsApi();\n return useQuery({\n queryKey: portalProductKeys.list(params),\n queryFn: () => api.listProducts(params),\n });\n}\n\nexport function usePortalProduct(\n id: string | number,\n options?: { enabled?: boolean },\n) {\n const api = usePortalProductsApi();\n return useQuery({\n queryKey: portalProductKeys.detail(id),\n queryFn: () => api.getProduct(id),\n enabled: options?.enabled,\n });\n}\n\nexport function usePortalProductSearch(\n query: string,\n params?: portalProducts.CursorPaginationParams & { enabled?: boolean },\n) {\n const api = usePortalProductsApi();\n const { enabled, ...paginationParams } = params ?? {};\n return useQuery({\n queryKey: portalProductKeys.search(query, paginationParams),\n queryFn: () => api.searchProducts(query, paginationParams),\n enabled: enabled ?? query.length > 0,\n });\n}\n\nexport function usePortalProductMedia(\n productId: string | number,\n options?: { enabled?: boolean },\n) {\n const api = usePortalProductsApi();\n return useQuery({\n queryKey: portalProductKeys.media(productId),\n queryFn: () => api.getProductMedia(productId),\n enabled: options?.enabled,\n });\n}\n","import { useState, useMemo, useCallback } from \"react\";\nimport type { portalProducts } from \"../portal-products-api\";\nimport { usePortalProductsApi } from \"../portal-products-api-context\";\nimport { useDebounce } from \"./use-debounce\";\n\nexport interface UsePortalProductCatalogParams {\n perPage?: number;\n}\n\n/** Page param is a cursor string from the BFF, or undefined for the first page. */\nexport type PortalProductPageParam = string | undefined;\n\nexport function usePortalProductCatalog({\n perPage = 25,\n}: UsePortalProductCatalogParams = {}) {\n const api = usePortalProductsApi();\n const [searchTerm, setSearchTerm] = useState(\"\");\n const debouncedSearchTerm = useDebounce(searchTerm, 300);\n const [currentSort, setCurrentSort] = useState<string>(\"created_at_desc\");\n\n const fetchProducts = useCallback(\n async (\n pageParam?: PortalProductPageParam,\n ): Promise<portalProducts.PortalProductsResponse> => {\n const cursor = pageParam;\n\n if (debouncedSearchTerm) {\n return api.searchProducts(debouncedSearchTerm, {\n cursor,\n limit: perPage,\n sort: currentSort,\n });\n }\n return api.listProducts({\n cursor,\n limit: perPage,\n sort: currentSort,\n });\n },\n [api, debouncedSearchTerm, perPage, currentSort],\n );\n\n const getNextPageParam = useCallback(\n (\n lastPage: portalProducts.PortalProductsResponse,\n _allPages: portalProducts.PortalProductsResponse[],\n lastPageParam: PortalProductPageParam,\n ): PortalProductPageParam => {\n const nextCursor = lastPage.meta?.pagination?.next_cursor ?? undefined;\n // Stop if the API returned the same cursor we just sent (prevents\n // infinite refetch loops when the backend doesn't advance).\n if (nextCursor != null && nextCursor === lastPageParam) {\n return undefined;\n }\n return nextCursor;\n },\n [],\n );\n\n const queryKey = useMemo(\n () => [\n \"portal-product-catalog\",\n debouncedSearchTerm || \"\",\n perPage,\n currentSort,\n ],\n [debouncedSearchTerm, perPage, currentSort],\n );\n\n return {\n searchTerm,\n setSearchTerm,\n debouncedSearchTerm,\n currentSort,\n setCurrentSort,\n fetchProducts,\n getNextPageParam,\n queryKey,\n perPage,\n };\n}\n","import { useMemo } from \"react\";\nimport { usePortalProduct } from \"./use-portal-products\";\n\nexport interface UsePortalProductDetailParams {\n productId: string;\n}\n\nexport function usePortalProductDetail({\n productId,\n}: UsePortalProductDetailParams) {\n const {\n data: productResponse,\n isLoading,\n error,\n } = usePortalProduct(productId);\n\n const product = productResponse?.product;\n\n const images = useMemo(() => {\n if (!product?.images) return [];\n return product.images.map((img, idx) => ({\n id: idx,\n url: img.url ?? \"\",\n alt: img.alt ?? null,\n }));\n }, [product?.images]);\n\n return {\n product,\n isLoading,\n error,\n images,\n };\n}\n","import type { products } from \"../types\";\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 \"../types\";\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 \"../types\";\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","import type { products } from \"../types\";\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 type {\n PortalProductsApi,\n portalProducts,\n} from \"@fluid-app/products-core\";\nimport type { FetchClient } from \"@fluid-app/portal-tenant-api-client\";\nimport { portalTenant } from \"@fluid-app/portal-tenant-api-client\";\n\n/**\n * Creates a PortalProductsApi-compatible adapter from a portal-tenant FetchClient.\n *\n * This bridges the auto-generated portal-tenant API client to the abstract\n * PortalProductsApi interface defined in @fluid-app/products-core, closing\n * over the FetchClient so consumers don't need to pass it per-call.\n *\n * Each method maps the BFF response to the port type at runtime rather than\n * using type assertions, so TypeScript catches schema drift between the\n * generated client and the hand-authored port types at compile time.\n */\nexport function createPortalProductsApiAdapter(\n client: FetchClient,\n): PortalProductsApi {\n return {\n listProducts: async (params) => {\n const response = await portalTenant.products_list(client, {\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n sort: params?.sort as\n | \"title_asc\"\n | \"title_desc\"\n | \"price_asc\"\n | \"price_desc\"\n | \"created_at_asc\"\n | \"created_at_desc\"\n | undefined,\n });\n return {\n products: response.products ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductsResponse;\n },\n\n getProduct: async (id) => {\n const response = await portalTenant.products_show(client, id);\n return {\n product: response.product ?? {},\n meta: response.meta,\n } satisfies portalProducts.PortalProductResponse;\n },\n\n searchProducts: async (query, params) => {\n const response = await portalTenant.products_search(client, {\n q: query,\n \"page[cursor]\": params?.cursor,\n \"page[limit]\": params?.limit,\n });\n return {\n products: response.products ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductsResponse;\n },\n\n getProductMedia: async (productId) => {\n const response = await portalTenant.products_media_list(\n client,\n productId,\n );\n return {\n media: response.media ?? [],\n meta: response.meta,\n } satisfies portalProducts.PortalProductMediaResponse;\n },\n };\n}\n","import { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport { PortalProductsCoreProvider } from \"@fluid-app/products-core\";\nimport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nimport { createPortalProductsApiAdapter } from \"../adapters/products-api-adapter\";\n\nexport function PortalProductsApiProvider({\n children,\n}: {\n children: ReactNode;\n}): ReactElement {\n const client = usePortalTenantClient();\n\n const api = useMemo(() => createPortalProductsApiAdapter(client), [client]);\n\n return (\n <PortalProductsCoreProvider api={api}>\n {children}\n </PortalProductsCoreProvider>\n );\n}\n"],"mappings":"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"}
@@ -6,8 +6,9 @@ require("./dist-thaj08s5.cjs");
6
6
  require("./es-xQF-WIMq.cjs");
7
7
  require("./SearchSort-CYhjl8tC.cjs");
8
8
  require("./dist-BSKQ_GoC.cjs");
9
- require("./use-portal-shareables-api-DcjYlAOy.cjs");
10
- require("./dist-lO2OG0T5.cjs");
11
- const require_ProductsScreen = require("./ProductsScreen-DbwSCY7G.cjs");
9
+ require("./PortalContentApiProvider-Di5emtYd.cjs");
10
+ require("./dist-BF_4vk1z.cjs");
11
+ require("./PortalProductsApiProvider-BquMHwvt.cjs");
12
+ const require_ProductsScreen = require("./ProductsScreen-KjjhlDGo.cjs");
12
13
  exports.ProductsScreen = require_ProductsScreen.ProductsScreen;
13
14
  exports.productsScreenPropertySchema = require_ProductsScreen.productsScreenPropertySchema;
@@ -1,12 +1,20 @@
1
1
  import { t as useAccount } from "./use-account-BJhzTl2j.mjs";
2
2
  import { n as useAppNavigation } from "./AppNavigationContext-D29xW9Ua.mjs";
3
- import { d as ShareablesCoreProvider, l as ShareablesUIProvider, s as ProductsApp, t as usePortalShareablesApi, u as ShareablesApiProvider } from "./use-portal-shareables-api-DRK9Y5dp.mjs";
3
+ import { a as ShareablesUIProvider, n as usePortalContentContext, o as ShareablesCoreProvider, r as ProductsApp, t as PortalContentApiProvider } from "./PortalContentApiProvider-CW0ADhPi.mjs";
4
+ import { t as PortalProductsApiProvider } from "./PortalProductsApiProvider-BCXX9NGK.mjs";
4
5
  import { useCallback, useMemo } from "react";
5
6
  import { jsx } from "react/jsx-runtime";
6
7
  //#region src/screens/ProductsScreen.tsx
7
8
  function ProductsScreen({ background, textColor, accentColor, padding, borderRadius, ...divProps }) {
8
- const shareablesApi = usePortalShareablesApi();
9
- const { productsApi: portalProductsApi } = shareablesApi;
9
+ return /* @__PURE__ */ jsx("div", {
10
+ ...divProps,
11
+ className: `h-full ${divProps.className ?? ""}`,
12
+ children: /* @__PURE__ */ jsx(PortalProductsApiProvider, { children: /* @__PURE__ */ jsx(PortalContentApiProvider, { children: /* @__PURE__ */ jsx(ProductsScreenContent, {}) }) })
13
+ });
14
+ }
15
+ /** Inner component rendered inside providers so hooks can access context. */
16
+ function ProductsScreenContent() {
17
+ const { productsApi: portalProductsApi } = usePortalContentContext();
10
18
  const { data: account } = useAccount();
11
19
  const { currentSlug, navigate } = useAppNavigation();
12
20
  const fetchProducts = useCallback(async (search, cursor, limit) => {
@@ -29,46 +37,33 @@ function ProductsScreen({ background, textColor, accentColor, padding, borderRad
29
37
  const handleBack = useCallback(() => {
30
38
  navigate("products");
31
39
  }, [navigate]);
32
- const coreConfig = useMemo(() => ({
33
- user: account ? { id: account.id } : null,
34
- repContext: true
35
- }), [account]);
36
- const uiConfig = useMemo(() => ({
37
- user: account ? {
38
- id: account.id,
39
- company: null
40
- } : void 0,
41
- basePath: "",
42
- navigate: (path) => {
43
- navigate(path);
44
- },
45
- showToast: (opts) => {
46
- console.log(`[Products] ${opts.type}: ${opts.title}`);
47
- }
48
- }), [account, navigate]);
49
- return /* @__PURE__ */ jsx("div", {
50
- ...divProps,
51
- className: `h-full ${divProps.className ?? ""}`,
52
- children: /* @__PURE__ */ jsx(ShareablesCoreProvider, {
53
- config: coreConfig,
54
- children: /* @__PURE__ */ jsx(ShareablesApiProvider, {
55
- media: shareablesApi.media,
56
- playlists: shareablesApi.playlists,
57
- fileResources: shareablesApi.fileResources,
58
- share: shareablesApi.share,
59
- productMedia: shareablesApi.productMedia,
60
- children: /* @__PURE__ */ jsx(ShareablesUIProvider, {
61
- config: uiConfig,
62
- children: /* @__PURE__ */ jsx(ProductsApp, {
63
- countryCode: void 0,
64
- companyLogoUrl: void 0,
65
- fetchProducts,
66
- fetchProduct,
67
- productId,
68
- onSelectProduct: handleSelectProduct,
69
- onBack: handleBack
70
- })
71
- })
40
+ return /* @__PURE__ */ jsx(ShareablesCoreProvider, {
41
+ config: useMemo(() => ({
42
+ user: account ? { id: account.id } : null,
43
+ repContext: true
44
+ }), [account]),
45
+ children: /* @__PURE__ */ jsx(ShareablesUIProvider, {
46
+ config: useMemo(() => ({
47
+ user: account ? {
48
+ id: account.id,
49
+ company: null
50
+ } : void 0,
51
+ basePath: "",
52
+ navigate: (path) => {
53
+ navigate(path);
54
+ },
55
+ showToast: (opts) => {
56
+ console.log(`[Products] ${opts.type}: ${opts.title}`);
57
+ }
58
+ }), [account, navigate]),
59
+ children: /* @__PURE__ */ jsx(ProductsApp, {
60
+ countryCode: void 0,
61
+ companyLogoUrl: void 0,
62
+ fetchProducts,
63
+ fetchProduct,
64
+ productId,
65
+ onSelectProduct: handleSelectProduct,
66
+ onBack: handleBack
72
67
  })
73
68
  })
74
69
  });
@@ -85,4 +80,4 @@ const productsScreenPropertySchema = {
85
80
  //#endregion
86
81
  export { productsScreenPropertySchema as n, ProductsScreen as t };
87
82
 
88
- //# sourceMappingURL=ProductsScreen-D6eoU86k.mjs.map
83
+ //# sourceMappingURL=ProductsScreen-CVNJudq9.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductsScreen-CVNJudq9.mjs","names":[],"sources":["../src/screens/ProductsScreen.tsx"],"sourcesContent":["import { useCallback, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { ShareablesCoreProvider } from \"@fluid-app/shareables-core\";\nimport { ShareablesUIProvider, ProductsApp } from \"@fluid-app/shareables-ui\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { PortalProductsApiProvider } from \"../products/PortalProductsApiProvider\";\nimport {\n PortalContentApiProvider,\n usePortalContentContext,\n} from \"../content/PortalContentApiProvider\";\n\ntype ProductsScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function ProductsScreen({\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}: ProductsScreenProps): React.JSX.Element {\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <PortalProductsApiProvider>\n <PortalContentApiProvider>\n <ProductsScreenContent />\n </PortalContentApiProvider>\n </PortalProductsApiProvider>\n </div>\n );\n}\n\n/** Inner component rendered inside providers so hooks can access context. */\nfunction ProductsScreenContent(): React.JSX.Element {\n const { productsApi: portalProductsApi } = usePortalContentContext();\n const { data: account } = useAccount();\n const { currentSlug, navigate } = useAppNavigation();\n\n const fetchProducts = useCallback(\n async (search: string, cursor?: string, limit?: number) => {\n if (search) {\n return portalProductsApi.searchProducts(search, { cursor, limit });\n }\n return portalProductsApi.listProducts({ cursor, limit });\n },\n [portalProductsApi],\n );\n\n const fetchProduct = useCallback(\n async (id: string | number) => portalProductsApi.getProduct(id),\n [portalProductsApi],\n );\n\n // Extract product ID from slug: \"products/123\" → \"123\"\n const productId = useMemo(() => {\n const match = currentSlug.match(/^products\\/(.+)/);\n return match?.[1] ?? null;\n }, [currentSlug]);\n\n const handleSelectProduct = useCallback(\n (id: string) => {\n navigate(`products/${id}`);\n },\n [navigate],\n );\n\n const handleBack = useCallback(() => {\n navigate(\"products\");\n }, [navigate]);\n\n const coreConfig = useMemo(\n () => ({\n user: account ? { id: account.id } : null,\n repContext: true,\n }),\n [account],\n );\n\n const uiConfig = useMemo(\n () => ({\n user: account\n ? {\n id: account.id,\n company: null, // TODO(portal-tenant): company branding not available from /api/account\n }\n : undefined,\n basePath: \"\",\n navigate: (path: string) => {\n navigate(path);\n },\n showToast: (opts: {\n title: string;\n type: \"success\" | \"error\" | \"warning\";\n }) => {\n console.log(`[Products] ${opts.type}: ${opts.title}`);\n },\n }),\n [account, navigate],\n );\n\n return (\n <ShareablesCoreProvider config={coreConfig}>\n <ShareablesUIProvider config={uiConfig}>\n <ProductsApp\n countryCode={undefined}\n companyLogoUrl={undefined}\n fetchProducts={fetchProducts}\n fetchProduct={fetchProduct}\n productId={productId}\n onSelectProduct={handleSelectProduct}\n onBack={handleBack}\n />\n </ShareablesUIProvider>\n </ShareablesCoreProvider>\n );\n}\n\nexport const productsScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ProductsScreen\",\n displayName: \"Products Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;AA0BA,SAAgB,eAAe,EAE7B,YACA,WACA,aACA,SACA,cAEA,GAAG,YACsC;AACzC,QACE,oBAAC,OAAD;EAAK,GAAI;EAAU,WAAW,UAAU,SAAS,aAAa;YAC5D,oBAAC,2BAAD,EAAA,UACE,oBAAC,0BAAD,EAAA,UACE,oBAAC,uBAAD,EAAyB,CAAA,EACA,CAAA,EACD,CAAA;EACxB,CAAA;;;AAKV,SAAS,wBAA2C;CAClD,MAAM,EAAE,aAAa,sBAAsB,yBAAyB;CACpE,MAAM,EAAE,MAAM,YAAY,YAAY;CACtC,MAAM,EAAE,aAAa,aAAa,kBAAkB;CAEpD,MAAM,gBAAgB,YACpB,OAAO,QAAgB,QAAiB,UAAmB;AACzD,MAAI,OACF,QAAO,kBAAkB,eAAe,QAAQ;GAAE;GAAQ;GAAO,CAAC;AAEpE,SAAO,kBAAkB,aAAa;GAAE;GAAQ;GAAO,CAAC;IAE1D,CAAC,kBAAkB,CACpB;CAED,MAAM,eAAe,YACnB,OAAO,OAAwB,kBAAkB,WAAW,GAAG,EAC/D,CAAC,kBAAkB,CACpB;CAGD,MAAM,YAAY,cAAc;AAE9B,SADc,YAAY,MAAM,kBAAkB,GACnC,MAAM;IACpB,CAAC,YAAY,CAAC;CAEjB,MAAM,sBAAsB,aACzB,OAAe;AACd,WAAS,YAAY,KAAK;IAE5B,CAAC,SAAS,CACX;CAED,MAAM,aAAa,kBAAkB;AACnC,WAAS,WAAW;IACnB,CAAC,SAAS,CAAC;AAgCd,QACE,oBAAC,wBAAD;EAAwB,QA/BP,eACV;GACL,MAAM,UAAU,EAAE,IAAI,QAAQ,IAAI,GAAG;GACrC,YAAY;GACb,GACD,CAAC,QAAQ,CACV;YA0BG,oBAAC,sBAAD;GAAsB,QAxBT,eACR;IACL,MAAM,UACF;KACE,IAAI,QAAQ;KACZ,SAAS;KACV,GACD,KAAA;IACJ,UAAU;IACV,WAAW,SAAiB;AAC1B,cAAS,KAAK;;IAEhB,YAAY,SAGN;AACJ,aAAQ,IAAI,cAAc,KAAK,KAAK,IAAI,KAAK,QAAQ;;IAExD,GACD,CAAC,SAAS,SAAS,CACpB;aAKK,oBAAC,aAAD;IACE,aAAa,KAAA;IACb,gBAAgB,KAAA;IACD;IACD;IACH;IACX,iBAAiB;IACjB,QAAQ;IACR,CAAA;GACmB,CAAA;EACA,CAAA;;AAI7B,MAAa,+BAAqD;CAChE,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ,EAAE;CACX"}
@@ -1,13 +1,21 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
2
  const require_use_account = require("./use-account-CrgtI83z.cjs");
3
3
  const require_AppNavigationContext = require("./AppNavigationContext-BiYNDj_X.cjs");
4
- const require_use_portal_shareables_api = require("./use-portal-shareables-api-DcjYlAOy.cjs");
4
+ const require_PortalContentApiProvider = require("./PortalContentApiProvider-Di5emtYd.cjs");
5
+ const require_PortalProductsApiProvider = require("./PortalProductsApiProvider-BquMHwvt.cjs");
5
6
  let react = require("react");
6
7
  let react_jsx_runtime = require("react/jsx-runtime");
7
8
  //#region src/screens/ProductsScreen.tsx
8
9
  function ProductsScreen({ background, textColor, accentColor, padding, borderRadius, ...divProps }) {
9
- const shareablesApi = require_use_portal_shareables_api.usePortalShareablesApi();
10
- const { productsApi: portalProductsApi } = shareablesApi;
10
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11
+ ...divProps,
12
+ className: `h-full ${divProps.className ?? ""}`,
13
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PortalProductsApiProvider.PortalProductsApiProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PortalContentApiProvider.PortalContentApiProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProductsScreenContent, {}) }) })
14
+ });
15
+ }
16
+ /** Inner component rendered inside providers so hooks can access context. */
17
+ function ProductsScreenContent() {
18
+ const { productsApi: portalProductsApi } = require_PortalContentApiProvider.usePortalContentContext();
11
19
  const { data: account } = require_use_account.useAccount();
12
20
  const { currentSlug, navigate } = require_AppNavigationContext.useAppNavigation();
13
21
  const fetchProducts = (0, react.useCallback)(async (search, cursor, limit) => {
@@ -30,46 +38,33 @@ function ProductsScreen({ background, textColor, accentColor, padding, borderRad
30
38
  const handleBack = (0, react.useCallback)(() => {
31
39
  navigate("products");
32
40
  }, [navigate]);
33
- const coreConfig = (0, react.useMemo)(() => ({
34
- user: account ? { id: account.id } : null,
35
- repContext: true
36
- }), [account]);
37
- const uiConfig = (0, react.useMemo)(() => ({
38
- user: account ? {
39
- id: account.id,
40
- company: null
41
- } : void 0,
42
- basePath: "",
43
- navigate: (path) => {
44
- navigate(path);
45
- },
46
- showToast: (opts) => {
47
- console.log(`[Products] ${opts.type}: ${opts.title}`);
48
- }
49
- }), [account, navigate]);
50
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
51
- ...divProps,
52
- className: `h-full ${divProps.className ?? ""}`,
53
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_use_portal_shareables_api.ShareablesCoreProvider, {
54
- config: coreConfig,
55
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_use_portal_shareables_api.ShareablesApiProvider, {
56
- media: shareablesApi.media,
57
- playlists: shareablesApi.playlists,
58
- fileResources: shareablesApi.fileResources,
59
- share: shareablesApi.share,
60
- productMedia: shareablesApi.productMedia,
61
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_use_portal_shareables_api.ShareablesUIProvider, {
62
- config: uiConfig,
63
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_use_portal_shareables_api.ProductsApp, {
64
- countryCode: void 0,
65
- companyLogoUrl: void 0,
66
- fetchProducts,
67
- fetchProduct,
68
- productId,
69
- onSelectProduct: handleSelectProduct,
70
- onBack: handleBack
71
- })
72
- })
41
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PortalContentApiProvider.ShareablesCoreProvider, {
42
+ config: (0, react.useMemo)(() => ({
43
+ user: account ? { id: account.id } : null,
44
+ repContext: true
45
+ }), [account]),
46
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PortalContentApiProvider.ShareablesUIProvider, {
47
+ config: (0, react.useMemo)(() => ({
48
+ user: account ? {
49
+ id: account.id,
50
+ company: null
51
+ } : void 0,
52
+ basePath: "",
53
+ navigate: (path) => {
54
+ navigate(path);
55
+ },
56
+ showToast: (opts) => {
57
+ console.log(`[Products] ${opts.type}: ${opts.title}`);
58
+ }
59
+ }), [account, navigate]),
60
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PortalContentApiProvider.ProductsApp, {
61
+ countryCode: void 0,
62
+ companyLogoUrl: void 0,
63
+ fetchProducts,
64
+ fetchProduct,
65
+ productId,
66
+ onSelectProduct: handleSelectProduct,
67
+ onBack: handleBack
73
68
  })
74
69
  })
75
70
  });
@@ -97,4 +92,4 @@ Object.defineProperty(exports, "productsScreenPropertySchema", {
97
92
  }
98
93
  });
99
94
 
100
- //# sourceMappingURL=ProductsScreen-DbwSCY7G.cjs.map
95
+ //# sourceMappingURL=ProductsScreen-KjjhlDGo.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductsScreen-KjjhlDGo.cjs","names":["PortalProductsApiProvider","PortalContentApiProvider","usePortalContentContext","useAccount","useAppNavigation","ShareablesCoreProvider","ShareablesUIProvider","ProductsApp"],"sources":["../src/screens/ProductsScreen.tsx"],"sourcesContent":["import { useCallback, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { ShareablesCoreProvider } from \"@fluid-app/shareables-core\";\nimport { ShareablesUIProvider, ProductsApp } from \"@fluid-app/shareables-ui\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { PortalProductsApiProvider } from \"../products/PortalProductsApiProvider\";\nimport {\n PortalContentApiProvider,\n usePortalContentContext,\n} from \"../content/PortalContentApiProvider\";\n\ntype ProductsScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function ProductsScreen({\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}: ProductsScreenProps): React.JSX.Element {\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <PortalProductsApiProvider>\n <PortalContentApiProvider>\n <ProductsScreenContent />\n </PortalContentApiProvider>\n </PortalProductsApiProvider>\n </div>\n );\n}\n\n/** Inner component rendered inside providers so hooks can access context. */\nfunction ProductsScreenContent(): React.JSX.Element {\n const { productsApi: portalProductsApi } = usePortalContentContext();\n const { data: account } = useAccount();\n const { currentSlug, navigate } = useAppNavigation();\n\n const fetchProducts = useCallback(\n async (search: string, cursor?: string, limit?: number) => {\n if (search) {\n return portalProductsApi.searchProducts(search, { cursor, limit });\n }\n return portalProductsApi.listProducts({ cursor, limit });\n },\n [portalProductsApi],\n );\n\n const fetchProduct = useCallback(\n async (id: string | number) => portalProductsApi.getProduct(id),\n [portalProductsApi],\n );\n\n // Extract product ID from slug: \"products/123\" → \"123\"\n const productId = useMemo(() => {\n const match = currentSlug.match(/^products\\/(.+)/);\n return match?.[1] ?? null;\n }, [currentSlug]);\n\n const handleSelectProduct = useCallback(\n (id: string) => {\n navigate(`products/${id}`);\n },\n [navigate],\n );\n\n const handleBack = useCallback(() => {\n navigate(\"products\");\n }, [navigate]);\n\n const coreConfig = useMemo(\n () => ({\n user: account ? { id: account.id } : null,\n repContext: true,\n }),\n [account],\n );\n\n const uiConfig = useMemo(\n () => ({\n user: account\n ? {\n id: account.id,\n company: null, // TODO(portal-tenant): company branding not available from /api/account\n }\n : undefined,\n basePath: \"\",\n navigate: (path: string) => {\n navigate(path);\n },\n showToast: (opts: {\n title: string;\n type: \"success\" | \"error\" | \"warning\";\n }) => {\n console.log(`[Products] ${opts.type}: ${opts.title}`);\n },\n }),\n [account, navigate],\n );\n\n return (\n <ShareablesCoreProvider config={coreConfig}>\n <ShareablesUIProvider config={uiConfig}>\n <ProductsApp\n countryCode={undefined}\n companyLogoUrl={undefined}\n fetchProducts={fetchProducts}\n fetchProduct={fetchProduct}\n productId={productId}\n onSelectProduct={handleSelectProduct}\n onBack={handleBack}\n />\n </ShareablesUIProvider>\n </ShareablesCoreProvider>\n );\n}\n\nexport const productsScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ProductsScreen\",\n displayName: \"Products Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;;AA0BA,SAAgB,eAAe,EAE7B,YACA,WACA,aACA,SACA,cAEA,GAAG,YACsC;AACzC,QACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAK,GAAI;EAAU,WAAW,UAAU,SAAS,aAAa;YAC5D,iBAAA,GAAA,kBAAA,KAACA,kCAAAA,2BAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,KAACC,iCAAAA,0BAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,KAAC,uBAAD,EAAyB,CAAA,EACA,CAAA,EACD,CAAA;EACxB,CAAA;;;AAKV,SAAS,wBAA2C;CAClD,MAAM,EAAE,aAAa,sBAAsBC,iCAAAA,yBAAyB;CACpE,MAAM,EAAE,MAAM,YAAYC,oBAAAA,YAAY;CACtC,MAAM,EAAE,aAAa,aAAaC,6BAAAA,kBAAkB;CAEpD,MAAM,iBAAA,GAAA,MAAA,aACJ,OAAO,QAAgB,QAAiB,UAAmB;AACzD,MAAI,OACF,QAAO,kBAAkB,eAAe,QAAQ;GAAE;GAAQ;GAAO,CAAC;AAEpE,SAAO,kBAAkB,aAAa;GAAE;GAAQ;GAAO,CAAC;IAE1D,CAAC,kBAAkB,CACpB;CAED,MAAM,gBAAA,GAAA,MAAA,aACJ,OAAO,OAAwB,kBAAkB,WAAW,GAAG,EAC/D,CAAC,kBAAkB,CACpB;CAGD,MAAM,aAAA,GAAA,MAAA,eAA0B;AAE9B,SADc,YAAY,MAAM,kBAAkB,GACnC,MAAM;IACpB,CAAC,YAAY,CAAC;CAEjB,MAAM,uBAAA,GAAA,MAAA,cACH,OAAe;AACd,WAAS,YAAY,KAAK;IAE5B,CAAC,SAAS,CACX;CAED,MAAM,cAAA,GAAA,MAAA,mBAA+B;AACnC,WAAS,WAAW;IACnB,CAAC,SAAS,CAAC;AAgCd,QACE,iBAAA,GAAA,kBAAA,KAACC,iCAAAA,wBAAD;EAAwB,SAAA,GAAA,MAAA,gBA9BjB;GACL,MAAM,UAAU,EAAE,IAAI,QAAQ,IAAI,GAAG;GACrC,YAAY;GACb,GACD,CAAC,QAAQ,CACV;YA0BG,iBAAA,GAAA,kBAAA,KAACC,iCAAAA,sBAAD;GAAsB,SAAA,GAAA,MAAA,gBAvBjB;IACL,MAAM,UACF;KACE,IAAI,QAAQ;KACZ,SAAS;KACV,GACD,KAAA;IACJ,UAAU;IACV,WAAW,SAAiB;AAC1B,cAAS,KAAK;;IAEhB,YAAY,SAGN;AACJ,aAAQ,IAAI,cAAc,KAAK,KAAK,IAAI,KAAK,QAAQ;;IAExD,GACD,CAAC,SAAS,SAAS,CACpB;aAKK,iBAAA,GAAA,kBAAA,KAACC,iCAAAA,aAAD;IACE,aAAa,KAAA;IACb,gBAAgB,KAAA;IACD;IACD;IACH;IACX,iBAAiB;IACjB,QAAQ;IACR,CAAA;GACmB,CAAA;EACA,CAAA;;AAI7B,MAAa,+BAAqD;CAChE,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ,EAAE;CACX"}
@@ -5,7 +5,8 @@ import "./ScreenHeaderContext-DjSO5A8k.mjs";
5
5
  import "./dist-CkIGP8my.mjs";
6
6
  import "./SearchSort-Br8J8JS8.mjs";
7
7
  import "./dist-C9vpl_rR.mjs";
8
- import "./use-portal-shareables-api-DRK9Y5dp.mjs";
9
- import "./sortable.esm-DSrWP4x9.mjs";
10
- import { n as productsScreenPropertySchema, t as ProductsScreen } from "./ProductsScreen-D6eoU86k.mjs";
8
+ import "./PortalContentApiProvider-CW0ADhPi.mjs";
9
+ import "./sortable.esm-E6JdQn7I.mjs";
10
+ import "./PortalProductsApiProvider-BCXX9NGK.mjs";
11
+ import { n as productsScreenPropertySchema, t as ProductsScreen } from "./ProductsScreen-CVNJudq9.mjs";
11
12
  export { ProductsScreen, productsScreenPropertySchema };
@@ -5,7 +5,8 @@ import "./ScreenHeaderContext-DjSO5A8k.mjs";
5
5
  import "./dist-CkIGP8my.mjs";
6
6
  import "./SearchSort-Br8J8JS8.mjs";
7
7
  import "./dist-C9vpl_rR.mjs";
8
- import "./use-portal-shareables-api-DRK9Y5dp.mjs";
9
- import { n as shareablesScreenPropertySchema, t as ShareablesScreen } from "./ShareablesScreen-DrQDQ1-U.mjs";
10
- import "./sortable.esm-DSrWP4x9.mjs";
8
+ import "./PortalContentApiProvider-CW0ADhPi.mjs";
9
+ import "./sortable.esm-E6JdQn7I.mjs";
10
+ import "./PortalProductsApiProvider-BCXX9NGK.mjs";
11
+ import { n as shareablesScreenPropertySchema, t as ShareablesScreen } from "./ShareablesScreen-DpEP_6u0.mjs";
11
12
  export { ShareablesScreen, shareablesScreenPropertySchema };
@@ -6,8 +6,9 @@ require("./dist-thaj08s5.cjs");
6
6
  require("./es-xQF-WIMq.cjs");
7
7
  require("./SearchSort-CYhjl8tC.cjs");
8
8
  require("./dist-BSKQ_GoC.cjs");
9
- require("./use-portal-shareables-api-DcjYlAOy.cjs");
10
- const require_ShareablesScreen = require("./ShareablesScreen-DPHZMh-V.cjs");
11
- require("./dist-lO2OG0T5.cjs");
9
+ require("./PortalContentApiProvider-Di5emtYd.cjs");
10
+ require("./dist-BF_4vk1z.cjs");
11
+ require("./PortalProductsApiProvider-BquMHwvt.cjs");
12
+ const require_ShareablesScreen = require("./ShareablesScreen-Dz8w2l3e.cjs");
12
13
  exports.ShareablesScreen = require_ShareablesScreen.ShareablesScreen;
13
14
  exports.shareablesScreenPropertySchema = require_ShareablesScreen.shareablesScreenPropertySchema;
@@ -0,0 +1,132 @@
1
+ import { t as useAccount } from "./use-account-BJhzTl2j.mjs";
2
+ import { n as useAppNavigation } from "./AppNavigationContext-D29xW9Ua.mjs";
3
+ import { a as ShareablesUIProvider, i as ShareablesApp, n as usePortalContentContext, o as ShareablesCoreProvider, t as PortalContentApiProvider } from "./PortalContentApiProvider-CW0ADhPi.mjs";
4
+ import { t as PortalProductsApiProvider } from "./PortalProductsApiProvider-BCXX9NGK.mjs";
5
+ import { useCallback, useMemo } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/screens/ShareablesScreen.tsx
8
+ /**
9
+ * Parse the current shareables sub-route from the full slug.
10
+ *
11
+ * System nav slugs are "share/products", "share/media", "share/playlists".
12
+ * Detail pages append an ID: "share/products/123", "share/media/456".
13
+ *
14
+ * "share/products" → screen="products", detailId=null
15
+ * "share/products/123" → screen="products", detailId="123"
16
+ * "share/media/456" → screen="media", detailId="456"
17
+ * "share/playlists" → screen="playlists", detailId=null
18
+ * "share/playlists/789" → screen="playlists", detailId="789"
19
+ * "share/files" → screen="files", detailId=null
20
+ * "share" → screen=null (default to products)
21
+ */
22
+ function parseShareablesRoute(currentSlug) {
23
+ const slugWithoutPrefix = currentSlug.replace(/^share\/?/, "");
24
+ if (!slugWithoutPrefix) return {
25
+ screen: null,
26
+ detailId: null,
27
+ action: null
28
+ };
29
+ const parts = slugWithoutPrefix.split("/");
30
+ return {
31
+ screen: parts[0] || null,
32
+ detailId: parts[1] || null,
33
+ action: parts[2] || null
34
+ };
35
+ }
36
+ function ShareablesScreen({ background, textColor, accentColor, padding, borderRadius, ...divProps }) {
37
+ return /* @__PURE__ */ jsx("div", {
38
+ ...divProps,
39
+ className: `h-full ${divProps.className ?? ""}`,
40
+ children: /* @__PURE__ */ jsx(PortalProductsApiProvider, { children: /* @__PURE__ */ jsx(PortalContentApiProvider, { children: /* @__PURE__ */ jsx(ShareablesScreenContent, {}) }) })
41
+ });
42
+ }
43
+ /** Inner component rendered inside providers so hooks can access context. */
44
+ function ShareablesScreenContent() {
45
+ const shareablesCtx = usePortalContentContext();
46
+ const { productsApi: portalProductsApi } = shareablesCtx;
47
+ const { data: account } = useAccount();
48
+ const { currentSlug, navigate } = useAppNavigation();
49
+ const isCustomer = account?.member_type === "customer";
50
+ const fetchProducts = useCallback(async (search, cursor, limit) => {
51
+ if (search) return portalProductsApi.searchProducts(search, {
52
+ cursor,
53
+ limit
54
+ });
55
+ return portalProductsApi.listProducts({
56
+ cursor,
57
+ limit
58
+ });
59
+ }, [portalProductsApi]);
60
+ const fetchProduct = useCallback(async (id) => portalProductsApi.getProduct(id), [portalProductsApi]);
61
+ const { screen, detailId, action } = parseShareablesRoute(currentSlug);
62
+ const handleNavigate = useCallback((subScreen, id) => {
63
+ navigate(id ? `share/${subScreen}/${id}` : `share/${subScreen}`);
64
+ }, [navigate]);
65
+ const handleBack = useCallback(() => {
66
+ if (detailId && screen) navigate(`share/${screen}`);
67
+ else navigate("share/products");
68
+ }, [
69
+ navigate,
70
+ detailId,
71
+ screen
72
+ ]);
73
+ return /* @__PURE__ */ jsx(ShareablesCoreProvider, {
74
+ config: useMemo(() => ({
75
+ user: account ? { id: account.id } : null,
76
+ repContext: true
77
+ }), [account]),
78
+ children: /* @__PURE__ */ jsx(ShareablesUIProvider, {
79
+ config: useMemo(() => ({
80
+ user: account ? {
81
+ id: account.id,
82
+ company: null
83
+ } : void 0,
84
+ affiliateId: null,
85
+ basePath: "",
86
+ navigate: (path) => {
87
+ const cleanPath = path.replace(/^\//, "");
88
+ navigate(cleanPath.startsWith("share/") ? cleanPath : `share/${cleanPath}`);
89
+ },
90
+ showToast: (opts) => {
91
+ console.warn(`[Shareables] ${opts.type}: ${opts.title}`);
92
+ },
93
+ filePickerApi: shareablesCtx.filePickerApi,
94
+ onToggleFavorite: void 0,
95
+ onDeletePlaylist: isCustomer ? void 0 : async (playlistId) => {
96
+ await shareablesCtx.playlistsAdapter.deletePlaylist(playlistId);
97
+ },
98
+ readOnly: isCustomer
99
+ }), [
100
+ account,
101
+ navigate,
102
+ shareablesCtx.filePickerApi,
103
+ isCustomer,
104
+ shareablesCtx.playlistsAdapter
105
+ ]),
106
+ children: /* @__PURE__ */ jsx(ShareablesApp, {
107
+ screen,
108
+ detailId,
109
+ action,
110
+ companyLogoUrl: void 0,
111
+ countryCode: void 0,
112
+ fetchProducts,
113
+ fetchProduct,
114
+ onNavigate: handleNavigate,
115
+ onBack: handleBack
116
+ })
117
+ })
118
+ });
119
+ }
120
+ const shareablesScreenPropertySchema = {
121
+ widgetType: "ShareablesScreen",
122
+ displayName: "Shareables Screen",
123
+ tabsConfig: [{
124
+ id: "styling",
125
+ label: "Styling"
126
+ }],
127
+ fields: []
128
+ };
129
+ //#endregion
130
+ export { shareablesScreenPropertySchema as n, ShareablesScreen as t };
131
+
132
+ //# sourceMappingURL=ShareablesScreen-DpEP_6u0.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShareablesScreen-DpEP_6u0.mjs","names":[],"sources":["../src/screens/ShareablesScreen.tsx"],"sourcesContent":["import { useCallback, useMemo, type ComponentProps } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { ShareablesCoreProvider } from \"@fluid-app/shareables-core\";\nimport { ShareablesUIProvider, ShareablesApp } from \"@fluid-app/shareables-ui\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { PortalProductsApiProvider } from \"../products/PortalProductsApiProvider\";\nimport {\n PortalContentApiProvider,\n usePortalContentContext,\n} from \"../content/PortalContentApiProvider\";\n\ntype ShareablesScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\n/**\n * Parse the current shareables sub-route from the full slug.\n *\n * System nav slugs are \"share/products\", \"share/media\", \"share/playlists\".\n * Detail pages append an ID: \"share/products/123\", \"share/media/456\".\n *\n * \"share/products\" → screen=\"products\", detailId=null\n * \"share/products/123\" → screen=\"products\", detailId=\"123\"\n * \"share/media/456\" → screen=\"media\", detailId=\"456\"\n * \"share/playlists\" → screen=\"playlists\", detailId=null\n * \"share/playlists/789\" → screen=\"playlists\", detailId=\"789\"\n * \"share/files\" → screen=\"files\", detailId=null\n * \"share\" → screen=null (default to products)\n */\nfunction parseShareablesRoute(currentSlug: string): {\n screen: string | null;\n detailId: string | null;\n action: string | null;\n} {\n // Strip the \"share\" prefix\n const slugWithoutPrefix = currentSlug.replace(/^share\\/?/, \"\");\n if (!slugWithoutPrefix) {\n return { screen: null, detailId: null, action: null };\n }\n\n const parts = slugWithoutPrefix.split(\"/\");\n const screen = parts[0] || null;\n const detailId = parts[1] || null;\n const action = parts[2] || null;\n return { screen, detailId, action };\n}\n\nexport function ShareablesScreen({\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}: ShareablesScreenProps): React.JSX.Element {\n return (\n <div {...divProps} className={`h-full ${divProps.className ?? \"\"}`}>\n <PortalProductsApiProvider>\n <PortalContentApiProvider>\n <ShareablesScreenContent />\n </PortalContentApiProvider>\n </PortalProductsApiProvider>\n </div>\n );\n}\n\n/** Inner component rendered inside providers so hooks can access context. */\nfunction ShareablesScreenContent(): React.JSX.Element {\n const shareablesCtx = usePortalContentContext();\n const { productsApi: portalProductsApi } = shareablesCtx;\n const { data: account } = useAccount();\n const { currentSlug, navigate } = useAppNavigation();\n const isCustomer = account?.member_type === \"customer\";\n\n const fetchProducts = useCallback(\n async (search: string, cursor?: string, limit?: number) => {\n if (search) {\n return portalProductsApi.searchProducts(search, { cursor, limit });\n }\n return portalProductsApi.listProducts({ cursor, limit });\n },\n [portalProductsApi],\n );\n\n const fetchProduct = useCallback(\n async (id: string | number) => portalProductsApi.getProduct(id),\n [portalProductsApi],\n );\n\n const { screen, detailId, action } = parseShareablesRoute(currentSlug);\n\n const handleNavigate = useCallback(\n (subScreen: string, id?: string) => {\n const path = id ? `share/${subScreen}/${id}` : `share/${subScreen}`;\n navigate(path);\n },\n [navigate],\n );\n\n const handleBack = useCallback(() => {\n if (detailId && screen) {\n navigate(`share/${screen}`);\n } else {\n navigate(\"share/products\");\n }\n }, [navigate, detailId, screen]);\n\n const coreConfig = useMemo(\n () => ({\n user: account ? { id: account.id } : null,\n repContext: true,\n }),\n [account],\n );\n\n const uiConfig = useMemo(\n () => ({\n user: account\n ? {\n id: account.id,\n company: null, // TODO(portal-tenant): company branding not available from /api/account\n }\n : undefined,\n // TODO(portal-tenant): affiliate_id not available from /api/account\n affiliateId: null,\n basePath: \"\",\n navigate: (path: string) => {\n const cleanPath = path.replace(/^\\//, \"\");\n const prefixed = cleanPath.startsWith(\"share/\")\n ? cleanPath\n : `share/${cleanPath}`;\n navigate(prefixed);\n },\n showToast: (opts: {\n title: string;\n type: \"success\" | \"error\" | \"warning\";\n }) => {\n console.warn(`[Shareables] ${opts.type}: ${opts.title}`);\n },\n filePickerApi: shareablesCtx.filePickerApi,\n // TODO(portal-tenant): affiliate_id not available from /api/account; favorites disabled\n onToggleFavorite: undefined,\n onDeletePlaylist: isCustomer\n ? undefined\n : async (playlistId: number) => {\n await shareablesCtx.playlistsAdapter.deletePlaylist(playlistId);\n },\n readOnly: isCustomer,\n }),\n [\n account,\n navigate,\n shareablesCtx.filePickerApi,\n isCustomer,\n shareablesCtx.playlistsAdapter,\n ],\n );\n\n return (\n <ShareablesCoreProvider config={coreConfig}>\n <ShareablesUIProvider config={uiConfig}>\n <ShareablesApp\n screen={screen}\n detailId={detailId}\n action={action}\n companyLogoUrl={undefined}\n countryCode={undefined}\n fetchProducts={fetchProducts}\n fetchProduct={fetchProduct}\n onNavigate={handleNavigate}\n onBack={handleBack}\n />\n </ShareablesUIProvider>\n </ShareablesCoreProvider>\n );\n}\n\nexport const shareablesScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShareablesScreen\",\n displayName: \"Shareables Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwCA,SAAS,qBAAqB,aAI5B;CAEA,MAAM,oBAAoB,YAAY,QAAQ,aAAa,GAAG;AAC9D,KAAI,CAAC,kBACH,QAAO;EAAE,QAAQ;EAAM,UAAU;EAAM,QAAQ;EAAM;CAGvD,MAAM,QAAQ,kBAAkB,MAAM,IAAI;AAI1C,QAAO;EAAE,QAHM,MAAM,MAAM;EAGV,UAFA,MAAM,MAAM;EAEF,QADZ,MAAM,MAAM;EACQ;;AAGrC,SAAgB,iBAAiB,EAE/B,YACA,WACA,aACA,SACA,cAEA,GAAG,YACwC;AAC3C,QACE,oBAAC,OAAD;EAAK,GAAI;EAAU,WAAW,UAAU,SAAS,aAAa;YAC5D,oBAAC,2BAAD,EAAA,UACE,oBAAC,0BAAD,EAAA,UACE,oBAAC,yBAAD,EAA2B,CAAA,EACF,CAAA,EACD,CAAA;EACxB,CAAA;;;AAKV,SAAS,0BAA6C;CACpD,MAAM,gBAAgB,yBAAyB;CAC/C,MAAM,EAAE,aAAa,sBAAsB;CAC3C,MAAM,EAAE,MAAM,YAAY,YAAY;CACtC,MAAM,EAAE,aAAa,aAAa,kBAAkB;CACpD,MAAM,aAAa,SAAS,gBAAgB;CAE5C,MAAM,gBAAgB,YACpB,OAAO,QAAgB,QAAiB,UAAmB;AACzD,MAAI,OACF,QAAO,kBAAkB,eAAe,QAAQ;GAAE;GAAQ;GAAO,CAAC;AAEpE,SAAO,kBAAkB,aAAa;GAAE;GAAQ;GAAO,CAAC;IAE1D,CAAC,kBAAkB,CACpB;CAED,MAAM,eAAe,YACnB,OAAO,OAAwB,kBAAkB,WAAW,GAAG,EAC/D,CAAC,kBAAkB,CACpB;CAED,MAAM,EAAE,QAAQ,UAAU,WAAW,qBAAqB,YAAY;CAEtE,MAAM,iBAAiB,aACpB,WAAmB,OAAgB;AAElC,WADa,KAAK,SAAS,UAAU,GAAG,OAAO,SAAS,YAC1C;IAEhB,CAAC,SAAS,CACX;CAED,MAAM,aAAa,kBAAkB;AACnC,MAAI,YAAY,OACd,UAAS,SAAS,SAAS;MAE3B,UAAS,iBAAiB;IAE3B;EAAC;EAAU;EAAU;EAAO,CAAC;AAqDhC,QACE,oBAAC,wBAAD;EAAwB,QApDP,eACV;GACL,MAAM,UAAU,EAAE,IAAI,QAAQ,IAAI,GAAG;GACrC,YAAY;GACb,GACD,CAAC,QAAQ,CACV;YA+CG,oBAAC,sBAAD;GAAsB,QA7CT,eACR;IACL,MAAM,UACF;KACE,IAAI,QAAQ;KACZ,SAAS;KACV,GACD,KAAA;IAEJ,aAAa;IACb,UAAU;IACV,WAAW,SAAiB;KAC1B,MAAM,YAAY,KAAK,QAAQ,OAAO,GAAG;AAIzC,cAHiB,UAAU,WAAW,SAAS,GAC3C,YACA,SAAS,YACK;;IAEpB,YAAY,SAGN;AACJ,aAAQ,KAAK,gBAAgB,KAAK,KAAK,IAAI,KAAK,QAAQ;;IAE1D,eAAe,cAAc;IAE7B,kBAAkB,KAAA;IAClB,kBAAkB,aACd,KAAA,IACA,OAAO,eAAuB;AAC5B,WAAM,cAAc,iBAAiB,eAAe,WAAW;;IAErE,UAAU;IACX,GACD;IACE;IACA;IACA,cAAc;IACd;IACA,cAAc;IACf,CACF;aAKK,oBAAC,eAAD;IACU;IACE;IACF;IACR,gBAAgB,KAAA;IAChB,aAAa,KAAA;IACE;IACD;IACd,YAAY;IACZ,QAAQ;IACR,CAAA;GACmB,CAAA;EACA,CAAA;;AAI7B,MAAa,iCAAuD;CAClE,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ,EAAE;CACX"}