@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,224 @@
1
+ import { PageLayout as e } from "../../components/PageLayout.js";
2
+ import { StatusBadge as t } from "../../components/StatusBadge.js";
3
+ import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
4
+ import { useMyStyleProfile as i, useUpsertStyleProfile as a } from "../../hooks/useCoolAndLovelyApi.js";
5
+ import "../../hooks/index.js";
6
+ import { parseCsv as o, titleCase as s } from "../../utils/formatters.js";
7
+ import { TagList as c } from "../../components/TagList.js";
8
+ import { SelectField as l, TextAreaField as u, TextField as d } from "../../components/Field.js";
9
+ import { useEffect as f, useState as p } from "react";
10
+ import { Loader2 as m, Pencil as h, Save as g, UserCircle as _, X as v } from "lucide-react";
11
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
12
+ import { GlassCard as x } from "@burdenoff/fe-libs/ui";
13
+ //#region src/coolandlovely/pages/profile/StyleProfilePage.tsx
14
+ var S = [
15
+ "PUBLIC",
16
+ "FOLLOWERS",
17
+ "PRIVATE"
18
+ ], C = {
19
+ displayName: "",
20
+ bio: "",
21
+ visibility: "PRIVATE",
22
+ stylePreferences: "",
23
+ palette: "",
24
+ favouriteBrands: ""
25
+ };
26
+ function w() {
27
+ let w = i(), T = a(), [E, D] = p(!1), [O, k] = p(C), A = w.data, j = !!A;
28
+ f(() => {
29
+ A && k({
30
+ displayName: A.displayName ?? "",
31
+ bio: A.bio ?? "",
32
+ visibility: A.visibility,
33
+ stylePreferences: A.stylePreferences.join(", "),
34
+ palette: A.palette.join(", "),
35
+ favouriteBrands: A.favouriteBrands.join(", ")
36
+ });
37
+ }, [A]), f(() => {
38
+ !w.isLoading && !j && D(!0);
39
+ }, [w.isLoading, j]);
40
+ let M = (e, t) => k((n) => ({
41
+ ...n,
42
+ [e]: t
43
+ })), N = S.map((e) => ({
44
+ value: e,
45
+ label: s(e)
46
+ }));
47
+ return w.isLoading ? /* @__PURE__ */ y(e, {
48
+ title: "Style Profile",
49
+ children: /* @__PURE__ */ y(r, { label: "Loading your style profile…" })
50
+ }) : w.isError ? /* @__PURE__ */ y(e, {
51
+ title: "Style Profile",
52
+ children: /* @__PURE__ */ y(n, { onRetry: () => void w.refetch() })
53
+ }) : !E && A ? /* @__PURE__ */ y(e, {
54
+ title: "Style Profile",
55
+ description: "Your taste, palette and sizing — the spine of personalised discovery",
56
+ actions: /* @__PURE__ */ b("button", {
57
+ type: "button",
58
+ onClick: () => D(!0),
59
+ 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",
60
+ children: [/* @__PURE__ */ y(h, { className: "h-4 w-4" }), " Edit"]
61
+ }),
62
+ children: /* @__PURE__ */ y("div", {
63
+ className: "mx-auto max-w-2xl",
64
+ children: /* @__PURE__ */ b(x, {
65
+ treatment: "glass",
66
+ className: "p-6",
67
+ children: [
68
+ /* @__PURE__ */ b("div", {
69
+ className: "flex items-center gap-4",
70
+ children: [/* @__PURE__ */ y("div", {
71
+ className: "flex h-14 w-14 items-center justify-center rounded-full bg-accent-purple-subtle text-text-primary",
72
+ children: /* @__PURE__ */ y(_, { className: "h-7 w-7" })
73
+ }), /* @__PURE__ */ b("div", {
74
+ className: "min-w-0 flex-1",
75
+ children: [/* @__PURE__ */ y("h2", {
76
+ className: "truncate text-xl font-semibold text-text-primary",
77
+ children: A.displayName || "Your style profile"
78
+ }), /* @__PURE__ */ y(t, {
79
+ status: A.visibility,
80
+ className: "mt-1"
81
+ })]
82
+ })]
83
+ }),
84
+ A.bio && /* @__PURE__ */ y("p", {
85
+ className: "mt-4 text-sm text-text-secondary",
86
+ children: A.bio
87
+ }),
88
+ /* @__PURE__ */ b("div", {
89
+ className: "mt-6 space-y-4",
90
+ children: [
91
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
92
+ className: "mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
93
+ children: "Style preferences"
94
+ }), A.stylePreferences.length ? /* @__PURE__ */ y(c, {
95
+ tags: A.stylePreferences,
96
+ tone: "accent",
97
+ max: 20
98
+ }) : /* @__PURE__ */ y("p", {
99
+ className: "text-sm text-text-secondary",
100
+ children: "—"
101
+ })] }),
102
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
103
+ className: "mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
104
+ children: "Palette"
105
+ }), A.palette.length ? /* @__PURE__ */ y(c, {
106
+ tags: A.palette,
107
+ max: 20
108
+ }) : /* @__PURE__ */ y("p", {
109
+ className: "text-sm text-text-secondary",
110
+ children: "—"
111
+ })] }),
112
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
113
+ className: "mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
114
+ children: "Favourite brands"
115
+ }), A.favouriteBrands.length ? /* @__PURE__ */ y(c, {
116
+ tags: A.favouriteBrands,
117
+ max: 20
118
+ }) : /* @__PURE__ */ y("p", {
119
+ className: "text-sm text-text-secondary",
120
+ children: "—"
121
+ })] })
122
+ ]
123
+ })
124
+ ]
125
+ })
126
+ })
127
+ }) : /* @__PURE__ */ y(e, {
128
+ title: j ? "Edit style profile" : "Create your style profile",
129
+ description: "This powers your personalised discovery feed",
130
+ actions: j ? /* @__PURE__ */ b("button", {
131
+ type: "button",
132
+ onClick: () => D(!1),
133
+ 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",
134
+ children: [/* @__PURE__ */ y(v, { className: "h-4 w-4" }), " Cancel"]
135
+ }) : void 0,
136
+ children: /* @__PURE__ */ y("form", {
137
+ onSubmit: (e) => {
138
+ e.preventDefault(), T.mutate({
139
+ displayName: O.displayName.trim() || void 0,
140
+ bio: O.bio.trim() || void 0,
141
+ visibility: O.visibility,
142
+ stylePreferences: o(O.stylePreferences),
143
+ palette: o(O.palette),
144
+ favouriteBrands: o(O.favouriteBrands)
145
+ }, { onSuccess: () => D(!1) });
146
+ },
147
+ className: "mx-auto max-w-2xl",
148
+ children: /* @__PURE__ */ b(x, {
149
+ treatment: "plain",
150
+ className: "p-6",
151
+ children: [
152
+ /* @__PURE__ */ y(d, {
153
+ id: "displayName",
154
+ label: "Display name",
155
+ value: O.displayName,
156
+ onChange: (e) => M("displayName", e),
157
+ placeholder: "How you want to appear"
158
+ }),
159
+ /* @__PURE__ */ y(u, {
160
+ id: "bio",
161
+ label: "Bio",
162
+ value: O.bio,
163
+ onChange: (e) => M("bio", e),
164
+ placeholder: "A sentence about your style…",
165
+ rows: 3,
166
+ className: "mt-4"
167
+ }),
168
+ /* @__PURE__ */ y(l, {
169
+ id: "visibility",
170
+ label: "Visibility",
171
+ value: O.visibility,
172
+ onChange: (e) => M("visibility", e),
173
+ options: N,
174
+ className: "mt-4"
175
+ }),
176
+ /* @__PURE__ */ y(d, {
177
+ id: "stylePreferences",
178
+ label: "Style preferences",
179
+ value: O.stylePreferences,
180
+ onChange: (e) => M("stylePreferences", e),
181
+ placeholder: "minimal, tailored, vintage",
182
+ hint: "Comma-separated",
183
+ className: "mt-4"
184
+ }),
185
+ /* @__PURE__ */ y(d, {
186
+ id: "palette",
187
+ label: "Palette",
188
+ value: O.palette,
189
+ onChange: (e) => M("palette", e),
190
+ placeholder: "neutrals, navy, rust",
191
+ hint: "Comma-separated",
192
+ className: "mt-4"
193
+ }),
194
+ /* @__PURE__ */ y(d, {
195
+ id: "favouriteBrands",
196
+ label: "Favourite brands",
197
+ value: O.favouriteBrands,
198
+ onChange: (e) => M("favouriteBrands", e),
199
+ placeholder: "Aurora Atelier, …",
200
+ hint: "Comma-separated",
201
+ className: "mt-4"
202
+ }),
203
+ T.isError && /* @__PURE__ */ y("p", {
204
+ className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
205
+ children: T.error instanceof Error ? T.error.message : "Something went wrong"
206
+ }),
207
+ /* @__PURE__ */ y("div", {
208
+ className: "mt-6 flex justify-end gap-2",
209
+ children: /* @__PURE__ */ b("button", {
210
+ type: "submit",
211
+ disabled: T.isPending,
212
+ 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",
213
+ children: [T.isPending ? /* @__PURE__ */ y(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ y(g, { className: "h-4 w-4" }), j ? "Save changes" : "Create profile"]
214
+ })
215
+ })
216
+ ]
217
+ })
218
+ })
219
+ });
220
+ }
221
+ //#endregion
222
+ export { w as StyleProfilePage };
223
+
224
+ //# sourceMappingURL=StyleProfilePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StyleProfilePage.js","names":[],"sources":["../../../../src/coolandlovely/pages/profile/StyleProfilePage.tsx"],"sourcesContent":["/**\n * Style Profile Page (self-scoped)\n * @module coolandlovely/pages/profile\n *\n * Views and edits the calling shopper's style profile (`cooMyStyleProfile` +\n * `cooUpsertStyleProfile`). One profile per workspace per shopper.\n */\n\nimport { useEffect, useState } from 'react';\nimport { Loader2, Save, UserCircle, Pencil, X } 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 { TextField, TextAreaField, SelectField } from '../../components/Field';\nimport { useMyStyleProfile, useUpsertStyleProfile } from '../../hooks';\nimport { parseCsv, titleCase } from '../../utils/formatters';\nimport type { CooVisibility } from '../../types';\n\nconst VISIBILITIES: CooVisibility[] = ['PUBLIC', 'FOLLOWERS', 'PRIVATE'];\n\ninterface FormState {\n displayName: string;\n bio: string;\n visibility: CooVisibility;\n stylePreferences: string;\n palette: string;\n favouriteBrands: string;\n}\n\nconst EMPTY: FormState = {\n displayName: '',\n bio: '',\n visibility: 'PRIVATE',\n stylePreferences: '',\n palette: '',\n favouriteBrands: '',\n};\n\nexport function StyleProfilePage() {\n const query = useMyStyleProfile();\n const upsert = useUpsertStyleProfile();\n\n const [editing, setEditing] = useState(false);\n const [form, setForm] = useState<FormState>(EMPTY);\n\n const profile = query.data;\n const hasProfile = Boolean(profile);\n\n useEffect(() => {\n if (profile) {\n setForm({\n displayName: profile.displayName ?? '',\n bio: profile.bio ?? '',\n visibility: profile.visibility,\n stylePreferences: profile.stylePreferences.join(', '),\n palette: profile.palette.join(', '),\n favouriteBrands: profile.favouriteBrands.join(', '),\n });\n }\n }, [profile]);\n\n // If there is no profile yet, drop straight into edit mode.\n useEffect(() => {\n if (!query.isLoading && !hasProfile) {\n setEditing(true);\n }\n }, [query.isLoading, hasProfile]);\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) =>\n setForm((f) => ({ ...f, [key]: value }));\n\n const visibilityOptions = VISIBILITIES.map((v) => ({ value: v, label: titleCase(v) }));\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n upsert.mutate(\n {\n displayName: form.displayName.trim() || undefined,\n bio: form.bio.trim() || undefined,\n visibility: form.visibility,\n stylePreferences: parseCsv(form.stylePreferences),\n palette: parseCsv(form.palette),\n favouriteBrands: parseCsv(form.favouriteBrands),\n },\n { onSuccess: () => setEditing(false) }\n );\n };\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Style Profile\">\n <LoadingView label=\"Loading your style profile…\" />\n </PageLayout>\n );\n }\n\n if (query.isError) {\n return (\n <PageLayout title=\"Style Profile\">\n <ErrorView onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n // ----- View mode -----\n if (!editing && profile) {\n return (\n <PageLayout\n title=\"Style Profile\"\n description=\"Your taste, palette and sizing — the spine of personalised discovery\"\n actions={\n <button\n type=\"button\"\n onClick={() => setEditing(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 transition-colors hover:bg-action-primary-bg/90\"\n >\n <Pencil className=\"h-4 w-4\" /> Edit\n </button>\n }\n >\n <div className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"flex h-14 w-14 items-center justify-center rounded-full bg-accent-purple-subtle text-text-primary\">\n <UserCircle className=\"h-7 w-7\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">\n {profile.displayName || 'Your style profile'}\n </h2>\n <StatusBadge status={profile.visibility} className=\"mt-1\" />\n </div>\n </div>\n {profile.bio && <p className=\"mt-4 text-sm text-text-secondary\">{profile.bio}</p>}\n\n <div className=\"mt-6 space-y-4\">\n <div>\n <p className=\"mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Style preferences\n </p>\n {profile.stylePreferences.length ? (\n <TagList tags={profile.stylePreferences} tone=\"accent\" max={20} />\n ) : (\n <p className=\"text-sm text-text-secondary\">—</p>\n )}\n </div>\n <div>\n <p className=\"mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Palette\n </p>\n {profile.palette.length ? (\n <TagList tags={profile.palette} max={20} />\n ) : (\n <p className=\"text-sm text-text-secondary\">—</p>\n )}\n </div>\n <div>\n <p className=\"mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Favourite brands\n </p>\n {profile.favouriteBrands.length ? (\n <TagList tags={profile.favouriteBrands} max={20} />\n ) : (\n <p className=\"text-sm text-text-secondary\">—</p>\n )}\n </div>\n </div>\n </GlassCard>\n </div>\n </PageLayout>\n );\n }\n\n // ----- Edit / create mode -----\n return (\n <PageLayout\n title={hasProfile ? 'Edit style profile' : 'Create your style profile'}\n description=\"This powers your personalised discovery feed\"\n actions={\n hasProfile ? (\n <button\n type=\"button\"\n onClick={() => setEditing(false)}\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 <X className=\"h-4 w-4\" /> Cancel\n </button>\n ) : undefined\n }\n >\n <form onSubmit={handleSubmit} className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <TextField\n id=\"displayName\"\n label=\"Display name\"\n value={form.displayName}\n onChange={(v) => setField('displayName', v)}\n placeholder=\"How you want to appear\"\n />\n <TextAreaField\n id=\"bio\"\n label=\"Bio\"\n value={form.bio}\n onChange={(v) => setField('bio', v)}\n placeholder=\"A sentence about your style…\"\n rows={3}\n className=\"mt-4\"\n />\n <SelectField\n id=\"visibility\"\n label=\"Visibility\"\n value={form.visibility}\n onChange={(v) => setField('visibility', v as CooVisibility)}\n options={visibilityOptions}\n className=\"mt-4\"\n />\n <TextField\n id=\"stylePreferences\"\n label=\"Style preferences\"\n value={form.stylePreferences}\n onChange={(v) => setField('stylePreferences', v)}\n placeholder=\"minimal, tailored, vintage\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n <TextField\n id=\"palette\"\n label=\"Palette\"\n value={form.palette}\n onChange={(v) => setField('palette', v)}\n placeholder=\"neutrals, navy, rust\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n <TextField\n id=\"favouriteBrands\"\n label=\"Favourite brands\"\n value={form.favouriteBrands}\n onChange={(v) => setField('favouriteBrands', v)}\n placeholder=\"Aurora Atelier, …\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n\n {upsert.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {upsert.error instanceof Error ? upsert.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={upsert.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 {upsert.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {hasProfile ? 'Save changes' : 'Create profile'}\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAoBA,IAAM,IAAgC;CAAC;CAAU;CAAa;CAAU,EAWlE,IAAmB;CACvB,aAAa;CACb,KAAK;CACL,YAAY;CACZ,kBAAkB;CAClB,SAAS;CACT,iBAAiB;CAClB;AAED,SAAgB,IAAmB;CACjC,IAAM,IAAQ,GAAmB,EAC3B,IAAS,GAAuB,EAEhC,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAM,KAAW,EAAoB,EAAM,EAE5C,IAAU,EAAM,MAChB,IAAa,EAAQ;AAgB3B,CAdA,QAAgB;AACd,EAAI,KACF,EAAQ;GACN,aAAa,EAAQ,eAAe;GACpC,KAAK,EAAQ,OAAO;GACpB,YAAY,EAAQ;GACpB,kBAAkB,EAAQ,iBAAiB,KAAK,KAAK;GACrD,SAAS,EAAQ,QAAQ,KAAK,KAAK;GACnC,iBAAiB,EAAQ,gBAAgB,KAAK,KAAK;GACpD,CAAC;IAEH,CAAC,EAAQ,CAAC,EAGb,QAAgB;AACd,EAAI,CAAC,EAAM,aAAa,CAAC,KACvB,EAAW,GAAK;IAEjB,CAAC,EAAM,WAAW,EAAW,CAAC;CAEjC,IAAM,KAAuC,GAAQ,MACnD,GAAS,OAAO;EAAE,GAAG;GAAI,IAAM;EAAO,EAAE,EAEpC,IAAoB,EAAa,KAAK,OAAO;EAAE,OAAO;EAAG,OAAO,EAAU,EAAE;EAAE,EAAE;AAuGtF,QAtFI,EAAM,YAEN,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD,EAAa,OAAM,+BAAgC,CAAA;EACxC,CAAA,GAIb,EAAM,UAEN,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD,EAAW,eAAe,KAAK,EAAM,SAAS,EAAI,CAAA;EACvC,CAAA,GAKb,CAAC,KAAW,IAEZ,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,GAAK;GAC/B,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,QACvB;;YAGX,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;OAC9B,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAQ,eAAe;QACrB,CAAA,EACL,kBAAC,GAAD;QAAa,QAAQ,EAAQ;QAAY,WAAU;QAAS,CAAA,CACxD;SACF;;KACL,EAAQ,OAAO,kBAAC,KAAD;MAAG,WAAU;gBAAoC,EAAQ;MAAQ,CAAA;KAEjF,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAyE;QAElF,CAAA,EACH,EAAQ,iBAAiB,SACxB,kBAAC,GAAD;QAAS,MAAM,EAAQ;QAAkB,MAAK;QAAS,KAAK;QAAM,CAAA,GAElE,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAK,CAAA,CAE9C,EAAA,CAAA;OACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAyE;QAElF,CAAA,EACH,EAAQ,QAAQ,SACf,kBAAC,GAAD;QAAS,MAAM,EAAQ;QAAS,KAAK;QAAM,CAAA,GAE3C,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAK,CAAA,CAE9C,EAAA,CAAA;OACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAyE;QAElF,CAAA,EACH,EAAQ,gBAAgB,SACvB,kBAAC,GAAD;QAAS,MAAM,EAAQ;QAAiB,KAAK;QAAM,CAAA,GAEnD,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAK,CAAA,CAE9C,EAAA,CAAA;OACF;;KACI;;GACR,CAAA;EACK,CAAA,GAMf,kBAAC,GAAD;EACE,OAAO,IAAa,uBAAuB;EAC3C,aAAY;EACZ,SACE,IACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,GAAM;GAChC,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA,EAAA,UAClB;OACP,KAAA;YAGN,kBAAC,QAAD;GAAM,WArHY,MAAuB;AAE3C,IADA,EAAE,gBAAgB,EAClB,EAAO,OACL;KACE,aAAa,EAAK,YAAY,MAAM,IAAI,KAAA;KACxC,KAAK,EAAK,IAAI,MAAM,IAAI,KAAA;KACxB,YAAY,EAAK;KACjB,kBAAkB,EAAS,EAAK,iBAAiB;KACjD,SAAS,EAAS,EAAK,QAAQ;KAC/B,iBAAiB,EAAS,EAAK,gBAAgB;KAChD,EACD,EAAE,iBAAiB,EAAW,GAAM,EAAE,CACvC;;GAyG+B,WAAU;aACtC,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC;KACE,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,eAAe,EAAE;MAC3C,aAAY;MACZ,CAAA;KACF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,OAAO,EAAE;MACnC,aAAY;MACZ,MAAM;MACN,WAAU;MACV,CAAA;KACF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,cAAc,EAAmB;MAC3D,SAAS;MACT,WAAU;MACV,CAAA;KACF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,oBAAoB,EAAE;MAChD,aAAY;MACZ,MAAK;MACL,WAAU;MACV,CAAA;KACF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,WAAW,EAAE;MACvC,aAAY;MACZ,MAAK;MACL,WAAU;MACV,CAAA;KACF,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,mBAAmB,EAAE;MAC/C,aAAY;MACZ,MAAK;MACL,WAAU;MACV,CAAA;KAED,EAAO,WACN,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAO,iBAAiB,QAAQ,EAAO,MAAM,UAAU;MACtD,CAAA;KAGN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,EAAO;OACjB,WAAU;iBAHZ,CAKG,EAAO,YACN,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAE7B,IAAa,iBAAiB,iBACxB;;MACL,CAAA;KACI;;GACP,CAAA;EACI,CAAA"}
@@ -0,0 +1,173 @@
1
+ import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
2
+ import { PageLayout as t } from "../../components/PageLayout.js";
3
+ import { useCreateWardrobeItem as n } from "../../hooks/useCoolAndLovelyApi.js";
4
+ import "../../hooks/index.js";
5
+ import { parseCsv as r, titleCase as i } from "../../utils/formatters.js";
6
+ import { STYLE_CATEGORIES as a } from "../../utils/icons.js";
7
+ import { SelectField as o, TextAreaField as s, TextField as c } from "../../components/Field.js";
8
+ import { useState as l } from "react";
9
+ import { ArrowLeft as u, Heart as d, Loader2 as f, Save as p } from "lucide-react";
10
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
11
+ import { GlassCard as g } from "@burdenoff/fe-libs/ui";
12
+ //#region src/coolandlovely/pages/wardrobe/WardrobeFormPage.tsx
13
+ var _ = [
14
+ "OWNED",
15
+ "WISHLIST",
16
+ "PURCHASED_ON_PLATFORM",
17
+ "GIFTED"
18
+ ], v = {
19
+ name: "",
20
+ category: "OTHER",
21
+ source: "OWNED",
22
+ brandName: "",
23
+ colour: "",
24
+ size: "",
25
+ notes: "",
26
+ tags: ""
27
+ };
28
+ function y() {
29
+ let { navigateTo: y } = e(), b = n(), [x, S] = l(v), [C, w] = l(), T = (e, t) => S((n) => ({
30
+ ...n,
31
+ [e]: t
32
+ })), E = a.map((e) => ({
33
+ value: e,
34
+ label: i(e)
35
+ })), D = _.map((e) => ({
36
+ value: e,
37
+ label: i(e)
38
+ }));
39
+ return /* @__PURE__ */ m(t, {
40
+ title: "Add a wardrobe piece",
41
+ description: "Catalogue something you own or want",
42
+ actions: /* @__PURE__ */ h("button", {
43
+ type: "button",
44
+ onClick: () => y("/wardrobe"),
45
+ 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",
46
+ children: [/* @__PURE__ */ m(u, { className: "h-4 w-4" }), " Cancel"]
47
+ }),
48
+ children: /* @__PURE__ */ m("form", {
49
+ onSubmit: (e) => {
50
+ if (e.preventDefault(), !x.name.trim()) {
51
+ w("Name is required");
52
+ return;
53
+ }
54
+ w(void 0), b.mutate({
55
+ name: x.name.trim(),
56
+ category: x.category,
57
+ source: x.source,
58
+ brandName: x.brandName.trim() || void 0,
59
+ colour: x.colour.trim() || void 0,
60
+ size: x.size.trim() || void 0,
61
+ notes: x.notes.trim() || void 0,
62
+ tags: r(x.tags)
63
+ }, { onSuccess: () => y("/wardrobe") });
64
+ },
65
+ className: "mx-auto max-w-2xl",
66
+ children: /* @__PURE__ */ h(g, {
67
+ treatment: "plain",
68
+ className: "p-6",
69
+ children: [
70
+ /* @__PURE__ */ h("div", {
71
+ className: "mb-6 flex items-center gap-3",
72
+ children: [/* @__PURE__ */ m("div", {
73
+ className: "flex h-11 w-11 items-center justify-center rounded-lg bg-status-error-bg-subtle text-status-error-text",
74
+ children: /* @__PURE__ */ m(d, { className: "h-5 w-5" })
75
+ }), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h2", {
76
+ className: "text-base font-semibold text-text-primary",
77
+ children: "Piece details"
78
+ }), /* @__PURE__ */ m("p", {
79
+ className: "text-xs text-text-secondary",
80
+ children: "Only you can see your wardrobe."
81
+ })] })]
82
+ }),
83
+ /* @__PURE__ */ m(c, {
84
+ id: "name",
85
+ label: "Name",
86
+ value: x.name,
87
+ onChange: (e) => T("name", e),
88
+ placeholder: "Vintage denim jacket",
89
+ required: !0,
90
+ error: C
91
+ }),
92
+ /* @__PURE__ */ h("div", {
93
+ className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2",
94
+ children: [/* @__PURE__ */ m(o, {
95
+ id: "category",
96
+ label: "Category",
97
+ value: x.category,
98
+ onChange: (e) => T("category", e),
99
+ options: E
100
+ }), /* @__PURE__ */ m(o, {
101
+ id: "source",
102
+ label: "Source",
103
+ value: x.source,
104
+ onChange: (e) => T("source", e),
105
+ options: D
106
+ })]
107
+ }),
108
+ /* @__PURE__ */ h("div", {
109
+ className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-3",
110
+ children: [
111
+ /* @__PURE__ */ m(c, {
112
+ id: "brandName",
113
+ label: "Brand",
114
+ value: x.brandName,
115
+ onChange: (e) => T("brandName", e),
116
+ placeholder: "Levi's"
117
+ }),
118
+ /* @__PURE__ */ m(c, {
119
+ id: "colour",
120
+ label: "Colour",
121
+ value: x.colour,
122
+ onChange: (e) => T("colour", e),
123
+ placeholder: "Indigo"
124
+ }),
125
+ /* @__PURE__ */ m(c, {
126
+ id: "size",
127
+ label: "Size",
128
+ value: x.size,
129
+ onChange: (e) => T("size", e),
130
+ placeholder: "M"
131
+ })
132
+ ]
133
+ }),
134
+ /* @__PURE__ */ m(s, {
135
+ id: "notes",
136
+ label: "Notes",
137
+ value: x.notes,
138
+ onChange: (e) => T("notes", e),
139
+ placeholder: "Where you got it, how you wear it…",
140
+ rows: 3,
141
+ className: "mt-4"
142
+ }),
143
+ /* @__PURE__ */ m(c, {
144
+ id: "tags",
145
+ label: "Tags",
146
+ value: x.tags,
147
+ onChange: (e) => T("tags", e),
148
+ placeholder: "denim, layering",
149
+ hint: "Comma-separated",
150
+ className: "mt-4"
151
+ }),
152
+ b.isError && /* @__PURE__ */ m("p", {
153
+ className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
154
+ children: b.error instanceof Error ? b.error.message : "Something went wrong"
155
+ }),
156
+ /* @__PURE__ */ m("div", {
157
+ className: "mt-6 flex justify-end gap-2",
158
+ children: /* @__PURE__ */ h("button", {
159
+ type: "submit",
160
+ disabled: b.isPending,
161
+ 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",
162
+ children: [b.isPending ? /* @__PURE__ */ m(f, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ m(p, { className: "h-4 w-4" }), "Add to wardrobe"]
163
+ })
164
+ })
165
+ ]
166
+ })
167
+ })
168
+ });
169
+ }
170
+ //#endregion
171
+ export { y as WardrobeFormPage };
172
+
173
+ //# sourceMappingURL=WardrobeFormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WardrobeFormPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/wardrobe/WardrobeFormPage.tsx"],"sourcesContent":["/**\n * Wardrobe Item Create Page (self-scoped)\n * @module coolandlovely/pages/wardrobe\n *\n * Drives `cooCreateWardrobeItem`. Belongs to the calling shopper's profile.\n */\n\nimport { useState } from 'react';\nimport { ArrowLeft, Loader2, Save, Heart } 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 { useCreateWardrobeItem } from '../../hooks';\nimport { STYLE_CATEGORIES } from '../../utils/icons';\nimport { parseCsv, titleCase } from '../../utils/formatters';\nimport type { CooStyleCategory, CooWardrobeSource } from '../../types';\n\nconst SOURCES: CooWardrobeSource[] = ['OWNED', 'WISHLIST', 'PURCHASED_ON_PLATFORM', 'GIFTED'];\n\ninterface FormState {\n name: string;\n category: CooStyleCategory;\n source: CooWardrobeSource;\n brandName: string;\n colour: string;\n size: string;\n notes: string;\n tags: string;\n}\n\nconst EMPTY: FormState = {\n name: '',\n category: 'OTHER',\n source: 'OWNED',\n brandName: '',\n colour: '',\n size: '',\n notes: '',\n tags: '',\n};\n\nexport function WardrobeFormPage() {\n const { navigateTo } = useCoolAndLovely();\n const createItem = useCreateWardrobeItem();\n\n const [form, setForm] = useState<FormState>(EMPTY);\n const [nameError, setNameError] = useState<string | undefined>();\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) =>\n setForm((f) => ({ ...f, [key]: value }));\n\n const categoryOptions = STYLE_CATEGORIES.map((c) => ({ value: c, label: titleCase(c) }));\n const sourceOptions = SOURCES.map((s) => ({ value: s, label: titleCase(s) }));\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!form.name.trim()) {\n setNameError('Name is required');\n return;\n }\n setNameError(undefined);\n\n createItem.mutate(\n {\n name: form.name.trim(),\n category: form.category,\n source: form.source,\n brandName: form.brandName.trim() || undefined,\n colour: form.colour.trim() || undefined,\n size: form.size.trim() || undefined,\n notes: form.notes.trim() || undefined,\n tags: parseCsv(form.tags),\n },\n { onSuccess: () => navigateTo('/wardrobe') }\n );\n };\n\n return (\n <PageLayout\n title=\"Add a wardrobe piece\"\n description=\"Catalogue something you own or want\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/wardrobe')}\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-error-bg-subtle text-status-error-text\">\n <Heart className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Piece details</h2>\n <p className=\"text-xs text-text-secondary\">Only you can see your wardrobe.</p>\n </div>\n </div>\n\n <TextField\n id=\"name\"\n label=\"Name\"\n value={form.name}\n onChange={(v) => setField('name', v)}\n placeholder=\"Vintage denim jacket\"\n required\n error={nameError}\n />\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <SelectField\n id=\"category\"\n label=\"Category\"\n value={form.category}\n onChange={(v) => setField('category', v as CooStyleCategory)}\n options={categoryOptions}\n />\n <SelectField\n id=\"source\"\n label=\"Source\"\n value={form.source}\n onChange={(v) => setField('source', v as CooWardrobeSource)}\n options={sourceOptions}\n />\n </div>\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <TextField\n id=\"brandName\"\n label=\"Brand\"\n value={form.brandName}\n onChange={(v) => setField('brandName', v)}\n placeholder=\"Levi's\"\n />\n <TextField\n id=\"colour\"\n label=\"Colour\"\n value={form.colour}\n onChange={(v) => setField('colour', v)}\n placeholder=\"Indigo\"\n />\n <TextField\n id=\"size\"\n label=\"Size\"\n value={form.size}\n onChange={(v) => setField('size', v)}\n placeholder=\"M\"\n />\n </div>\n\n <TextAreaField\n id=\"notes\"\n label=\"Notes\"\n value={form.notes}\n onChange={(v) => setField('notes', v)}\n placeholder=\"Where you got it, how you wear it…\"\n rows={3}\n className=\"mt-4\"\n />\n\n <TextField\n id=\"tags\"\n label=\"Tags\"\n value={form.tags}\n onChange={(v) => setField('tags', v)}\n placeholder=\"denim, layering\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n\n {createItem.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {createItem.error instanceof Error ? createItem.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={createItem.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 {createItem.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Add to wardrobe\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,172 @@
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 { useLogWardrobeWear as s, useRemoveWardrobeItem as c, useWardrobeItems as l } from "../../hooks/useCoolAndLovelyApi.js";
8
+ import "../../hooks/index.js";
9
+ import { formatRelativeTime as u, titleCase as d } from "../../utils/formatters.js";
10
+ import { STYLE_CATEGORIES as f, categoryIcon as p } from "../../utils/icons.js";
11
+ import { EmptyState as m } from "../../components/EmptyState.js";
12
+ import { useState as h } from "react";
13
+ import { CheckCircle2 as g, Heart as _, Loader2 as v, Plus as y, Trash2 as b } from "lucide-react";
14
+ import { jsx as x, jsxs as S } from "react/jsx-runtime";
15
+ //#region src/coolandlovely/pages/wardrobe/WardrobeListPage.tsx
16
+ var C = [
17
+ {
18
+ value: "ALL",
19
+ label: "All"
20
+ },
21
+ {
22
+ value: "OWNED",
23
+ label: "Owned"
24
+ },
25
+ {
26
+ value: "WISHLIST",
27
+ label: "Wishlist"
28
+ },
29
+ {
30
+ value: "PURCHASED_ON_PLATFORM",
31
+ label: "Purchased"
32
+ },
33
+ {
34
+ value: "GIFTED",
35
+ label: "Gifted"
36
+ }
37
+ ];
38
+ function w() {
39
+ let { navigateTo: w } = e(), [T, E] = h("ALL"), [D, O] = h("ALL"), k = l({
40
+ category: T === "ALL" ? void 0 : T,
41
+ source: D === "ALL" ? void 0 : D
42
+ }), A = s(), j = c(), M = k.data ?? [];
43
+ return /* @__PURE__ */ S(n, {
44
+ title: "My Wardrobe",
45
+ description: "A private catalogue of what you own — with a wear log",
46
+ actions: /* @__PURE__ */ S("button", {
47
+ type: "button",
48
+ onClick: () => w("/wardrobe/new"),
49
+ 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",
50
+ children: [/* @__PURE__ */ x(y, { className: "h-4 w-4" }), " Add piece"]
51
+ }),
52
+ children: [
53
+ /* @__PURE__ */ x("div", {
54
+ className: "mb-3 flex flex-wrap items-center gap-2",
55
+ children: C.map((e) => /* @__PURE__ */ x("button", {
56
+ type: "button",
57
+ onClick: () => O(e.value),
58
+ className: t("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", D === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
59
+ children: e.label
60
+ }, e.value))
61
+ }),
62
+ /* @__PURE__ */ S("div", {
63
+ className: "mb-6 flex flex-wrap items-center gap-2",
64
+ children: [/* @__PURE__ */ x("button", {
65
+ type: "button",
66
+ onClick: () => E("ALL"),
67
+ className: t("rounded-full px-3 py-1.5 text-xs font-medium transition-colors", T === "ALL" ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
68
+ children: "All categories"
69
+ }), f.map((e) => /* @__PURE__ */ S("button", {
70
+ type: "button",
71
+ onClick: () => E(e),
72
+ className: t("inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors", T === e ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
73
+ children: [
74
+ p(e, 14),
75
+ " ",
76
+ d(e)
77
+ ]
78
+ }, e))]
79
+ }),
80
+ k.isLoading ? /* @__PURE__ */ x(a, { label: "Loading your wardrobe…" }) : k.isError ? /* @__PURE__ */ x(i, { onRetry: () => void k.refetch() }) : M.length === 0 ? /* @__PURE__ */ x(m, {
81
+ illustration: "empty-data",
82
+ icon: /* @__PURE__ */ x(_, { className: "h-10 w-10" }),
83
+ title: "Your wardrobe is empty",
84
+ description: "Add the pieces you already own (or want) to start your digital closet.",
85
+ action: /* @__PURE__ */ S("button", {
86
+ type: "button",
87
+ onClick: () => w("/wardrobe/new"),
88
+ 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",
89
+ children: [/* @__PURE__ */ x(y, { className: "h-4 w-4" }), " Add piece"]
90
+ })
91
+ }) : /* @__PURE__ */ x("div", {
92
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
93
+ children: M.map((e) => /* @__PURE__ */ S("div", {
94
+ className: "flex flex-col overflow-hidden rounded-lg border border-border-seam bg-bg-surface shadow-sm",
95
+ children: [
96
+ /* @__PURE__ */ S("div", {
97
+ className: "flex items-start gap-3 p-4",
98
+ children: [/* @__PURE__ */ x(o, {
99
+ icon: p(e.category, 22),
100
+ seed: e.id,
101
+ className: "h-14 w-14 shrink-0"
102
+ }), /* @__PURE__ */ S("div", {
103
+ className: "min-w-0 flex-1",
104
+ children: [
105
+ /* @__PURE__ */ x("p", {
106
+ className: "truncate text-sm font-semibold text-text-primary",
107
+ children: e.name
108
+ }),
109
+ e.brandName && /* @__PURE__ */ x("p", {
110
+ className: "truncate text-xs text-text-secondary",
111
+ children: e.brandName
112
+ }),
113
+ /* @__PURE__ */ S("div", {
114
+ className: "mt-1.5 flex flex-wrap items-center gap-1.5",
115
+ children: [
116
+ /* @__PURE__ */ x(r, { status: e.source }),
117
+ e.colour && /* @__PURE__ */ x("span", {
118
+ className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
119
+ children: e.colour
120
+ }),
121
+ e.size && /* @__PURE__ */ x("span", {
122
+ className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
123
+ children: e.size
124
+ })
125
+ ]
126
+ })
127
+ ]
128
+ })]
129
+ }),
130
+ e.notes && /* @__PURE__ */ x("p", {
131
+ className: "px-4 pb-2 text-xs text-text-secondary line-clamp-2",
132
+ children: e.notes
133
+ }),
134
+ /* @__PURE__ */ S("div", {
135
+ className: "mt-auto flex items-center justify-between border-t border-border-seam px-4 py-2.5",
136
+ children: [/* @__PURE__ */ S("span", {
137
+ className: "text-xs text-text-secondary",
138
+ children: [
139
+ "Worn ",
140
+ e.timesWorn,
141
+ "×",
142
+ e.lastWornAt ? ` · ${u(e.lastWornAt)}` : ""
143
+ ]
144
+ }), /* @__PURE__ */ S("div", {
145
+ className: "flex items-center gap-1",
146
+ children: [/* @__PURE__ */ S("button", {
147
+ type: "button",
148
+ disabled: A.isPending,
149
+ onClick: () => A.mutate(e.id),
150
+ "aria-label": "Log a wear",
151
+ className: "inline-flex items-center gap-1 rounded-lg px-2 py-1 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50",
152
+ children: [A.isPending && A.variables === e.id ? /* @__PURE__ */ x(v, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ x(g, { className: "h-3.5 w-3.5" }), "Wore it"]
153
+ }), /* @__PURE__ */ x("button", {
154
+ type: "button",
155
+ disabled: j.isPending,
156
+ onClick: () => j.mutate(e.id),
157
+ "aria-label": "Remove piece",
158
+ className: "inline-flex h-7 w-7 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",
159
+ children: /* @__PURE__ */ x(b, { className: "h-3.5 w-3.5" })
160
+ })]
161
+ })]
162
+ })
163
+ ]
164
+ }, e.id))
165
+ })
166
+ ]
167
+ });
168
+ }
169
+ //#endregion
170
+ export { w as WardrobeListPage };
171
+
172
+ //# sourceMappingURL=WardrobeListPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WardrobeListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/wardrobe/WardrobeListPage.tsx"],"sourcesContent":["/**\n * Wardrobe List Page (self-scoped)\n * @module coolandlovely/pages/wardrobe\n *\n * The shopper's digital wardrobe (`cooListWardrobeItems`, self-scoped) with\n * category & source filters, wear logging (`cooLogWardrobeWear`) and removal\n * (`cooRemoveWardrobeItem`).\n */\n\nimport { useState } from 'react';\nimport { Plus, Heart, CheckCircle2, Trash2, Loader2 } 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 { useWardrobeItems, useLogWardrobeWear, useRemoveWardrobeItem } from '../../hooks';\nimport { categoryIcon, STYLE_CATEGORIES } from '../../utils/icons';\nimport { titleCase, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooStyleCategory, CooWardrobeSource } from '../../types';\n\nconst SOURCES: Array<{ value: CooWardrobeSource | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'OWNED', label: 'Owned' },\n { value: 'WISHLIST', label: 'Wishlist' },\n { value: 'PURCHASED_ON_PLATFORM', label: 'Purchased' },\n { value: 'GIFTED', label: 'Gifted' },\n];\n\nexport function WardrobeListPage() {\n const { navigateTo } = useCoolAndLovely();\n const [category, setCategory] = useState<CooStyleCategory | 'ALL'>('ALL');\n const [source, setSource] = useState<CooWardrobeSource | 'ALL'>('ALL');\n\n const query = useWardrobeItems({\n category: category === 'ALL' ? undefined : category,\n source: source === 'ALL' ? undefined : source,\n });\n const logWear = useLogWardrobeWear();\n const remove = useRemoveWardrobeItem();\n\n const items = query.data ?? [];\n\n return (\n <PageLayout\n title=\"My Wardrobe\"\n description=\"A private catalogue of what you own — with a wear log\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/wardrobe/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\" /> Add piece\n </button>\n }\n >\n {/* Source filter */}\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n {SOURCES.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => setSource(opt.value)}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n source === 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 </div>\n\n {/* Category filter */}\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setCategory('ALL')}\n className={cn(\n 'rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n category === 'ALL'\n ? 'bg-accent-purple-subtle text-text-primary'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n All categories\n </button>\n {STYLE_CATEGORIES.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => setCategory(c)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n category === c\n ? 'bg-accent-purple-subtle text-text-primary'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {categoryIcon(c, 14)} {titleCase(c)}\n </button>\n ))}\n </div>\n\n {query.isLoading ? (\n <LoadingView label=\"Loading your wardrobe…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Heart className=\"h-10 w-10\" />}\n title=\"Your wardrobe is empty\"\n description=\"Add the pieces you already own (or want) to start your digital closet.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/wardrobe/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\" /> Add piece\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((item) => (\n <div\n key={item.id}\n className=\"flex flex-col overflow-hidden rounded-lg border border-border-seam bg-bg-surface shadow-sm\"\n >\n <div className=\"flex items-start gap-3 p-4\">\n <Thumbnail icon={categoryIcon(item.category, 22)} seed={item.id} className=\"h-14 w-14 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{item.name}</p>\n {item.brandName && (\n <p className=\"truncate text-xs text-text-secondary\">{item.brandName}</p>\n )}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={item.source} />\n {item.colour && (\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {item.colour}\n </span>\n )}\n {item.size && (\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {item.size}\n </span>\n )}\n </div>\n </div>\n </div>\n {item.notes && (\n <p className=\"px-4 pb-2 text-xs text-text-secondary line-clamp-2\">{item.notes}</p>\n )}\n <div className=\"mt-auto flex items-center justify-between border-t border-border-seam px-4 py-2.5\">\n <span className=\"text-xs text-text-secondary\">\n Worn {item.timesWorn}×\n {item.lastWornAt ? ` · ${formatRelativeTime(item.lastWornAt)}` : ''}\n </span>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n disabled={logWear.isPending}\n onClick={() => logWear.mutate(item.id)}\n aria-label=\"Log a wear\"\n className=\"inline-flex items-center gap-1 rounded-lg px-2 py-1 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50\"\n >\n {logWear.isPending && logWear.variables === item.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"h-3.5 w-3.5\" />\n )}\n Wore it\n </button>\n <button\n type=\"button\"\n disabled={remove.isPending}\n onClick={() => remove.mutate(item.id)}\n aria-label=\"Remove piece\"\n className=\"inline-flex h-7 w-7 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 <Trash2 className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}