@burdenoff/microfe-coolandlovely 2026.625.2 → 2026.628.1
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 +66 -122
- package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoutes.js +87 -175
- package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
- package/dist/coolandlovely/components/Field.js +81 -0
- package/dist/coolandlovely/components/Field.js.map +1 -0
- package/dist/coolandlovely/components/StateViews.js +34 -0
- package/dist/coolandlovely/components/StateViews.js.map +1 -0
- package/dist/coolandlovely/components/StatusBadge.js +25 -22
- package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
- package/dist/coolandlovely/components/TagList.js +21 -0
- package/dist/coolandlovely/components/TagList.js.map +1 -0
- package/dist/coolandlovely/components/Thumbnail.js +29 -0
- package/dist/coolandlovely/components/Thumbnail.js.map +1 -0
- package/dist/coolandlovely/context/CoolAndLovelyContext.js +10 -10
- package/dist/coolandlovely/context/CoolAndLovelyContext.js.map +1 -1
- package/dist/coolandlovely/hooks/index.js +1 -0
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +462 -169
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +251 -845
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +319 -0
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +190 -0
- package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +158 -0
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +253 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +206 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +129 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +217 -0
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js +217 -0
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +122 -0
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +230 -0
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +276 -0
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js +170 -0
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +124 -0
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +156 -0
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -0
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +238 -0
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/looks/LookFormPage.js +209 -0
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/looks/LooksListPage.js +181 -0
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -0
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +224 -0
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +173 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +172 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -0
- package/dist/coolandlovely/store/coolandlovelyStore.js +2 -6
- package/dist/coolandlovely/store/coolandlovelyStore.js.map +1 -1
- package/dist/coolandlovely/utils/formatters.js +31 -23
- package/dist/coolandlovely/utils/formatters.js.map +1 -1
- package/dist/coolandlovely/utils/icons.js +34 -0
- package/dist/coolandlovely/utils/icons.js.map +1 -0
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/dist/coolandlovely/components/InvestmentCard.js +0 -98
- package/dist/coolandlovely/components/InvestmentCard.js.map +0 -1
- package/dist/coolandlovely/components/RatingDisplay.js +0 -35
- package/dist/coolandlovely/components/RatingDisplay.js.map +0 -1
- package/dist/coolandlovely/mocks/data.js +0 -1517
- package/dist/coolandlovely/mocks/data.js.map +0 -1
- package/dist/coolandlovely/mocks/users.js +0 -34
- package/dist/coolandlovely/mocks/users.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminAnalyticsPage.js +0 -651
- package/dist/coolandlovely/pages/admin/AdminAnalyticsPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminDashboard.js +0 -673
- package/dist/coolandlovely/pages/admin/AdminDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminFinancialPage.js +0 -427
- package/dist/coolandlovely/pages/admin/AdminFinancialPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminOrdersPage.js +0 -665
- package/dist/coolandlovely/pages/admin/AdminOrdersPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminProfilePage.js +0 -264
- package/dist/coolandlovely/pages/admin/AdminProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminSettingsPage.js +0 -654
- package/dist/coolandlovely/pages/admin/AdminSettingsPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/CartManagementPage.js +0 -874
- package/dist/coolandlovely/pages/admin/CartManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/UserManagementPage.js +0 -898
- package/dist/coolandlovely/pages/admin/UserManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/AnalyticsDashboard.js +0 -842
- package/dist/coolandlovely/pages/cart-owner/AnalyticsDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/BusinessSettingsPage.js +0 -705
- package/dist/coolandlovely/pages/cart-owner/BusinessSettingsPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/CartOwnerDashboard.js +0 -802
- package/dist/coolandlovely/pages/cart-owner/CartOwnerDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/CartOwnerProfilePage.js +0 -891
- package/dist/coolandlovely/pages/cart-owner/CartOwnerProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/CartServiceConfigPage.js +0 -639
- package/dist/coolandlovely/pages/cart-owner/CartServiceConfigPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/InventoryManagementPage.js +0 -914
- package/dist/coolandlovely/pages/cart-owner/InventoryManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/OrderManagementPage.js +0 -882
- package/dist/coolandlovely/pages/cart-owner/OrderManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/StaffManagementPage.js +0 -831
- package/dist/coolandlovely/pages/cart-owner/StaffManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/common/HelpSupportPage.js +0 -1325
- package/dist/coolandlovely/pages/common/HelpSupportPage.js.map +0 -1
- package/dist/coolandlovely/pages/common/NotificationCenterPage.js +0 -671
- package/dist/coolandlovely/pages/common/NotificationCenterPage.js.map +0 -1
- package/dist/coolandlovely/pages/common/SettingsPage.js +0 -973
- package/dist/coolandlovely/pages/common/SettingsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/BookingDetailsPage.js +0 -630
- package/dist/coolandlovely/pages/customer/BookingDetailsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CartDetailPage.js +0 -623
- package/dist/coolandlovely/pages/customer/CartDetailPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CartDiscoveryPage.js +0 -558
- package/dist/coolandlovely/pages/customer/CartDiscoveryPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CheckoutPage.js +0 -529
- package/dist/coolandlovely/pages/customer/CheckoutPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CustomerDashboard.js +0 -538
- package/dist/coolandlovely/pages/customer/CustomerDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CustomerProfilePage.js +0 -658
- package/dist/coolandlovely/pages/customer/CustomerProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/DirectServiceBookingPage.js +0 -632
- package/dist/coolandlovely/pages/customer/DirectServiceBookingPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/FavoritesPage.js +0 -487
- package/dist/coolandlovely/pages/customer/FavoritesPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/OrderHistoryPage.js +0 -447
- package/dist/coolandlovely/pages/customer/OrderHistoryPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/OrderTrackingPage.js +0 -425
- package/dist/coolandlovely/pages/customer/OrderTrackingPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/PaymentMethodsPage.js +0 -494
- package/dist/coolandlovely/pages/customer/PaymentMethodsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/ReviewsPage.js +0 -616
- package/dist/coolandlovely/pages/customer/ReviewsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/SavedAddressesPage.js +0 -449
- package/dist/coolandlovely/pages/customer/SavedAddressesPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestmentDetailsPage.js +0 -887
- package/dist/coolandlovely/pages/investor/InvestmentDetailsPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestmentOpportunitiesPage.js +0 -945
- package/dist/coolandlovely/pages/investor/InvestmentOpportunitiesPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorAnalyticsPage.js +0 -687
- package/dist/coolandlovely/pages/investor/InvestorAnalyticsPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorDashboard.js +0 -455
- package/dist/coolandlovely/pages/investor/InvestorDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorInvestmentsPage.js +0 -405
- package/dist/coolandlovely/pages/investor/InvestorInvestmentsPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorPortfolioPage.js +0 -602
- package/dist/coolandlovely/pages/investor/InvestorPortfolioPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorProfilePage.js +0 -1070
- package/dist/coolandlovely/pages/investor/InvestorProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/TaskDetailsPage.js +0 -533
- package/dist/coolandlovely/pages/worker/TaskDetailsPage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerDashboard.js +0 -671
- package/dist/coolandlovely/pages/worker/WorkerDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerOrdersPage.js +0 -680
- package/dist/coolandlovely/pages/worker/WorkerOrdersPage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerProfilePage.js +0 -692
- package/dist/coolandlovely/pages/worker/WorkerProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerSchedulePage.js +0 -583
- package/dist/coolandlovely/pages/worker/WorkerSchedulePage.js.map +0 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { cn as t } from "../../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
+
import { useCreateLook as r, useStyleItems as i } from "../../hooks/useCoolAndLovelyApi.js";
|
|
5
|
+
import "../../hooks/index.js";
|
|
6
|
+
import { formatPrice as a, parseCsv as o, titleCase as s } from "../../utils/formatters.js";
|
|
7
|
+
import { SelectField as c, TextAreaField as l, TextField as u } from "../../components/Field.js";
|
|
8
|
+
import { useMemo as d, useState as f } from "react";
|
|
9
|
+
import { ArrowLeft as p, Loader2 as m, Plus as h, Save as g, Sparkles as _, X as v } from "lucide-react";
|
|
10
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
11
|
+
import { GlassCard as x } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/coolandlovely/pages/looks/LookFormPage.tsx
|
|
13
|
+
var S = [
|
|
14
|
+
"EVERYDAY",
|
|
15
|
+
"WORK",
|
|
16
|
+
"EVENING",
|
|
17
|
+
"WEDDING_GUEST",
|
|
18
|
+
"VACATION",
|
|
19
|
+
"PARTY",
|
|
20
|
+
"ACTIVE",
|
|
21
|
+
"OTHER"
|
|
22
|
+
], C = [
|
|
23
|
+
"PUBLIC",
|
|
24
|
+
"FOLLOWERS",
|
|
25
|
+
"PRIVATE"
|
|
26
|
+
];
|
|
27
|
+
function w() {
|
|
28
|
+
let { navigateTo: w } = e(), T = r(), E = i({
|
|
29
|
+
status: "ACTIVE",
|
|
30
|
+
pagination: {
|
|
31
|
+
limit: 100,
|
|
32
|
+
offset: 0
|
|
33
|
+
}
|
|
34
|
+
}), [D, O] = f(""), [k, A] = f(""), [j, M] = f("EVERYDAY"), [N, P] = f("PUBLIC"), [F, I] = f(""), [L, R] = f([]), [z, B] = f(), V = d(() => (E.data?.items ?? []).map((e) => ({
|
|
35
|
+
value: e.id,
|
|
36
|
+
label: `${e.name}${e.brand?.name ? ` · ${e.brand.name}` : ""} — ${a(e.price, e.currency)}`
|
|
37
|
+
})), [E.data]), H = S.map((e) => ({
|
|
38
|
+
value: e,
|
|
39
|
+
label: s(e)
|
|
40
|
+
})), U = C.map((e) => ({
|
|
41
|
+
value: e,
|
|
42
|
+
label: s(e)
|
|
43
|
+
})), W = () => R((e) => [...e, {
|
|
44
|
+
styleItemId: "",
|
|
45
|
+
styling: ""
|
|
46
|
+
}]), G = (e) => R((t) => t.filter((t, n) => n !== e)), K = (e, t) => R((n) => n.map((n, r) => r === e ? {
|
|
47
|
+
...n,
|
|
48
|
+
...t
|
|
49
|
+
} : n));
|
|
50
|
+
return /* @__PURE__ */ y(n, {
|
|
51
|
+
title: "New look",
|
|
52
|
+
description: "Compose a shop-the-look outfit from catalogue pieces",
|
|
53
|
+
actions: /* @__PURE__ */ b("button", {
|
|
54
|
+
type: "button",
|
|
55
|
+
onClick: () => w("/looks"),
|
|
56
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
57
|
+
children: [/* @__PURE__ */ y(p, { className: "h-4 w-4" }), " Cancel"]
|
|
58
|
+
}),
|
|
59
|
+
children: /* @__PURE__ */ y("form", {
|
|
60
|
+
onSubmit: (e) => {
|
|
61
|
+
if (e.preventDefault(), !D.trim()) {
|
|
62
|
+
B("Title is required");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
B(void 0);
|
|
66
|
+
let t = L.filter((e) => e.styleItemId).map((e, t) => ({
|
|
67
|
+
styleItemId: e.styleItemId,
|
|
68
|
+
position: t,
|
|
69
|
+
styling: e.styling.trim() || void 0
|
|
70
|
+
}));
|
|
71
|
+
T.mutate({
|
|
72
|
+
title: D.trim(),
|
|
73
|
+
caption: k.trim() || void 0,
|
|
74
|
+
occasion: j,
|
|
75
|
+
visibility: N,
|
|
76
|
+
tags: o(F),
|
|
77
|
+
items: t.length > 0 ? t : void 0
|
|
78
|
+
}, { onSuccess: (e) => w(`/looks/${e.id}`) });
|
|
79
|
+
},
|
|
80
|
+
className: "mx-auto max-w-2xl",
|
|
81
|
+
children: /* @__PURE__ */ b(x, {
|
|
82
|
+
treatment: "plain",
|
|
83
|
+
className: "p-6",
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ b("div", {
|
|
86
|
+
className: "mb-6 flex items-center gap-3",
|
|
87
|
+
children: [/* @__PURE__ */ y("div", {
|
|
88
|
+
className: "flex h-11 w-11 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text",
|
|
89
|
+
children: /* @__PURE__ */ y(_, { className: "h-5 w-5" })
|
|
90
|
+
}), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h2", {
|
|
91
|
+
className: "text-base font-semibold text-text-primary",
|
|
92
|
+
children: "Look details"
|
|
93
|
+
}), /* @__PURE__ */ y("p", {
|
|
94
|
+
className: "text-xs text-text-secondary",
|
|
95
|
+
children: "Created as a draft — publish it from the look page."
|
|
96
|
+
})] })]
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ y(u, {
|
|
99
|
+
id: "title",
|
|
100
|
+
label: "Title",
|
|
101
|
+
value: D,
|
|
102
|
+
onChange: O,
|
|
103
|
+
placeholder: "Coastal grandma, refined",
|
|
104
|
+
required: !0,
|
|
105
|
+
error: z
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ y(l, {
|
|
108
|
+
id: "caption",
|
|
109
|
+
label: "Caption",
|
|
110
|
+
value: k,
|
|
111
|
+
onChange: A,
|
|
112
|
+
placeholder: "How to style it…",
|
|
113
|
+
rows: 3,
|
|
114
|
+
className: "mt-4"
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ b("div", {
|
|
117
|
+
className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
118
|
+
children: [/* @__PURE__ */ y(c, {
|
|
119
|
+
id: "occasion",
|
|
120
|
+
label: "Occasion",
|
|
121
|
+
value: j,
|
|
122
|
+
onChange: (e) => M(e),
|
|
123
|
+
options: H
|
|
124
|
+
}), /* @__PURE__ */ y(c, {
|
|
125
|
+
id: "visibility",
|
|
126
|
+
label: "Visibility",
|
|
127
|
+
value: N,
|
|
128
|
+
onChange: (e) => P(e),
|
|
129
|
+
options: U
|
|
130
|
+
})]
|
|
131
|
+
}),
|
|
132
|
+
/* @__PURE__ */ y(u, {
|
|
133
|
+
id: "tags",
|
|
134
|
+
label: "Tags",
|
|
135
|
+
value: F,
|
|
136
|
+
onChange: I,
|
|
137
|
+
placeholder: "linen, neutral",
|
|
138
|
+
hint: "Comma-separated",
|
|
139
|
+
className: "mt-4"
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ b("div", {
|
|
142
|
+
className: "mt-6",
|
|
143
|
+
children: [/* @__PURE__ */ b("div", {
|
|
144
|
+
className: "mb-2 flex items-center justify-between",
|
|
145
|
+
children: [/* @__PURE__ */ y("p", {
|
|
146
|
+
className: "text-sm font-medium text-text-primary",
|
|
147
|
+
children: "Pieces in this look"
|
|
148
|
+
}), /* @__PURE__ */ b("button", {
|
|
149
|
+
type: "button",
|
|
150
|
+
onClick: W,
|
|
151
|
+
className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
152
|
+
children: [/* @__PURE__ */ y(h, { className: "h-3.5 w-3.5" }), " Add piece"]
|
|
153
|
+
})]
|
|
154
|
+
}), L.length === 0 ? /* @__PURE__ */ y("p", {
|
|
155
|
+
className: "rounded-lg border border-dashed border-border-seam p-4 text-center text-xs text-text-secondary",
|
|
156
|
+
children: "No pieces added yet. A look can be published without pieces and styled later."
|
|
157
|
+
}) : /* @__PURE__ */ y("div", {
|
|
158
|
+
className: "space-y-3",
|
|
159
|
+
children: L.map((e, n) => /* @__PURE__ */ y("div", {
|
|
160
|
+
className: "rounded-lg border border-border-seam bg-bg-surface p-3",
|
|
161
|
+
children: /* @__PURE__ */ b("div", {
|
|
162
|
+
className: "flex items-start gap-2",
|
|
163
|
+
children: [/* @__PURE__ */ b("div", {
|
|
164
|
+
className: "flex-1 space-y-2",
|
|
165
|
+
children: [/* @__PURE__ */ y(c, {
|
|
166
|
+
label: `Piece ${n + 1}`,
|
|
167
|
+
value: e.styleItemId,
|
|
168
|
+
onChange: (e) => K(n, { styleItemId: e }),
|
|
169
|
+
options: V,
|
|
170
|
+
placeholder: E.isLoading ? "Loading items…" : "Select a style item"
|
|
171
|
+
}), /* @__PURE__ */ y(u, {
|
|
172
|
+
label: "Styling note",
|
|
173
|
+
value: e.styling,
|
|
174
|
+
onChange: (e) => K(n, { styling: e }),
|
|
175
|
+
placeholder: "Cuff the sleeves, tuck the front…"
|
|
176
|
+
})]
|
|
177
|
+
}), /* @__PURE__ */ y("button", {
|
|
178
|
+
type: "button",
|
|
179
|
+
onClick: () => G(n),
|
|
180
|
+
"aria-label": "Remove piece",
|
|
181
|
+
className: t("mt-7 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg", "text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text"),
|
|
182
|
+
children: /* @__PURE__ */ y(v, { className: "h-4 w-4" })
|
|
183
|
+
})]
|
|
184
|
+
})
|
|
185
|
+
}, n))
|
|
186
|
+
})]
|
|
187
|
+
}),
|
|
188
|
+
T.isError && /* @__PURE__ */ y("p", {
|
|
189
|
+
className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
190
|
+
children: T.error instanceof Error ? T.error.message : "Something went wrong"
|
|
191
|
+
}),
|
|
192
|
+
/* @__PURE__ */ y("div", {
|
|
193
|
+
className: "mt-6 flex justify-end gap-2",
|
|
194
|
+
children: /* @__PURE__ */ b("button", {
|
|
195
|
+
type: "submit",
|
|
196
|
+
disabled: T.isPending,
|
|
197
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
198
|
+
children: [T.isPending ? /* @__PURE__ */ y(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ y(g, { className: "h-4 w-4" }), "Create look"]
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
]
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
export { w as LookFormPage };
|
|
208
|
+
|
|
209
|
+
//# sourceMappingURL=LookFormPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookFormPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/looks/LookFormPage.tsx"],"sourcesContent":["/**\n * Look Create Page\n * @module coolandlovely/pages/looks\n *\n * Drives `cooCreateLook`. A look is created with its title/occasion/visibility\n * and an inline list of look items (catalogue pieces + styling notes), matching\n * the `CooCreateLookInput` shape exactly.\n */\n\nimport { useMemo, useState } from 'react';\nimport { ArrowLeft, Loader2, Save, Sparkles, Plus, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { TextField, TextAreaField, SelectField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useCreateLook, useStyleItems } from '../../hooks';\nimport { parseCsv, titleCase, formatPrice } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type {\n CooLookOccasion,\n CooVisibility,\n CooLookItemInput,\n} from '../../types';\n\nconst OCCASIONS: CooLookOccasion[] = [\n 'EVERYDAY',\n 'WORK',\n 'EVENING',\n 'WEDDING_GUEST',\n 'VACATION',\n 'PARTY',\n 'ACTIVE',\n 'OTHER',\n];\n\nconst VISIBILITIES: CooVisibility[] = ['PUBLIC', 'FOLLOWERS', 'PRIVATE'];\n\ninterface DraftItem {\n styleItemId: string;\n styling: string;\n}\n\nexport function LookFormPage() {\n const { navigateTo } = useCoolAndLovely();\n const createLook = useCreateLook();\n const styleItems = useStyleItems({\n status: 'ACTIVE',\n pagination: { limit: 100, offset: 0 },\n });\n\n const [title, setTitle] = useState('');\n const [caption, setCaption] = useState('');\n const [occasion, setOccasion] = useState<CooLookOccasion>('EVERYDAY');\n const [visibility, setVisibility] = useState<CooVisibility>('PUBLIC');\n const [tags, setTags] = useState('');\n const [draftItems, setDraftItems] = useState<DraftItem[]>([]);\n const [titleError, setTitleError] = useState<string | undefined>();\n\n const itemOptions = useMemo(\n () =>\n (styleItems.data?.items ?? []).map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''} — ${formatPrice(i.price, i.currency)}`,\n })),\n [styleItems.data]\n );\n\n const occasionOptions = OCCASIONS.map((o) => ({ value: o, label: titleCase(o) }));\n const visibilityOptions = VISIBILITIES.map((v) => ({ value: v, label: titleCase(v) }));\n\n const addDraftItem = () => setDraftItems((d) => [...d, { styleItemId: '', styling: '' }]);\n const removeDraftItem = (idx: number) =>\n setDraftItems((d) => d.filter((_, i) => i !== idx));\n const setDraftItem = (idx: number, patch: Partial<DraftItem>) =>\n setDraftItems((d) => d.map((it, i) => (i === idx ? { ...it, ...patch } : it)));\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!title.trim()) {\n setTitleError('Title is required');\n return;\n }\n setTitleError(undefined);\n\n const items: CooLookItemInput[] = draftItems\n .filter((d) => d.styleItemId)\n .map((d, idx) => ({\n styleItemId: d.styleItemId,\n position: idx,\n styling: d.styling.trim() || undefined,\n }));\n\n createLook.mutate(\n {\n title: title.trim(),\n caption: caption.trim() || undefined,\n occasion,\n visibility,\n tags: parseCsv(tags),\n items: items.length > 0 ? items : undefined,\n },\n { onSuccess: (look) => navigateTo(`/looks/${look.id}`) }\n );\n };\n\n return (\n <PageLayout\n title=\"New look\"\n description=\"Compose a shop-the-look outfit from catalogue pieces\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text\">\n <Sparkles className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Look details</h2>\n <p className=\"text-xs text-text-secondary\">\n Created as a draft — publish it from the look page.\n </p>\n </div>\n </div>\n\n <TextField\n id=\"title\"\n label=\"Title\"\n value={title}\n onChange={setTitle}\n placeholder=\"Coastal grandma, refined\"\n required\n error={titleError}\n />\n\n <TextAreaField\n id=\"caption\"\n label=\"Caption\"\n value={caption}\n onChange={setCaption}\n placeholder=\"How to style it…\"\n rows={3}\n className=\"mt-4\"\n />\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <SelectField\n id=\"occasion\"\n label=\"Occasion\"\n value={occasion}\n onChange={(v) => setOccasion(v as CooLookOccasion)}\n options={occasionOptions}\n />\n <SelectField\n id=\"visibility\"\n label=\"Visibility\"\n value={visibility}\n onChange={(v) => setVisibility(v as CooVisibility)}\n options={visibilityOptions}\n />\n </div>\n\n <TextField\n id=\"tags\"\n label=\"Tags\"\n value={tags}\n onChange={setTags}\n placeholder=\"linen, neutral\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n\n {/* Look items */}\n <div className=\"mt-6\">\n <div className=\"mb-2 flex items-center justify-between\">\n <p className=\"text-sm font-medium text-text-primary\">Pieces in this look</p>\n <button\n type=\"button\"\n onClick={addDraftItem}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n <Plus className=\"h-3.5 w-3.5\" /> Add piece\n </button>\n </div>\n {draftItems.length === 0 ? (\n <p className=\"rounded-lg border border-dashed border-border-seam p-4 text-center text-xs text-text-secondary\">\n No pieces added yet. A look can be published without pieces and styled later.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {draftItems.map((di, idx) => (\n <div\n key={idx}\n className=\"rounded-lg border border-border-seam bg-bg-surface p-3\"\n >\n <div className=\"flex items-start gap-2\">\n <div className=\"flex-1 space-y-2\">\n <SelectField\n label={`Piece ${idx + 1}`}\n value={di.styleItemId}\n onChange={(v) => setDraftItem(idx, { styleItemId: v })}\n options={itemOptions}\n placeholder={\n styleItems.isLoading ? 'Loading items…' : 'Select a style item'\n }\n />\n <TextField\n label=\"Styling note\"\n value={di.styling}\n onChange={(v) => setDraftItem(idx, { styling: v })}\n placeholder=\"Cuff the sleeves, tuck the front…\"\n />\n </div>\n <button\n type=\"button\"\n onClick={() => removeDraftItem(idx)}\n aria-label=\"Remove piece\"\n className={cn(\n 'mt-7 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg',\n 'text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text'\n )}\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {createLook.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {createLook.error instanceof Error ? createLook.error.message : 'Something went wrong'}\n </p>\n )}\n\n <div className=\"mt-6 flex justify-end gap-2\">\n <button\n type=\"submit\"\n disabled={createLook.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {createLook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Create look\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { cn as t } from "../../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
+
import { StatusBadge as r } from "../../components/StatusBadge.js";
|
|
5
|
+
import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
|
|
6
|
+
import { Thumbnail as o } from "../../components/Thumbnail.js";
|
|
7
|
+
import { useLooks as s } from "../../hooks/useCoolAndLovelyApi.js";
|
|
8
|
+
import "../../hooks/index.js";
|
|
9
|
+
import { titleCase as c } from "../../utils/formatters.js";
|
|
10
|
+
import { EmptyState as l } from "../../components/EmptyState.js";
|
|
11
|
+
import { useState as u } from "react";
|
|
12
|
+
import { Bookmark as d, ChevronLeft as f, ChevronRight as p, Heart as m, Plus as h, Sparkles as g, Star as _ } from "lucide-react";
|
|
13
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
14
|
+
//#region src/coolandlovely/pages/looks/LooksListPage.tsx
|
|
15
|
+
var x = 24, S = [
|
|
16
|
+
{
|
|
17
|
+
value: "ALL",
|
|
18
|
+
label: "All"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
value: "EVERYDAY",
|
|
22
|
+
label: "Everyday"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: "WORK",
|
|
26
|
+
label: "Work"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
value: "EVENING",
|
|
30
|
+
label: "Evening"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: "WEDDING_GUEST",
|
|
34
|
+
label: "Wedding guest"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
value: "VACATION",
|
|
38
|
+
label: "Vacation"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
value: "PARTY",
|
|
42
|
+
label: "Party"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
value: "ACTIVE",
|
|
46
|
+
label: "Active"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
value: "OTHER",
|
|
50
|
+
label: "Other"
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
function C() {
|
|
54
|
+
let { navigateTo: C } = e(), [w, T] = u("ALL"), [E, D] = u(!1), [O, k] = u(0), A = s({
|
|
55
|
+
occasion: w === "ALL" ? void 0 : w,
|
|
56
|
+
featured: E ? !0 : void 0,
|
|
57
|
+
pagination: {
|
|
58
|
+
limit: x,
|
|
59
|
+
offset: O
|
|
60
|
+
}
|
|
61
|
+
}), j = A.data?.items ?? [], M = A.data?.pageInfo.hasMore ?? !1;
|
|
62
|
+
return /* @__PURE__ */ b(n, {
|
|
63
|
+
title: "Looks",
|
|
64
|
+
description: "Shop-the-look outfits built from real catalogue pieces",
|
|
65
|
+
actions: /* @__PURE__ */ b("button", {
|
|
66
|
+
type: "button",
|
|
67
|
+
onClick: () => C("/looks/new"),
|
|
68
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
|
|
69
|
+
children: [/* @__PURE__ */ y(h, { className: "h-4 w-4" }), " New look"]
|
|
70
|
+
}),
|
|
71
|
+
children: [/* @__PURE__ */ b("div", {
|
|
72
|
+
className: "mb-6 flex flex-wrap items-center gap-2",
|
|
73
|
+
children: [S.map((e) => /* @__PURE__ */ y("button", {
|
|
74
|
+
type: "button",
|
|
75
|
+
onClick: () => {
|
|
76
|
+
T(e.value), k(0);
|
|
77
|
+
},
|
|
78
|
+
className: t("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", w === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
79
|
+
children: e.label
|
|
80
|
+
}, e.value)), /* @__PURE__ */ b("button", {
|
|
81
|
+
type: "button",
|
|
82
|
+
onClick: () => {
|
|
83
|
+
D((e) => !e), k(0);
|
|
84
|
+
},
|
|
85
|
+
className: t("ml-auto inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors", E ? "bg-status-warning-bg-subtle text-status-warning-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
86
|
+
children: [/* @__PURE__ */ y(_, { className: t("h-3.5 w-3.5", E && "fill-status-warning-text") }), " Featured"]
|
|
87
|
+
})]
|
|
88
|
+
}), A.isLoading ? /* @__PURE__ */ y(a, { label: "Loading looks…" }) : A.isError ? /* @__PURE__ */ y(i, { onRetry: () => void A.refetch() }) : j.length === 0 ? /* @__PURE__ */ y(l, {
|
|
89
|
+
illustration: "empty-data",
|
|
90
|
+
icon: /* @__PURE__ */ y(g, { className: "h-10 w-10" }),
|
|
91
|
+
title: "No looks yet",
|
|
92
|
+
description: "Publish a shop-the-look outfit from catalogue pieces.",
|
|
93
|
+
action: /* @__PURE__ */ b("button", {
|
|
94
|
+
type: "button",
|
|
95
|
+
onClick: () => C("/looks/new"),
|
|
96
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
97
|
+
children: [/* @__PURE__ */ y(h, { className: "h-4 w-4" }), " New look"]
|
|
98
|
+
})
|
|
99
|
+
}) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("div", {
|
|
100
|
+
className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4",
|
|
101
|
+
children: j.map((e) => /* @__PURE__ */ b("button", {
|
|
102
|
+
type: "button",
|
|
103
|
+
onClick: () => C(`/looks/${e.id}`),
|
|
104
|
+
className: "overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md",
|
|
105
|
+
children: [/* @__PURE__ */ b("div", {
|
|
106
|
+
className: "relative",
|
|
107
|
+
children: [
|
|
108
|
+
/* @__PURE__ */ y(o, {
|
|
109
|
+
icon: /* @__PURE__ */ y(g, { className: "h-9 w-9" }),
|
|
110
|
+
seed: e.id,
|
|
111
|
+
className: "h-40 w-full"
|
|
112
|
+
}),
|
|
113
|
+
e.isFeatured && /* @__PURE__ */ y("div", {
|
|
114
|
+
className: "absolute left-2 top-2",
|
|
115
|
+
children: /* @__PURE__ */ b("span", {
|
|
116
|
+
className: "inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text",
|
|
117
|
+
children: [/* @__PURE__ */ y(_, { className: "h-3 w-3 fill-status-warning-text" }), " Featured"]
|
|
118
|
+
})
|
|
119
|
+
}),
|
|
120
|
+
e.publishedAt === null && /* @__PURE__ */ y("div", {
|
|
121
|
+
className: "absolute right-2 top-2",
|
|
122
|
+
children: /* @__PURE__ */ y(r, { status: "draft" })
|
|
123
|
+
})
|
|
124
|
+
]
|
|
125
|
+
}), /* @__PURE__ */ b("div", {
|
|
126
|
+
className: "p-3",
|
|
127
|
+
children: [/* @__PURE__ */ y("p", {
|
|
128
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
129
|
+
children: e.title
|
|
130
|
+
}), /* @__PURE__ */ b("div", {
|
|
131
|
+
className: "mt-1.5 flex items-center justify-between text-xs text-text-secondary",
|
|
132
|
+
children: [/* @__PURE__ */ y("span", { children: c(e.occasion) }), /* @__PURE__ */ b("span", {
|
|
133
|
+
className: "flex items-center gap-2",
|
|
134
|
+
children: [/* @__PURE__ */ b("span", {
|
|
135
|
+
className: "flex items-center gap-1",
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ y(m, { className: "h-3 w-3" }),
|
|
138
|
+
" ",
|
|
139
|
+
e.likeCount
|
|
140
|
+
]
|
|
141
|
+
}), /* @__PURE__ */ b("span", {
|
|
142
|
+
className: "flex items-center gap-1",
|
|
143
|
+
children: [
|
|
144
|
+
/* @__PURE__ */ y(d, { className: "h-3 w-3" }),
|
|
145
|
+
" ",
|
|
146
|
+
e.saveCount
|
|
147
|
+
]
|
|
148
|
+
})]
|
|
149
|
+
})]
|
|
150
|
+
})]
|
|
151
|
+
})]
|
|
152
|
+
}, e.id))
|
|
153
|
+
}), (O > 0 || M) && /* @__PURE__ */ b("div", {
|
|
154
|
+
className: "mt-6 flex items-center justify-center gap-2",
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ b("button", {
|
|
157
|
+
type: "button",
|
|
158
|
+
disabled: O === 0,
|
|
159
|
+
onClick: () => k((e) => Math.max(0, e - x)),
|
|
160
|
+
className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40",
|
|
161
|
+
children: [/* @__PURE__ */ y(f, { className: "h-4 w-4" }), " Previous"]
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ b("span", {
|
|
164
|
+
className: "text-sm text-text-secondary",
|
|
165
|
+
children: ["Page ", O / x + 1]
|
|
166
|
+
}),
|
|
167
|
+
/* @__PURE__ */ b("button", {
|
|
168
|
+
type: "button",
|
|
169
|
+
disabled: !M,
|
|
170
|
+
onClick: () => k((e) => e + x),
|
|
171
|
+
className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40",
|
|
172
|
+
children: ["Next ", /* @__PURE__ */ y(p, { className: "h-4 w-4" })]
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
})] })]
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
export { C as LooksListPage };
|
|
180
|
+
|
|
181
|
+
//# sourceMappingURL=LooksListPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LooksListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/looks/LooksListPage.tsx"],"sourcesContent":["/**\n * Looks List Page\n * @module coolandlovely/pages/looks\n *\n * Cursor-paginated feed of published looks (`cooListLooks`) with occasion &\n * featured filters.\n */\n\nimport { useState } from 'react';\nimport { Plus, Heart, Bookmark, Sparkles, ChevronLeft, ChevronRight, Star } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLooks } from '../../hooks';\nimport { titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooLookOccasion } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst OCCASIONS: Array<{ value: CooLookOccasion | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'EVERYDAY', label: 'Everyday' },\n { value: 'WORK', label: 'Work' },\n { value: 'EVENING', label: 'Evening' },\n { value: 'WEDDING_GUEST', label: 'Wedding guest' },\n { value: 'VACATION', label: 'Vacation' },\n { value: 'PARTY', label: 'Party' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'OTHER', label: 'Other' },\n];\n\nexport function LooksListPage() {\n const { navigateTo } = useCoolAndLovely();\n const [occasion, setOccasion] = useState<CooLookOccasion | 'ALL'>('ALL');\n const [featuredOnly, setFeaturedOnly] = useState(false);\n const [offset, setOffset] = useState(0);\n\n const query = useLooks({\n occasion: occasion === 'ALL' ? undefined : occasion,\n featured: featuredOnly ? true : undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n const looks = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title=\"Looks\"\n description=\"Shop-the-look outfits built from real catalogue pieces\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" /> New look\n </button>\n }\n >\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n {OCCASIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => {\n setOccasion(opt.value);\n setOffset(0);\n }}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n occasion === opt.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {opt.label}\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => {\n setFeaturedOnly((v) => !v);\n setOffset(0);\n }}\n className={cn(\n 'ml-auto inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n featuredOnly\n ? 'bg-status-warning-bg-subtle text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n <Star className={cn('h-3.5 w-3.5', featuredOnly && 'fill-status-warning-text')} /> Featured\n </button>\n </div>\n\n {query.isLoading ? (\n <LoadingView label=\"Loading looks…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : looks.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Sparkles className=\"h-10 w-10\" />}\n title=\"No looks yet\"\n description=\"Publish a shop-the-look outfit from catalogue pieces.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <Plus className=\"h-4 w-4\" /> New look\n </button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {looks.map((look) => (\n <button\n key={look.id}\n type=\"button\"\n onClick={() => navigateTo(`/looks/${look.id}`)}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div className=\"relative\">\n <Thumbnail icon={<Sparkles className=\"h-9 w-9\" />} seed={look.id} className=\"h-40 w-full\" />\n {look.isFeatured && (\n <div className=\"absolute left-2 top-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\">\n <Star className=\"h-3 w-3 fill-status-warning-text\" /> Featured\n </span>\n </div>\n )}\n {look.publishedAt === null && (\n <div className=\"absolute right-2 top-2\">\n <StatusBadge status=\"draft\" />\n </div>\n )}\n </div>\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 text-xs text-text-secondary\">\n <span>{titleCase(look.occasion)}</span>\n <span className=\"flex items-center gap-2\">\n <span className=\"flex items-center gap-1\">\n <Heart className=\"h-3 w-3\" /> {look.likeCount}\n </span>\n <span className=\"flex items-center gap-1\">\n <Bookmark className=\"h-3 w-3\" /> {look.saveCount}\n </span>\n </span>\n </div>\n </div>\n </button>\n ))}\n </div>\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-center gap-2\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Previous\n </button>\n <span className=\"text-sm text-text-secondary\">Page {offset / PAGE_SIZE + 1}</span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|