@burdenoff/microfe-coolandlovely 2026.706.1 → 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 (89) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  3. package/dist/coolandlovely/CoolAndLovelyRoutes.js +14 -6
  4. package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
  5. package/dist/coolandlovely/components/ActorName.js +29 -0
  6. package/dist/coolandlovely/components/ActorName.js.map +1 -0
  7. package/dist/coolandlovely/components/AddToLookbookButton.js +81 -0
  8. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -0
  9. package/dist/coolandlovely/components/BrandCover.js +39 -0
  10. package/dist/coolandlovely/components/BrandCover.js.map +1 -0
  11. package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
  12. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
  13. package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
  14. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
  15. package/dist/coolandlovely/components/Comments.js +109 -0
  16. package/dist/coolandlovely/components/Comments.js.map +1 -0
  17. package/dist/coolandlovely/components/Field.js +22 -2
  18. package/dist/coolandlovely/components/Field.js.map +1 -1
  19. package/dist/coolandlovely/components/FileImage.js +17 -0
  20. package/dist/coolandlovely/components/FileImage.js.map +1 -0
  21. package/dist/coolandlovely/components/FollowButton.js +24 -0
  22. package/dist/coolandlovely/components/FollowButton.js.map +1 -0
  23. package/dist/coolandlovely/components/ImageUploadField.js +153 -0
  24. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -0
  25. package/dist/coolandlovely/components/LookCard.js +77 -0
  26. package/dist/coolandlovely/components/LookCard.js.map +1 -0
  27. package/dist/coolandlovely/components/MetricsCard.js +1 -1
  28. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  29. package/dist/coolandlovely/components/ShopItemCard.js +45 -0
  30. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
  31. package/dist/coolandlovely/components/StepTracker.js +35 -0
  32. package/dist/coolandlovely/components/StepTracker.js.map +1 -0
  33. package/dist/coolandlovely/components/Thumbnail.js +25 -14
  34. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  35. package/dist/coolandlovely/components/VisibilityChip.js +32 -0
  36. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
  37. package/dist/coolandlovely/hooks/index.js +2 -0
  38. package/dist/coolandlovely/hooks/useComments.js +68 -0
  39. package/dist/coolandlovely/hooks/useComments.js.map +1 -0
  40. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +155 -60
  41. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  42. package/dist/coolandlovely/hooks/useFiles.js +111 -0
  43. package/dist/coolandlovely/hooks/useFiles.js.map +1 -0
  44. package/dist/coolandlovely/pages/HomePage.js +130 -133
  45. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  46. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +261 -228
  47. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  48. package/dist/coolandlovely/pages/brands/BrandFormPage.js +146 -104
  49. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  50. package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -11
  51. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  52. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
  53. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  54. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  55. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
  56. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +134 -107
  58. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  59. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +117 -102
  60. package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -1
  61. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -54
  62. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  63. package/dist/coolandlovely/pages/discover/DiscoverPage.js +223 -0
  64. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -0
  65. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +176 -144
  66. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  67. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +144 -130
  68. package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -1
  69. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +73 -62
  70. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  71. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +215 -102
  72. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  73. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +115 -86
  74. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  75. package/dist/coolandlovely/pages/looks/LookDetailPage.js +217 -155
  76. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  77. package/dist/coolandlovely/pages/looks/LookFormPage.js +147 -96
  78. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  79. package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -95
  80. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  81. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +376 -146
  82. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  83. package/dist/coolandlovely/pages/trends/TrendsPage.js +249 -0
  84. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -0
  85. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +136 -88
  86. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  87. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -74
  88. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  89. package/package.json +1 -1
@@ -3,16 +3,15 @@ import { cn as t } from "../../utils/cn.js";
3
3
  import { PageLayout as n } from "../../components/PageLayout.js";
4
4
  import { StatusBadge as r } from "../../components/StatusBadge.js";
5
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";
6
+ import { useLooks as o } from "../../hooks/useCoolAndLovelyApi.js";
7
+ import { LookCard as s } from "../../components/LookCard.js";
8
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";
9
+ import { EmptyState as c } from "../../components/EmptyState.js";
10
+ import { useState as l } from "react";
11
+ import { ChevronLeft as u, ChevronRight as d, Plus as f, Sparkles as p, Star as m } from "lucide-react";
12
+ import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
14
13
  //#region src/coolandlovely/pages/looks/LooksListPage.tsx
15
- var x = 24, S = [
14
+ var v = 24, y = [
16
15
  {
17
16
  value: "ALL",
18
17
  label: "All"
@@ -50,136 +49,102 @@ var x = 24, S = [
50
49
  label: "Other"
51
50
  }
52
51
  ];
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,
52
+ function b() {
53
+ let { navigateTo: b } = e(), [x, S] = l("ALL"), [C, w] = l(!1), [T, E] = l(0), D = o({
54
+ occasion: x === "ALL" ? void 0 : x,
55
+ featured: C ? !0 : void 0,
57
56
  pagination: {
58
- limit: x,
59
- offset: O
57
+ limit: v,
58
+ offset: T
60
59
  }
61
- }), j = A.data?.items ?? [], M = A.data?.pageInfo.hasMore ?? !1;
62
- return /* @__PURE__ */ b(n, {
60
+ }), O = D.data?.items ?? [], k = D.data?.pageInfo.hasMore ?? !1;
61
+ return /* @__PURE__ */ _(n, {
63
62
  title: "Looks",
64
63
  description: "Shop-the-look outfits built from real catalogue pieces",
65
- actions: /* @__PURE__ */ b("button", {
64
+ actions: /* @__PURE__ */ _("button", {
66
65
  type: "button",
67
- onClick: () => C("/looks/new"),
66
+ onClick: () => b("/looks/new"),
68
67
  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"]
68
+ children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), " New look"]
70
69
  }),
71
- children: [/* @__PURE__ */ b("div", {
70
+ children: [/* @__PURE__ */ _("div", {
72
71
  className: "mb-6 flex flex-wrap items-center gap-2",
73
- children: [S.map((e) => /* @__PURE__ */ y("button", {
72
+ children: [y.map((e) => /* @__PURE__ */ g("button", {
74
73
  type: "button",
75
74
  onClick: () => {
76
- T(e.value), k(0);
75
+ S(e.value), E(0);
77
76
  },
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"),
77
+ className: t("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", x === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
79
78
  children: e.label
80
- }, e.value)), /* @__PURE__ */ b("button", {
79
+ }, e.value)), /* @__PURE__ */ _("button", {
81
80
  type: "button",
82
81
  onClick: () => {
83
- D((e) => !e), k(0);
82
+ w((e) => !e), E(0);
84
83
  },
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"),
84
+ className: t("ml-auto inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors", C ? "bg-status-warning-bg-subtle text-status-warning-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
86
85
  children: [
87
- /* @__PURE__ */ y(_, { className: t("h-3.5 w-3.5", E && "fill-status-warning-text") }),
86
+ /* @__PURE__ */ g(m, { className: t("h-3.5 w-3.5", C && "fill-status-warning-text") }),
88
87
  " ",
89
88
  "Featured"
90
89
  ]
91
90
  })]
92
- }), A.isLoading ? /* @__PURE__ */ y(a, { label: "Loading looks…" }) : A.isError ? /* @__PURE__ */ y(i, { onRetry: () => void A.refetch() }) : j.length === 0 ? /* @__PURE__ */ y(l, {
91
+ }), D.isLoading ? /* @__PURE__ */ g(a, { label: "Loading looks…" }) : D.isError ? /* @__PURE__ */ g(i, { onRetry: () => void D.refetch() }) : O.length === 0 ? /* @__PURE__ */ g(c, {
93
92
  illustration: "empty-data",
94
- icon: /* @__PURE__ */ y(g, { className: "h-10 w-10" }),
93
+ icon: /* @__PURE__ */ g(p, { className: "h-10 w-10" }),
95
94
  title: "No looks yet",
96
95
  description: "Publish a shop-the-look outfit from catalogue pieces.",
97
- action: /* @__PURE__ */ b("button", {
96
+ action: /* @__PURE__ */ _("button", {
98
97
  type: "button",
99
- onClick: () => C("/looks/new"),
98
+ onClick: () => b("/looks/new"),
100
99
  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",
101
- children: [/* @__PURE__ */ y(h, { className: "h-4 w-4" }), " New look"]
100
+ children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), " New look"]
102
101
  })
103
- }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("div", {
102
+ }) : /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("div", {
104
103
  className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4",
105
- children: j.map((e) => /* @__PURE__ */ b("button", {
106
- type: "button",
107
- onClick: () => C(`/looks/${e.id}`),
108
- 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",
109
- children: [/* @__PURE__ */ b("div", {
110
- className: "relative",
111
- children: [
112
- /* @__PURE__ */ y(o, {
113
- icon: /* @__PURE__ */ y(g, { className: "h-9 w-9" }),
114
- seed: e.id,
115
- className: "h-40 w-full"
116
- }),
117
- e.isFeatured && /* @__PURE__ */ y("div", {
118
- className: "absolute left-2 top-2",
119
- children: /* @__PURE__ */ b("span", {
120
- 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",
121
- children: [/* @__PURE__ */ y(_, { className: "h-3 w-3 fill-status-warning-text" }), " Featured"]
122
- })
123
- }),
124
- e.publishedAt === null && /* @__PURE__ */ y("div", {
125
- className: "absolute right-2 top-2",
126
- children: /* @__PURE__ */ y(r, { status: "draft" })
127
- })
128
- ]
129
- }), /* @__PURE__ */ b("div", {
130
- className: "p-3",
131
- children: [/* @__PURE__ */ y("p", {
132
- className: "truncate text-sm font-medium text-text-primary",
133
- children: e.title
134
- }), /* @__PURE__ */ b("div", {
135
- className: "mt-1.5 flex items-center justify-between text-xs text-text-secondary",
136
- children: [/* @__PURE__ */ y("span", { children: c(e.occasion) }), /* @__PURE__ */ b("span", {
137
- className: "flex items-center gap-2",
138
- children: [/* @__PURE__ */ b("span", {
139
- className: "flex items-center gap-1",
140
- children: [
141
- /* @__PURE__ */ y(m, { className: "h-3 w-3" }),
142
- " ",
143
- e.likeCount
144
- ]
145
- }), /* @__PURE__ */ b("span", {
146
- className: "flex items-center gap-1",
147
- children: [
148
- /* @__PURE__ */ y(d, { className: "h-3 w-3" }),
149
- " ",
150
- e.saveCount
151
- ]
152
- })]
153
- })]
154
- })]
104
+ children: O.map((e) => /* @__PURE__ */ _("div", {
105
+ className: "relative",
106
+ children: [/* @__PURE__ */ g(s, {
107
+ id: e.id,
108
+ title: e.title,
109
+ occasion: e.occasion,
110
+ imageFileId: e.image?.id,
111
+ likeCount: e.likeCount,
112
+ onOpen: () => b(`/looks/${e.id}`),
113
+ showActions: !0
114
+ }), (e.isFeatured || e.publishedAt === null) && /* @__PURE__ */ _("div", {
115
+ className: "pointer-events-none absolute left-2 top-2 z-10 flex flex-col items-start gap-1",
116
+ children: [e.isFeatured && /* @__PURE__ */ _("span", {
117
+ 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 shadow-sm backdrop-blur",
118
+ children: [/* @__PURE__ */ g(m, { className: "h-3 w-3 fill-status-warning-text" }), " Featured"]
119
+ }), e.publishedAt === null && /* @__PURE__ */ g(r, { status: "draft" })]
155
120
  })]
156
121
  }, e.id))
157
- }), (O > 0 || M) && /* @__PURE__ */ b("div", {
122
+ }), (T > 0 || k) && /* @__PURE__ */ _("div", {
158
123
  className: "mt-6 flex items-center justify-center gap-2",
159
124
  children: [
160
- /* @__PURE__ */ b("button", {
125
+ /* @__PURE__ */ _("button", {
161
126
  type: "button",
162
- disabled: O === 0,
163
- onClick: () => k((e) => Math.max(0, e - x)),
127
+ disabled: T === 0,
128
+ onClick: () => E((e) => Math.max(0, e - v)),
164
129
  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",
165
- children: [/* @__PURE__ */ y(f, { className: "h-4 w-4" }), " Previous"]
130
+ children: [/* @__PURE__ */ g(u, { className: "h-4 w-4" }), " Previous"]
166
131
  }),
167
- /* @__PURE__ */ b("span", {
132
+ /* @__PURE__ */ _("span", {
168
133
  className: "text-sm text-text-secondary",
169
- children: ["Page ", O / x + 1]
134
+ children: ["Page ", T / v + 1]
170
135
  }),
171
- /* @__PURE__ */ b("button", {
136
+ /* @__PURE__ */ _("button", {
172
137
  type: "button",
173
- disabled: !M,
174
- onClick: () => k((e) => e + x),
138
+ disabled: !k,
139
+ onClick: () => E((e) => e + v),
175
140
  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",
176
- children: ["Next ", /* @__PURE__ */ y(p, { className: "h-4 w-4" })]
141
+ children: ["Next ", /* @__PURE__ */ g(d, { className: "h-4 w-4" })]
177
142
  })
178
143
  ]
179
144
  })] })]
180
145
  });
181
146
  }
182
147
  //#endregion
183
- export { C as LooksListPage };
148
+ export { b as LooksListPage };
184
149
 
185
150
  //# sourceMappingURL=LooksListPage.js.map
@@ -1 +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')} />{' '}\n 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\n icon={<Sparkles className=\"h-9 w-9\" />}\n seed={look.id}\n className=\"h-40 w-full\"\n />\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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"}
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, 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 { LookCard } from '../../components/LookCard';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLooks } from '../../hooks';\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')} />{' '}\n 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 <div key={look.id} className=\"relative\">\n <LookCard\n id={look.id}\n title={look.title}\n occasion={look.occasion}\n imageFileId={look.image?.id}\n likeCount={look.likeCount}\n onOpen={() => navigateTo(`/looks/${look.id}`)}\n showActions\n />\n {(look.isFeatured || look.publishedAt === null) && (\n <div className=\"pointer-events-none absolute left-2 top-2 z-10 flex flex-col items-start gap-1\">\n {look.isFeatured && (\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 shadow-sm backdrop-blur\">\n <Star className=\"h-3 w-3 fill-status-warning-text\" /> Featured\n </span>\n )}\n {look.publishedAt === null && <StatusBadge status=\"draft\" />}\n </div>\n )}\n </div>\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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"}