@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.
Files changed (165) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.js +66 -122
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  3. package/dist/coolandlovely/CoolAndLovelyRoutes.js +87 -175
  4. package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
  5. package/dist/coolandlovely/components/Field.js +81 -0
  6. package/dist/coolandlovely/components/Field.js.map +1 -0
  7. package/dist/coolandlovely/components/StateViews.js +34 -0
  8. package/dist/coolandlovely/components/StateViews.js.map +1 -0
  9. package/dist/coolandlovely/components/StatusBadge.js +25 -22
  10. package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
  11. package/dist/coolandlovely/components/TagList.js +21 -0
  12. package/dist/coolandlovely/components/TagList.js.map +1 -0
  13. package/dist/coolandlovely/components/Thumbnail.js +29 -0
  14. package/dist/coolandlovely/components/Thumbnail.js.map +1 -0
  15. package/dist/coolandlovely/context/CoolAndLovelyContext.js +10 -10
  16. package/dist/coolandlovely/context/CoolAndLovelyContext.js.map +1 -1
  17. package/dist/coolandlovely/hooks/index.js +1 -0
  18. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +462 -169
  19. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  20. package/dist/coolandlovely/pages/HomePage.js +251 -845
  21. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  22. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +319 -0
  23. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -0
  24. package/dist/coolandlovely/pages/brands/BrandFormPage.js +190 -0
  25. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -0
  26. package/dist/coolandlovely/pages/brands/BrandsListPage.js +158 -0
  27. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -0
  28. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +253 -0
  29. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -0
  30. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +206 -0
  31. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js.map +1 -0
  32. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +129 -0
  33. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -0
  34. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +217 -0
  35. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -0
  36. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +217 -0
  37. package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -0
  38. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +122 -0
  39. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -0
  40. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +230 -0
  41. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -0
  42. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +276 -0
  43. package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -0
  44. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +170 -0
  45. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -0
  46. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +124 -0
  47. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -0
  48. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +156 -0
  49. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -0
  50. package/dist/coolandlovely/pages/looks/LookDetailPage.js +238 -0
  51. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -0
  52. package/dist/coolandlovely/pages/looks/LookFormPage.js +209 -0
  53. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -0
  54. package/dist/coolandlovely/pages/looks/LooksListPage.js +181 -0
  55. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -0
  56. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +224 -0
  57. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -0
  58. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +173 -0
  59. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -0
  60. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +172 -0
  61. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -0
  62. package/dist/coolandlovely/store/coolandlovelyStore.js +2 -6
  63. package/dist/coolandlovely/store/coolandlovelyStore.js.map +1 -1
  64. package/dist/coolandlovely/utils/formatters.js +31 -23
  65. package/dist/coolandlovely/utils/formatters.js.map +1 -1
  66. package/dist/coolandlovely/utils/icons.js +34 -0
  67. package/dist/coolandlovely/utils/icons.js.map +1 -0
  68. package/dist/index.js +4 -4
  69. package/package.json +1 -1
  70. package/dist/coolandlovely/components/InvestmentCard.js +0 -98
  71. package/dist/coolandlovely/components/InvestmentCard.js.map +0 -1
  72. package/dist/coolandlovely/components/RatingDisplay.js +0 -35
  73. package/dist/coolandlovely/components/RatingDisplay.js.map +0 -1
  74. package/dist/coolandlovely/mocks/data.js +0 -1517
  75. package/dist/coolandlovely/mocks/data.js.map +0 -1
  76. package/dist/coolandlovely/mocks/users.js +0 -34
  77. package/dist/coolandlovely/mocks/users.js.map +0 -1
  78. package/dist/coolandlovely/pages/admin/AdminAnalyticsPage.js +0 -651
  79. package/dist/coolandlovely/pages/admin/AdminAnalyticsPage.js.map +0 -1
  80. package/dist/coolandlovely/pages/admin/AdminDashboard.js +0 -673
  81. package/dist/coolandlovely/pages/admin/AdminDashboard.js.map +0 -1
  82. package/dist/coolandlovely/pages/admin/AdminFinancialPage.js +0 -427
  83. package/dist/coolandlovely/pages/admin/AdminFinancialPage.js.map +0 -1
  84. package/dist/coolandlovely/pages/admin/AdminOrdersPage.js +0 -665
  85. package/dist/coolandlovely/pages/admin/AdminOrdersPage.js.map +0 -1
  86. package/dist/coolandlovely/pages/admin/AdminProfilePage.js +0 -264
  87. package/dist/coolandlovely/pages/admin/AdminProfilePage.js.map +0 -1
  88. package/dist/coolandlovely/pages/admin/AdminSettingsPage.js +0 -654
  89. package/dist/coolandlovely/pages/admin/AdminSettingsPage.js.map +0 -1
  90. package/dist/coolandlovely/pages/admin/CartManagementPage.js +0 -874
  91. package/dist/coolandlovely/pages/admin/CartManagementPage.js.map +0 -1
  92. package/dist/coolandlovely/pages/admin/UserManagementPage.js +0 -898
  93. package/dist/coolandlovely/pages/admin/UserManagementPage.js.map +0 -1
  94. package/dist/coolandlovely/pages/cart-owner/AnalyticsDashboard.js +0 -842
  95. package/dist/coolandlovely/pages/cart-owner/AnalyticsDashboard.js.map +0 -1
  96. package/dist/coolandlovely/pages/cart-owner/BusinessSettingsPage.js +0 -705
  97. package/dist/coolandlovely/pages/cart-owner/BusinessSettingsPage.js.map +0 -1
  98. package/dist/coolandlovely/pages/cart-owner/CartOwnerDashboard.js +0 -802
  99. package/dist/coolandlovely/pages/cart-owner/CartOwnerDashboard.js.map +0 -1
  100. package/dist/coolandlovely/pages/cart-owner/CartOwnerProfilePage.js +0 -891
  101. package/dist/coolandlovely/pages/cart-owner/CartOwnerProfilePage.js.map +0 -1
  102. package/dist/coolandlovely/pages/cart-owner/CartServiceConfigPage.js +0 -639
  103. package/dist/coolandlovely/pages/cart-owner/CartServiceConfigPage.js.map +0 -1
  104. package/dist/coolandlovely/pages/cart-owner/InventoryManagementPage.js +0 -914
  105. package/dist/coolandlovely/pages/cart-owner/InventoryManagementPage.js.map +0 -1
  106. package/dist/coolandlovely/pages/cart-owner/OrderManagementPage.js +0 -882
  107. package/dist/coolandlovely/pages/cart-owner/OrderManagementPage.js.map +0 -1
  108. package/dist/coolandlovely/pages/cart-owner/StaffManagementPage.js +0 -831
  109. package/dist/coolandlovely/pages/cart-owner/StaffManagementPage.js.map +0 -1
  110. package/dist/coolandlovely/pages/common/HelpSupportPage.js +0 -1325
  111. package/dist/coolandlovely/pages/common/HelpSupportPage.js.map +0 -1
  112. package/dist/coolandlovely/pages/common/NotificationCenterPage.js +0 -671
  113. package/dist/coolandlovely/pages/common/NotificationCenterPage.js.map +0 -1
  114. package/dist/coolandlovely/pages/common/SettingsPage.js +0 -973
  115. package/dist/coolandlovely/pages/common/SettingsPage.js.map +0 -1
  116. package/dist/coolandlovely/pages/customer/BookingDetailsPage.js +0 -630
  117. package/dist/coolandlovely/pages/customer/BookingDetailsPage.js.map +0 -1
  118. package/dist/coolandlovely/pages/customer/CartDetailPage.js +0 -623
  119. package/dist/coolandlovely/pages/customer/CartDetailPage.js.map +0 -1
  120. package/dist/coolandlovely/pages/customer/CartDiscoveryPage.js +0 -558
  121. package/dist/coolandlovely/pages/customer/CartDiscoveryPage.js.map +0 -1
  122. package/dist/coolandlovely/pages/customer/CheckoutPage.js +0 -529
  123. package/dist/coolandlovely/pages/customer/CheckoutPage.js.map +0 -1
  124. package/dist/coolandlovely/pages/customer/CustomerDashboard.js +0 -538
  125. package/dist/coolandlovely/pages/customer/CustomerDashboard.js.map +0 -1
  126. package/dist/coolandlovely/pages/customer/CustomerProfilePage.js +0 -658
  127. package/dist/coolandlovely/pages/customer/CustomerProfilePage.js.map +0 -1
  128. package/dist/coolandlovely/pages/customer/DirectServiceBookingPage.js +0 -632
  129. package/dist/coolandlovely/pages/customer/DirectServiceBookingPage.js.map +0 -1
  130. package/dist/coolandlovely/pages/customer/FavoritesPage.js +0 -487
  131. package/dist/coolandlovely/pages/customer/FavoritesPage.js.map +0 -1
  132. package/dist/coolandlovely/pages/customer/OrderHistoryPage.js +0 -447
  133. package/dist/coolandlovely/pages/customer/OrderHistoryPage.js.map +0 -1
  134. package/dist/coolandlovely/pages/customer/OrderTrackingPage.js +0 -425
  135. package/dist/coolandlovely/pages/customer/OrderTrackingPage.js.map +0 -1
  136. package/dist/coolandlovely/pages/customer/PaymentMethodsPage.js +0 -494
  137. package/dist/coolandlovely/pages/customer/PaymentMethodsPage.js.map +0 -1
  138. package/dist/coolandlovely/pages/customer/ReviewsPage.js +0 -616
  139. package/dist/coolandlovely/pages/customer/ReviewsPage.js.map +0 -1
  140. package/dist/coolandlovely/pages/customer/SavedAddressesPage.js +0 -449
  141. package/dist/coolandlovely/pages/customer/SavedAddressesPage.js.map +0 -1
  142. package/dist/coolandlovely/pages/investor/InvestmentDetailsPage.js +0 -887
  143. package/dist/coolandlovely/pages/investor/InvestmentDetailsPage.js.map +0 -1
  144. package/dist/coolandlovely/pages/investor/InvestmentOpportunitiesPage.js +0 -945
  145. package/dist/coolandlovely/pages/investor/InvestmentOpportunitiesPage.js.map +0 -1
  146. package/dist/coolandlovely/pages/investor/InvestorAnalyticsPage.js +0 -687
  147. package/dist/coolandlovely/pages/investor/InvestorAnalyticsPage.js.map +0 -1
  148. package/dist/coolandlovely/pages/investor/InvestorDashboard.js +0 -455
  149. package/dist/coolandlovely/pages/investor/InvestorDashboard.js.map +0 -1
  150. package/dist/coolandlovely/pages/investor/InvestorInvestmentsPage.js +0 -405
  151. package/dist/coolandlovely/pages/investor/InvestorInvestmentsPage.js.map +0 -1
  152. package/dist/coolandlovely/pages/investor/InvestorPortfolioPage.js +0 -602
  153. package/dist/coolandlovely/pages/investor/InvestorPortfolioPage.js.map +0 -1
  154. package/dist/coolandlovely/pages/investor/InvestorProfilePage.js +0 -1070
  155. package/dist/coolandlovely/pages/investor/InvestorProfilePage.js.map +0 -1
  156. package/dist/coolandlovely/pages/worker/TaskDetailsPage.js +0 -533
  157. package/dist/coolandlovely/pages/worker/TaskDetailsPage.js.map +0 -1
  158. package/dist/coolandlovely/pages/worker/WorkerDashboard.js +0 -671
  159. package/dist/coolandlovely/pages/worker/WorkerDashboard.js.map +0 -1
  160. package/dist/coolandlovely/pages/worker/WorkerOrdersPage.js +0 -680
  161. package/dist/coolandlovely/pages/worker/WorkerOrdersPage.js.map +0 -1
  162. package/dist/coolandlovely/pages/worker/WorkerProfilePage.js +0 -692
  163. package/dist/coolandlovely/pages/worker/WorkerProfilePage.js.map +0 -1
  164. package/dist/coolandlovely/pages/worker/WorkerSchedulePage.js +0 -583
  165. 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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"}