@burdenoff/microfe-coolandlovely 2026.706.2 → 2026.706.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
- package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
- package/dist/coolandlovely/components/AddToLookbookButton.js +22 -22
- package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
- package/dist/coolandlovely/components/BrandCover.js +39 -0
- package/dist/coolandlovely/components/BrandCover.js.map +1 -0
- package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
- package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
- package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
- package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
- package/dist/coolandlovely/components/Comments.js +4 -4
- package/dist/coolandlovely/components/Comments.js.map +1 -1
- package/dist/coolandlovely/components/FileImage.js.map +1 -1
- package/dist/coolandlovely/components/ImageUploadField.js +139 -57
- package/dist/coolandlovely/components/ImageUploadField.js.map +1 -1
- package/dist/coolandlovely/components/LookCard.js +77 -0
- package/dist/coolandlovely/components/LookCard.js.map +1 -0
- package/dist/coolandlovely/components/MetricsCard.js +1 -1
- package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
- package/dist/coolandlovely/components/ShopItemCard.js +45 -0
- package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
- package/dist/coolandlovely/components/StepTracker.js +35 -0
- package/dist/coolandlovely/components/StepTracker.js.map +1 -0
- package/dist/coolandlovely/components/Thumbnail.js +15 -11
- package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
- package/dist/coolandlovely/components/VisibilityChip.js +32 -0
- package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +94 -69
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/hooks/useFiles.js +49 -32
- package/dist/coolandlovely/hooks/useFiles.js.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +112 -133
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +253 -242
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +116 -112
- package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -13
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +131 -108
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js +1 -1
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -56
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +175 -147
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +1 -1
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js +71 -62
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +149 -127
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +69 -53
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -113
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookFormPage.js +122 -110
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -97
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +372 -210
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/pages/trends/TrendsPage.js +1 -1
- package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +130 -94
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -76
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,39 +6,43 @@ import { StatusBadge as i } from "../components/StatusBadge.js";
|
|
|
6
6
|
import { ErrorView as a, LoadingView as o } from "../components/StateViews.js";
|
|
7
7
|
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
|
+
import { LookCard as p } from "../components/LookCard.js";
|
|
9
10
|
import "../hooks/index.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { EmphasisPanel as j, PagePurpose as M } from "@burdenoff/fe-libs/ui";
|
|
11
|
+
import { useMemo as m } from "react";
|
|
12
|
+
import { ArrowRight as h, BookHeart as g, Compass as _, Handshake as v, Heart as y, Plus as b, Shapes as x, Shirt as S, Sparkles as C, Star as w, Store as T, TrendingUp as E, UserCircle as D } from "lucide-react";
|
|
13
|
+
import { jsx as O, jsxs as k } from "react/jsx-runtime";
|
|
14
|
+
import { EmphasisPanel as A, PagePurpose as j } from "@burdenoff/fe-libs/ui";
|
|
15
15
|
//#region src/coolandlovely/pages/HomePage.tsx
|
|
16
|
+
function M() {
|
|
17
|
+
let e = (/* @__PURE__ */ new Date()).getHours();
|
|
18
|
+
return e < 12 ? "Good morning" : e < 18 ? "Good afternoon" : "Good evening";
|
|
19
|
+
}
|
|
16
20
|
var N = [
|
|
17
21
|
{
|
|
18
22
|
label: "New Brand",
|
|
19
23
|
description: "Launch a storefront",
|
|
20
|
-
icon: /* @__PURE__ */
|
|
24
|
+
icon: /* @__PURE__ */ O(T, { className: "h-5 w-5" }),
|
|
21
25
|
path: "/brands/new",
|
|
22
26
|
color: "bg-accent-purple-subtle text-text-primary"
|
|
23
27
|
},
|
|
24
28
|
{
|
|
25
29
|
label: "Add Style Item",
|
|
26
30
|
description: "List a piece",
|
|
27
|
-
icon: /* @__PURE__ */
|
|
31
|
+
icon: /* @__PURE__ */ O(S, { className: "h-5 w-5" }),
|
|
28
32
|
path: "/items/new",
|
|
29
33
|
color: "bg-[var(--color-accent-soft)] text-text-primary"
|
|
30
34
|
},
|
|
31
35
|
{
|
|
32
36
|
label: "Publish a Look",
|
|
33
37
|
description: "Shop-the-look outfit",
|
|
34
|
-
icon: /* @__PURE__ */
|
|
38
|
+
icon: /* @__PURE__ */ O(C, { className: "h-5 w-5" }),
|
|
35
39
|
path: "/looks/new",
|
|
36
40
|
color: "bg-status-success-bg-subtle text-status-success-text"
|
|
37
41
|
},
|
|
38
42
|
{
|
|
39
43
|
label: "My Wardrobe",
|
|
40
44
|
description: "What you own",
|
|
41
|
-
icon: /* @__PURE__ */
|
|
45
|
+
icon: /* @__PURE__ */ O(y, { className: "h-5 w-5" }),
|
|
42
46
|
path: "/wardrobe",
|
|
43
47
|
color: "bg-status-warning-bg-subtle text-status-warning-text"
|
|
44
48
|
}
|
|
@@ -46,77 +50,77 @@ var N = [
|
|
|
46
50
|
{
|
|
47
51
|
label: "Brands",
|
|
48
52
|
description: "Storefronts with a publish lifecycle",
|
|
49
|
-
icon: /* @__PURE__ */
|
|
53
|
+
icon: /* @__PURE__ */ O(T, { className: "h-6 w-6" }),
|
|
50
54
|
path: "/brands",
|
|
51
55
|
color: "bg-accent-purple-subtle text-text-primary"
|
|
52
56
|
},
|
|
53
57
|
{
|
|
54
58
|
label: "Collections",
|
|
55
59
|
description: "Seasonal drops & curated edits",
|
|
56
|
-
icon: /* @__PURE__ */
|
|
60
|
+
icon: /* @__PURE__ */ O(x, { className: "h-6 w-6" }),
|
|
57
61
|
path: "/collections",
|
|
58
62
|
color: "bg-accent-blue-subtle text-accent-blue"
|
|
59
63
|
},
|
|
60
64
|
{
|
|
61
65
|
label: "Style Items",
|
|
62
66
|
description: "The shoppable catalogue",
|
|
63
|
-
icon: /* @__PURE__ */
|
|
67
|
+
icon: /* @__PURE__ */ O(S, { className: "h-6 w-6" }),
|
|
64
68
|
path: "/items",
|
|
65
69
|
color: "bg-[var(--color-accent-soft)] text-text-primary"
|
|
66
70
|
},
|
|
67
71
|
{
|
|
68
72
|
label: "Looks",
|
|
69
73
|
description: "Published shop-the-look outfits",
|
|
70
|
-
icon: /* @__PURE__ */
|
|
74
|
+
icon: /* @__PURE__ */ O(C, { className: "h-6 w-6" }),
|
|
71
75
|
path: "/looks",
|
|
72
76
|
color: "bg-status-success-bg-subtle text-status-success-text"
|
|
73
77
|
},
|
|
74
78
|
{
|
|
75
79
|
label: "Collaborations",
|
|
76
80
|
description: "Brand × creator partnerships",
|
|
77
|
-
icon: /* @__PURE__ */
|
|
81
|
+
icon: /* @__PURE__ */ O(v, { className: "h-6 w-6" }),
|
|
78
82
|
path: "/collaborations",
|
|
79
83
|
color: "bg-status-warning-bg-subtle text-status-warning-text"
|
|
80
84
|
},
|
|
81
85
|
{
|
|
82
86
|
label: "Wardrobe",
|
|
83
87
|
description: "Your digital closet & wear log",
|
|
84
|
-
icon: /* @__PURE__ */
|
|
88
|
+
icon: /* @__PURE__ */ O(y, { className: "h-6 w-6" }),
|
|
85
89
|
path: "/wardrobe",
|
|
86
90
|
color: "bg-status-error-bg-subtle text-status-error-text"
|
|
87
91
|
},
|
|
88
92
|
{
|
|
89
93
|
label: "Lookbooks",
|
|
90
94
|
description: "Themed boards of saved pieces",
|
|
91
|
-
icon: /* @__PURE__ */
|
|
95
|
+
icon: /* @__PURE__ */ O(g, { className: "h-6 w-6" }),
|
|
92
96
|
path: "/lookbooks",
|
|
93
97
|
color: "bg-accent-purple-subtle text-text-primary"
|
|
94
98
|
},
|
|
95
99
|
{
|
|
96
100
|
label: "Style Profile",
|
|
97
101
|
description: "Your taste, palette & sizing",
|
|
98
|
-
icon: /* @__PURE__ */
|
|
102
|
+
icon: /* @__PURE__ */ O(D, { className: "h-6 w-6" }),
|
|
99
103
|
path: "/profile",
|
|
100
104
|
color: "bg-accent-blue-subtle text-accent-blue"
|
|
101
105
|
},
|
|
102
106
|
{
|
|
103
107
|
label: "Discover",
|
|
104
108
|
description: "A feed tuned to your taste",
|
|
105
|
-
icon: /* @__PURE__ */
|
|
109
|
+
icon: /* @__PURE__ */ O(_, { className: "h-6 w-6" }),
|
|
106
110
|
path: "/discover",
|
|
107
111
|
color: "bg-[var(--color-accent-soft)] text-text-primary"
|
|
108
112
|
},
|
|
109
113
|
{
|
|
110
114
|
label: "Trends",
|
|
111
115
|
description: "Live signals from platform activity",
|
|
112
|
-
icon: /* @__PURE__ */
|
|
116
|
+
icon: /* @__PURE__ */ O(E, { className: "h-6 w-6" }),
|
|
113
117
|
path: "/trends",
|
|
114
118
|
color: "bg-status-success-bg-subtle text-status-success-text"
|
|
115
119
|
}
|
|
116
120
|
];
|
|
117
121
|
function F() {
|
|
118
|
-
let { navigateTo:
|
|
119
|
-
let e =
|
|
122
|
+
let { navigateTo: g, currentUser: _ } = e(), y = c(), x = d(), E = u(), D = l(), F = f(), I = _?.firstName ?? _?.name ?? "there", L = y.isLoading || x.isLoading || E.isLoading || D.isLoading || F.isLoading, R = y.isError && x.isError && E.isError && D.isError, z = m(() => {
|
|
123
|
+
let e = y.data?.items ?? [], t = x.data?.items ?? [], n = E.data?.items ?? [], r = D.data ?? [], i = F.data ?? [];
|
|
120
124
|
return {
|
|
121
125
|
totalBrands: e.length,
|
|
122
126
|
publishedBrands: e.filter((e) => e.status === "PUBLISHED").length,
|
|
@@ -128,226 +132,201 @@ function F() {
|
|
|
128
132
|
wardrobeItems: i.length
|
|
129
133
|
};
|
|
130
134
|
}, [
|
|
131
|
-
|
|
135
|
+
y.data,
|
|
136
|
+
x.data,
|
|
137
|
+
E.data,
|
|
132
138
|
D.data,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
]), V = h(() => [...O.data?.items ?? []].slice(0, 6), [O.data]), H = h(() => [...S.data?.items ?? []].sort((e, t) => new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime()).slice(0, 4), [S.data]);
|
|
137
|
-
if (R) return /* @__PURE__ */ k(n, {
|
|
139
|
+
F.data
|
|
140
|
+
]), B = m(() => [...E.data?.items ?? []].slice(0, 5), [E.data]), V = m(() => [...y.data?.items ?? []].sort((e, t) => new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime()).slice(0, 4), [y.data]);
|
|
141
|
+
if (L) return /* @__PURE__ */ O(n, {
|
|
138
142
|
title: "CoolAndLovely",
|
|
139
|
-
children: /* @__PURE__ */
|
|
143
|
+
children: /* @__PURE__ */ O(o, { label: "Loading your style command center…" })
|
|
140
144
|
});
|
|
141
|
-
if (
|
|
145
|
+
if (R) return /* @__PURE__ */ O(n, {
|
|
142
146
|
title: "CoolAndLovely",
|
|
143
|
-
children: /* @__PURE__ */
|
|
147
|
+
children: /* @__PURE__ */ O(a, {
|
|
144
148
|
title: "Could not load CoolAndLovely",
|
|
145
149
|
message: "The fashion service did not respond. Please try again.",
|
|
146
150
|
onRetry: () => {
|
|
147
|
-
|
|
151
|
+
y.refetch(), x.refetch(), E.refetch(), D.refetch(), F.refetch();
|
|
148
152
|
}
|
|
149
153
|
})
|
|
150
154
|
});
|
|
151
|
-
let
|
|
155
|
+
let H = [
|
|
152
156
|
{
|
|
153
|
-
icon: /* @__PURE__ */
|
|
154
|
-
value:
|
|
157
|
+
icon: /* @__PURE__ */ O(T, { className: "h-5 w-5" }),
|
|
158
|
+
value: z.totalBrands.toString(),
|
|
155
159
|
label: "Brands",
|
|
156
|
-
sub: `${
|
|
160
|
+
sub: `${z.publishedBrands} published`
|
|
157
161
|
},
|
|
158
162
|
{
|
|
159
|
-
icon: /* @__PURE__ */
|
|
160
|
-
value:
|
|
163
|
+
icon: /* @__PURE__ */ O(S, { className: "h-5 w-5" }),
|
|
164
|
+
value: z.totalStyleItems.toString(),
|
|
161
165
|
label: "Style Items",
|
|
162
|
-
sub: `${
|
|
166
|
+
sub: `${z.activeStyleItems} active`
|
|
163
167
|
},
|
|
164
168
|
{
|
|
165
|
-
icon: /* @__PURE__ */
|
|
166
|
-
value:
|
|
169
|
+
icon: /* @__PURE__ */ O(C, { className: "h-5 w-5" }),
|
|
170
|
+
value: z.totalLooks.toString(),
|
|
167
171
|
label: "Looks",
|
|
168
|
-
sub: `${
|
|
172
|
+
sub: `${z.featuredLooks} featured`
|
|
169
173
|
},
|
|
170
174
|
{
|
|
171
|
-
icon: /* @__PURE__ */
|
|
172
|
-
value:
|
|
175
|
+
icon: /* @__PURE__ */ O(v, { className: "h-5 w-5" }),
|
|
176
|
+
value: z.activeCollaborations.toString(),
|
|
173
177
|
label: "Active Collabs",
|
|
174
|
-
sub: `${
|
|
178
|
+
sub: `${z.wardrobeItems} in wardrobe`
|
|
175
179
|
}
|
|
176
180
|
];
|
|
177
|
-
return /* @__PURE__ */
|
|
178
|
-
title:
|
|
181
|
+
return /* @__PURE__ */ k(n, {
|
|
182
|
+
title: `${M()}, ${I}`,
|
|
179
183
|
description: "Your fashion & lifestyle command center",
|
|
180
184
|
hero: !0,
|
|
181
185
|
children: [
|
|
182
|
-
/* @__PURE__ */
|
|
186
|
+
/* @__PURE__ */ O(j, {
|
|
183
187
|
className: "mb-6",
|
|
184
188
|
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."
|
|
185
189
|
}),
|
|
186
|
-
/* @__PURE__ */ A
|
|
190
|
+
/* @__PURE__ */ k(A, {
|
|
187
191
|
className: "mb-8",
|
|
188
|
-
children: [/* @__PURE__ */
|
|
192
|
+
children: [/* @__PURE__ */ O("h2", {
|
|
189
193
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
190
194
|
children: "At a glance"
|
|
191
|
-
}), /* @__PURE__ */
|
|
195
|
+
}), /* @__PURE__ */ O("div", {
|
|
192
196
|
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
193
|
-
children:
|
|
197
|
+
children: H.map((e, t) => /* @__PURE__ */ O(r, {
|
|
194
198
|
icon: e.icon,
|
|
195
199
|
value: e.value,
|
|
196
200
|
label: e.label,
|
|
197
201
|
trendValue: e.sub,
|
|
198
|
-
trend: "neutral"
|
|
202
|
+
trend: "neutral",
|
|
203
|
+
featured: t === 0
|
|
199
204
|
}, e.label))
|
|
200
205
|
})]
|
|
201
206
|
}),
|
|
202
|
-
/* @__PURE__ */
|
|
207
|
+
/* @__PURE__ */ k("section", {
|
|
203
208
|
className: "mb-8",
|
|
204
|
-
children: [/* @__PURE__ */
|
|
209
|
+
children: [/* @__PURE__ */ O("h2", {
|
|
205
210
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
206
211
|
children: "Quick actions"
|
|
207
|
-
}), /* @__PURE__ */
|
|
212
|
+
}), /* @__PURE__ */ O("div", {
|
|
208
213
|
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
209
|
-
children: N.map((e) => /* @__PURE__ */
|
|
214
|
+
children: N.map((e) => /* @__PURE__ */ k("button", {
|
|
210
215
|
type: "button",
|
|
211
|
-
onClick: () =>
|
|
216
|
+
onClick: () => g(e.path),
|
|
212
217
|
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"),
|
|
213
218
|
children: [
|
|
214
|
-
/* @__PURE__ */
|
|
219
|
+
/* @__PURE__ */ O("div", {
|
|
215
220
|
className: t("flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", e.color),
|
|
216
221
|
children: e.icon
|
|
217
222
|
}),
|
|
218
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ k("div", {
|
|
219
224
|
className: "min-w-0 flex-1",
|
|
220
|
-
children: [/* @__PURE__ */
|
|
225
|
+
children: [/* @__PURE__ */ O("p", {
|
|
221
226
|
className: "text-sm font-semibold text-text-primary",
|
|
222
227
|
children: e.label
|
|
223
|
-
}), /* @__PURE__ */
|
|
228
|
+
}), /* @__PURE__ */ O("p", {
|
|
224
229
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
225
230
|
children: e.description
|
|
226
231
|
})]
|
|
227
232
|
}),
|
|
228
|
-
/* @__PURE__ */
|
|
233
|
+
/* @__PURE__ */ O(b, { className: "mt-1 h-4 w-4 shrink-0 text-text-secondary" })
|
|
229
234
|
]
|
|
230
235
|
}, e.label))
|
|
231
236
|
})]
|
|
232
237
|
}),
|
|
233
|
-
/* @__PURE__ */
|
|
238
|
+
/* @__PURE__ */ k("section", {
|
|
234
239
|
className: "mb-8",
|
|
235
|
-
children: [/* @__PURE__ */
|
|
240
|
+
children: [/* @__PURE__ */ O("h2", {
|
|
236
241
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
237
242
|
children: "Explore"
|
|
238
|
-
}), /* @__PURE__ */
|
|
243
|
+
}), /* @__PURE__ */ O("div", {
|
|
239
244
|
className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4",
|
|
240
|
-
children: P.map((e) => /* @__PURE__ */
|
|
245
|
+
children: P.map((e) => /* @__PURE__ */ k("button", {
|
|
241
246
|
type: "button",
|
|
242
|
-
onClick: () =>
|
|
247
|
+
onClick: () => g(e.path),
|
|
243
248
|
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",
|
|
244
|
-
children: [/* @__PURE__ */
|
|
249
|
+
children: [/* @__PURE__ */ O("div", {
|
|
245
250
|
className: t("flex h-12 w-12 items-center justify-center rounded-full", e.color),
|
|
246
251
|
children: e.icon
|
|
247
|
-
}), /* @__PURE__ */
|
|
252
|
+
}), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
|
|
248
253
|
className: "text-sm font-semibold text-text-primary",
|
|
249
254
|
children: e.label
|
|
250
|
-
}), /* @__PURE__ */
|
|
255
|
+
}), /* @__PURE__ */ O("p", {
|
|
251
256
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
252
257
|
children: e.description
|
|
253
258
|
})] })]
|
|
254
259
|
}, e.label))
|
|
255
260
|
})]
|
|
256
261
|
}),
|
|
257
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ k("section", {
|
|
258
263
|
className: "mb-8",
|
|
259
|
-
children: [/* @__PURE__ */
|
|
264
|
+
children: [/* @__PURE__ */ k("div", {
|
|
260
265
|
className: "mb-4 flex items-center justify-between",
|
|
261
|
-
children: [/* @__PURE__ */
|
|
266
|
+
children: [/* @__PURE__ */ O("h2", {
|
|
262
267
|
className: "text-lg font-semibold text-text-primary",
|
|
263
268
|
children: "Latest looks"
|
|
264
|
-
}), /* @__PURE__ */
|
|
269
|
+
}), /* @__PURE__ */ k("button", {
|
|
265
270
|
type: "button",
|
|
266
|
-
onClick: () =>
|
|
271
|
+
onClick: () => g("/looks"),
|
|
267
272
|
className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
268
|
-
children: ["View all ", /* @__PURE__ */
|
|
273
|
+
children: ["View all ", /* @__PURE__ */ O(h, { className: "h-3.5 w-3.5" })]
|
|
269
274
|
})]
|
|
270
|
-
}),
|
|
275
|
+
}), B.length === 0 ? /* @__PURE__ */ O("p", {
|
|
271
276
|
className: "rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary",
|
|
272
277
|
children: "No looks published yet. Be the first to publish a shop-the-look outfit."
|
|
273
|
-
}) : /* @__PURE__ */
|
|
274
|
-
className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-
|
|
275
|
-
children:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
alt: e.title,
|
|
284
|
-
className: "h-28 w-full"
|
|
285
|
-
}), /* @__PURE__ */ A("div", {
|
|
286
|
-
className: "p-3",
|
|
287
|
-
children: [/* @__PURE__ */ k("p", {
|
|
288
|
-
className: "truncate text-sm font-medium text-text-primary",
|
|
289
|
-
children: e.title
|
|
290
|
-
}), /* @__PURE__ */ A("div", {
|
|
291
|
-
className: "mt-1.5 flex items-center justify-between",
|
|
292
|
-
children: [/* @__PURE__ */ k("span", {
|
|
293
|
-
className: "text-xs text-text-secondary",
|
|
294
|
-
children: m(e.occasion)
|
|
295
|
-
}), /* @__PURE__ */ A("span", {
|
|
296
|
-
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
297
|
-
children: [
|
|
298
|
-
/* @__PURE__ */ k(b, { className: "h-3 w-3" }),
|
|
299
|
-
" ",
|
|
300
|
-
e.likeCount
|
|
301
|
-
]
|
|
302
|
-
})]
|
|
303
|
-
})]
|
|
304
|
-
})]
|
|
278
|
+
}) : /* @__PURE__ */ O("div", {
|
|
279
|
+
className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5",
|
|
280
|
+
children: B.map((e) => /* @__PURE__ */ O(p, {
|
|
281
|
+
id: e.id,
|
|
282
|
+
title: e.title,
|
|
283
|
+
occasion: e.occasion,
|
|
284
|
+
imageFileId: e.image?.id,
|
|
285
|
+
likeCount: e.likeCount,
|
|
286
|
+
onOpen: () => g(`/looks/${e.id}`),
|
|
287
|
+
showActions: !0
|
|
305
288
|
}, e.id))
|
|
306
289
|
})]
|
|
307
290
|
}),
|
|
308
|
-
|
|
291
|
+
V.length > 0 && /* @__PURE__ */ k("section", { children: [/* @__PURE__ */ k("div", {
|
|
309
292
|
className: "mb-4 flex items-center justify-between",
|
|
310
|
-
children: [/* @__PURE__ */
|
|
293
|
+
children: [/* @__PURE__ */ O("h2", {
|
|
311
294
|
className: "text-lg font-semibold text-text-primary",
|
|
312
295
|
children: "Recently updated brands"
|
|
313
|
-
}), /* @__PURE__ */
|
|
296
|
+
}), /* @__PURE__ */ k("button", {
|
|
314
297
|
type: "button",
|
|
315
|
-
onClick: () =>
|
|
298
|
+
onClick: () => g("/brands"),
|
|
316
299
|
className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
317
|
-
children: ["View all ", /* @__PURE__ */
|
|
300
|
+
children: ["View all ", /* @__PURE__ */ O(h, { className: "h-3.5 w-3.5" })]
|
|
318
301
|
})]
|
|
319
|
-
}), /* @__PURE__ */
|
|
320
|
-
className: "
|
|
321
|
-
children:
|
|
302
|
+
}), /* @__PURE__ */ O("div", {
|
|
303
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
304
|
+
children: V.map((e) => /* @__PURE__ */ k("button", {
|
|
322
305
|
type: "button",
|
|
323
|
-
onClick: () =>
|
|
324
|
-
className: "flex
|
|
306
|
+
onClick: () => g(`/brands/${e.id}`),
|
|
307
|
+
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",
|
|
325
308
|
children: [
|
|
326
|
-
/* @__PURE__ */
|
|
327
|
-
icon: /* @__PURE__ */
|
|
309
|
+
/* @__PURE__ */ O(s, {
|
|
310
|
+
icon: /* @__PURE__ */ O(T, { className: "h-5 w-5" }),
|
|
328
311
|
seed: e.id,
|
|
329
312
|
fileId: e.logo?.id ?? e.cover?.id,
|
|
330
313
|
alt: e.name,
|
|
331
|
-
className: "h-11 w-11 shrink-0"
|
|
314
|
+
className: "h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface"
|
|
332
315
|
}),
|
|
333
|
-
/* @__PURE__ */
|
|
316
|
+
/* @__PURE__ */ k("div", {
|
|
334
317
|
className: "min-w-0 flex-1",
|
|
335
|
-
children: [/* @__PURE__ */
|
|
336
|
-
className: "flex items-center gap-
|
|
337
|
-
children: [/* @__PURE__ */
|
|
318
|
+
children: [/* @__PURE__ */ k("div", {
|
|
319
|
+
className: "flex items-center gap-1.5",
|
|
320
|
+
children: [/* @__PURE__ */ O("p", {
|
|
338
321
|
className: "truncate text-sm font-medium text-text-primary",
|
|
339
322
|
children: e.name
|
|
340
|
-
}), e.isFeatured && /* @__PURE__ */
|
|
341
|
-
}), /* @__PURE__ */
|
|
323
|
+
}), e.isFeatured && /* @__PURE__ */ O(w, { className: "h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text" })]
|
|
324
|
+
}), /* @__PURE__ */ k("p", {
|
|
342
325
|
className: "truncate text-xs text-text-secondary",
|
|
343
326
|
children: ["@", e.handle]
|
|
344
327
|
})]
|
|
345
328
|
}),
|
|
346
|
-
/* @__PURE__ */
|
|
347
|
-
/* @__PURE__ */ k("span", {
|
|
348
|
-
className: "hidden shrink-0 text-xs text-text-secondary sm:inline",
|
|
349
|
-
children: p(e.updatedAt)
|
|
350
|
-
})
|
|
329
|
+
/* @__PURE__ */ O(i, { status: e.status })
|
|
351
330
|
]
|
|
352
331
|
}, e.id))
|
|
353
332
|
})] })
|
|
@@ -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 { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useBrands, useStyleItems, useLooks, useCollaborations, useWardrobeItems } from '../hooks';\nimport { formatRelativeTime, titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\nimport type { DashboardStats } from '../types';\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, 6), [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={`Hello, ${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) => (\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 />\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-6\">\n {latestLooks.map((look) => (\n <button\n key={look.id}\n type=\"button\"\n onClick={() => navigateTo(`/looks/${look.id}`)}\n className=\"group overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Sparkles className=\"h-7 w-7\" />}\n seed={look.id}\n fileId={look.image?.id}\n alt={look.title}\n className=\"h-28 w-full\"\n />\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{look.title}</p>\n <div className=\"mt-1.5 flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">{titleCase(look.occasion)}</span>\n <span className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Heart className=\"h-3 w-3\" /> {look.likeCount}\n </span>\n </div>\n </div>\n </button>\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=\"divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"flex w-full items-center gap-4 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50\"\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\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\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 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 <span className=\"hidden shrink-0 text-xs text-text-secondary sm:inline\">\n {formatRelativeTime(brand.updatedAt)}\n </span>\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 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 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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"}
|