@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.
- package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
- package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoutes.js +14 -6
- package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
- package/dist/coolandlovely/components/ActorName.js +29 -0
- package/dist/coolandlovely/components/ActorName.js.map +1 -0
- package/dist/coolandlovely/components/AddToLookbookButton.js +81 -0
- package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -0
- package/dist/coolandlovely/components/BrandCover.js +39 -0
- package/dist/coolandlovely/components/BrandCover.js.map +1 -0
- package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
- package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
- package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
- package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
- package/dist/coolandlovely/components/Comments.js +109 -0
- package/dist/coolandlovely/components/Comments.js.map +1 -0
- package/dist/coolandlovely/components/Field.js +22 -2
- package/dist/coolandlovely/components/Field.js.map +1 -1
- package/dist/coolandlovely/components/FileImage.js +17 -0
- package/dist/coolandlovely/components/FileImage.js.map +1 -0
- package/dist/coolandlovely/components/FollowButton.js +24 -0
- package/dist/coolandlovely/components/FollowButton.js.map +1 -0
- package/dist/coolandlovely/components/ImageUploadField.js +153 -0
- package/dist/coolandlovely/components/ImageUploadField.js.map +1 -0
- package/dist/coolandlovely/components/LookCard.js +77 -0
- package/dist/coolandlovely/components/LookCard.js.map +1 -0
- package/dist/coolandlovely/components/MetricsCard.js +1 -1
- package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
- package/dist/coolandlovely/components/ShopItemCard.js +45 -0
- package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
- package/dist/coolandlovely/components/StepTracker.js +35 -0
- package/dist/coolandlovely/components/StepTracker.js.map +1 -0
- package/dist/coolandlovely/components/Thumbnail.js +25 -14
- package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
- package/dist/coolandlovely/components/VisibilityChip.js +32 -0
- package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
- package/dist/coolandlovely/hooks/index.js +2 -0
- package/dist/coolandlovely/hooks/useComments.js +68 -0
- package/dist/coolandlovely/hooks/useComments.js.map +1 -0
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +155 -60
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/hooks/useFiles.js +111 -0
- package/dist/coolandlovely/hooks/useFiles.js.map +1 -0
- package/dist/coolandlovely/pages/HomePage.js +130 -133
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +261 -228
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +146 -104
- package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -11
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +134 -107
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js +117 -102
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -54
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js +223 -0
- package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -0
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +176 -144
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +144 -130
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js +73 -62
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +215 -102
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +115 -86
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +217 -155
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookFormPage.js +147 -96
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -95
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +376 -146
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/pages/trends/TrendsPage.js +249 -0
- package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +136 -88
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -74
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
- 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 {
|
|
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 {
|
|
7
|
-
import {
|
|
8
|
-
import { useMemo as
|
|
9
|
-
import { ArrowLeft as
|
|
10
|
-
import { jsx as
|
|
11
|
-
import { GlassCard as
|
|
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
|
|
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
|
-
],
|
|
24
|
+
], b = [
|
|
23
25
|
"PUBLIC",
|
|
24
26
|
"FOLLOWERS",
|
|
25
27
|
"PRIVATE"
|
|
26
28
|
];
|
|
27
|
-
function
|
|
28
|
-
let { navigateTo:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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}` : ""} — ${
|
|
37
|
-
})), [
|
|
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:
|
|
40
|
-
})),
|
|
44
|
+
label: c(e)
|
|
45
|
+
})), ue = b.map((e) => ({
|
|
41
46
|
value: e,
|
|
42
|
-
label:
|
|
43
|
-
})),
|
|
44
|
-
styleItemId:
|
|
45
|
-
|
|
46
|
-
|
|
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__ */
|
|
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__ */
|
|
60
|
+
actions: /* @__PURE__ */ _("button", {
|
|
54
61
|
type: "button",
|
|
55
|
-
onClick: () =>
|
|
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__ */
|
|
64
|
+
children: [/* @__PURE__ */ g(ie, { className: "h-4 w-4" }), " Cancel"]
|
|
58
65
|
}),
|
|
59
|
-
children: /* @__PURE__ */
|
|
66
|
+
children: /* @__PURE__ */ g("form", {
|
|
60
67
|
onSubmit: (e) => {
|
|
61
68
|
if (e.preventDefault(), !D.trim()) {
|
|
62
|
-
|
|
69
|
+
K("Title is required");
|
|
63
70
|
return;
|
|
64
71
|
}
|
|
65
|
-
|
|
66
|
-
let 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
|
-
|
|
78
|
+
C.mutate({
|
|
72
79
|
title: D.trim(),
|
|
73
|
-
caption:
|
|
74
|
-
occasion:
|
|
75
|
-
visibility:
|
|
76
|
-
|
|
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) =>
|
|
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__ */
|
|
95
|
+
children: /* @__PURE__ */ _(v, {
|
|
82
96
|
treatment: "plain",
|
|
83
97
|
className: "p-6",
|
|
84
98
|
children: [
|
|
85
|
-
/* @__PURE__ */
|
|
99
|
+
/* @__PURE__ */ _("div", {
|
|
86
100
|
className: "mb-6 flex items-center gap-3",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
90
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
112
|
+
/* @__PURE__ */ g(u, {
|
|
99
113
|
id: "title",
|
|
100
114
|
label: "Title",
|
|
101
115
|
value: D,
|
|
102
|
-
onChange:
|
|
116
|
+
onChange: ce,
|
|
103
117
|
placeholder: "Coastal grandma, refined",
|
|
104
118
|
required: !0,
|
|
105
|
-
error:
|
|
119
|
+
error: le
|
|
106
120
|
}),
|
|
107
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ g(ne, {
|
|
108
122
|
id: "caption",
|
|
109
123
|
label: "Caption",
|
|
110
|
-
value:
|
|
111
|
-
onChange:
|
|
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__ */
|
|
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__ */
|
|
141
|
+
children: [/* @__PURE__ */ g(l, {
|
|
119
142
|
id: "occasion",
|
|
120
143
|
label: "Occasion",
|
|
121
|
-
value:
|
|
122
|
-
onChange: (e) =>
|
|
123
|
-
options:
|
|
124
|
-
}), /* @__PURE__ */
|
|
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:
|
|
128
|
-
onChange: (e) =>
|
|
129
|
-
options:
|
|
150
|
+
value: M,
|
|
151
|
+
onChange: (e) => N(e),
|
|
152
|
+
options: ue
|
|
130
153
|
})]
|
|
131
154
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
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:
|
|
136
|
-
onChange:
|
|
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__ */
|
|
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__ */
|
|
144
|
-
className: "mb-
|
|
145
|
-
children: [/* @__PURE__ */
|
|
146
|
-
className: "
|
|
147
|
-
children:
|
|
148
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
}),
|
|
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__ */
|
|
208
|
+
}) : /* @__PURE__ */ g("div", {
|
|
158
209
|
className: "space-y-3",
|
|
159
|
-
children:
|
|
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__ */
|
|
212
|
+
children: /* @__PURE__ */ _("div", {
|
|
162
213
|
className: "flex items-start gap-2",
|
|
163
|
-
children: [/* @__PURE__ */
|
|
214
|
+
children: [/* @__PURE__ */ _("div", {
|
|
164
215
|
className: "flex-1 space-y-2",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
216
|
+
children: [/* @__PURE__ */ g(l, {
|
|
166
217
|
label: `Piece ${n + 1}`,
|
|
167
218
|
value: e.styleItemId,
|
|
168
|
-
onChange: (e) =>
|
|
169
|
-
options:
|
|
170
|
-
placeholder:
|
|
171
|
-
}), /* @__PURE__ */
|
|
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) =>
|
|
225
|
+
onChange: (e) => Q(n, { styling: e }),
|
|
175
226
|
placeholder: "Cuff the sleeves, tuck the front…"
|
|
176
227
|
})]
|
|
177
|
-
}), /* @__PURE__ */
|
|
228
|
+
}), /* @__PURE__ */ g("button", {
|
|
178
229
|
type: "button",
|
|
179
|
-
onClick: () =>
|
|
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__ */
|
|
233
|
+
children: /* @__PURE__ */ g(se, { className: "h-4 w-4" })
|
|
183
234
|
})]
|
|
184
235
|
})
|
|
185
236
|
}, n))
|
|
186
237
|
})]
|
|
187
238
|
}),
|
|
188
|
-
|
|
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:
|
|
241
|
+
children: C.error instanceof Error ? C.error.message : "Something went wrong"
|
|
191
242
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
243
|
+
/* @__PURE__ */ g("div", {
|
|
193
244
|
className: "mt-6 flex justify-end gap-2",
|
|
194
|
-
children: /* @__PURE__ */
|
|
245
|
+
children: /* @__PURE__ */ _("button", {
|
|
195
246
|
type: "submit",
|
|
196
|
-
disabled:
|
|
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: [
|
|
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 {
|
|
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":";;;;;;;;;;;;AAoBA,IAAM,IAA+B;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAgC;CAAC;CAAU;CAAa;CAAU;AAOxE,SAAgB,IAAe;CAC7B,IAAM,EAAE,kBAAe,GAAkB,EACnC,IAAa,GAAe,EAC5B,IAAa,EAAc;EAC/B,QAAQ;EACR,YAAY;GAAE,OAAO;GAAK,QAAQ;GAAG;EACtC,CAAC,EAEI,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,GAAY,KAAiB,EAAsB,EAAE,CAAC,EACvD,CAAC,GAAY,KAAiB,GAA8B,EAE5D,IAAc,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,EAAY,EAAE,OAAO,EAAE,SAAS;EACnG,EAAE,EACL,CAAC,EAAW,KAAK,CAClB,EAEK,IAAkB,EAAU,KAAK,OAAO;EAAE,OAAO;EAAG,OAAO,EAAU,EAAE;EAAE,EAAE,EAC3E,IAAoB,EAAa,KAAK,OAAO;EAAE,OAAO;EAAG,OAAO,EAAU,EAAE;EAAE,EAAE,EAEhF,UAAqB,GAAe,MAAM,CAAC,GAAG,GAAG;EAAE,aAAa;EAAI,SAAS;EAAI,CAAC,CAAC,EACnF,KAAmB,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;AA+BhF,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,SAAS;GACnC,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,UAC1B;;YAGX,kBAAC,QAAD;GAAM,WA3CY,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,MAAM,EAAS,EAAK;KACpB,OAAO,EAAM,SAAS,IAAI,IAAQ,KAAA;KACnC,EACD,EAAE,YAAY,MAAS,EAAW,UAAU,EAAK,KAAK,EAAE,CACzD;;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,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,aAAY;MACZ,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,EAAgB,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,EAAW;OACrB,WAAU;iBAHZ,CAKG,EAAW,YACV,kBAAC,GAAD,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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"}
|