@burdenoff/microfe-coolandlovely 2026.706.1 → 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 (89) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  3. package/dist/coolandlovely/CoolAndLovelyRoutes.js +14 -6
  4. package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
  5. package/dist/coolandlovely/components/ActorName.js +29 -0
  6. package/dist/coolandlovely/components/ActorName.js.map +1 -0
  7. package/dist/coolandlovely/components/AddToLookbookButton.js +81 -0
  8. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -0
  9. package/dist/coolandlovely/components/BrandCover.js +39 -0
  10. package/dist/coolandlovely/components/BrandCover.js.map +1 -0
  11. package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
  12. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
  13. package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
  14. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
  15. package/dist/coolandlovely/components/Comments.js +109 -0
  16. package/dist/coolandlovely/components/Comments.js.map +1 -0
  17. package/dist/coolandlovely/components/Field.js +22 -2
  18. package/dist/coolandlovely/components/Field.js.map +1 -1
  19. package/dist/coolandlovely/components/FileImage.js +17 -0
  20. package/dist/coolandlovely/components/FileImage.js.map +1 -0
  21. package/dist/coolandlovely/components/FollowButton.js +24 -0
  22. package/dist/coolandlovely/components/FollowButton.js.map +1 -0
  23. package/dist/coolandlovely/components/ImageUploadField.js +153 -0
  24. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -0
  25. package/dist/coolandlovely/components/LookCard.js +77 -0
  26. package/dist/coolandlovely/components/LookCard.js.map +1 -0
  27. package/dist/coolandlovely/components/MetricsCard.js +1 -1
  28. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  29. package/dist/coolandlovely/components/ShopItemCard.js +45 -0
  30. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
  31. package/dist/coolandlovely/components/StepTracker.js +35 -0
  32. package/dist/coolandlovely/components/StepTracker.js.map +1 -0
  33. package/dist/coolandlovely/components/Thumbnail.js +25 -14
  34. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  35. package/dist/coolandlovely/components/VisibilityChip.js +32 -0
  36. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
  37. package/dist/coolandlovely/hooks/index.js +2 -0
  38. package/dist/coolandlovely/hooks/useComments.js +68 -0
  39. package/dist/coolandlovely/hooks/useComments.js.map +1 -0
  40. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +155 -60
  41. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  42. package/dist/coolandlovely/hooks/useFiles.js +111 -0
  43. package/dist/coolandlovely/hooks/useFiles.js.map +1 -0
  44. package/dist/coolandlovely/pages/HomePage.js +130 -133
  45. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  46. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +261 -228
  47. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  48. package/dist/coolandlovely/pages/brands/BrandFormPage.js +146 -104
  49. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  50. package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -11
  51. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  52. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
  53. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  54. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  55. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
  56. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +134 -107
  58. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  59. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +117 -102
  60. package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -1
  61. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -54
  62. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  63. package/dist/coolandlovely/pages/discover/DiscoverPage.js +223 -0
  64. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -0
  65. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +176 -144
  66. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  67. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +144 -130
  68. package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -1
  69. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +73 -62
  70. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  71. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +215 -102
  72. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  73. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +115 -86
  74. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  75. package/dist/coolandlovely/pages/looks/LookDetailPage.js +217 -155
  76. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  77. package/dist/coolandlovely/pages/looks/LookFormPage.js +147 -96
  78. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  79. package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -95
  80. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  81. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +376 -146
  82. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  83. package/dist/coolandlovely/pages/trends/TrendsPage.js +249 -0
  84. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -0
  85. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +136 -88
  86. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  87. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -74
  88. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  89. package/package.json +1 -1
@@ -1,16 +1,18 @@
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 { useCreateLook as r, useStyleItems as i } from "../../hooks/useCoolAndLovelyApi.js";
4
+ import { COO_FILE_FOLDERS as r } from "../../hooks/useFiles.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";
5
7
  import "../../hooks/index.js";
6
- import { formatPrice as a, parseCsv as o, titleCase as s } from "../../utils/formatters.js";
7
- import { SelectField as c, TextAreaField as l, TextField as u } from "../../components/Field.js";
8
- import { useMemo as d, useState as f } from "react";
9
- import { ArrowLeft as p, Loader2 as m, Plus as h, Save as g, Sparkles as _, X as v } from "lucide-react";
10
- import { jsx as y, jsxs as b } from "react/jsx-runtime";
11
- import { GlassCard as x } 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";
12
14
  //#region src/coolandlovely/pages/looks/LookFormPage.tsx
13
- var S = [
15
+ var y = [
14
16
  "EVERYDAY",
15
17
  "WORK",
16
18
  "EVENING",
@@ -19,183 +21,232 @@ var S = [
19
21
  "PARTY",
20
22
  "ACTIVE",
21
23
  "OTHER"
22
- ], C = [
24
+ ], b = [
23
25
  "PUBLIC",
24
26
  "FOLLOWERS",
25
27
  "PRIVATE"
26
28
  ];
27
- function w() {
28
- let { navigateTo: w } = e(), T = r(), E = i({
29
- status: "ACTIVE",
30
- pagination: {
31
- limit: 100,
32
- offset: 0
33
- }
34
- }), [D, O] = f(""), [k, A] = f(""), [j, M] = f("EVERYDAY"), [N, P] = f("PUBLIC"), [F, I] = f(""), [L, R] = f([]), [z, B] = f(), V = d(() => (E.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) => ({
35
34
  value: e.id,
36
- label: `${e.name}${e.brand?.name ? ` · ${e.brand.name}` : ""} — ${a(e.price, e.currency)}`
37
- })), [E.data]), H = S.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
+ value: e.id,
41
+ label: e.title
42
+ })), [E.data]), Z = y.map((e) => ({
38
43
  value: e,
39
- label: s(e)
40
- })), U = C.map((e) => ({
44
+ label: c(e)
45
+ })), ue = b.map((e) => ({
41
46
  value: e,
42
- label: s(e)
43
- })), W = () => R((e) => [...e, {
44
- styleItemId: "",
45
- styling: ""
46
- }]), G = (e) => R((t) => t.filter((t, n) => n !== e)), K = (e, t) => R((n) => n.map((n, r) => r === e ? {
47
+ label: c(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 ? {
47
54
  ...n,
48
55
  ...t
49
- } : n));
50
- return /* @__PURE__ */ y(n, {
56
+ } : n)), $ = C.isPending || w.isPending;
57
+ return /* @__PURE__ */ g(n, {
51
58
  title: "New look",
52
59
  description: "Compose a shop-the-look outfit from catalogue pieces",
53
- actions: /* @__PURE__ */ b("button", {
60
+ actions: /* @__PURE__ */ _("button", {
54
61
  type: "button",
55
- onClick: () => w("/looks"),
62
+ onClick: () => x("/looks"),
56
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",
57
- children: [/* @__PURE__ */ y(p, { className: "h-4 w-4" }), " Cancel"]
64
+ children: [/* @__PURE__ */ g(ie, { className: "h-4 w-4" }), " Cancel"]
58
65
  }),
59
- children: /* @__PURE__ */ y("form", {
66
+ children: /* @__PURE__ */ g("form", {
60
67
  onSubmit: (e) => {
61
68
  if (e.preventDefault(), !D.trim()) {
62
- B("Title is required");
69
+ K("Title is required");
63
70
  return;
64
71
  }
65
- B(void 0);
66
- let t = L.filter((e) => e.styleItemId).map((e, t) => ({
72
+ K(void 0);
73
+ let t = H.filter((e) => e.styleItemId).map((e, t) => ({
67
74
  styleItemId: e.styleItemId,
68
75
  position: t,
69
76
  styling: e.styling.trim() || void 0
70
77
  }));
71
- T.mutate({
78
+ C.mutate({
72
79
  title: D.trim(),
73
- caption: k.trim() || void 0,
74
- occasion: j,
75
- visibility: N,
76
- tags: o(F),
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),
77
86
  items: t.length > 0 ? t : void 0
78
- }, { onSuccess: (e) => w(`/looks/${e.id}`) });
87
+ }, { onSuccess: (e) => {
88
+ B ? w.mutate({
89
+ id: e.id,
90
+ input: { isFeatured: !0 }
91
+ }, { onSuccess: () => x(`/looks/${e.id}`) }) : x(`/looks/${e.id}`);
92
+ } });
79
93
  },
80
94
  className: "mx-auto max-w-2xl",
81
- children: /* @__PURE__ */ b(x, {
95
+ children: /* @__PURE__ */ _(v, {
82
96
  treatment: "plain",
83
97
  className: "p-6",
84
98
  children: [
85
- /* @__PURE__ */ b("div", {
99
+ /* @__PURE__ */ _("div", {
86
100
  className: "mb-6 flex items-center gap-3",
87
- children: [/* @__PURE__ */ y("div", {
101
+ children: [/* @__PURE__ */ g("div", {
88
102
  className: "flex h-11 w-11 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text",
89
- children: /* @__PURE__ */ y(_, { className: "h-5 w-5" })
90
- }), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h2", {
103
+ children: /* @__PURE__ */ g(oe, { className: "h-5 w-5" })
104
+ }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h2", {
91
105
  className: "text-base font-semibold text-text-primary",
92
106
  children: "Look details"
93
- }), /* @__PURE__ */ y("p", {
107
+ }), /* @__PURE__ */ g("p", {
94
108
  className: "text-xs text-text-secondary",
95
109
  children: "Created as a draft — publish it from the look page."
96
110
  })] })]
97
111
  }),
98
- /* @__PURE__ */ y(u, {
112
+ /* @__PURE__ */ g(u, {
99
113
  id: "title",
100
114
  label: "Title",
101
115
  value: D,
102
- onChange: O,
116
+ onChange: ce,
103
117
  placeholder: "Coastal grandma, refined",
104
118
  required: !0,
105
- error: z
119
+ error: le
106
120
  }),
107
- /* @__PURE__ */ y(l, {
121
+ /* @__PURE__ */ g(ne, {
108
122
  id: "caption",
109
123
  label: "Caption",
110
- value: k,
111
- onChange: A,
124
+ value: O,
125
+ onChange: k,
112
126
  placeholder: "How to style it…",
113
127
  rows: 3,
114
128
  className: "mt-4"
115
129
  }),
116
- /* @__PURE__ */ b("div", {
130
+ /* @__PURE__ */ g(d, {
131
+ label: "Look photo",
132
+ aspect: "wide",
133
+ value: I,
134
+ onChange: L,
135
+ folder: r.looks,
136
+ hint: "The styled outfit shot",
137
+ className: "mt-4"
138
+ }),
139
+ /* @__PURE__ */ _("div", {
117
140
  className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2",
118
- children: [/* @__PURE__ */ y(c, {
141
+ children: [/* @__PURE__ */ g(l, {
119
142
  id: "occasion",
120
143
  label: "Occasion",
121
- value: j,
122
- onChange: (e) => M(e),
123
- options: H
124
- }), /* @__PURE__ */ y(c, {
144
+ value: A,
145
+ onChange: (e) => j(e),
146
+ options: Z
147
+ }), /* @__PURE__ */ g(l, {
125
148
  id: "visibility",
126
149
  label: "Visibility",
127
- value: N,
128
- onChange: (e) => P(e),
129
- options: U
150
+ value: M,
151
+ onChange: (e) => N(e),
152
+ options: ue
130
153
  })]
131
154
  }),
132
- /* @__PURE__ */ y(u, {
155
+ /* @__PURE__ */ g(l, {
156
+ id: "collaborationId",
157
+ label: "Collaboration (optional)",
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",
162
+ hint: "Attribute this look to one of your collaborations",
163
+ className: "mt-4"
164
+ }),
165
+ /* @__PURE__ */ g(u, {
133
166
  id: "tags",
134
167
  label: "Tags",
135
- value: F,
136
- onChange: I,
168
+ value: P,
169
+ onChange: F,
137
170
  placeholder: "linen, neutral",
138
171
  hint: "Comma-separated",
139
172
  className: "mt-4"
140
173
  }),
141
- /* @__PURE__ */ b("div", {
174
+ /* @__PURE__ */ g(re, {
175
+ id: "isFeatured",
176
+ label: "Feature this look",
177
+ checked: B,
178
+ onChange: V,
179
+ hint: "Featured looks surface in the Featured filter and carry a star badge.",
180
+ className: "mt-4"
181
+ }),
182
+ /* @__PURE__ */ _("div", {
142
183
  className: "mt-6",
143
- children: [/* @__PURE__ */ b("div", {
144
- className: "mb-2 flex items-center justify-between",
145
- children: [/* @__PURE__ */ y("p", {
146
- className: "text-sm font-medium text-text-primary",
147
- children: "Pieces in this look"
148
- }), /* @__PURE__ */ b("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", {
149
197
  type: "button",
150
- onClick: W,
151
- className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
152
- children: [/* @__PURE__ */ y(h, { 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"]
153
204
  })]
154
- }), L.length === 0 ? /* @__PURE__ */ y("p", {
205
+ }), H.length === 0 ? /* @__PURE__ */ g("p", {
155
206
  className: "rounded-lg border border-dashed border-border-seam p-4 text-center text-xs text-text-secondary",
156
207
  children: "No pieces added yet. A look can be published without pieces and styled later."
157
- }) : /* @__PURE__ */ y("div", {
208
+ }) : /* @__PURE__ */ g("div", {
158
209
  className: "space-y-3",
159
- children: L.map((e, n) => /* @__PURE__ */ y("div", {
210
+ children: H.map((e, n) => /* @__PURE__ */ g("div", {
160
211
  className: "rounded-lg border border-border-seam bg-bg-surface p-3",
161
- children: /* @__PURE__ */ b("div", {
212
+ children: /* @__PURE__ */ _("div", {
162
213
  className: "flex items-start gap-2",
163
- children: [/* @__PURE__ */ b("div", {
214
+ children: [/* @__PURE__ */ _("div", {
164
215
  className: "flex-1 space-y-2",
165
- children: [/* @__PURE__ */ y(c, {
216
+ children: [/* @__PURE__ */ g(l, {
166
217
  label: `Piece ${n + 1}`,
167
218
  value: e.styleItemId,
168
- onChange: (e) => K(n, { styleItemId: e }),
169
- options: V,
170
- placeholder: E.isLoading ? "Loading items…" : "Select a style item"
171
- }), /* @__PURE__ */ y(u, {
219
+ onChange: (e) => Q(n, { styleItemId: e }),
220
+ options: J,
221
+ placeholder: T.isLoading ? "Loading items…" : "Select a style item"
222
+ }), /* @__PURE__ */ g(u, {
172
223
  label: "Styling note",
173
224
  value: e.styling,
174
- onChange: (e) => K(n, { styling: e }),
225
+ onChange: (e) => Q(n, { styling: e }),
175
226
  placeholder: "Cuff the sleeves, tuck the front…"
176
227
  })]
177
- }), /* @__PURE__ */ y("button", {
228
+ }), /* @__PURE__ */ g("button", {
178
229
  type: "button",
179
- onClick: () => G(n),
230
+ onClick: () => fe(n),
180
231
  "aria-label": "Remove piece",
181
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"),
182
- children: /* @__PURE__ */ y(v, { className: "h-4 w-4" })
233
+ children: /* @__PURE__ */ g(se, { className: "h-4 w-4" })
183
234
  })]
184
235
  })
185
236
  }, n))
186
237
  })]
187
238
  }),
188
- T.isError && /* @__PURE__ */ y("p", {
239
+ C.isError && /* @__PURE__ */ g("p", {
189
240
  className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
190
- children: T.error instanceof Error ? T.error.message : "Something went wrong"
241
+ children: C.error instanceof Error ? C.error.message : "Something went wrong"
191
242
  }),
192
- /* @__PURE__ */ y("div", {
243
+ /* @__PURE__ */ g("div", {
193
244
  className: "mt-6 flex justify-end gap-2",
194
- children: /* @__PURE__ */ b("button", {
245
+ children: /* @__PURE__ */ _("button", {
195
246
  type: "submit",
196
- disabled: T.isPending,
247
+ disabled: $,
197
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",
198
- children: [T.isPending ? /* @__PURE__ */ y(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ y(g, { 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"]
199
250
  })
200
251
  })
201
252
  ]
@@ -204,6 +255,6 @@ function w() {
204
255
  });
205
256
  }
206
257
  //#endregion
207
- export { w as LookFormPage };
258
+ export { x as LookFormPage };
208
259
 
209
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 } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useCreateLook, useStyleItems } from '../../hooks';\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 } = useCoolAndLovely();\n const createLook = useCreateLook();\n const styleItems = useStyleItems({\n status: 'ACTIVE',\n pagination: { limit: 100, offset: 0 },\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 [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 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 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 tags: parseCsv(tags),\n items: items.length > 0 ? items : undefined,\n },\n { onSuccess: (look) => navigateTo(`/looks/${look.id}`) }\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 <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 <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 {/* 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={createLook.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 {createLook.isPending ? (\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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"}
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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"}