@burdenoff/microfe-coolandlovely 2026.706.2 → 2026.706.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  3. package/dist/coolandlovely/components/AddToLookbookButton.js +22 -22
  4. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
  5. package/dist/coolandlovely/components/BrandCover.js +39 -0
  6. package/dist/coolandlovely/components/BrandCover.js.map +1 -0
  7. package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
  8. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
  9. package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
  10. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
  11. package/dist/coolandlovely/components/Comments.js +4 -4
  12. package/dist/coolandlovely/components/Comments.js.map +1 -1
  13. package/dist/coolandlovely/components/FileImage.js.map +1 -1
  14. package/dist/coolandlovely/components/ImageUploadField.js +139 -57
  15. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -1
  16. package/dist/coolandlovely/components/LookCard.js +77 -0
  17. package/dist/coolandlovely/components/LookCard.js.map +1 -0
  18. package/dist/coolandlovely/components/MetricsCard.js +1 -1
  19. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  20. package/dist/coolandlovely/components/ShopItemCard.js +45 -0
  21. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
  22. package/dist/coolandlovely/components/StepTracker.js +35 -0
  23. package/dist/coolandlovely/components/StepTracker.js.map +1 -0
  24. package/dist/coolandlovely/components/Thumbnail.js +15 -11
  25. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  26. package/dist/coolandlovely/components/VisibilityChip.js +32 -0
  27. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
  28. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +94 -69
  29. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  30. package/dist/coolandlovely/hooks/useFiles.js +49 -32
  31. package/dist/coolandlovely/hooks/useFiles.js.map +1 -1
  32. package/dist/coolandlovely/pages/HomePage.js +112 -133
  33. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  34. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +253 -242
  35. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  36. package/dist/coolandlovely/pages/brands/BrandFormPage.js +116 -112
  37. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  38. package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -13
  39. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  40. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
  41. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  42. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  43. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
  44. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  45. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +131 -108
  46. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  47. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +1 -1
  48. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -56
  49. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  50. package/dist/coolandlovely/pages/discover/DiscoverPage.js +1 -1
  51. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  52. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +175 -147
  53. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  54. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +1 -1
  55. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +71 -62
  56. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +149 -127
  58. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  59. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +69 -53
  60. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  61. package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -113
  62. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  63. package/dist/coolandlovely/pages/looks/LookFormPage.js +122 -110
  64. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  65. package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -97
  66. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  67. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +372 -210
  68. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  69. package/dist/coolandlovely/pages/trends/TrendsPage.js +1 -1
  70. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -1
  71. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +130 -94
  72. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  73. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -76
  74. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  75. package/package.json +1 -1
@@ -5,8 +5,8 @@ import { StatusBadge as r } from "../../components/StatusBadge.js";
5
5
  import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
6
6
  import { Thumbnail as o } from "../../components/Thumbnail.js";
7
7
  import { useLogWardrobeWear as s, useRemoveWardrobeItem as c, useWardrobeItems as l } from "../../hooks/useCoolAndLovelyApi.js";
8
- import "../../hooks/index.js";
9
8
  import { formatRelativeTime as u, titleCase as d } from "../../utils/formatters.js";
9
+ import "../../hooks/index.js";
10
10
  import { STYLE_CATEGORIES as f, categoryIcon as p } from "../../utils/icons.js";
11
11
  import { EmptyState as m } from "../../components/EmptyState.js";
12
12
  import { useState as h } from "react";
@@ -89,81 +89,80 @@ function w() {
89
89
  children: [/* @__PURE__ */ x(y, { className: "h-4 w-4" }), " Add piece"]
90
90
  })
91
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
- fileId: e.image?.id,
102
- alt: e.name,
103
- className: "h-14 w-14 shrink-0"
104
- }), /* @__PURE__ */ S("div", {
105
- className: "min-w-0 flex-1",
106
- children: [
107
- /* @__PURE__ */ x("p", {
108
- className: "truncate text-sm font-semibold text-text-primary",
109
- children: e.name
110
- }),
111
- e.brandName && /* @__PURE__ */ x("p", {
112
- className: "truncate text-xs text-text-secondary",
113
- children: e.brandName
114
- }),
115
- /* @__PURE__ */ S("div", {
116
- className: "mt-1.5 flex flex-wrap items-center gap-1.5",
117
- children: [
118
- /* @__PURE__ */ x(r, { status: e.source }),
119
- e.colour && /* @__PURE__ */ x("span", {
120
- className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
121
- children: e.colour
122
- }),
123
- e.size && /* @__PURE__ */ x("span", {
124
- className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
125
- children: e.size
126
- })
127
- ]
128
- })
129
- ]
130
- })]
131
- }),
132
- e.notes && /* @__PURE__ */ x("p", {
133
- className: "px-4 pb-2 text-xs text-text-secondary line-clamp-2",
134
- children: e.notes
135
- }),
136
- /* @__PURE__ */ S("div", {
137
- className: "mt-auto flex items-center justify-between border-t border-border-seam px-4 py-2.5",
138
- children: [/* @__PURE__ */ S("span", {
139
- className: "text-xs text-text-secondary",
140
- children: [
141
- "Worn ",
142
- e.timesWorn,
143
- "×",
144
- e.lastWornAt ? ` · ${u(e.lastWornAt)}` : ""
145
- ]
146
- }), /* @__PURE__ */ S("div", {
147
- className: "flex items-center gap-1",
148
- children: [/* @__PURE__ */ S("button", {
149
- type: "button",
150
- disabled: A.isPending,
151
- onClick: () => A.mutate(e.id),
152
- "aria-label": "Log a wear",
153
- 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",
154
- 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"]
155
- }), /* @__PURE__ */ x("button", {
156
- type: "button",
157
- disabled: j.isPending,
158
- onClick: () => j.mutate(e.id),
159
- "aria-label": "Remove piece",
160
- 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",
161
- children: /* @__PURE__ */ x(b, { className: "h-3.5 w-3.5" })
162
- })]
163
- })]
164
- })
165
- ]
166
- }, e.id))
92
+ className: "grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
93
+ children: M.map((e) => {
94
+ let t = e.brandName || e.colour || d(e.category), n = A.isPending && A.variables === e.id, i = j.isPending && j.variables === e.id;
95
+ return /* @__PURE__ */ S("div", {
96
+ className: "group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg",
97
+ children: [/* @__PURE__ */ S("div", {
98
+ className: "relative",
99
+ children: [
100
+ /* @__PURE__ */ x(o, {
101
+ icon: p(e.category, 40),
102
+ seed: e.id,
103
+ fileId: e.image?.id,
104
+ alt: e.name,
105
+ className: "aspect-[4/5] w-full rounded-none"
106
+ }),
107
+ /* @__PURE__ */ S("div", {
108
+ className: "absolute right-2.5 top-2.5 flex h-12 w-12 flex-col items-center justify-center rounded-full bg-bg-surface/90 text-center leading-none shadow-sm ring-1 ring-[var(--color-accent-soft)] backdrop-blur",
109
+ children: [/* @__PURE__ */ x("span", {
110
+ className: "text-[9px] font-medium uppercase tracking-wide text-text-secondary",
111
+ children: "worn"
112
+ }), /* @__PURE__ */ S("span", {
113
+ className: "text-sm font-bold tabular-nums text-text-primary",
114
+ children: [e.timesWorn, "×"]
115
+ })]
116
+ }),
117
+ /* @__PURE__ */ S("div", {
118
+ className: "absolute inset-x-0 bottom-0 flex items-center gap-1.5 bg-gradient-to-t from-bg-canvas/90 to-transparent p-2.5 pt-8 opacity-100 transition-opacity duration-200 sm:opacity-0 sm:focus-within:opacity-100 sm:group-hover:opacity-100",
119
+ children: [/* @__PURE__ */ S("button", {
120
+ type: "button",
121
+ disabled: A.isPending,
122
+ onClick: () => A.mutate(e.id),
123
+ "aria-label": `Log a wear for ${e.name}`,
124
+ className: "inline-flex items-center gap-1 rounded-lg bg-bg-surface/90 px-2.5 py-1 text-xs font-medium text-status-success-text shadow-sm backdrop-blur transition-colors hover:bg-bg-surface disabled:opacity-50",
125
+ children: [n ? /* @__PURE__ */ x(v, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ x(g, { className: "h-3.5 w-3.5" }), "Wore it"]
126
+ }), /* @__PURE__ */ x("button", {
127
+ type: "button",
128
+ disabled: j.isPending,
129
+ onClick: () => j.mutate(e.id),
130
+ "aria-label": `Remove ${e.name}`,
131
+ className: "ml-auto inline-flex h-7 w-7 items-center justify-center rounded-lg bg-bg-surface/90 text-text-secondary shadow-sm backdrop-blur transition-colors hover:text-status-error-text disabled:opacity-50",
132
+ children: i ? /* @__PURE__ */ x(v, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ x(b, { className: "h-3.5 w-3.5" })
133
+ })]
134
+ })
135
+ ]
136
+ }), /* @__PURE__ */ S("div", {
137
+ className: "flex flex-1 flex-col p-4",
138
+ children: [
139
+ /* @__PURE__ */ x("p", {
140
+ className: "truncate text-sm font-semibold text-text-primary",
141
+ children: e.name
142
+ }),
143
+ /* @__PURE__ */ x("p", {
144
+ className: "truncate text-xs text-text-secondary",
145
+ children: t
146
+ }),
147
+ /* @__PURE__ */ S("div", {
148
+ className: "mt-2 flex flex-wrap items-center gap-1.5",
149
+ children: [/* @__PURE__ */ x(r, { status: e.source }), e.size && /* @__PURE__ */ x("span", {
150
+ className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
151
+ children: e.size
152
+ })]
153
+ }),
154
+ e.notes && /* @__PURE__ */ x("p", {
155
+ className: "mt-2 line-clamp-2 text-xs text-text-secondary",
156
+ children: e.notes
157
+ }),
158
+ /* @__PURE__ */ x("div", {
159
+ className: "mt-3 border-t border-border-seam pt-2 text-xs text-text-secondary",
160
+ children: e.lastWornAt ? `Last worn ${u(e.lastWornAt)}` : "Not worn yet"
161
+ })
162
+ ]
163
+ })]
164
+ }, e.id);
165
+ })
167
166
  })
168
167
  ]
169
168
  });
@@ -1 +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\n icon={categoryIcon(item.category, 22)}\n seed={item.id}\n fileId={item.image?.id}\n alt={item.name}\n className=\"h-14 w-14 shrink-0\"\n />\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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"}
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) rendered\n * as an image-forward gallery — each card is a large piece photo with a circular\n * \"Worn N×\" badge and a hover-revealed action bar (wear log via\n * `cooLogWardrobeWear`, removal via `cooRemoveWardrobeItem`), with category &\n * source filters.\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-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {items.map((item) => {\n const subtitle = item.brandName || item.colour || titleCase(item.category);\n const wearing = logWear.isPending && logWear.variables === item.id;\n const removing = remove.isPending && remove.variables === item.id;\n return (\n <div\n key={item.id}\n className=\"group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg\"\n >\n {/* Image-forward hero */}\n <div className=\"relative\">\n <Thumbnail\n icon={categoryIcon(item.category, 40)}\n seed={item.id}\n fileId={item.image?.id}\n alt={item.name}\n className=\"aspect-[4/5] w-full rounded-none\"\n />\n\n {/* Worn-count badge */}\n <div className=\"absolute right-2.5 top-2.5 flex h-12 w-12 flex-col items-center justify-center rounded-full bg-bg-surface/90 text-center leading-none shadow-sm ring-1 ring-[var(--color-accent-soft)] backdrop-blur\">\n <span className=\"text-[9px] font-medium uppercase tracking-wide text-text-secondary\">\n worn\n </span>\n <span className=\"text-sm font-bold tabular-nums text-text-primary\">\n {item.timesWorn}×\n </span>\n </div>\n\n {/* Hover action bar (keeps the resting card pure imagery) */}\n <div className=\"absolute inset-x-0 bottom-0 flex items-center gap-1.5 bg-gradient-to-t from-bg-canvas/90 to-transparent p-2.5 pt-8 opacity-100 transition-opacity duration-200 sm:opacity-0 sm:focus-within:opacity-100 sm:group-hover:opacity-100\">\n <button\n type=\"button\"\n disabled={logWear.isPending}\n onClick={() => logWear.mutate(item.id)}\n aria-label={`Log a wear for ${item.name}`}\n className=\"inline-flex items-center gap-1 rounded-lg bg-bg-surface/90 px-2.5 py-1 text-xs font-medium text-status-success-text shadow-sm backdrop-blur transition-colors hover:bg-bg-surface disabled:opacity-50\"\n >\n {wearing ? (\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 ${item.name}`}\n className=\"ml-auto inline-flex h-7 w-7 items-center justify-center rounded-lg bg-bg-surface/90 text-text-secondary shadow-sm backdrop-blur transition-colors hover:text-status-error-text disabled:opacity-50\"\n >\n {removing ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n </div>\n\n {/* Details */}\n <div className=\"flex flex-1 flex-col p-4\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{item.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{subtitle}</p>\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={item.source} />\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 {item.notes && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{item.notes}</p>\n )}\n <div className=\"mt-3 border-t border-border-seam pt-2 text-xs text-text-secondary\">\n {item.lastWornAt\n ? `Last worn ${formatRelativeTime(item.lastWornAt)}`\n : 'Not worn yet'}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAyBA,IAAM,IAAsE;CAC1E;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAY,OAAO;EAAY;CACxC;EAAE,OAAO;EAAyB,OAAO;EAAa;CACtD;EAAE,OAAO;EAAU,OAAO;EAAU;CACrC;AAED,SAAgB,IAAmB;CACjC,IAAM,EAAE,kBAAe,GAAkB,EACnC,CAAC,GAAU,KAAe,EAAmC,MAAM,EACnE,CAAC,GAAQ,KAAa,EAAoC,MAAM,EAEhE,IAAQ,EAAiB;EAC7B,UAAU,MAAa,QAAQ,KAAA,IAAY;EAC3C,QAAQ,MAAW,QAAQ,KAAA,IAAY;EACxC,CAAC,EACI,IAAU,GAAoB,EAC9B,IAAS,GAAuB,EAEhC,IAAQ,EAAM,QAAQ,EAAE;AAE9B,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,gBAAgB;GAC1C,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,aACrB;;YAVb;GAcE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MACZ,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe,EAAU,EAAI,MAAM;KACnC,WAAW,EACT,kEACA,MAAW,EAAI,QACX,kDACA,2DACL;eAEA,EAAI;KACE,EAXF,EAAI,MAWF,CACT;IACE,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAY,MAAM;KACjC,WAAW,EACT,kEACA,MAAa,QACT,8CACA,2DACL;eACF;KAEQ,CAAA,EACR,EAAiB,KAAK,MACrB,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe,EAAY,EAAE;KAC7B,WAAW,EACT,mGACA,MAAa,IACT,8CACA,2DACL;eATH;MAWG,EAAa,GAAG,GAAG;MAAC;MAAE,EAAU,EAAE;MAC5B;OAXF,EAWE,CACT,CACE;;GAEL,EAAM,YACL,kBAAC,GAAD,EAAa,OAAM,0BAA2B,CAAA,GAC5C,EAAM,UACR,kBAAC,GAAD,EAAW,eAAe,KAAK,EAAM,SAAS,EAAI,CAAA,GAChD,EAAM,WAAW,IACnB,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;IACrC,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,gBAAgB;KAC1C,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,aACrB;;IAEX,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,MAAS;KACnB,IAAM,IAAW,EAAK,aAAa,EAAK,UAAU,EAAU,EAAK,SAAS,EACpE,IAAU,EAAQ,aAAa,EAAQ,cAAc,EAAK,IAC1D,IAAW,EAAO,aAAa,EAAO,cAAc,EAAK;AAC/D,YACE,kBAAC,OAAD;MAEE,WAAU;gBAFZ,CAKE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SACE,MAAM,EAAa,EAAK,UAAU,GAAG;SACrC,MAAM,EAAK;SACX,QAAQ,EAAK,OAAO;SACpB,KAAK,EAAK;SACV,WAAU;SACV,CAAA;QAGF,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAqE;UAE9E,CAAA,EACP,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CACG,EAAK,WAAU,IACX;YACH;;QAGN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,MAAK;UACL,UAAU,EAAQ;UAClB,eAAe,EAAQ,OAAO,EAAK,GAAG;UACtC,cAAY,kBAAkB,EAAK;UACnC,WAAU;oBALZ,CAOG,IACC,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAc,WAAU,eAAgB,CAAA,EACxC,UAEK;aACT,kBAAC,UAAD;UACE,MAAK;UACL,UAAU,EAAO;UACjB,eAAe,EAAO,OAAO,EAAK,GAAG;UACrC,cAAY,UAAU,EAAK;UAC3B,WAAU;oBAET,IACC,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;UAE7B,CAAA,CACL;;QACF;UAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,KAAD;SAAG,WAAU;mBAAoD,EAAK;SAAS,CAAA;QAC/E,kBAAC,KAAD;SAAG,WAAU;mBAAwC;SAAa,CAAA;QAClE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAa,QAAQ,EAAK,QAAU,CAAA,EACnC,EAAK,QACJ,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAK;UACD,CAAA,CAEL;;QACL,EAAK,SACJ,kBAAC,KAAD;SAAG,WAAU;mBAAiD,EAAK;SAAU,CAAA;QAE/E,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAK,aACF,aAAa,EAAmB,EAAK,WAAW,KAChD;SACA,CAAA;QACF;SACF;QA5EC,EAAK,GA4EN;MAER;IACE,CAAA;GAEG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-coolandlovely",
3
- "version": "2026.706.2",
3
+ "version": "2026.706.3",
4
4
  "description": "Burdenoff CoolAndLovely Microfrontend",
5
5
  "type": "module",
6
6
  "files": [