@burdenoff/microfe-coolandlovely 2026.720.1 → 2026.720.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/dist/coolandlovely/CoolAndLovelyRoutes.d.ts.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoutes.js +8 -0
- package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
- package/dist/coolandlovely/components/Field.d.ts +3 -0
- package/dist/coolandlovely/components/Field.d.ts.map +1 -1
- package/dist/coolandlovely/components/Field.js +12 -9
- package/dist/coolandlovely/components/Field.js.map +1 -1
- package/dist/coolandlovely/components/MetricsCard.d.ts +7 -0
- package/dist/coolandlovely/components/MetricsCard.d.ts.map +1 -1
- package/dist/coolandlovely/components/MetricsCard.js +37 -13
- package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
- package/dist/coolandlovely/hooks/index.d.ts +1 -1
- package/dist/coolandlovely/hooks/index.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts +54 -4
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +197 -79
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/pages/HomePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +112 -101
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +9 -9
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +135 -111
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.d.ts +5 -4
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +201 -107
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +110 -99
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js +7 -6
- package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +4 -4
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +116 -104
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +15 -13
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.d.ts +11 -6
- package/dist/coolandlovely/pages/profile/StyleProfilePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +195 -176
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/types/index.d.ts +40 -0
- package/dist/coolandlovely/types/index.d.ts.map +1 -1
- package/dist/coolandlovely/utils/index.d.ts +1 -0
- package/dist/coolandlovely/utils/index.d.ts.map +1 -1
- package/dist/coolandlovely/utils/index.js +5 -0
- package/dist/coolandlovely/utils/sparkline.d.ts +31 -0
- package/dist/coolandlovely/utils/sparkline.d.ts.map +1 -0
- package/dist/coolandlovely/utils/sparkline.js +29 -0
- package/dist/coolandlovely/utils/sparkline.js.map +1 -0
- package/dist/utils/pageContext.d.ts +8 -1
- package/dist/utils/pageContext.d.ts.map +1 -1
- package/dist/utils/pageContext.js +22 -27
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,119 +8,121 @@ import { Thumbnail as s } from "../components/Thumbnail.js";
|
|
|
8
8
|
import { useBrands as c, useCollaborations as l, useLooks as u, useStyleItems as d, useWardrobeItems as f } from "../hooks/useCoolAndLovelyApi.js";
|
|
9
9
|
import { LookCard as p } from "../components/LookCard.js";
|
|
10
10
|
import "../hooks/index.js";
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
11
|
+
import { buildCreatedAtSparkline as m, sparklineDeltaLabel as h, sparklineTrendDirection as g } from "../utils/sparkline.js";
|
|
12
|
+
import "../utils/index.js";
|
|
13
|
+
import { useMemo as _ } from "react";
|
|
14
|
+
import { ArrowRight as v, BookHeart as y, Compass as b, Handshake as x, Heart as S, Plus as C, Shapes as w, Shirt as T, Sparkles as E, Star as D, Store as O, TrendingUp as k, UserCircle as A } from "lucide-react";
|
|
15
|
+
import { jsx as j, jsxs as M } from "react/jsx-runtime";
|
|
16
|
+
import { EmphasisPanel as N, PagePurpose as P } from "@burdenoff/fe-libs/ui";
|
|
15
17
|
//#region src/coolandlovely/pages/HomePage.tsx
|
|
16
|
-
function
|
|
18
|
+
function F() {
|
|
17
19
|
let e = (/* @__PURE__ */ new Date()).getHours();
|
|
18
20
|
return e < 12 ? "Good morning" : e < 18 ? "Good afternoon" : "Good evening";
|
|
19
21
|
}
|
|
20
|
-
var
|
|
22
|
+
var I = [
|
|
21
23
|
{
|
|
22
24
|
label: "New Brand",
|
|
23
25
|
description: "Launch a storefront",
|
|
24
|
-
icon: /* @__PURE__ */ O
|
|
26
|
+
icon: /* @__PURE__ */ j(O, { className: "h-5 w-5" }),
|
|
25
27
|
path: "/brands/new",
|
|
26
28
|
color: "bg-accent-purple-subtle text-text-primary"
|
|
27
29
|
},
|
|
28
30
|
{
|
|
29
31
|
label: "Add Style Item",
|
|
30
32
|
description: "List a piece",
|
|
31
|
-
icon: /* @__PURE__ */
|
|
33
|
+
icon: /* @__PURE__ */ j(T, { className: "h-5 w-5" }),
|
|
32
34
|
path: "/items/new",
|
|
33
35
|
color: "bg-[var(--color-accent-soft)] text-text-primary"
|
|
34
36
|
},
|
|
35
37
|
{
|
|
36
38
|
label: "Publish a Look",
|
|
37
39
|
description: "Shop-the-look outfit",
|
|
38
|
-
icon: /* @__PURE__ */
|
|
40
|
+
icon: /* @__PURE__ */ j(E, { className: "h-5 w-5" }),
|
|
39
41
|
path: "/looks/new",
|
|
40
42
|
color: "bg-status-success-bg-subtle text-status-success-text"
|
|
41
43
|
},
|
|
42
44
|
{
|
|
43
45
|
label: "My Wardrobe",
|
|
44
46
|
description: "What you own",
|
|
45
|
-
icon: /* @__PURE__ */
|
|
47
|
+
icon: /* @__PURE__ */ j(S, { className: "h-5 w-5" }),
|
|
46
48
|
path: "/wardrobe",
|
|
47
49
|
color: "bg-status-warning-bg-subtle text-status-warning-text"
|
|
48
50
|
}
|
|
49
|
-
],
|
|
51
|
+
], L = [
|
|
50
52
|
{
|
|
51
53
|
label: "Brands",
|
|
52
54
|
description: "Storefronts with a publish lifecycle",
|
|
53
|
-
icon: /* @__PURE__ */ O
|
|
55
|
+
icon: /* @__PURE__ */ j(O, { className: "h-6 w-6" }),
|
|
54
56
|
path: "/brands",
|
|
55
57
|
color: "bg-accent-purple-subtle text-text-primary"
|
|
56
58
|
},
|
|
57
59
|
{
|
|
58
60
|
label: "Collections",
|
|
59
61
|
description: "Seasonal drops & curated edits",
|
|
60
|
-
icon: /* @__PURE__ */
|
|
62
|
+
icon: /* @__PURE__ */ j(w, { className: "h-6 w-6" }),
|
|
61
63
|
path: "/collections",
|
|
62
64
|
color: "bg-accent-blue-subtle text-accent-blue"
|
|
63
65
|
},
|
|
64
66
|
{
|
|
65
67
|
label: "Style Items",
|
|
66
68
|
description: "The shoppable catalogue",
|
|
67
|
-
icon: /* @__PURE__ */
|
|
69
|
+
icon: /* @__PURE__ */ j(T, { className: "h-6 w-6" }),
|
|
68
70
|
path: "/items",
|
|
69
71
|
color: "bg-[var(--color-accent-soft)] text-text-primary"
|
|
70
72
|
},
|
|
71
73
|
{
|
|
72
74
|
label: "Looks",
|
|
73
75
|
description: "Published shop-the-look outfits",
|
|
74
|
-
icon: /* @__PURE__ */
|
|
76
|
+
icon: /* @__PURE__ */ j(E, { className: "h-6 w-6" }),
|
|
75
77
|
path: "/looks",
|
|
76
78
|
color: "bg-status-success-bg-subtle text-status-success-text"
|
|
77
79
|
},
|
|
78
80
|
{
|
|
79
81
|
label: "Collaborations",
|
|
80
82
|
description: "Brand × creator partnerships",
|
|
81
|
-
icon: /* @__PURE__ */
|
|
83
|
+
icon: /* @__PURE__ */ j(x, { className: "h-6 w-6" }),
|
|
82
84
|
path: "/collaborations",
|
|
83
85
|
color: "bg-status-warning-bg-subtle text-status-warning-text"
|
|
84
86
|
},
|
|
85
87
|
{
|
|
86
88
|
label: "Wardrobe",
|
|
87
89
|
description: "Your digital closet & wear log",
|
|
88
|
-
icon: /* @__PURE__ */
|
|
90
|
+
icon: /* @__PURE__ */ j(S, { className: "h-6 w-6" }),
|
|
89
91
|
path: "/wardrobe",
|
|
90
92
|
color: "bg-status-error-bg-subtle text-status-error-text"
|
|
91
93
|
},
|
|
92
94
|
{
|
|
93
95
|
label: "Lookbooks",
|
|
94
96
|
description: "Themed boards of saved pieces",
|
|
95
|
-
icon: /* @__PURE__ */
|
|
97
|
+
icon: /* @__PURE__ */ j(y, { className: "h-6 w-6" }),
|
|
96
98
|
path: "/lookbooks",
|
|
97
99
|
color: "bg-accent-purple-subtle text-text-primary"
|
|
98
100
|
},
|
|
99
101
|
{
|
|
100
102
|
label: "Style Profile",
|
|
101
103
|
description: "Your taste, palette & sizing",
|
|
102
|
-
icon: /* @__PURE__ */
|
|
104
|
+
icon: /* @__PURE__ */ j(A, { className: "h-6 w-6" }),
|
|
103
105
|
path: "/profile",
|
|
104
106
|
color: "bg-accent-blue-subtle text-accent-blue"
|
|
105
107
|
},
|
|
106
108
|
{
|
|
107
109
|
label: "Discover",
|
|
108
110
|
description: "A feed tuned to your taste",
|
|
109
|
-
icon: /* @__PURE__ */
|
|
111
|
+
icon: /* @__PURE__ */ j(b, { className: "h-6 w-6" }),
|
|
110
112
|
path: "/discover",
|
|
111
113
|
color: "bg-[var(--color-accent-soft)] text-text-primary"
|
|
112
114
|
},
|
|
113
115
|
{
|
|
114
116
|
label: "Trends",
|
|
115
117
|
description: "Live signals from platform activity",
|
|
116
|
-
icon: /* @__PURE__ */
|
|
118
|
+
icon: /* @__PURE__ */ j(k, { className: "h-6 w-6" }),
|
|
117
119
|
path: "/trends",
|
|
118
120
|
color: "bg-status-success-bg-subtle text-status-success-text"
|
|
119
121
|
}
|
|
120
122
|
];
|
|
121
|
-
function
|
|
122
|
-
let { navigateTo:
|
|
123
|
-
let e =
|
|
123
|
+
function R() {
|
|
124
|
+
let { navigateTo: y, currentUser: b } = e(), S = c(), w = d(), k = u(), A = l(), R = f(), z = b?.firstName ?? b?.name ?? "there", B = S.isLoading || w.isLoading || k.isLoading || A.isLoading || R.isLoading, V = S.isError && w.isError && k.isError && A.isError, H = _(() => {
|
|
125
|
+
let e = S.data?.items ?? [], t = w.data?.items ?? [], n = k.data?.items ?? [], r = A.data ?? [], i = R.data ?? [];
|
|
124
126
|
return {
|
|
125
127
|
totalBrands: e.length,
|
|
126
128
|
publishedBrands: e.filter((e) => e.status === "PUBLISHED").length,
|
|
@@ -132,152 +134,161 @@ function F() {
|
|
|
132
134
|
wardrobeItems: i.length
|
|
133
135
|
};
|
|
134
136
|
}, [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
]),
|
|
141
|
-
if (
|
|
137
|
+
S.data,
|
|
138
|
+
w.data,
|
|
139
|
+
k.data,
|
|
140
|
+
A.data,
|
|
141
|
+
R.data
|
|
142
|
+
]), U = _(() => [...k.data?.items ?? []].slice(0, 5), [k.data]), W = _(() => [...S.data?.items ?? []].sort((e, t) => new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime()).slice(0, 4), [S.data]);
|
|
143
|
+
if (B) return /* @__PURE__ */ j(n, {
|
|
142
144
|
title: "CoolAndLovely",
|
|
143
|
-
children: /* @__PURE__ */
|
|
145
|
+
children: /* @__PURE__ */ j(o, { label: "Loading your style command center…" })
|
|
144
146
|
});
|
|
145
|
-
if (
|
|
147
|
+
if (V) return /* @__PURE__ */ j(n, {
|
|
146
148
|
title: "CoolAndLovely",
|
|
147
|
-
children: /* @__PURE__ */
|
|
149
|
+
children: /* @__PURE__ */ j(a, {
|
|
148
150
|
title: "Could not load CoolAndLovely",
|
|
149
151
|
message: "The fashion service did not respond. Please try again.",
|
|
150
152
|
onRetry: () => {
|
|
151
|
-
|
|
153
|
+
S.refetch(), w.refetch(), k.refetch(), A.refetch(), R.refetch();
|
|
152
154
|
}
|
|
153
155
|
})
|
|
154
156
|
});
|
|
155
|
-
let
|
|
157
|
+
let G = m(S.data?.items ?? []), K = m(w.data?.items ?? []), q = m(k.data?.items ?? []), J = m(A.data ?? []), Y = [
|
|
156
158
|
{
|
|
157
|
-
icon: /* @__PURE__ */ O
|
|
158
|
-
value:
|
|
159
|
+
icon: /* @__PURE__ */ j(O, { className: "h-5 w-5" }),
|
|
160
|
+
value: H.totalBrands.toString(),
|
|
159
161
|
label: "Brands",
|
|
160
|
-
sub: `${
|
|
162
|
+
sub: h(G) ?? `${H.publishedBrands} published`,
|
|
163
|
+
sparklineData: G,
|
|
164
|
+
trend: g(G)
|
|
161
165
|
},
|
|
162
166
|
{
|
|
163
|
-
icon: /* @__PURE__ */
|
|
164
|
-
value:
|
|
167
|
+
icon: /* @__PURE__ */ j(T, { className: "h-5 w-5" }),
|
|
168
|
+
value: H.totalStyleItems.toString(),
|
|
165
169
|
label: "Style Items",
|
|
166
|
-
sub: `${
|
|
170
|
+
sub: h(K) ?? `${H.activeStyleItems} active`,
|
|
171
|
+
sparklineData: K,
|
|
172
|
+
trend: g(K)
|
|
167
173
|
},
|
|
168
174
|
{
|
|
169
|
-
icon: /* @__PURE__ */
|
|
170
|
-
value:
|
|
175
|
+
icon: /* @__PURE__ */ j(E, { className: "h-5 w-5" }),
|
|
176
|
+
value: H.totalLooks.toString(),
|
|
171
177
|
label: "Looks",
|
|
172
|
-
sub: `${
|
|
178
|
+
sub: h(q) ?? `${H.featuredLooks} featured`,
|
|
179
|
+
sparklineData: q,
|
|
180
|
+
trend: g(q)
|
|
173
181
|
},
|
|
174
182
|
{
|
|
175
|
-
icon: /* @__PURE__ */
|
|
176
|
-
value:
|
|
183
|
+
icon: /* @__PURE__ */ j(x, { className: "h-5 w-5" }),
|
|
184
|
+
value: H.activeCollaborations.toString(),
|
|
177
185
|
label: "Active Collabs",
|
|
178
|
-
sub: `${
|
|
186
|
+
sub: h(J) ?? `${H.wardrobeItems} in wardrobe`,
|
|
187
|
+
sparklineData: J,
|
|
188
|
+
trend: g(J)
|
|
179
189
|
}
|
|
180
190
|
];
|
|
181
|
-
return /* @__PURE__ */
|
|
182
|
-
title: `${
|
|
191
|
+
return /* @__PURE__ */ M(n, {
|
|
192
|
+
title: `${F()}, ${z}`,
|
|
183
193
|
description: "Your fashion & lifestyle command center",
|
|
184
194
|
hero: !0,
|
|
185
195
|
children: [
|
|
186
|
-
/* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ j(P, {
|
|
187
197
|
className: "mb-6",
|
|
188
198
|
children: "CoolAndLovely connects the three sides of style on one workspace-native model: brands launch storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators publish shop-the-look outfits and run brand collaborations. Jump into any module below — all data is live from the CoolAndLovely service."
|
|
189
199
|
}),
|
|
190
|
-
/* @__PURE__ */
|
|
200
|
+
/* @__PURE__ */ M(N, {
|
|
191
201
|
className: "mb-8",
|
|
192
|
-
children: [/* @__PURE__ */
|
|
202
|
+
children: [/* @__PURE__ */ j("h2", {
|
|
193
203
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
194
204
|
children: "At a glance"
|
|
195
|
-
}), /* @__PURE__ */
|
|
205
|
+
}), /* @__PURE__ */ j("div", {
|
|
196
206
|
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
197
|
-
children:
|
|
207
|
+
children: Y.map((e, t) => /* @__PURE__ */ j(r, {
|
|
198
208
|
icon: e.icon,
|
|
199
209
|
value: e.value,
|
|
200
210
|
label: e.label,
|
|
201
211
|
trendValue: e.sub,
|
|
202
|
-
trend:
|
|
212
|
+
trend: e.trend,
|
|
213
|
+
sparklineData: e.sparklineData,
|
|
203
214
|
featured: t === 0
|
|
204
215
|
}, e.label))
|
|
205
216
|
})]
|
|
206
217
|
}),
|
|
207
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ M("section", {
|
|
208
219
|
className: "mb-8",
|
|
209
|
-
children: [/* @__PURE__ */
|
|
220
|
+
children: [/* @__PURE__ */ j("h2", {
|
|
210
221
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
211
222
|
children: "Quick actions"
|
|
212
|
-
}), /* @__PURE__ */
|
|
223
|
+
}), /* @__PURE__ */ j("div", {
|
|
213
224
|
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
214
|
-
children:
|
|
225
|
+
children: I.map((e) => /* @__PURE__ */ M("button", {
|
|
215
226
|
type: "button",
|
|
216
|
-
onClick: () =>
|
|
227
|
+
onClick: () => y(e.path),
|
|
217
228
|
className: t("flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all", "hover:border-accent-foreground/20 hover:shadow-md"),
|
|
218
229
|
children: [
|
|
219
|
-
/* @__PURE__ */
|
|
230
|
+
/* @__PURE__ */ j("div", {
|
|
220
231
|
className: t("flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", e.color),
|
|
221
232
|
children: e.icon
|
|
222
233
|
}),
|
|
223
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ M("div", {
|
|
224
235
|
className: "min-w-0 flex-1",
|
|
225
|
-
children: [/* @__PURE__ */
|
|
236
|
+
children: [/* @__PURE__ */ j("p", {
|
|
226
237
|
className: "text-sm font-semibold text-text-primary",
|
|
227
238
|
children: e.label
|
|
228
|
-
}), /* @__PURE__ */
|
|
239
|
+
}), /* @__PURE__ */ j("p", {
|
|
229
240
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
230
241
|
children: e.description
|
|
231
242
|
})]
|
|
232
243
|
}),
|
|
233
|
-
/* @__PURE__ */
|
|
244
|
+
/* @__PURE__ */ j(C, { className: "mt-1 h-4 w-4 shrink-0 text-text-secondary" })
|
|
234
245
|
]
|
|
235
246
|
}, e.label))
|
|
236
247
|
})]
|
|
237
248
|
}),
|
|
238
|
-
/* @__PURE__ */
|
|
249
|
+
/* @__PURE__ */ M("section", {
|
|
239
250
|
className: "mb-8",
|
|
240
|
-
children: [/* @__PURE__ */
|
|
251
|
+
children: [/* @__PURE__ */ j("h2", {
|
|
241
252
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
242
253
|
children: "Explore"
|
|
243
|
-
}), /* @__PURE__ */
|
|
254
|
+
}), /* @__PURE__ */ j("div", {
|
|
244
255
|
className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4",
|
|
245
|
-
children:
|
|
256
|
+
children: L.map((e) => /* @__PURE__ */ M("button", {
|
|
246
257
|
type: "button",
|
|
247
|
-
onClick: () =>
|
|
258
|
+
onClick: () => y(e.path),
|
|
248
259
|
className: "flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md",
|
|
249
|
-
children: [/* @__PURE__ */
|
|
260
|
+
children: [/* @__PURE__ */ j("div", {
|
|
250
261
|
className: t("flex h-12 w-12 items-center justify-center rounded-full", e.color),
|
|
251
262
|
children: e.icon
|
|
252
|
-
}), /* @__PURE__ */
|
|
263
|
+
}), /* @__PURE__ */ M("div", { children: [/* @__PURE__ */ j("p", {
|
|
253
264
|
className: "text-sm font-semibold text-text-primary",
|
|
254
265
|
children: e.label
|
|
255
|
-
}), /* @__PURE__ */
|
|
266
|
+
}), /* @__PURE__ */ j("p", {
|
|
256
267
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
257
268
|
children: e.description
|
|
258
269
|
})] })]
|
|
259
270
|
}, e.label))
|
|
260
271
|
})]
|
|
261
272
|
}),
|
|
262
|
-
/* @__PURE__ */
|
|
273
|
+
/* @__PURE__ */ M("section", {
|
|
263
274
|
className: "mb-8",
|
|
264
|
-
children: [/* @__PURE__ */
|
|
275
|
+
children: [/* @__PURE__ */ M("div", {
|
|
265
276
|
className: "mb-4 flex items-center justify-between",
|
|
266
|
-
children: [/* @__PURE__ */
|
|
277
|
+
children: [/* @__PURE__ */ j("h2", {
|
|
267
278
|
className: "text-lg font-semibold text-text-primary",
|
|
268
279
|
children: "Latest looks"
|
|
269
|
-
}), /* @__PURE__ */
|
|
280
|
+
}), /* @__PURE__ */ M("button", {
|
|
270
281
|
type: "button",
|
|
271
|
-
onClick: () =>
|
|
282
|
+
onClick: () => y("/looks"),
|
|
272
283
|
className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
273
|
-
children: ["View all ", /* @__PURE__ */
|
|
284
|
+
children: ["View all ", /* @__PURE__ */ j(v, { className: "h-3.5 w-3.5" })]
|
|
274
285
|
})]
|
|
275
|
-
}),
|
|
286
|
+
}), U.length === 0 ? /* @__PURE__ */ j("p", {
|
|
276
287
|
className: "rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary",
|
|
277
288
|
children: "No looks published yet. Be the first to publish a shop-the-look outfit."
|
|
278
|
-
}) : /* @__PURE__ */
|
|
289
|
+
}) : /* @__PURE__ */ j("div", {
|
|
279
290
|
className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5",
|
|
280
|
-
children:
|
|
291
|
+
children: U.map((e) => /* @__PURE__ */ j(p, {
|
|
281
292
|
id: e.id,
|
|
282
293
|
title: e.title,
|
|
283
294
|
occasion: e.occasion,
|
|
@@ -285,50 +296,50 @@ function F() {
|
|
|
285
296
|
likeCount: e.likeCount,
|
|
286
297
|
isLikedByMe: e.isLikedByMe,
|
|
287
298
|
isSavedByMe: e.isSavedByMe,
|
|
288
|
-
onOpen: () =>
|
|
299
|
+
onOpen: () => y(`/looks/${e.id}`),
|
|
289
300
|
showActions: !0
|
|
290
301
|
}, e.id))
|
|
291
302
|
})]
|
|
292
303
|
}),
|
|
293
|
-
|
|
304
|
+
W.length > 0 && /* @__PURE__ */ M("section", { children: [/* @__PURE__ */ M("div", {
|
|
294
305
|
className: "mb-4 flex items-center justify-between",
|
|
295
|
-
children: [/* @__PURE__ */
|
|
306
|
+
children: [/* @__PURE__ */ j("h2", {
|
|
296
307
|
className: "text-lg font-semibold text-text-primary",
|
|
297
308
|
children: "Recently updated brands"
|
|
298
|
-
}), /* @__PURE__ */
|
|
309
|
+
}), /* @__PURE__ */ M("button", {
|
|
299
310
|
type: "button",
|
|
300
|
-
onClick: () =>
|
|
311
|
+
onClick: () => y("/brands"),
|
|
301
312
|
className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
302
|
-
children: ["View all ", /* @__PURE__ */
|
|
313
|
+
children: ["View all ", /* @__PURE__ */ j(v, { className: "h-3.5 w-3.5" })]
|
|
303
314
|
})]
|
|
304
|
-
}), /* @__PURE__ */
|
|
315
|
+
}), /* @__PURE__ */ j("div", {
|
|
305
316
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
306
|
-
children:
|
|
317
|
+
children: W.map((e) => /* @__PURE__ */ M("button", {
|
|
307
318
|
type: "button",
|
|
308
|
-
onClick: () =>
|
|
319
|
+
onClick: () => y(`/brands/${e.id}`),
|
|
309
320
|
className: "group flex items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-3 text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md",
|
|
310
321
|
children: [
|
|
311
|
-
/* @__PURE__ */
|
|
312
|
-
icon: /* @__PURE__ */ O
|
|
322
|
+
/* @__PURE__ */ j(s, {
|
|
323
|
+
icon: /* @__PURE__ */ j(O, { className: "h-5 w-5" }),
|
|
313
324
|
seed: e.id,
|
|
314
325
|
fileId: e.logo?.id ?? e.cover?.id,
|
|
315
326
|
alt: e.name,
|
|
316
327
|
className: "h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface"
|
|
317
328
|
}),
|
|
318
|
-
/* @__PURE__ */
|
|
329
|
+
/* @__PURE__ */ M("div", {
|
|
319
330
|
className: "min-w-0 flex-1",
|
|
320
|
-
children: [/* @__PURE__ */
|
|
331
|
+
children: [/* @__PURE__ */ M("div", {
|
|
321
332
|
className: "flex items-center gap-1.5",
|
|
322
|
-
children: [/* @__PURE__ */
|
|
333
|
+
children: [/* @__PURE__ */ j("p", {
|
|
323
334
|
className: "truncate text-sm font-medium text-text-primary",
|
|
324
335
|
children: e.name
|
|
325
|
-
}), e.isFeatured && /* @__PURE__ */
|
|
326
|
-
}), /* @__PURE__ */
|
|
336
|
+
}), e.isFeatured && /* @__PURE__ */ j(D, { className: "h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text" })]
|
|
337
|
+
}), /* @__PURE__ */ M("p", {
|
|
327
338
|
className: "truncate text-xs text-text-secondary",
|
|
328
339
|
children: ["@", e.handle]
|
|
329
340
|
})]
|
|
330
341
|
}),
|
|
331
|
-
/* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ j(i, { status: e.status })
|
|
332
343
|
]
|
|
333
344
|
}, e.id))
|
|
334
345
|
})] })
|
|
@@ -336,6 +347,6 @@ function F() {
|
|
|
336
347
|
});
|
|
337
348
|
}
|
|
338
349
|
//#endregion
|
|
339
|
-
export {
|
|
350
|
+
export { R as HomePage };
|
|
340
351
|
|
|
341
352
|
//# sourceMappingURL=HomePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { LookCard } from '../components/LookCard';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useBrands, useStyleItems, useLooks, useCollaborations, useWardrobeItems } from '../hooks';\nimport { cn } from '../utils/cn';\nimport type { DashboardStats } from '../types';\n\n/** Time-of-day greeting for the dashboard title (local clock, honest — no data). */\nfunction greeting(): string {\n const hour = new Date().getHours();\n if (hour < 12) return 'Good morning';\n if (hour < 18) return 'Good afternoon';\n return 'Good evening';\n}\n\ninterface QuickAction {\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n];\n\nconst modules = [\n {\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Discover',\n description: 'A feed tuned to your taste',\n icon: <Compass className=\"h-6 w-6\" />,\n path: '/discover',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Trends',\n description: 'Live signals from platform activity',\n icon: <TrendingUp className=\"h-6 w-6\" />,\n path: '/trends',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 5), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <LoadingView label=\"Loading your style command center…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <ErrorView\n title=\"Could not load CoolAndLovely\"\n message=\"The fashion service did not respond. Please try again.\"\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n label: 'Brands',\n sub: `${stats.publishedBrands} published`,\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n label: 'Style Items',\n sub: `${stats.activeStyleItems} active`,\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n label: 'Looks',\n sub: `${stats.featuredLooks} featured`,\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n label: 'Active Collabs',\n sub: `${stats.wardrobeItems} in wardrobe`,\n },\n ];\n\n return (\n <PageLayout\n title={`${greeting()}, ${firstName}`}\n description=\"Your fashion & lifestyle command center\"\n hero\n >\n <PagePurpose className=\"mb-6\">\n CoolAndLovely connects the three sides of style on one workspace-native model: brands launch\n storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators\n publish shop-the-look outfits and run brand collaborations. Jump into any module below — all\n data is live from the CoolAndLovely service.\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m, index) => (\n <MetricsCard\n key={m.label}\n icon={m.icon}\n value={m.value}\n label={m.label}\n trendValue={m.sub}\n trend=\"neutral\"\n featured={index === 0}\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Quick actions</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{action.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{action.description}</p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Explore</h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.label}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn('flex h-12 w-12 items-center justify-center rounded-full', mod.color)}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{mod.description}</p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Latest looks</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n No looks published yet. Be the first to publish a shop-the-look outfit.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5\">\n {latestLooks.map((look) => (\n <LookCard\n key={look.id}\n id={look.id}\n title={look.title}\n occasion={look.occasion}\n imageFileId={look.image?.id}\n likeCount={look.likeCount}\n isLikedByMe={look.isLikedByMe}\n isSavedByMe={look.isSavedByMe}\n onOpen={() => navigateTo(`/looks/${look.id}`)}\n showActions\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently updated brands</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-3 text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n fileId={brand.logo?.id ?? brand.cover?.id}\n alt={brand.name}\n className=\"h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { LookCard } from '../components/LookCard';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useBrands, useStyleItems, useLooks, useCollaborations, useWardrobeItems } from '../hooks';\nimport { cn } from '../utils/cn';\nimport { buildCreatedAtSparkline, sparklineDeltaLabel, sparklineTrendDirection } from '../utils';\nimport type { DashboardStats } from '../types';\n\n/** Time-of-day greeting for the dashboard title (local clock, honest — no data). */\nfunction greeting(): string {\n const hour = new Date().getHours();\n if (hour < 12) return 'Good morning';\n if (hour < 18) return 'Good afternoon';\n return 'Good evening';\n}\n\ninterface QuickAction {\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n];\n\nconst modules = [\n {\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Discover',\n description: 'A feed tuned to your taste',\n icon: <Compass className=\"h-6 w-6\" />,\n path: '/discover',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Trends',\n description: 'Live signals from platform activity',\n icon: <TrendingUp className=\"h-6 w-6\" />,\n path: '/trends',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 5), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <LoadingView label=\"Loading your style command center…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <ErrorView\n title=\"Could not load CoolAndLovely\"\n message=\"The fashion service did not respond. Please try again.\"\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n // Real (non-fabricated) 7-day sparkline series derived from each entity's\n // own createdAt — falls back to the plain text sub-label when there is no\n // meaningful history yet (e.g. everything was seeded at once).\n const brandSparkline = buildCreatedAtSparkline(brands.data?.items ?? []);\n const itemSparkline = buildCreatedAtSparkline(items.data?.items ?? []);\n const lookSparkline = buildCreatedAtSparkline(looks.data?.items ?? []);\n const collabSparkline = buildCreatedAtSparkline(collaborations.data ?? []);\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n label: 'Brands',\n sub: sparklineDeltaLabel(brandSparkline) ?? `${stats.publishedBrands} published`,\n sparklineData: brandSparkline,\n trend: sparklineTrendDirection(brandSparkline),\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n label: 'Style Items',\n sub: sparklineDeltaLabel(itemSparkline) ?? `${stats.activeStyleItems} active`,\n sparklineData: itemSparkline,\n trend: sparklineTrendDirection(itemSparkline),\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n label: 'Looks',\n sub: sparklineDeltaLabel(lookSparkline) ?? `${stats.featuredLooks} featured`,\n sparklineData: lookSparkline,\n trend: sparklineTrendDirection(lookSparkline),\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n label: 'Active Collabs',\n sub: sparklineDeltaLabel(collabSparkline) ?? `${stats.wardrobeItems} in wardrobe`,\n sparklineData: collabSparkline,\n trend: sparklineTrendDirection(collabSparkline),\n },\n ];\n\n return (\n <PageLayout\n title={`${greeting()}, ${firstName}`}\n description=\"Your fashion & lifestyle command center\"\n hero\n >\n <PagePurpose className=\"mb-6\">\n CoolAndLovely connects the three sides of style on one workspace-native model: brands launch\n storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators\n publish shop-the-look outfits and run brand collaborations. Jump into any module below — all\n data is live from the CoolAndLovely service.\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m, index) => (\n <MetricsCard\n key={m.label}\n icon={m.icon}\n value={m.value}\n label={m.label}\n trendValue={m.sub}\n trend={m.trend}\n sparklineData={m.sparklineData}\n featured={index === 0}\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Quick actions</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{action.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{action.description}</p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Explore</h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.label}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn('flex h-12 w-12 items-center justify-center rounded-full', mod.color)}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{mod.description}</p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Latest looks</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n No looks published yet. Be the first to publish a shop-the-look outfit.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5\">\n {latestLooks.map((look) => (\n <LookCard\n key={look.id}\n id={look.id}\n title={look.title}\n occasion={look.occasion}\n imageFileId={look.image?.id}\n likeCount={look.likeCount}\n isLikedByMe={look.isLikedByMe}\n isSavedByMe={look.isSavedByMe}\n onOpen={() => navigateTo(`/looks/${look.id}`)}\n showActions\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently updated brands</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-3 text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n fileId={brand.logo?.id ?? brand.cover?.id}\n alt={brand.name}\n className=\"h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -7,10 +7,10 @@ import { Thumbnail as o } from "../../components/Thumbnail.js";
|
|
|
7
7
|
import { useBrand as s, useTransitionBrand as c, useUpdateBrand as l } from "../../hooks/useCoolAndLovelyApi.js";
|
|
8
8
|
import { formatDate as u, formatPrice as d, titleCase as f } from "../../utils/formatters.js";
|
|
9
9
|
import "../../hooks/index.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
10
|
+
import { categoryIcon as p } from "../../utils/icons.js";
|
|
11
|
+
import { BrandCover as m } from "../../components/BrandCover.js";
|
|
12
|
+
import { TagList as h } from "../../components/TagList.js";
|
|
13
|
+
import { Comments as g } from "../../components/Comments.js";
|
|
14
14
|
import { ArrowLeft as _, ExternalLink as v, Loader2 as y, Pencil as b, Plus as x, Shapes as S, Shirt as C, Store as w } from "lucide-react";
|
|
15
15
|
import { useParams as T } from "react-router-dom";
|
|
16
16
|
import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
@@ -50,7 +50,7 @@ function j() {
|
|
|
50
50
|
children: [/* @__PURE__ */ O("div", {
|
|
51
51
|
className: "space-y-6 lg:col-span-2",
|
|
52
52
|
children: [
|
|
53
|
-
/* @__PURE__ */ D(
|
|
53
|
+
/* @__PURE__ */ D(m, {
|
|
54
54
|
coverFileId: I.cover?.id,
|
|
55
55
|
logoFileId: I.logo?.id,
|
|
56
56
|
name: I.name,
|
|
@@ -100,7 +100,7 @@ function j() {
|
|
|
100
100
|
children: [
|
|
101
101
|
/* @__PURE__ */ O("div", {
|
|
102
102
|
className: "flex flex-wrap items-center justify-between gap-3",
|
|
103
|
-
children: [I.aesthetics.length > 0 ? /* @__PURE__ */ D(
|
|
103
|
+
children: [I.aesthetics.length > 0 ? /* @__PURE__ */ D(h, {
|
|
104
104
|
tags: I.aesthetics.map(f),
|
|
105
105
|
tone: "accent",
|
|
106
106
|
max: 12
|
|
@@ -110,7 +110,7 @@ function j() {
|
|
|
110
110
|
className: "text-sm text-text-secondary",
|
|
111
111
|
children: I.bio
|
|
112
112
|
}),
|
|
113
|
-
I.tags.length > 0 && /* @__PURE__ */ D(
|
|
113
|
+
I.tags.length > 0 && /* @__PURE__ */ D(h, {
|
|
114
114
|
tags: I.tags,
|
|
115
115
|
max: 12
|
|
116
116
|
}),
|
|
@@ -203,7 +203,7 @@ function j() {
|
|
|
203
203
|
onClick: () => M(`/items/${e.id}`),
|
|
204
204
|
className: "overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left transition-all hover:border-accent-foreground/20 hover:shadow-md",
|
|
205
205
|
children: [/* @__PURE__ */ D(o, {
|
|
206
|
-
icon:
|
|
206
|
+
icon: p(e.category, 24),
|
|
207
207
|
seed: e.id,
|
|
208
208
|
fileId: e.image?.id,
|
|
209
209
|
alt: e.name,
|
|
@@ -336,7 +336,7 @@ function j() {
|
|
|
336
336
|
]
|
|
337
337
|
})]
|
|
338
338
|
}),
|
|
339
|
-
/* @__PURE__ */ D(
|
|
339
|
+
/* @__PURE__ */ D(g, {
|
|
340
340
|
entityType: "CooBrand",
|
|
341
341
|
entityId: I.id,
|
|
342
342
|
className: "mt-0"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollaborationDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/collaborations/CollaborationDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoCH,wBAAgB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"CollaborationDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/collaborations/CollaborationDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoCH,wBAAgB,uBAAuB,gCAwOtC"}
|