@burdenoff/microfe-coolandlovely 2026.1101.8 → 2026.1102.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/coolandlovely/CoolAndLovelyRoot.d.ts.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoot.js +8 -0
- package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoutes.d.ts.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoutes.js +20 -16
- package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
- package/dist/coolandlovely/components/WardrobeItemCard.d.ts +2 -1
- package/dist/coolandlovely/components/WardrobeItemCard.d.ts.map +1 -1
- package/dist/coolandlovely/components/WardrobeItemCard.js +4 -3
- package/dist/coolandlovely/components/WardrobeItemCard.js.map +1 -1
- package/dist/coolandlovely/components/prices/BestPriceBadge.d.ts +7 -0
- package/dist/coolandlovely/components/prices/BestPriceBadge.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/BestPriceBadge.js +22 -0
- package/dist/coolandlovely/components/prices/BestPriceBadge.js.map +1 -0
- package/dist/coolandlovely/components/prices/LookbookPriceBoard.d.ts +10 -0
- package/dist/coolandlovely/components/prices/LookbookPriceBoard.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/LookbookPriceBoard.js +120 -0
- package/dist/coolandlovely/components/prices/LookbookPriceBoard.js.map +1 -0
- package/dist/coolandlovely/components/prices/PriceComparison.d.ts +5 -0
- package/dist/coolandlovely/components/prices/PriceComparison.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/PriceComparison.js +95 -0
- package/dist/coolandlovely/components/prices/PriceComparison.js.map +1 -0
- package/dist/coolandlovely/components/prices/PriceCurrencySelect.d.ts +6 -0
- package/dist/coolandlovely/components/prices/PriceCurrencySelect.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/PriceCurrencySelect.js +38 -0
- package/dist/coolandlovely/components/prices/PriceCurrencySelect.js.map +1 -0
- package/dist/coolandlovely/components/prices/PricePanel.d.ts +6 -0
- package/dist/coolandlovely/components/prices/PricePanel.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/PricePanel.js +105 -0
- package/dist/coolandlovely/components/prices/PricePanel.js.map +1 -0
- package/dist/coolandlovely/components/prices/PriceWatchDisclosure.d.ts +2 -0
- package/dist/coolandlovely/components/prices/PriceWatchDisclosure.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/PriceWatchDisclosure.js +28 -0
- package/dist/coolandlovely/components/prices/PriceWatchDisclosure.js.map +1 -0
- package/dist/coolandlovely/components/prices/PriceWatchForm.d.ts +5 -0
- package/dist/coolandlovely/components/prices/PriceWatchForm.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/PriceWatchForm.js +89 -0
- package/dist/coolandlovely/components/prices/PriceWatchForm.js.map +1 -0
- package/dist/coolandlovely/components/prices/PriceWatches.d.ts +3 -0
- package/dist/coolandlovely/components/prices/PriceWatches.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/PriceWatches.js +189 -0
- package/dist/coolandlovely/components/prices/PriceWatches.js.map +1 -0
- package/dist/coolandlovely/components/prices/RetailerOfferLink.d.ts +6 -0
- package/dist/coolandlovely/components/prices/RetailerOfferLink.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/RetailerOfferLink.js +28 -0
- package/dist/coolandlovely/components/prices/RetailerOfferLink.js.map +1 -0
- package/dist/coolandlovely/components/prices/money.d.ts +6 -0
- package/dist/coolandlovely/components/prices/money.d.ts.map +1 -0
- package/dist/coolandlovely/components/prices/money.js +42 -0
- package/dist/coolandlovely/components/prices/money.js.map +1 -0
- package/dist/coolandlovely/hooks/useContextNavigation.d.ts +3 -0
- package/dist/coolandlovely/hooks/useContextNavigation.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useContextNavigation.js +31 -0
- package/dist/coolandlovely/hooks/useContextNavigation.js.map +1 -0
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +1 -1
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/hooks/useCreatorMarketplace.d.ts +118 -0
- package/dist/coolandlovely/hooks/useCreatorMarketplace.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useCreatorMarketplace.js +120 -0
- package/dist/coolandlovely/hooks/useCreatorMarketplace.js.map +1 -0
- package/dist/coolandlovely/hooks/usePriceApi.d.ts +57 -0
- package/dist/coolandlovely/hooks/usePriceApi.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/usePriceApi.js +111 -0
- package/dist/coolandlovely/hooks/usePriceApi.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +139 -121
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +140 -137
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +104 -95
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.js +49 -40
- package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StoreListedPieceView.d.ts.map +1 -1
- package/dist/coolandlovely/pages/items/StoreListedPieceView.js +130 -123
- package/dist/coolandlovely/pages/items/StoreListedPieceView.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +241 -230
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +64 -119
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/marketplace/CreatorMarketplacePage.d.ts +2 -0
- package/dist/coolandlovely/pages/marketplace/CreatorMarketplacePage.d.ts.map +1 -0
- package/dist/coolandlovely/pages/marketplace/CreatorMarketplacePage.js +714 -0
- package/dist/coolandlovely/pages/marketplace/CreatorMarketplacePage.js.map +1 -0
- package/dist/coolandlovely/pages/profile/StyleProfilePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +214 -209
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +83 -49
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
- package/dist/coolandlovely/types/index.d.ts +5 -0
- package/dist/coolandlovely/types/index.d.ts.map +1 -1
- package/dist/generated/wspace-operations.d.ts +931 -2
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-operations.js +221 -1
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.d.ts +539 -1
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/generated/wspace-types.js +14 -0
- package/dist/generated/wspace-types.js.map +1 -0
- package/package.json +4 -3
|
@@ -3,79 +3,74 @@ import { PageLayout as t } from "../../components/PageLayout.js";
|
|
|
3
3
|
import { useAddLookbookEntry as n, useDeleteLookbook as r, useLookbook as i, useLooks as a, useRemoveLookbookEntry as o, useStyleItems as s } from "../../hooks/useCoolAndLovelyApi.js";
|
|
4
4
|
import { useAssistantEntityName as c } from "../../hooks/useAssistantEntity.js";
|
|
5
5
|
import "../../hooks/index.js";
|
|
6
|
-
import { ErrorView as
|
|
7
|
-
import { Thumbnail as u } from "../../components/Thumbnail.js";
|
|
6
|
+
import { ErrorView as l, LoadingView as u } from "../../components/StateViews.js";
|
|
8
7
|
import { formatRelativeTime as d } from "../../utils/formatters.js";
|
|
9
8
|
import { SelectField as f, TextField as p } from "../../components/Field.js";
|
|
10
9
|
import { TagList as m } from "../../components/TagList.js";
|
|
11
10
|
import { VisibilityChip as h } from "../../components/VisibilityChip.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { LookbookPriceBoard as g } from "../../components/prices/LookbookPriceBoard.js";
|
|
12
|
+
import { useMemo as _, useState as v } from "react";
|
|
13
|
+
import { ArrowLeft as y, BookHeart as b, Clock as x, Loader2 as S, Plus as C, Trash2 as w, X as T } from "lucide-react";
|
|
14
14
|
import { useParams as E } from "react-router";
|
|
15
15
|
import { jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
16
16
|
import { GlassCard as k } from "@burdenoff/fe-libs/ui";
|
|
17
17
|
//#region src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
];
|
|
23
|
-
function j() {
|
|
24
|
-
let { lookbookId: j } = E(), { navigateTo: M } = e(), N = i(j), P = o(), F = n(), I = r();
|
|
25
|
-
c(j, N.data?.name);
|
|
26
|
-
let [L, R] = _(!1), [z, B] = _("look"), [V, H] = _(""), [U, W] = _(""), G = a({ pagination: {
|
|
18
|
+
function A() {
|
|
19
|
+
let { lookbookId: A } = E(), { navigateTo: j } = e(), M = i(A), N = o(), P = n(), F = r();
|
|
20
|
+
c(A, M.data?.name);
|
|
21
|
+
let [I, L] = v(!1), [R, z] = v("look"), [B, V] = v(""), [H, U] = v(""), W = a({ pagination: {
|
|
27
22
|
limit: 100,
|
|
28
23
|
offset: 0
|
|
29
|
-
} }),
|
|
24
|
+
} }), G = s({ pagination: {
|
|
30
25
|
limit: 100,
|
|
31
26
|
offset: 0
|
|
32
|
-
} }),
|
|
27
|
+
} }), K = _(() => (G.data?.items ?? []).filter((e) => e.status !== "RETIRED"), [G.data]), q = _(() => (W.data?.items ?? []).map((e) => ({
|
|
33
28
|
value: e.id,
|
|
34
29
|
label: e.title
|
|
35
|
-
})), [
|
|
30
|
+
})), [W.data]), J = _(() => K.map((e) => ({
|
|
36
31
|
value: e.id,
|
|
37
32
|
label: `${e.name}${e.brand?.name ? ` · ${e.brand.name}` : ""}`
|
|
38
|
-
})), [Y]);
|
|
39
|
-
if (
|
|
33
|
+
})), [K]), Y = _(() => [...M.data?.entries ?? []].sort((e, t) => e.position - t.position), [M.data?.entries]);
|
|
34
|
+
if (M.isLoading) return /* @__PURE__ */ D(t, {
|
|
40
35
|
title: "Lookbook",
|
|
41
|
-
children: /* @__PURE__ */ D(
|
|
36
|
+
children: /* @__PURE__ */ D(u, { label: "Loading lookbook…" })
|
|
42
37
|
});
|
|
43
|
-
if (
|
|
38
|
+
if (M.isError || !M.data) return /* @__PURE__ */ D(t, {
|
|
44
39
|
title: "Lookbook",
|
|
45
|
-
children: /* @__PURE__ */ D(
|
|
40
|
+
children: /* @__PURE__ */ D(l, {
|
|
46
41
|
title: "Lookbook not found",
|
|
47
|
-
onRetry: () => void
|
|
42
|
+
onRetry: () => void M.refetch()
|
|
48
43
|
})
|
|
49
44
|
});
|
|
50
|
-
let
|
|
45
|
+
let X = M.data;
|
|
51
46
|
return /* @__PURE__ */ D(t, {
|
|
52
|
-
title:
|
|
53
|
-
description:
|
|
47
|
+
title: X.name,
|
|
48
|
+
description: X.description ?? void 0,
|
|
54
49
|
actions: /* @__PURE__ */ O("div", {
|
|
55
50
|
className: "flex flex-wrap items-center gap-2",
|
|
56
51
|
children: [
|
|
57
52
|
/* @__PURE__ */ O("button", {
|
|
58
53
|
type: "button",
|
|
59
|
-
onClick: () =>
|
|
54
|
+
onClick: () => j("/lookbooks"),
|
|
60
55
|
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",
|
|
61
|
-
children: [/* @__PURE__ */ D(
|
|
56
|
+
children: [/* @__PURE__ */ D(y, { className: "h-4 w-4" }), " Back"]
|
|
62
57
|
}),
|
|
63
58
|
/* @__PURE__ */ O("button", {
|
|
64
59
|
type: "button",
|
|
65
60
|
onClick: () => {
|
|
66
|
-
|
|
61
|
+
L((e) => !e), V("");
|
|
67
62
|
},
|
|
68
63
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
|
|
69
|
-
children: [D(
|
|
64
|
+
children: [D(I ? T : C, { className: "h-4 w-4" }), I ? "Close" : "Add entry"]
|
|
70
65
|
}),
|
|
71
66
|
/* @__PURE__ */ O("button", {
|
|
72
67
|
type: "button",
|
|
73
|
-
disabled:
|
|
68
|
+
disabled: F.isPending,
|
|
74
69
|
onClick: () => {
|
|
75
|
-
|
|
70
|
+
A && F.mutate(A, { onSuccess: () => j("/lookbooks") });
|
|
76
71
|
},
|
|
77
72
|
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
|
|
78
|
-
children: [
|
|
73
|
+
children: [F.isPending ? /* @__PURE__ */ D(S, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ D(w, { className: "h-4 w-4" }), "Delete"]
|
|
79
74
|
})
|
|
80
75
|
]
|
|
81
76
|
}),
|
|
@@ -89,50 +84,50 @@ function j() {
|
|
|
89
84
|
className: "flex items-center gap-3",
|
|
90
85
|
children: [/* @__PURE__ */ D("div", {
|
|
91
86
|
className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary",
|
|
92
|
-
children: /* @__PURE__ */ D(
|
|
87
|
+
children: /* @__PURE__ */ D(b, { className: "h-6 w-6" })
|
|
93
88
|
}), /* @__PURE__ */ O("div", {
|
|
94
89
|
className: "min-w-0 flex-1",
|
|
95
90
|
children: [/* @__PURE__ */ D("h2", {
|
|
96
91
|
className: "truncate text-xl font-semibold text-text-primary",
|
|
97
|
-
children:
|
|
92
|
+
children: X.name
|
|
98
93
|
}), /* @__PURE__ */ O("div", {
|
|
99
94
|
className: "mt-1.5 flex flex-wrap items-center gap-2",
|
|
100
95
|
children: [
|
|
101
|
-
/* @__PURE__ */ D(h, { visibility:
|
|
96
|
+
/* @__PURE__ */ D(h, { visibility: X.visibility }),
|
|
102
97
|
/* @__PURE__ */ O("span", {
|
|
103
98
|
className: "inline-flex items-center gap-1 text-xs text-text-secondary",
|
|
104
99
|
children: [
|
|
105
|
-
/* @__PURE__ */ D(
|
|
100
|
+
/* @__PURE__ */ D(x, { className: "h-3.5 w-3.5" }),
|
|
106
101
|
" Updated ",
|
|
107
|
-
d(
|
|
102
|
+
d(X.updatedAt)
|
|
108
103
|
]
|
|
109
104
|
}),
|
|
110
105
|
/* @__PURE__ */ O("span", {
|
|
111
106
|
className: "text-xs text-text-secondary tabular-nums",
|
|
112
107
|
children: [
|
|
113
108
|
"· ",
|
|
114
|
-
|
|
109
|
+
Y.length,
|
|
115
110
|
" ",
|
|
116
|
-
|
|
111
|
+
Y.length === 1 ? "item" : "items"
|
|
117
112
|
]
|
|
118
113
|
})
|
|
119
114
|
]
|
|
120
115
|
})]
|
|
121
116
|
})]
|
|
122
|
-
}),
|
|
123
|
-
tags:
|
|
117
|
+
}), X.tags.length > 0 && /* @__PURE__ */ D(m, {
|
|
118
|
+
tags: X.tags,
|
|
124
119
|
className: "mt-4"
|
|
125
120
|
})]
|
|
126
121
|
}),
|
|
127
|
-
|
|
122
|
+
I && /* @__PURE__ */ D("form", {
|
|
128
123
|
onSubmit: (e) => {
|
|
129
|
-
e.preventDefault(), !(!
|
|
130
|
-
lookbookId:
|
|
131
|
-
lookId:
|
|
132
|
-
styleItemId:
|
|
133
|
-
note:
|
|
124
|
+
e.preventDefault(), !(!A || !B) && P.mutate({
|
|
125
|
+
lookbookId: A,
|
|
126
|
+
lookId: R === "look" ? B : void 0,
|
|
127
|
+
styleItemId: R === "item" ? B : void 0,
|
|
128
|
+
note: H.trim() || void 0
|
|
134
129
|
}, { onSuccess: () => {
|
|
135
|
-
|
|
130
|
+
V(""), U(""), L(!1);
|
|
136
131
|
} });
|
|
137
132
|
},
|
|
138
133
|
className: "mt-6",
|
|
@@ -142,111 +137,61 @@ function j() {
|
|
|
142
137
|
children: [
|
|
143
138
|
/* @__PURE__ */ O("h3", {
|
|
144
139
|
className: "mb-4 flex items-center gap-2 text-base font-semibold text-text-primary",
|
|
145
|
-
children: [/* @__PURE__ */ D(
|
|
140
|
+
children: [/* @__PURE__ */ D(C, { className: "h-5 w-5" }), " Add to this lookbook"]
|
|
146
141
|
}),
|
|
147
142
|
/* @__PURE__ */ D("div", {
|
|
148
143
|
className: "mb-4 flex flex-wrap gap-2",
|
|
149
144
|
children: ["look", "item"].map((e) => /* @__PURE__ */ D("button", {
|
|
150
145
|
type: "button",
|
|
151
146
|
onClick: () => {
|
|
152
|
-
|
|
147
|
+
z(e), V("");
|
|
153
148
|
},
|
|
154
|
-
className:
|
|
149
|
+
className: R === e ? "rounded-full bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text" : "rounded-full bg-bg-sunken px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary",
|
|
155
150
|
children: e === "look" ? "Save a look" : "Clip a piece"
|
|
156
151
|
}, e))
|
|
157
152
|
}),
|
|
158
153
|
/* @__PURE__ */ D(f, {
|
|
159
|
-
label:
|
|
160
|
-
value:
|
|
161
|
-
onChange:
|
|
162
|
-
options:
|
|
163
|
-
placeholder:
|
|
154
|
+
label: R === "look" ? "Look" : "Style item",
|
|
155
|
+
value: B,
|
|
156
|
+
onChange: V,
|
|
157
|
+
options: R === "look" ? q : J,
|
|
158
|
+
placeholder: R === "look" ? W.isLoading ? "Loading looks…" : "Select a look" : G.isLoading ? "Loading items…" : "Select a style item"
|
|
164
159
|
}),
|
|
165
160
|
/* @__PURE__ */ D(p, {
|
|
166
161
|
label: "Note (optional)",
|
|
167
|
-
value:
|
|
168
|
-
onChange:
|
|
162
|
+
value: H,
|
|
163
|
+
onChange: U,
|
|
169
164
|
placeholder: "Why you saved it…",
|
|
170
165
|
className: "mt-4"
|
|
171
166
|
}),
|
|
172
|
-
|
|
167
|
+
P.isError && /* @__PURE__ */ D("p", {
|
|
173
168
|
className: "mt-3 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
174
|
-
children:
|
|
169
|
+
children: P.error instanceof Error ? P.error.message : "Could not add entry"
|
|
175
170
|
}),
|
|
176
171
|
/* @__PURE__ */ D("div", {
|
|
177
172
|
className: "mt-4 flex justify-end",
|
|
178
173
|
children: /* @__PURE__ */ O("button", {
|
|
179
174
|
type: "submit",
|
|
180
|
-
disabled: !
|
|
175
|
+
disabled: !B || P.isPending,
|
|
181
176
|
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",
|
|
182
|
-
children: [
|
|
177
|
+
children: [P.isPending ? /* @__PURE__ */ D(S, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ D(C, { className: "h-4 w-4" }), "Add entry"]
|
|
183
178
|
})
|
|
184
179
|
})
|
|
185
180
|
]
|
|
186
181
|
})
|
|
187
182
|
}),
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
$.length,
|
|
195
|
-
")"
|
|
196
|
-
]
|
|
197
|
-
}), $.length === 0 ? /* @__PURE__ */ D("p", {
|
|
198
|
-
className: "rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary",
|
|
199
|
-
children: "This lookbook has no entries yet. Clip a catalogue piece or save a look to add one."
|
|
200
|
-
}) : /* @__PURE__ */ D("div", {
|
|
201
|
-
className: "columns-2 gap-4 md:columns-3",
|
|
202
|
-
children: $.map((e, t) => {
|
|
203
|
-
let n = !!e.lookId, r = e.lookId ? q.get(e.lookId) : void 0, i = e.styleItemId ? J.get(e.styleItemId) : void 0, a = n ? r?.title ?? "Saved look" : i?.name ?? "Clipped piece", o = n ? r?.image?.id : i?.image?.id, s = P.isPending && P.variables === e.id;
|
|
204
|
-
return /* @__PURE__ */ O("div", {
|
|
205
|
-
className: "group relative mb-4 break-inside-avoid overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg",
|
|
206
|
-
children: [/* @__PURE__ */ D("button", {
|
|
207
|
-
type: "button",
|
|
208
|
-
onClick: () => {
|
|
209
|
-
n && e.lookId ? M(`/looks/${e.lookId}`) : e.styleItemId && M(`/items/${e.styleItemId}`);
|
|
210
|
-
},
|
|
211
|
-
"aria-label": `Open ${a}`,
|
|
212
|
-
className: "block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]",
|
|
213
|
-
children: /* @__PURE__ */ D(u, {
|
|
214
|
-
icon: D(n ? S : C, { className: "h-8 w-8" }),
|
|
215
|
-
seed: e.id,
|
|
216
|
-
fileId: o,
|
|
217
|
-
alt: a,
|
|
218
|
-
className: `w-full rounded-none ${A[t % A.length]}`,
|
|
219
|
-
overlay: /* @__PURE__ */ O("div", {
|
|
220
|
-
className: "pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-1.5 bg-gradient-to-t from-bg-canvas/90 via-bg-canvas/40 to-transparent p-3 pt-10",
|
|
221
|
-
children: [e.note && /* @__PURE__ */ D("span", {
|
|
222
|
-
className: "inline-flex w-fit max-w-full items-center rounded-md bg-bg-canvas/80 px-2 py-0.5 text-[11px] font-medium text-text-primary backdrop-blur",
|
|
223
|
-
children: /* @__PURE__ */ D("span", {
|
|
224
|
-
className: "truncate",
|
|
225
|
-
children: e.note
|
|
226
|
-
})
|
|
227
|
-
}), /* @__PURE__ */ D("p", {
|
|
228
|
-
className: "truncate text-sm font-semibold text-text-primary",
|
|
229
|
-
children: a
|
|
230
|
-
})]
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
}), /* @__PURE__ */ D("button", {
|
|
234
|
-
type: "button",
|
|
235
|
-
disabled: P.isPending,
|
|
236
|
-
onClick: () => P.mutate(e.id),
|
|
237
|
-
"aria-label": `Remove ${a}`,
|
|
238
|
-
className: "absolute right-2 top-2 z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-bg-surface/90 text-text-secondary opacity-100 shadow-sm backdrop-blur transition-opacity hover:text-status-error-text sm:opacity-0 sm:focus-visible:opacity-100 sm:group-hover:opacity-100 disabled:opacity-60",
|
|
239
|
-
children: s ? /* @__PURE__ */ D(b, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ D(T, { className: "h-3.5 w-3.5" })
|
|
240
|
-
})]
|
|
241
|
-
}, e.id);
|
|
242
|
-
})
|
|
243
|
-
})]
|
|
183
|
+
/* @__PURE__ */ D(g, {
|
|
184
|
+
entries: Y,
|
|
185
|
+
looks: W.data?.items ?? [],
|
|
186
|
+
items: G.data?.items ?? [],
|
|
187
|
+
removingEntryId: N.isPending ? N.variables : void 0,
|
|
188
|
+
onRemove: (e) => N.mutate(e)
|
|
244
189
|
})
|
|
245
190
|
]
|
|
246
191
|
})
|
|
247
192
|
});
|
|
248
193
|
}
|
|
249
194
|
//#endregion
|
|
250
|
-
export {
|
|
195
|
+
export { A as LookbookDetailPage };
|
|
251
196
|
|
|
252
197
|
//# sourceMappingURL=LookbookDetailPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LookbookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx"],"sourcesContent":["/**\n * Lookbook Detail Page\n * @module coolandlovely/pages/lookbooks\n *\n * Single lookbook (`cooLookbook`) rendered as a Pinterest-style masonry board.\n * Each entry's `lookId` / `styleItemId` is resolved client-side against the\n * already-fetched looks/items lists to a real title + image, with an optional\n * note badge and a hover X-remove (`cooRemoveLookbookEntry`). New entries are\n * added inline (`cooAddLookbookEntry`).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, BookHeart, Sparkles, Tag, Loader2, Plus, Trash2, X, Clock } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { VisibilityChip } from '../../components/VisibilityChip';\nimport { SelectField, TextField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport {\n useLookbook,\n useRemoveLookbookEntry,\n useAddLookbookEntry,\n useDeleteLookbook,\n useLooks,\n useStyleItems,\n useAssistantEntityName,\n} from '../../hooks';\nimport { formatRelativeTime } from '../../utils/formatters';\n\ntype EntryKind = 'look' | 'item';\n\n/** Alternating aspect ratios give the board its masonry rhythm. */\nconst TILE_ASPECTS = ['aspect-[3/4]', 'aspect-square', 'aspect-[4/5]'];\n\nexport function LookbookDetailPage() {\n const { lookbookId } = useParams<{ lookbookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbook(lookbookId);\n const removeEntry = useRemoveLookbookEntry();\n const addEntry = useAddLookbookEntry();\n const deleteLookbook = useDeleteLookbook();\n // Name the board for the assistant's screen context.\n useAssistantEntityName(lookbookId, query.data?.name);\n\n const [adding, setAdding] = useState(false);\n const [entryKind, setEntryKind] = useState<EntryKind>('look');\n const [selectedId, setSelectedId] = useState('');\n const [note, setNote] = useState('');\n\n const looks = useLooks({ pagination: { limit: 100, offset: 0 } });\n const items = useStyleItems({ pagination: { limit: 100, offset: 0 } });\n\n const lookById = useMemo(\n () => new Map((looks.data?.items ?? []).map((l) => [l.id, l])),\n [looks.data]\n );\n const itemById = useMemo(\n () => new Map((items.data?.items ?? []).map((i) => [i.id, i])),\n [items.data]\n );\n const usableItems = useMemo(\n () => (items.data?.items ?? []).filter((i) => i.status !== 'RETIRED'),\n [items.data]\n );\n\n const lookOptions = useMemo(\n () => (looks.data?.items ?? []).map((l) => ({ value: l.id, label: l.title })),\n [looks.data]\n );\n const itemOptions = useMemo(\n () =>\n usableItems.map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''}`,\n })),\n [usableItems]\n );\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Lookbook\">\n <LoadingView label=\"Loading lookbook…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Lookbook\">\n <ErrorView title=\"Lookbook not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const lookbook = query.data;\n const entries = [...(lookbook.entries ?? [])].sort((a, b) => a.position - b.position);\n\n const handleAddEntry = (e: React.FormEvent) => {\n e.preventDefault();\n if (!lookbookId || !selectedId) return;\n addEntry.mutate(\n {\n lookbookId,\n lookId: entryKind === 'look' ? selectedId : undefined,\n styleItemId: entryKind === 'item' ? selectedId : undefined,\n note: note.trim() || undefined,\n },\n {\n onSuccess: () => {\n setSelectedId('');\n setNote('');\n setAdding(false);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title={lookbook.name}\n description={lookbook.description ?? undefined}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/lookbooks')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setAdding((v) => !v);\n setSelectedId('');\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n {adding ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {adding ? 'Close' : 'Add entry'}\n </button>\n <button\n type=\"button\"\n disabled={deleteLookbook.isPending}\n onClick={() => {\n if (!lookbookId) return;\n deleteLookbook.mutate(lookbookId, { onSuccess: () => navigateTo('/lookbooks') });\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {deleteLookbook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Delete\n </button>\n </div>\n }\n >\n <div className=\"mx-auto max-w-5xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <BookHeart className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">{lookbook.name}</h2>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2\">\n <VisibilityChip visibility={lookbook.visibility} />\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" /> Updated {formatRelativeTime(lookbook.updatedAt)}\n </span>\n <span className=\"text-xs text-text-secondary tabular-nums\">\n · {entries.length} {entries.length === 1 ? 'item' : 'items'}\n </span>\n </div>\n </div>\n </div>\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} className=\"mt-4\" />}\n </GlassCard>\n\n {adding && (\n <form onSubmit={handleAddEntry} className=\"mt-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <Plus className=\"h-5 w-5\" /> Add to this lookbook\n </h3>\n <div className=\"mb-4 flex flex-wrap gap-2\">\n {(['look', 'item'] as EntryKind[]).map((k) => (\n <button\n key={k}\n type=\"button\"\n onClick={() => {\n setEntryKind(k);\n setSelectedId('');\n }}\n className={\n entryKind === k\n ? 'rounded-full bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text'\n : 'rounded-full bg-bg-sunken px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary'\n }\n >\n {k === 'look' ? 'Save a look' : 'Clip a piece'}\n </button>\n ))}\n </div>\n <SelectField\n label={entryKind === 'look' ? 'Look' : 'Style item'}\n value={selectedId}\n onChange={setSelectedId}\n options={entryKind === 'look' ? lookOptions : itemOptions}\n placeholder={\n entryKind === 'look'\n ? looks.isLoading\n ? 'Loading looks…'\n : 'Select a look'\n : items.isLoading\n ? 'Loading items…'\n : 'Select a style item'\n }\n />\n <TextField\n label=\"Note (optional)\"\n value={note}\n onChange={setNote}\n placeholder=\"Why you saved it…\"\n className=\"mt-4\"\n />\n {addEntry.isError && (\n <p className=\"mt-3 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {addEntry.error instanceof Error ? addEntry.error.message : 'Could not add entry'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={!selectedId || addEntry.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addEntry.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Add entry\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">Board ({entries.length})</h3>\n {entries.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n This lookbook has no entries yet. Clip a catalogue piece or save a look to add one.\n </p>\n ) : (\n <div className=\"columns-2 gap-4 md:columns-3\">\n {entries.map((entry, idx) => {\n const isLook = Boolean(entry.lookId);\n const look = entry.lookId ? lookById.get(entry.lookId) : undefined;\n const item = entry.styleItemId ? itemById.get(entry.styleItemId) : undefined;\n const title = isLook\n ? (look?.title ?? 'Saved look')\n : (item?.name ?? 'Clipped piece');\n const imageFileId = isLook ? look?.image?.id : item?.image?.id;\n const removing = removeEntry.isPending && removeEntry.variables === entry.id;\n const onOpen = () => {\n if (isLook && entry.lookId) navigateTo(`/looks/${entry.lookId}`);\n else if (entry.styleItemId) navigateTo(`/items/${entry.styleItemId}`);\n };\n return (\n <div\n key={entry.id}\n className=\"group relative mb-4 break-inside-avoid overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg\"\n >\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${title}`}\n className=\"block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <Thumbnail\n icon={\n isLook ? <Sparkles className=\"h-8 w-8\" /> : <Tag className=\"h-8 w-8\" />\n }\n seed={entry.id}\n fileId={imageFileId}\n alt={title}\n className={`w-full rounded-none ${TILE_ASPECTS[idx % TILE_ASPECTS.length]}`}\n overlay={\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-1.5 bg-gradient-to-t from-bg-canvas/90 via-bg-canvas/40 to-transparent p-3 pt-10\">\n {entry.note && (\n <span className=\"inline-flex w-fit max-w-full items-center rounded-md bg-bg-canvas/80 px-2 py-0.5 text-[11px] font-medium text-text-primary backdrop-blur\">\n <span className=\"truncate\">{entry.note}</span>\n </span>\n )}\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {title}\n </p>\n </div>\n }\n />\n </button>\n <button\n type=\"button\"\n disabled={removeEntry.isPending}\n onClick={() => removeEntry.mutate(entry.id)}\n aria-label={`Remove ${title}`}\n className=\"absolute right-2 top-2 z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-bg-surface/90 text-text-secondary opacity-100 shadow-sm backdrop-blur transition-opacity hover:text-status-error-text sm:opacity-0 sm:focus-visible:opacity-100 sm:group-hover:opacity-100 disabled:opacity-60\"\n >\n {removing ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <X className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LookbookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx"],"sourcesContent":["/**\n * Lookbook Detail Page\n * @module coolandlovely/pages/lookbooks\n *\n * Single lookbook (`cooLookbook`) rendered as a Pinterest-style masonry board.\n * Each entry's `lookId` / `styleItemId` is resolved client-side against the\n * already-fetched looks/items lists to a real title + image, with an optional\n * note badge and a hover X-remove (`cooRemoveLookbookEntry`). New entries are\n * added inline (`cooAddLookbookEntry`).\n */\n\nimport { LookbookPriceBoard } from '../../components/prices/LookbookPriceBoard';\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, BookHeart, Loader2, Plus, Trash2, X, Clock } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { TagList } from '../../components/TagList';\nimport { VisibilityChip } from '../../components/VisibilityChip';\nimport { SelectField, TextField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport {\n useLookbook,\n useRemoveLookbookEntry,\n useAddLookbookEntry,\n useDeleteLookbook,\n useLooks,\n useStyleItems,\n useAssistantEntityName,\n} from '../../hooks';\nimport { formatRelativeTime } from '../../utils/formatters';\n\ntype EntryKind = 'look' | 'item';\n\nexport function LookbookDetailPage() {\n const { lookbookId } = useParams<{ lookbookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbook(lookbookId);\n const removeEntry = useRemoveLookbookEntry();\n const addEntry = useAddLookbookEntry();\n const deleteLookbook = useDeleteLookbook();\n // Name the board for the assistant's screen context.\n useAssistantEntityName(lookbookId, query.data?.name);\n\n const [adding, setAdding] = useState(false);\n const [entryKind, setEntryKind] = useState<EntryKind>('look');\n const [selectedId, setSelectedId] = useState('');\n const [note, setNote] = useState('');\n\n const looks = useLooks({ pagination: { limit: 100, offset: 0 } });\n const items = useStyleItems({ pagination: { limit: 100, offset: 0 } });\n\n const usableItems = useMemo(\n () => (items.data?.items ?? []).filter((i) => i.status !== 'RETIRED'),\n [items.data]\n );\n\n const lookOptions = useMemo(\n () => (looks.data?.items ?? []).map((l) => ({ value: l.id, label: l.title })),\n [looks.data]\n );\n const itemOptions = useMemo(\n () =>\n usableItems.map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''}`,\n })),\n [usableItems]\n );\n\n const entries = useMemo(\n () => [...(query.data?.entries ?? [])].sort((a, b) => a.position - b.position),\n [query.data?.entries]\n );\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Lookbook\">\n <LoadingView label=\"Loading lookbook…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Lookbook\">\n <ErrorView title=\"Lookbook not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const lookbook = query.data;\n\n const handleAddEntry = (e: React.FormEvent) => {\n e.preventDefault();\n if (!lookbookId || !selectedId) return;\n addEntry.mutate(\n {\n lookbookId,\n lookId: entryKind === 'look' ? selectedId : undefined,\n styleItemId: entryKind === 'item' ? selectedId : undefined,\n note: note.trim() || undefined,\n },\n {\n onSuccess: () => {\n setSelectedId('');\n setNote('');\n setAdding(false);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title={lookbook.name}\n description={lookbook.description ?? undefined}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/lookbooks')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setAdding((v) => !v);\n setSelectedId('');\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n {adding ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {adding ? 'Close' : 'Add entry'}\n </button>\n <button\n type=\"button\"\n disabled={deleteLookbook.isPending}\n onClick={() => {\n if (!lookbookId) return;\n deleteLookbook.mutate(lookbookId, { onSuccess: () => navigateTo('/lookbooks') });\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {deleteLookbook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Delete\n </button>\n </div>\n }\n >\n <div className=\"mx-auto max-w-5xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <BookHeart className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">{lookbook.name}</h2>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2\">\n <VisibilityChip visibility={lookbook.visibility} />\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" /> Updated {formatRelativeTime(lookbook.updatedAt)}\n </span>\n <span className=\"text-xs text-text-secondary tabular-nums\">\n · {entries.length} {entries.length === 1 ? 'item' : 'items'}\n </span>\n </div>\n </div>\n </div>\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} className=\"mt-4\" />}\n </GlassCard>\n\n {adding && (\n <form onSubmit={handleAddEntry} className=\"mt-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <Plus className=\"h-5 w-5\" /> Add to this lookbook\n </h3>\n <div className=\"mb-4 flex flex-wrap gap-2\">\n {(['look', 'item'] as EntryKind[]).map((k) => (\n <button\n key={k}\n type=\"button\"\n onClick={() => {\n setEntryKind(k);\n setSelectedId('');\n }}\n className={\n entryKind === k\n ? 'rounded-full bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text'\n : 'rounded-full bg-bg-sunken px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary'\n }\n >\n {k === 'look' ? 'Save a look' : 'Clip a piece'}\n </button>\n ))}\n </div>\n <SelectField\n label={entryKind === 'look' ? 'Look' : 'Style item'}\n value={selectedId}\n onChange={setSelectedId}\n options={entryKind === 'look' ? lookOptions : itemOptions}\n placeholder={\n entryKind === 'look'\n ? looks.isLoading\n ? 'Loading looks…'\n : 'Select a look'\n : items.isLoading\n ? 'Loading items…'\n : 'Select a style item'\n }\n />\n <TextField\n label=\"Note (optional)\"\n value={note}\n onChange={setNote}\n placeholder=\"Why you saved it…\"\n className=\"mt-4\"\n />\n {addEntry.isError && (\n <p className=\"mt-3 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {addEntry.error instanceof Error ? addEntry.error.message : 'Could not add entry'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={!selectedId || addEntry.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addEntry.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Add entry\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n <LookbookPriceBoard\n entries={entries}\n looks={looks.data?.items ?? []}\n items={items.data?.items ?? []}\n removingEntryId={removeEntry.isPending ? removeEntry.variables : undefined}\n onRemove={(id) => removeEntry.mutate(id)}\n />\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreatorMarketplacePage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/marketplace/CreatorMarketplacePage.tsx"],"names":[],"mappings":"AAyhCA,wBAAgB,sBAAsB,gCAOrC"}
|