@burdenoff/microfe-coolandlovely 2026.706.2 → 2026.706.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  3. package/dist/coolandlovely/components/AddToLookbookButton.js +22 -22
  4. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
  5. package/dist/coolandlovely/components/BrandCover.js +39 -0
  6. package/dist/coolandlovely/components/BrandCover.js.map +1 -0
  7. package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
  8. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
  9. package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
  10. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
  11. package/dist/coolandlovely/components/Comments.js +4 -4
  12. package/dist/coolandlovely/components/Comments.js.map +1 -1
  13. package/dist/coolandlovely/components/FileImage.js.map +1 -1
  14. package/dist/coolandlovely/components/ImageUploadField.js +139 -57
  15. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -1
  16. package/dist/coolandlovely/components/LookCard.js +77 -0
  17. package/dist/coolandlovely/components/LookCard.js.map +1 -0
  18. package/dist/coolandlovely/components/MetricsCard.js +1 -1
  19. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  20. package/dist/coolandlovely/components/ShopItemCard.js +45 -0
  21. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
  22. package/dist/coolandlovely/components/StepTracker.js +35 -0
  23. package/dist/coolandlovely/components/StepTracker.js.map +1 -0
  24. package/dist/coolandlovely/components/Thumbnail.js +15 -11
  25. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  26. package/dist/coolandlovely/components/VisibilityChip.js +32 -0
  27. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
  28. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +94 -69
  29. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  30. package/dist/coolandlovely/hooks/useFiles.js +49 -32
  31. package/dist/coolandlovely/hooks/useFiles.js.map +1 -1
  32. package/dist/coolandlovely/pages/HomePage.js +112 -133
  33. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  34. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +253 -242
  35. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  36. package/dist/coolandlovely/pages/brands/BrandFormPage.js +116 -112
  37. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  38. package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -13
  39. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  40. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
  41. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  42. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  43. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
  44. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  45. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +131 -108
  46. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  47. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +1 -1
  48. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -56
  49. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  50. package/dist/coolandlovely/pages/discover/DiscoverPage.js +1 -1
  51. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  52. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +175 -147
  53. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  54. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +1 -1
  55. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +71 -62
  56. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +149 -127
  58. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  59. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +69 -53
  60. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  61. package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -113
  62. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  63. package/dist/coolandlovely/pages/looks/LookFormPage.js +122 -110
  64. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  65. package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -97
  66. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  67. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +372 -210
  68. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  69. package/dist/coolandlovely/pages/trends/TrendsPage.js +1 -1
  70. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -1
  71. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +130 -94
  72. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  73. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -76
  74. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  75. package/package.json +1 -1
@@ -2,17 +2,17 @@ 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
4
  import { COO_FILE_FOLDERS as r } from "../../hooks/useFiles.js";
5
- import { useCollaborations as i, useCreateLook as a, useStyleItems as o, useUpdateLook as s } from "../../hooks/useCoolAndLovelyApi.js";
5
+ import { useCollaborations as ee, useCreateLook as i, useStyleItems as a, useUpdateLook as te } from "../../hooks/useCoolAndLovelyApi.js";
6
+ import { formatPrice as o, parseCsv as s, titleCase as c } from "../../utils/formatters.js";
6
7
  import "../../hooks/index.js";
7
- import { formatPrice as ee, parseCsv as te, titleCase as c } from "../../utils/formatters.js";
8
- import { SelectField as l, TextAreaField as u, TextField as d, ToggleField as f } from "../../components/Field.js";
9
- import { ImageUploadField as p } from "../../components/ImageUploadField.js";
10
- import { useMemo as m, useState as h } from "react";
11
- import { ArrowLeft as ne, Loader2 as re, Plus as g, Save as _, Sparkles as v, X as y } from "lucide-react";
12
- import { jsx as b, jsxs as x } from "react/jsx-runtime";
13
- import { GlassCard as S } from "@burdenoff/fe-libs/ui";
8
+ import { SelectField as l, TextAreaField as ne, TextField as u, ToggleField as re } from "../../components/Field.js";
9
+ import { ImageUploadField as d } from "../../components/ImageUploadField.js";
10
+ import { useMemo as f, useState as p } from "react";
11
+ import { ArrowLeft as ie, Loader2 as ae, Plus as m, Save as h, Sparkles as oe, X as se } from "lucide-react";
12
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
13
+ import { GlassCard as v } from "@burdenoff/fe-libs/ui";
14
14
  //#region src/coolandlovely/pages/looks/LookFormPage.tsx
15
- var C = [
15
+ var y = [
16
16
  "EVERYDAY",
17
17
  "WORK",
18
18
  "EVENING",
@@ -21,220 +21,232 @@ var C = [
21
21
  "PARTY",
22
22
  "ACTIVE",
23
23
  "OTHER"
24
- ], w = [
24
+ ], b = [
25
25
  "PUBLIC",
26
26
  "FOLLOWERS",
27
27
  "PRIVATE"
28
28
  ];
29
- function T() {
30
- let { navigateTo: T, currentUser: E } = e(), D = a(), O = s(), k = o({
31
- status: "ACTIVE",
32
- pagination: {
33
- limit: 100,
34
- offset: 0
35
- }
36
- }), A = i(E?.id ? { creatorId: E.id } : {}), [j, M] = h(""), [N, P] = h(""), [F, I] = h("EVERYDAY"), [L, R] = h("PUBLIC"), [z, B] = h(""), [V, H] = h(""), [U, W] = h(""), [G, K] = h(!1), [q, J] = h([]), [Y, X] = h(), ie = m(() => (k.data?.items ?? []).map((e) => ({
29
+ function x() {
30
+ let { navigateTo: x, currentUser: S } = e(), C = i(), w = te(), T = a({ pagination: {
31
+ limit: 100,
32
+ offset: 0
33
+ } }), E = ee(S?.id ? { creatorId: S.id } : {}), [D, ce] = p(""), [O, k] = p(""), [A, j] = p("EVERYDAY"), [M, N] = p("PUBLIC"), [P, F] = p(""), [I, L] = p(""), [R, z] = p(""), [B, V] = p(!1), [H, U] = p([]), [W, G] = p(""), [le, K] = p(), q = f(() => (T.data?.items ?? []).filter((e) => e.status !== "RETIRED"), [T.data]), J = f(() => q.map((e) => ({
37
34
  value: e.id,
38
- label: `${e.name}${e.brand?.name ? ` · ${e.brand.name}` : ""} — ${ee(e.price, e.currency)}`
39
- })), [k.data]), Z = m(() => (A.data ?? []).map((e) => ({
35
+ label: `${e.name}${e.brand?.name ? ` · ${e.brand.name}` : ""} — ${o(e.price, e.currency)}`
36
+ })), [q]), Y = f(() => {
37
+ let e = new Set(H.map((e) => e.styleItemId));
38
+ return J.filter((t) => !e.has(t.value));
39
+ }, [J, H]), X = f(() => (E.data ?? []).map((e) => ({
40
40
  value: e.id,
41
41
  label: e.title
42
- })), [A.data]), ae = C.map((e) => ({
42
+ })), [E.data]), Z = y.map((e) => ({
43
43
  value: e,
44
44
  label: c(e)
45
- })), oe = w.map((e) => ({
45
+ })), ue = b.map((e) => ({
46
46
  value: e,
47
47
  label: c(e)
48
- })), se = () => J((e) => [...e, {
49
- styleItemId: "",
50
- styling: ""
51
- }]), ce = (e) => J((t) => t.filter((t, n) => n !== e)), Q = (e, t) => J((n) => n.map((n, r) => r === e ? {
48
+ })), de = (e) => {
49
+ e && U((t) => t.some((t) => t.styleItemId === e) ? t : [...t, {
50
+ styleItemId: e,
51
+ styling: ""
52
+ }]);
53
+ }, fe = (e) => U((t) => t.filter((t, n) => n !== e)), Q = (e, t) => U((n) => n.map((n, r) => r === e ? {
52
54
  ...n,
53
55
  ...t
54
- } : n)), $ = D.isPending || O.isPending;
55
- return /* @__PURE__ */ b(n, {
56
+ } : n)), $ = C.isPending || w.isPending;
57
+ return /* @__PURE__ */ g(n, {
56
58
  title: "New look",
57
59
  description: "Compose a shop-the-look outfit from catalogue pieces",
58
- actions: /* @__PURE__ */ x("button", {
60
+ actions: /* @__PURE__ */ _("button", {
59
61
  type: "button",
60
- onClick: () => T("/looks"),
62
+ onClick: () => x("/looks"),
61
63
  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",
62
- children: [/* @__PURE__ */ b(ne, { className: "h-4 w-4" }), " Cancel"]
64
+ children: [/* @__PURE__ */ g(ie, { className: "h-4 w-4" }), " Cancel"]
63
65
  }),
64
- children: /* @__PURE__ */ b("form", {
66
+ children: /* @__PURE__ */ g("form", {
65
67
  onSubmit: (e) => {
66
- if (e.preventDefault(), !j.trim()) {
67
- X("Title is required");
68
+ if (e.preventDefault(), !D.trim()) {
69
+ K("Title is required");
68
70
  return;
69
71
  }
70
- X(void 0);
71
- let t = q.filter((e) => e.styleItemId).map((e, t) => ({
72
+ K(void 0);
73
+ let t = H.filter((e) => e.styleItemId).map((e, t) => ({
72
74
  styleItemId: e.styleItemId,
73
75
  position: t,
74
76
  styling: e.styling.trim() || void 0
75
77
  }));
76
- D.mutate({
77
- title: j.trim(),
78
- caption: N.trim() || void 0,
79
- occasion: F,
80
- visibility: L,
81
- collaborationId: U || void 0,
82
- imageFileId: V || void 0,
83
- tags: te(z),
78
+ C.mutate({
79
+ title: D.trim(),
80
+ caption: O.trim() || void 0,
81
+ occasion: A,
82
+ visibility: M,
83
+ collaborationId: R || void 0,
84
+ imageFileId: I || void 0,
85
+ tags: s(P),
84
86
  items: t.length > 0 ? t : void 0
85
87
  }, { onSuccess: (e) => {
86
- G ? O.mutate({
88
+ B ? w.mutate({
87
89
  id: e.id,
88
90
  input: { isFeatured: !0 }
89
- }, { onSuccess: () => T(`/looks/${e.id}`) }) : T(`/looks/${e.id}`);
91
+ }, { onSuccess: () => x(`/looks/${e.id}`) }) : x(`/looks/${e.id}`);
90
92
  } });
91
93
  },
92
94
  className: "mx-auto max-w-2xl",
93
- children: /* @__PURE__ */ x(S, {
95
+ children: /* @__PURE__ */ _(v, {
94
96
  treatment: "plain",
95
97
  className: "p-6",
96
98
  children: [
97
- /* @__PURE__ */ x("div", {
99
+ /* @__PURE__ */ _("div", {
98
100
  className: "mb-6 flex items-center gap-3",
99
- children: [/* @__PURE__ */ b("div", {
101
+ children: [/* @__PURE__ */ g("div", {
100
102
  className: "flex h-11 w-11 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text",
101
- children: /* @__PURE__ */ b(v, { className: "h-5 w-5" })
102
- }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
103
+ children: /* @__PURE__ */ g(oe, { className: "h-5 w-5" })
104
+ }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h2", {
103
105
  className: "text-base font-semibold text-text-primary",
104
106
  children: "Look details"
105
- }), /* @__PURE__ */ b("p", {
107
+ }), /* @__PURE__ */ g("p", {
106
108
  className: "text-xs text-text-secondary",
107
109
  children: "Created as a draft — publish it from the look page."
108
110
  })] })]
109
111
  }),
110
- /* @__PURE__ */ b(d, {
112
+ /* @__PURE__ */ g(u, {
111
113
  id: "title",
112
114
  label: "Title",
113
- value: j,
114
- onChange: M,
115
+ value: D,
116
+ onChange: ce,
115
117
  placeholder: "Coastal grandma, refined",
116
118
  required: !0,
117
- error: Y
119
+ error: le
118
120
  }),
119
- /* @__PURE__ */ b(u, {
121
+ /* @__PURE__ */ g(ne, {
120
122
  id: "caption",
121
123
  label: "Caption",
122
- value: N,
123
- onChange: P,
124
+ value: O,
125
+ onChange: k,
124
126
  placeholder: "How to style it…",
125
127
  rows: 3,
126
128
  className: "mt-4"
127
129
  }),
128
- /* @__PURE__ */ b(p, {
130
+ /* @__PURE__ */ g(d, {
129
131
  label: "Look photo",
130
132
  aspect: "wide",
131
- value: V,
132
- onChange: H,
133
+ value: I,
134
+ onChange: L,
133
135
  folder: r.looks,
134
136
  hint: "The styled outfit shot",
135
137
  className: "mt-4"
136
138
  }),
137
- /* @__PURE__ */ x("div", {
139
+ /* @__PURE__ */ _("div", {
138
140
  className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2",
139
- children: [/* @__PURE__ */ b(l, {
141
+ children: [/* @__PURE__ */ g(l, {
140
142
  id: "occasion",
141
143
  label: "Occasion",
142
- value: F,
143
- onChange: (e) => I(e),
144
- options: ae
145
- }), /* @__PURE__ */ b(l, {
144
+ value: A,
145
+ onChange: (e) => j(e),
146
+ options: Z
147
+ }), /* @__PURE__ */ g(l, {
146
148
  id: "visibility",
147
149
  label: "Visibility",
148
- value: L,
149
- onChange: (e) => R(e),
150
- options: oe
150
+ value: M,
151
+ onChange: (e) => N(e),
152
+ options: ue
151
153
  })]
152
154
  }),
153
- /* @__PURE__ */ b(l, {
155
+ /* @__PURE__ */ g(l, {
154
156
  id: "collaborationId",
155
157
  label: "Collaboration (optional)",
156
- value: U,
157
- onChange: W,
158
- options: Z,
159
- placeholder: A.isLoading ? "Loading collaborations…" : Z.length === 0 ? "No collaborations yet" : "Not part of a collaboration",
158
+ value: R,
159
+ onChange: z,
160
+ options: X,
161
+ placeholder: E.isLoading ? "Loading collaborations…" : X.length === 0 ? "No collaborations yet" : "Not part of a collaboration",
160
162
  hint: "Attribute this look to one of your collaborations",
161
163
  className: "mt-4"
162
164
  }),
163
- /* @__PURE__ */ b(d, {
165
+ /* @__PURE__ */ g(u, {
164
166
  id: "tags",
165
167
  label: "Tags",
166
- value: z,
167
- onChange: B,
168
+ value: P,
169
+ onChange: F,
168
170
  placeholder: "linen, neutral",
169
171
  hint: "Comma-separated",
170
172
  className: "mt-4"
171
173
  }),
172
- /* @__PURE__ */ b(f, {
174
+ /* @__PURE__ */ g(re, {
173
175
  id: "isFeatured",
174
176
  label: "Feature this look",
175
- checked: G,
176
- onChange: K,
177
+ checked: B,
178
+ onChange: V,
177
179
  hint: "Featured looks surface in the Featured filter and carry a star badge.",
178
180
  className: "mt-4"
179
181
  }),
180
- /* @__PURE__ */ x("div", {
182
+ /* @__PURE__ */ _("div", {
181
183
  className: "mt-6",
182
- children: [/* @__PURE__ */ x("div", {
183
- className: "mb-2 flex items-center justify-between",
184
- children: [/* @__PURE__ */ b("p", {
185
- className: "text-sm font-medium text-text-primary",
186
- children: "Pieces in this look"
187
- }), /* @__PURE__ */ x("button", {
184
+ children: [/* @__PURE__ */ _("div", {
185
+ className: "mb-3 flex flex-col gap-3 sm:flex-row sm:items-end",
186
+ children: [/* @__PURE__ */ g("div", {
187
+ className: "flex-1",
188
+ children: /* @__PURE__ */ g(l, {
189
+ id: "add-style-item",
190
+ label: "Pieces in this look",
191
+ value: W,
192
+ onChange: G,
193
+ options: Y,
194
+ placeholder: T.isLoading ? "Loading catalogue…" : Y.length === 0 ? "All available pieces are already added" : "Select a catalogue piece"
195
+ })
196
+ }), /* @__PURE__ */ _("button", {
188
197
  type: "button",
189
- onClick: se,
190
- className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
191
- children: [/* @__PURE__ */ b(g, { className: "h-3.5 w-3.5" }), " Add piece"]
198
+ disabled: !W,
199
+ onClick: () => {
200
+ de(W), G("");
201
+ },
202
+ className: "inline-flex items-center justify-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 disabled:cursor-not-allowed disabled:opacity-50",
203
+ children: [/* @__PURE__ */ g(m, { className: "h-4 w-4" }), " Add piece"]
192
204
  })]
193
- }), q.length === 0 ? /* @__PURE__ */ b("p", {
205
+ }), H.length === 0 ? /* @__PURE__ */ g("p", {
194
206
  className: "rounded-lg border border-dashed border-border-seam p-4 text-center text-xs text-text-secondary",
195
207
  children: "No pieces added yet. A look can be published without pieces and styled later."
196
- }) : /* @__PURE__ */ b("div", {
208
+ }) : /* @__PURE__ */ g("div", {
197
209
  className: "space-y-3",
198
- children: q.map((e, n) => /* @__PURE__ */ b("div", {
210
+ children: H.map((e, n) => /* @__PURE__ */ g("div", {
199
211
  className: "rounded-lg border border-border-seam bg-bg-surface p-3",
200
- children: /* @__PURE__ */ x("div", {
212
+ children: /* @__PURE__ */ _("div", {
201
213
  className: "flex items-start gap-2",
202
- children: [/* @__PURE__ */ x("div", {
214
+ children: [/* @__PURE__ */ _("div", {
203
215
  className: "flex-1 space-y-2",
204
- children: [/* @__PURE__ */ b(l, {
216
+ children: [/* @__PURE__ */ g(l, {
205
217
  label: `Piece ${n + 1}`,
206
218
  value: e.styleItemId,
207
219
  onChange: (e) => Q(n, { styleItemId: e }),
208
- options: ie,
209
- placeholder: k.isLoading ? "Loading items…" : "Select a style item"
210
- }), /* @__PURE__ */ b(d, {
220
+ options: J,
221
+ placeholder: T.isLoading ? "Loading items…" : "Select a style item"
222
+ }), /* @__PURE__ */ g(u, {
211
223
  label: "Styling note",
212
224
  value: e.styling,
213
225
  onChange: (e) => Q(n, { styling: e }),
214
226
  placeholder: "Cuff the sleeves, tuck the front…"
215
227
  })]
216
- }), /* @__PURE__ */ b("button", {
228
+ }), /* @__PURE__ */ g("button", {
217
229
  type: "button",
218
- onClick: () => ce(n),
230
+ onClick: () => fe(n),
219
231
  "aria-label": "Remove piece",
220
232
  className: t("mt-7 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg", "text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text"),
221
- children: /* @__PURE__ */ b(y, { className: "h-4 w-4" })
233
+ children: /* @__PURE__ */ g(se, { className: "h-4 w-4" })
222
234
  })]
223
235
  })
224
236
  }, n))
225
237
  })]
226
238
  }),
227
- D.isError && /* @__PURE__ */ b("p", {
239
+ C.isError && /* @__PURE__ */ g("p", {
228
240
  className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
229
- children: D.error instanceof Error ? D.error.message : "Something went wrong"
241
+ children: C.error instanceof Error ? C.error.message : "Something went wrong"
230
242
  }),
231
- /* @__PURE__ */ b("div", {
243
+ /* @__PURE__ */ g("div", {
232
244
  className: "mt-6 flex justify-end gap-2",
233
- children: /* @__PURE__ */ x("button", {
245
+ children: /* @__PURE__ */ _("button", {
234
246
  type: "submit",
235
247
  disabled: $,
236
248
  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",
237
- children: [$ ? /* @__PURE__ */ b(re, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ b(_, { className: "h-4 w-4" }), "Create look"]
249
+ children: [$ ? /* @__PURE__ */ g(ae, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(h, { className: "h-4 w-4" }), "Create look"]
238
250
  })
239
251
  })
240
252
  ]
@@ -243,6 +255,6 @@ function T() {
243
255
  });
244
256
  }
245
257
  //#endregion
246
- export { T as LookFormPage };
258
+ export { x as LookFormPage };
247
259
 
248
260
  //# sourceMappingURL=LookFormPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LookFormPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/looks/LookFormPage.tsx"],"sourcesContent":["/**\n * Look Create Page\n * @module coolandlovely/pages/looks\n *\n * Drives `cooCreateLook`. A look is created with its title/occasion/visibility\n * and an inline list of look items (catalogue pieces + styling notes), matching\n * the `CooCreateLookInput` shape exactly.\n */\n\nimport { useMemo, useState } from 'react';\nimport { ArrowLeft, Loader2, Save, Sparkles, Plus, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { TextField, TextAreaField, SelectField, ToggleField } from '../../components/Field';\nimport { ImageUploadField } from '../../components/ImageUploadField';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useCreateLook, useUpdateLook, useStyleItems, useCollaborations } from '../../hooks';\nimport { COO_FILE_FOLDERS } from '../../hooks/useFiles';\nimport { parseCsv, titleCase, formatPrice } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooLookOccasion, CooVisibility, CooLookItemInput } from '../../types';\n\nconst OCCASIONS: CooLookOccasion[] = [\n 'EVERYDAY',\n 'WORK',\n 'EVENING',\n 'WEDDING_GUEST',\n 'VACATION',\n 'PARTY',\n 'ACTIVE',\n 'OTHER',\n];\n\nconst VISIBILITIES: CooVisibility[] = ['PUBLIC', 'FOLLOWERS', 'PRIVATE'];\n\ninterface DraftItem {\n styleItemId: string;\n styling: string;\n}\n\nexport function LookFormPage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n const createLook = useCreateLook();\n const updateLook = useUpdateLook();\n const styleItems = useStyleItems({\n status: 'ACTIVE',\n pagination: { limit: 100, offset: 0 },\n });\n // The caller's own collaborations — a look can be attributed to one.\n const collaborations = useCollaborations(\n currentUser?.id ? { creatorId: currentUser.id } : {}\n );\n\n const [title, setTitle] = useState('');\n const [caption, setCaption] = useState('');\n const [occasion, setOccasion] = useState<CooLookOccasion>('EVERYDAY');\n const [visibility, setVisibility] = useState<CooVisibility>('PUBLIC');\n const [tags, setTags] = useState('');\n const [imageFileId, setImageFileId] = useState('');\n const [collaborationId, setCollaborationId] = useState('');\n const [isFeatured, setIsFeatured] = useState(false);\n const [draftItems, setDraftItems] = useState<DraftItem[]>([]);\n const [titleError, setTitleError] = useState<string | undefined>();\n\n const itemOptions = useMemo(\n () =>\n (styleItems.data?.items ?? []).map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''} — ${formatPrice(i.price, i.currency)}`,\n })),\n [styleItems.data]\n );\n\n const collaborationOptions = useMemo(\n () => (collaborations.data ?? []).map((c) => ({ value: c.id, label: c.title })),\n [collaborations.data]\n );\n\n const occasionOptions = OCCASIONS.map((o) => ({ value: o, label: titleCase(o) }));\n const visibilityOptions = VISIBILITIES.map((v) => ({ value: v, label: titleCase(v) }));\n\n const addDraftItem = () => setDraftItems((d) => [...d, { styleItemId: '', styling: '' }]);\n const removeDraftItem = (idx: number) => setDraftItems((d) => d.filter((_, i) => i !== idx));\n const setDraftItem = (idx: number, patch: Partial<DraftItem>) =>\n setDraftItems((d) => d.map((it, i) => (i === idx ? { ...it, ...patch } : it)));\n\n const submitting = createLook.isPending || updateLook.isPending;\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!title.trim()) {\n setTitleError('Title is required');\n return;\n }\n setTitleError(undefined);\n\n const items: CooLookItemInput[] = draftItems\n .filter((d) => d.styleItemId)\n .map((d, idx) => ({\n styleItemId: d.styleItemId,\n position: idx,\n styling: d.styling.trim() || undefined,\n }));\n\n createLook.mutate(\n {\n title: title.trim(),\n caption: caption.trim() || undefined,\n occasion,\n visibility,\n collaborationId: collaborationId || undefined,\n imageFileId: imageFileId || undefined,\n tags: parseCsv(tags),\n items: items.length > 0 ? items : undefined,\n },\n {\n onSuccess: (look) => {\n // isFeatured is only settable via update — feature right after create.\n if (isFeatured) {\n updateLook.mutate(\n { id: look.id, input: { isFeatured: true } },\n { onSuccess: () => navigateTo(`/looks/${look.id}`) }\n );\n } else {\n navigateTo(`/looks/${look.id}`);\n }\n },\n }\n );\n };\n\n return (\n <PageLayout\n title=\"New look\"\n description=\"Compose a shop-the-look outfit from catalogue pieces\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\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\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text\">\n <Sparkles className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Look details</h2>\n <p className=\"text-xs text-text-secondary\">\n Created as a draft — publish it from the look page.\n </p>\n </div>\n </div>\n\n <TextField\n id=\"title\"\n label=\"Title\"\n value={title}\n onChange={setTitle}\n placeholder=\"Coastal grandma, refined\"\n required\n error={titleError}\n />\n\n <TextAreaField\n id=\"caption\"\n label=\"Caption\"\n value={caption}\n onChange={setCaption}\n placeholder=\"How to style it…\"\n rows={3}\n className=\"mt-4\"\n />\n\n <ImageUploadField\n label=\"Look photo\"\n aspect=\"wide\"\n value={imageFileId}\n onChange={setImageFileId}\n folder={COO_FILE_FOLDERS.looks}\n hint=\"The styled outfit shot\"\n className=\"mt-4\"\n />\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <SelectField\n id=\"occasion\"\n label=\"Occasion\"\n value={occasion}\n onChange={(v) => setOccasion(v as CooLookOccasion)}\n options={occasionOptions}\n />\n <SelectField\n id=\"visibility\"\n label=\"Visibility\"\n value={visibility}\n onChange={(v) => setVisibility(v as CooVisibility)}\n options={visibilityOptions}\n />\n </div>\n\n <SelectField\n id=\"collaborationId\"\n label=\"Collaboration (optional)\"\n value={collaborationId}\n onChange={setCollaborationId}\n options={collaborationOptions}\n placeholder={\n collaborations.isLoading\n ? 'Loading collaborations…'\n : collaborationOptions.length === 0\n ? 'No collaborations yet'\n : 'Not part of a collaboration'\n }\n hint=\"Attribute this look to one of your collaborations\"\n className=\"mt-4\"\n />\n\n <TextField\n id=\"tags\"\n label=\"Tags\"\n value={tags}\n onChange={setTags}\n placeholder=\"linen, neutral\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n\n <ToggleField\n id=\"isFeatured\"\n label=\"Feature this look\"\n checked={isFeatured}\n onChange={setIsFeatured}\n hint=\"Featured looks surface in the Featured filter and carry a star badge.\"\n className=\"mt-4\"\n />\n\n {/* Look items */}\n <div className=\"mt-6\">\n <div className=\"mb-2 flex items-center justify-between\">\n <p className=\"text-sm font-medium text-text-primary\">Pieces in this look</p>\n <button\n type=\"button\"\n onClick={addDraftItem}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n <Plus className=\"h-3.5 w-3.5\" /> Add piece\n </button>\n </div>\n {draftItems.length === 0 ? (\n <p className=\"rounded-lg border border-dashed border-border-seam p-4 text-center text-xs text-text-secondary\">\n No pieces added yet. A look can be published without pieces and styled later.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {draftItems.map((di, idx) => (\n <div key={idx} className=\"rounded-lg border border-border-seam bg-bg-surface p-3\">\n <div className=\"flex items-start gap-2\">\n <div className=\"flex-1 space-y-2\">\n <SelectField\n label={`Piece ${idx + 1}`}\n value={di.styleItemId}\n onChange={(v) => setDraftItem(idx, { styleItemId: v })}\n options={itemOptions}\n placeholder={\n styleItems.isLoading ? 'Loading items…' : 'Select a style item'\n }\n />\n <TextField\n label=\"Styling note\"\n value={di.styling}\n onChange={(v) => setDraftItem(idx, { styling: v })}\n placeholder=\"Cuff the sleeves, tuck the front…\"\n />\n </div>\n <button\n type=\"button\"\n onClick={() => removeDraftItem(idx)}\n aria-label=\"Remove piece\"\n className={cn(\n 'mt-7 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg',\n 'text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text'\n )}\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {createLook.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {createLook.error instanceof Error\n ? createLook.error.message\n : 'Something went wrong'}\n </p>\n )}\n\n <div className=\"mt-6 flex justify-end gap-2\">\n <button\n type=\"submit\"\n disabled={submitting}\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 {submitting ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Create look\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,IAAM,IAA+B;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAgC;CAAC;CAAU;CAAa;CAAU;AAOxE,SAAgB,IAAe;CAC7B,IAAM,EAAE,eAAY,mBAAgB,GAAkB,EAChD,IAAa,GAAe,EAC5B,IAAa,GAAe,EAC5B,IAAa,EAAc;EAC/B,QAAQ;EACR,YAAY;GAAE,OAAO;GAAK,QAAQ;GAAG;EACtC,CAAC,EAEI,IAAiB,EACrB,GAAa,KAAK,EAAE,WAAW,EAAY,IAAI,GAAG,EAAE,CACrD,EAEK,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAU,KAAe,EAA0B,WAAW,EAC/D,CAAC,GAAY,KAAiB,EAAwB,SAAS,EAC/D,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAiB,KAAsB,EAAS,GAAG,EACpD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAY,KAAiB,EAAsB,EAAE,CAAC,EACvD,CAAC,GAAY,KAAiB,GAA8B,EAE5D,KAAc,SAEf,EAAW,MAAM,SAAS,EAAE,EAAE,KAAK,OAAO;EACzC,OAAO,EAAE;EACT,OAAO,GAAG,EAAE,OAAO,EAAE,OAAO,OAAO,MAAM,EAAE,MAAM,SAAS,GAAG,KAAK,GAAY,EAAE,OAAO,EAAE,SAAS;EACnG,EAAE,EACL,CAAC,EAAW,KAAK,CAClB,EAEK,IAAuB,SACpB,EAAe,QAAQ,EAAE,EAAE,KAAK,OAAO;EAAE,OAAO,EAAE;EAAI,OAAO,EAAE;EAAO,EAAE,EAC/E,CAAC,EAAe,KAAK,CACtB,EAEK,KAAkB,EAAU,KAAK,OAAO;EAAE,OAAO;EAAG,OAAO,EAAU,EAAE;EAAE,EAAE,EAC3E,KAAoB,EAAa,KAAK,OAAO;EAAE,OAAO;EAAG,OAAO,EAAU,EAAE;EAAE,EAAE,EAEhF,WAAqB,GAAe,MAAM,CAAC,GAAG,GAAG;EAAE,aAAa;EAAI,SAAS;EAAI,CAAC,CAAC,EACnF,MAAmB,MAAgB,GAAe,MAAM,EAAE,QAAQ,GAAG,MAAM,MAAM,EAAI,CAAC,EACtF,KAAgB,GAAa,MACjC,GAAe,MAAM,EAAE,KAAK,GAAI,MAAO,MAAM,IAAM;EAAE,GAAG;EAAI,GAAG;EAAO,GAAG,EAAI,CAAC,EAE1E,IAAa,EAAW,aAAa,EAAW;AA6CtD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,SAAS;GACnC,WAAU;aAHZ,CAKE,kBAAC,IAAD,EAAW,WAAU,WAAY,CAAA,EAAA,UAC1B;;YAGX,kBAAC,QAAD;GAAM,WAzDY,MAAuB;AAE3C,QADA,EAAE,gBAAgB,EACd,CAAC,EAAM,MAAM,EAAE;AACjB,OAAc,oBAAoB;AAClC;;AAEF,MAAc,KAAA,EAAU;IAExB,IAAM,IAA4B,EAC/B,QAAQ,MAAM,EAAE,YAAY,CAC5B,KAAK,GAAG,OAAS;KAChB,aAAa,EAAE;KACf,UAAU;KACV,SAAS,EAAE,QAAQ,MAAM,IAAI,KAAA;KAC9B,EAAE;AAEL,MAAW,OACT;KACE,OAAO,EAAM,MAAM;KACnB,SAAS,EAAQ,MAAM,IAAI,KAAA;KAC3B;KACA;KACA,iBAAiB,KAAmB,KAAA;KACpC,aAAa,KAAe,KAAA;KAC5B,MAAM,GAAS,EAAK;KACpB,OAAO,EAAM,SAAS,IAAI,IAAQ,KAAA;KACnC,EACD,EACE,YAAY,MAAS;AAEnB,KAAI,IACF,EAAW,OACT;MAAE,IAAI,EAAK;MAAI,OAAO,EAAE,YAAY,IAAM;MAAE,EAC5C,EAAE,iBAAiB,EAAW,UAAU,EAAK,KAAK,EAAE,CACrD,GAED,EAAW,UAAU,EAAK,KAAK;OAGpC,CACF;;GAiB+B,WAAU;aACtC,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OAC5B,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA4C;OAAiB,CAAA,EAC3E,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAEvC,CAAA,CACA,EAAA,CAAA,CACF;;KAEN,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO;MACP,UAAU;MACV,aAAY;MACZ,UAAA;MACA,OAAO;MACP,CAAA;KAEF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO;MACP,UAAU;MACV,aAAY;MACZ,MAAM;MACN,WAAU;MACV,CAAA;KAEF,kBAAC,GAAD;MACE,OAAM;MACN,QAAO;MACP,OAAO;MACP,UAAU;MACV,QAAQ,EAAiB;MACzB,MAAK;MACL,WAAU;MACV,CAAA;KAEF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO;OACP,WAAW,MAAM,EAAY,EAAqB;OAClD,SAAS;OACT,CAAA,EACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO;OACP,WAAW,MAAM,EAAc,EAAmB;OAClD,SAAS;OACT,CAAA,CACE;;KAEN,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO;MACP,UAAU;MACV,SAAS;MACT,aACE,EAAe,YACX,4BACA,EAAqB,WAAW,IAC9B,0BACA;MAER,MAAK;MACL,WAAU;MACV,CAAA;KAEF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO;MACP,UAAU;MACV,aAAY;MACZ,MAAK;MACL,WAAU;MACV,CAAA;KAEF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,SAAS;MACT,UAAU;MACV,MAAK;MACL,WAAU;MACV,CAAA;KAGF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAwC;QAAuB,CAAA,EAC5E,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,aACzB;UACL;UACL,EAAW,WAAW,IACrB,kBAAC,KAAD;OAAG,WAAU;iBAAiG;OAE1G,CAAA,GAEJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAW,KAAK,GAAI,MACnB,kBAAC,OAAD;QAAe,WAAU;kBACvB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WACE,OAAO,SAAS,IAAM;WACtB,OAAO,EAAG;WACV,WAAW,MAAM,EAAa,GAAK,EAAE,aAAa,GAAG,CAAC;WACtD,SAAS;WACT,aACE,EAAW,YAAY,mBAAmB;WAE5C,CAAA,EACF,kBAAC,GAAD;WACE,OAAM;WACN,OAAO,EAAG;WACV,WAAW,MAAM,EAAa,GAAK,EAAE,SAAS,GAAG,CAAC;WAClD,aAAY;WACZ,CAAA,CACE;aACN,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,GAAgB,EAAI;UACnC,cAAW;UACX,WAAW,EACT,4EACA,qGACD;oBAED,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;UAClB,CAAA,CACL;;QACF,EA/BI,EA+BJ,CACN;OACE,CAAA,CAEJ;;KAEL,EAAW,WACV,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAW,iBAAiB,QACzB,EAAW,MAAM,UACjB;MACF,CAAA;KAGN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OACE,MAAK;OACL,UAAU;OACV,WAAU;iBAHZ,CAKG,IACC,kBAAC,IAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC5B,cAEK;;MACL,CAAA;KACI;;GACP,CAAA;EACI,CAAA"}
1
+ {"version":3,"file":"LookFormPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/looks/LookFormPage.tsx"],"sourcesContent":["/**\n * Look Create Page\n * @module coolandlovely/pages/looks\n *\n * Drives `cooCreateLook`. A look is created with its title/occasion/visibility\n * and an inline list of look items (catalogue pieces + styling notes), matching\n * the `CooCreateLookInput` shape exactly.\n */\n\nimport { useMemo, useState } from 'react';\nimport { ArrowLeft, Loader2, Plus, Save, Sparkles, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { TextField, TextAreaField, SelectField, ToggleField } from '../../components/Field';\nimport { ImageUploadField } from '../../components/ImageUploadField';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useCreateLook, useUpdateLook, useStyleItems, useCollaborations } from '../../hooks';\nimport { COO_FILE_FOLDERS } from '../../hooks/useFiles';\nimport { parseCsv, titleCase, formatPrice } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooLookOccasion, CooVisibility, CooLookItemInput } from '../../types';\n\nconst OCCASIONS: CooLookOccasion[] = [\n 'EVERYDAY',\n 'WORK',\n 'EVENING',\n 'WEDDING_GUEST',\n 'VACATION',\n 'PARTY',\n 'ACTIVE',\n 'OTHER',\n];\n\nconst VISIBILITIES: CooVisibility[] = ['PUBLIC', 'FOLLOWERS', 'PRIVATE'];\n\ninterface DraftItem {\n styleItemId: string;\n styling: string;\n}\n\nexport function LookFormPage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n const createLook = useCreateLook();\n const updateLook = useUpdateLook();\n const styleItems = useStyleItems({ pagination: { limit: 100, offset: 0 } });\n // The caller's own collaborations — a look can be attributed to one.\n const collaborations = useCollaborations(currentUser?.id ? { creatorId: currentUser.id } : {});\n\n const [title, setTitle] = useState('');\n const [caption, setCaption] = useState('');\n const [occasion, setOccasion] = useState<CooLookOccasion>('EVERYDAY');\n const [visibility, setVisibility] = useState<CooVisibility>('PUBLIC');\n const [tags, setTags] = useState('');\n const [imageFileId, setImageFileId] = useState('');\n const [collaborationId, setCollaborationId] = useState('');\n const [isFeatured, setIsFeatured] = useState(false);\n const [draftItems, setDraftItems] = useState<DraftItem[]>([]);\n const [addValue, setAddValue] = useState('');\n const [titleError, setTitleError] = useState<string | undefined>();\n\n const usableItems = useMemo(\n () => (styleItems.data?.items ?? []).filter((i) => i.status !== 'RETIRED'),\n [styleItems.data]\n );\n\n const itemOptions = useMemo(\n () =>\n usableItems.map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''} — ${formatPrice(i.price, i.currency)}`,\n })),\n [usableItems]\n );\n\n // Pieces not yet added — the right-rail \"add\" picker only offers these.\n const availableOptions = useMemo(() => {\n const chosen = new Set(draftItems.map((d) => d.styleItemId));\n return itemOptions.filter((o) => !chosen.has(o.value));\n }, [itemOptions, draftItems]);\n\n const collaborationOptions = useMemo(\n () => (collaborations.data ?? []).map((c) => ({ value: c.id, label: c.title })),\n [collaborations.data]\n );\n\n const occasionOptions = OCCASIONS.map((o) => ({ value: o, label: titleCase(o) }));\n const visibilityOptions = VISIBILITIES.map((v) => ({ value: v, label: titleCase(v) }));\n\n const addDraftItem = (styleItemId: string) => {\n if (!styleItemId) return;\n setDraftItems((d) =>\n d.some((it) => it.styleItemId === styleItemId) ? d : [...d, { styleItemId, styling: '' }]\n );\n };\n const removeDraftItem = (idx: number) => setDraftItems((d) => d.filter((_, i) => i !== idx));\n const setDraftItem = (idx: number, patch: Partial<DraftItem>) =>\n setDraftItems((d) => d.map((it, i) => (i === idx ? { ...it, ...patch } : it)));\n\n const submitting = createLook.isPending || updateLook.isPending;\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!title.trim()) {\n setTitleError('Title is required');\n return;\n }\n setTitleError(undefined);\n\n const items: CooLookItemInput[] = draftItems\n .filter((d) => d.styleItemId)\n .map((d, idx) => ({\n styleItemId: d.styleItemId,\n position: idx,\n styling: d.styling.trim() || undefined,\n }));\n\n createLook.mutate(\n {\n title: title.trim(),\n caption: caption.trim() || undefined,\n occasion,\n visibility,\n collaborationId: collaborationId || undefined,\n imageFileId: imageFileId || undefined,\n tags: parseCsv(tags),\n items: items.length > 0 ? items : undefined,\n },\n {\n onSuccess: (look) => {\n // isFeatured is only settable via update — feature right after create.\n if (isFeatured) {\n updateLook.mutate(\n { id: look.id, input: { isFeatured: true } },\n { onSuccess: () => navigateTo(`/looks/${look.id}`) }\n );\n } else {\n navigateTo(`/looks/${look.id}`);\n }\n },\n }\n );\n };\n\n return (\n <PageLayout\n title=\"New look\"\n description=\"Compose a shop-the-look outfit from catalogue pieces\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\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\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text\">\n <Sparkles className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Look details</h2>\n <p className=\"text-xs text-text-secondary\">\n Created as a draft — publish it from the look page.\n </p>\n </div>\n </div>\n\n <TextField\n id=\"title\"\n label=\"Title\"\n value={title}\n onChange={setTitle}\n placeholder=\"Coastal grandma, refined\"\n required\n error={titleError}\n />\n\n <TextAreaField\n id=\"caption\"\n label=\"Caption\"\n value={caption}\n onChange={setCaption}\n placeholder=\"How to style it…\"\n rows={3}\n className=\"mt-4\"\n />\n\n <ImageUploadField\n label=\"Look photo\"\n aspect=\"wide\"\n value={imageFileId}\n onChange={setImageFileId}\n folder={COO_FILE_FOLDERS.looks}\n hint=\"The styled outfit shot\"\n className=\"mt-4\"\n />\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <SelectField\n id=\"occasion\"\n label=\"Occasion\"\n value={occasion}\n onChange={(v) => setOccasion(v as CooLookOccasion)}\n options={occasionOptions}\n />\n <SelectField\n id=\"visibility\"\n label=\"Visibility\"\n value={visibility}\n onChange={(v) => setVisibility(v as CooVisibility)}\n options={visibilityOptions}\n />\n </div>\n\n <SelectField\n id=\"collaborationId\"\n label=\"Collaboration (optional)\"\n value={collaborationId}\n onChange={setCollaborationId}\n options={collaborationOptions}\n placeholder={\n collaborations.isLoading\n ? 'Loading collaborations…'\n : collaborationOptions.length === 0\n ? 'No collaborations yet'\n : 'Not part of a collaboration'\n }\n hint=\"Attribute this look to one of your collaborations\"\n className=\"mt-4\"\n />\n\n <TextField\n id=\"tags\"\n label=\"Tags\"\n value={tags}\n onChange={setTags}\n placeholder=\"linen, neutral\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n\n <ToggleField\n id=\"isFeatured\"\n label=\"Feature this look\"\n checked={isFeatured}\n onChange={setIsFeatured}\n hint=\"Featured looks surface in the Featured filter and carry a star badge.\"\n className=\"mt-4\"\n />\n\n {/* Look items */}\n <div className=\"mt-6\">\n <div className=\"mb-3 flex flex-col gap-3 sm:flex-row sm:items-end\">\n <div className=\"flex-1\">\n <SelectField\n id=\"add-style-item\"\n label=\"Pieces in this look\"\n value={addValue}\n onChange={setAddValue}\n options={availableOptions}\n placeholder={\n styleItems.isLoading\n ? 'Loading catalogue…'\n : availableOptions.length === 0\n ? 'All available pieces are already added'\n : 'Select a catalogue piece'\n }\n />\n </div>\n <button\n type=\"button\"\n disabled={!addValue}\n onClick={() => {\n addDraftItem(addValue);\n setAddValue('');\n }}\n className=\"inline-flex items-center justify-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 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Plus className=\"h-4 w-4\" /> Add piece\n </button>\n </div>\n {draftItems.length === 0 ? (\n <p className=\"rounded-lg border border-dashed border-border-seam p-4 text-center text-xs text-text-secondary\">\n No pieces added yet. A look can be published without pieces and styled later.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {draftItems.map((di, idx) => (\n <div key={idx} className=\"rounded-lg border border-border-seam bg-bg-surface p-3\">\n <div className=\"flex items-start gap-2\">\n <div className=\"flex-1 space-y-2\">\n <SelectField\n label={`Piece ${idx + 1}`}\n value={di.styleItemId}\n onChange={(v) => setDraftItem(idx, { styleItemId: v })}\n options={itemOptions}\n placeholder={\n styleItems.isLoading ? 'Loading items…' : 'Select a style item'\n }\n />\n <TextField\n label=\"Styling note\"\n value={di.styling}\n onChange={(v) => setDraftItem(idx, { styling: v })}\n placeholder=\"Cuff the sleeves, tuck the front…\"\n />\n </div>\n <button\n type=\"button\"\n onClick={() => removeDraftItem(idx)}\n aria-label=\"Remove piece\"\n className={cn(\n 'mt-7 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg',\n 'text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text'\n )}\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {createLook.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {createLook.error instanceof Error\n ? createLook.error.message\n : 'Something went wrong'}\n </p>\n )}\n\n <div className=\"mt-6 flex justify-end gap-2\">\n <button\n type=\"submit\"\n disabled={submitting}\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 {submitting ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Create look\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}