@burdenoff/microfe-coolandlovely 2026.1031.2 → 2026.1101.2

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 (141) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.d.ts.map +1 -1
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js +16 -4
  3. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  4. package/dist/coolandlovely/CoolAndLovelyRoutes.d.ts.map +1 -1
  5. package/dist/coolandlovely/CoolAndLovelyRoutes.js +19 -15
  6. package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
  7. package/dist/coolandlovely/components/ActivityTrail.js +1 -1
  8. package/dist/coolandlovely/components/ActivityTrail.js.map +1 -1
  9. package/dist/coolandlovely/components/ActorName.js.map +1 -1
  10. package/dist/coolandlovely/components/AddToCartPanel.js.map +1 -1
  11. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
  12. package/dist/coolandlovely/components/BrandCard.js.map +1 -1
  13. package/dist/coolandlovely/components/BrandCover.js.map +1 -1
  14. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -1
  15. package/dist/coolandlovely/components/CollaborationFeeCard.js.map +1 -1
  16. package/dist/coolandlovely/components/CollectionCard.js.map +1 -1
  17. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -1
  18. package/dist/coolandlovely/components/Comments.js.map +1 -1
  19. package/dist/coolandlovely/components/EmptyState.js.map +1 -1
  20. package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -1
  21. package/dist/coolandlovely/components/Field.js.map +1 -1
  22. package/dist/coolandlovely/components/FileImage.js.map +1 -1
  23. package/dist/coolandlovely/components/FollowButton.js.map +1 -1
  24. package/dist/coolandlovely/components/FulfilmentTimeline.js.map +1 -1
  25. package/dist/coolandlovely/components/ImageUploadField.js +3 -1
  26. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -1
  27. package/dist/coolandlovely/components/LookCard.js.map +1 -1
  28. package/dist/coolandlovely/components/LookbookCard.js.map +1 -1
  29. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  30. package/dist/coolandlovely/components/PageLayout.js.map +1 -1
  31. package/dist/coolandlovely/components/PayForOrder.js.map +1 -1
  32. package/dist/coolandlovely/components/RefundPanel.js +1 -1
  33. package/dist/coolandlovely/components/RefundPanel.js.map +1 -1
  34. package/dist/coolandlovely/components/ReturnRequests.js +1 -1
  35. package/dist/coolandlovely/components/ReturnRequests.js.map +1 -1
  36. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -1
  37. package/dist/coolandlovely/components/StateViews.js.map +1 -1
  38. package/dist/coolandlovely/components/StatusBadge.js +1 -1
  39. package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
  40. package/dist/coolandlovely/components/StepTracker.js.map +1 -1
  41. package/dist/coolandlovely/components/StockEditor.js.map +1 -1
  42. package/dist/coolandlovely/components/StoreListingCard.js.map +1 -1
  43. package/dist/coolandlovely/components/TagList.js.map +1 -1
  44. package/dist/coolandlovely/components/Thumbnail.js +1 -1
  45. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  46. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -1
  47. package/dist/coolandlovely/components/WardrobeItemCard.js.map +1 -1
  48. package/dist/coolandlovely/components/index.js +2 -1
  49. package/dist/coolandlovely/context/CoolAndLovelyContext.js +2 -1
  50. package/dist/coolandlovely/context/CoolAndLovelyContext.js.map +1 -1
  51. package/dist/coolandlovely/context/index.js +2 -1
  52. package/dist/coolandlovely/hooks/index.js +4 -3
  53. package/dist/coolandlovely/hooks/useActivity.d.ts +1 -1
  54. package/dist/coolandlovely/hooks/useActivity.d.ts.map +1 -1
  55. package/dist/coolandlovely/hooks/useActivity.js.map +1 -1
  56. package/dist/coolandlovely/hooks/useAssistantEntity.js.map +1 -1
  57. package/dist/coolandlovely/hooks/useBrandOrdersApi.d.ts +4 -4
  58. package/dist/coolandlovely/hooks/useBrandOrdersApi.d.ts.map +1 -1
  59. package/dist/coolandlovely/hooks/useBrandOrdersApi.js.map +1 -1
  60. package/dist/coolandlovely/hooks/useCatalogImport.js.map +1 -1
  61. package/dist/coolandlovely/hooks/useComments.d.ts +1 -1
  62. package/dist/coolandlovely/hooks/useComments.d.ts.map +1 -1
  63. package/dist/coolandlovely/hooks/useComments.js.map +1 -1
  64. package/dist/coolandlovely/hooks/useCommerceApi.d.ts +7 -7
  65. package/dist/coolandlovely/hooks/useCommerceApi.d.ts.map +1 -1
  66. package/dist/coolandlovely/hooks/useCommerceApi.js.map +1 -1
  67. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts +24 -24
  68. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts.map +1 -1
  69. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  70. package/dist/coolandlovely/hooks/useEditorialApi.d.ts +95 -0
  71. package/dist/coolandlovely/hooks/useEditorialApi.d.ts.map +1 -0
  72. package/dist/coolandlovely/hooks/useEditorialApi.js +107 -0
  73. package/dist/coolandlovely/hooks/useEditorialApi.js.map +1 -0
  74. package/dist/coolandlovely/hooks/useFiles.js.map +1 -1
  75. package/dist/coolandlovely/hooks/useInventoryApi.d.ts +1 -1
  76. package/dist/coolandlovely/hooks/useInventoryApi.d.ts.map +1 -1
  77. package/dist/coolandlovely/hooks/useInventoryApi.js.map +1 -1
  78. package/dist/coolandlovely/hooks/useTr.js.map +1 -1
  79. package/dist/coolandlovely/index.js +6 -5
  80. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  81. package/dist/coolandlovely/pages/admin/AdminBrandsPage.js.map +1 -1
  82. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js.map +1 -1
  83. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js.map +1 -1
  84. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  85. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  86. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  87. package/dist/coolandlovely/pages/brands/ImportCatalogPage.js.map +1 -1
  88. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  89. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  90. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js.map +1 -1
  91. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  92. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  93. package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -1
  94. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  95. package/dist/coolandlovely/pages/commerce/BrandOrdersPage.js.map +1 -1
  96. package/dist/coolandlovely/pages/commerce/CartPage.js.map +1 -1
  97. package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.js.map +1 -1
  98. package/dist/coolandlovely/pages/commerce/OrdersPage.js.map +1 -1
  99. package/dist/coolandlovely/pages/discover/DiscoverPage.js +1 -1
  100. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  101. package/dist/coolandlovely/pages/editorial/EditorialPage.d.ts +2 -0
  102. package/dist/coolandlovely/pages/editorial/EditorialPage.d.ts.map +1 -0
  103. package/dist/coolandlovely/pages/editorial/EditorialPage.js +486 -0
  104. package/dist/coolandlovely/pages/editorial/EditorialPage.js.map +1 -0
  105. package/dist/coolandlovely/pages/items/StoreListedPieceView.js.map +1 -1
  106. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  107. package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -1
  108. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  109. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  110. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  111. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  112. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  113. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  114. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +1 -1
  115. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  116. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -1
  117. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  118. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  119. package/dist/coolandlovely/store/coolandlovelyStore.js.map +1 -1
  120. package/dist/coolandlovely/store/index.js +2 -1
  121. package/dist/coolandlovely/types/commerce.d.ts.map +1 -1
  122. package/dist/coolandlovely/types/index.d.ts.map +1 -1
  123. package/dist/coolandlovely/utils/cn.js +1 -1
  124. package/dist/coolandlovely/utils/cn.js.map +1 -1
  125. package/dist/coolandlovely/utils/formatters.js.map +1 -1
  126. package/dist/coolandlovely/utils/icons.js.map +1 -1
  127. package/dist/coolandlovely/utils/index.js +2 -1
  128. package/dist/coolandlovely/utils/navigation.js.map +1 -1
  129. package/dist/coolandlovely/utils/paymentProviders.js +22 -22
  130. package/dist/coolandlovely/utils/paymentProviders.js.map +1 -1
  131. package/dist/coolandlovely/utils/sparkline.js +13 -15
  132. package/dist/coolandlovely/utils/sparkline.js.map +1 -1
  133. package/dist/node_modules/clsx/dist/clsx.js +1 -1
  134. package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
  135. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +225 -146
  136. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
  137. package/dist/services/createSandboxAssistantTransport.js.map +1 -1
  138. package/dist/stories/DiscoverPage.stories.d.ts.map +1 -1
  139. package/dist/utils/pageContext.js +1 -1
  140. package/dist/utils/pageContext.js.map +1 -1
  141. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"file":"CoolAndLovelyRoot.d.ts","sourceRoot":"","sources":["../../src/coolandlovely/CoolAndLovelyRoot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA0BhC,OAAO,KAAK,EAAE,sBAAsB,EAAW,MAAM,SAAS,CAAC;AAY/D,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAuNxD,CAAC"}
1
+ {"version":3,"file":"CoolAndLovelyRoot.d.ts","sourceRoot":"","sources":["../../src/coolandlovely/CoolAndLovelyRoot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA0BhC,OAAO,KAAK,EAAE,sBAAsB,EAAW,MAAM,SAAS,CAAC;AAY/D,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAgOxD,CAAC"}
@@ -19,7 +19,8 @@ var D = new a({ defaultOptions: { queries: {
19
19
  return n === e ? t : n;
20
20
  }, [j]), N = E("coolandlovely.admin.curation", !1), P = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
21
21
  return i(() => {
22
- if (O) return O([
22
+ if (!O) return;
23
+ let e = [
23
24
  {
24
25
  id: "coolandlovely-overview",
25
26
  label: M("coolandlovely.nav.overview", "Overview"),
@@ -132,6 +133,14 @@ var D = new a({ defaultOptions: { queries: {
132
133
  order: 41,
133
134
  group: "signals"
134
135
  },
136
+ {
137
+ id: "coolandlovely-journal",
138
+ label: M("coolandlovely.nav.journal", "Journal"),
139
+ path: P(A, "journal"),
140
+ icon: /* @__PURE__ */ C(b, { size: 20 }),
141
+ order: 42,
142
+ group: "signals"
143
+ },
135
144
  ...N ? [
136
145
  {
137
146
  id: "coolandlovely-admin",
@@ -166,20 +175,23 @@ var D = new a({ defaultOptions: { queries: {
166
175
  group: "admin"
167
176
  }
168
177
  ] : []
169
- ]);
178
+ ];
179
+ return O(e);
170
180
  }, [
171
181
  O,
172
182
  A,
173
183
  M,
174
184
  N
175
185
  ]), i(() => {
176
- if (k) return k([{
186
+ if (!k) return;
187
+ let e = [{
177
188
  id: "coolandlovely-status",
178
189
  label: M("coolandlovely.footer.ready", "CoolAndLovely"),
179
190
  type: "status",
180
191
  order: 10,
181
192
  section: "left"
182
- }]);
193
+ }];
194
+ return k(e);
183
195
  }, [k, M]), /* @__PURE__ */ C(o, {
184
196
  client: D,
185
197
  children: /* @__PURE__ */ C(T, {
@@ -1 +1 @@
1
- {"version":3,"file":"CoolAndLovelyRoot.js","names":[],"sources":["../../src/coolandlovely/CoolAndLovelyRoot.tsx"],"sourcesContent":["/**\n * CoolAndLovely Root Component\n * @module coolandlovely\n *\n * Single Root export consumed by host application shells. The export name and\n * prop contract (basePath / navigate / currentUser / apiGatewayUrl /\n * registerNavItems / registerFooterItems / workspace context) are stable — the\n * host imports `CoolAndLovelyRoot` the same way regardless of the internal UI.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useEffect } from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Home,\n Store,\n Shapes,\n Shirt,\n Sparkles,\n Handshake,\n Heart,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n Shield,\n ShoppingBag,\n Package,\n Wallet,\n} from 'lucide-react';\nimport { CoolAndLovelyProvider } from './context/CoolAndLovelyContext';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags/useFeatureFlag';\nimport { CoolAndLovelyRoutes } from './CoolAndLovelyRoutes';\nimport { FeedbackWidget } from './components';\nimport type { CoolAndLovelyRootProps, NavItem } from './types';\n\n// Create a query client for this microfrontend\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n retry: 1,\n },\n },\n});\n\nexport const CoolAndLovelyRoot: FC<CoolAndLovelyRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n const showAdmin = useFeatureFlag('coolandlovely.admin.curation', false);\n\n // Helper to join paths correctly\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell on mount.\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'coolandlovely-overview',\n label: t('coolandlovely.nav.overview', 'Overview'),\n path: joinPath(basePath, ''),\n icon: <Home size={20} />,\n order: 1,\n group: 'main',\n },\n // Catalog\n {\n id: 'coolandlovely-brands',\n label: t('coolandlovely.nav.brands', 'Brands'),\n path: joinPath(basePath, 'brands'),\n icon: <Store size={20} />,\n order: 10,\n group: 'catalog',\n },\n {\n id: 'coolandlovely-collections',\n label: t('coolandlovely.nav.collections', 'Collections'),\n path: joinPath(basePath, 'collections'),\n icon: <Shapes size={20} />,\n order: 11,\n group: 'catalog',\n },\n {\n id: 'coolandlovely-items',\n label: t('coolandlovely.nav.items', 'Style Items'),\n path: joinPath(basePath, 'items'),\n icon: <Shirt size={20} />,\n order: 12,\n group: 'catalog',\n },\n // Social\n {\n id: 'coolandlovely-looks',\n label: t('coolandlovely.nav.looks', 'Looks'),\n path: joinPath(basePath, 'looks'),\n icon: <Sparkles size={20} />,\n order: 20,\n group: 'social',\n },\n {\n id: 'coolandlovely-collaborations',\n label: t('coolandlovely.nav.collaborations', 'Collaborations'),\n path: joinPath(basePath, 'collaborations'),\n icon: <Handshake size={20} />,\n order: 21,\n group: 'social',\n },\n // My style\n {\n id: 'coolandlovely-wardrobe',\n label: t('coolandlovely.nav.wardrobe', 'My Wardrobe'),\n path: joinPath(basePath, 'wardrobe'),\n icon: <Heart size={20} />,\n order: 30,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-lookbooks',\n label: t('coolandlovely.nav.lookbooks', 'Lookbooks'),\n path: joinPath(basePath, 'lookbooks'),\n icon: <BookHeart size={20} />,\n order: 31,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-profile',\n label: t('coolandlovely.nav.profile', 'Style Profile'),\n path: joinPath(basePath, 'profile'),\n icon: <UserCircle size={20} />,\n order: 32,\n group: 'my-style',\n },\n // Shopping (BOFF-3370)\n {\n id: 'coolandlovely-cart',\n label: t('coolandlovely.nav.cart', 'Cart'),\n path: joinPath(basePath, 'cart'),\n icon: <ShoppingBag size={20} />,\n order: 33,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-orders',\n label: t('coolandlovely.nav.orders', 'My Orders'),\n path: joinPath(basePath, 'orders'),\n icon: <Package size={20} />,\n order: 34,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-earnings',\n label: t('coolandlovely.nav.earnings', 'Creator Earnings'),\n path: joinPath(basePath, 'earnings'),\n icon: <Wallet size={20} />,\n order: 35,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-discover',\n label: t('coolandlovely.nav.discover', 'Discover'),\n path: joinPath(basePath, 'discover'),\n icon: <Compass size={20} />,\n order: 40,\n group: 'signals',\n },\n {\n id: 'coolandlovely-trends',\n label: t('coolandlovely.nav.trends', 'Trends'),\n path: joinPath(basePath, 'trends'),\n icon: <TrendingUp size={20} />,\n order: 41,\n group: 'signals',\n },\n // Admin curation (feature-flagged)\n ...(showAdmin\n ? [\n {\n id: 'coolandlovely-admin',\n label: t('coolandlovely.nav.admin', 'Admin'),\n path: joinPath(basePath, 'admin/brands'),\n icon: <Shield size={20} />,\n order: 50,\n group: 'admin',\n },\n {\n id: 'coolandlovely-admin-brands',\n label: t('coolandlovely.nav.adminBrands', 'Brands'),\n path: joinPath(basePath, 'admin/brands'),\n icon: <Store size={20} />,\n order: 51,\n group: 'admin',\n },\n {\n id: 'coolandlovely-admin-collaborations',\n label: t('coolandlovely.nav.adminCollaborations', 'Collaborations'),\n path: joinPath(basePath, 'admin/collaborations'),\n icon: <Handshake size={20} />,\n order: 52,\n group: 'admin',\n },\n {\n id: 'coolandlovely-admin-featured-items',\n label: t('coolandlovely.nav.adminFeaturedItems', 'Featured Items'),\n path: joinPath(basePath, 'admin/featured-items'),\n icon: <Shirt size={20} />,\n order: 53,\n group: 'admin',\n },\n ]\n : []),\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, t, showAdmin]);\n\n // Register footer items\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'coolandlovely-status',\n label: t('coolandlovely.footer.ready', 'CoolAndLovely'),\n type: 'status' as const,\n order: 10,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, t]);\n\n return (\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={props.workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <CoolAndLovelyProvider {...props}>\n <CoolAndLovelyRoutes />\n <FeedbackWidget />\n </CoolAndLovelyProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CoolAndLovelyRoot.js","names":[],"sources":["../../src/coolandlovely/CoolAndLovelyRoot.tsx"],"sourcesContent":["/**\n * CoolAndLovely Root Component\n * @module coolandlovely\n *\n * Single Root export consumed by host application shells. The export name and\n * prop contract (basePath / navigate / currentUser / apiGatewayUrl /\n * registerNavItems / registerFooterItems / workspace context) are stable — the\n * host imports `CoolAndLovelyRoot` the same way regardless of the internal UI.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useEffect } from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Home,\n Store,\n Shapes,\n Shirt,\n Sparkles,\n Handshake,\n Heart,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n Shield,\n ShoppingBag,\n Package,\n Wallet,\n} from 'lucide-react';\nimport { CoolAndLovelyProvider } from './context/CoolAndLovelyContext';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags/useFeatureFlag';\nimport { CoolAndLovelyRoutes } from './CoolAndLovelyRoutes';\nimport { FeedbackWidget } from './components';\nimport type { CoolAndLovelyRootProps, NavItem } from './types';\n\n// Create a query client for this microfrontend\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n retry: 1,\n },\n },\n});\n\nexport const CoolAndLovelyRoot: FC<CoolAndLovelyRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n const showAdmin = useFeatureFlag('coolandlovely.admin.curation', false);\n\n // Helper to join paths correctly\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell on mount.\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'coolandlovely-overview',\n label: t('coolandlovely.nav.overview', 'Overview'),\n path: joinPath(basePath, ''),\n icon: <Home size={20} />,\n order: 1,\n group: 'main',\n },\n // Catalog\n {\n id: 'coolandlovely-brands',\n label: t('coolandlovely.nav.brands', 'Brands'),\n path: joinPath(basePath, 'brands'),\n icon: <Store size={20} />,\n order: 10,\n group: 'catalog',\n },\n {\n id: 'coolandlovely-collections',\n label: t('coolandlovely.nav.collections', 'Collections'),\n path: joinPath(basePath, 'collections'),\n icon: <Shapes size={20} />,\n order: 11,\n group: 'catalog',\n },\n {\n id: 'coolandlovely-items',\n label: t('coolandlovely.nav.items', 'Style Items'),\n path: joinPath(basePath, 'items'),\n icon: <Shirt size={20} />,\n order: 12,\n group: 'catalog',\n },\n // Social\n {\n id: 'coolandlovely-looks',\n label: t('coolandlovely.nav.looks', 'Looks'),\n path: joinPath(basePath, 'looks'),\n icon: <Sparkles size={20} />,\n order: 20,\n group: 'social',\n },\n {\n id: 'coolandlovely-collaborations',\n label: t('coolandlovely.nav.collaborations', 'Collaborations'),\n path: joinPath(basePath, 'collaborations'),\n icon: <Handshake size={20} />,\n order: 21,\n group: 'social',\n },\n // My style\n {\n id: 'coolandlovely-wardrobe',\n label: t('coolandlovely.nav.wardrobe', 'My Wardrobe'),\n path: joinPath(basePath, 'wardrobe'),\n icon: <Heart size={20} />,\n order: 30,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-lookbooks',\n label: t('coolandlovely.nav.lookbooks', 'Lookbooks'),\n path: joinPath(basePath, 'lookbooks'),\n icon: <BookHeart size={20} />,\n order: 31,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-profile',\n label: t('coolandlovely.nav.profile', 'Style Profile'),\n path: joinPath(basePath, 'profile'),\n icon: <UserCircle size={20} />,\n order: 32,\n group: 'my-style',\n },\n // Shopping (BOFF-3370)\n {\n id: 'coolandlovely-cart',\n label: t('coolandlovely.nav.cart', 'Cart'),\n path: joinPath(basePath, 'cart'),\n icon: <ShoppingBag size={20} />,\n order: 33,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-orders',\n label: t('coolandlovely.nav.orders', 'My Orders'),\n path: joinPath(basePath, 'orders'),\n icon: <Package size={20} />,\n order: 34,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-earnings',\n label: t('coolandlovely.nav.earnings', 'Creator Earnings'),\n path: joinPath(basePath, 'earnings'),\n icon: <Wallet size={20} />,\n order: 35,\n group: 'my-style',\n },\n {\n id: 'coolandlovely-discover',\n label: t('coolandlovely.nav.discover', 'Discover'),\n path: joinPath(basePath, 'discover'),\n icon: <Compass size={20} />,\n order: 40,\n group: 'signals',\n },\n {\n id: 'coolandlovely-trends',\n label: t('coolandlovely.nav.trends', 'Trends'),\n path: joinPath(basePath, 'trends'),\n icon: <TrendingUp size={20} />,\n order: 41,\n group: 'signals',\n },\n // Admin curation (feature-flagged)\n {\n id: 'coolandlovely-journal',\n label: t('coolandlovely.nav.journal', 'Journal'),\n path: joinPath(basePath, 'journal'),\n icon: <TrendingUp size={20} />,\n order: 42,\n group: 'signals',\n },\n // Admin curation (feature-flagged)\n ...(showAdmin\n ? [\n {\n id: 'coolandlovely-admin',\n label: t('coolandlovely.nav.admin', 'Admin'),\n path: joinPath(basePath, 'admin/brands'),\n icon: <Shield size={20} />,\n order: 50,\n group: 'admin',\n },\n {\n id: 'coolandlovely-admin-brands',\n label: t('coolandlovely.nav.adminBrands', 'Brands'),\n path: joinPath(basePath, 'admin/brands'),\n icon: <Store size={20} />,\n order: 51,\n group: 'admin',\n },\n {\n id: 'coolandlovely-admin-collaborations',\n label: t('coolandlovely.nav.adminCollaborations', 'Collaborations'),\n path: joinPath(basePath, 'admin/collaborations'),\n icon: <Handshake size={20} />,\n order: 52,\n group: 'admin',\n },\n {\n id: 'coolandlovely-admin-featured-items',\n label: t('coolandlovely.nav.adminFeaturedItems', 'Featured Items'),\n path: joinPath(basePath, 'admin/featured-items'),\n icon: <Shirt size={20} />,\n order: 53,\n group: 'admin',\n },\n ]\n : []),\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, t, showAdmin]);\n\n // Register footer items\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'coolandlovely-status',\n label: t('coolandlovely.footer.ready', 'CoolAndLovely'),\n type: 'status' as const,\n order: 10,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, t]);\n\n return (\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={props.workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <CoolAndLovelyProvider {...props}>\n <CoolAndLovelyRoutes />\n <FeedbackWidget />\n </CoolAndLovelyProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CoolAndLovelyRoutes.d.ts","sourceRoot":"","sources":["../../src/coolandlovely/CoolAndLovelyRoutes.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAE,EAAkB,MAAM,OAAO,CAAC;AAiJhD,eAAO,MAAM,mBAAmB,EAAE,EA2FjC,CAAC"}
1
+ {"version":3,"file":"CoolAndLovelyRoutes.d.ts","sourceRoot":"","sources":["../../src/coolandlovely/CoolAndLovelyRoutes.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAE,EAAkB,MAAM,OAAO,CAAC;AAoJhD,eAAO,MAAM,mBAAmB,EAAE,EA+FjC,CAAC"}
@@ -4,15 +4,15 @@ import { Suspense as n, lazy as r } from "react";
4
4
  import { Navigate as i, Route as a, Routes as o, useLocation as s } from "react-router";
5
5
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
6
  //#region src/coolandlovely/CoolAndLovelyRoutes.tsx
7
- var u = r(() => import("./pages/brands/BrandsListPage.js").then((e) => ({ default: e.BrandsListPage }))), d = r(() => import("./pages/brands/BrandDetailPage.js").then((e) => ({ default: e.BrandDetailPage }))), f = r(() => import("./pages/brands/BrandFormPage.js").then((e) => ({ default: e.BrandFormPage }))), p = r(() => import("./pages/brands/ImportCatalogPage.js").then((e) => ({ default: e.ImportCatalogPage }))), m = r(() => import("./pages/collections/CollectionsListPage.js").then((e) => ({ default: e.CollectionsListPage }))), h = r(() => import("./pages/collections/CollectionDetailPage.js").then((e) => ({ default: e.CollectionDetailPage }))), g = r(() => import("./pages/collections/CollectionFormPage.js").then((e) => ({ default: e.CollectionFormPage }))), _ = r(() => import("./pages/items/StyleItemsListPage.js").then((e) => ({ default: e.StyleItemsListPage }))), v = r(() => import("./pages/items/StyleItemDetailPage.js").then((e) => ({ default: e.StyleItemDetailPage }))), y = r(() => import("./pages/items/StyleItemFormPage.js").then((e) => ({ default: e.StyleItemFormPage }))), b = r(() => import("./pages/looks/LooksListPage.js").then((e) => ({ default: e.LooksListPage }))), x = r(() => import("./pages/looks/LookDetailPage.js").then((e) => ({ default: e.LookDetailPage }))), S = r(() => import("./pages/looks/LookFormPage.js").then((e) => ({ default: e.LookFormPage }))), C = r(() => import("./pages/collaborations/CollaborationsListPage.js").then((e) => ({ default: e.CollaborationsListPage }))), w = r(() => import("./pages/collaborations/CollaborationDetailPage.js").then((e) => ({ default: e.CollaborationDetailPage }))), T = r(() => import("./pages/collaborations/CollaborationFormPage.js").then((e) => ({ default: e.CollaborationFormPage }))), E = r(() => import("./pages/wardrobe/WardrobeListPage.js").then((e) => ({ default: e.WardrobeListPage }))), D = r(() => import("./pages/wardrobe/WardrobeFormPage.js").then((e) => ({ default: e.WardrobeFormPage }))), O = r(() => import("./pages/lookbooks/LookbooksListPage.js").then((e) => ({ default: e.LookbooksListPage }))), k = r(() => import("./pages/lookbooks/LookbookDetailPage.js").then((e) => ({ default: e.LookbookDetailPage }))), A = r(() => import("./pages/profile/StyleProfilePage.js").then((e) => ({ default: e.StyleProfilePage }))), j = r(() => import("./pages/discover/DiscoverPage.js").then((e) => ({ default: e.DiscoverPage }))), M = r(() => import("./pages/trends/TrendsPage.js").then((e) => ({ default: e.TrendsPage }))), N = r(() => import("./pages/commerce/CartPage.js").then((e) => ({ default: e.CartPage }))), P = r(() => import("./pages/commerce/BrandOrdersPage.js").then((e) => ({ default: e.BrandOrdersPage }))), F = r(() => import("./pages/commerce/BrandOrdersPage.js").then((e) => ({ default: e.BrandOrderDetailPage }))), I = r(() => import("./pages/commerce/OrdersPage.js").then((e) => ({ default: e.OrdersPage }))), L = r(() => import("./pages/commerce/OrdersPage.js").then((e) => ({ default: e.OrderDetailPage }))), R = r(() => import("./pages/commerce/CreatorEarningsPage.js").then((e) => ({ default: e.CreatorEarningsPage }))), z = r(() => import("./pages/admin/AdminBrandsPage.js").then((e) => ({ default: e.AdminBrandsPage }))), B = r(() => import("./pages/admin/AdminCollaborationsPage.js").then((e) => ({ default: e.AdminCollaborationsPage }))), V = r(() => import("./pages/admin/AdminFeaturedItemsPage.js").then((e) => ({ default: e.AdminFeaturedItemsPage }))), H = () => /* @__PURE__ */ c("div", {
7
+ var u = r(() => import("./pages/brands/BrandsListPage.js").then((e) => ({ default: e.BrandsListPage }))), d = r(() => import("./pages/brands/BrandDetailPage.js").then((e) => ({ default: e.BrandDetailPage }))), f = r(() => import("./pages/brands/BrandFormPage.js").then((e) => ({ default: e.BrandFormPage }))), p = r(() => import("./pages/brands/ImportCatalogPage.js").then((e) => ({ default: e.ImportCatalogPage }))), m = r(() => import("./pages/collections/CollectionsListPage.js").then((e) => ({ default: e.CollectionsListPage }))), h = r(() => import("./pages/collections/CollectionDetailPage.js").then((e) => ({ default: e.CollectionDetailPage }))), g = r(() => import("./pages/collections/CollectionFormPage.js").then((e) => ({ default: e.CollectionFormPage }))), _ = r(() => import("./pages/items/StyleItemsListPage.js").then((e) => ({ default: e.StyleItemsListPage }))), v = r(() => import("./pages/items/StyleItemDetailPage.js").then((e) => ({ default: e.StyleItemDetailPage }))), y = r(() => import("./pages/items/StyleItemFormPage.js").then((e) => ({ default: e.StyleItemFormPage }))), b = r(() => import("./pages/looks/LooksListPage.js").then((e) => ({ default: e.LooksListPage }))), x = r(() => import("./pages/looks/LookDetailPage.js").then((e) => ({ default: e.LookDetailPage }))), S = r(() => import("./pages/looks/LookFormPage.js").then((e) => ({ default: e.LookFormPage }))), C = r(() => import("./pages/collaborations/CollaborationsListPage.js").then((e) => ({ default: e.CollaborationsListPage }))), w = r(() => import("./pages/collaborations/CollaborationDetailPage.js").then((e) => ({ default: e.CollaborationDetailPage }))), T = r(() => import("./pages/collaborations/CollaborationFormPage.js").then((e) => ({ default: e.CollaborationFormPage }))), E = r(() => import("./pages/wardrobe/WardrobeListPage.js").then((e) => ({ default: e.WardrobeListPage }))), D = r(() => import("./pages/wardrobe/WardrobeFormPage.js").then((e) => ({ default: e.WardrobeFormPage }))), O = r(() => import("./pages/lookbooks/LookbooksListPage.js").then((e) => ({ default: e.LookbooksListPage }))), k = r(() => import("./pages/lookbooks/LookbookDetailPage.js").then((e) => ({ default: e.LookbookDetailPage }))), A = r(() => import("./pages/profile/StyleProfilePage.js").then((e) => ({ default: e.StyleProfilePage }))), j = r(() => import("./pages/discover/DiscoverPage.js").then((e) => ({ default: e.DiscoverPage }))), M = r(() => import("./pages/trends/TrendsPage.js").then((e) => ({ default: e.TrendsPage }))), N = r(() => import("./pages/editorial/EditorialPage.js").then((e) => ({ default: e.EditorialPage }))), P = r(() => import("./pages/commerce/CartPage.js").then((e) => ({ default: e.CartPage }))), F = r(() => import("./pages/commerce/BrandOrdersPage.js").then((e) => ({ default: e.BrandOrdersPage }))), I = r(() => import("./pages/commerce/BrandOrdersPage.js").then((e) => ({ default: e.BrandOrderDetailPage }))), L = r(() => import("./pages/commerce/OrdersPage.js").then((e) => ({ default: e.OrdersPage }))), R = r(() => import("./pages/commerce/OrdersPage.js").then((e) => ({ default: e.OrderDetailPage }))), z = r(() => import("./pages/commerce/CreatorEarningsPage.js").then((e) => ({ default: e.CreatorEarningsPage }))), B = r(() => import("./pages/admin/AdminBrandsPage.js").then((e) => ({ default: e.AdminBrandsPage }))), V = r(() => import("./pages/admin/AdminCollaborationsPage.js").then((e) => ({ default: e.AdminCollaborationsPage }))), H = r(() => import("./pages/admin/AdminFeaturedItemsPage.js").then((e) => ({ default: e.AdminFeaturedItemsPage }))), U = () => /* @__PURE__ */ c("div", {
8
8
  className: "flex min-h-[400px] items-center justify-center",
9
9
  children: /* @__PURE__ */ c("div", { className: "h-8 w-8 animate-spin rounded-full border-b-2 border-primary" })
10
- }), U = () => {
11
- let { basePath: r, isStandalone: U } = e(), W = s(), G = r === "/" ? "" : (r ?? "").replace(/\/$/, "");
12
- return G && !U && !(W.pathname === G || W.pathname.startsWith(G + "/")) ? null : /* @__PURE__ */ c("div", {
10
+ }), W = () => {
11
+ let { basePath: r, isStandalone: W, workspaceId: G, currentUser: K } = e(), q = s(), J = r === "/" ? "" : (r ?? "").replace(/\/$/, "");
12
+ return J && !W && !(q.pathname === J || q.pathname.startsWith(J + "/")) ? null : /* @__PURE__ */ c("div", {
13
13
  className: "flex min-h-full flex-col",
14
14
  children: /* @__PURE__ */ c(n, {
15
- fallback: /* @__PURE__ */ c(H, {}),
15
+ fallback: /* @__PURE__ */ c(U, {}),
16
16
  children: /* @__PURE__ */ l(o, { children: [
17
17
  /* @__PURE__ */ c(a, {
18
18
  index: !0,
@@ -130,29 +130,33 @@ var u = r(() => import("./pages/brands/BrandsListPage.js").then((e) => ({ defaul
130
130
  path: "trends",
131
131
  element: /* @__PURE__ */ c(M, {})
132
132
  }),
133
+ /* @__PURE__ */ c(a, {
134
+ path: "journal",
135
+ element: /* @__PURE__ */ c(N, {}, `${G ?? ""}:${K?.id ?? ""}`)
136
+ }),
133
137
  /* @__PURE__ */ c(a, {
134
138
  path: "cart",
135
- element: /* @__PURE__ */ c(N, {})
139
+ element: /* @__PURE__ */ c(P, {})
136
140
  }),
137
141
  /* @__PURE__ */ c(a, {
138
142
  path: "orders",
139
- element: /* @__PURE__ */ c(I, {})
143
+ element: /* @__PURE__ */ c(L, {})
140
144
  }),
141
145
  /* @__PURE__ */ c(a, {
142
146
  path: "orders/:orderId",
143
- element: /* @__PURE__ */ c(L, {})
147
+ element: /* @__PURE__ */ c(R, {})
144
148
  }),
145
149
  /* @__PURE__ */ c(a, {
146
150
  path: "earnings",
147
- element: /* @__PURE__ */ c(R, {})
151
+ element: /* @__PURE__ */ c(z, {})
148
152
  }),
149
153
  /* @__PURE__ */ c(a, {
150
154
  path: "brand-orders",
151
- element: /* @__PURE__ */ c(P, {})
155
+ element: /* @__PURE__ */ c(F, {})
152
156
  }),
153
157
  /* @__PURE__ */ c(a, {
154
158
  path: "brand-orders/:brandId/:orderId",
155
- element: /* @__PURE__ */ c(F, {})
159
+ element: /* @__PURE__ */ c(I, {})
156
160
  }),
157
161
  /* @__PURE__ */ c(a, {
158
162
  path: "admin",
@@ -163,15 +167,15 @@ var u = r(() => import("./pages/brands/BrandsListPage.js").then((e) => ({ defaul
163
167
  }),
164
168
  /* @__PURE__ */ c(a, {
165
169
  path: "admin/brands",
166
- element: /* @__PURE__ */ c(z, {})
170
+ element: /* @__PURE__ */ c(B, {})
167
171
  }),
168
172
  /* @__PURE__ */ c(a, {
169
173
  path: "admin/collaborations",
170
- element: /* @__PURE__ */ c(B, {})
174
+ element: /* @__PURE__ */ c(V, {})
171
175
  }),
172
176
  /* @__PURE__ */ c(a, {
173
177
  path: "admin/featured-items",
174
- element: /* @__PURE__ */ c(V, {})
178
+ element: /* @__PURE__ */ c(H, {})
175
179
  }),
176
180
  /* @__PURE__ */ c(a, {
177
181
  path: "*",
@@ -185,6 +189,6 @@ var u = r(() => import("./pages/brands/BrandsListPage.js").then((e) => ({ defaul
185
189
  });
186
190
  };
187
191
  //#endregion
188
- export { U as CoolAndLovelyRoutes };
192
+ export { W as CoolAndLovelyRoutes };
189
193
 
190
194
  //# sourceMappingURL=CoolAndLovelyRoutes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CoolAndLovelyRoutes.js","names":[],"sources":["../../src/coolandlovely/CoolAndLovelyRoutes.tsx"],"sourcesContent":["/**\n * CoolAndLovely Routes\n * @module coolandlovely\n *\n * Real fashion-domain routes wired to the live `wspace-coolandlovely-svc`\n * GraphQL contract: overview, brands, collections, style items, looks,\n * collaborations, wardrobe, lookbooks and the shopper's style profile.\n */\n\nimport { type FC, lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router';\nimport { useCoolAndLovely } from './context/CoolAndLovelyContext';\n\n// Eager-loaded overview (primary landing page)\nimport { HomePage } from './pages/HomePage';\n\n// Brands\nconst BrandsListPage = lazy(() =>\n import('./pages/brands/BrandsListPage').then((m) => ({ default: m.BrandsListPage }))\n);\nconst BrandDetailPage = lazy(() =>\n import('./pages/brands/BrandDetailPage').then((m) => ({ default: m.BrandDetailPage }))\n);\nconst BrandFormPage = lazy(() =>\n import('./pages/brands/BrandFormPage').then((m) => ({ default: m.BrandFormPage }))\n);\nconst ImportCatalogPage = lazy(() =>\n import('./pages/brands/ImportCatalogPage').then((m) => ({ default: m.ImportCatalogPage }))\n);\n\n// Collections\nconst CollectionsListPage = lazy(() =>\n import('./pages/collections/CollectionsListPage').then((m) => ({\n default: m.CollectionsListPage,\n }))\n);\nconst CollectionDetailPage = lazy(() =>\n import('./pages/collections/CollectionDetailPage').then((m) => ({\n default: m.CollectionDetailPage,\n }))\n);\nconst CollectionFormPage = lazy(() =>\n import('./pages/collections/CollectionFormPage').then((m) => ({ default: m.CollectionFormPage }))\n);\n\n// Style items\nconst StyleItemsListPage = lazy(() =>\n import('./pages/items/StyleItemsListPage').then((m) => ({ default: m.StyleItemsListPage }))\n);\nconst StyleItemDetailPage = lazy(() =>\n import('./pages/items/StyleItemDetailPage').then((m) => ({ default: m.StyleItemDetailPage }))\n);\nconst StyleItemFormPage = lazy(() =>\n import('./pages/items/StyleItemFormPage').then((m) => ({ default: m.StyleItemFormPage }))\n);\n\n// Looks\nconst LooksListPage = lazy(() =>\n import('./pages/looks/LooksListPage').then((m) => ({ default: m.LooksListPage }))\n);\nconst LookDetailPage = lazy(() =>\n import('./pages/looks/LookDetailPage').then((m) => ({ default: m.LookDetailPage }))\n);\nconst LookFormPage = lazy(() =>\n import('./pages/looks/LookFormPage').then((m) => ({ default: m.LookFormPage }))\n);\n\n// Collaborations\nconst CollaborationsListPage = lazy(() =>\n import('./pages/collaborations/CollaborationsListPage').then((m) => ({\n default: m.CollaborationsListPage,\n }))\n);\nconst CollaborationDetailPage = lazy(() =>\n import('./pages/collaborations/CollaborationDetailPage').then((m) => ({\n default: m.CollaborationDetailPage,\n }))\n);\nconst CollaborationFormPage = lazy(() =>\n import('./pages/collaborations/CollaborationFormPage').then((m) => ({\n default: m.CollaborationFormPage,\n }))\n);\n\n// Wardrobe (self-scoped)\nconst WardrobeListPage = lazy(() =>\n import('./pages/wardrobe/WardrobeListPage').then((m) => ({ default: m.WardrobeListPage }))\n);\nconst WardrobeFormPage = lazy(() =>\n import('./pages/wardrobe/WardrobeFormPage').then((m) => ({ default: m.WardrobeFormPage }))\n);\n\n// Lookbooks\nconst LookbooksListPage = lazy(() =>\n import('./pages/lookbooks/LookbooksListPage').then((m) => ({ default: m.LookbooksListPage }))\n);\nconst LookbookDetailPage = lazy(() =>\n import('./pages/lookbooks/LookbookDetailPage').then((m) => ({ default: m.LookbookDetailPage }))\n);\n\n// Style profile (self-scoped)\nconst StyleProfilePage = lazy(() =>\n import('./pages/profile/StyleProfilePage').then((m) => ({ default: m.StyleProfilePage }))\n);\n\n// Discovery + trends (client-side analytics over the real catalogue/social lists)\nconst DiscoverPage = lazy(() =>\n import('./pages/discover/DiscoverPage').then((m) => ({ default: m.DiscoverPage }))\n);\nconst TrendsPage = lazy(() =>\n import('./pages/trends/TrendsPage').then((m) => ({ default: m.TrendsPage }))\n);\n\n// Commerce (BOFF-3370): cart, orders, creator earnings\nconst CartPage = lazy(() =>\n import('./pages/commerce/CartPage').then((m) => ({ default: m.CartPage }))\n);\nconst BrandOrdersPage = lazy(() =>\n import('./pages/commerce/BrandOrdersPage').then((m) => ({ default: m.BrandOrdersPage }))\n);\nconst BrandOrderDetailPage = lazy(() =>\n import('./pages/commerce/BrandOrdersPage').then((m) => ({ default: m.BrandOrderDetailPage }))\n);\nconst OrdersPage = lazy(() =>\n import('./pages/commerce/OrdersPage').then((m) => ({ default: m.OrdersPage }))\n);\nconst OrderDetailPage = lazy(() =>\n import('./pages/commerce/OrdersPage').then((m) => ({ default: m.OrderDetailPage }))\n);\nconst CreatorEarningsPage = lazy(() =>\n import('./pages/commerce/CreatorEarningsPage').then((m) => ({ default: m.CreatorEarningsPage }))\n);\n\n// Admin curation (gated by the `coolandlovely.admin.curation` feature flag)\nconst AdminBrandsPage = lazy(() =>\n import('./pages/admin/AdminBrandsPage').then((m) => ({ default: m.AdminBrandsPage }))\n);\nconst AdminCollaborationsPage = lazy(() =>\n import('./pages/admin/AdminCollaborationsPage').then((m) => ({\n default: m.AdminCollaborationsPage,\n }))\n);\nconst AdminFeaturedItemsPage = lazy(() =>\n import('./pages/admin/AdminFeaturedItemsPage').then((m) => ({\n default: m.AdminFeaturedItemsPage,\n }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-b-2 border-primary\" />\n </div>\n);\n\nexport const CoolAndLovelyRoutes: FC = () => {\n const { basePath, isStandalone } = useCoolAndLovely();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n // Guard: only render if the path matches the basePath (when integrated).\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Overview */}\n <Route index element={<HomePage />} />\n\n {/* Brands */}\n <Route path=\"brands\" element={<BrandsListPage />} />\n <Route path=\"brands/new\" element={<BrandFormPage />} />\n <Route path=\"brands/import\" element={<ImportCatalogPage />} />\n <Route path=\"brands/:brandId\" element={<BrandDetailPage />} />\n <Route path=\"brands/:brandId/edit\" element={<BrandFormPage />} />\n\n {/* Collections */}\n <Route path=\"collections\" element={<CollectionsListPage />} />\n <Route path=\"collections/new\" element={<CollectionFormPage />} />\n <Route path=\"collections/:collectionId\" element={<CollectionDetailPage />} />\n <Route path=\"collections/:collectionId/edit\" element={<CollectionFormPage />} />\n\n {/* Style items */}\n <Route path=\"items\" element={<StyleItemsListPage />} />\n <Route path=\"items/new\" element={<StyleItemFormPage />} />\n <Route path=\"items/:itemId\" element={<StyleItemDetailPage />} />\n <Route path=\"items/:itemId/edit\" element={<StyleItemFormPage />} />\n\n {/* Looks */}\n <Route path=\"looks\" element={<LooksListPage />} />\n <Route path=\"looks/new\" element={<LookFormPage />} />\n <Route path=\"looks/:lookId\" element={<LookDetailPage />} />\n\n {/* Collaborations */}\n <Route path=\"collaborations\" element={<CollaborationsListPage />} />\n <Route path=\"collaborations/new\" element={<CollaborationFormPage />} />\n <Route path=\"collaborations/:collaborationId\" element={<CollaborationDetailPage />} />\n <Route path=\"collaborations/:collaborationId/edit\" element={<CollaborationFormPage />} />\n\n {/* Wardrobe (self-scoped) */}\n <Route path=\"wardrobe\" element={<WardrobeListPage />} />\n <Route path=\"wardrobe/new\" element={<WardrobeFormPage />} />\n\n {/* Lookbooks */}\n <Route path=\"lookbooks\" element={<LookbooksListPage />} />\n <Route path=\"lookbooks/:lookbookId\" element={<LookbookDetailPage />} />\n\n {/* Style profile (self-scoped) + public profiles */}\n <Route path=\"profile\" element={<StyleProfilePage />} />\n <Route path=\"profile/:userId\" element={<StyleProfilePage />} />\n\n {/* Discovery + trends */}\n <Route path=\"discover\" element={<DiscoverPage />} />\n <Route path=\"trends\" element={<TrendsPage />} />\n\n {/* Commerce */}\n <Route path=\"cart\" element={<CartPage />} />\n <Route path=\"orders\" element={<OrdersPage />} />\n <Route path=\"orders/:orderId\" element={<OrderDetailPage />} />\n <Route path=\"earnings\" element={<CreatorEarningsPage />} />\n\n {/* Brand-side orders and fulfilment (BOFF-9636) */}\n <Route path=\"brand-orders\" element={<BrandOrdersPage />} />\n <Route path=\"brand-orders/:brandId/:orderId\" element={<BrandOrderDetailPage />} />\n\n {/* Admin curation */}\n <Route path=\"admin\" element={<Navigate to=\"admin/brands\" replace />} />\n <Route path=\"admin/brands\" element={<AdminBrandsPage />} />\n <Route path=\"admin/collaborations\" element={<AdminCollaborationsPage />} />\n <Route path=\"admin/featured-items\" element={<AdminFeaturedItemsPage />} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CoolAndLovelyRoutes.js","names":[],"sources":["../../src/coolandlovely/CoolAndLovelyRoutes.tsx"],"sourcesContent":["/**\n * CoolAndLovely Routes\n * @module coolandlovely\n *\n * Real fashion-domain routes wired to the live `wspace-coolandlovely-svc`\n * GraphQL contract: overview, brands, collections, style items, looks,\n * collaborations, wardrobe, lookbooks and the shopper's style profile.\n */\n\nimport { type FC, lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router';\nimport { useCoolAndLovely } from './context/CoolAndLovelyContext';\n\n// Eager-loaded overview (primary landing page)\nimport { HomePage } from './pages/HomePage';\n\n// Brands\nconst BrandsListPage = lazy(() =>\n import('./pages/brands/BrandsListPage').then((m) => ({ default: m.BrandsListPage }))\n);\nconst BrandDetailPage = lazy(() =>\n import('./pages/brands/BrandDetailPage').then((m) => ({ default: m.BrandDetailPage }))\n);\nconst BrandFormPage = lazy(() =>\n import('./pages/brands/BrandFormPage').then((m) => ({ default: m.BrandFormPage }))\n);\nconst ImportCatalogPage = lazy(() =>\n import('./pages/brands/ImportCatalogPage').then((m) => ({ default: m.ImportCatalogPage }))\n);\n\n// Collections\nconst CollectionsListPage = lazy(() =>\n import('./pages/collections/CollectionsListPage').then((m) => ({\n default: m.CollectionsListPage,\n }))\n);\nconst CollectionDetailPage = lazy(() =>\n import('./pages/collections/CollectionDetailPage').then((m) => ({\n default: m.CollectionDetailPage,\n }))\n);\nconst CollectionFormPage = lazy(() =>\n import('./pages/collections/CollectionFormPage').then((m) => ({ default: m.CollectionFormPage }))\n);\n\n// Style items\nconst StyleItemsListPage = lazy(() =>\n import('./pages/items/StyleItemsListPage').then((m) => ({ default: m.StyleItemsListPage }))\n);\nconst StyleItemDetailPage = lazy(() =>\n import('./pages/items/StyleItemDetailPage').then((m) => ({ default: m.StyleItemDetailPage }))\n);\nconst StyleItemFormPage = lazy(() =>\n import('./pages/items/StyleItemFormPage').then((m) => ({ default: m.StyleItemFormPage }))\n);\n\n// Looks\nconst LooksListPage = lazy(() =>\n import('./pages/looks/LooksListPage').then((m) => ({ default: m.LooksListPage }))\n);\nconst LookDetailPage = lazy(() =>\n import('./pages/looks/LookDetailPage').then((m) => ({ default: m.LookDetailPage }))\n);\nconst LookFormPage = lazy(() =>\n import('./pages/looks/LookFormPage').then((m) => ({ default: m.LookFormPage }))\n);\n\n// Collaborations\nconst CollaborationsListPage = lazy(() =>\n import('./pages/collaborations/CollaborationsListPage').then((m) => ({\n default: m.CollaborationsListPage,\n }))\n);\nconst CollaborationDetailPage = lazy(() =>\n import('./pages/collaborations/CollaborationDetailPage').then((m) => ({\n default: m.CollaborationDetailPage,\n }))\n);\nconst CollaborationFormPage = lazy(() =>\n import('./pages/collaborations/CollaborationFormPage').then((m) => ({\n default: m.CollaborationFormPage,\n }))\n);\n\n// Wardrobe (self-scoped)\nconst WardrobeListPage = lazy(() =>\n import('./pages/wardrobe/WardrobeListPage').then((m) => ({ default: m.WardrobeListPage }))\n);\nconst WardrobeFormPage = lazy(() =>\n import('./pages/wardrobe/WardrobeFormPage').then((m) => ({ default: m.WardrobeFormPage }))\n);\n\n// Lookbooks\nconst LookbooksListPage = lazy(() =>\n import('./pages/lookbooks/LookbooksListPage').then((m) => ({ default: m.LookbooksListPage }))\n);\nconst LookbookDetailPage = lazy(() =>\n import('./pages/lookbooks/LookbookDetailPage').then((m) => ({ default: m.LookbookDetailPage }))\n);\n\n// Style profile (self-scoped)\nconst StyleProfilePage = lazy(() =>\n import('./pages/profile/StyleProfilePage').then((m) => ({ default: m.StyleProfilePage }))\n);\n\n// Discovery + trends (client-side analytics over the real catalogue/social lists)\nconst DiscoverPage = lazy(() =>\n import('./pages/discover/DiscoverPage').then((m) => ({ default: m.DiscoverPage }))\n);\nconst TrendsPage = lazy(() =>\n import('./pages/trends/TrendsPage').then((m) => ({ default: m.TrendsPage }))\n);\nconst EditorialPage = lazy(() =>\n import('./pages/editorial/EditorialPage').then((module) => ({ default: module.EditorialPage }))\n);\n\n// Commerce (BOFF-3370): cart, orders, creator earnings\nconst CartPage = lazy(() =>\n import('./pages/commerce/CartPage').then((m) => ({ default: m.CartPage }))\n);\nconst BrandOrdersPage = lazy(() =>\n import('./pages/commerce/BrandOrdersPage').then((m) => ({ default: m.BrandOrdersPage }))\n);\nconst BrandOrderDetailPage = lazy(() =>\n import('./pages/commerce/BrandOrdersPage').then((m) => ({ default: m.BrandOrderDetailPage }))\n);\nconst OrdersPage = lazy(() =>\n import('./pages/commerce/OrdersPage').then((m) => ({ default: m.OrdersPage }))\n);\nconst OrderDetailPage = lazy(() =>\n import('./pages/commerce/OrdersPage').then((m) => ({ default: m.OrderDetailPage }))\n);\nconst CreatorEarningsPage = lazy(() =>\n import('./pages/commerce/CreatorEarningsPage').then((m) => ({ default: m.CreatorEarningsPage }))\n);\n\n// Admin curation (gated by the `coolandlovely.admin.curation` feature flag)\nconst AdminBrandsPage = lazy(() =>\n import('./pages/admin/AdminBrandsPage').then((m) => ({ default: m.AdminBrandsPage }))\n);\nconst AdminCollaborationsPage = lazy(() =>\n import('./pages/admin/AdminCollaborationsPage').then((m) => ({\n default: m.AdminCollaborationsPage,\n }))\n);\nconst AdminFeaturedItemsPage = lazy(() =>\n import('./pages/admin/AdminFeaturedItemsPage').then((m) => ({\n default: m.AdminFeaturedItemsPage,\n }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-b-2 border-primary\" />\n </div>\n);\n\nexport const CoolAndLovelyRoutes: FC = () => {\n const { basePath, isStandalone, workspaceId, currentUser } = useCoolAndLovely();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n // Guard: only render if the path matches the basePath (when integrated).\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Overview */}\n <Route index element={<HomePage />} />\n\n {/* Brands */}\n <Route path=\"brands\" element={<BrandsListPage />} />\n <Route path=\"brands/new\" element={<BrandFormPage />} />\n <Route path=\"brands/import\" element={<ImportCatalogPage />} />\n <Route path=\"brands/:brandId\" element={<BrandDetailPage />} />\n <Route path=\"brands/:brandId/edit\" element={<BrandFormPage />} />\n\n {/* Collections */}\n <Route path=\"collections\" element={<CollectionsListPage />} />\n <Route path=\"collections/new\" element={<CollectionFormPage />} />\n <Route path=\"collections/:collectionId\" element={<CollectionDetailPage />} />\n <Route path=\"collections/:collectionId/edit\" element={<CollectionFormPage />} />\n\n {/* Style items */}\n <Route path=\"items\" element={<StyleItemsListPage />} />\n <Route path=\"items/new\" element={<StyleItemFormPage />} />\n <Route path=\"items/:itemId\" element={<StyleItemDetailPage />} />\n <Route path=\"items/:itemId/edit\" element={<StyleItemFormPage />} />\n\n {/* Looks */}\n <Route path=\"looks\" element={<LooksListPage />} />\n <Route path=\"looks/new\" element={<LookFormPage />} />\n <Route path=\"looks/:lookId\" element={<LookDetailPage />} />\n\n {/* Collaborations */}\n <Route path=\"collaborations\" element={<CollaborationsListPage />} />\n <Route path=\"collaborations/new\" element={<CollaborationFormPage />} />\n <Route path=\"collaborations/:collaborationId\" element={<CollaborationDetailPage />} />\n <Route path=\"collaborations/:collaborationId/edit\" element={<CollaborationFormPage />} />\n\n {/* Wardrobe (self-scoped) */}\n <Route path=\"wardrobe\" element={<WardrobeListPage />} />\n <Route path=\"wardrobe/new\" element={<WardrobeFormPage />} />\n\n {/* Lookbooks */}\n <Route path=\"lookbooks\" element={<LookbooksListPage />} />\n <Route path=\"lookbooks/:lookbookId\" element={<LookbookDetailPage />} />\n\n {/* Style profile (self-scoped) + public profiles */}\n <Route path=\"profile\" element={<StyleProfilePage />} />\n <Route path=\"profile/:userId\" element={<StyleProfilePage />} />\n\n {/* Discovery + trends */}\n <Route path=\"discover\" element={<DiscoverPage />} />\n <Route path=\"trends\" element={<TrendsPage />} />\n <Route\n path=\"journal\"\n element={<EditorialPage key={`${workspaceId ?? ''}:${currentUser?.id ?? ''}`} />}\n />\n\n {/* Commerce */}\n <Route path=\"cart\" element={<CartPage />} />\n <Route path=\"orders\" element={<OrdersPage />} />\n <Route path=\"orders/:orderId\" element={<OrderDetailPage />} />\n <Route path=\"earnings\" element={<CreatorEarningsPage />} />\n\n {/* Brand-side orders and fulfilment (BOFF-9636) */}\n <Route path=\"brand-orders\" element={<BrandOrdersPage />} />\n <Route path=\"brand-orders/:brandId/:orderId\" element={<BrandOrderDetailPage />} />\n\n {/* Admin curation */}\n <Route path=\"admin\" element={<Navigate to=\"admin/brands\" replace />} />\n <Route path=\"admin/brands\" element={<AdminBrandsPage />} />\n <Route path=\"admin/collaborations\" element={<AdminCollaborationsPage />} />\n <Route path=\"admin/featured-items\" element={<AdminFeaturedItemsPage />} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":";;;;;;AAiBA,IAAM,IAAiB,QACrB,OAAO,mCAAgC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CACrF,GACM,IAAkB,QACtB,OAAO,oCAAiC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,CACvF,GACM,IAAgB,QACpB,OAAO,kCAA+B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CACnF,GACM,IAAoB,QACxB,OAAO,sCAAmC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC3F,GAGM,IAAsB,QAC1B,OAAO,6CAA0C,CAAC,MAAM,OAAO,EAC7D,SAAS,EAAE,oBACb,EAAE,CACJ,GACM,IAAuB,QAC3B,OAAO,8CAA2C,CAAC,MAAM,OAAO,EAC9D,SAAS,EAAE,qBACb,EAAE,CACJ,GACM,IAAqB,QACzB,OAAO,4CAAyC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAClG,GAGM,IAAqB,QACzB,OAAO,sCAAmC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAC5F,GACM,IAAsB,QAC1B,OAAO,uCAAoC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAC9F,GACM,IAAoB,QACxB,OAAO,qCAAkC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC1F,GAGM,IAAgB,QACpB,OAAO,iCAA8B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAClF,GACM,IAAiB,QACrB,OAAO,kCAA+B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CACpF,GACM,IAAe,QACnB,OAAO,gCAA6B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAChF,GAGM,IAAyB,QAC7B,OAAO,mDAAgD,CAAC,MAAM,OAAO,EACnE,SAAS,EAAE,uBACb,EAAE,CACJ,GACM,IAA0B,QAC9B,OAAO,oDAAiD,CAAC,MAAM,OAAO,EACpE,SAAS,EAAE,wBACb,EAAE,CACJ,GACM,IAAwB,QAC5B,OAAO,kDAA+C,CAAC,MAAM,OAAO,EAClE,SAAS,EAAE,sBACb,EAAE,CACJ,GAGM,IAAmB,QACvB,OAAO,uCAAoC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAC3F,GACM,IAAmB,QACvB,OAAO,uCAAoC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAC3F,GAGM,IAAoB,QACxB,OAAO,yCAAsC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC9F,GACM,IAAqB,QACzB,OAAO,0CAAuC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAChG,GAGM,IAAmB,QACvB,OAAO,sCAAmC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAC1F,GAGM,IAAe,QACnB,OAAO,mCAAgC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CACnF,GACM,IAAa,QACjB,OAAO,+BAA4B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAC7E,GACM,IAAgB,QACpB,OAAO,qCAAkC,CAAC,MAAM,OAAY,EAAE,SAAS,EAAO,cAAc,EAAE,CAChG,GAGM,IAAW,QACf,OAAO,+BAA4B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAC3E,GACM,IAAkB,QACtB,OAAO,sCAAmC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,CACzF,GACM,IAAuB,QAC3B,OAAO,sCAAmC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CAC9F,GACM,IAAa,QACjB,OAAO,iCAA8B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAC/E,GACM,IAAkB,QACtB,OAAO,iCAA8B,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,CACpF,GACM,IAAsB,QAC1B,OAAO,0CAAuC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CACjG,GAGM,IAAkB,QACtB,OAAO,mCAAgC,CAAC,MAAM,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,CACtF,GACM,IAA0B,QAC9B,OAAO,2CAAwC,CAAC,MAAM,OAAO,EAC3D,SAAS,EAAE,wBACb,EAAE,CACJ,GACM,IAAyB,QAC7B,OAAO,0CAAuC,CAAC,MAAM,OAAO,EAC1D,SAAS,EAAE,uBACb,EAAE,CACJ,GAEM,UACJ,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,OAAD,EAAK,WAAU,8DAA+D,CAAA;AAC3E,CAAA,GAGM,UAAgC;CAC3C,IAAM,EAAE,aAAU,iBAAc,gBAAa,mBAAgB,EAAiB,GACxE,IAAW,EAAY,GAEvB,IAAqB,MAAa,MAAM,MAAM,KAAY,GAAA,CAAI,QAAQ,OAAO,EAAE;CAYrF,OATI,KAAsB,CAAC,KAIrB,EAFF,EAAS,aAAa,KACtB,EAAS,SAAS,WAAW,IAAqB,GAAG,KAE9C,OAKT,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GAAU,UAAU,kBAAC,GAAD,CAAa,CAAA;aAC/B,kBAAC,GAAD,EAAA,UAAA;IAEE,kBAAC,GAAD;KAAO,OAAA;KAAM,SAAS,kBAAC,GAAD,CAAW,CAAA;IAAI,CAAA;IAGrC,kBAAC,GAAD;KAAO,MAAK;KAAS,SAAS,kBAAC,GAAD,CAAiB,CAAA;IAAI,CAAA;IACnD,kBAAC,GAAD;KAAO,MAAK;KAAa,SAAS,kBAAC,GAAD,CAAgB,CAAA;IAAI,CAAA;IACtD,kBAAC,GAAD;KAAO,MAAK;KAAgB,SAAS,kBAAC,GAAD,CAAoB,CAAA;IAAI,CAAA;IAC7D,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,CAAkB,CAAA;IAAI,CAAA;IAC7D,kBAAC,GAAD;KAAO,MAAK;KAAuB,SAAS,kBAAC,GAAD,CAAgB,CAAA;IAAI,CAAA;IAGhE,kBAAC,GAAD;KAAO,MAAK;KAAc,SAAS,kBAAC,GAAD,CAAsB,CAAA;IAAI,CAAA;IAC7D,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,CAAqB,CAAA;IAAI,CAAA;IAChE,kBAAC,GAAD;KAAO,MAAK;KAA4B,SAAS,kBAAC,GAAD,CAAuB,CAAA;IAAI,CAAA;IAC5E,kBAAC,GAAD;KAAO,MAAK;KAAiC,SAAS,kBAAC,GAAD,CAAqB,CAAA;IAAI,CAAA;IAG/E,kBAAC,GAAD;KAAO,MAAK;KAAQ,SAAS,kBAAC,GAAD,CAAqB,CAAA;IAAI,CAAA;IACtD,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,CAAoB,CAAA;IAAI,CAAA;IACzD,kBAAC,GAAD;KAAO,MAAK;KAAgB,SAAS,kBAAC,GAAD,CAAsB,CAAA;IAAI,CAAA;IAC/D,kBAAC,GAAD;KAAO,MAAK;KAAqB,SAAS,kBAAC,GAAD,CAAoB,CAAA;IAAI,CAAA;IAGlE,kBAAC,GAAD;KAAO,MAAK;KAAQ,SAAS,kBAAC,GAAD,CAAgB,CAAA;IAAI,CAAA;IACjD,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,CAAe,CAAA;IAAI,CAAA;IACpD,kBAAC,GAAD;KAAO,MAAK;KAAgB,SAAS,kBAAC,GAAD,CAAiB,CAAA;IAAI,CAAA;IAG1D,kBAAC,GAAD;KAAO,MAAK;KAAiB,SAAS,kBAAC,GAAD,CAAyB,CAAA;IAAI,CAAA;IACnE,kBAAC,GAAD;KAAO,MAAK;KAAqB,SAAS,kBAAC,GAAD,CAAwB,CAAA;IAAI,CAAA;IACtE,kBAAC,GAAD;KAAO,MAAK;KAAkC,SAAS,kBAAC,GAAD,CAA0B,CAAA;IAAI,CAAA;IACrF,kBAAC,GAAD;KAAO,MAAK;KAAuC,SAAS,kBAAC,GAAD,CAAwB,CAAA;IAAI,CAAA;IAGxF,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,CAAmB,CAAA;IAAI,CAAA;IACvD,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,CAAmB,CAAA;IAAI,CAAA;IAG3D,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,CAAoB,CAAA;IAAI,CAAA;IACzD,kBAAC,GAAD;KAAO,MAAK;KAAwB,SAAS,kBAAC,GAAD,CAAqB,CAAA;IAAI,CAAA;IAGtE,kBAAC,GAAD;KAAO,MAAK;KAAU,SAAS,kBAAC,GAAD,CAAmB,CAAA;IAAI,CAAA;IACtD,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,CAAmB,CAAA;IAAI,CAAA;IAG9D,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,CAAe,CAAA;IAAI,CAAA;IACnD,kBAAC,GAAD;KAAO,MAAK;KAAS,SAAS,kBAAC,GAAD,CAAa,CAAA;IAAI,CAAA;IAC/C,kBAAC,GAAD;KACE,MAAK;KACL,SAAS,kBAAC,GAAD,CAAsE,GAAlD,GAAG,KAAe,GAAG,GAAG,GAAa,MAAM,IAAO;IAChF,CAAA;IAGD,kBAAC,GAAD;KAAO,MAAK;KAAO,SAAS,kBAAC,GAAD,CAAW,CAAA;IAAI,CAAA;IAC3C,kBAAC,GAAD;KAAO,MAAK;KAAS,SAAS,kBAAC,GAAD,CAAa,CAAA;IAAI,CAAA;IAC/C,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,CAAkB,CAAA;IAAI,CAAA;IAC7D,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,CAAsB,CAAA;IAAI,CAAA;IAG1D,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,CAAkB,CAAA;IAAI,CAAA;IAC1D,kBAAC,GAAD;KAAO,MAAK;KAAiC,SAAS,kBAAC,GAAD,CAAuB,CAAA;IAAI,CAAA;IAGjF,kBAAC,GAAD;KAAO,MAAK;KAAQ,SAAS,kBAAC,GAAD;MAAU,IAAG;MAAe,SAAA;KAAS,CAAA;IAAI,CAAA;IACtE,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,CAAkB,CAAA;IAAI,CAAA;IAC1D,kBAAC,GAAD;KAAO,MAAK;KAAuB,SAAS,kBAAC,GAAD,CAA0B,CAAA;IAAI,CAAA;IAC1E,kBAAC,GAAD;KAAO,MAAK;KAAuB,SAAS,kBAAC,GAAD,CAAyB,CAAA;IAAI,CAAA;IAGzE,kBAAC,GAAD;KAAO,MAAK;KAAI,SAAS,kBAAC,GAAD;MAAU,IAAG;MAAG,SAAA;KAAS,CAAA;IAAI,CAAA;GAChD,EAAA,CAAA;EACA,CAAA;CACP,CAAA;AAET"}
@@ -65,6 +65,6 @@ var p = ({ resourceType: i, resourceId: p, title: m = "Audit trail", limit: h =
65
65
  });
66
66
  };
67
67
  //#endregion
68
- export { p as ActivityTrail };
68
+ export { p as ActivityTrail, f as formatActivityAction };
69
69
 
70
70
  //# sourceMappingURL=ActivityTrail.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ActivityTrail.js","names":[],"sources":["../../../src/coolandlovely/components/ActivityTrail.tsx"],"sourcesContent":["/**\n * ActivityTrail — resource-scoped audit trail for a CoolAndLovely record.\n * @module coolandlovely/components\n *\n * Reads the platform `ActivityLog` rows that the svc's mutation-audit plugin\n * already writes for every `coo*` mutation (see `hooks/useActivity.ts`). Who\n * changed what, when — resolved to a real display name via `ActorName`.\n *\n * No fabricated history: a record with no rows shows an empty state, and a\n * caller without the workspace `activity-log:read` permission sees the error\n * instead of a convincing-looking blank timeline.\n */\n\nimport type { FC } from 'react';\nimport { History, Loader2, RotateCw } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { ActorName } from './ActorName';\nimport { useEntityActivity } from '../hooks/useActivity';\nimport { formatDate, formatRelativeTime, titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\n\ninterface ActivityTrailProps {\n /** ActivityLog.resourceType written by the svc, e.g. \"coolandlovely-brand\". */\n resourceType: string;\n /** The record id (the panel stays idle until it is present). */\n resourceId?: string;\n /** Section heading (default \"Audit trail\"). */\n title?: string;\n /** Max entries to request (default 20). */\n limit?: number;\n className?: string;\n}\n\n/**\n * `coolandlovely.brand_transitioned` → \"Brand transitioned\".\n *\n * Only reshapes the snake_case event names the audit plugin emits; anything\n * else (the generic `graphql_mutation` fallback, which carries the raw field\n * name) is shown verbatim rather than mangled by a lowercase pass.\n */\nexport function formatActivityAction(action: string): string {\n const tail = action.includes('.') ? action.slice(action.lastIndexOf('.') + 1) : action;\n if (!/^[a-z0-9_]+$/.test(tail)) return action;\n const label = titleCase(tail);\n return label.charAt(0) + label.slice(1).toLowerCase();\n}\n\nexport const ActivityTrail: FC<ActivityTrailProps> = ({\n resourceType,\n resourceId,\n title = 'Audit trail',\n limit = 20,\n className,\n}) => {\n const query = useEntityActivity(resourceType, resourceId, limit);\n const entries = query.data ?? [];\n\n return (\n <GlassCard treatment=\"plain\" className={cn('p-5', className)}>\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <History className=\"h-4 w-4\" /> {title}\n </h3>\n\n {query.isError ? (\n <div className=\"space-y-2\">\n <p className=\"text-sm text-text-secondary\">\n Activity could not be loaded for this record.\n </p>\n <button\n type=\"button\"\n onClick={() => void query.refetch()}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <RotateCw className=\"h-3.5 w-3.5\" /> Retry\n </button>\n </div>\n ) : !query.isFetched ? (\n // Deliberately NOT `isLoading`: a query held back (no record id, no\n // workspace) has never run, and \"no changes recorded\" would be a claim\n // about data we never asked for.\n <p className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Loading activity…\n </p>\n ) : entries.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No changes recorded yet. Edits, lifecycle transitions and archival are logged here as they\n happen.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-seam\">\n {entries.map((entry) => (\n <li key={entry.id} className=\"flex items-start justify-between gap-3 py-2.5 first:pt-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {formatActivityAction(entry.action)}\n </p>\n <p className=\"mt-0.5 truncate text-xs text-text-secondary\">\n <ActorName id={entry.userId} />\n </p>\n </div>\n <time\n dateTime={entry.timestamp}\n title={formatDate(entry.timestamp)}\n className=\"shrink-0 whitespace-nowrap text-xs text-text-secondary\"\n >\n {formatRelativeTime(entry.timestamp)}\n </time>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n );\n};\n"],"mappings":";;;;;;;;AAwCA,SAAgB,EAAqB,GAAwB;CAC3D,IAAM,IAAO,EAAO,SAAS,IAAI,GAAG,EAAO,MAAM,EAAO,YAAY,IAAI,GAAG,EAAE,GAAG;AAChF,KAAI,CAAC,eAAe,KAAK,EAAK,CAAE,QAAO;CACvC,IAAM,IAAQ,EAAU,EAAK;AAC7B,QAAO,EAAM,OAAO,EAAE,GAAG,EAAM,MAAM,EAAE,CAAC,aAAa;;AAGvD,IAAa,KAAyC,EACpD,iBACA,eACA,WAAQ,eACR,WAAQ,IACR,mBACI;CACJ,IAAM,IAAQ,EAAkB,GAAc,GAAY,EAAM,EAC1D,IAAU,EAAM,QAAQ,EAAE;AAEhC,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAW,EAAG,OAAO,EAAU;YAA5D,CACE,kBAAC,MAAD;GAAI,WAAU;aAAd;IACE,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;;IAAE;IAC9B;MAEJ,EAAM,UACL,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA,EACJ,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,KAAK,EAAM,SAAS;IACnC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EAAA,SAC7B;MACL;OACH,EAAM,YAOP,EAAQ,WAAW,IACrB,kBAAC,KAAD;GAAG,WAAU;aAA8B;GAGvC,CAAA,GAEJ,kBAAC,MAAD;GAAI,WAAU;aACX,EAAQ,KAAK,MACZ,kBAAC,MAAD;IAAmB,WAAU;cAA7B,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAqB,EAAM,OAAO;MACjC,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBACX,kBAAC,GAAD,EAAW,IAAI,EAAM,QAAU,CAAA;MAC7B,CAAA,CACA;QACN,kBAAC,QAAD;KACE,UAAU,EAAM;KAChB,OAAO,EAAW,EAAM,UAAU;KAClC,WAAU;eAET,EAAmB,EAAM,UAAU;KAC/B,CAAA,CACJ;MAhBI,EAAM,GAgBV,CACL;GACC,CAAA,GA7BL,kBAAC,KAAD;GAAG,WAAU;aAAb,CACE,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,EAAA,qBAC1C;KA6BI"}
1
+ {"version":3,"file":"ActivityTrail.js","names":[],"sources":["../../../src/coolandlovely/components/ActivityTrail.tsx"],"sourcesContent":["/**\n * ActivityTrail — resource-scoped audit trail for a CoolAndLovely record.\n * @module coolandlovely/components\n *\n * Reads the platform `ActivityLog` rows that the svc's mutation-audit plugin\n * already writes for every `coo*` mutation (see `hooks/useActivity.ts`). Who\n * changed what, when — resolved to a real display name via `ActorName`.\n *\n * No fabricated history: a record with no rows shows an empty state, and a\n * caller without the workspace `activity-log:read` permission sees the error\n * instead of a convincing-looking blank timeline.\n */\n\nimport type { FC } from 'react';\nimport { History, Loader2, RotateCw } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { ActorName } from './ActorName';\nimport { useEntityActivity } from '../hooks/useActivity';\nimport { formatDate, formatRelativeTime, titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\n\ninterface ActivityTrailProps {\n /** ActivityLog.resourceType written by the svc, e.g. \"coolandlovely-brand\". */\n resourceType: string;\n /** The record id (the panel stays idle until it is present). */\n resourceId?: string;\n /** Section heading (default \"Audit trail\"). */\n title?: string;\n /** Max entries to request (default 20). */\n limit?: number;\n className?: string;\n}\n\n/**\n * `coolandlovely.brand_transitioned` → \"Brand transitioned\".\n *\n * Only reshapes the snake_case event names the audit plugin emits; anything\n * else (the generic `graphql_mutation` fallback, which carries the raw field\n * name) is shown verbatim rather than mangled by a lowercase pass.\n */\nexport function formatActivityAction(action: string): string {\n const tail = action.includes('.') ? action.slice(action.lastIndexOf('.') + 1) : action;\n if (!/^[a-z0-9_]+$/.test(tail)) return action;\n const label = titleCase(tail);\n return label.charAt(0) + label.slice(1).toLowerCase();\n}\n\nexport const ActivityTrail: FC<ActivityTrailProps> = ({\n resourceType,\n resourceId,\n title = 'Audit trail',\n limit = 20,\n className,\n}) => {\n const query = useEntityActivity(resourceType, resourceId, limit);\n const entries = query.data ?? [];\n\n return (\n <GlassCard treatment=\"plain\" className={cn('p-5', className)}>\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <History className=\"h-4 w-4\" /> {title}\n </h3>\n\n {query.isError ? (\n <div className=\"space-y-2\">\n <p className=\"text-sm text-text-secondary\">\n Activity could not be loaded for this record.\n </p>\n <button\n type=\"button\"\n onClick={() => void query.refetch()}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <RotateCw className=\"h-3.5 w-3.5\" /> Retry\n </button>\n </div>\n ) : !query.isFetched ? (\n // Deliberately NOT `isLoading`: a query held back (no record id, no\n // workspace) has never run, and \"no changes recorded\" would be a claim\n // about data we never asked for.\n <p className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Loading activity…\n </p>\n ) : entries.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No changes recorded yet. Edits, lifecycle transitions and archival are logged here as they\n happen.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-seam\">\n {entries.map((entry) => (\n <li key={entry.id} className=\"flex items-start justify-between gap-3 py-2.5 first:pt-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {formatActivityAction(entry.action)}\n </p>\n <p className=\"mt-0.5 truncate text-xs text-text-secondary\">\n <ActorName id={entry.userId} />\n </p>\n </div>\n <time\n dateTime={entry.timestamp}\n title={formatDate(entry.timestamp)}\n className=\"shrink-0 whitespace-nowrap text-xs text-text-secondary\"\n >\n {formatRelativeTime(entry.timestamp)}\n </time>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n );\n};\n"],"mappings":";;;;;;;;AAwCA,SAAgB,EAAqB,GAAwB;CAC3D,IAAM,IAAO,EAAO,SAAS,GAAG,IAAI,EAAO,MAAM,EAAO,YAAY,GAAG,IAAI,CAAC,IAAI;CAChF,IAAI,CAAC,eAAe,KAAK,CAAI,GAAG,OAAO;CACvC,IAAM,IAAQ,EAAU,CAAI;CAC5B,OAAO,EAAM,OAAO,CAAC,IAAI,EAAM,MAAM,CAAC,CAAC,CAAC,YAAY;AACtD;AAEA,IAAa,KAAyC,EACpD,iBACA,eACA,WAAQ,eACR,WAAQ,IACR,mBACI;CACJ,IAAM,IAAQ,EAAkB,GAAc,GAAY,CAAK,GACzD,IAAU,EAAM,QAAQ,CAAC;CAE/B,OACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAW,EAAG,OAAO,CAAS;YAA3D,CACE,kBAAC,MAAD;GAAI,WAAU;aAAd;IACE,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;IAAC;IAAE;GAC/B;MAEH,EAAM,UACL,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA8B;GAExC,CAAA,GACH,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,KAAK,EAAM,QAAQ;IAClC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,GAAC,QAC9B;KACL;OACF,EAAM,YAOP,EAAQ,WAAW,IACrB,kBAAC,KAAD;GAAG,WAAU;aAA8B;EAGxC,CAAA,IAEH,kBAAC,MAAD;GAAI,WAAU;aACX,EAAQ,KAAK,MACZ,kBAAC,MAAD;IAAmB,WAAU;cAA7B,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAqB,EAAM,MAAM;KACjC,CAAA,GACH,kBAAC,KAAD;MAAG,WAAU;gBACX,kBAAC,GAAD,EAAW,IAAI,EAAM,OAAS,CAAA;KAC7B,CAAA,CACA;QACL,kBAAC,QAAD;KACE,UAAU,EAAM;KAChB,OAAO,EAAW,EAAM,SAAS;KACjC,WAAU;eAET,EAAmB,EAAM,SAAS;IAC/B,CAAA,CACJ;MAhBK,EAAM,EAgBX,CACL;EACC,CAAA,IA7BJ,kBAAC,KAAD;GAAG,WAAU;aAAb,CACE,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,GAAC,oBAC3C;IA6BI;;AAEf"}
@@ -1 +1 @@
1
- {"version":3,"file":"ActorName.js","names":[],"sources":["../../../src/coolandlovely/components/ActorName.tsx"],"sourcesContent":["/**\n * ActorName — render an actor UUID as a human-readable display name.\n * @module coolandlovely/components\n *\n * Wraps fe-libs `useActorProfiles`, which resolves identities via the GLOBAL\n * gateway (workspace-member → org-member → admin lookup → local fallback) with\n * an internal 5-minute cache. NEVER select firstName/name on a federated\n * wspace `User` — resolve names here instead. The MFE is handed a *workspace*\n * `apiGatewayUrl`; `useActorProfiles` detects that and only uses it for the\n * workspace-scoped tier, resolving the global gateway from the hostname itself,\n * so forwarding our workspace URL is safe. Failures degrade to a short-id\n * fallback, never an error.\n */\n\nimport type { FC } from 'react';\nimport { useMemo } from 'react';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\n\ninterface ActorNameProps {\n /** Actor id (usually a user UUID). */\n id?: string | null;\n /** Actor type — 'user' (default), 'app', 'service', 'super_admin'. */\n actorType?: string;\n className?: string;\n /** Shown when `id` is absent/empty (default \"—\"). */\n fallback?: string;\n}\n\nexport const ActorName: FC<ActorNameProps> = ({\n id,\n actorType = 'user',\n className,\n fallback = '—',\n}) => {\n const { apiGatewayUrl, authToken, workspaceId } = useCoolAndLovely();\n\n const actors = useMemo<ActorInput[]>(\n () => (id ? [{ actorId: id, actorType }] : []),\n [id, actorType]\n );\n const profiles = useActorProfiles(actors, { apiGatewayUrl, authToken, workspaceId });\n\n if (!id) return <span className={className}>{fallback}</span>;\n\n const profile = profiles[id];\n const displayName = profile?.displayName ?? id;\n\n return (\n <span className={className} title={profile?.email ?? id}>\n {displayName}\n </span>\n );\n};\n"],"mappings":";;;;;AA6BA,IAAa,KAAiC,EAC5C,OACA,eAAY,QACZ,cACA,cAAW,UACP;CACJ,IAAM,EAAE,kBAAe,cAAW,mBAAgB,GAAkB,EAM9D,IAAW,EAJF,QACN,IAAK,CAAC;EAAE,SAAS;EAAI;EAAW,CAAC,GAAG,EAAE,EAC7C,CAAC,GAAI,EAAU,CAChB,EACyC;EAAE;EAAe;EAAW;EAAa,CAAC;AAEpF,KAAI,CAAC,EAAI,QAAO,kBAAC,QAAD;EAAiB;YAAY;EAAgB,CAAA;CAE7D,IAAM,IAAU,EAAS,IACnB,IAAc,GAAS,eAAe;AAE5C,QACE,kBAAC,QAAD;EAAiB;EAAW,OAAO,GAAS,SAAS;YAClD;EACI,CAAA"}
1
+ {"version":3,"file":"ActorName.js","names":[],"sources":["../../../src/coolandlovely/components/ActorName.tsx"],"sourcesContent":["/**\n * ActorName — render an actor UUID as a human-readable display name.\n * @module coolandlovely/components\n *\n * Wraps fe-libs `useActorProfiles`, which resolves identities via the GLOBAL\n * gateway (workspace-member → org-member → admin lookup → local fallback) with\n * an internal 5-minute cache. NEVER select firstName/name on a federated\n * wspace `User` — resolve names here instead. The MFE is handed a *workspace*\n * `apiGatewayUrl`; `useActorProfiles` detects that and only uses it for the\n * workspace-scoped tier, resolving the global gateway from the hostname itself,\n * so forwarding our workspace URL is safe. Failures degrade to a short-id\n * fallback, never an error.\n */\n\nimport type { FC } from 'react';\nimport { useMemo } from 'react';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\n\ninterface ActorNameProps {\n /** Actor id (usually a user UUID). */\n id?: string | null;\n /** Actor type — 'user' (default), 'app', 'service', 'super_admin'. */\n actorType?: string;\n className?: string;\n /** Shown when `id` is absent/empty (default \"—\"). */\n fallback?: string;\n}\n\nexport const ActorName: FC<ActorNameProps> = ({\n id,\n actorType = 'user',\n className,\n fallback = '—',\n}) => {\n const { apiGatewayUrl, authToken, workspaceId } = useCoolAndLovely();\n\n const actors = useMemo<ActorInput[]>(\n () => (id ? [{ actorId: id, actorType }] : []),\n [id, actorType]\n );\n const profiles = useActorProfiles(actors, { apiGatewayUrl, authToken, workspaceId });\n\n if (!id) return <span className={className}>{fallback}</span>;\n\n const profile = profiles[id];\n const displayName = profile?.displayName ?? id;\n\n return (\n <span className={className} title={profile?.email ?? id}>\n {displayName}\n </span>\n );\n};\n"],"mappings":";;;;;AA6BA,IAAa,KAAiC,EAC5C,OACA,eAAY,QACZ,cACA,cAAW,UACP;CACJ,IAAM,EAAE,kBAAe,cAAW,mBAAgB,EAAiB,GAM7D,IAAW,EAJF,QACN,IAAK,CAAC;EAAE,SAAS;EAAI;CAAU,CAAC,IAAI,CAAC,GAC5C,CAAC,GAAI,CAAS,CAEkB,GAAQ;EAAE;EAAe;EAAW;CAAY,CAAC;CAEnF,IAAI,CAAC,GAAI,OAAO,kBAAC,QAAD;EAAiB;YAAY;CAAe,CAAA;CAE5D,IAAM,IAAU,EAAS,IACnB,IAAc,GAAS,eAAe;CAE5C,OACE,kBAAC,QAAD;EAAiB;EAAW,OAAO,GAAS,SAAS;YAClD;CACG,CAAA;AAEV"}
@@ -1 +1 @@
1
- {"version":3,"file":"AddToCartPanel.js","names":[],"sources":["../../../src/coolandlovely/components/AddToCartPanel.tsx"],"sourcesContent":["/**\n * AddToCartPanel — size / colour / quantity picker + \"Add to cart\" for a catalogue piece.\n * @module coolandlovely/components\n *\n * When the shopper arrived from a look (\"shop the look\"), the look id rides along so the look's\n * creator earns the commission once the order is paid (BOFF-3370).\n *\n * When the brand tracks stock for the piece (BOFF-9637), sold-out sizes and colours are disabled,\n * the quantity is capped at what is left, and a low stock count is shown. The service checks the\n * stock again when the piece is added and when the order is placed.\n */\n\nimport { type FC, useState } from 'react';\nimport { Check, Loader2, ShoppingBag, Sparkles } from 'lucide-react';\n\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useTr } from '../hooks';\nimport { useAddToCart } from '../hooks/useCommerceApi';\nimport { useStyleItemInventory } from '../hooks/useInventoryApi';\nimport type { CooStyleItem, CooStyleItemInventory } from '../types';\nimport { cn } from '../utils/cn';\n\ninterface AddToCartPanelProps {\n item: Pick<CooStyleItem, 'id' | 'status' | 'price' | 'sizes' | 'colours'>;\n lookId?: string | null;\n}\n\nconst MAX_QUANTITY = 10;\n/** Stock at or below this is shown to the shopper as \"Only N left\". */\nconst LOW_STOCK = 5;\n\n/**\n * Units left of a size/colour choice while the piece tracks stock; null when it does not (or the\n * stock could not be read — the service still enforces it). A partial choice (size picked, colour\n * not yet) sums every variant that matches it.\n */\nfunction unitsLeft(\n inventory: CooStyleItemInventory | null | undefined,\n size: string | null,\n colour: string | null\n): number | null {\n if (!inventory?.trackInventory) return null;\n return inventory.variants\n .filter(\n (variant) =>\n (size === null || (variant.size ?? '') === size) &&\n (colour === null || (variant.colour ?? '') === colour)\n )\n .reduce((sum, variant) => sum + variant.stockQuantity, 0);\n}\n\nconst chip = (selected: boolean) =>\n cn(\n 'inline-flex min-w-9 items-center justify-center rounded-full border px-3 py-1 text-sm font-medium transition-colors',\n selected\n ? 'border-action-primary-bg bg-action-primary-bg text-action-primary-text'\n : 'border-border-seam text-text-primary hover:bg-bg-sunken'\n );\n\nexport const AddToCartPanel: FC<AddToCartPanelProps> = ({ item, lookId }) => {\n const tr = useTr();\n const { navigateTo } = useCoolAndLovely();\n const addToCart = useAddToCart();\n const inventory = useStyleItemInventory(item.id);\n const [size, setSize] = useState<string | null>(\n item.sizes.length === 1 ? (item.sizes[0] ?? null) : null\n );\n const [colour, setColour] = useState<string | null>(\n item.colours.length === 1 ? (item.colours[0] ?? null) : null\n );\n const [quantity, setQuantity] = useState(1);\n\n const purchasable = item.status === 'ACTIVE' && item.price > 0;\n const needsSize = item.sizes.length > 0 && !size;\n const needsColour = item.colours.length > 0 && !colour;\n const stock = inventory.data;\n const left = needsSize || needsColour ? null : unitsLeft(stock, size ?? '', colour ?? '');\n const soldOut = left !== null && left <= 0;\n const maxQuantity = left === null ? MAX_QUANTITY : Math.max(1, Math.min(MAX_QUANTITY, left));\n\n if (!purchasable) {\n return (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {tr('coolandlovely.cart.notForSale', 'This piece is not available to buy right now.')}\n </p>\n );\n }\n\n return (\n <div className=\"space-y-4\" data-testid=\"add-to-cart-panel\">\n {lookId && (\n <p className=\"inline-flex items-center gap-1.5 text-xs font-medium text-accent-primary\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {tr(\n 'coolandlovely.cart.shoppingLook',\n 'Shopping from a look — your purchase supports its creator'\n )}\n </p>\n )}\n {item.sizes.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseSize', 'Choose a size')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.sizes.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={size === option}\n disabled={unitsLeft(stock, option, colour) === 0}\n onClick={() => {\n setSize(option);\n setQuantity(1);\n }}\n className={cn(chip(size === option), 'disabled:opacity-40 disabled:line-through')}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n {item.colours.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseColour', 'Choose a colour')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.colours.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={colour === option}\n disabled={unitsLeft(stock, size, option) === 0}\n onClick={() => {\n setColour(option);\n setQuantity(1);\n }}\n className={cn(chip(colour === option), 'disabled:opacity-40 disabled:line-through')}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n <div className=\"flex items-center gap-3\">\n <label htmlFor=\"coo-add-quantity\" className=\"text-sm text-text-secondary\">\n {tr('coolandlovely.cart.quantity', 'Quantity')}\n </label>\n <select\n id=\"coo-add-quantity\"\n value={quantity}\n onChange={(event) => setQuantity(Number(event.target.value))}\n className=\"rounded-lg border border-border-seam bg-bg-surface px-2 py-1.5 text-sm text-text-primary\"\n >\n {Array.from({ length: maxQuantity }, (_, index) => index + 1).map((value) => (\n <option key={value} value={value}>\n {value}\n </option>\n ))}\n </select>\n </div>\n {left !== null && (\n <p\n data-testid=\"stock-left\"\n className={cn(\n 'text-sm font-medium',\n soldOut || left <= LOW_STOCK ? 'text-status-warning-text' : 'text-text-secondary'\n )}\n >\n {soldOut\n ? tr('coolandlovely.cart.soldOut', 'Sold out in this size and colour')\n : left <= LOW_STOCK\n ? tr('coolandlovely.cart.onlyLeft', 'Only {{count}} left', { count: left })\n : tr('coolandlovely.cart.inStock', 'In stock')}\n </p>\n )}\n {addToCart.error && (\n <p className=\"text-sm text-status-error-text\">{addToCart.error.message}</p>\n )}\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n disabled={needsSize || needsColour || soldOut || addToCart.isPending}\n onClick={() =>\n addToCart.mutate({\n input: { styleItemId: item.id, quantity, size, colour, lookId: lookId ?? null },\n })\n }\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addToCart.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : addToCart.isSuccess ? (\n <Check className=\"h-4 w-4\" />\n ) : (\n <ShoppingBag className=\"h-4 w-4\" />\n )}\n {addToCart.isSuccess\n ? tr('coolandlovely.cart.added', 'Added to cart')\n : tr('coolandlovely.cart.add', 'Add to cart')}\n </button>\n {addToCart.isSuccess && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/cart')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n {tr('coolandlovely.cart.viewCart', 'View cart')}\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AddToCartPanel.js","names":[],"sources":["../../../src/coolandlovely/components/AddToCartPanel.tsx"],"sourcesContent":["/**\n * AddToCartPanel — size / colour / quantity picker + \"Add to cart\" for a catalogue piece.\n * @module coolandlovely/components\n *\n * When the shopper arrived from a look (\"shop the look\"), the look id rides along so the look's\n * creator earns the commission once the order is paid (BOFF-3370).\n *\n * When the brand tracks stock for the piece (BOFF-9637), sold-out sizes and colours are disabled,\n * the quantity is capped at what is left, and a low stock count is shown. The service checks the\n * stock again when the piece is added and when the order is placed.\n */\n\nimport { type FC, useState } from 'react';\nimport { Check, Loader2, ShoppingBag, Sparkles } from 'lucide-react';\n\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useTr } from '../hooks';\nimport { useAddToCart } from '../hooks/useCommerceApi';\nimport { useStyleItemInventory } from '../hooks/useInventoryApi';\nimport type { CooStyleItem, CooStyleItemInventory } from '../types';\nimport { cn } from '../utils/cn';\n\ninterface AddToCartPanelProps {\n item: Pick<CooStyleItem, 'id' | 'status' | 'price' | 'sizes' | 'colours'>;\n lookId?: string | null;\n}\n\nconst MAX_QUANTITY = 10;\n/** Stock at or below this is shown to the shopper as \"Only N left\". */\nconst LOW_STOCK = 5;\n\n/**\n * Units left of a size/colour choice while the piece tracks stock; null when it does not (or the\n * stock could not be read — the service still enforces it). A partial choice (size picked, colour\n * not yet) sums every variant that matches it.\n */\nfunction unitsLeft(\n inventory: CooStyleItemInventory | null | undefined,\n size: string | null,\n colour: string | null\n): number | null {\n if (!inventory?.trackInventory) return null;\n return inventory.variants\n .filter(\n (variant) =>\n (size === null || (variant.size ?? '') === size) &&\n (colour === null || (variant.colour ?? '') === colour)\n )\n .reduce((sum, variant) => sum + variant.stockQuantity, 0);\n}\n\nconst chip = (selected: boolean) =>\n cn(\n 'inline-flex min-w-9 items-center justify-center rounded-full border px-3 py-1 text-sm font-medium transition-colors',\n selected\n ? 'border-action-primary-bg bg-action-primary-bg text-action-primary-text'\n : 'border-border-seam text-text-primary hover:bg-bg-sunken'\n );\n\nexport const AddToCartPanel: FC<AddToCartPanelProps> = ({ item, lookId }) => {\n const tr = useTr();\n const { navigateTo } = useCoolAndLovely();\n const addToCart = useAddToCart();\n const inventory = useStyleItemInventory(item.id);\n const [size, setSize] = useState<string | null>(\n item.sizes.length === 1 ? (item.sizes[0] ?? null) : null\n );\n const [colour, setColour] = useState<string | null>(\n item.colours.length === 1 ? (item.colours[0] ?? null) : null\n );\n const [quantity, setQuantity] = useState(1);\n\n const purchasable = item.status === 'ACTIVE' && item.price > 0;\n const needsSize = item.sizes.length > 0 && !size;\n const needsColour = item.colours.length > 0 && !colour;\n const stock = inventory.data;\n const left = needsSize || needsColour ? null : unitsLeft(stock, size ?? '', colour ?? '');\n const soldOut = left !== null && left <= 0;\n const maxQuantity = left === null ? MAX_QUANTITY : Math.max(1, Math.min(MAX_QUANTITY, left));\n\n if (!purchasable) {\n return (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {tr('coolandlovely.cart.notForSale', 'This piece is not available to buy right now.')}\n </p>\n );\n }\n\n return (\n <div className=\"space-y-4\" data-testid=\"add-to-cart-panel\">\n {lookId && (\n <p className=\"inline-flex items-center gap-1.5 text-xs font-medium text-accent-primary\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {tr(\n 'coolandlovely.cart.shoppingLook',\n 'Shopping from a look — your purchase supports its creator'\n )}\n </p>\n )}\n {item.sizes.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseSize', 'Choose a size')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.sizes.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={size === option}\n disabled={unitsLeft(stock, option, colour) === 0}\n onClick={() => {\n setSize(option);\n setQuantity(1);\n }}\n className={cn(chip(size === option), 'disabled:opacity-40 disabled:line-through')}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n {item.colours.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseColour', 'Choose a colour')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.colours.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={colour === option}\n disabled={unitsLeft(stock, size, option) === 0}\n onClick={() => {\n setColour(option);\n setQuantity(1);\n }}\n className={cn(chip(colour === option), 'disabled:opacity-40 disabled:line-through')}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n <div className=\"flex items-center gap-3\">\n <label htmlFor=\"coo-add-quantity\" className=\"text-sm text-text-secondary\">\n {tr('coolandlovely.cart.quantity', 'Quantity')}\n </label>\n <select\n id=\"coo-add-quantity\"\n value={quantity}\n onChange={(event) => setQuantity(Number(event.target.value))}\n className=\"rounded-lg border border-border-seam bg-bg-surface px-2 py-1.5 text-sm text-text-primary\"\n >\n {Array.from({ length: maxQuantity }, (_, index) => index + 1).map((value) => (\n <option key={value} value={value}>\n {value}\n </option>\n ))}\n </select>\n </div>\n {left !== null && (\n <p\n data-testid=\"stock-left\"\n className={cn(\n 'text-sm font-medium',\n soldOut || left <= LOW_STOCK ? 'text-status-warning-text' : 'text-text-secondary'\n )}\n >\n {soldOut\n ? tr('coolandlovely.cart.soldOut', 'Sold out in this size and colour')\n : left <= LOW_STOCK\n ? tr('coolandlovely.cart.onlyLeft', 'Only {{count}} left', { count: left })\n : tr('coolandlovely.cart.inStock', 'In stock')}\n </p>\n )}\n {addToCart.error && (\n <p className=\"text-sm text-status-error-text\">{addToCart.error.message}</p>\n )}\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n disabled={needsSize || needsColour || soldOut || addToCart.isPending}\n onClick={() =>\n addToCart.mutate({\n input: { styleItemId: item.id, quantity, size, colour, lookId: lookId ?? null },\n })\n }\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addToCart.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : addToCart.isSuccess ? (\n <Check className=\"h-4 w-4\" />\n ) : (\n <ShoppingBag className=\"h-4 w-4\" />\n )}\n {addToCart.isSuccess\n ? tr('coolandlovely.cart.added', 'Added to cart')\n : tr('coolandlovely.cart.add', 'Add to cart')}\n </button>\n {addToCart.isSuccess && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/cart')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n {tr('coolandlovely.cart.viewCart', 'View cart')}\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AddToLookbookButton.js","names":[],"sources":["../../../src/coolandlovely/components/AddToLookbookButton.tsx"],"sourcesContent":["/**\n * AddToLookbookButton — clip a look or style item into one of the caller's\n * lookbooks (cooAddLookbookEntry).\n * @module coolandlovely/components\n *\n * Exactly one of `lookId` / `styleItemId` should be provided. Opens a small\n * menu of the caller's lookbooks; picking one adds the entry. If the shopper\n * has no lookbooks yet, offers a jump to the lookbooks page.\n */\n\nimport type { FC } from 'react';\nimport { useEffect, useRef, useState } from 'react';\nimport { BookHeart, Check, ChevronDown, Loader2, Plus } from 'lucide-react';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useAddLookbookEntry, useLookbooks } from '../hooks/useCoolAndLovelyApi';\nimport { cn } from '../utils/cn';\n\ninterface AddToLookbookButtonProps {\n lookId?: string;\n styleItemId?: string;\n className?: string;\n}\n\nexport const AddToLookbookButton: FC<AddToLookbookButtonProps> = ({\n lookId,\n styleItemId,\n className,\n}) => {\n const { navigateTo, currentUser } = useCoolAndLovely();\n const lookbooks = useLookbooks(currentUser?.id);\n const addEntry = useAddLookbookEntry();\n const [open, setOpen] = useState(false);\n const [addedTo, setAddedTo] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', onClick);\n return () => document.removeEventListener('mousedown', onClick);\n }, [open]);\n\n const boards = (lookbooks.data ?? []).filter((lb) => lb.ownerId === currentUser?.id);\n\n const handleAdd = (lookbookId: string) => {\n addEntry.mutate(\n { lookbookId, lookId, styleItemId },\n {\n onSuccess: () => {\n setAddedTo(lookbookId);\n setTimeout(() => {\n setOpen(false);\n setAddedTo(null);\n }, 900);\n },\n }\n );\n };\n\n return (\n <div ref={containerRef} className={cn('relative', className)}>\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n data-tour=\"coolandlovely-add-to-lookbook\"\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <BookHeart className=\"h-4 w-4\" /> Add to lookbook\n <ChevronDown className=\"h-3.5 w-3.5\" />\n </button>\n\n {open && (\n <div\n role=\"menu\"\n className=\"absolute right-0 z-20 mt-1 w-60 overflow-hidden rounded-lg border border-border-seam bg-bg-surface shadow-lg\"\n >\n {lookbooks.isLoading ? (\n <div className=\"flex items-center gap-2 px-3 py-3 text-sm text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Loading lookbooks…\n </div>\n ) : boards.length === 0 ? (\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false);\n navigateTo('/lookbooks');\n }}\n className=\"flex w-full items-center gap-2 px-3 py-3 text-left text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" /> Create your first lookbook\n </button>\n ) : (\n <ul className=\"max-h-64 overflow-auto py-1\">\n {boards.map((lb) => {\n const done = addedTo === lb.id;\n return (\n <li key={lb.id}>\n <button\n type=\"button\"\n role=\"menuitem\"\n disabled={addEntry.isPending}\n onClick={() => handleAdd(lb.id)}\n className=\"flex w-full items-center justify-between gap-2 px-3 py-2 text-left text-sm text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n <span className=\"truncate\">{lb.name}</span>\n {done ? (\n <Check className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n ) : addEntry.isPending ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin text-text-secondary\" />\n ) : null}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n {addEntry.isError && (\n <p className=\"border-t border-border-seam px-3 py-2 text-xs text-status-error-text\">\n {addEntry.error instanceof Error ? addEntry.error.message : 'Could not add'}\n </p>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;AAuBA,IAAa,KAAqD,EAChE,WACA,gBACA,mBACI;CACJ,IAAM,EAAE,eAAY,mBAAgB,GAAkB,EAChD,IAAY,EAAa,GAAa,GAAG,EACzC,IAAW,GAAqB,EAChC,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAS,KAAc,EAAwB,KAAK,EACrD,IAAe,EAA8B,KAAK;AAExD,SAAgB;AACd,MAAI,CAAC,EAAM;EACX,IAAM,KAAW,MAAkB;AACjC,GAAI,EAAa,WAAW,CAAC,EAAa,QAAQ,SAAS,EAAE,OAAe,IAC1E,EAAQ,GAAM;;AAIlB,SADA,SAAS,iBAAiB,aAAa,EAAQ,QAClC,SAAS,oBAAoB,aAAa,EAAQ;IAC9D,CAAC,EAAK,CAAC;CAEV,IAAM,KAAU,EAAU,QAAQ,EAAE,EAAE,QAAQ,MAAO,EAAG,YAAY,GAAa,GAAG,EAE9E,KAAa,MAAuB;AACxC,IAAS,OACP;GAAE;GAAY;GAAQ;GAAa,EACnC,EACE,iBAAiB;AAEf,GADA,EAAW,EAAW,EACtB,iBAAiB;AAEf,IADA,EAAQ,GAAM,EACd,EAAW,KAAK;MACf,IAAI;KAEV,CACF;;AAGH,QACE,kBAAC,OAAD;EAAK,KAAK;EAAc,WAAW,EAAG,YAAY,EAAU;YAA5D,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAS,MAAM,CAAC,EAAE;GACjC,iBAAc;GACd,iBAAe;GACf,aAAU;GACV,WAAU;aANZ;IAQE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;;IACjC,kBAAC,GAAD,EAAa,WAAU,eAAgB,CAAA;IAChC;MAER,KACC,kBAAC,OAAD;GACE,MAAK;GACL,WAAU;aAFZ,CAIG,EAAU,YACT,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,EAAA,sBACxC;QACJ,EAAO,WAAW,IACpB,kBAAC,UAAD;IACE,MAAK;IACL,eAAe;AAEb,KADA,EAAQ,GAAM,EACd,EAAW,aAAa;;IAE1B,WAAU;cANZ,CAQE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,8BACrB;QAET,kBAAC,MAAD;IAAI,WAAU;cACX,EAAO,KAAK,MAAO;KAClB,IAAM,IAAO,MAAY,EAAG;AAC5B,YACE,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;MACE,MAAK;MACL,MAAK;MACL,UAAU,EAAS;MACnB,eAAe,EAAU,EAAG,GAAG;MAC/B,WAAU;gBALZ,CAOE,kBAAC,QAAD;OAAM,WAAU;iBAAY,EAAG;OAAY,CAAA,EAC1C,IACC,kBAAC,GAAD,EAAO,WAAU,6CAA8C,CAAA,GAC7D,EAAS,YACX,kBAAC,GAAD,EAAS,WAAU,qDAAsD,CAAA,GACvE,KACG;SACN,EAfI,EAAG,GAeP;MAEP;IACC,CAAA,EAEN,EAAS,WACR,kBAAC,KAAD;IAAG,WAAU;cACV,EAAS,iBAAiB,QAAQ,EAAS,MAAM,UAAU;IAC1D,CAAA,CAEF;KAEJ"}
1
+ {"version":3,"file":"AddToLookbookButton.js","names":[],"sources":["../../../src/coolandlovely/components/AddToLookbookButton.tsx"],"sourcesContent":["/**\n * AddToLookbookButton — clip a look or style item into one of the caller's\n * lookbooks (cooAddLookbookEntry).\n * @module coolandlovely/components\n *\n * Exactly one of `lookId` / `styleItemId` should be provided. Opens a small\n * menu of the caller's lookbooks; picking one adds the entry. If the shopper\n * has no lookbooks yet, offers a jump to the lookbooks page.\n */\n\nimport type { FC } from 'react';\nimport { useEffect, useRef, useState } from 'react';\nimport { BookHeart, Check, ChevronDown, Loader2, Plus } from 'lucide-react';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useAddLookbookEntry, useLookbooks } from '../hooks/useCoolAndLovelyApi';\nimport { cn } from '../utils/cn';\n\ninterface AddToLookbookButtonProps {\n lookId?: string;\n styleItemId?: string;\n className?: string;\n}\n\nexport const AddToLookbookButton: FC<AddToLookbookButtonProps> = ({\n lookId,\n styleItemId,\n className,\n}) => {\n const { navigateTo, currentUser } = useCoolAndLovely();\n const lookbooks = useLookbooks(currentUser?.id);\n const addEntry = useAddLookbookEntry();\n const [open, setOpen] = useState(false);\n const [addedTo, setAddedTo] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', onClick);\n return () => document.removeEventListener('mousedown', onClick);\n }, [open]);\n\n const boards = (lookbooks.data ?? []).filter((lb) => lb.ownerId === currentUser?.id);\n\n const handleAdd = (lookbookId: string) => {\n addEntry.mutate(\n { lookbookId, lookId, styleItemId },\n {\n onSuccess: () => {\n setAddedTo(lookbookId);\n setTimeout(() => {\n setOpen(false);\n setAddedTo(null);\n }, 900);\n },\n }\n );\n };\n\n return (\n <div ref={containerRef} className={cn('relative', className)}>\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n data-tour=\"coolandlovely-add-to-lookbook\"\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <BookHeart className=\"h-4 w-4\" /> Add to lookbook\n <ChevronDown className=\"h-3.5 w-3.5\" />\n </button>\n\n {open && (\n <div\n role=\"menu\"\n className=\"absolute right-0 z-20 mt-1 w-60 overflow-hidden rounded-lg border border-border-seam bg-bg-surface shadow-lg\"\n >\n {lookbooks.isLoading ? (\n <div className=\"flex items-center gap-2 px-3 py-3 text-sm text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Loading lookbooks…\n </div>\n ) : boards.length === 0 ? (\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false);\n navigateTo('/lookbooks');\n }}\n className=\"flex w-full items-center gap-2 px-3 py-3 text-left text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" /> Create your first lookbook\n </button>\n ) : (\n <ul className=\"max-h-64 overflow-auto py-1\">\n {boards.map((lb) => {\n const done = addedTo === lb.id;\n return (\n <li key={lb.id}>\n <button\n type=\"button\"\n role=\"menuitem\"\n disabled={addEntry.isPending}\n onClick={() => handleAdd(lb.id)}\n className=\"flex w-full items-center justify-between gap-2 px-3 py-2 text-left text-sm text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n <span className=\"truncate\">{lb.name}</span>\n {done ? (\n <Check className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n ) : addEntry.isPending ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin text-text-secondary\" />\n ) : null}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n {addEntry.isError && (\n <p className=\"border-t border-border-seam px-3 py-2 text-xs text-status-error-text\">\n {addEntry.error instanceof Error ? addEntry.error.message : 'Could not add'}\n </p>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;AAuBA,IAAa,KAAqD,EAChE,WACA,gBACA,mBACI;CACJ,IAAM,EAAE,eAAY,mBAAgB,EAAiB,GAC/C,IAAY,EAAa,GAAa,EAAE,GACxC,IAAW,EAAoB,GAC/B,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAS,KAAc,EAAwB,IAAI,GACpD,IAAe,EAA8B,IAAI;CAEvD,QAAgB;EACd,IAAI,CAAC,GAAM;EACX,IAAM,KAAW,MAAkB;GACjC,AAAI,EAAa,WAAW,CAAC,EAAa,QAAQ,SAAS,EAAE,MAAc,KACzE,EAAQ,EAAK;EAEjB;EAEA,OADA,SAAS,iBAAiB,aAAa,CAAO,SACjC,SAAS,oBAAoB,aAAa,CAAO;CAChE,GAAG,CAAC,CAAI,CAAC;CAET,IAAM,KAAU,EAAU,QAAQ,CAAC,EAAA,CAAG,QAAQ,MAAO,EAAG,YAAY,GAAa,EAAE,GAE7E,KAAa,MAAuB;EACxC,EAAS,OACP;GAAE;GAAY;GAAQ;EAAY,GAClC,EACE,iBAAiB;GAEf,AADA,EAAW,CAAU,GACrB,iBAAiB;IAEf,AADA,EAAQ,EAAK,GACb,EAAW,IAAI;GACjB,GAAG,GAAG;EACR,EACF,CACF;CACF;CAEA,OACE,kBAAC,OAAD;EAAK,KAAK;EAAc,WAAW,EAAG,YAAY,CAAS;YAA3D,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAS,MAAM,CAAC,CAAC;GAChC,iBAAc;GACd,iBAAe;GACf,aAAU;GACV,WAAU;aANZ;IAQE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;IAAC;IACjC,kBAAC,GAAD,EAAa,WAAU,cAAe,CAAA;GAChC;MAEP,KACC,kBAAC,OAAD;GACE,MAAK;GACL,WAAU;aAFZ,CAIG,EAAU,YACT,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,GAAC,qBACzC;QACH,EAAO,WAAW,IACpB,kBAAC,UAAD;IACE,MAAK;IACL,eAAe;KAEb,AADA,EAAQ,EAAK,GACb,EAAW,YAAY;IACzB;IACA,WAAU;cANZ,CAQE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,GAAC,6BACtB;QAER,kBAAC,MAAD;IAAI,WAAU;cACX,EAAO,KAAK,MAAO;KAClB,IAAM,IAAO,MAAY,EAAG;KAC5B,OACE,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;MACE,MAAK;MACL,MAAK;MACL,UAAU,EAAS;MACnB,eAAe,EAAU,EAAG,EAAE;MAC9B,WAAU;gBALZ,CAOE,kBAAC,QAAD;OAAM,WAAU;iBAAY,EAAG;MAAW,CAAA,GACzC,IACC,kBAAC,GAAD,EAAO,WAAU,4CAA6C,CAAA,IAC5D,EAAS,YACX,kBAAC,GAAD,EAAS,WAAU,oDAAqD,CAAA,IACtE,IACE;QACN,GAfK,EAAG,EAeR;IAER,CAAC;GACC,CAAA,GAEL,EAAS,WACR,kBAAC,KAAD;IAAG,WAAU;cACV,EAAS,iBAAiB,QAAQ,EAAS,MAAM,UAAU;GAC3D,CAAA,CAEF;IAEJ;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"BrandCard.js","names":[],"sources":["../../../src/coolandlovely/components/BrandCard.tsx"],"sourcesContent":["/**\n * BrandCard — a clickable brand storefront card for list/grids.\n * @module coolandlovely/components\n *\n * A photographic cover with an overlapping circular logo, name, handle,\n * status badge, tagline and aesthetics. Extracted from BrandsListPage so it\n * can be rendered and tested in isolation (Storybook, tests).\n */\n\nimport type { FC } from 'react';\nimport { Star } from 'lucide-react';\nimport { BrandCover } from './BrandCover';\nimport { StatusBadge } from './StatusBadge';\nimport { TagList } from './TagList';\nimport { titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\nimport type { CooBrand } from '../types';\n\ninterface BrandCardProps {\n brand: CooBrand;\n onClick?: () => void;\n className?: string;\n}\n\nexport const BrandCard: FC<BrandCardProps> = ({ brand, onClick, className }) => {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={`Open brand ${brand.name}`}\n className={cn(\n 'group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]',\n className\n )}\n >\n <BrandCover\n coverFileId={brand.cover?.id}\n logoFileId={brand.logo?.id}\n name={brand.name}\n seed={brand.id}\n showLogo\n />\n <div className=\"px-4 pb-4 pt-3\">\n <div className=\"flex min-h-[2.5rem] items-start justify-between gap-2 pl-20\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{brand.name}</h3>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </div>\n {brand.tagline && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">{brand.tagline}</p>\n )}\n {brand.aesthetics.length > 0 && (\n <TagList tags={brand.aesthetics.map(titleCase)} max={4} className=\"mt-3\" tone=\"accent\" />\n )}\n </div>\n </button>\n );\n};\n"],"mappings":";;;;;;;;AAwBA,IAAa,KAAiC,EAAE,UAAO,YAAS,mBAE5D,kBAAC,UAAD;CACE,MAAK;CACI;CACT,cAAY,cAAc,EAAM;CAChC,WAAW,EACT,qSACA,EACD;WAPH,CASE,kBAAC,GAAD;EACE,aAAa,EAAM,OAAO;EAC1B,YAAY,EAAM,MAAM;EACxB,MAAM,EAAM;EACZ,MAAM,EAAM;EACZ,UAAA;EACA,CAAA,EACF,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAsD,EAAM;OAAU,CAAA,EACnF,EAAM,cACL,kBAAC,GAAD,EAAM,WAAU,0EAA2E,CAAA,CAEzF;SACN,kBAAC,KAAD;MAAG,WAAU;gBAAb,CAAoD,KAAE,EAAM,OAAW;QACnE;QACN,kBAAC,GAAD,EAAa,QAAQ,EAAM,QAAU,CAAA,CACjC;;GACL,EAAM,WACL,kBAAC,KAAD;IAAG,WAAU;cAAiD,EAAM;IAAY,CAAA;GAEjF,EAAM,WAAW,SAAS,KACzB,kBAAC,GAAD;IAAS,MAAM,EAAM,WAAW,IAAI,EAAU;IAAE,KAAK;IAAG,WAAU;IAAO,MAAK;IAAW,CAAA;GAEvF;IACC"}
1
+ {"version":3,"file":"BrandCard.js","names":[],"sources":["../../../src/coolandlovely/components/BrandCard.tsx"],"sourcesContent":["/**\n * BrandCard — a clickable brand storefront card for list/grids.\n * @module coolandlovely/components\n *\n * A photographic cover with an overlapping circular logo, name, handle,\n * status badge, tagline and aesthetics. Extracted from BrandsListPage so it\n * can be rendered and tested in isolation (Storybook, tests).\n */\n\nimport type { FC } from 'react';\nimport { Star } from 'lucide-react';\nimport { BrandCover } from './BrandCover';\nimport { StatusBadge } from './StatusBadge';\nimport { TagList } from './TagList';\nimport { titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\nimport type { CooBrand } from '../types';\n\ninterface BrandCardProps {\n brand: CooBrand;\n onClick?: () => void;\n className?: string;\n}\n\nexport const BrandCard: FC<BrandCardProps> = ({ brand, onClick, className }) => {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={`Open brand ${brand.name}`}\n className={cn(\n 'group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]',\n className\n )}\n >\n <BrandCover\n coverFileId={brand.cover?.id}\n logoFileId={brand.logo?.id}\n name={brand.name}\n seed={brand.id}\n showLogo\n />\n <div className=\"px-4 pb-4 pt-3\">\n <div className=\"flex min-h-[2.5rem] items-start justify-between gap-2 pl-20\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{brand.name}</h3>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </div>\n {brand.tagline && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">{brand.tagline}</p>\n )}\n {brand.aesthetics.length > 0 && (\n <TagList tags={brand.aesthetics.map(titleCase)} max={4} className=\"mt-3\" tone=\"accent\" />\n )}\n </div>\n </button>\n );\n};\n"],"mappings":";;;;;;;;AAwBA,IAAa,KAAiC,EAAE,UAAO,YAAS,mBAE5D,kBAAC,UAAD;CACE,MAAK;CACI;CACT,cAAY,cAAc,EAAM;CAChC,WAAW,EACT,qSACA,CACF;WAPF,CASE,kBAAC,GAAD;EACE,aAAa,EAAM,OAAO;EAC1B,YAAY,EAAM,MAAM;EACxB,MAAM,EAAM;EACZ,MAAM,EAAM;EACZ,UAAA;CACD,CAAA,GACD,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAsD,EAAM;MAAS,CAAA,GAClF,EAAM,cACL,kBAAC,GAAD,EAAM,WAAU,yEAA0E,CAAA,CAEzF;SACL,kBAAC,KAAD;MAAG,WAAU;gBAAb,CAAoD,KAAE,EAAM,MAAU;OACnE;QACL,kBAAC,GAAD,EAAa,QAAQ,EAAM,OAAS,CAAA,CACjC;;GACJ,EAAM,WACL,kBAAC,KAAD;IAAG,WAAU;cAAiD,EAAM;GAAW,CAAA;GAEhF,EAAM,WAAW,SAAS,KACzB,kBAAC,GAAD;IAAS,MAAM,EAAM,WAAW,IAAI,CAAS;IAAG,KAAK;IAAG,WAAU;IAAO,MAAK;GAAU,CAAA;EAEvF;GACC"}
@@ -1 +1 @@
1
- {"version":3,"file":"BrandCover.js","names":[],"sources":["../../../src/coolandlovely/components/BrandCover.tsx"],"sourcesContent":["/**\n * BrandCover — a brand cover banner with an optional overlapping round logo.\n * @module coolandlovely/components\n *\n * A photographic cover (`Thumbnail` on the cover File) with a bottom token scrim\n * so overlaid text/logos read against imagery; an optional circular logo avatar\n * straddles the bottom-left, and an optional `overlay` node (name / handle /\n * actions) renders over the scrim. Reused by the brands list card and the brand\n * detail hero. Token-only.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { Store } from 'lucide-react';\nimport { Thumbnail } from './Thumbnail';\nimport { cn } from '../utils/cn';\n\ninterface BrandCoverProps {\n coverFileId?: string | null;\n logoFileId?: string | null;\n name: string;\n /** Stable seed for the tint (usually the brand id). Falls back to `name`. */\n seed?: string;\n className?: string;\n /** When true, render the circular logo avatar straddling the bottom-left. */\n showLogo?: boolean;\n /** Content rendered over the scrim (e.g. name + @handle + actions). */\n overlay?: ReactNode;\n}\n\nexport const BrandCover: FC<BrandCoverProps> = ({\n coverFileId,\n logoFileId,\n name,\n seed,\n className,\n showLogo = false,\n overlay,\n}) => {\n const tintSeed = seed ?? name;\n return (\n <div className={cn('relative', className)}>\n <Thumbnail\n icon={<Store className=\"h-8 w-8\" />}\n seed={tintSeed}\n fileId={coverFileId}\n alt={name}\n className=\"aspect-[16/6] w-full\"\n overlay={\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-2/3 bg-gradient-to-t from-bg-canvas/70 to-transparent\" />\n }\n />\n {overlay ? <div className=\"absolute inset-0\">{overlay}</div> : null}\n {showLogo ? (\n <div className=\"absolute bottom-0 left-4 translate-y-1/2\">\n <Thumbnail\n icon={<Store className=\"h-6 w-6\" />}\n seed={tintSeed}\n fileId={logoFileId}\n alt={`${name} logo`}\n className=\"h-16 w-16 rounded-full shadow-md ring-2 ring-bg-surface\"\n />\n </div>\n ) : null}\n </div>\n );\n};\n"],"mappings":";;;;;AA6BA,IAAa,KAAmC,EAC9C,gBACA,eACA,SACA,SACA,cACA,cAAW,IACX,iBACI;CACJ,IAAM,IAAW,KAAQ;AACzB,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,YAAY,EAAU;YAAzC;GACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;IACnC,MAAM;IACN,QAAQ;IACR,KAAK;IACL,WAAU;IACV,SACE,kBAAC,OAAD,EAAK,WAAU,2GAA4G,CAAA;IAE7H,CAAA;GACD,IAAU,kBAAC,OAAD;IAAK,WAAU;cAAoB;IAAc,CAAA,GAAG;GAC9D,IACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,MAAM;KACN,QAAQ;KACR,KAAK,GAAG,EAAK;KACb,WAAU;KACV,CAAA;IACE,CAAA,GACJ;GACA"}
1
+ {"version":3,"file":"BrandCover.js","names":[],"sources":["../../../src/coolandlovely/components/BrandCover.tsx"],"sourcesContent":["/**\n * BrandCover — a brand cover banner with an optional overlapping round logo.\n * @module coolandlovely/components\n *\n * A photographic cover (`Thumbnail` on the cover File) with a bottom token scrim\n * so overlaid text/logos read against imagery; an optional circular logo avatar\n * straddles the bottom-left, and an optional `overlay` node (name / handle /\n * actions) renders over the scrim. Reused by the brands list card and the brand\n * detail hero. Token-only.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { Store } from 'lucide-react';\nimport { Thumbnail } from './Thumbnail';\nimport { cn } from '../utils/cn';\n\ninterface BrandCoverProps {\n coverFileId?: string | null;\n logoFileId?: string | null;\n name: string;\n /** Stable seed for the tint (usually the brand id). Falls back to `name`. */\n seed?: string;\n className?: string;\n /** When true, render the circular logo avatar straddling the bottom-left. */\n showLogo?: boolean;\n /** Content rendered over the scrim (e.g. name + @handle + actions). */\n overlay?: ReactNode;\n}\n\nexport const BrandCover: FC<BrandCoverProps> = ({\n coverFileId,\n logoFileId,\n name,\n seed,\n className,\n showLogo = false,\n overlay,\n}) => {\n const tintSeed = seed ?? name;\n return (\n <div className={cn('relative', className)}>\n <Thumbnail\n icon={<Store className=\"h-8 w-8\" />}\n seed={tintSeed}\n fileId={coverFileId}\n alt={name}\n className=\"aspect-[16/6] w-full\"\n overlay={\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-2/3 bg-gradient-to-t from-bg-canvas/70 to-transparent\" />\n }\n />\n {overlay ? <div className=\"absolute inset-0\">{overlay}</div> : null}\n {showLogo ? (\n <div className=\"absolute bottom-0 left-4 translate-y-1/2\">\n <Thumbnail\n icon={<Store className=\"h-6 w-6\" />}\n seed={tintSeed}\n fileId={logoFileId}\n alt={`${name} logo`}\n className=\"h-16 w-16 rounded-full shadow-md ring-2 ring-bg-surface\"\n />\n </div>\n ) : null}\n </div>\n );\n};\n"],"mappings":";;;;;AA6BA,IAAa,KAAmC,EAC9C,gBACA,eACA,SACA,SACA,cACA,cAAW,IACX,iBACI;CACJ,IAAM,IAAW,KAAQ;CACzB,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,YAAY,CAAS;YAAxC;GACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;IAClC,MAAM;IACN,QAAQ;IACR,KAAK;IACL,WAAU;IACV,SACE,kBAAC,OAAD,EAAK,WAAU,0GAA2G,CAAA;GAE7H,CAAA;GACA,IAAU,kBAAC,OAAD;IAAK,WAAU;cAAoB;GAAa,CAAA,IAAI;GAC9D,IACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;KAClC,MAAM;KACN,QAAQ;KACR,KAAK,GAAG,EAAK;KACb,WAAU;IACX,CAAA;GACE,CAAA,IACH;EACD;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"BrandCreatorAvatars.js","names":[],"sources":["../../../src/coolandlovely/components/BrandCreatorAvatars.tsx"],"sourcesContent":["/**\n * BrandCreatorAvatars — a brand-logo × creator-avatar pair.\n * @module coolandlovely/components\n *\n * The signature of a collaboration: the brand's round logo joined by a small \"×\"\n * to the creator's avatar. The creator's name + avatar are resolved via fe-libs\n * `useActorProfiles` on the GLOBAL gateway (NEVER select firstName on a federated\n * wspace User). Brand imagery reuses `Thumbnail` (round). Used by the\n * collaborations list and the collaboration detail header.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Store } from 'lucide-react';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { Thumbnail } from './Thumbnail';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { cn } from '../utils/cn';\n\ninterface BrandCreatorAvatarsProps {\n brandLogoFileId?: string | null;\n /** Stable seed for the brand logo tint (usually the brand id). */\n brandSeed?: string;\n /** Actor id of the creator (resolved to name + avatar via useActorProfiles). */\n creatorId: string;\n size?: 'sm' | 'md';\n className?: string;\n}\n\nfunction initials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '?';\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n\nexport const BrandCreatorAvatars: FC<BrandCreatorAvatarsProps> = ({\n brandLogoFileId,\n brandSeed,\n creatorId,\n size = 'md',\n className,\n}) => {\n const { apiGatewayUrl, authToken, workspaceId } = useCoolAndLovely();\n const [avatarFailed, setAvatarFailed] = useState(false);\n\n const actors = useMemo<ActorInput[]>(\n () => [{ actorId: creatorId, actorType: 'user' }],\n [creatorId]\n );\n const profiles = useActorProfiles(actors, { apiGatewayUrl, authToken, workspaceId });\n const creator = profiles[creatorId];\n const creatorName = creator?.displayName ?? 'Creator';\n const avatarUrl = creator?.avatarUrl;\n\n const dim = size === 'sm' ? 'h-7 w-7' : 'h-10 w-10';\n const iconSize = size === 'sm' ? 'h-3.5 w-3.5' : 'h-5 w-5';\n const initialsSize = size === 'sm' ? 'text-[10px]' : 'text-xs';\n\n return (\n <div className={cn('flex items-center gap-1.5', className)}>\n <Thumbnail\n icon={<Store className={iconSize} />}\n seed={brandSeed}\n fileId={brandLogoFileId}\n alt=\"Brand logo\"\n className={cn(dim, 'rounded-full ring-2 ring-bg-surface')}\n />\n <span className=\"text-xs font-medium text-text-secondary\" aria-hidden=\"true\">\n &times;\n </span>\n <span\n title={creatorName}\n className={cn(\n 'relative flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-bg-sunken ring-2 ring-bg-surface',\n dim\n )}\n >\n {avatarUrl && !avatarFailed ? (\n <img\n src={avatarUrl}\n alt={creatorName}\n loading=\"lazy\"\n onError={() => setAvatarFailed(true)}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <span className={cn('font-semibold text-text-secondary', initialsSize)}>\n {initials(creatorName)}\n </span>\n )}\n </span>\n </div>\n );\n};\n"],"mappings":";;;;;;;;AA6BA,SAAS,EAAS,GAAsB;CACtC,IAAM,IAAQ,EAAK,MAAM,CAAC,MAAM,MAAM,CAAC,OAAO,QAAQ;AAGtD,QAFI,EAAM,WAAW,IAAU,MAC3B,EAAM,WAAW,IAAU,EAAM,GAAG,MAAM,GAAG,EAAE,CAAC,aAAa,IACzD,EAAM,GAAG,KAAK,EAAM,EAAM,SAAS,GAAG,IAAI,aAAa;;AAGjE,IAAa,KAAqD,EAChE,oBACA,cACA,cACA,UAAO,MACP,mBACI;CACJ,IAAM,EAAE,kBAAe,cAAW,mBAAgB,GAAkB,EAC9D,CAAC,GAAc,KAAmB,EAAS,GAAM,EAOjD,IADW,EAJF,QACP,CAAC;EAAE,SAAS;EAAW,WAAW;EAAQ,CAAC,EACjD,CAAC,EAAU,CACZ,EACyC;EAAE;EAAe;EAAW;EAAa,CAAC,CAC3D,IACnB,IAAc,GAAS,eAAe,WACtC,IAAY,GAAS,WAErB,IAAM,MAAS,OAAO,YAAY,aAClC,IAAW,MAAS,OAAO,gBAAgB,WAC3C,IAAe,MAAS,OAAO,gBAAgB;AAErD,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,6BAA6B,EAAU;YAA1D;GACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,WAAW,GAAY,CAAA;IACpC,MAAM;IACN,QAAQ;IACR,KAAI;IACJ,WAAW,EAAG,GAAK,sCAAsC;IACzD,CAAA;GACF,kBAAC,QAAD;IAAM,WAAU;IAA0C,eAAY;cAAO;IAEtE,CAAA;GACP,kBAAC,QAAD;IACE,OAAO;IACP,WAAW,EACT,uHACA,EACD;cAEA,KAAa,CAAC,IACb,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,SAAQ;KACR,eAAe,EAAgB,GAAK;KACpC,WAAU;KACV,CAAA,GAEF,kBAAC,QAAD;KAAM,WAAW,EAAG,qCAAqC,EAAa;eACnE,EAAS,EAAY;KACjB,CAAA;IAEJ,CAAA;GACH"}
1
+ {"version":3,"file":"BrandCreatorAvatars.js","names":[],"sources":["../../../src/coolandlovely/components/BrandCreatorAvatars.tsx"],"sourcesContent":["/**\n * BrandCreatorAvatars — a brand-logo × creator-avatar pair.\n * @module coolandlovely/components\n *\n * The signature of a collaboration: the brand's round logo joined by a small \"×\"\n * to the creator's avatar. The creator's name + avatar are resolved via fe-libs\n * `useActorProfiles` on the GLOBAL gateway (NEVER select firstName on a federated\n * wspace User). Brand imagery reuses `Thumbnail` (round). Used by the\n * collaborations list and the collaboration detail header.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Store } from 'lucide-react';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { Thumbnail } from './Thumbnail';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { cn } from '../utils/cn';\n\ninterface BrandCreatorAvatarsProps {\n brandLogoFileId?: string | null;\n /** Stable seed for the brand logo tint (usually the brand id). */\n brandSeed?: string;\n /** Actor id of the creator (resolved to name + avatar via useActorProfiles). */\n creatorId: string;\n size?: 'sm' | 'md';\n className?: string;\n}\n\nfunction initials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '?';\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n\nexport const BrandCreatorAvatars: FC<BrandCreatorAvatarsProps> = ({\n brandLogoFileId,\n brandSeed,\n creatorId,\n size = 'md',\n className,\n}) => {\n const { apiGatewayUrl, authToken, workspaceId } = useCoolAndLovely();\n const [avatarFailed, setAvatarFailed] = useState(false);\n\n const actors = useMemo<ActorInput[]>(\n () => [{ actorId: creatorId, actorType: 'user' }],\n [creatorId]\n );\n const profiles = useActorProfiles(actors, { apiGatewayUrl, authToken, workspaceId });\n const creator = profiles[creatorId];\n const creatorName = creator?.displayName ?? 'Creator';\n const avatarUrl = creator?.avatarUrl;\n\n const dim = size === 'sm' ? 'h-7 w-7' : 'h-10 w-10';\n const iconSize = size === 'sm' ? 'h-3.5 w-3.5' : 'h-5 w-5';\n const initialsSize = size === 'sm' ? 'text-[10px]' : 'text-xs';\n\n return (\n <div className={cn('flex items-center gap-1.5', className)}>\n <Thumbnail\n icon={<Store className={iconSize} />}\n seed={brandSeed}\n fileId={brandLogoFileId}\n alt=\"Brand logo\"\n className={cn(dim, 'rounded-full ring-2 ring-bg-surface')}\n />\n <span className=\"text-xs font-medium text-text-secondary\" aria-hidden=\"true\">\n &times;\n </span>\n <span\n title={creatorName}\n className={cn(\n 'relative flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-bg-sunken ring-2 ring-bg-surface',\n dim\n )}\n >\n {avatarUrl && !avatarFailed ? (\n <img\n src={avatarUrl}\n alt={creatorName}\n loading=\"lazy\"\n onError={() => setAvatarFailed(true)}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <span className={cn('font-semibold text-text-secondary', initialsSize)}>\n {initials(creatorName)}\n </span>\n )}\n </span>\n </div>\n );\n};\n"],"mappings":";;;;;;;;AA6BA,SAAS,EAAS,GAAsB;CACtC,IAAM,IAAQ,EAAK,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAGrD,OAFI,EAAM,WAAW,IAAU,MAC3B,EAAM,WAAW,IAAU,EAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY,KACxD,EAAM,EAAE,CAAC,KAAK,EAAM,EAAM,SAAS,EAAE,CAAC,GAAA,CAAI,YAAY;AAChE;AAEA,IAAa,KAAqD,EAChE,oBACA,cACA,cACA,UAAO,MACP,mBACI;CACJ,IAAM,EAAE,kBAAe,cAAW,mBAAgB,EAAiB,GAC7D,CAAC,GAAc,KAAmB,EAAS,EAAK,GAOhD,IADW,EAJF,QACP,CAAC;EAAE,SAAS;EAAW,WAAW;CAAO,CAAC,GAChD,CAAC,CAAS,CAEsB,GAAQ;EAAE;EAAe;EAAW;CAAY,CAClE,CAAA,CAAS,IACnB,IAAc,GAAS,eAAe,WACtC,IAAY,GAAS,WAErB,IAAM,MAAS,OAAO,YAAY,aAClC,IAAW,MAAS,OAAO,gBAAgB,WAC3C,IAAe,MAAS,OAAO,gBAAgB;CAErD,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,6BAA6B,CAAS;YAAzD;GACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,WAAW,EAAW,CAAA;IACnC,MAAM;IACN,QAAQ;IACR,KAAI;IACJ,WAAW,EAAG,GAAK,qCAAqC;GACzD,CAAA;GACD,kBAAC,QAAD;IAAM,WAAU;IAA0C,eAAY;cAAO;GAEvE,CAAA;GACN,kBAAC,QAAD;IACE,OAAO;IACP,WAAW,EACT,uHACA,CACF;cAEC,KAAa,CAAC,IACb,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,SAAQ;KACR,eAAe,EAAgB,EAAI;KACnC,WAAU;IACX,CAAA,IAED,kBAAC,QAAD;KAAM,WAAW,EAAG,qCAAqC,CAAY;eAClE,EAAS,CAAW;IACjB,CAAA;GAEJ,CAAA;EACH;;AAET"}