@burdenoff/microfe-coolandlovely 2026.827.1 → 2026.1024.1

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 (71) hide show
  1. package/dist/coolandlovely/components/ActivityTrail.js +7 -7
  2. package/dist/coolandlovely/components/Comments.js +4 -4
  3. package/dist/coolandlovely/components/FeedbackWidget.d.ts.map +1 -1
  4. package/dist/coolandlovely/components/FeedbackWidget.js +72 -70
  5. package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -1
  6. package/dist/coolandlovely/components/LookCard.js +24 -24
  7. package/dist/coolandlovely/components/StateViews.d.ts.map +1 -1
  8. package/dist/coolandlovely/components/StateViews.js +35 -26
  9. package/dist/coolandlovely/components/StateViews.js.map +1 -1
  10. package/dist/coolandlovely/components/StatusBadge.d.ts.map +1 -1
  11. package/dist/coolandlovely/components/StatusBadge.js +45 -15
  12. package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
  13. package/dist/coolandlovely/components/VisibilityChip.d.ts.map +1 -1
  14. package/dist/coolandlovely/components/VisibilityChip.js +22 -17
  15. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -1
  16. package/dist/coolandlovely/hooks/index.d.ts +1 -0
  17. package/dist/coolandlovely/hooks/index.d.ts.map +1 -1
  18. package/dist/coolandlovely/hooks/index.js +2 -1
  19. package/dist/coolandlovely/hooks/useTr.d.ts +2 -0
  20. package/dist/coolandlovely/hooks/useTr.d.ts.map +1 -0
  21. package/dist/coolandlovely/hooks/useTr.js +14 -0
  22. package/dist/coolandlovely/hooks/useTr.js.map +1 -0
  23. package/dist/coolandlovely/pages/HomePage.d.ts.map +1 -1
  24. package/dist/coolandlovely/pages/HomePage.js +172 -132
  25. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  26. package/dist/coolandlovely/pages/admin/AdminBrandsPage.js +10 -10
  27. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js +16 -16
  28. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js +17 -17
  29. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +22 -22
  30. package/dist/coolandlovely/pages/brands/BrandFormPage.js +9 -9
  31. package/dist/coolandlovely/pages/brands/BrandsListPage.d.ts.map +1 -1
  32. package/dist/coolandlovely/pages/brands/BrandsListPage.js +80 -76
  33. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  34. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +21 -21
  35. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +7 -7
  36. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.d.ts.map +1 -1
  37. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +82 -81
  38. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  39. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +19 -19
  40. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +9 -9
  41. package/dist/coolandlovely/pages/collections/CollectionsListPage.d.ts.map +1 -1
  42. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +54 -55
  43. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  44. package/dist/coolandlovely/pages/discover/DiscoverPage.d.ts.map +1 -1
  45. package/dist/coolandlovely/pages/discover/DiscoverPage.js +322 -315
  46. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  47. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +21 -21
  48. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +10 -10
  49. package/dist/coolandlovely/pages/items/StyleItemsListPage.d.ts.map +1 -1
  50. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +105 -95
  51. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  52. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +20 -20
  53. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  54. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.d.ts.map +1 -1
  55. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +88 -79
  56. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/looks/LookDetailPage.js +18 -18
  58. package/dist/coolandlovely/pages/looks/LookFormPage.js +6 -6
  59. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  60. package/dist/coolandlovely/pages/looks/LooksListPage.d.ts.map +1 -1
  61. package/dist/coolandlovely/pages/looks/LooksListPage.js +94 -94
  62. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  63. package/dist/coolandlovely/pages/profile/StyleProfilePage.d.ts.map +1 -1
  64. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +226 -199
  65. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  66. package/dist/coolandlovely/pages/trends/TrendsPage.js +8 -8
  67. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +5 -5
  68. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.d.ts.map +1 -1
  69. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +71 -69
  70. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  71. package/package.json +1 -1
@@ -1,147 +1,149 @@
1
1
  import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
2
2
  import { cn as t } from "../../utils/cn.js";
3
3
  import { PageLayout as n } from "../../components/PageLayout.js";
4
- import { StatusBadge as r } from "../../components/StatusBadge.js";
5
- import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
6
- import { Thumbnail as o } from "../../components/Thumbnail.js";
7
- import { useStyleItems as s } from "../../hooks/useCoolAndLovelyApi.js";
8
- import { formatPrice as c, titleCase as l } from "../../utils/formatters.js";
4
+ import { useStyleItems as r } from "../../hooks/useCoolAndLovelyApi.js";
5
+ import { useTr as i } from "../../hooks/useTr.js";
9
6
  import "../../hooks/index.js";
10
- import { STYLE_CATEGORIES as u, categoryIcon as d } from "../../utils/icons.js";
11
- import { EmptyState as f } from "../../components/EmptyState.js";
12
- import { ColourSwatchRow as p } from "../../components/ColourSwatchRow.js";
13
- import { useState as m } from "react";
14
- import { ChevronLeft as h, ChevronRight as g, Plus as _, Shirt as v } from "lucide-react";
15
- import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
7
+ import { StatusBadge as a } from "../../components/StatusBadge.js";
8
+ import { ErrorView as o, LoadingView as s } from "../../components/StateViews.js";
9
+ import { Thumbnail as c } from "../../components/Thumbnail.js";
10
+ import { formatPrice as l, titleCase as u } from "../../utils/formatters.js";
11
+ import { STYLE_CATEGORIES as d, categoryIcon as f } from "../../utils/icons.js";
12
+ import { EmptyState as p } from "../../components/EmptyState.js";
13
+ import { ColourSwatchRow as m } from "../../components/ColourSwatchRow.js";
14
+ import { useMemo as h, useState as g } from "react";
15
+ import { ChevronLeft as _, ChevronRight as v, Plus as y, Shirt as b } from "lucide-react";
16
+ import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
16
17
  //#region src/coolandlovely/pages/items/StyleItemsListPage.tsx
17
- var S = 24, C = [
18
- {
19
- value: "ALL",
20
- label: "All"
21
- },
22
- {
23
- value: "ACTIVE",
24
- label: "Active"
25
- },
26
- {
27
- value: "DRAFT",
28
- label: "Draft"
29
- },
30
- {
31
- value: "OUT_OF_STOCK",
32
- label: "Out of stock"
33
- },
34
- {
35
- value: "RETIRED",
36
- label: "Retired"
37
- }
18
+ var w = 24, T = [
19
+ "ACTIVE",
20
+ "DRAFT",
21
+ "OUT_OF_STOCK",
22
+ "RETIRED"
38
23
  ];
39
- function w() {
40
- let { navigateTo: w } = e(), [T, E] = m("ALL"), [D, O] = m("ALL"), [k, A] = m(0), j = s({
41
- category: T === "ALL" ? void 0 : T,
42
- status: D === "ALL" ? void 0 : D,
24
+ function E(e) {
25
+ return e.replace(/_/g, " ").replace(/\b\w/g, (e) => e.toUpperCase());
26
+ }
27
+ function D() {
28
+ let { navigateTo: D } = e(), O = i(), [k, A] = g("ALL"), [j, M] = g("ALL"), [N, P] = g(0), F = h(() => [{
29
+ value: "ALL",
30
+ label: O("coolandlovely.filters.all", "All")
31
+ }, ...T.map((e) => ({
32
+ value: e,
33
+ label: O(`coolandlovely.status.${e.toLowerCase()}`, E(e))
34
+ }))], [O]), I = r({
35
+ category: k === "ALL" ? void 0 : k,
36
+ status: j === "ALL" ? void 0 : j,
43
37
  pagination: {
44
- limit: S,
45
- offset: k
38
+ limit: w,
39
+ offset: N
46
40
  }
47
- }), M = j.data?.items ?? [], N = j.data?.pageInfo.hasMore ?? !1;
48
- return /* @__PURE__ */ x(n, {
49
- title: "Style Items",
50
- description: "The shoppable catalogue across every brand",
51
- actions: /* @__PURE__ */ x("button", {
41
+ }), L = I.data?.items ?? [], R = I.data?.pageInfo.hasMore ?? !1;
42
+ return /* @__PURE__ */ C(n, {
43
+ title: O("coolandlovely.page.styleItemsTitle", "Style Items"),
44
+ description: O("coolandlovely.page.styleItemsSubtitle", "The shoppable catalogue across every brand"),
45
+ actions: /* @__PURE__ */ C("button", {
52
46
  type: "button",
53
- onClick: () => w("/items/new"),
47
+ onClick: () => D("/items/new"),
54
48
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
55
- children: [/* @__PURE__ */ b(_, { className: "h-4 w-4" }), " New item"]
49
+ children: [
50
+ /* @__PURE__ */ S(y, { className: "h-4 w-4" }),
51
+ " ",
52
+ O("coolandlovely.actions.newItem", "New item")
53
+ ]
56
54
  }),
57
55
  children: [
58
- /* @__PURE__ */ b("div", {
56
+ /* @__PURE__ */ S("div", {
59
57
  className: "mb-3 flex flex-wrap items-center gap-2",
60
- children: C.map((e) => /* @__PURE__ */ b("button", {
58
+ children: F.map((e) => /* @__PURE__ */ S("button", {
61
59
  type: "button",
62
60
  onClick: () => {
63
- O(e.value), A(0);
61
+ M(e.value), P(0);
64
62
  },
65
- className: t("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", D === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
63
+ className: t("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", j === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
66
64
  children: e.label
67
65
  }, e.value))
68
66
  }),
69
- /* @__PURE__ */ x("div", {
67
+ /* @__PURE__ */ C("div", {
70
68
  className: "mb-6 flex flex-wrap items-center gap-2",
71
- children: [/* @__PURE__ */ b("button", {
69
+ children: [/* @__PURE__ */ S("button", {
72
70
  type: "button",
73
71
  onClick: () => {
74
- E("ALL"), A(0);
72
+ A("ALL"), P(0);
75
73
  },
76
- className: t("rounded-full px-3 py-1.5 text-xs font-medium transition-colors", T === "ALL" ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
77
- children: "All categories"
78
- }), u.map((e) => /* @__PURE__ */ x("button", {
74
+ className: t("rounded-full px-3 py-1.5 text-xs font-medium transition-colors", k === "ALL" ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
75
+ children: O("coolandlovely.filters.allCategories", "All categories")
76
+ }), d.map((e) => /* @__PURE__ */ C("button", {
79
77
  type: "button",
80
78
  onClick: () => {
81
- E(e), A(0);
79
+ A(e), P(0);
82
80
  },
83
- className: t("inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors", T === e ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
81
+ className: t("inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors", k === e ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
84
82
  children: [
85
- d(e, 14),
83
+ f(e, 14),
86
84
  " ",
87
- l(e)
85
+ O(`coolandlovely.category.${e.toLowerCase()}`, u(e))
88
86
  ]
89
87
  }, e))]
90
88
  }),
91
- j.isLoading ? /* @__PURE__ */ b(a, { label: "Loading catalogue…" }) : j.isError ? /* @__PURE__ */ b(i, { onRetry: () => void j.refetch() }) : M.length === 0 ? /* @__PURE__ */ b(f, {
89
+ I.isLoading ? /* @__PURE__ */ S(s, { label: O("coolandlovely.loading.catalogue", "Loading catalogue…") }) : I.isError ? /* @__PURE__ */ S(o, { onRetry: () => void I.refetch() }) : L.length === 0 ? /* @__PURE__ */ S(p, {
92
90
  illustration: "empty-data",
93
- icon: /* @__PURE__ */ b(v, { className: "h-10 w-10" }),
94
- title: "No items found",
95
- description: "No style items match these filters yet.",
96
- action: /* @__PURE__ */ x("button", {
91
+ icon: /* @__PURE__ */ S(b, { className: "h-10 w-10" }),
92
+ title: O("coolandlovely.empty.itemsTitle", "No items found"),
93
+ description: O("coolandlovely.empty.itemsDescription", "No style items match these filters yet."),
94
+ action: /* @__PURE__ */ C("button", {
97
95
  type: "button",
98
- onClick: () => w("/items/new"),
96
+ onClick: () => D("/items/new"),
99
97
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
100
- children: [/* @__PURE__ */ b(_, { className: "h-4 w-4" }), " New item"]
98
+ children: [
99
+ /* @__PURE__ */ S(y, { className: "h-4 w-4" }),
100
+ " ",
101
+ O("coolandlovely.actions.newItem", "New item")
102
+ ]
101
103
  })
102
- }) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b("div", {
104
+ }) : /* @__PURE__ */ C(x, { children: [/* @__PURE__ */ S("div", {
103
105
  className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4",
104
- children: M.map((e) => /* @__PURE__ */ x("button", {
106
+ children: L.map((e) => /* @__PURE__ */ C("button", {
105
107
  type: "button",
106
- onClick: () => w(`/items/${e.id}`),
108
+ onClick: () => D(`/items/${e.id}`),
107
109
  className: "group overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md",
108
- children: [/* @__PURE__ */ x("div", {
110
+ children: [/* @__PURE__ */ C("div", {
109
111
  className: "relative",
110
- children: [/* @__PURE__ */ b(o, {
111
- icon: d(e.category, 32),
112
+ children: [/* @__PURE__ */ S(c, {
113
+ icon: f(e.category, 32),
112
114
  seed: e.id,
113
115
  fileId: e.image?.id,
114
116
  alt: e.name,
115
117
  className: "aspect-[3/4] w-full"
116
- }), /* @__PURE__ */ b("div", {
118
+ }), /* @__PURE__ */ S("div", {
117
119
  className: "absolute right-2 top-2",
118
- children: /* @__PURE__ */ b(r, {
120
+ children: /* @__PURE__ */ S(a, {
119
121
  status: e.status,
120
122
  className: "shadow-sm"
121
123
  })
122
124
  })]
123
- }), /* @__PURE__ */ x("div", {
125
+ }), /* @__PURE__ */ C("div", {
124
126
  className: "p-3",
125
127
  children: [
126
- /* @__PURE__ */ b("p", {
128
+ /* @__PURE__ */ S("p", {
127
129
  className: "truncate text-sm font-medium text-text-primary",
128
130
  children: e.name
129
131
  }),
130
- e.brand?.name && /* @__PURE__ */ b("p", {
132
+ e.brand?.name && /* @__PURE__ */ S("p", {
131
133
  className: "truncate text-xs text-text-secondary",
132
134
  children: e.brand.name
133
135
  }),
134
- /* @__PURE__ */ x("div", {
136
+ /* @__PURE__ */ C("div", {
135
137
  className: "mt-1.5 flex items-center justify-between gap-2",
136
- children: [/* @__PURE__ */ b("span", {
138
+ children: [/* @__PURE__ */ S("span", {
137
139
  className: "text-sm font-semibold text-text-primary tabular-nums",
138
- children: c(e.price, e.currency)
139
- }), /* @__PURE__ */ b("span", {
140
+ children: l(e.price, e.currency)
141
+ }), /* @__PURE__ */ S("span", {
140
142
  className: "text-xs text-text-secondary",
141
- children: l(e.category)
143
+ children: u(e.category)
142
144
  })]
143
145
  }),
144
- e.colours.length > 0 && /* @__PURE__ */ b(p, {
146
+ e.colours.length > 0 && /* @__PURE__ */ S(m, {
145
147
  colours: e.colours,
146
148
  max: 5,
147
149
  className: "mt-2"
@@ -149,26 +151,34 @@ function w() {
149
151
  ]
150
152
  })]
151
153
  }, e.id))
152
- }), (k > 0 || N) && /* @__PURE__ */ x("div", {
154
+ }), (N > 0 || R) && /* @__PURE__ */ C("div", {
153
155
  className: "mt-6 flex items-center justify-center gap-2",
154
156
  children: [
155
- /* @__PURE__ */ x("button", {
157
+ /* @__PURE__ */ C("button", {
156
158
  type: "button",
157
- disabled: k === 0,
158
- onClick: () => A((e) => Math.max(0, e - S)),
159
+ disabled: N === 0,
160
+ onClick: () => P((e) => Math.max(0, e - w)),
159
161
  className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40",
160
- children: [/* @__PURE__ */ b(h, { className: "h-4 w-4" }), " Previous"]
162
+ children: [
163
+ /* @__PURE__ */ S(_, { className: "h-4 w-4" }),
164
+ " ",
165
+ O("coolandlovely.pagination.previous", "Previous")
166
+ ]
161
167
  }),
162
- /* @__PURE__ */ x("span", {
168
+ /* @__PURE__ */ S("span", {
163
169
  className: "text-sm text-text-secondary",
164
- children: ["Page ", k / S + 1]
170
+ children: O("coolandlovely.pagination.page", "Page {{page}}", { page: N / w + 1 })
165
171
  }),
166
- /* @__PURE__ */ x("button", {
172
+ /* @__PURE__ */ C("button", {
167
173
  type: "button",
168
- disabled: !N,
169
- onClick: () => A((e) => e + S),
174
+ disabled: !R,
175
+ onClick: () => P((e) => e + w),
170
176
  className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40",
171
- children: ["Next ", /* @__PURE__ */ b(g, { className: "h-4 w-4" })]
177
+ children: [
178
+ O("coolandlovely.pagination.next", "Next"),
179
+ " ",
180
+ /* @__PURE__ */ S(v, { className: "h-4 w-4" })
181
+ ]
172
182
  })
173
183
  ]
174
184
  })] })
@@ -176,6 +186,6 @@ function w() {
176
186
  });
177
187
  }
178
188
  //#endregion
179
- export { w as StyleItemsListPage };
189
+ export { D as StyleItemsListPage };
180
190
 
181
191
  //# sourceMappingURL=StyleItemsListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StyleItemsListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/items/StyleItemsListPage.tsx"],"sourcesContent":["/**\n * Style Items List Page\n * @module coolandlovely/pages/items\n *\n * Cursor-paginated catalogue (`cooListStyleItems`) with category & status\n * filters. Cards link to the item detail page.\n */\n\nimport { useState } from 'react';\nimport { Plus, ChevronLeft, ChevronRight, Shirt } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { ColourSwatchRow } from '../../components/ColourSwatchRow';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useStyleItems } from '../../hooks';\nimport { categoryIcon, STYLE_CATEGORIES } from '../../utils/icons';\nimport { formatPrice, titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooStyleCategory, CooStyleItemStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst STATUS_OPTIONS: Array<{ value: CooStyleItemStatus | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'OUT_OF_STOCK', label: 'Out of stock' },\n { value: 'RETIRED', label: 'Retired' },\n];\n\nexport function StyleItemsListPage() {\n const { navigateTo } = useCoolAndLovely();\n const [category, setCategory] = useState<CooStyleCategory | 'ALL'>('ALL');\n const [status, setStatus] = useState<CooStyleItemStatus | 'ALL'>('ALL');\n const [offset, setOffset] = useState(0);\n\n const query = useStyleItems({\n category: category === 'ALL' ? undefined : category,\n status: status === 'ALL' ? undefined : status,\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title=\"Style Items\"\n description=\"The shoppable catalogue across every brand\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/items/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" /> New item\n </button>\n }\n >\n {/* Status filter */}\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n {STATUS_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => {\n setStatus(opt.value);\n setOffset(0);\n }}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n status === opt.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n\n {/* Category filter */}\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setCategory('ALL');\n setOffset(0);\n }}\n className={cn(\n 'rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n category === 'ALL'\n ? 'bg-accent-purple-subtle text-text-primary'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n All categories\n </button>\n {STYLE_CATEGORIES.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => {\n setCategory(c);\n setOffset(0);\n }}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n category === c\n ? 'bg-accent-purple-subtle text-text-primary'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {categoryIcon(c, 14)} {titleCase(c)}\n </button>\n ))}\n </div>\n\n {query.isLoading ? (\n <LoadingView label=\"Loading catalogue…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Shirt className=\"h-10 w-10\" />}\n title=\"No items found\"\n description=\"No style items match these filters yet.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/items/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <Plus className=\"h-4 w-4\" /> New item\n </button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => navigateTo(`/items/${item.id}`)}\n className=\"group overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div className=\"relative\">\n <Thumbnail\n icon={categoryIcon(item.category, 32)}\n seed={item.id}\n fileId={item.image?.id}\n alt={item.name}\n className=\"aspect-[3/4] w-full\"\n />\n <div className=\"absolute right-2 top-2\">\n <StatusBadge status={item.status} className=\"shadow-sm\" />\n </div>\n </div>\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{item.name}</p>\n {item.brand?.name && (\n <p className=\"truncate text-xs text-text-secondary\">{item.brand.name}</p>\n )}\n <div className=\"mt-1.5 flex items-center justify-between gap-2\">\n <span className=\"text-sm font-semibold text-text-primary tabular-nums\">\n {formatPrice(item.price, item.currency)}\n </span>\n <span className=\"text-xs text-text-secondary\">{titleCase(item.category)}</span>\n </div>\n {item.colours.length > 0 && (\n <ColourSwatchRow colours={item.colours} max={5} className=\"mt-2\" />\n )}\n </div>\n </button>\n ))}\n </div>\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-center gap-2\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Previous\n </button>\n <span className=\"text-sm text-text-secondary\">Page {offset / PAGE_SIZE + 1}</span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"StyleItemsListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/items/StyleItemsListPage.tsx"],"sourcesContent":["/**\n * Style Items List Page\n * @module coolandlovely/pages/items\n *\n * Cursor-paginated catalogue (`cooListStyleItems`) with category & status\n * filters. Cards link to the item detail page.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, ChevronLeft, ChevronRight, Shirt } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { ColourSwatchRow } from '../../components/ColourSwatchRow';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useStyleItems, useTr } from '../../hooks';\nimport { categoryIcon, STYLE_CATEGORIES } from '../../utils/icons';\nimport { formatPrice, titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooStyleCategory, CooStyleItemStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\nconst STATUS_ORDER: CooStyleItemStatus[] = ['ACTIVE', 'DRAFT', 'OUT_OF_STOCK', 'RETIRED'];\n\nfunction formatStatusLabel(status: string): string {\n return status.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nexport function StyleItemsListPage() {\n const { navigateTo } = useCoolAndLovely();\n const tr = useTr();\n const [category, setCategory] = useState<CooStyleCategory | 'ALL'>('ALL');\n const [status, setStatus] = useState<CooStyleItemStatus | 'ALL'>('ALL');\n const [offset, setOffset] = useState(0);\n\n const statusOptions = useMemo(\n () => [\n { value: 'ALL' as const, label: tr('coolandlovely.filters.all', 'All') },\n ...STATUS_ORDER.map((value) => ({\n value,\n label: tr(`coolandlovely.status.${value.toLowerCase()}`, formatStatusLabel(value)),\n })),\n ],\n [tr]\n );\n\n const query = useStyleItems({\n category: category === 'ALL' ? undefined : category,\n status: status === 'ALL' ? undefined : status,\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title={tr('coolandlovely.page.styleItemsTitle', 'Style Items')}\n description={tr(\n 'coolandlovely.page.styleItemsSubtitle',\n 'The shoppable catalogue across every brand'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/items/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" /> {tr('coolandlovely.actions.newItem', 'New item')}\n </button>\n }\n >\n {/* Status filter */}\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n {statusOptions.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => {\n setStatus(opt.value);\n setOffset(0);\n }}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n status === opt.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n\n {/* Category filter */}\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setCategory('ALL');\n setOffset(0);\n }}\n className={cn(\n 'rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n category === 'ALL'\n ? 'bg-accent-purple-subtle text-text-primary'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {tr('coolandlovely.filters.allCategories', 'All categories')}\n </button>\n {STYLE_CATEGORIES.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => {\n setCategory(c);\n setOffset(0);\n }}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n category === c\n ? 'bg-accent-purple-subtle text-text-primary'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {categoryIcon(c, 14)} {tr(`coolandlovely.category.${c.toLowerCase()}`, titleCase(c))}\n </button>\n ))}\n </div>\n\n {query.isLoading ? (\n <LoadingView label={tr('coolandlovely.loading.catalogue', 'Loading catalogue…')} />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Shirt className=\"h-10 w-10\" />}\n title={tr('coolandlovely.empty.itemsTitle', 'No items found')}\n description={tr(\n 'coolandlovely.empty.itemsDescription',\n 'No style items match these filters yet.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/items/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <Plus className=\"h-4 w-4\" /> {tr('coolandlovely.actions.newItem', 'New item')}\n </button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => navigateTo(`/items/${item.id}`)}\n className=\"group overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div className=\"relative\">\n <Thumbnail\n icon={categoryIcon(item.category, 32)}\n seed={item.id}\n fileId={item.image?.id}\n alt={item.name}\n className=\"aspect-[3/4] w-full\"\n />\n <div className=\"absolute right-2 top-2\">\n <StatusBadge status={item.status} className=\"shadow-sm\" />\n </div>\n </div>\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{item.name}</p>\n {item.brand?.name && (\n <p className=\"truncate text-xs text-text-secondary\">{item.brand.name}</p>\n )}\n <div className=\"mt-1.5 flex items-center justify-between gap-2\">\n <span className=\"text-sm font-semibold text-text-primary tabular-nums\">\n {formatPrice(item.price, item.currency)}\n </span>\n <span className=\"text-xs text-text-secondary\">{titleCase(item.category)}</span>\n </div>\n {item.colours.length > 0 && (\n <ColourSwatchRow colours={item.colours} max={5} className=\"mt-2\" />\n )}\n </div>\n </button>\n ))}\n </div>\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-center gap-2\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />{' '}\n {tr('coolandlovely.pagination.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {tr('coolandlovely.pagination.page', 'Page {{page}}', {\n page: offset / PAGE_SIZE + 1,\n })}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {tr('coolandlovely.pagination.next', 'Next')} <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuBA,IAAM,IAAY,IACZ,IAAqC;CAAC;CAAU;CAAS;CAAgB;CAAU;AAEzF,SAAS,EAAkB,GAAwB;AACjD,QAAO,EAAO,QAAQ,MAAM,IAAI,CAAC,QAAQ,UAAU,MAAM,EAAE,aAAa,CAAC;;AAG3E,SAAgB,IAAqB;CACnC,IAAM,EAAE,kBAAe,GAAkB,EACnC,IAAK,GAAO,EACZ,CAAC,GAAU,KAAe,EAAmC,MAAM,EACnE,CAAC,GAAQ,KAAa,EAAqC,MAAM,EACjE,CAAC,GAAQ,KAAa,EAAS,EAAE,EAEjC,IAAgB,QACd,CACJ;EAAE,OAAO;EAAgB,OAAO,EAAG,6BAA6B,MAAM;EAAE,EACxE,GAAG,EAAa,KAAK,OAAW;EAC9B;EACA,OAAO,EAAG,wBAAwB,EAAM,aAAa,IAAI,EAAkB,EAAM,CAAC;EACnF,EAAE,CACJ,EACD,CAAC,EAAG,CACL,EAEK,IAAQ,EAAc;EAC1B,UAAU,MAAa,QAAQ,KAAA,IAAY;EAC3C,QAAQ,MAAW,QAAQ,KAAA,IAAY;EACvC,YAAY;GAAE,OAAO;GAAW;GAAQ;EACzC,CAAC,EAEI,IAAQ,EAAM,MAAM,SAAS,EAAE,EAC/B,IAAU,EAAM,MAAM,SAAS,WAAW;AAEhD,QACE,kBAAC,GAAD;EACE,OAAO,EAAG,sCAAsC,cAAc;EAC9D,aAAa,EACX,yCACA,6CACD;EACD,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,aAAa;GACvC,WAAU;aAHZ;IAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;;IAAE,EAAG,iCAAiC,WAAW;IACtE;;YAbb;GAiBE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAc,KAAK,MAClB,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe;AAEb,MADA,EAAU,EAAI,MAAM,EACpB,EAAU,EAAE;;KAEd,WAAW,EACT,kEACA,MAAW,EAAI,QACX,kDACA,2DACL;eAEA,EAAI;KACE,EAdF,EAAI,MAcF,CACT;IACE,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe;AAEb,MADA,EAAY,MAAM,EAClB,EAAU,EAAE;;KAEd,WAAW,EACT,kEACA,MAAa,QACT,8CACA,2DACL;eAEA,EAAG,uCAAuC,iBAAiB;KACrD,CAAA,EACR,EAAiB,KAAK,MACrB,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe;AAEb,MADA,EAAY,EAAE,EACd,EAAU,EAAE;;KAEd,WAAW,EACT,mGACA,MAAa,IACT,8CACA,2DACL;eAZH;MAcG,EAAa,GAAG,GAAG;MAAC;MAAE,EAAG,0BAA0B,EAAE,aAAa,IAAI,EAAU,EAAE,CAAC;MAC7E;OAdF,EAcE,CACT,CACE;;GAEL,EAAM,YACL,kBAAC,GAAD,EAAa,OAAO,EAAG,mCAAmC,qBAAqB,EAAI,CAAA,GACjF,EAAM,UACR,kBAAC,GAAD,EAAW,eAAe,KAAK,EAAM,SAAS,EAAI,CAAA,GAChD,EAAM,WAAW,IACnB,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;IACrC,OAAO,EAAG,kCAAkC,iBAAiB;IAC7D,aAAa,EACX,wCACA,0CACD;IACD,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,aAAa;KACvC,WAAU;eAHZ;MAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;;MAAE,EAAG,iCAAiC,WAAW;MACtE;;IAEX,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,MACV,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe,EAAW,UAAU,EAAK,KAAK;KAC9C,WAAU;eAJZ,CAME,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,MAAM,EAAa,EAAK,UAAU,GAAG;OACrC,MAAM,EAAK;OACX,QAAQ,EAAK,OAAO;OACpB,KAAK,EAAK;OACV,WAAU;OACV,CAAA,EACF,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAa,QAAQ,EAAK;QAAQ,WAAU;QAAc,CAAA;OACtD,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAkD,EAAK;QAAS,CAAA;OAC5E,EAAK,OAAO,QACX,kBAAC,KAAD;QAAG,WAAU;kBAAwC,EAAK,MAAM;QAAS,CAAA;OAE3E,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAY,EAAK,OAAO,EAAK,SAAS;SAClC,CAAA,EACP,kBAAC,QAAD;SAAM,WAAU;mBAA+B,EAAU,EAAK,SAAS;SAAQ,CAAA,CAC3E;;OACL,EAAK,QAAQ,SAAS,KACrB,kBAAC,GAAD;QAAiB,SAAS,EAAK;QAAS,KAAK;QAAG,WAAU;QAAS,CAAA;OAEjE;QACC;OAhCF,EAAK,GAgCH,CACT;IACE,CAAA,GAEJ,IAAS,KAAK,MACd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAW;MACrB,eAAe,GAAW,MAAM,KAAK,IAAI,GAAG,IAAI,EAAU,CAAC;MAC3D,WAAU;gBAJZ;OAME,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;OAAC;OACnC,EAAG,qCAAqC,WAAW;OAC7C;;KACT,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,iCAAiC,iBAAiB,EACpD,MAAM,IAAS,IAAY,GAC5B,CAAC;MACG,CAAA;KACP,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,CAAC;MACX,eAAe,GAAW,MAAM,IAAI,EAAU;MAC9C,WAAU;gBAJZ;OAMG,EAAG,iCAAiC,OAAO;OAAC;OAAC,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;OAC3E;;KACL;MAEP,EAAA,CAAA;GAEM"}
@@ -1,11 +1,11 @@
1
1
  import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
2
2
  import { PageLayout as t } from "../../components/PageLayout.js";
3
- import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
4
- import { Thumbnail as i } from "../../components/Thumbnail.js";
5
- import { useAddLookbookEntry as a, useDeleteLookbook as o, useLookbook as s, useLooks as c, useRemoveLookbookEntry as ee, useStyleItems as l } from "../../hooks/useCoolAndLovelyApi.js";
6
- import { formatRelativeTime as u } from "../../utils/formatters.js";
7
- import { useAssistantEntityName as d } from "../../hooks/useAssistantEntity.js";
3
+ import { useAddLookbookEntry as n, useDeleteLookbook as r, useLookbook as i, useLooks as a, useRemoveLookbookEntry as o, useStyleItems as s } from "../../hooks/useCoolAndLovelyApi.js";
4
+ import { useAssistantEntityName as c } from "../../hooks/useAssistantEntity.js";
8
5
  import "../../hooks/index.js";
6
+ import { ErrorView as ee, LoadingView as l } from "../../components/StateViews.js";
7
+ import { Thumbnail as u } from "../../components/Thumbnail.js";
8
+ import { formatRelativeTime as d } from "../../utils/formatters.js";
9
9
  import { SelectField as f, TextField as p } from "../../components/Field.js";
10
10
  import { TagList as m } from "../../components/TagList.js";
11
11
  import { VisibilityChip as h } from "../../components/VisibilityChip.js";
@@ -21,12 +21,12 @@ var A = [
21
21
  "aspect-[4/5]"
22
22
  ];
23
23
  function j() {
24
- let { lookbookId: j } = E(), { navigateTo: M } = e(), N = s(j), P = ee(), F = a(), I = o();
25
- d(j, N.data?.name);
26
- let [L, R] = _(!1), [z, B] = _("look"), [V, H] = _(""), [U, W] = _(""), G = c({ pagination: {
24
+ let { lookbookId: j } = E(), { navigateTo: M } = e(), N = i(j), P = o(), F = n(), I = r();
25
+ c(j, N.data?.name);
26
+ let [L, R] = _(!1), [z, B] = _("look"), [V, H] = _(""), [U, W] = _(""), G = a({ pagination: {
27
27
  limit: 100,
28
28
  offset: 0
29
- } }), K = l({ pagination: {
29
+ } }), K = s({ pagination: {
30
30
  limit: 100,
31
31
  offset: 0
32
32
  } }), q = g(() => new Map((G.data?.items ?? []).map((e) => [e.id, e])), [G.data]), J = g(() => new Map((K.data?.items ?? []).map((e) => [e.id, e])), [K.data]), Y = g(() => (K.data?.items ?? []).filter((e) => e.status !== "RETIRED"), [K.data]), X = g(() => (G.data?.items ?? []).map((e) => ({
@@ -38,11 +38,11 @@ function j() {
38
38
  })), [Y]);
39
39
  if (N.isLoading) return /* @__PURE__ */ D(t, {
40
40
  title: "Lookbook",
41
- children: /* @__PURE__ */ D(r, { label: "Loading lookbook…" })
41
+ children: /* @__PURE__ */ D(l, { label: "Loading lookbook…" })
42
42
  });
43
43
  if (N.isError || !N.data) return /* @__PURE__ */ D(t, {
44
44
  title: "Lookbook",
45
- children: /* @__PURE__ */ D(n, {
45
+ children: /* @__PURE__ */ D(ee, {
46
46
  title: "Lookbook not found",
47
47
  onRetry: () => void N.refetch()
48
48
  })
@@ -104,7 +104,7 @@ function j() {
104
104
  children: [
105
105
  /* @__PURE__ */ D(y, { className: "h-3.5 w-3.5" }),
106
106
  " Updated ",
107
- u(Q.updatedAt)
107
+ d(Q.updatedAt)
108
108
  ]
109
109
  }),
110
110
  /* @__PURE__ */ O("span", {
@@ -200,7 +200,7 @@ function j() {
200
200
  }) : /* @__PURE__ */ D("div", {
201
201
  className: "columns-2 gap-4 md:columns-3",
202
202
  children: $.map((e, t) => {
203
- let n = !!e.lookId, r = e.lookId ? q.get(e.lookId) : void 0, a = e.styleItemId ? J.get(e.styleItemId) : void 0, o = n ? r?.title ?? "Saved look" : a?.name ?? "Clipped piece", s = n ? r?.image?.id : a?.image?.id, c = P.isPending && P.variables === e.id;
203
+ let n = !!e.lookId, r = e.lookId ? q.get(e.lookId) : void 0, i = e.styleItemId ? J.get(e.styleItemId) : void 0, a = n ? r?.title ?? "Saved look" : i?.name ?? "Clipped piece", o = n ? r?.image?.id : i?.image?.id, s = P.isPending && P.variables === e.id;
204
204
  return /* @__PURE__ */ O("div", {
205
205
  className: "group relative mb-4 break-inside-avoid overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg",
206
206
  children: [/* @__PURE__ */ D("button", {
@@ -208,13 +208,13 @@ function j() {
208
208
  onClick: () => {
209
209
  n && e.lookId ? M(`/looks/${e.lookId}`) : e.styleItemId && M(`/items/${e.styleItemId}`);
210
210
  },
211
- "aria-label": `Open ${o}`,
211
+ "aria-label": `Open ${a}`,
212
212
  className: "block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]",
213
- children: /* @__PURE__ */ D(i, {
213
+ children: /* @__PURE__ */ D(u, {
214
214
  icon: D(n ? S : C, { className: "h-8 w-8" }),
215
215
  seed: e.id,
216
- fileId: s,
217
- alt: o,
216
+ fileId: o,
217
+ alt: a,
218
218
  className: `w-full rounded-none ${A[t % A.length]}`,
219
219
  overlay: /* @__PURE__ */ O("div", {
220
220
  className: "pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-1.5 bg-gradient-to-t from-bg-canvas/90 via-bg-canvas/40 to-transparent p-3 pt-10",
@@ -226,7 +226,7 @@ function j() {
226
226
  })
227
227
  }), /* @__PURE__ */ D("p", {
228
228
  className: "truncate text-sm font-semibold text-text-primary",
229
- children: o
229
+ children: a
230
230
  })]
231
231
  })
232
232
  })
@@ -234,9 +234,9 @@ function j() {
234
234
  type: "button",
235
235
  disabled: P.isPending,
236
236
  onClick: () => P.mutate(e.id),
237
- "aria-label": `Remove ${o}`,
237
+ "aria-label": `Remove ${a}`,
238
238
  className: "absolute right-2 top-2 z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-bg-surface/90 text-text-secondary opacity-100 shadow-sm backdrop-blur transition-opacity hover:text-status-error-text sm:opacity-0 sm:focus-visible:opacity-100 sm:group-hover:opacity-100 disabled:opacity-60",
239
- children: c ? /* @__PURE__ */ D(b, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ D(T, { className: "h-3.5 w-3.5" })
239
+ children: s ? /* @__PURE__ */ D(b, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ D(T, { className: "h-3.5 w-3.5" })
240
240
  })]
241
241
  }, e.id);
242
242
  })
@@ -1 +1 @@
1
- {"version":3,"file":"LookbookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx"],"sourcesContent":["/**\n * Lookbook Detail Page\n * @module coolandlovely/pages/lookbooks\n *\n * Single lookbook (`cooLookbook`) rendered as a Pinterest-style masonry board.\n * Each entry's `lookId` / `styleItemId` is resolved client-side against the\n * already-fetched looks/items lists to a real title + image, with an optional\n * note badge and a hover X-remove (`cooRemoveLookbookEntry`). New entries are\n * added inline (`cooAddLookbookEntry`).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, BookHeart, Sparkles, Tag, Loader2, Plus, Trash2, X, Clock } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { VisibilityChip } from '../../components/VisibilityChip';\nimport { SelectField, TextField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport {\n useLookbook,\n useRemoveLookbookEntry,\n useAddLookbookEntry,\n useDeleteLookbook,\n useLooks,\n useStyleItems,\n useAssistantEntityName,\n} from '../../hooks';\nimport { formatRelativeTime } from '../../utils/formatters';\n\ntype EntryKind = 'look' | 'item';\n\n/** Alternating aspect ratios give the board its masonry rhythm. */\nconst TILE_ASPECTS = ['aspect-[3/4]', 'aspect-square', 'aspect-[4/5]'];\n\nexport function LookbookDetailPage() {\n const { lookbookId } = useParams<{ lookbookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbook(lookbookId);\n const removeEntry = useRemoveLookbookEntry();\n const addEntry = useAddLookbookEntry();\n const deleteLookbook = useDeleteLookbook();\n // Name the board for the assistant's screen context.\n useAssistantEntityName(lookbookId, query.data?.name);\n\n const [adding, setAdding] = useState(false);\n const [entryKind, setEntryKind] = useState<EntryKind>('look');\n const [selectedId, setSelectedId] = useState('');\n const [note, setNote] = useState('');\n\n const looks = useLooks({ pagination: { limit: 100, offset: 0 } });\n const items = useStyleItems({ pagination: { limit: 100, offset: 0 } });\n\n const lookById = useMemo(\n () => new Map((looks.data?.items ?? []).map((l) => [l.id, l])),\n [looks.data]\n );\n const itemById = useMemo(\n () => new Map((items.data?.items ?? []).map((i) => [i.id, i])),\n [items.data]\n );\n const usableItems = useMemo(\n () => (items.data?.items ?? []).filter((i) => i.status !== 'RETIRED'),\n [items.data]\n );\n\n const lookOptions = useMemo(\n () => (looks.data?.items ?? []).map((l) => ({ value: l.id, label: l.title })),\n [looks.data]\n );\n const itemOptions = useMemo(\n () =>\n usableItems.map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''}`,\n })),\n [usableItems]\n );\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Lookbook\">\n <LoadingView label=\"Loading lookbook…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Lookbook\">\n <ErrorView title=\"Lookbook not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const lookbook = query.data;\n const entries = [...(lookbook.entries ?? [])].sort((a, b) => a.position - b.position);\n\n const handleAddEntry = (e: React.FormEvent) => {\n e.preventDefault();\n if (!lookbookId || !selectedId) return;\n addEntry.mutate(\n {\n lookbookId,\n lookId: entryKind === 'look' ? selectedId : undefined,\n styleItemId: entryKind === 'item' ? selectedId : undefined,\n note: note.trim() || undefined,\n },\n {\n onSuccess: () => {\n setSelectedId('');\n setNote('');\n setAdding(false);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title={lookbook.name}\n description={lookbook.description ?? undefined}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/lookbooks')}\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\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setAdding((v) => !v);\n setSelectedId('');\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n {adding ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {adding ? 'Close' : 'Add entry'}\n </button>\n <button\n type=\"button\"\n disabled={deleteLookbook.isPending}\n onClick={() => {\n if (!lookbookId) return;\n deleteLookbook.mutate(lookbookId, { onSuccess: () => navigateTo('/lookbooks') });\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {deleteLookbook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Delete\n </button>\n </div>\n }\n >\n <div className=\"mx-auto max-w-5xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <BookHeart className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">{lookbook.name}</h2>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2\">\n <VisibilityChip visibility={lookbook.visibility} />\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" /> Updated {formatRelativeTime(lookbook.updatedAt)}\n </span>\n <span className=\"text-xs text-text-secondary tabular-nums\">\n · {entries.length} {entries.length === 1 ? 'item' : 'items'}\n </span>\n </div>\n </div>\n </div>\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} className=\"mt-4\" />}\n </GlassCard>\n\n {adding && (\n <form onSubmit={handleAddEntry} className=\"mt-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <Plus className=\"h-5 w-5\" /> Add to this lookbook\n </h3>\n <div className=\"mb-4 flex flex-wrap gap-2\">\n {(['look', 'item'] as EntryKind[]).map((k) => (\n <button\n key={k}\n type=\"button\"\n onClick={() => {\n setEntryKind(k);\n setSelectedId('');\n }}\n className={\n entryKind === k\n ? 'rounded-full bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text'\n : 'rounded-full bg-bg-sunken px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary'\n }\n >\n {k === 'look' ? 'Save a look' : 'Clip a piece'}\n </button>\n ))}\n </div>\n <SelectField\n label={entryKind === 'look' ? 'Look' : 'Style item'}\n value={selectedId}\n onChange={setSelectedId}\n options={entryKind === 'look' ? lookOptions : itemOptions}\n placeholder={\n entryKind === 'look'\n ? looks.isLoading\n ? 'Loading looks…'\n : 'Select a look'\n : items.isLoading\n ? 'Loading items…'\n : 'Select a style item'\n }\n />\n <TextField\n label=\"Note (optional)\"\n value={note}\n onChange={setNote}\n placeholder=\"Why you saved it…\"\n className=\"mt-4\"\n />\n {addEntry.isError && (\n <p className=\"mt-3 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {addEntry.error instanceof Error ? addEntry.error.message : 'Could not add entry'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={!selectedId || addEntry.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addEntry.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Add entry\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">Board ({entries.length})</h3>\n {entries.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n This lookbook has no entries yet. Clip a catalogue piece or save a look to add one.\n </p>\n ) : (\n <div className=\"columns-2 gap-4 md:columns-3\">\n {entries.map((entry, idx) => {\n const isLook = Boolean(entry.lookId);\n const look = entry.lookId ? lookById.get(entry.lookId) : undefined;\n const item = entry.styleItemId ? itemById.get(entry.styleItemId) : undefined;\n const title = isLook\n ? (look?.title ?? 'Saved look')\n : (item?.name ?? 'Clipped piece');\n const imageFileId = isLook ? look?.image?.id : item?.image?.id;\n const removing = removeEntry.isPending && removeEntry.variables === entry.id;\n const onOpen = () => {\n if (isLook && entry.lookId) navigateTo(`/looks/${entry.lookId}`);\n else if (entry.styleItemId) navigateTo(`/items/${entry.styleItemId}`);\n };\n return (\n <div\n key={entry.id}\n className=\"group relative mb-4 break-inside-avoid overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg\"\n >\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${title}`}\n className=\"block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <Thumbnail\n icon={\n isLook ? <Sparkles className=\"h-8 w-8\" /> : <Tag className=\"h-8 w-8\" />\n }\n seed={entry.id}\n fileId={imageFileId}\n alt={title}\n className={`w-full rounded-none ${TILE_ASPECTS[idx % TILE_ASPECTS.length]}`}\n overlay={\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-1.5 bg-gradient-to-t from-bg-canvas/90 via-bg-canvas/40 to-transparent p-3 pt-10\">\n {entry.note && (\n <span className=\"inline-flex w-fit max-w-full items-center rounded-md bg-bg-canvas/80 px-2 py-0.5 text-[11px] font-medium text-text-primary backdrop-blur\">\n <span className=\"truncate\">{entry.note}</span>\n </span>\n )}\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {title}\n </p>\n </div>\n }\n />\n </button>\n <button\n type=\"button\"\n disabled={removeEntry.isPending}\n onClick={() => removeEntry.mutate(entry.id)}\n aria-label={`Remove ${title}`}\n className=\"absolute right-2 top-2 z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-bg-surface/90 text-text-secondary opacity-100 shadow-sm backdrop-blur transition-opacity hover:text-status-error-text sm:opacity-0 sm:focus-visible:opacity-100 sm:group-hover:opacity-100 disabled:opacity-60\"\n >\n {removing ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <X className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LookbookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx"],"sourcesContent":["/**\n * Lookbook Detail Page\n * @module coolandlovely/pages/lookbooks\n *\n * Single lookbook (`cooLookbook`) rendered as a Pinterest-style masonry board.\n * Each entry's `lookId` / `styleItemId` is resolved client-side against the\n * already-fetched looks/items lists to a real title + image, with an optional\n * note badge and a hover X-remove (`cooRemoveLookbookEntry`). New entries are\n * added inline (`cooAddLookbookEntry`).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, BookHeart, Sparkles, Tag, Loader2, Plus, Trash2, X, Clock } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { VisibilityChip } from '../../components/VisibilityChip';\nimport { SelectField, TextField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport {\n useLookbook,\n useRemoveLookbookEntry,\n useAddLookbookEntry,\n useDeleteLookbook,\n useLooks,\n useStyleItems,\n useAssistantEntityName,\n} from '../../hooks';\nimport { formatRelativeTime } from '../../utils/formatters';\n\ntype EntryKind = 'look' | 'item';\n\n/** Alternating aspect ratios give the board its masonry rhythm. */\nconst TILE_ASPECTS = ['aspect-[3/4]', 'aspect-square', 'aspect-[4/5]'];\n\nexport function LookbookDetailPage() {\n const { lookbookId } = useParams<{ lookbookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbook(lookbookId);\n const removeEntry = useRemoveLookbookEntry();\n const addEntry = useAddLookbookEntry();\n const deleteLookbook = useDeleteLookbook();\n // Name the board for the assistant's screen context.\n useAssistantEntityName(lookbookId, query.data?.name);\n\n const [adding, setAdding] = useState(false);\n const [entryKind, setEntryKind] = useState<EntryKind>('look');\n const [selectedId, setSelectedId] = useState('');\n const [note, setNote] = useState('');\n\n const looks = useLooks({ pagination: { limit: 100, offset: 0 } });\n const items = useStyleItems({ pagination: { limit: 100, offset: 0 } });\n\n const lookById = useMemo(\n () => new Map((looks.data?.items ?? []).map((l) => [l.id, l])),\n [looks.data]\n );\n const itemById = useMemo(\n () => new Map((items.data?.items ?? []).map((i) => [i.id, i])),\n [items.data]\n );\n const usableItems = useMemo(\n () => (items.data?.items ?? []).filter((i) => i.status !== 'RETIRED'),\n [items.data]\n );\n\n const lookOptions = useMemo(\n () => (looks.data?.items ?? []).map((l) => ({ value: l.id, label: l.title })),\n [looks.data]\n );\n const itemOptions = useMemo(\n () =>\n usableItems.map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''}`,\n })),\n [usableItems]\n );\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Lookbook\">\n <LoadingView label=\"Loading lookbook…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Lookbook\">\n <ErrorView title=\"Lookbook not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const lookbook = query.data;\n const entries = [...(lookbook.entries ?? [])].sort((a, b) => a.position - b.position);\n\n const handleAddEntry = (e: React.FormEvent) => {\n e.preventDefault();\n if (!lookbookId || !selectedId) return;\n addEntry.mutate(\n {\n lookbookId,\n lookId: entryKind === 'look' ? selectedId : undefined,\n styleItemId: entryKind === 'item' ? selectedId : undefined,\n note: note.trim() || undefined,\n },\n {\n onSuccess: () => {\n setSelectedId('');\n setNote('');\n setAdding(false);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title={lookbook.name}\n description={lookbook.description ?? undefined}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/lookbooks')}\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\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setAdding((v) => !v);\n setSelectedId('');\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n {adding ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {adding ? 'Close' : 'Add entry'}\n </button>\n <button\n type=\"button\"\n disabled={deleteLookbook.isPending}\n onClick={() => {\n if (!lookbookId) return;\n deleteLookbook.mutate(lookbookId, { onSuccess: () => navigateTo('/lookbooks') });\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {deleteLookbook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Delete\n </button>\n </div>\n }\n >\n <div className=\"mx-auto max-w-5xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <BookHeart className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">{lookbook.name}</h2>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2\">\n <VisibilityChip visibility={lookbook.visibility} />\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" /> Updated {formatRelativeTime(lookbook.updatedAt)}\n </span>\n <span className=\"text-xs text-text-secondary tabular-nums\">\n · {entries.length} {entries.length === 1 ? 'item' : 'items'}\n </span>\n </div>\n </div>\n </div>\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} className=\"mt-4\" />}\n </GlassCard>\n\n {adding && (\n <form onSubmit={handleAddEntry} className=\"mt-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <Plus className=\"h-5 w-5\" /> Add to this lookbook\n </h3>\n <div className=\"mb-4 flex flex-wrap gap-2\">\n {(['look', 'item'] as EntryKind[]).map((k) => (\n <button\n key={k}\n type=\"button\"\n onClick={() => {\n setEntryKind(k);\n setSelectedId('');\n }}\n className={\n entryKind === k\n ? 'rounded-full bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text'\n : 'rounded-full bg-bg-sunken px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary'\n }\n >\n {k === 'look' ? 'Save a look' : 'Clip a piece'}\n </button>\n ))}\n </div>\n <SelectField\n label={entryKind === 'look' ? 'Look' : 'Style item'}\n value={selectedId}\n onChange={setSelectedId}\n options={entryKind === 'look' ? lookOptions : itemOptions}\n placeholder={\n entryKind === 'look'\n ? looks.isLoading\n ? 'Loading looks…'\n : 'Select a look'\n : items.isLoading\n ? 'Loading items…'\n : 'Select a style item'\n }\n />\n <TextField\n label=\"Note (optional)\"\n value={note}\n onChange={setNote}\n placeholder=\"Why you saved it…\"\n className=\"mt-4\"\n />\n {addEntry.isError && (\n <p className=\"mt-3 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {addEntry.error instanceof Error ? addEntry.error.message : 'Could not add entry'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={!selectedId || addEntry.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addEntry.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Add entry\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">Board ({entries.length})</h3>\n {entries.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n This lookbook has no entries yet. Clip a catalogue piece or save a look to add one.\n </p>\n ) : (\n <div className=\"columns-2 gap-4 md:columns-3\">\n {entries.map((entry, idx) => {\n const isLook = Boolean(entry.lookId);\n const look = entry.lookId ? lookById.get(entry.lookId) : undefined;\n const item = entry.styleItemId ? itemById.get(entry.styleItemId) : undefined;\n const title = isLook\n ? (look?.title ?? 'Saved look')\n : (item?.name ?? 'Clipped piece');\n const imageFileId = isLook ? look?.image?.id : item?.image?.id;\n const removing = removeEntry.isPending && removeEntry.variables === entry.id;\n const onOpen = () => {\n if (isLook && entry.lookId) navigateTo(`/looks/${entry.lookId}`);\n else if (entry.styleItemId) navigateTo(`/items/${entry.styleItemId}`);\n };\n return (\n <div\n key={entry.id}\n className=\"group relative mb-4 break-inside-avoid overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg\"\n >\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${title}`}\n className=\"block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <Thumbnail\n icon={\n isLook ? <Sparkles className=\"h-8 w-8\" /> : <Tag className=\"h-8 w-8\" />\n }\n seed={entry.id}\n fileId={imageFileId}\n alt={title}\n className={`w-full rounded-none ${TILE_ASPECTS[idx % TILE_ASPECTS.length]}`}\n overlay={\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-1.5 bg-gradient-to-t from-bg-canvas/90 via-bg-canvas/40 to-transparent p-3 pt-10\">\n {entry.note && (\n <span className=\"inline-flex w-fit max-w-full items-center rounded-md bg-bg-canvas/80 px-2 py-0.5 text-[11px] font-medium text-text-primary backdrop-blur\">\n <span className=\"truncate\">{entry.note}</span>\n </span>\n )}\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {title}\n </p>\n </div>\n }\n />\n </button>\n <button\n type=\"button\"\n disabled={removeEntry.isPending}\n onClick={() => removeEntry.mutate(entry.id)}\n aria-label={`Remove ${title}`}\n className=\"absolute right-2 top-2 z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-bg-surface/90 text-text-secondary opacity-100 shadow-sm backdrop-blur transition-opacity hover:text-status-error-text sm:opacity-0 sm:focus-visible:opacity-100 sm:group-hover:opacity-100 disabled:opacity-60\"\n >\n {removing ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <X className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"LookbooksListPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/lookbooks/LookbooksListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAmBH,wBAAgB,iBAAiB,gCAoKhC"}
1
+ {"version":3,"file":"LookbooksListPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/lookbooks/LookbooksListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAmBH,wBAAgB,iBAAiB,gCAwLhC"}