@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,124 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { StatusBadge as n } from "../../components/StatusBadge.js";
|
|
4
|
+
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
|
+
import { useLookbook as a, useRemoveLookbookEntry as o } from "../../hooks/useCoolAndLovelyApi.js";
|
|
6
|
+
import "../../hooks/index.js";
|
|
7
|
+
import { TagList as s } from "../../components/TagList.js";
|
|
8
|
+
import { ArrowLeft as c, BookHeart as l, Loader2 as u, Sparkles as d, Tag as f, Trash2 as p } from "lucide-react";
|
|
9
|
+
import { useParams as m } from "react-router-dom";
|
|
10
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
11
|
+
import { GlassCard as _ } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx
|
|
13
|
+
function v() {
|
|
14
|
+
let { lookbookId: v } = m(), { navigateTo: y } = e(), b = a(v), x = o();
|
|
15
|
+
if (b.isLoading) return /* @__PURE__ */ h(t, {
|
|
16
|
+
title: "Lookbook",
|
|
17
|
+
children: /* @__PURE__ */ h(i, { label: "Loading lookbook…" })
|
|
18
|
+
});
|
|
19
|
+
if (b.isError || !b.data) return /* @__PURE__ */ h(t, {
|
|
20
|
+
title: "Lookbook",
|
|
21
|
+
children: /* @__PURE__ */ h(r, {
|
|
22
|
+
title: "Lookbook not found",
|
|
23
|
+
onRetry: () => void b.refetch()
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
let S = b.data, C = [...S.entries ?? []].sort((e, t) => e.position - t.position);
|
|
27
|
+
return /* @__PURE__ */ h(t, {
|
|
28
|
+
title: S.name,
|
|
29
|
+
description: S.description ?? void 0,
|
|
30
|
+
actions: /* @__PURE__ */ g("button", {
|
|
31
|
+
type: "button",
|
|
32
|
+
onClick: () => y("/lookbooks"),
|
|
33
|
+
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",
|
|
34
|
+
children: [/* @__PURE__ */ h(c, { className: "h-4 w-4" }), " Back"]
|
|
35
|
+
}),
|
|
36
|
+
children: /* @__PURE__ */ g("div", {
|
|
37
|
+
className: "mx-auto max-w-3xl",
|
|
38
|
+
children: [/* @__PURE__ */ g(_, {
|
|
39
|
+
treatment: "glass",
|
|
40
|
+
className: "p-6",
|
|
41
|
+
children: [/* @__PURE__ */ g("div", {
|
|
42
|
+
className: "flex items-center gap-3",
|
|
43
|
+
children: [/* @__PURE__ */ h("div", {
|
|
44
|
+
className: "flex h-12 w-12 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary",
|
|
45
|
+
children: /* @__PURE__ */ h(l, { className: "h-6 w-6" })
|
|
46
|
+
}), /* @__PURE__ */ g("div", {
|
|
47
|
+
className: "min-w-0 flex-1",
|
|
48
|
+
children: [/* @__PURE__ */ h("h2", {
|
|
49
|
+
className: "truncate text-xl font-semibold text-text-primary",
|
|
50
|
+
children: S.name
|
|
51
|
+
}), /* @__PURE__ */ h(n, {
|
|
52
|
+
status: S.visibility,
|
|
53
|
+
className: "mt-1"
|
|
54
|
+
})]
|
|
55
|
+
})]
|
|
56
|
+
}), S.tags.length > 0 && /* @__PURE__ */ h(s, {
|
|
57
|
+
tags: S.tags,
|
|
58
|
+
className: "mt-4"
|
|
59
|
+
})]
|
|
60
|
+
}), /* @__PURE__ */ g("section", {
|
|
61
|
+
className: "mt-6",
|
|
62
|
+
children: [/* @__PURE__ */ g("h3", {
|
|
63
|
+
className: "mb-3 text-lg font-semibold text-text-primary",
|
|
64
|
+
children: [
|
|
65
|
+
"Entries (",
|
|
66
|
+
C.length,
|
|
67
|
+
")"
|
|
68
|
+
]
|
|
69
|
+
}), C.length === 0 ? /* @__PURE__ */ h("p", {
|
|
70
|
+
className: "rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary",
|
|
71
|
+
children: "This lookbook has no entries yet. Clip a catalogue piece or save a look to add one."
|
|
72
|
+
}) : /* @__PURE__ */ h("div", {
|
|
73
|
+
className: "divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface",
|
|
74
|
+
children: C.map((e) => {
|
|
75
|
+
let t = !!e.lookId;
|
|
76
|
+
return /* @__PURE__ */ g("div", {
|
|
77
|
+
className: "flex items-center gap-4 px-4 py-3",
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ h("div", {
|
|
80
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary",
|
|
81
|
+
children: h(t ? d : f, { className: "h-5 w-5" })
|
|
82
|
+
}),
|
|
83
|
+
/* @__PURE__ */ g("div", {
|
|
84
|
+
className: "min-w-0 flex-1",
|
|
85
|
+
children: [/* @__PURE__ */ h("p", {
|
|
86
|
+
className: "text-sm font-medium text-text-primary",
|
|
87
|
+
children: t ? "Saved look" : "Clipped piece"
|
|
88
|
+
}), e.note && /* @__PURE__ */ h("p", {
|
|
89
|
+
className: "truncate text-xs text-text-secondary",
|
|
90
|
+
children: e.note
|
|
91
|
+
})]
|
|
92
|
+
}),
|
|
93
|
+
t && e.lookId && /* @__PURE__ */ h("button", {
|
|
94
|
+
type: "button",
|
|
95
|
+
onClick: () => y(`/looks/${e.lookId}`),
|
|
96
|
+
className: "shrink-0 text-xs font-medium text-primary hover:underline",
|
|
97
|
+
children: "View"
|
|
98
|
+
}),
|
|
99
|
+
!t && e.styleItemId && /* @__PURE__ */ h("button", {
|
|
100
|
+
type: "button",
|
|
101
|
+
onClick: () => y(`/items/${e.styleItemId}`),
|
|
102
|
+
className: "shrink-0 text-xs font-medium text-primary hover:underline",
|
|
103
|
+
children: "View"
|
|
104
|
+
}),
|
|
105
|
+
/* @__PURE__ */ h("button", {
|
|
106
|
+
type: "button",
|
|
107
|
+
disabled: x.isPending,
|
|
108
|
+
onClick: () => x.mutate(e.id),
|
|
109
|
+
"aria-label": "Remove entry",
|
|
110
|
+
className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-lg text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-50",
|
|
111
|
+
children: x.isPending && x.variables === e.id ? /* @__PURE__ */ h(u, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ h(p, { className: "h-3.5 w-3.5" })
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
}, e.id);
|
|
115
|
+
})
|
|
116
|
+
})]
|
|
117
|
+
})]
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { v as LookbookDetailPage };
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=LookbookDetailPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookbookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx"],"sourcesContent":["/**\n * Lookbook Detail Page\n * @module coolandlovely/pages/lookbooks\n *\n * Single lookbook (`cooLookbook`) showing its clipped entries, with per-entry\n * removal (`cooRemoveLookbookEntry`).\n */\n\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, BookHeart, Sparkles, Tag, Trash2, Loader2 } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLookbook, useRemoveLookbookEntry } from '../../hooks';\n\nexport function LookbookDetailPage() {\n const { lookbookId } = useParams<{ lookbookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbook(lookbookId);\n const removeEntry = useRemoveLookbookEntry();\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Lookbook\">\n <LoadingView label=\"Loading lookbook…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Lookbook\">\n <ErrorView title=\"Lookbook not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const lookbook = query.data;\n const entries = [...(lookbook.entries ?? [])].sort((a, b) => a.position - b.position);\n\n return (\n <PageLayout\n title={lookbook.name}\n description={lookbook.description ?? undefined}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/lookbooks')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n <div className=\"mx-auto max-w-3xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <BookHeart className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">{lookbook.name}</h2>\n <StatusBadge status={lookbook.visibility} className=\"mt-1\" />\n </div>\n </div>\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} className=\"mt-4\" />}\n </GlassCard>\n\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">\n Entries ({entries.length})\n </h3>\n {entries.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n This lookbook has no entries yet. Clip a catalogue piece or save a look to add one.\n </p>\n ) : (\n <div className=\"divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface\">\n {entries.map((entry) => {\n const isLook = Boolean(entry.lookId);\n return (\n <div key={entry.id} className=\"flex items-center gap-4 px-4 py-3\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary\">\n {isLook ? <Sparkles className=\"h-5 w-5\" /> : <Tag className=\"h-5 w-5\" />}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {isLook ? 'Saved look' : 'Clipped piece'}\n </p>\n {entry.note && (\n <p className=\"truncate text-xs text-text-secondary\">{entry.note}</p>\n )}\n </div>\n {isLook && entry.lookId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/looks/${entry.lookId}`)}\n className=\"shrink-0 text-xs font-medium text-primary hover:underline\"\n >\n View\n </button>\n )}\n {!isLook && entry.styleItemId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/items/${entry.styleItemId}`)}\n className=\"shrink-0 text-xs font-medium text-primary hover:underline\"\n >\n View\n </button>\n )}\n <button\n type=\"button\"\n disabled={removeEntry.isPending}\n onClick={() => removeEntry.mutate(entry.id)}\n aria-label=\"Remove entry\"\n className=\"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-lg text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-50\"\n >\n {removeEntry.isPending && removeEntry.variables === entry.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { StatusBadge as n } from "../../components/StatusBadge.js";
|
|
4
|
+
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
|
+
import { Thumbnail as a } from "../../components/Thumbnail.js";
|
|
6
|
+
import { useCreateLookbook as o, useLookbooks as s } from "../../hooks/useCoolAndLovelyApi.js";
|
|
7
|
+
import "../../hooks/index.js";
|
|
8
|
+
import { parseCsv as c, titleCase as l } from "../../utils/formatters.js";
|
|
9
|
+
import { TagList as u } from "../../components/TagList.js";
|
|
10
|
+
import { SelectField as d, TextAreaField as f, TextField as p } from "../../components/Field.js";
|
|
11
|
+
import { EmptyState as m } from "../../components/EmptyState.js";
|
|
12
|
+
import { useState as h } from "react";
|
|
13
|
+
import { BookHeart as g, Loader2 as _, Plus as v, X as y } from "lucide-react";
|
|
14
|
+
import { jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
15
|
+
import { GlassCard as S } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
//#region src/coolandlovely/pages/lookbooks/LookbooksListPage.tsx
|
|
17
|
+
var C = [
|
|
18
|
+
"PRIVATE",
|
|
19
|
+
"FOLLOWERS",
|
|
20
|
+
"PUBLIC"
|
|
21
|
+
];
|
|
22
|
+
function w() {
|
|
23
|
+
let { navigateTo: w } = e(), T = s(), E = o(), [D, O] = h(!1), [k, A] = h(""), [j, M] = h(""), [N, P] = h("PRIVATE"), [F, I] = h(""), [L, R] = h(), z = T.data ?? [], B = C.map((e) => ({
|
|
24
|
+
value: e,
|
|
25
|
+
label: l(e)
|
|
26
|
+
}));
|
|
27
|
+
return /* @__PURE__ */ x(t, {
|
|
28
|
+
title: "Lookbooks",
|
|
29
|
+
description: "Themed boards of saved catalogue pieces and looks",
|
|
30
|
+
actions: /* @__PURE__ */ x("button", {
|
|
31
|
+
type: "button",
|
|
32
|
+
onClick: () => O((e) => !e),
|
|
33
|
+
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",
|
|
34
|
+
children: [b(D ? y : v, { className: "h-4 w-4" }), D ? "Close" : "New lookbook"]
|
|
35
|
+
}),
|
|
36
|
+
children: [D && /* @__PURE__ */ b("form", {
|
|
37
|
+
onSubmit: (e) => {
|
|
38
|
+
if (e.preventDefault(), !k.trim()) {
|
|
39
|
+
R("Name is required");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
R(void 0), E.mutate({
|
|
43
|
+
name: k.trim(),
|
|
44
|
+
description: j.trim() || void 0,
|
|
45
|
+
visibility: N,
|
|
46
|
+
tags: c(F)
|
|
47
|
+
}, { onSuccess: (e) => {
|
|
48
|
+
O(!1), A(""), M(""), I(""), P("PRIVATE"), w(`/lookbooks/${e.id}`);
|
|
49
|
+
} });
|
|
50
|
+
},
|
|
51
|
+
className: "mb-6",
|
|
52
|
+
children: /* @__PURE__ */ x(S, {
|
|
53
|
+
treatment: "plain",
|
|
54
|
+
className: "p-5",
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ x("h2", {
|
|
57
|
+
className: "mb-4 flex items-center gap-2 text-base font-semibold text-text-primary",
|
|
58
|
+
children: [/* @__PURE__ */ b(g, { className: "h-5 w-5" }), " New lookbook"]
|
|
59
|
+
}),
|
|
60
|
+
/* @__PURE__ */ x("div", {
|
|
61
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
62
|
+
children: [/* @__PURE__ */ b(p, {
|
|
63
|
+
label: "Name",
|
|
64
|
+
value: k,
|
|
65
|
+
onChange: A,
|
|
66
|
+
placeholder: "Summer wedding guest",
|
|
67
|
+
required: !0,
|
|
68
|
+
error: L
|
|
69
|
+
}), /* @__PURE__ */ b(d, {
|
|
70
|
+
label: "Visibility",
|
|
71
|
+
value: N,
|
|
72
|
+
onChange: (e) => P(e),
|
|
73
|
+
options: B
|
|
74
|
+
})]
|
|
75
|
+
}),
|
|
76
|
+
/* @__PURE__ */ b(f, {
|
|
77
|
+
label: "Description",
|
|
78
|
+
value: j,
|
|
79
|
+
onChange: M,
|
|
80
|
+
placeholder: "What this board is for…",
|
|
81
|
+
rows: 2,
|
|
82
|
+
className: "mt-4"
|
|
83
|
+
}),
|
|
84
|
+
/* @__PURE__ */ b(p, {
|
|
85
|
+
label: "Tags",
|
|
86
|
+
value: F,
|
|
87
|
+
onChange: I,
|
|
88
|
+
placeholder: "summer, formal",
|
|
89
|
+
hint: "Comma-separated",
|
|
90
|
+
className: "mt-4"
|
|
91
|
+
}),
|
|
92
|
+
E.isError && /* @__PURE__ */ b("p", {
|
|
93
|
+
className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
94
|
+
children: E.error instanceof Error ? E.error.message : "Something went wrong"
|
|
95
|
+
}),
|
|
96
|
+
/* @__PURE__ */ b("div", {
|
|
97
|
+
className: "mt-4 flex justify-end",
|
|
98
|
+
children: /* @__PURE__ */ x("button", {
|
|
99
|
+
type: "submit",
|
|
100
|
+
disabled: E.isPending,
|
|
101
|
+
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",
|
|
102
|
+
children: [E.isPending ? /* @__PURE__ */ b(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ b(v, { className: "h-4 w-4" }), "Create lookbook"]
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
})
|
|
107
|
+
}), T.isLoading ? /* @__PURE__ */ b(i, { label: "Loading lookbooks…" }) : T.isError ? /* @__PURE__ */ b(r, { onRetry: () => void T.refetch() }) : z.length === 0 ? /* @__PURE__ */ b(m, {
|
|
108
|
+
illustration: "empty-data",
|
|
109
|
+
icon: /* @__PURE__ */ b(g, { className: "h-10 w-10" }),
|
|
110
|
+
title: "No lookbooks yet",
|
|
111
|
+
description: "Create a themed board to clip catalogue pieces and looks.",
|
|
112
|
+
action: /* @__PURE__ */ x("button", {
|
|
113
|
+
type: "button",
|
|
114
|
+
onClick: () => O(!0),
|
|
115
|
+
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",
|
|
116
|
+
children: [/* @__PURE__ */ b(v, { className: "h-4 w-4" }), " New lookbook"]
|
|
117
|
+
})
|
|
118
|
+
}) : /* @__PURE__ */ b("div", {
|
|
119
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
120
|
+
children: z.map((e) => /* @__PURE__ */ x("button", {
|
|
121
|
+
type: "button",
|
|
122
|
+
onClick: () => w(`/lookbooks/${e.id}`),
|
|
123
|
+
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",
|
|
124
|
+
children: [/* @__PURE__ */ b(a, {
|
|
125
|
+
icon: /* @__PURE__ */ b(g, { className: "h-9 w-9" }),
|
|
126
|
+
seed: e.id,
|
|
127
|
+
className: "h-28 w-full"
|
|
128
|
+
}), /* @__PURE__ */ x("div", {
|
|
129
|
+
className: "p-4",
|
|
130
|
+
children: [
|
|
131
|
+
/* @__PURE__ */ x("div", {
|
|
132
|
+
className: "flex items-start justify-between gap-2",
|
|
133
|
+
children: [/* @__PURE__ */ b("h3", {
|
|
134
|
+
className: "truncate text-base font-semibold text-text-primary",
|
|
135
|
+
children: e.name
|
|
136
|
+
}), /* @__PURE__ */ b(n, { status: e.visibility })]
|
|
137
|
+
}),
|
|
138
|
+
e.description && /* @__PURE__ */ b("p", {
|
|
139
|
+
className: "mt-1.5 line-clamp-2 text-sm text-text-secondary",
|
|
140
|
+
children: e.description
|
|
141
|
+
}),
|
|
142
|
+
e.tags.length > 0 && /* @__PURE__ */ b(u, {
|
|
143
|
+
tags: e.tags,
|
|
144
|
+
max: 4,
|
|
145
|
+
className: "mt-3"
|
|
146
|
+
})
|
|
147
|
+
]
|
|
148
|
+
})]
|
|
149
|
+
}, e.id))
|
|
150
|
+
})]
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
export { w as LookbooksListPage };
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=LookbooksListPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookbooksListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbooksListPage.tsx"],"sourcesContent":["/**\n * Lookbooks List Page\n * @module coolandlovely/pages/lookbooks\n *\n * Lists themed lookbook boards (`cooListLookbooks`) and supports creating one\n * inline (`cooCreateLookbook`).\n */\n\nimport { useState } from 'react';\nimport { Plus, BookHeart, Loader2, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\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 { TagList } from '../../components/TagList';\nimport { TextField, TextAreaField, SelectField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLookbooks, useCreateLookbook } from '../../hooks';\nimport { parseCsv, titleCase } from '../../utils/formatters';\nimport type { CooVisibility } from '../../types';\n\nconst VISIBILITIES: CooVisibility[] = ['PRIVATE', 'FOLLOWERS', 'PUBLIC'];\n\nexport function LookbooksListPage() {\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbooks();\n const createLookbook = useCreateLookbook();\n\n const [creating, setCreating] = useState(false);\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [visibility, setVisibility] = useState<CooVisibility>('PRIVATE');\n const [tags, setTags] = useState('');\n const [nameError, setNameError] = useState<string | undefined>();\n\n const lookbooks = query.data ?? [];\n const visibilityOptions = VISIBILITIES.map((v) => ({ value: v, label: titleCase(v) }));\n\n const handleCreate = (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) {\n setNameError('Name is required');\n return;\n }\n setNameError(undefined);\n createLookbook.mutate(\n {\n name: name.trim(),\n description: description.trim() || undefined,\n visibility,\n tags: parseCsv(tags),\n },\n {\n onSuccess: (lb) => {\n setCreating(false);\n setName('');\n setDescription('');\n setTags('');\n setVisibility('PRIVATE');\n navigateTo(`/lookbooks/${lb.id}`);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title=\"Lookbooks\"\n description=\"Themed boards of saved catalogue pieces and looks\"\n actions={\n <button\n type=\"button\"\n onClick={() => setCreating((v) => !v)}\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 {creating ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {creating ? 'Close' : 'New lookbook'}\n </button>\n }\n >\n {creating && (\n <form onSubmit={handleCreate} className=\"mb-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h2 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <BookHeart className=\"h-5 w-5\" /> New lookbook\n </h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n label=\"Name\"\n value={name}\n onChange={setName}\n placeholder=\"Summer wedding guest\"\n required\n error={nameError}\n />\n <SelectField\n label=\"Visibility\"\n value={visibility}\n onChange={(v) => setVisibility(v as CooVisibility)}\n options={visibilityOptions}\n />\n </div>\n <TextAreaField\n label=\"Description\"\n value={description}\n onChange={setDescription}\n placeholder=\"What this board is for…\"\n rows={2}\n className=\"mt-4\"\n />\n <TextField\n label=\"Tags\"\n value={tags}\n onChange={setTags}\n placeholder=\"summer, formal\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n {createLookbook.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {createLookbook.error instanceof Error\n ? createLookbook.error.message\n : 'Something went wrong'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={createLookbook.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 {createLookbook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Create lookbook\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n {query.isLoading ? (\n <LoadingView label=\"Loading lookbooks…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : lookbooks.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BookHeart className=\"h-10 w-10\" />}\n title=\"No lookbooks yet\"\n description=\"Create a themed board to clip catalogue pieces and looks.\"\n action={\n <button\n type=\"button\"\n onClick={() => setCreating(true)}\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 lookbook\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {lookbooks.map((lb) => (\n <button\n key={lb.id}\n type=\"button\"\n onClick={() => navigateTo(`/lookbooks/${lb.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 <Thumbnail icon={<BookHeart className=\"h-9 w-9\" />} seed={lb.id} className=\"h-28 w-full\" />\n <div className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{lb.name}</h3>\n <StatusBadge status={lb.visibility} />\n </div>\n {lb.description && (\n <p className=\"mt-1.5 line-clamp-2 text-sm text-text-secondary\">{lb.description}</p>\n )}\n {lb.tags.length > 0 && <TagList tags={lb.tags} max={4} className=\"mt-3\" />}\n </div>\n </button>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,IAAM,IAAgC;CAAC;CAAW;CAAa;CAAS;AAExE,SAAgB,IAAoB;CAClC,IAAM,EAAE,kBAAe,GAAkB,EACnC,IAAQ,GAAc,EACtB,IAAiB,GAAmB,EAEpC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAY,KAAiB,EAAwB,UAAU,EAChE,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAW,KAAgB,GAA8B,EAE1D,IAAY,EAAM,QAAQ,EAAE,EAC5B,IAAoB,EAAa,KAAK,OAAO;EAAE,OAAO;EAAG,OAAO,EAAU,EAAE;EAAE,EAAE;AA6BtF,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAa,MAAM,CAAC,EAAE;GACrC,WAAU;aAHZ,CAKc,EAAX,IAAY,IAA4B,GAA7B,EAAG,WAAU,WAAY,CAA+B,EACnE,IAAW,UAAU,eACf;;YAXb,CAcG,KACC,kBAAC,QAAD;GAAM,WA3CU,MAAuB;AAE3C,QADA,EAAE,gBAAgB,EACd,CAAC,EAAK,MAAM,EAAE;AAChB,OAAa,mBAAmB;AAChC;;AAGF,IADA,EAAa,KAAA,EAAU,EACvB,EAAe,OACb;KACE,MAAM,EAAK,MAAM;KACjB,aAAa,EAAY,MAAM,IAAI,KAAA;KACnC;KACA,MAAM,EAAS,EAAK;KACrB,EACD,EACE,YAAY,MAAO;AAMjB,KALA,EAAY,GAAM,EAClB,EAAQ,GAAG,EACX,EAAe,GAAG,EAClB,EAAQ,GAAG,EACX,EAAc,UAAU,EACxB,EAAW,cAAc,EAAG,KAAK;OAEpC,CACF;;GAmBiC,WAAU;aACtC,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAd,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,gBAC9B;;KACL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,OAAM;OACN,OAAO;OACP,UAAU;OACV,aAAY;OACZ,UAAA;OACA,OAAO;OACP,CAAA,EACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO;OACP,WAAW,MAAM,EAAc,EAAmB;OAClD,SAAS;OACT,CAAA,CACE;;KACN,kBAAC,GAAD;MACE,OAAM;MACN,OAAO;MACP,UAAU;MACV,aAAY;MACZ,MAAM;MACN,WAAU;MACV,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO;MACP,UAAU;MACV,aAAY;MACZ,MAAK;MACL,WAAU;MACV,CAAA;KACD,EAAe,WACd,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAe,iBAAiB,QAC7B,EAAe,MAAM,UACrB;MACF,CAAA;KAEN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,EAAe;OACzB,WAAU;iBAHZ,CAKG,EAAe,YACd,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC5B,kBAEK;;MACL,CAAA;KACI;;GACP,CAAA,EAGR,EAAM,YACL,kBAAC,GAAD,EAAa,OAAM,sBAAuB,CAAA,GACxC,EAAM,UACR,kBAAC,GAAD,EAAW,eAAe,KAAK,EAAM,SAAS,EAAI,CAAA,GAChD,EAAU,WAAW,IACvB,kBAAC,GAAD;GACE,cAAa;GACb,MAAM,kBAAC,GAAD,EAAW,WAAU,aAAc,CAAA;GACzC,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAY,GAAK;IAChC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBACrB;;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAU,KAAK,MACd,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,EAAW,cAAc,EAAG,KAAK;IAChD,WAAU;cAJZ,CAME,kBAAC,GAAD;KAAW,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;KAAE,MAAM,EAAG;KAAI,WAAU;KAAgB,CAAA,EAC3F,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAAsD,EAAG;QAAU,CAAA,EACjF,kBAAC,GAAD,EAAa,QAAQ,EAAG,YAAc,CAAA,CAClC;;MACL,EAAG,eACF,kBAAC,KAAD;OAAG,WAAU;iBAAmD,EAAG;OAAgB,CAAA;MAEpF,EAAG,KAAK,SAAS,KAAK,kBAAC,GAAD;OAAS,MAAM,EAAG;OAAM,KAAK;OAAG,WAAU;OAAS,CAAA;MACtE;OACC;MAhBF,EAAG,GAgBD,CACT;GACE,CAAA,CAEG"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { StatusBadge as n } from "../../components/StatusBadge.js";
|
|
4
|
+
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
|
+
import { Thumbnail as a } from "../../components/Thumbnail.js";
|
|
6
|
+
import { useDeleteLook as o, useLikeLook as s, useLook as c, usePublishLook as l } from "../../hooks/useCoolAndLovelyApi.js";
|
|
7
|
+
import "../../hooks/index.js";
|
|
8
|
+
import { formatDate as u, formatPrice as d, titleCase as f } from "../../utils/formatters.js";
|
|
9
|
+
import { TagList as p } from "../../components/TagList.js";
|
|
10
|
+
import { ArrowLeft as m, Bookmark as h, Heart as g, Loader2 as _, Rocket as v, Sparkles as y, Star as b, Trash2 as x } from "lucide-react";
|
|
11
|
+
import { useParams as S } from "react-router-dom";
|
|
12
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
13
|
+
import { GlassCard as T } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
//#region src/coolandlovely/pages/looks/LookDetailPage.tsx
|
|
15
|
+
function E() {
|
|
16
|
+
let { lookId: E } = S(), { navigateTo: D } = e(), O = c(E), k = s(), A = l(), j = o();
|
|
17
|
+
if (O.isLoading) return /* @__PURE__ */ C(t, {
|
|
18
|
+
title: "Look",
|
|
19
|
+
children: /* @__PURE__ */ C(i, { label: "Loading look…" })
|
|
20
|
+
});
|
|
21
|
+
if (O.isError || !O.data) return /* @__PURE__ */ C(t, {
|
|
22
|
+
title: "Look",
|
|
23
|
+
children: /* @__PURE__ */ C(r, {
|
|
24
|
+
title: "Look not found",
|
|
25
|
+
onRetry: () => void O.refetch()
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
let M = O.data, N = M.items ?? [], P = M.publishedAt !== null;
|
|
29
|
+
return /* @__PURE__ */ C(t, {
|
|
30
|
+
title: M.title,
|
|
31
|
+
description: f(M.occasion),
|
|
32
|
+
actions: /* @__PURE__ */ w("div", {
|
|
33
|
+
className: "flex items-center gap-2",
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ w("button", {
|
|
36
|
+
type: "button",
|
|
37
|
+
onClick: () => D("/looks"),
|
|
38
|
+
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",
|
|
39
|
+
children: [/* @__PURE__ */ C(m, { className: "h-4 w-4" }), " Back"]
|
|
40
|
+
}),
|
|
41
|
+
!P && /* @__PURE__ */ w("button", {
|
|
42
|
+
type: "button",
|
|
43
|
+
disabled: A.isPending,
|
|
44
|
+
onClick: () => A.mutate(M.id),
|
|
45
|
+
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 disabled:opacity-50",
|
|
46
|
+
children: [A.isPending ? /* @__PURE__ */ C(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ C(v, { className: "h-4 w-4" }), "Publish"]
|
|
47
|
+
}),
|
|
48
|
+
/* @__PURE__ */ w("button", {
|
|
49
|
+
type: "button",
|
|
50
|
+
disabled: j.isPending,
|
|
51
|
+
onClick: () => j.mutate(M.id, { onSuccess: () => D("/looks") }),
|
|
52
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
|
|
53
|
+
children: [j.isPending ? /* @__PURE__ */ C(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ C(x, { className: "h-4 w-4" }), "Delete"]
|
|
54
|
+
})
|
|
55
|
+
]
|
|
56
|
+
}),
|
|
57
|
+
children: /* @__PURE__ */ w("div", {
|
|
58
|
+
className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
|
|
59
|
+
children: [/* @__PURE__ */ w("div", {
|
|
60
|
+
className: "lg:col-span-2",
|
|
61
|
+
children: [/* @__PURE__ */ w(T, {
|
|
62
|
+
treatment: "glass",
|
|
63
|
+
className: "overflow-hidden p-0",
|
|
64
|
+
children: [/* @__PURE__ */ C(a, {
|
|
65
|
+
icon: /* @__PURE__ */ C(y, { className: "h-14 w-14" }),
|
|
66
|
+
seed: M.id,
|
|
67
|
+
className: "h-60 w-full"
|
|
68
|
+
}), /* @__PURE__ */ w("div", {
|
|
69
|
+
className: "p-6",
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ w("div", {
|
|
72
|
+
className: "flex items-center gap-2",
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ C("h2", {
|
|
75
|
+
className: "text-xl font-semibold text-text-primary",
|
|
76
|
+
children: M.title
|
|
77
|
+
}),
|
|
78
|
+
M.isFeatured && /* @__PURE__ */ C(b, { className: "h-4 w-4 fill-status-warning-text text-status-warning-text" }),
|
|
79
|
+
/* @__PURE__ */ C(n, {
|
|
80
|
+
status: P ? "published" : "draft",
|
|
81
|
+
className: "ml-auto"
|
|
82
|
+
})
|
|
83
|
+
]
|
|
84
|
+
}),
|
|
85
|
+
M.caption && /* @__PURE__ */ C("p", {
|
|
86
|
+
className: "mt-3 text-sm text-text-secondary",
|
|
87
|
+
children: M.caption
|
|
88
|
+
}),
|
|
89
|
+
M.tags.length > 0 && /* @__PURE__ */ C(p, {
|
|
90
|
+
tags: M.tags,
|
|
91
|
+
className: "mt-4",
|
|
92
|
+
tone: "accent"
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ w("div", {
|
|
95
|
+
className: "mt-5 flex items-center gap-3",
|
|
96
|
+
children: [/* @__PURE__ */ w("button", {
|
|
97
|
+
type: "button",
|
|
98
|
+
disabled: k.isPending,
|
|
99
|
+
onClick: () => k.mutate(M.id),
|
|
100
|
+
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-status-error-bg-subtle disabled:opacity-50",
|
|
101
|
+
children: [
|
|
102
|
+
k.isPending ? /* @__PURE__ */ C(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ C(g, { className: "h-4 w-4" }),
|
|
103
|
+
"Like · ",
|
|
104
|
+
M.likeCount
|
|
105
|
+
]
|
|
106
|
+
}), /* @__PURE__ */ w("span", {
|
|
107
|
+
className: "inline-flex items-center gap-1.5 text-sm text-text-secondary",
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ C(h, { className: "h-4 w-4" }),
|
|
110
|
+
" ",
|
|
111
|
+
M.saveCount,
|
|
112
|
+
" saves"
|
|
113
|
+
]
|
|
114
|
+
})]
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
})]
|
|
118
|
+
}), /* @__PURE__ */ w("section", {
|
|
119
|
+
className: "mt-6",
|
|
120
|
+
children: [/* @__PURE__ */ w("h3", {
|
|
121
|
+
className: "mb-3 text-lg font-semibold text-text-primary",
|
|
122
|
+
children: [
|
|
123
|
+
"Shop the look (",
|
|
124
|
+
N.length,
|
|
125
|
+
")"
|
|
126
|
+
]
|
|
127
|
+
}), N.length === 0 ? /* @__PURE__ */ C("p", {
|
|
128
|
+
className: "rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary",
|
|
129
|
+
children: "No items tagged in this look yet."
|
|
130
|
+
}) : /* @__PURE__ */ C("div", {
|
|
131
|
+
className: "divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface",
|
|
132
|
+
children: [...N].sort((e, t) => e.position - t.position).map((e) => /* @__PURE__ */ w("button", {
|
|
133
|
+
type: "button",
|
|
134
|
+
onClick: () => e.styleItem && D(`/items/${e.styleItem.id}`),
|
|
135
|
+
className: "flex w-full items-center gap-4 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50",
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ C(a, {
|
|
138
|
+
icon: /* @__PURE__ */ C(y, { className: "h-5 w-5" }),
|
|
139
|
+
seed: e.id,
|
|
140
|
+
className: "h-11 w-11 shrink-0"
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ w("div", {
|
|
143
|
+
className: "min-w-0 flex-1",
|
|
144
|
+
children: [/* @__PURE__ */ C("p", {
|
|
145
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
146
|
+
children: e.styleItem?.name ?? "Catalogue piece"
|
|
147
|
+
}), e.styling && /* @__PURE__ */ C("p", {
|
|
148
|
+
className: "truncate text-xs text-text-secondary",
|
|
149
|
+
children: e.styling
|
|
150
|
+
})]
|
|
151
|
+
}),
|
|
152
|
+
e.styleItem && /* @__PURE__ */ C("span", {
|
|
153
|
+
className: "shrink-0 text-sm font-semibold text-text-primary",
|
|
154
|
+
children: d(e.styleItem.price, e.styleItem.currency)
|
|
155
|
+
})
|
|
156
|
+
]
|
|
157
|
+
}, e.id))
|
|
158
|
+
})]
|
|
159
|
+
})]
|
|
160
|
+
}), /* @__PURE__ */ w("div", {
|
|
161
|
+
className: "space-y-4",
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ w(T, {
|
|
164
|
+
treatment: "plain",
|
|
165
|
+
className: "p-5",
|
|
166
|
+
children: [/* @__PURE__ */ C("h3", {
|
|
167
|
+
className: "mb-3 text-sm font-semibold text-text-primary",
|
|
168
|
+
children: "Details"
|
|
169
|
+
}), /* @__PURE__ */ w("dl", {
|
|
170
|
+
className: "space-y-2 text-sm",
|
|
171
|
+
children: [
|
|
172
|
+
/* @__PURE__ */ w("div", {
|
|
173
|
+
className: "flex justify-between gap-2",
|
|
174
|
+
children: [/* @__PURE__ */ C("dt", {
|
|
175
|
+
className: "text-text-secondary",
|
|
176
|
+
children: "Occasion"
|
|
177
|
+
}), /* @__PURE__ */ C("dd", {
|
|
178
|
+
className: "text-text-primary",
|
|
179
|
+
children: f(M.occasion)
|
|
180
|
+
})]
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ w("div", {
|
|
183
|
+
className: "flex justify-between gap-2",
|
|
184
|
+
children: [/* @__PURE__ */ C("dt", {
|
|
185
|
+
className: "text-text-secondary",
|
|
186
|
+
children: "Visibility"
|
|
187
|
+
}), /* @__PURE__ */ C("dd", {
|
|
188
|
+
className: "text-text-primary",
|
|
189
|
+
children: f(M.visibility)
|
|
190
|
+
})]
|
|
191
|
+
}),
|
|
192
|
+
/* @__PURE__ */ w("div", {
|
|
193
|
+
className: "flex justify-between gap-2",
|
|
194
|
+
children: [/* @__PURE__ */ C("dt", {
|
|
195
|
+
className: "text-text-secondary",
|
|
196
|
+
children: "Published"
|
|
197
|
+
}), /* @__PURE__ */ C("dd", {
|
|
198
|
+
className: "text-text-primary",
|
|
199
|
+
children: M.publishedAt ? u(M.publishedAt, {
|
|
200
|
+
hour: void 0,
|
|
201
|
+
minute: void 0
|
|
202
|
+
}) : "—"
|
|
203
|
+
})]
|
|
204
|
+
})
|
|
205
|
+
]
|
|
206
|
+
})]
|
|
207
|
+
}),
|
|
208
|
+
M.collaborationId && /* @__PURE__ */ w("button", {
|
|
209
|
+
type: "button",
|
|
210
|
+
onClick: () => D(`/collaborations/${M.collaborationId}`),
|
|
211
|
+
className: "flex w-full items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken/50",
|
|
212
|
+
children: [/* @__PURE__ */ C("div", {
|
|
213
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-status-warning-bg-subtle text-status-warning-text",
|
|
214
|
+
children: /* @__PURE__ */ C(y, { className: "h-5 w-5" })
|
|
215
|
+
}), /* @__PURE__ */ w("div", {
|
|
216
|
+
className: "min-w-0",
|
|
217
|
+
children: [/* @__PURE__ */ C("p", {
|
|
218
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
219
|
+
children: "Part of a collaboration"
|
|
220
|
+
}), /* @__PURE__ */ C("p", {
|
|
221
|
+
className: "truncate text-xs text-text-secondary",
|
|
222
|
+
children: "View collaboration"
|
|
223
|
+
})]
|
|
224
|
+
})]
|
|
225
|
+
}),
|
|
226
|
+
(A.isError || j.isError || k.isError) && /* @__PURE__ */ C("p", {
|
|
227
|
+
className: "rounded-lg bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text",
|
|
228
|
+
children: (A.error ?? j.error ?? k.error) instanceof Error ? (A.error ?? j.error ?? k.error)?.message : "Action failed"
|
|
229
|
+
})
|
|
230
|
+
]
|
|
231
|
+
})]
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
export { E as LookDetailPage };
|
|
237
|
+
|
|
238
|
+
//# sourceMappingURL=LookDetailPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/looks/LookDetailPage.tsx"],"sourcesContent":["/**\n * Look Detail Page\n * @module coolandlovely/pages/looks\n *\n * Single look (`cooLook`) with its look items, like (`cooLikeLook`), publish\n * (`cooPublishLook`) and delete (`cooDeleteLook`) actions.\n */\n\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Heart,\n Bookmark,\n Rocket,\n Trash2,\n Loader2,\n Sparkles,\n Star,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLook, useLikeLook, usePublishLook, useDeleteLook } from '../../hooks';\nimport { formatPrice, formatDate, titleCase } from '../../utils/formatters';\n\nexport function LookDetailPage() {\n const { lookId } = useParams<{ lookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLook(lookId);\n const like = useLikeLook();\n const publish = usePublishLook();\n const remove = useDeleteLook();\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Look\">\n <LoadingView label=\"Loading look…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Look\">\n <ErrorView title=\"Look not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const look = query.data;\n const items = look.items ?? [];\n const isPublished = look.publishedAt !== null;\n\n return (\n <PageLayout\n title={look.title}\n description={titleCase(look.occasion)}\n actions={\n <div className=\"flex items-center gap-2\">\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\" /> Back\n </button>\n {!isPublished && (\n <button\n type=\"button\"\n disabled={publish.isPending}\n onClick={() => publish.mutate(look.id)}\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 disabled:opacity-50\"\n >\n {publish.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Rocket className=\"h-4 w-4\" />\n )}\n Publish\n </button>\n )}\n <button\n type=\"button\"\n disabled={remove.isPending}\n onClick={() => remove.mutate(look.id, { onSuccess: () => 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-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {remove.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Delete\n </button>\n </div>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">\n <GlassCard treatment=\"glass\" className=\"overflow-hidden p-0\">\n <Thumbnail icon={<Sparkles className=\"h-14 w-14\" />} seed={look.id} className=\"h-60 w-full\" />\n <div className=\"p-6\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{look.title}</h2>\n {look.isFeatured && (\n <Star className=\"h-4 w-4 fill-status-warning-text text-status-warning-text\" />\n )}\n <StatusBadge status={isPublished ? 'published' : 'draft'} className=\"ml-auto\" />\n </div>\n {look.caption && <p className=\"mt-3 text-sm text-text-secondary\">{look.caption}</p>}\n {look.tags.length > 0 && <TagList tags={look.tags} className=\"mt-4\" tone=\"accent\" />}\n\n <div className=\"mt-5 flex items-center gap-3\">\n <button\n type=\"button\"\n disabled={like.isPending}\n onClick={() => like.mutate(look.id)}\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-status-error-bg-subtle disabled:opacity-50\"\n >\n {like.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Heart className=\"h-4 w-4\" />\n )}\n Like · {look.likeCount}\n </button>\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Bookmark className=\"h-4 w-4\" /> {look.saveCount} saves\n </span>\n </div>\n </div>\n </GlassCard>\n\n {/* Shop the look */}\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">\n Shop the look ({items.length})\n </h3>\n {items.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n No items tagged in this look yet.\n </p>\n ) : (\n <div className=\"divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface\">\n {[...items]\n .sort((a, b) => a.position - b.position)\n .map((li) => (\n <button\n key={li.id}\n type=\"button\"\n onClick={() => li.styleItem && navigateTo(`/items/${li.styleItem.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 icon={<Sparkles className=\"h-5 w-5\" />} seed={li.id} className=\"h-11 w-11 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {li.styleItem?.name ?? 'Catalogue piece'}\n </p>\n {li.styling && (\n <p className=\"truncate text-xs text-text-secondary\">{li.styling}</p>\n )}\n </div>\n {li.styleItem && (\n <span className=\"shrink-0 text-sm font-semibold text-text-primary\">\n {formatPrice(li.styleItem.price, li.styleItem.currency)}\n </span>\n )}\n </button>\n ))}\n </div>\n )}\n </section>\n </div>\n\n <div className=\"space-y-4\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Details</h3>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Occasion</dt>\n <dd className=\"text-text-primary\">{titleCase(look.occasion)}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Visibility</dt>\n <dd className=\"text-text-primary\">{titleCase(look.visibility)}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Published</dt>\n <dd className=\"text-text-primary\">\n {look.publishedAt\n ? formatDate(look.publishedAt, { hour: undefined, minute: undefined })\n : '—'}\n </dd>\n </div>\n </dl>\n </GlassCard>\n\n {look.collaborationId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/collaborations/${look.collaborationId}`)}\n className=\"flex w-full items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-status-warning-bg-subtle text-status-warning-text\">\n <Sparkles className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">Part of a collaboration</p>\n <p className=\"truncate text-xs text-text-secondary\">View collaboration</p>\n </div>\n </button>\n )}\n\n {(publish.isError || remove.isError || like.isError) && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {(publish.error ?? remove.error ?? like.error) instanceof Error\n ? (publish.error ?? remove.error ?? like.error)?.message\n : 'Action failed'}\n </p>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|