@burdenoff/microfe-coolandlovely 2026.706.2 → 2026.706.3

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 (75) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  3. package/dist/coolandlovely/components/AddToLookbookButton.js +22 -22
  4. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
  5. package/dist/coolandlovely/components/BrandCover.js +39 -0
  6. package/dist/coolandlovely/components/BrandCover.js.map +1 -0
  7. package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
  8. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
  9. package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
  10. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
  11. package/dist/coolandlovely/components/Comments.js +4 -4
  12. package/dist/coolandlovely/components/Comments.js.map +1 -1
  13. package/dist/coolandlovely/components/FileImage.js.map +1 -1
  14. package/dist/coolandlovely/components/ImageUploadField.js +139 -57
  15. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -1
  16. package/dist/coolandlovely/components/LookCard.js +77 -0
  17. package/dist/coolandlovely/components/LookCard.js.map +1 -0
  18. package/dist/coolandlovely/components/MetricsCard.js +1 -1
  19. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  20. package/dist/coolandlovely/components/ShopItemCard.js +45 -0
  21. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
  22. package/dist/coolandlovely/components/StepTracker.js +35 -0
  23. package/dist/coolandlovely/components/StepTracker.js.map +1 -0
  24. package/dist/coolandlovely/components/Thumbnail.js +15 -11
  25. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  26. package/dist/coolandlovely/components/VisibilityChip.js +32 -0
  27. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
  28. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +94 -69
  29. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  30. package/dist/coolandlovely/hooks/useFiles.js +49 -32
  31. package/dist/coolandlovely/hooks/useFiles.js.map +1 -1
  32. package/dist/coolandlovely/pages/HomePage.js +112 -133
  33. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  34. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +253 -242
  35. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  36. package/dist/coolandlovely/pages/brands/BrandFormPage.js +116 -112
  37. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  38. package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -13
  39. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  40. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
  41. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  42. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  43. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
  44. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  45. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +131 -108
  46. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  47. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +1 -1
  48. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -56
  49. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  50. package/dist/coolandlovely/pages/discover/DiscoverPage.js +1 -1
  51. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  52. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +175 -147
  53. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  54. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +1 -1
  55. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +71 -62
  56. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +149 -127
  58. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  59. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +69 -53
  60. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  61. package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -113
  62. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  63. package/dist/coolandlovely/pages/looks/LookFormPage.js +122 -110
  64. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  65. package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -97
  66. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  67. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +372 -210
  68. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  69. package/dist/coolandlovely/pages/trends/TrendsPage.js +1 -1
  70. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -1
  71. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +130 -94
  72. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  73. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -76
  74. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  75. package/package.json +1 -1
@@ -3,119 +3,135 @@ import { CoolAndLovelyRoutes as t } from "./CoolAndLovelyRoutes.js";
3
3
  import { useCallback as n, useEffect as r } from "react";
4
4
  import { QueryClient as i, QueryClientProvider as a } from "@tanstack/react-query";
5
5
  import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
6
- import { BookHeart as s, Handshake as c, Heart as l, Home as u, Shapes as d, Shirt as f, Sparkles as p, Store as m, UserCircle as h } from "lucide-react";
7
- import { jsx as g } from "react/jsx-runtime";
8
- import { FeatureFlagProvider as _ } from "@burdenoff/fe-libs/shared/feature-flags";
6
+ import { BookHeart as s, Compass as c, Handshake as l, Heart as u, Home as d, Shapes as f, Shirt as p, Sparkles as m, Store as h, TrendingUp as g, UserCircle as _ } from "lucide-react";
7
+ import { jsx as v } from "react/jsx-runtime";
8
+ import { FeatureFlagProvider as y } from "@burdenoff/fe-libs/shared/feature-flags";
9
9
  //#region src/coolandlovely/CoolAndLovelyRoot.tsx
10
- var v = new i({ defaultOptions: { queries: {
10
+ var b = new i({ defaultOptions: { queries: {
11
11
  staleTime: 300 * 1e3,
12
12
  retry: 1
13
- } } }), y = (i) => {
14
- let { registerNavItems: y, registerFooterItems: b, basePath: x = "/" } = i, { t: S } = o(), C = n((e, t) => {
15
- let n = S(e);
13
+ } } }), x = (i) => {
14
+ let { registerNavItems: x, registerFooterItems: S, basePath: C = "/" } = i, { t: w } = o(), T = n((e, t) => {
15
+ let n = w(e);
16
16
  return n === e ? t : n;
17
- }, [S]), w = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
17
+ }, [w]), E = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
18
18
  return r(() => {
19
- if (y) return y([
19
+ if (x) return x([
20
20
  {
21
21
  id: "coolandlovely-overview",
22
- label: C("coolandlovely.nav.overview", "Overview"),
23
- path: w(x, ""),
24
- icon: /* @__PURE__ */ g(u, { size: 20 }),
22
+ label: T("coolandlovely.nav.overview", "Overview"),
23
+ path: E(C, ""),
24
+ icon: /* @__PURE__ */ v(d, { size: 20 }),
25
25
  order: 1,
26
26
  group: "main"
27
27
  },
28
28
  {
29
29
  id: "coolandlovely-brands",
30
- label: C("coolandlovely.nav.brands", "Brands"),
31
- path: w(x, "brands"),
32
- icon: /* @__PURE__ */ g(m, { size: 20 }),
30
+ label: T("coolandlovely.nav.brands", "Brands"),
31
+ path: E(C, "brands"),
32
+ icon: /* @__PURE__ */ v(h, { size: 20 }),
33
33
  order: 10,
34
34
  group: "catalog"
35
35
  },
36
36
  {
37
37
  id: "coolandlovely-collections",
38
- label: C("coolandlovely.nav.collections", "Collections"),
39
- path: w(x, "collections"),
40
- icon: /* @__PURE__ */ g(d, { size: 20 }),
38
+ label: T("coolandlovely.nav.collections", "Collections"),
39
+ path: E(C, "collections"),
40
+ icon: /* @__PURE__ */ v(f, { size: 20 }),
41
41
  order: 11,
42
42
  group: "catalog"
43
43
  },
44
44
  {
45
45
  id: "coolandlovely-items",
46
- label: C("coolandlovely.nav.items", "Style Items"),
47
- path: w(x, "items"),
48
- icon: /* @__PURE__ */ g(f, { size: 20 }),
46
+ label: T("coolandlovely.nav.items", "Style Items"),
47
+ path: E(C, "items"),
48
+ icon: /* @__PURE__ */ v(p, { size: 20 }),
49
49
  order: 12,
50
50
  group: "catalog"
51
51
  },
52
52
  {
53
53
  id: "coolandlovely-looks",
54
- label: C("coolandlovely.nav.looks", "Looks"),
55
- path: w(x, "looks"),
56
- icon: /* @__PURE__ */ g(p, { size: 20 }),
54
+ label: T("coolandlovely.nav.looks", "Looks"),
55
+ path: E(C, "looks"),
56
+ icon: /* @__PURE__ */ v(m, { size: 20 }),
57
57
  order: 20,
58
58
  group: "social"
59
59
  },
60
60
  {
61
61
  id: "coolandlovely-collaborations",
62
- label: C("coolandlovely.nav.collaborations", "Collaborations"),
63
- path: w(x, "collaborations"),
64
- icon: /* @__PURE__ */ g(c, { size: 20 }),
62
+ label: T("coolandlovely.nav.collaborations", "Collaborations"),
63
+ path: E(C, "collaborations"),
64
+ icon: /* @__PURE__ */ v(l, { size: 20 }),
65
65
  order: 21,
66
66
  group: "social"
67
67
  },
68
68
  {
69
69
  id: "coolandlovely-wardrobe",
70
- label: C("coolandlovely.nav.wardrobe", "My Wardrobe"),
71
- path: w(x, "wardrobe"),
72
- icon: /* @__PURE__ */ g(l, { size: 20 }),
70
+ label: T("coolandlovely.nav.wardrobe", "My Wardrobe"),
71
+ path: E(C, "wardrobe"),
72
+ icon: /* @__PURE__ */ v(u, { size: 20 }),
73
73
  order: 30,
74
74
  group: "my-style"
75
75
  },
76
76
  {
77
77
  id: "coolandlovely-lookbooks",
78
- label: C("coolandlovely.nav.lookbooks", "Lookbooks"),
79
- path: w(x, "lookbooks"),
80
- icon: /* @__PURE__ */ g(s, { size: 20 }),
78
+ label: T("coolandlovely.nav.lookbooks", "Lookbooks"),
79
+ path: E(C, "lookbooks"),
80
+ icon: /* @__PURE__ */ v(s, { size: 20 }),
81
81
  order: 31,
82
82
  group: "my-style"
83
83
  },
84
84
  {
85
85
  id: "coolandlovely-profile",
86
- label: C("coolandlovely.nav.profile", "Style Profile"),
87
- path: w(x, "profile"),
88
- icon: /* @__PURE__ */ g(h, { size: 20 }),
86
+ label: T("coolandlovely.nav.profile", "Style Profile"),
87
+ path: E(C, "profile"),
88
+ icon: /* @__PURE__ */ v(_, { size: 20 }),
89
89
  order: 32,
90
90
  group: "my-style"
91
+ },
92
+ {
93
+ id: "coolandlovely-discover",
94
+ label: T("coolandlovely.nav.discover", "Discover"),
95
+ path: E(C, "discover"),
96
+ icon: /* @__PURE__ */ v(c, { size: 20 }),
97
+ order: 40,
98
+ group: "signals"
99
+ },
100
+ {
101
+ id: "coolandlovely-trends",
102
+ label: T("coolandlovely.nav.trends", "Trends"),
103
+ path: E(C, "trends"),
104
+ icon: /* @__PURE__ */ v(g, { size: 20 }),
105
+ order: 41,
106
+ group: "signals"
91
107
  }
92
108
  ]);
93
109
  }, [
94
- y,
95
110
  x,
96
- C
111
+ C,
112
+ T
97
113
  ]), r(() => {
98
- if (b) return b([{
114
+ if (S) return S([{
99
115
  id: "coolandlovely-status",
100
- label: C("coolandlovely.footer.ready", "CoolAndLovely"),
116
+ label: T("coolandlovely.footer.ready", "CoolAndLovely"),
101
117
  type: "status",
102
118
  order: 10,
103
119
  section: "left"
104
120
  }]);
105
- }, [b, C]), /* @__PURE__ */ g(a, {
106
- client: v,
107
- children: /* @__PURE__ */ g(_, {
121
+ }, [S, T]), /* @__PURE__ */ v(a, {
122
+ client: b,
123
+ children: /* @__PURE__ */ v(y, {
108
124
  workspaceId: null,
109
125
  gateway: "global",
110
126
  defaultEnabled: !0,
111
- children: /* @__PURE__ */ g(e, {
127
+ children: /* @__PURE__ */ v(e, {
112
128
  ...i,
113
- children: /* @__PURE__ */ g(t, {})
129
+ children: /* @__PURE__ */ v(t, {})
114
130
  })
115
131
  })
116
132
  });
117
133
  };
118
134
  //#endregion
119
- export { y as CoolAndLovelyRoot };
135
+ export { x as CoolAndLovelyRoot };
120
136
 
121
137
  //# sourceMappingURL=CoolAndLovelyRoot.js.map
@@ -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} from 'lucide-react';\nimport { CoolAndLovelyProvider } from './context/CoolAndLovelyContext';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { CoolAndLovelyRoutes } from './CoolAndLovelyRoutes';\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\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 ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, t]);\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 workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <CoolAndLovelyProvider {...props}>\n <CoolAndLovelyRoutes />\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} from 'lucide-react';\nimport { CoolAndLovelyProvider } from './context/CoolAndLovelyContext';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { CoolAndLovelyRoutes } from './CoolAndLovelyRoutes';\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\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 {\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 ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, t]);\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 workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <CoolAndLovelyProvider {...props}>\n <CoolAndLovelyRoutes />\n </CoolAndLovelyProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n"],"mappings":"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"}
@@ -6,71 +6,71 @@ import { BookHeart as s, Check as c, ChevronDown as l, Loader2 as u, Plus as d }
6
6
  import { jsx as f, jsxs as p } from "react/jsx-runtime";
7
7
  //#region src/coolandlovely/components/AddToLookbookButton.tsx
8
8
  var m = ({ lookId: m, styleItemId: h, className: g }) => {
9
- let { navigateTo: _ } = e(), v = r(), y = n(), [b, x] = o(!1), [S, C] = o(null), w = a(null);
9
+ let { navigateTo: _, currentUser: v } = e(), y = r(v?.id), b = n(), [x, S] = o(!1), [C, w] = o(null), T = a(null);
10
10
  i(() => {
11
- if (!b) return;
11
+ if (!x) return;
12
12
  let e = (e) => {
13
- w.current && !w.current.contains(e.target) && x(!1);
13
+ T.current && !T.current.contains(e.target) && S(!1);
14
14
  };
15
15
  return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
16
- }, [b]);
17
- let T = v.data ?? [], E = (e) => {
18
- y.mutate({
16
+ }, [x]);
17
+ let E = (y.data ?? []).filter((e) => e.ownerId === v?.id), D = (e) => {
18
+ b.mutate({
19
19
  lookbookId: e,
20
20
  lookId: m,
21
21
  styleItemId: h
22
22
  }, { onSuccess: () => {
23
- C(e), setTimeout(() => {
24
- x(!1), C(null);
23
+ w(e), setTimeout(() => {
24
+ S(!1), w(null);
25
25
  }, 900);
26
26
  } });
27
27
  };
28
28
  return /* @__PURE__ */ p("div", {
29
- ref: w,
29
+ ref: T,
30
30
  className: t("relative", g),
31
31
  children: [/* @__PURE__ */ p("button", {
32
32
  type: "button",
33
- onClick: () => x((e) => !e),
33
+ onClick: () => S((e) => !e),
34
34
  "aria-haspopup": "menu",
35
- "aria-expanded": b,
35
+ "aria-expanded": x,
36
36
  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)]",
37
37
  children: [
38
38
  /* @__PURE__ */ f(s, { className: "h-4 w-4" }),
39
39
  " Add to lookbook",
40
40
  /* @__PURE__ */ f(l, { className: "h-3.5 w-3.5" })
41
41
  ]
42
- }), b && /* @__PURE__ */ p("div", {
42
+ }), x && /* @__PURE__ */ p("div", {
43
43
  role: "menu",
44
44
  className: "absolute right-0 z-20 mt-1 w-60 overflow-hidden rounded-lg border border-border-seam bg-bg-surface shadow-lg",
45
- children: [v.isLoading ? /* @__PURE__ */ p("div", {
45
+ children: [y.isLoading ? /* @__PURE__ */ p("div", {
46
46
  className: "flex items-center gap-2 px-3 py-3 text-sm text-text-secondary",
47
47
  children: [/* @__PURE__ */ f(u, { className: "h-4 w-4 animate-spin" }), " Loading lookbooks…"]
48
- }) : T.length === 0 ? /* @__PURE__ */ p("button", {
48
+ }) : E.length === 0 ? /* @__PURE__ */ p("button", {
49
49
  type: "button",
50
50
  onClick: () => {
51
- x(!1), _("/lookbooks");
51
+ S(!1), _("/lookbooks");
52
52
  },
53
53
  className: "flex w-full items-center gap-2 px-3 py-3 text-left text-sm text-text-primary hover:bg-bg-sunken",
54
54
  children: [/* @__PURE__ */ f(d, { className: "h-4 w-4" }), " Create your first lookbook"]
55
55
  }) : /* @__PURE__ */ f("ul", {
56
56
  className: "max-h-64 overflow-auto py-1",
57
- children: T.map((e) => {
58
- let t = S === e.id;
57
+ children: E.map((e) => {
58
+ let t = C === e.id;
59
59
  return /* @__PURE__ */ f("li", { children: /* @__PURE__ */ p("button", {
60
60
  type: "button",
61
61
  role: "menuitem",
62
- disabled: y.isPending,
63
- onClick: () => E(e.id),
62
+ disabled: b.isPending,
63
+ onClick: () => D(e.id),
64
64
  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",
65
65
  children: [/* @__PURE__ */ f("span", {
66
66
  className: "truncate",
67
67
  children: e.name
68
- }), t ? /* @__PURE__ */ f(c, { className: "h-4 w-4 shrink-0 text-status-success-text" }) : y.isPending ? /* @__PURE__ */ f(u, { className: "h-4 w-4 shrink-0 animate-spin text-text-secondary" }) : null]
68
+ }), t ? /* @__PURE__ */ f(c, { className: "h-4 w-4 shrink-0 text-status-success-text" }) : b.isPending ? /* @__PURE__ */ f(u, { className: "h-4 w-4 shrink-0 animate-spin text-text-secondary" }) : null]
69
69
  }) }, e.id);
70
70
  })
71
- }), y.isError && /* @__PURE__ */ f("p", {
71
+ }), b.isError && /* @__PURE__ */ f("p", {
72
72
  className: "border-t border-border-seam px-3 py-2 text-xs text-status-error-text",
73
- children: y.error instanceof Error ? y.error.message : "Could not add"
73
+ children: b.error instanceof Error ? b.error.message : "Could not add"
74
74
  })]
75
75
  })]
76
76
  });
@@ -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 } = useCoolAndLovely();\n const lookbooks = useLookbooks();\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 ?? [];\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 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,kBAAe,GAAkB,EACnC,IAAY,GAAc,EAC1B,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,IAAS,EAAU,QAAQ,EAAE,EAE7B,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,WAAU;aALZ;IAOE,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 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,WAAU;aALZ;IAOE,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"}
@@ -0,0 +1,39 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { Thumbnail as t } from "./Thumbnail.js";
3
+ import { Store as n } from "lucide-react";
4
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
+ //#region src/coolandlovely/components/BrandCover.tsx
6
+ var a = ({ coverFileId: a, logoFileId: o, name: s, seed: c, className: l, showLogo: u = !1, overlay: d }) => {
7
+ let f = c ?? s;
8
+ return /* @__PURE__ */ i("div", {
9
+ className: e("relative", l),
10
+ children: [
11
+ /* @__PURE__ */ r(t, {
12
+ icon: /* @__PURE__ */ r(n, { className: "h-8 w-8" }),
13
+ seed: f,
14
+ fileId: a,
15
+ alt: s,
16
+ className: "aspect-[16/6] w-full",
17
+ overlay: /* @__PURE__ */ r("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-2/3 bg-gradient-to-t from-bg-canvas/70 to-transparent" })
18
+ }),
19
+ d ? /* @__PURE__ */ r("div", {
20
+ className: "absolute inset-0",
21
+ children: d
22
+ }) : null,
23
+ u ? /* @__PURE__ */ r("div", {
24
+ className: "absolute bottom-0 left-4 translate-y-1/2",
25
+ children: /* @__PURE__ */ r(t, {
26
+ icon: /* @__PURE__ */ r(n, { className: "h-6 w-6" }),
27
+ seed: f,
28
+ fileId: o,
29
+ alt: `${s} logo`,
30
+ className: "h-16 w-16 rounded-full shadow-md ring-2 ring-bg-surface"
31
+ })
32
+ }) : null
33
+ ]
34
+ });
35
+ };
36
+ //#endregion
37
+ export { a as BrandCover };
38
+
39
+ //# sourceMappingURL=BrandCover.js.map
@@ -0,0 +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"}
@@ -0,0 +1,57 @@
1
+ import { useCoolAndLovely as e } from "../context/CoolAndLovelyContext.js";
2
+ import { cn as t } from "../utils/cn.js";
3
+ import { Thumbnail as n } from "./Thumbnail.js";
4
+ import { useMemo as r, useState as i } from "react";
5
+ import { Store as a } from "lucide-react";
6
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ import { useActorProfiles as c } from "@burdenoff/fe-libs/shared";
8
+ //#region src/coolandlovely/components/BrandCreatorAvatars.tsx
9
+ function l(e) {
10
+ let t = e.trim().split(/\s+/).filter(Boolean);
11
+ return t.length === 0 ? "?" : t.length === 1 ? t[0].slice(0, 2).toUpperCase() : (t[0][0] + t[t.length - 1][0]).toUpperCase();
12
+ }
13
+ var u = ({ brandLogoFileId: u, brandSeed: d, creatorId: f, size: p = "md", className: m }) => {
14
+ let { apiGatewayUrl: h, authToken: g, workspaceId: _ } = e(), [v, y] = i(!1), b = c(r(() => [{
15
+ actorId: f,
16
+ actorType: "user"
17
+ }], [f]), {
18
+ apiGatewayUrl: h,
19
+ authToken: g,
20
+ workspaceId: _
21
+ })[f], x = b?.displayName ?? "Creator", S = b?.avatarUrl, C = p === "sm" ? "h-7 w-7" : "h-10 w-10", w = p === "sm" ? "h-3.5 w-3.5" : "h-5 w-5", T = p === "sm" ? "text-[10px]" : "text-xs";
22
+ return /* @__PURE__ */ s("div", {
23
+ className: t("flex items-center gap-1.5", m),
24
+ children: [
25
+ /* @__PURE__ */ o(n, {
26
+ icon: /* @__PURE__ */ o(a, { className: w }),
27
+ seed: d,
28
+ fileId: u,
29
+ alt: "Brand logo",
30
+ className: t(C, "rounded-full ring-2 ring-bg-surface")
31
+ }),
32
+ /* @__PURE__ */ o("span", {
33
+ className: "text-xs font-medium text-text-secondary",
34
+ "aria-hidden": "true",
35
+ children: "×"
36
+ }),
37
+ /* @__PURE__ */ o("span", {
38
+ title: x,
39
+ className: t("relative flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-bg-sunken ring-2 ring-bg-surface", C),
40
+ children: S && !v ? /* @__PURE__ */ o("img", {
41
+ src: S,
42
+ alt: x,
43
+ loading: "lazy",
44
+ onError: () => y(!0),
45
+ className: "h-full w-full object-cover"
46
+ }) : /* @__PURE__ */ o("span", {
47
+ className: t("font-semibold text-text-secondary", T),
48
+ children: l(x)
49
+ })
50
+ })
51
+ ]
52
+ });
53
+ };
54
+ //#endregion
55
+ export { u as BrandCreatorAvatars };
56
+
57
+ //# sourceMappingURL=BrandCreatorAvatars.js.map
@@ -0,0 +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"}
@@ -0,0 +1,86 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ //#region src/coolandlovely/components/ColourSwatchRow.tsx
4
+ var r = {
5
+ black: "#111111",
6
+ white: "#ffffff",
7
+ ivory: "#fffff0",
8
+ cream: "#f5f1e3",
9
+ ecru: "#c2b280",
10
+ beige: "#f0e6d2",
11
+ sand: "#d8c3a5",
12
+ stone: "#a8a29e",
13
+ taupe: "#b38b6d",
14
+ grey: "#9ca3af",
15
+ gray: "#9ca3af",
16
+ charcoal: "#36454f",
17
+ silver: "#c0c0c0",
18
+ navy: "#1f2a44",
19
+ blue: "#2563eb",
20
+ cobalt: "#0047ab",
21
+ denim: "#1560bd",
22
+ indigo: "#4f46e5",
23
+ teal: "#0d9488",
24
+ turquoise: "#40e0d0",
25
+ green: "#16a34a",
26
+ olive: "#808000",
27
+ sage: "#9caf88",
28
+ mint: "#a7e8bd",
29
+ emerald: "#10b981",
30
+ khaki: "#c3b091",
31
+ mustard: "#e1ad01",
32
+ yellow: "#eab308",
33
+ gold: "#d4af37",
34
+ orange: "#ea580c",
35
+ rust: "#b7410e",
36
+ terracotta: "#e2725b",
37
+ coral: "#ff7f50",
38
+ peach: "#ffcba4",
39
+ camel: "#c19a6b",
40
+ tan: "#d2b48c",
41
+ brown: "#7c4a2d",
42
+ chocolate: "#5c3a21",
43
+ wine: "#722f37",
44
+ burgundy: "#800020",
45
+ maroon: "#800000",
46
+ red: "#dc2626",
47
+ pink: "#f9a8d4",
48
+ blush: "#e5a4a4",
49
+ rose: "#f43f5e",
50
+ lavender: "#b57edc",
51
+ lilac: "#c8a2c8",
52
+ purple: "#9333ea",
53
+ plum: "#8e4585"
54
+ }, i = Object.keys(r).sort((e, t) => t.length - e.length);
55
+ function a(e) {
56
+ let t = e.trim().toLowerCase();
57
+ if (!t) return null;
58
+ let n = r[t];
59
+ if (n) return n;
60
+ let a = i.find((e) => t.includes(e));
61
+ return a ? r[a] : null;
62
+ }
63
+ var o = ({ colours: r, max: i = 6, className: o }) => {
64
+ let s = r.filter((e) => e && e.trim().length > 0);
65
+ if (s.length === 0) return null;
66
+ let c = s.slice(0, i), l = s.length - c.length;
67
+ return /* @__PURE__ */ n("div", {
68
+ className: e("flex flex-wrap items-center gap-1.5", o),
69
+ children: [c.map((n, r) => {
70
+ let i = a(n);
71
+ return /* @__PURE__ */ t("span", {
72
+ title: n,
73
+ "aria-label": n,
74
+ style: i ? { backgroundColor: i } : void 0,
75
+ className: e("inline-block h-4 w-4 rounded-full ring-1 ring-inset ring-border-seam", i ? void 0 : "bg-bg-sunken")
76
+ }, `${n}-${r}`);
77
+ }), l > 0 ? /* @__PURE__ */ n("span", {
78
+ className: "text-xs font-medium text-text-secondary tabular-nums",
79
+ children: ["+", l]
80
+ }) : null]
81
+ });
82
+ };
83
+ //#endregion
84
+ export { o as ColourSwatchRow };
85
+
86
+ //# sourceMappingURL=ColourSwatchRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColourSwatchRow.js","names":[],"sources":["../../../src/coolandlovely/components/ColourSwatchRow.tsx"],"sourcesContent":["/**\n * ColourSwatchRow — render a set of colour names as small swatch dots.\n * @module coolandlovely/components\n *\n * CoolAndLovely style items / wardrobe / palettes carry free-text colour names\n * (e.g. \"navy\", \"rust\", \"camel\"). We map the common ones to a representative\n * CSS colour so the catalogue reads like a real fashion product rather than a\n * row of grey chips. The swatch fill is CONTENT data (the colour the garment\n * actually is), not a theme decision, so an inline `backgroundColor` is used for\n * mapped names; unknown names fall back to a neutral token-styled dot that keeps\n * its label in a tooltip. Everything structural/text uses semantic tokens.\n */\n\nimport type { CSSProperties, FC } from 'react';\nimport { cn } from '../utils/cn';\n\ninterface ColourSwatchRowProps {\n colours: string[];\n /** Max swatches to show before collapsing into a \"+N\" chip. */\n max?: number;\n className?: string;\n}\n\n/**\n * Common fashion colour names → a representative CSS colour. Keys are matched\n * case-insensitively; multi-word names (e.g. \"dusty rose\", \"forest green\") fall\n * back to a keyword scan so \"… green\" still resolves to green.\n */\nconst COLOUR_MAP: Record<string, string> = {\n black: '#111111',\n white: '#ffffff',\n ivory: '#fffff0',\n cream: '#f5f1e3',\n ecru: '#c2b280',\n beige: '#f0e6d2',\n sand: '#d8c3a5',\n stone: '#a8a29e',\n taupe: '#b38b6d',\n grey: '#9ca3af',\n gray: '#9ca3af',\n charcoal: '#36454f',\n silver: '#c0c0c0',\n navy: '#1f2a44',\n blue: '#2563eb',\n cobalt: '#0047ab',\n denim: '#1560bd',\n indigo: '#4f46e5',\n teal: '#0d9488',\n turquoise: '#40e0d0',\n green: '#16a34a',\n olive: '#808000',\n sage: '#9caf88',\n mint: '#a7e8bd',\n emerald: '#10b981',\n khaki: '#c3b091',\n mustard: '#e1ad01',\n yellow: '#eab308',\n gold: '#d4af37',\n orange: '#ea580c',\n rust: '#b7410e',\n terracotta: '#e2725b',\n coral: '#ff7f50',\n peach: '#ffcba4',\n camel: '#c19a6b',\n tan: '#d2b48c',\n brown: '#7c4a2d',\n chocolate: '#5c3a21',\n wine: '#722f37',\n burgundy: '#800020',\n maroon: '#800000',\n red: '#dc2626',\n pink: '#f9a8d4',\n blush: '#e5a4a4',\n rose: '#f43f5e',\n lavender: '#b57edc',\n lilac: '#c8a2c8',\n purple: '#9333ea',\n plum: '#8e4585',\n};\n\n/** Ordered keyword list for the \"contains\" fallback (longer keys first). */\nconst COLOUR_KEYWORDS = Object.keys(COLOUR_MAP).sort((a, b) => b.length - a.length);\n\nfunction resolveColour(name: string): string | null {\n const normalized = name.trim().toLowerCase();\n if (!normalized) return null;\n const direct = COLOUR_MAP[normalized];\n if (direct) return direct;\n const keyword = COLOUR_KEYWORDS.find((key) => normalized.includes(key));\n return keyword ? COLOUR_MAP[keyword] : null;\n}\n\nexport const ColourSwatchRow: FC<ColourSwatchRowProps> = ({ colours, max = 6, className }) => {\n const cleaned = colours.filter((c) => c && c.trim().length > 0);\n if (cleaned.length === 0) return null;\n\n const shown = cleaned.slice(0, max);\n const overflow = cleaned.length - shown.length;\n\n return (\n <div className={cn('flex flex-wrap items-center gap-1.5', className)}>\n {shown.map((colour, index) => {\n const css = resolveColour(colour);\n const style: CSSProperties | undefined = css ? { backgroundColor: css } : undefined;\n return (\n <span\n key={`${colour}-${index}`}\n title={colour}\n aria-label={colour}\n style={style}\n className={cn(\n 'inline-block h-4 w-4 rounded-full ring-1 ring-inset ring-border-seam',\n css ? undefined : 'bg-bg-sunken'\n )}\n />\n );\n })}\n {overflow > 0 ? (\n <span className=\"text-xs font-medium text-text-secondary tabular-nums\">+{overflow}</span>\n ) : null}\n </div>\n );\n};\n"],"mappings":";;;AA4BA,IAAM,IAAqC;CACzC,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,MAAM;CACN,OAAO;CACP,MAAM;CACN,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,UAAU;CACV,QAAQ;CACR,MAAM;CACN,MAAM;CACN,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,MAAM;CACN,WAAW;CACX,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,SAAS;CACT,OAAO;CACP,SAAS;CACT,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,MAAM;CACN,YAAY;CACZ,OAAO;CACP,OAAO;CACP,OAAO;CACP,KAAK;CACL,OAAO;CACP,WAAW;CACX,MAAM;CACN,UAAU;CACV,QAAQ;CACR,KAAK;CACL,MAAM;CACN,OAAO;CACP,MAAM;CACN,UAAU;CACV,OAAO;CACP,QAAQ;CACR,MAAM;CACP,EAGK,IAAkB,OAAO,KAAK,EAAW,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO;AAEnF,SAAS,EAAc,GAA6B;CAClD,IAAM,IAAa,EAAK,MAAM,CAAC,aAAa;AAC5C,KAAI,CAAC,EAAY,QAAO;CACxB,IAAM,IAAS,EAAW;AAC1B,KAAI,EAAQ,QAAO;CACnB,IAAM,IAAU,EAAgB,MAAM,MAAQ,EAAW,SAAS,EAAI,CAAC;AACvE,QAAO,IAAU,EAAW,KAAW;;AAGzC,IAAa,KAA6C,EAAE,YAAS,SAAM,GAAG,mBAAgB;CAC5F,IAAM,IAAU,EAAQ,QAAQ,MAAM,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE;AAC/D,KAAI,EAAQ,WAAW,EAAG,QAAO;CAEjC,IAAM,IAAQ,EAAQ,MAAM,GAAG,EAAI,EAC7B,IAAW,EAAQ,SAAS,EAAM;AAExC,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,uCAAuC,EAAU;YAApE,CACG,EAAM,KAAK,GAAQ,MAAU;GAC5B,IAAM,IAAM,EAAc,EAAO;AAEjC,UACE,kBAAC,QAAD;IAEE,OAAO;IACP,cAAY;IACL,OAN8B,IAAM,EAAE,iBAAiB,GAAK,GAAG,KAAA;IAOtE,WAAW,EACT,wEACA,IAAM,KAAA,IAAY,eACnB;IACD,EARK,GAAG,EAAO,GAAG,IAQlB;IAEJ,EACD,IAAW,IACV,kBAAC,QAAD;GAAM,WAAU;aAAhB,CAAuE,KAAE,EAAgB;OACvF,KACA"}
@@ -1,6 +1,6 @@
1
1
  import { cn as e } from "../utils/cn.js";
2
- import { useAddComment as t, useEntityComments as n } from "../hooks/useComments.js";
3
- import { formatRelativeTime as r } from "../utils/formatters.js";
2
+ import { formatRelativeTime as t } from "../utils/formatters.js";
3
+ import { useAddComment as n, useEntityComments as r } from "../hooks/useComments.js";
4
4
  import { ActorName as i } from "./ActorName.js";
5
5
  import { useState as a } from "react";
6
6
  import { Loader2 as o, MessageSquare as s, Send as c } from "lucide-react";
@@ -8,7 +8,7 @@ import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
8
  import { GlassCard as d } from "@burdenoff/fe-libs/ui";
9
9
  //#region src/coolandlovely/components/Comments.tsx
10
10
  var f = ({ entityType: f, entityId: p, title: m = "Reviews & feedback", className: h }) => {
11
- let g = n(f, p), _ = t(), [v, y] = a(""), b = g.data?.comments ?? [], x = g.data?.total ?? b.length;
11
+ let g = r(f, p), _ = n(), [v, y] = a(""), b = g.data?.comments ?? [], x = g.data?.total ?? b.length;
12
12
  return /* @__PURE__ */ u("section", {
13
13
  className: e("mt-6", h),
14
14
  children: [
@@ -91,7 +91,7 @@ var f = ({ entityType: f, entityId: p, title: m = "Reviews & feedback", classNam
91
91
  className: "truncate text-sm font-medium text-text-primary"
92
92
  }), /* @__PURE__ */ u("span", {
93
93
  className: "shrink-0 text-xs text-text-secondary",
94
- children: [r(e.createdAt), e.edited ? " · edited" : ""]
94
+ children: [t(e.createdAt), e.edited ? " · edited" : ""]
95
95
  })]
96
96
  }), /* @__PURE__ */ l("p", {
97
97
  className: "mt-1 whitespace-pre-wrap break-words text-sm text-text-secondary",