@burdenoff/microfe-coolandlovely 2026.805.1 → 2026.820.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 (66) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.d.ts.map +1 -1
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js +60 -58
  3. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  4. package/dist/coolandlovely/components/AddToLookbookButton.d.ts.map +1 -1
  5. package/dist/coolandlovely/components/AddToLookbookButton.js +1 -0
  6. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
  7. package/dist/coolandlovely/components/BrandCard.d.ts +11 -0
  8. package/dist/coolandlovely/components/BrandCard.d.ts.map +1 -0
  9. package/dist/coolandlovely/components/BrandCard.js +55 -0
  10. package/dist/coolandlovely/components/BrandCard.js.map +1 -0
  11. package/dist/coolandlovely/components/CollectionCard.d.ts +11 -0
  12. package/dist/coolandlovely/components/CollectionCard.d.ts.map +1 -0
  13. package/dist/coolandlovely/components/CollectionCard.js +60 -0
  14. package/dist/coolandlovely/components/CollectionCard.js.map +1 -0
  15. package/dist/coolandlovely/components/FeedbackWidget.d.ts +4 -0
  16. package/dist/coolandlovely/components/FeedbackWidget.d.ts.map +1 -0
  17. package/dist/coolandlovely/components/FeedbackWidget.js +134 -0
  18. package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -0
  19. package/dist/coolandlovely/components/LookbookCard.d.ts +11 -0
  20. package/dist/coolandlovely/components/LookbookCard.d.ts.map +1 -0
  21. package/dist/coolandlovely/components/LookbookCard.js +60 -0
  22. package/dist/coolandlovely/components/LookbookCard.js.map +1 -0
  23. package/dist/coolandlovely/components/WardrobeItemCard.d.ts +14 -0
  24. package/dist/coolandlovely/components/WardrobeItemCard.d.ts.map +1 -0
  25. package/dist/coolandlovely/components/WardrobeItemCard.js +92 -0
  26. package/dist/coolandlovely/components/WardrobeItemCard.js.map +1 -0
  27. package/dist/coolandlovely/components/index.d.ts +5 -0
  28. package/dist/coolandlovely/components/index.d.ts.map +1 -1
  29. package/dist/coolandlovely/components/index.js +26 -0
  30. package/dist/coolandlovely/pages/HomePage.d.ts.map +1 -1
  31. package/dist/coolandlovely/pages/HomePage.js +10 -4
  32. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  33. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +7 -7
  34. package/dist/coolandlovely/pages/brands/BrandsListPage.d.ts.map +1 -1
  35. package/dist/coolandlovely/pages/brands/BrandsListPage.js +48 -89
  36. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  37. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +4 -4
  38. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +13 -13
  39. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +4 -4
  40. package/dist/coolandlovely/pages/collections/CollectionsListPage.d.ts.map +1 -1
  41. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +28 -74
  42. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  43. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +5 -5
  44. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.d.ts.map +1 -1
  45. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +66 -112
  46. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  47. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +124 -124
  48. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  49. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.d.ts.map +1 -1
  50. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +46 -120
  51. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  52. package/dist/stories/BrandCard.stories.d.ts +10 -0
  53. package/dist/stories/BrandCard.stories.d.ts.map +1 -0
  54. package/dist/stories/CollectionCard.stories.d.ts +10 -0
  55. package/dist/stories/CollectionCard.stories.d.ts.map +1 -0
  56. package/dist/stories/FeedbackWidget.stories.d.ts +9 -0
  57. package/dist/stories/FeedbackWidget.stories.d.ts.map +1 -0
  58. package/dist/stories/LookCard.stories.d.ts +10 -0
  59. package/dist/stories/LookCard.stories.d.ts.map +1 -0
  60. package/dist/stories/LookbookCard.stories.d.ts +10 -0
  61. package/dist/stories/LookbookCard.stories.d.ts.map +1 -0
  62. package/dist/stories/WardrobeItemCard.stories.d.ts +11 -0
  63. package/dist/stories/WardrobeItemCard.stories.d.ts.map +1 -0
  64. package/dist/stories/__fixtures__/coolandlovely.d.ts +13 -0
  65. package/dist/stories/__fixtures__/coolandlovely.d.ts.map +1 -0
  66. package/package.json +4 -1
@@ -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;AAoBhC,OAAO,KAAK,EAAE,sBAAsB,EAAW,MAAM,SAAS,CAAC;AAY/D,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAmJxD,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;AAqBhC,OAAO,KAAK,EAAE,sBAAsB,EAAW,MAAM,SAAS,CAAC;AAY/D,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAoJxD,CAAC"}
@@ -1,137 +1,139 @@
1
1
  import { CoolAndLovelyProvider as e } from "./context/CoolAndLovelyContext.js";
2
2
  import { CoolAndLovelyRoutes as t } from "./CoolAndLovelyRoutes.js";
3
- import { useCallback as n, useEffect as r } from "react";
4
- import { QueryClient as i, QueryClientProvider as a } from "@tanstack/react-query";
5
- import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
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";
3
+ import { FeedbackWidget as n } from "./components/FeedbackWidget.js";
4
+ import "./components/index.js";
5
+ import { useCallback as r, useEffect as i } from "react";
6
+ import { QueryClient as a, QueryClientProvider as o } from "@tanstack/react-query";
7
+ import { useI18n as s } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
+ import { BookHeart as c, Compass as l, Handshake as u, Heart as d, Home as f, Shapes as p, Shirt as m, Sparkles as h, Store as g, TrendingUp as _, UserCircle as v } from "lucide-react";
9
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
10
+ import { FeatureFlagProvider as x } from "@burdenoff/fe-libs/shared/feature-flags";
9
11
  //#region src/coolandlovely/CoolAndLovelyRoot.tsx
10
- var b = new i({ defaultOptions: { queries: {
12
+ var S = new a({ defaultOptions: { queries: {
11
13
  staleTime: 300 * 1e3,
12
14
  retry: 1
13
- } } }), x = (i) => {
14
- let { registerNavItems: x, registerFooterItems: S, basePath: C = "/" } = i, { t: w } = o(), T = n((e, t) => {
15
- let n = w(e);
15
+ } } }), C = (a) => {
16
+ let { registerNavItems: C, registerFooterItems: w, basePath: T = "/" } = a, { t: E } = s(), D = r((e, t) => {
17
+ let n = E(e);
16
18
  return n === e ? t : n;
17
- }, [w]), E = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
18
- return r(() => {
19
- if (x) return x([
19
+ }, [E]), O = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
20
+ return i(() => {
21
+ if (C) return C([
20
22
  {
21
23
  id: "coolandlovely-overview",
22
- label: T("coolandlovely.nav.overview", "Overview"),
23
- path: E(C, ""),
24
- icon: /* @__PURE__ */ v(d, { size: 20 }),
24
+ label: D("coolandlovely.nav.overview", "Overview"),
25
+ path: O(T, ""),
26
+ icon: /* @__PURE__ */ y(f, { size: 20 }),
25
27
  order: 1,
26
28
  group: "main"
27
29
  },
28
30
  {
29
31
  id: "coolandlovely-brands",
30
- label: T("coolandlovely.nav.brands", "Brands"),
31
- path: E(C, "brands"),
32
- icon: /* @__PURE__ */ v(h, { size: 20 }),
32
+ label: D("coolandlovely.nav.brands", "Brands"),
33
+ path: O(T, "brands"),
34
+ icon: /* @__PURE__ */ y(g, { size: 20 }),
33
35
  order: 10,
34
36
  group: "catalog"
35
37
  },
36
38
  {
37
39
  id: "coolandlovely-collections",
38
- label: T("coolandlovely.nav.collections", "Collections"),
39
- path: E(C, "collections"),
40
- icon: /* @__PURE__ */ v(f, { size: 20 }),
40
+ label: D("coolandlovely.nav.collections", "Collections"),
41
+ path: O(T, "collections"),
42
+ icon: /* @__PURE__ */ y(p, { size: 20 }),
41
43
  order: 11,
42
44
  group: "catalog"
43
45
  },
44
46
  {
45
47
  id: "coolandlovely-items",
46
- label: T("coolandlovely.nav.items", "Style Items"),
47
- path: E(C, "items"),
48
- icon: /* @__PURE__ */ v(p, { size: 20 }),
48
+ label: D("coolandlovely.nav.items", "Style Items"),
49
+ path: O(T, "items"),
50
+ icon: /* @__PURE__ */ y(m, { size: 20 }),
49
51
  order: 12,
50
52
  group: "catalog"
51
53
  },
52
54
  {
53
55
  id: "coolandlovely-looks",
54
- label: T("coolandlovely.nav.looks", "Looks"),
55
- path: E(C, "looks"),
56
- icon: /* @__PURE__ */ v(m, { size: 20 }),
56
+ label: D("coolandlovely.nav.looks", "Looks"),
57
+ path: O(T, "looks"),
58
+ icon: /* @__PURE__ */ y(h, { size: 20 }),
57
59
  order: 20,
58
60
  group: "social"
59
61
  },
60
62
  {
61
63
  id: "coolandlovely-collaborations",
62
- label: T("coolandlovely.nav.collaborations", "Collaborations"),
63
- path: E(C, "collaborations"),
64
- icon: /* @__PURE__ */ v(l, { size: 20 }),
64
+ label: D("coolandlovely.nav.collaborations", "Collaborations"),
65
+ path: O(T, "collaborations"),
66
+ icon: /* @__PURE__ */ y(u, { size: 20 }),
65
67
  order: 21,
66
68
  group: "social"
67
69
  },
68
70
  {
69
71
  id: "coolandlovely-wardrobe",
70
- label: T("coolandlovely.nav.wardrobe", "My Wardrobe"),
71
- path: E(C, "wardrobe"),
72
- icon: /* @__PURE__ */ v(u, { size: 20 }),
72
+ label: D("coolandlovely.nav.wardrobe", "My Wardrobe"),
73
+ path: O(T, "wardrobe"),
74
+ icon: /* @__PURE__ */ y(d, { size: 20 }),
73
75
  order: 30,
74
76
  group: "my-style"
75
77
  },
76
78
  {
77
79
  id: "coolandlovely-lookbooks",
78
- label: T("coolandlovely.nav.lookbooks", "Lookbooks"),
79
- path: E(C, "lookbooks"),
80
- icon: /* @__PURE__ */ v(s, { size: 20 }),
80
+ label: D("coolandlovely.nav.lookbooks", "Lookbooks"),
81
+ path: O(T, "lookbooks"),
82
+ icon: /* @__PURE__ */ y(c, { size: 20 }),
81
83
  order: 31,
82
84
  group: "my-style"
83
85
  },
84
86
  {
85
87
  id: "coolandlovely-profile",
86
- label: T("coolandlovely.nav.profile", "Style Profile"),
87
- path: E(C, "profile"),
88
- icon: /* @__PURE__ */ v(_, { size: 20 }),
88
+ label: D("coolandlovely.nav.profile", "Style Profile"),
89
+ path: O(T, "profile"),
90
+ icon: /* @__PURE__ */ y(v, { size: 20 }),
89
91
  order: 32,
90
92
  group: "my-style"
91
93
  },
92
94
  {
93
95
  id: "coolandlovely-discover",
94
- label: T("coolandlovely.nav.discover", "Discover"),
95
- path: E(C, "discover"),
96
- icon: /* @__PURE__ */ v(c, { size: 20 }),
96
+ label: D("coolandlovely.nav.discover", "Discover"),
97
+ path: O(T, "discover"),
98
+ icon: /* @__PURE__ */ y(l, { size: 20 }),
97
99
  order: 40,
98
100
  group: "signals"
99
101
  },
100
102
  {
101
103
  id: "coolandlovely-trends",
102
- label: T("coolandlovely.nav.trends", "Trends"),
103
- path: E(C, "trends"),
104
- icon: /* @__PURE__ */ v(g, { size: 20 }),
104
+ label: D("coolandlovely.nav.trends", "Trends"),
105
+ path: O(T, "trends"),
106
+ icon: /* @__PURE__ */ y(_, { size: 20 }),
105
107
  order: 41,
106
108
  group: "signals"
107
109
  }
108
110
  ]);
109
111
  }, [
110
- x,
111
112
  C,
112
- T
113
- ]), r(() => {
114
- if (S) return S([{
113
+ T,
114
+ D
115
+ ]), i(() => {
116
+ if (w) return w([{
115
117
  id: "coolandlovely-status",
116
- label: T("coolandlovely.footer.ready", "CoolAndLovely"),
118
+ label: D("coolandlovely.footer.ready", "CoolAndLovely"),
117
119
  type: "status",
118
120
  order: 10,
119
121
  section: "left"
120
122
  }]);
121
- }, [S, T]), /* @__PURE__ */ v(a, {
122
- client: b,
123
- children: /* @__PURE__ */ v(y, {
123
+ }, [w, D]), /* @__PURE__ */ y(o, {
124
+ client: S,
125
+ children: /* @__PURE__ */ y(x, {
124
126
  workspaceId: null,
125
127
  gateway: "global",
126
128
  defaultEnabled: !0,
127
- children: /* @__PURE__ */ v(e, {
128
- ...i,
129
- children: /* @__PURE__ */ v(t, {})
129
+ children: /* @__PURE__ */ b(e, {
130
+ ...a,
131
+ children: [/* @__PURE__ */ y(t, {}), /* @__PURE__ */ y(n, {})]
130
132
  })
131
133
  })
132
134
  });
133
135
  };
134
136
  //#endregion
135
- export { x as CoolAndLovelyRoot };
137
+ export { C as CoolAndLovelyRoot };
136
138
 
137
139
  //# 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 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":";;;;;;;;;AAiCA,IAAM,IAAc,IAAI,EAAY,EAClC,gBAAgB,EACd,SAAS;CACP,WAAW,MAAS;CACpB,OAAO;CACR,EACF,EACF,CAAC,EAEW,KAAiD,MAAU;CACtE,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,QAAQ,GAC5D,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAU,CACZ,EAGK,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AA4HtD,QAvHA,QAAgB;AACT,QAiGL,QADgB,EA9FY;GAC1B;IACE,IAAI;IACJ,OAAO,EAAE,8BAA8B,WAAW;IAClD,MAAM,EAAS,GAAU,GAAG;IAC5B,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;IACxB,OAAO;IACP,OAAO;IACR;GAED;IACE,IAAI;IACJ,OAAO,EAAE,4BAA4B,SAAS;IAC9C,MAAM,EAAS,GAAU,SAAS;IAClC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,iCAAiC,cAAc;IACxD,MAAM,EAAS,GAAU,cAAc;IACvC,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;IAC1B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,2BAA2B,cAAc;IAClD,MAAM,EAAS,GAAU,QAAQ;IACjC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GAED;IACE,IAAI;IACJ,OAAO,EAAE,2BAA2B,QAAQ;IAC5C,MAAM,EAAS,GAAU,QAAQ;IACjC,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;IAC5B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,oCAAoC,iBAAiB;IAC9D,MAAM,EAAS,GAAU,iBAAiB;IAC1C,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;IAC7B,OAAO;IACP,OAAO;IACR;GAED;IACE,IAAI;IACJ,OAAO,EAAE,8BAA8B,cAAc;IACrD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,+BAA+B,YAAY;IACpD,MAAM,EAAS,GAAU,YAAY;IACrC,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;IAC7B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,6BAA6B,gBAAgB;IACtD,MAAM,EAAS,GAAU,UAAU;IACnC,MAAM,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;IAC9B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,8BAA8B,WAAW;IAClD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;IAC3B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,4BAA4B,SAAS;IAC9C,MAAM,EAAS,GAAU,SAAS;IAClC,MAAM,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;IAC9B,OAAO;IACP,OAAO;IACR;GACF,CAEyC;IAEzC;EAAC;EAAkB;EAAU;EAAE,CAAC,EAGnC,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO,EAAE,8BAA8B,gBAAgB;GACvD,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,GAAqB,EAAE,CAAC,EAG1B,kBAAC,GAAD;EAAqB,QAAQ;YAC3B,kBAAC,GAAD;GAAqB,aAAa;GAAM,SAAQ;GAAS,gBAAgB;aACvE,kBAAC,GAAD;IAAuB,GAAI;cACzB,kBAAC,GAAD,EAAuB,CAAA;IACD,CAAA;GACJ,CAAA;EACF,CAAA"}
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 { 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\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 <FeedbackWidget />\n </CoolAndLovelyProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n"],"mappings":";;;;;;;;;;;AAkCA,IAAM,IAAc,IAAI,EAAY,EAClC,gBAAgB,EACd,SAAS;CACP,WAAW,MAAS;CACpB,OAAO;CACR,EACF,EACF,CAAC,EAEW,KAAiD,MAAU;CACtE,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,QAAQ,GAC5D,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAU,CACZ,EAGK,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AA4HtD,QAvHA,QAAgB;AACT,QAiGL,QADgB,EA9FY;GAC1B;IACE,IAAI;IACJ,OAAO,EAAE,8BAA8B,WAAW;IAClD,MAAM,EAAS,GAAU,GAAG;IAC5B,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;IACxB,OAAO;IACP,OAAO;IACR;GAED;IACE,IAAI;IACJ,OAAO,EAAE,4BAA4B,SAAS;IAC9C,MAAM,EAAS,GAAU,SAAS;IAClC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,iCAAiC,cAAc;IACxD,MAAM,EAAS,GAAU,cAAc;IACvC,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;IAC1B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,2BAA2B,cAAc;IAClD,MAAM,EAAS,GAAU,QAAQ;IACjC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GAED;IACE,IAAI;IACJ,OAAO,EAAE,2BAA2B,QAAQ;IAC5C,MAAM,EAAS,GAAU,QAAQ;IACjC,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;IAC5B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,oCAAoC,iBAAiB;IAC9D,MAAM,EAAS,GAAU,iBAAiB;IAC1C,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;IAC7B,OAAO;IACP,OAAO;IACR;GAED;IACE,IAAI;IACJ,OAAO,EAAE,8BAA8B,cAAc;IACrD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,+BAA+B,YAAY;IACpD,MAAM,EAAS,GAAU,YAAY;IACrC,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;IAC7B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,6BAA6B,gBAAgB;IACtD,MAAM,EAAS,GAAU,UAAU;IACnC,MAAM,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;IAC9B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,8BAA8B,WAAW;IAClD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;IAC3B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,4BAA4B,SAAS;IAC9C,MAAM,EAAS,GAAU,SAAS;IAClC,MAAM,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;IAC9B,OAAO;IACP,OAAO;IACR;GACF,CAEyC;IAEzC;EAAC;EAAkB;EAAU;EAAE,CAAC,EAGnC,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO,EAAE,8BAA8B,gBAAgB;GACvD,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,GAAqB,EAAE,CAAC,EAG1B,kBAAC,GAAD;EAAqB,QAAQ;YAC3B,kBAAC,GAAD;GAAqB,aAAa;GAAM,SAAQ;GAAS,gBAAgB;aACvE,kBAAC,GAAD;IAAuB,GAAI;cAA3B,CACE,kBAAC,GAAD,EAAuB,CAAA,EACvB,kBAAC,GAAD,EAAkB,CAAA,CACI;;GACJ,CAAA;EACF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"AddToLookbookButton.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/AddToLookbookButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAOhC,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CA2G5D,CAAC"}
1
+ {"version":3,"file":"AddToLookbookButton.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/AddToLookbookButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAOhC,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CA4G5D,CAAC"}
@@ -33,6 +33,7 @@ var m = ({ lookId: m, styleItemId: h, className: g }) => {
33
33
  onClick: () => S((e) => !e),
34
34
  "aria-haspopup": "menu",
35
35
  "aria-expanded": x,
36
+ "data-tour": "coolandlovely-add-to-lookbook",
36
37
  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
38
  children: [
38
39
  /* @__PURE__ */ f(s, { className: "h-4 w-4" }),
@@ -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 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"}
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"}
@@ -0,0 +1,11 @@
1
+ import { FC } from 'react';
2
+ import { CooBrand } from '../types';
3
+
4
+ interface BrandCardProps {
5
+ brand: CooBrand;
6
+ onClick?: () => void;
7
+ className?: string;
8
+ }
9
+ export declare const BrandCard: FC<BrandCardProps>;
10
+ export {};
11
+ //# sourceMappingURL=BrandCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrandCard.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/BrandCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAOhC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,UAAU,cAAc;IACtB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAwCxC,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { StatusBadge as t } from "./StatusBadge.js";
3
+ import { titleCase as n } from "../utils/formatters.js";
4
+ import { TagList as r } from "./TagList.js";
5
+ import { BrandCover as i } from "./BrandCover.js";
6
+ import { Star as a } from "lucide-react";
7
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
8
+ //#region src/coolandlovely/components/BrandCard.tsx
9
+ var c = ({ brand: c, onClick: l, className: u }) => /* @__PURE__ */ s("button", {
10
+ type: "button",
11
+ onClick: l,
12
+ "aria-label": `Open brand ${c.name}`,
13
+ className: e("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)]", u),
14
+ children: [/* @__PURE__ */ o(i, {
15
+ coverFileId: c.cover?.id,
16
+ logoFileId: c.logo?.id,
17
+ name: c.name,
18
+ seed: c.id,
19
+ showLogo: !0
20
+ }), /* @__PURE__ */ s("div", {
21
+ className: "px-4 pb-4 pt-3",
22
+ children: [
23
+ /* @__PURE__ */ s("div", {
24
+ className: "flex min-h-[2.5rem] items-start justify-between gap-2 pl-20",
25
+ children: [/* @__PURE__ */ s("div", {
26
+ className: "min-w-0",
27
+ children: [/* @__PURE__ */ s("div", {
28
+ className: "flex items-center gap-1.5",
29
+ children: [/* @__PURE__ */ o("h3", {
30
+ className: "truncate text-base font-semibold text-text-primary",
31
+ children: c.name
32
+ }), c.isFeatured && /* @__PURE__ */ o(a, { className: "h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text" })]
33
+ }), /* @__PURE__ */ s("p", {
34
+ className: "truncate text-xs text-text-secondary",
35
+ children: ["@", c.handle]
36
+ })]
37
+ }), /* @__PURE__ */ o(t, { status: c.status })]
38
+ }),
39
+ c.tagline && /* @__PURE__ */ o("p", {
40
+ className: "mt-2 line-clamp-2 text-sm text-text-secondary",
41
+ children: c.tagline
42
+ }),
43
+ c.aesthetics.length > 0 && /* @__PURE__ */ o(r, {
44
+ tags: c.aesthetics.map(n),
45
+ max: 4,
46
+ className: "mt-3",
47
+ tone: "accent"
48
+ })
49
+ ]
50
+ })]
51
+ });
52
+ //#endregion
53
+ export { c as BrandCard };
54
+
55
+ //# sourceMappingURL=BrandCard.js.map
@@ -0,0 +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"}
@@ -0,0 +1,11 @@
1
+ import { FC } from 'react';
2
+ import { CooCollection } from '../types';
3
+
4
+ interface CollectionCardProps {
5
+ collection: CooCollection;
6
+ onClick?: () => void;
7
+ className?: string;
8
+ }
9
+ export declare const CollectionCard: FC<CollectionCardProps>;
10
+ export {};
11
+ //# sourceMappingURL=CollectionCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollectionCard.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/CollectionCard.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAOhC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,UAAU,mBAAmB;IAC3B,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,CAyDlD,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { StatusBadge as t } from "./StatusBadge.js";
3
+ import { Thumbnail as n } from "./Thumbnail.js";
4
+ import { formatDate as r, titleCase as i } from "../utils/formatters.js";
5
+ import { TagList as a } from "./TagList.js";
6
+ import { Shapes as o } from "lucide-react";
7
+ import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
8
+ //#region src/coolandlovely/components/CollectionCard.tsx
9
+ var u = ({ collection: u, onClick: d, className: f }) => /* @__PURE__ */ l("button", {
10
+ type: "button",
11
+ onClick: d,
12
+ "aria-label": `Open collection ${u.name}`,
13
+ className: e("group 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-[var(--color-focus-ring)]", f),
14
+ children: [/* @__PURE__ */ l("div", {
15
+ className: "relative",
16
+ children: [/* @__PURE__ */ c(n, {
17
+ icon: /* @__PURE__ */ c(o, { className: "h-10 w-10" }),
18
+ seed: u.id,
19
+ fileId: u.cover?.id,
20
+ alt: u.name,
21
+ className: "aspect-[16/10] w-full",
22
+ overlay: /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("div", { className: "pointer-events-none absolute inset-0 bg-gradient-to-t from-bg-canvas via-bg-canvas/55 to-transparent" }), /* @__PURE__ */ l("div", {
23
+ className: "absolute inset-x-0 bottom-0 flex items-end justify-between gap-2 p-4",
24
+ children: [/* @__PURE__ */ l("div", {
25
+ className: "min-w-0",
26
+ children: [/* @__PURE__ */ c("h3", {
27
+ className: "truncate text-lg font-semibold text-text-primary",
28
+ children: u.name
29
+ }), u.brand?.name && /* @__PURE__ */ c("p", {
30
+ className: "truncate text-xs text-text-secondary",
31
+ children: u.brand.name
32
+ })]
33
+ }), /* @__PURE__ */ c(t, {
34
+ status: u.status,
35
+ className: "shrink-0 shadow-sm"
36
+ })]
37
+ })] })
38
+ }), /* @__PURE__ */ c("span", {
39
+ className: "absolute right-3 top-3 rounded-full border border-border-seam bg-bg-surface/85 px-2.5 py-0.5 text-xs font-medium text-text-primary backdrop-blur-sm",
40
+ children: i(u.season)
41
+ })]
42
+ }), (u.publishAt || u.tags.length > 0) && /* @__PURE__ */ l("div", {
43
+ className: "p-4",
44
+ children: [u.publishAt && /* @__PURE__ */ l("p", {
45
+ className: "text-xs text-text-secondary",
46
+ children: ["Publishes ", r(u.publishAt, {
47
+ hour: void 0,
48
+ minute: void 0
49
+ })]
50
+ }), u.tags.length > 0 && /* @__PURE__ */ c(a, {
51
+ tags: u.tags,
52
+ max: 4,
53
+ className: u.publishAt ? "mt-2" : void 0
54
+ })]
55
+ })]
56
+ });
57
+ //#endregion
58
+ export { u as CollectionCard };
59
+
60
+ //# sourceMappingURL=CollectionCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollectionCard.js","names":[],"sources":["../../../src/coolandlovely/components/CollectionCard.tsx"],"sourcesContent":["/**\n * CollectionCard — a seasonal collection drop card for list/grids.\n * @module coolandlovely/components\n *\n * Cover image with a season pill, status badge, brand line, publish date and\n * tags. Extracted from CollectionsListPage for isolated rendering/tests.\n */\n\nimport type { FC } from 'react';\nimport { Shapes } from 'lucide-react';\nimport { Thumbnail } from './Thumbnail';\nimport { StatusBadge } from './StatusBadge';\nimport { TagList } from './TagList';\nimport { titleCase, formatDate } from '../utils/formatters';\nimport { cn } from '../utils/cn';\nimport type { CooCollection } from '../types';\n\ninterface CollectionCardProps {\n collection: CooCollection;\n onClick?: () => void;\n className?: string;\n}\n\nexport const CollectionCard: FC<CollectionCardProps> = ({ collection, onClick, className }) => {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={`Open collection ${collection.name}`}\n className={cn(\n 'group 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-[var(--color-focus-ring)]',\n className\n )}\n >\n <div className=\"relative\">\n <Thumbnail\n icon={<Shapes className=\"h-10 w-10\" />}\n seed={collection.id}\n fileId={collection.cover?.id}\n alt={collection.name}\n className=\"aspect-[16/10] w-full\"\n overlay={\n <>\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-t from-bg-canvas via-bg-canvas/55 to-transparent\" />\n <div className=\"absolute inset-x-0 bottom-0 flex items-end justify-between gap-2 p-4\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary\">\n {collection.name}\n </h3>\n {collection.brand?.name && (\n <p className=\"truncate text-xs text-text-secondary\">{collection.brand.name}</p>\n )}\n </div>\n <StatusBadge status={collection.status} className=\"shrink-0 shadow-sm\" />\n </div>\n </>\n }\n />\n <span className=\"absolute right-3 top-3 rounded-full border border-border-seam bg-bg-surface/85 px-2.5 py-0.5 text-xs font-medium text-text-primary backdrop-blur-sm\">\n {titleCase(collection.season)}\n </span>\n </div>\n {(collection.publishAt || collection.tags.length > 0) && (\n <div className=\"p-4\">\n {collection.publishAt && (\n <p className=\"text-xs text-text-secondary\">\n Publishes {formatDate(collection.publishAt, { hour: undefined, minute: undefined })}\n </p>\n )}\n {collection.tags.length > 0 && (\n <TagList\n tags={collection.tags}\n max={4}\n className={collection.publishAt ? 'mt-2' : undefined}\n />\n )}\n </div>\n )}\n </button>\n );\n};\n"],"mappings":";;;;;;;;AAuBA,IAAa,KAA2C,EAAE,eAAY,YAAS,mBAE3E,kBAAC,UAAD;CACE,MAAK;CACI;CACT,cAAY,mBAAmB,EAAW;CAC1C,WAAW,EACT,8PACA,EACD;WAPH,CASE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;GACtC,MAAM,EAAW;GACjB,QAAQ,EAAW,OAAO;GAC1B,KAAK,EAAW;GAChB,WAAU;GACV,SACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD,EAAK,WAAU,wGAAyG,CAAA,EACxH,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAW;MACT,CAAA,EACJ,EAAW,OAAO,QACjB,kBAAC,KAAD;MAAG,WAAU;gBAAwC,EAAW,MAAM;MAAS,CAAA,CAE7E;QACN,kBAAC,GAAD;KAAa,QAAQ,EAAW;KAAQ,WAAU;KAAuB,CAAA,CACrE;MACL,EAAA,CAAA;GAEL,CAAA,EACF,kBAAC,QAAD;GAAM,WAAU;aACb,EAAU,EAAW,OAAO;GACxB,CAAA,CACH;MACJ,EAAW,aAAa,EAAW,KAAK,SAAS,MACjD,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,EAAW,aACV,kBAAC,KAAD;GAAG,WAAU;aAAb,CAA2C,cAC9B,EAAW,EAAW,WAAW;IAAE,MAAM,KAAA;IAAW,QAAQ,KAAA;IAAW,CAAC,CACjF;MAEL,EAAW,KAAK,SAAS,KACxB,kBAAC,GAAD;GACE,MAAM,EAAW;GACjB,KAAK;GACL,WAAW,EAAW,YAAY,SAAS,KAAA;GAC3C,CAAA,CAEA;IAED"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const FeedbackWidget: FC;
4
+ //# sourceMappingURL=FeedbackWidget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeedbackWidget.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/FeedbackWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA6ChC,eAAO,MAAM,cAAc,EAAE,EAwJ5B,CAAC"}
@@ -0,0 +1,134 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { TextAreaField as t } from "./Field.js";
3
+ import { useCallback as n, useEffect as r, useState as i } from "react";
4
+ import { MessageSquarePlus as a, Send as o, X as s } from "lucide-react";
5
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
+ import { GlassCard as u } from "@burdenoff/fe-libs/ui";
7
+ //#region src/coolandlovely/components/FeedbackWidget.tsx
8
+ var d = "coolandlovely-feedback-submitted-at", f = 10080 * 60 * 1e3;
9
+ function p() {
10
+ if (typeof window > "u") return !1;
11
+ let e = window.localStorage.getItem(d);
12
+ if (!e) return !1;
13
+ try {
14
+ let t = parseInt(e, 10);
15
+ return Date.now() - t < f;
16
+ } catch {
17
+ return !1;
18
+ }
19
+ }
20
+ function m() {
21
+ typeof window > "u" || window.localStorage.setItem(d, String(Date.now()));
22
+ }
23
+ function h(e) {
24
+ typeof window > "u" || window.dispatchEvent(new CustomEvent("coolandlovely:feedback_submitted", {
25
+ detail: e,
26
+ bubbles: !0
27
+ }));
28
+ }
29
+ var g = () => {
30
+ let [d, f] = i(!1), [g, _] = i(null), [v, y] = i(""), [b, x] = i(!1), [S, C] = i(!1);
31
+ r(() => {
32
+ C(p());
33
+ }, []);
34
+ let w = n(() => {
35
+ f((e) => !e);
36
+ }, []), T = n(() => {
37
+ f(!1);
38
+ }, []), E = n(() => {
39
+ g !== null && (h({
40
+ score: g,
41
+ comment: v.trim(),
42
+ path: typeof window < "u" ? window.location.pathname : "",
43
+ submittedAt: (/* @__PURE__ */ new Date()).toISOString()
44
+ }), m(), x(!0), C(!0), window.setTimeout(() => {
45
+ f(!1), _(null), y(""), x(!1);
46
+ }, 2500));
47
+ }, [g, v]);
48
+ return S && !d ? null : /* @__PURE__ */ l("div", {
49
+ className: "fixed bottom-4 right-4 z-50 flex flex-col items-end gap-3",
50
+ children: [d && /* @__PURE__ */ l(u, {
51
+ className: "w-[min(92vw,360px)] overflow-hidden p-0 shadow-xl",
52
+ children: [/* @__PURE__ */ l("div", {
53
+ className: "flex items-center justify-between border-b border-border-seam px-4 py-3",
54
+ children: [/* @__PURE__ */ c("h2", {
55
+ className: "text-sm font-semibold text-text-primary",
56
+ children: "How is CoolAndLovely working for you?"
57
+ }), /* @__PURE__ */ c("button", {
58
+ type: "button",
59
+ onClick: T,
60
+ "aria-label": "Close feedback",
61
+ className: "rounded-md p-1 text-text-secondary transition-colors hover:bg-bg-surface hover:text-text-primary",
62
+ children: /* @__PURE__ */ c(s, {
63
+ className: "h-4 w-4",
64
+ "aria-hidden": "true"
65
+ })
66
+ })]
67
+ }), /* @__PURE__ */ c("div", {
68
+ className: "p-4",
69
+ children: b ? /* @__PURE__ */ l("div", {
70
+ className: "py-6 text-center",
71
+ children: [/* @__PURE__ */ c("p", {
72
+ className: "text-sm font-medium text-status-success-text",
73
+ children: "Thank you for your feedback!"
74
+ }), /* @__PURE__ */ c("p", {
75
+ className: "mt-1 text-xs text-text-secondary",
76
+ children: "Your input helps us improve CoolAndLovely."
77
+ })]
78
+ }) : /* @__PURE__ */ l("div", {
79
+ className: "space-y-4",
80
+ children: [
81
+ /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("p", {
82
+ className: "mb-2 text-xs font-medium text-text-secondary",
83
+ children: "Rate your experience (0 = not likely, 10 = very likely)"
84
+ }), /* @__PURE__ */ c("div", {
85
+ className: "flex justify-between gap-1",
86
+ role: "group",
87
+ "aria-label": "NPS score",
88
+ children: Array.from({ length: 11 }, (e, t) => t).map((t) => /* @__PURE__ */ c("button", {
89
+ type: "button",
90
+ onClick: () => _(t),
91
+ "aria-pressed": g === t,
92
+ className: e("flex h-8 w-7 items-center justify-center rounded-md text-xs font-medium transition-colors", g === t ? "bg-action-primary-bg text-action-primary-text" : "border border-border-seam bg-bg-surface text-text-secondary hover:border-action-primary-bg hover:text-text-primary"),
93
+ children: t
94
+ }, t))
95
+ })] }),
96
+ /* @__PURE__ */ c(t, {
97
+ id: "feedback-comment",
98
+ label: "What could be better?",
99
+ value: v,
100
+ onChange: y,
101
+ placeholder: "Tell us what’s working or what we should improve…",
102
+ rows: 3,
103
+ hint: "Optional — your comment is stored with your score and current page."
104
+ }),
105
+ /* @__PURE__ */ l("button", {
106
+ type: "button",
107
+ onClick: E,
108
+ disabled: g === null,
109
+ className: e("inline-flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors", g === null ? "cursor-not-allowed bg-bg-surface text-text-secondary" : "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90"),
110
+ children: [/* @__PURE__ */ c(o, {
111
+ className: "h-4 w-4",
112
+ "aria-hidden": "true"
113
+ }), "Submit feedback"]
114
+ })
115
+ ]
116
+ })
117
+ })]
118
+ }), /* @__PURE__ */ c("button", {
119
+ type: "button",
120
+ onClick: w,
121
+ "aria-label": d ? "Close feedback" : "Give feedback",
122
+ "aria-expanded": d,
123
+ className: e("flex h-12 w-12 items-center justify-center rounded-full shadow-lg transition-transform hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", d ? "bg-bg-surface text-text-primary" : "bg-action-primary-bg text-action-primary-text"),
124
+ children: c(d ? s : a, {
125
+ className: "h-5 w-5",
126
+ "aria-hidden": "true"
127
+ })
128
+ })]
129
+ });
130
+ };
131
+ //#endregion
132
+ export { g as FeedbackWidget };
133
+
134
+ //# sourceMappingURL=FeedbackWidget.js.map