@burdenoff/microfe-coolandlovely 2026.820.3 → 2026.820.5

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 (39) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.d.ts.map +1 -1
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js +90 -54
  3. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  4. package/dist/coolandlovely/CoolAndLovelyRoutes.d.ts.map +1 -1
  5. package/dist/coolandlovely/CoolAndLovelyRoutes.js +25 -6
  6. package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
  7. package/dist/coolandlovely/hooks/index.d.ts +2 -2
  8. package/dist/coolandlovely/hooks/index.d.ts.map +1 -1
  9. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts +19 -1
  10. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts.map +1 -1
  11. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +121 -14
  12. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  13. package/dist/coolandlovely/index.d.ts +1 -1
  14. package/dist/coolandlovely/index.d.ts.map +1 -1
  15. package/dist/coolandlovely/pages/admin/AdminBrandsPage.d.ts +9 -0
  16. package/dist/coolandlovely/pages/admin/AdminBrandsPage.d.ts.map +1 -0
  17. package/dist/coolandlovely/pages/admin/AdminBrandsPage.js +82 -0
  18. package/dist/coolandlovely/pages/admin/AdminBrandsPage.js.map +1 -0
  19. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.d.ts +9 -0
  20. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.d.ts.map +1 -0
  21. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js +142 -0
  22. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js.map +1 -0
  23. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.d.ts +9 -0
  24. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.d.ts.map +1 -0
  25. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js +139 -0
  26. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js.map +1 -0
  27. package/dist/coolandlovely/pages/admin/index.d.ts +8 -0
  28. package/dist/coolandlovely/pages/admin/index.d.ts.map +1 -0
  29. package/dist/coolandlovely/pages/discover/DiscoverPage.d.ts +7 -6
  30. package/dist/coolandlovely/pages/discover/DiscoverPage.d.ts.map +1 -1
  31. package/dist/coolandlovely/pages/discover/DiscoverPage.js +480 -165
  32. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  33. package/dist/coolandlovely/types/index.d.ts +24 -0
  34. package/dist/coolandlovely/types/index.d.ts.map +1 -1
  35. package/dist/stories/DiscoverPage.stories.d.ts +11 -0
  36. package/dist/stories/DiscoverPage.stories.d.ts.map +1 -0
  37. package/dist/stories/__fixtures__/coolandlovely.d.ts +3 -1
  38. package/dist/stories/__fixtures__/coolandlovely.d.ts.map +1 -1
  39. package/package.json +1 -1
@@ -0,0 +1,82 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { PageLayout as t } from "../../components/PageLayout.js";
3
+ import { StatusBadge as n } from "../../components/StatusBadge.js";
4
+ import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
5
+ import { Thumbnail as a } from "../../components/Thumbnail.js";
6
+ import { useApproveBrand as o, useBrands as s, useRejectBrand as c } from "../../hooks/useCoolAndLovelyApi.js";
7
+ import "../../hooks/index.js";
8
+ import { EmptyState as l } from "../../components/EmptyState.js";
9
+ import { Check as u, Store as d, X as f } from "lucide-react";
10
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
11
+ //#region src/coolandlovely/pages/admin/AdminBrandsPage.tsx
12
+ function h({ brand: t }) {
13
+ let r = o(), i = c(), s = r.isPending || i.isPending;
14
+ return /* @__PURE__ */ m("div", {
15
+ className: "flex items-center gap-4 rounded-lg border border-border-seam bg-bg-surface p-4",
16
+ children: [
17
+ /* @__PURE__ */ p(a, {
18
+ icon: /* @__PURE__ */ p(d, { className: "h-5 w-5" }),
19
+ seed: t.id,
20
+ fileId: t.logo?.id ?? t.cover?.id,
21
+ alt: t.name,
22
+ className: "h-12 w-12 shrink-0 rounded-lg"
23
+ }),
24
+ /* @__PURE__ */ m("div", {
25
+ className: "min-w-0 flex-1",
26
+ children: [
27
+ /* @__PURE__ */ m("div", {
28
+ className: "flex items-center gap-2",
29
+ children: [/* @__PURE__ */ p("h3", {
30
+ className: "truncate text-sm font-semibold text-text-primary",
31
+ children: t.name
32
+ }), /* @__PURE__ */ p(n, { status: t.status })]
33
+ }),
34
+ /* @__PURE__ */ m("p", {
35
+ className: "truncate text-xs text-text-secondary",
36
+ children: ["@", t.handle]
37
+ }),
38
+ t.tagline && /* @__PURE__ */ p("p", {
39
+ className: "mt-1 truncate text-xs text-text-secondary",
40
+ children: t.tagline
41
+ })
42
+ ]
43
+ }),
44
+ /* @__PURE__ */ m("div", {
45
+ className: "flex shrink-0 items-center gap-2",
46
+ children: [/* @__PURE__ */ m("button", {
47
+ type: "button",
48
+ disabled: s,
49
+ onClick: () => r.mutate(t.id),
50
+ className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-success-bg-subtle text-status-success-text hover:bg-status-success-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
51
+ children: [/* @__PURE__ */ p(u, { className: "h-4 w-4" }), " Approve"]
52
+ }), /* @__PURE__ */ m("button", {
53
+ type: "button",
54
+ disabled: s,
55
+ onClick: () => i.mutate(t.id),
56
+ className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-error-bg-subtle text-status-error-text hover:bg-status-error-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
57
+ children: [/* @__PURE__ */ p(f, { className: "h-4 w-4" }), " Reject"]
58
+ })]
59
+ })
60
+ ]
61
+ });
62
+ }
63
+ function g() {
64
+ let e = s({ status: "PENDING_REVIEW" }), n = e.data?.items ?? [];
65
+ return /* @__PURE__ */ p(t, {
66
+ title: "Admin — Brands",
67
+ description: "Approve or reject storefronts pending review",
68
+ children: e.isLoading ? /* @__PURE__ */ p(i, { label: "Loading pending brands…" }) : e.isError ? /* @__PURE__ */ p(r, { onRetry: () => void e.refetch() }) : n.length === 0 ? /* @__PURE__ */ p(l, {
69
+ illustration: "empty-data",
70
+ icon: /* @__PURE__ */ p(d, { className: "h-10 w-10" }),
71
+ title: "No pending brands",
72
+ description: "All brand storefronts have been reviewed."
73
+ }) : /* @__PURE__ */ p("div", {
74
+ className: "space-y-3",
75
+ children: n.map((e) => /* @__PURE__ */ p(h, { brand: e }, e.id))
76
+ })
77
+ });
78
+ }
79
+ //#endregion
80
+ export { g as AdminBrandsPage };
81
+
82
+ //# sourceMappingURL=AdminBrandsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminBrandsPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/admin/AdminBrandsPage.tsx"],"sourcesContent":["/**\n * Admin Brands Page\n * @module coolandlovely/pages/admin\n *\n * Curation surface for brand storefront approvals. Lists brands pending review\n * and lets admins approve (PUBLISHED) or reject (DRAFT) them.\n */\n\nimport { Check, Store, X } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { useBrands, useApproveBrand, useRejectBrand } from '../../hooks';\nimport { cn } from '../../utils/cn';\nimport type { CooBrand } from '../../types';\n\nfunction BrandReviewRow({ brand }: { brand: CooBrand }) {\n const approve = useApproveBrand();\n const reject = useRejectBrand();\n const isBusy = approve.isPending || reject.isPending;\n\n return (\n <div className=\"flex items-center gap-4 rounded-lg border border-border-seam bg-bg-surface p-4\">\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n fileId={brand.logo?.id ?? brand.cover?.id}\n alt={brand.name}\n className=\"h-12 w-12 shrink-0 rounded-lg\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{brand.name}</h3>\n <StatusBadge status={brand.status} />\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n {brand.tagline && (\n <p className=\"mt-1 truncate text-xs text-text-secondary\">{brand.tagline}</p>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => approve.mutate(brand.id)}\n className={cn(\n 'inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium',\n 'bg-status-success-bg-subtle text-status-success-text hover:bg-status-success-bg-subtle/80',\n 'disabled:cursor-not-allowed disabled:opacity-50'\n )}\n >\n <Check className=\"h-4 w-4\" /> Approve\n </button>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => reject.mutate(brand.id)}\n className={cn(\n 'inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium',\n 'bg-status-error-bg-subtle text-status-error-text hover:bg-status-error-bg-subtle/80',\n 'disabled:cursor-not-allowed disabled:opacity-50'\n )}\n >\n <X className=\"h-4 w-4\" /> Reject\n </button>\n </div>\n </div>\n );\n}\n\nexport function AdminBrandsPage() {\n const query = useBrands({ status: 'PENDING_REVIEW' });\n const brands = query.data?.items ?? [];\n\n return (\n <PageLayout title=\"Admin — Brands\" description=\"Approve or reject storefronts pending review\">\n {query.isLoading ? (\n <LoadingView label=\"Loading pending brands…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : brands.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Store className=\"h-10 w-10\" />}\n title=\"No pending brands\"\n description=\"All brand storefronts have been reviewed.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {brands.map((brand) => (\n <BrandReviewRow key={brand.id} brand={brand} />\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;AAkBA,SAAS,EAAe,EAAE,YAA8B;CACtD,IAAM,IAAU,GAAiB,EAC3B,IAAS,GAAgB,EACzB,IAAS,EAAQ,aAAa,EAAO;AAE3C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;IACnC,MAAM,EAAM;IACZ,QAAQ,EAAM,MAAM,MAAM,EAAM,OAAO;IACvC,KAAK,EAAM;IACX,WAAU;IACV,CAAA;GACF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAoD,EAAM;OAAU,CAAA,EAClF,kBAAC,GAAD,EAAa,QAAQ,EAAM,QAAU,CAAA,CACjC;;KACN,kBAAC,KAAD;MAAG,WAAU;gBAAb,CAAoD,KAAE,EAAM,OAAW;;KACtE,EAAM,WACL,kBAAC,KAAD;MAAG,WAAU;gBAA6C,EAAM;MAAY,CAAA;KAE1E;;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,eAAe,EAAQ,OAAO,EAAM,GAAG;KACvC,WAAW,EACT,6EACA,6FACA,kDACD;eARH,CAUE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA,EAAA,WACtB;QACT,kBAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,eAAe,EAAO,OAAO,EAAM,GAAG;KACtC,WAAW,EACT,6EACA,uFACA,kDACD;eARH,CAUE,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA,EAAA,UAClB;OACL;;GACF;;;AAIV,SAAgB,IAAkB;CAChC,IAAM,IAAQ,EAAU,EAAE,QAAQ,kBAAkB,CAAC,EAC/C,IAAS,EAAM,MAAM,SAAS,EAAE;AAEtC,QACE,kBAAC,GAAD;EAAY,OAAM;EAAiB,aAAY;YAC5C,EAAM,YACL,kBAAC,GAAD,EAAa,OAAM,2BAA4B,CAAA,GAC7C,EAAM,UACR,kBAAC,GAAD,EAAW,eAAe,KAAK,EAAM,SAAS,EAAI,CAAA,GAChD,EAAO,WAAW,IACpB,kBAAC,GAAD;GACE,cAAa;GACb,MAAM,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;GACrC,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAO,KAAK,MACX,kBAAC,GAAD,EAAsC,UAAS,EAA1B,EAAM,GAAoB,CAC/C;GACE,CAAA;EAEG,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Collaborations Page
3
+ * @module coolandlovely/pages/admin
4
+ *
5
+ * Curation surface for brand × creator collaborations. Lists collaborations that
6
+ * can be advanced through their lifecycle via APPROVE/REJECT admin decisions.
7
+ */
8
+ export declare function AdminCollaborationsPage(): import("react").JSX.Element;
9
+ //# sourceMappingURL=AdminCollaborationsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminCollaborationsPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/admin/AdminCollaborationsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwGH,wBAAgB,uBAAuB,gCAgDtC"}
@@ -0,0 +1,142 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { PageLayout as t } from "../../components/PageLayout.js";
3
+ import { StatusBadge as n } from "../../components/StatusBadge.js";
4
+ import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
5
+ import { useCollaborations as a, useReviewCollaboration as o } from "../../hooks/useCoolAndLovelyApi.js";
6
+ import { formatPrice as s, titleCase as c } from "../../utils/formatters.js";
7
+ import "../../hooks/index.js";
8
+ import { EmptyState as l } from "../../components/EmptyState.js";
9
+ import { ActorName as u } from "../../components/ActorName.js";
10
+ import { useState as d } from "react";
11
+ import { Check as f, FileText as p, Handshake as m, X as h } from "lucide-react";
12
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
13
+ //#region src/coolandlovely/pages/admin/AdminCollaborationsPage.tsx
14
+ var v = [
15
+ "PROPOSED",
16
+ "ACCEPTED",
17
+ "IN_PROGRESS"
18
+ ], y = [
19
+ {
20
+ value: "ALL",
21
+ label: "All"
22
+ },
23
+ {
24
+ value: "PROPOSED",
25
+ label: "Proposed"
26
+ },
27
+ {
28
+ value: "ACCEPTED",
29
+ label: "Accepted"
30
+ },
31
+ {
32
+ value: "IN_PROGRESS",
33
+ label: "In progress"
34
+ }
35
+ ];
36
+ function b({ collaboration: t }) {
37
+ let r = o(), [i, a] = d(""), l = v.includes(t.status), m = r.isPending;
38
+ return /* @__PURE__ */ _("div", {
39
+ className: "rounded-lg border border-border-seam bg-bg-surface p-4",
40
+ children: [
41
+ /* @__PURE__ */ g("div", {
42
+ className: "flex items-start gap-3",
43
+ children: /* @__PURE__ */ _("div", {
44
+ className: "min-w-0 flex-1",
45
+ children: [/* @__PURE__ */ _("div", {
46
+ className: "flex items-center gap-2",
47
+ children: [/* @__PURE__ */ g("h3", {
48
+ className: "truncate text-sm font-semibold text-text-primary",
49
+ children: t.title
50
+ }), /* @__PURE__ */ g(n, { status: t.status })]
51
+ }), /* @__PURE__ */ _("p", {
52
+ className: "mt-0.5 text-xs text-text-secondary",
53
+ children: [
54
+ t.brand?.name ?? "Brand",
55
+ " × ",
56
+ /* @__PURE__ */ g(u, { id: t.creatorId })
57
+ ]
58
+ })]
59
+ })
60
+ }),
61
+ t.brief && /* @__PURE__ */ g("p", {
62
+ className: "mt-3 line-clamp-2 text-sm text-text-secondary",
63
+ children: t.brief
64
+ }),
65
+ /* @__PURE__ */ _("div", {
66
+ className: "mt-3 flex flex-wrap items-center gap-2 text-xs text-text-secondary",
67
+ children: [/* @__PURE__ */ g("span", {
68
+ className: "rounded-full bg-bg-sunken px-2 py-0.5",
69
+ children: c(t.role)
70
+ }), /* @__PURE__ */ g("span", { children: s(t.compensation, t.currency) })]
71
+ }),
72
+ l && /* @__PURE__ */ _("div", {
73
+ className: "mt-4 space-y-3 border-t border-border-seam pt-3",
74
+ children: [/* @__PURE__ */ _("div", {
75
+ className: "flex items-center gap-2",
76
+ children: [/* @__PURE__ */ g(p, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ g("input", {
77
+ type: "text",
78
+ value: i,
79
+ onChange: (e) => a(e.target.value),
80
+ placeholder: "Optional review notes",
81
+ className: "flex-1 rounded-md border border-border-seam bg-bg-surface px-3 py-1.5 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:ring-1 focus:ring-primary"
82
+ })]
83
+ }), /* @__PURE__ */ _("div", {
84
+ className: "flex items-center justify-end gap-2",
85
+ children: [/* @__PURE__ */ _("button", {
86
+ type: "button",
87
+ disabled: m,
88
+ onClick: () => r.mutate({
89
+ id: t.id,
90
+ input: {
91
+ decision: "REJECT",
92
+ notes: i
93
+ }
94
+ }),
95
+ className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-error-bg-subtle text-status-error-text hover:bg-status-error-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
96
+ children: [/* @__PURE__ */ g(h, { className: "h-4 w-4" }), " Reject"]
97
+ }), /* @__PURE__ */ _("button", {
98
+ type: "button",
99
+ disabled: m,
100
+ onClick: () => r.mutate({
101
+ id: t.id,
102
+ input: {
103
+ decision: "APPROVE",
104
+ notes: i
105
+ }
106
+ }),
107
+ className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-success-bg-subtle text-status-success-text hover:bg-status-success-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
108
+ children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), " Approve"]
109
+ })]
110
+ })]
111
+ })
112
+ ]
113
+ });
114
+ }
115
+ function x() {
116
+ let [n, o] = d("ALL"), s = a({ status: n === "ALL" ? void 0 : n }), c = s.data ?? [];
117
+ return /* @__PURE__ */ _(t, {
118
+ title: "Admin — Collaborations",
119
+ description: "Review and advance brand × creator partnerships",
120
+ children: [/* @__PURE__ */ g("div", {
121
+ className: "mb-6 flex flex-wrap items-center gap-2",
122
+ children: y.map((t) => /* @__PURE__ */ g("button", {
123
+ type: "button",
124
+ onClick: () => o(t.value),
125
+ className: e("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", n === t.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
126
+ children: t.label
127
+ }, t.value))
128
+ }), s.isLoading ? /* @__PURE__ */ g(i, { label: "Loading collaborations…" }) : s.isError ? /* @__PURE__ */ g(r, { onRetry: () => void s.refetch() }) : c.length === 0 ? /* @__PURE__ */ g(l, {
129
+ illustration: "empty-data",
130
+ icon: /* @__PURE__ */ g(m, { className: "h-10 w-10" }),
131
+ title: "No collaborations to review",
132
+ description: "There are no collaborations matching this filter."
133
+ }) : /* @__PURE__ */ g("div", {
134
+ className: "space-y-3",
135
+ children: c.map((e) => /* @__PURE__ */ g(b, { collaboration: e }, e.id))
136
+ })]
137
+ });
138
+ }
139
+ //#endregion
140
+ export { x as AdminCollaborationsPage };
141
+
142
+ //# sourceMappingURL=AdminCollaborationsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminCollaborationsPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/admin/AdminCollaborationsPage.tsx"],"sourcesContent":["/**\n * Admin Collaborations Page\n * @module coolandlovely/pages/admin\n *\n * Curation surface for brand × creator collaborations. Lists collaborations that\n * can be advanced through their lifecycle via APPROVE/REJECT admin decisions.\n */\n\nimport { useState } from 'react';\nimport { Check, FileText, Handshake, X } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { ActorName } from '../../components/ActorName';\nimport { useCollaborations, useReviewCollaboration } from '../../hooks';\nimport { formatPrice, titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooCollaboration, CooCollaborationStatus } from '../../types';\n\nconst REVIEWABLE_STATUSES: CooCollaborationStatus[] = ['PROPOSED', 'ACCEPTED', 'IN_PROGRESS'];\n\nconst STATUS_OPTIONS: Array<{ value: CooCollaborationStatus | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'PROPOSED', label: 'Proposed' },\n { value: 'ACCEPTED', label: 'Accepted' },\n { value: 'IN_PROGRESS', label: 'In progress' },\n];\n\nfunction CollaborationReviewRow({ collaboration }: { collaboration: CooCollaboration }) {\n const review = useReviewCollaboration();\n const [notes, setNotes] = useState('');\n const isReviewable = REVIEWABLE_STATUSES.includes(collaboration.status);\n const isBusy = review.isPending;\n\n return (\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {collaboration.title}\n </h3>\n <StatusBadge status={collaboration.status} />\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {collaboration.brand?.name ?? 'Brand'} × <ActorName id={collaboration.creatorId} />\n </p>\n </div>\n </div>\n\n {collaboration.brief && (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{collaboration.brief}</p>\n )}\n\n <div className=\"mt-3 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {titleCase(collaboration.role)}\n </span>\n <span>{formatPrice(collaboration.compensation, collaboration.currency)}</span>\n </div>\n\n {isReviewable && (\n <div className=\"mt-4 space-y-3 border-t border-border-seam pt-3\">\n <div className=\"flex items-center gap-2\">\n <FileText className=\"h-4 w-4 text-text-secondary\" />\n <input\n type=\"text\"\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder=\"Optional review notes\"\n className=\"flex-1 rounded-md border border-border-seam bg-bg-surface px-3 py-1.5 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() =>\n review.mutate({ id: collaboration.id, input: { decision: 'REJECT', notes } })\n }\n className={cn(\n 'inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium',\n 'bg-status-error-bg-subtle text-status-error-text hover:bg-status-error-bg-subtle/80',\n 'disabled:cursor-not-allowed disabled:opacity-50'\n )}\n >\n <X className=\"h-4 w-4\" /> Reject\n </button>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() =>\n review.mutate({ id: collaboration.id, input: { decision: 'APPROVE', notes } })\n }\n className={cn(\n 'inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium',\n 'bg-status-success-bg-subtle text-status-success-text hover:bg-status-success-bg-subtle/80',\n 'disabled:cursor-not-allowed disabled:opacity-50'\n )}\n >\n <Check className=\"h-4 w-4\" /> Approve\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport function AdminCollaborationsPage() {\n const [status, setStatus] = useState<CooCollaborationStatus | 'ALL'>('ALL');\n const query = useCollaborations({ status: status === 'ALL' ? undefined : status });\n const collaborations = query.data ?? [];\n\n return (\n <PageLayout\n title=\"Admin — Collaborations\"\n description=\"Review and advance brand × creator partnerships\"\n >\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n {STATUS_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => setStatus(opt.value)}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n status === 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 {query.isLoading ? (\n <LoadingView label=\"Loading collaborations…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : collaborations.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Handshake className=\"h-10 w-10\" />}\n title=\"No collaborations to review\"\n description=\"There are no collaborations matching this filter.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {collaborations.map((collab) => (\n <CollaborationReviewRow key={collab.id} collaboration={collab} />\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAoBA,IAAM,IAAgD;CAAC;CAAY;CAAY;CAAc,EAEvF,IAAkF;CACtF;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAY,OAAO;EAAY;CACxC;EAAE,OAAO;EAAY,OAAO;EAAY;CACxC;EAAE,OAAO;EAAe,OAAO;EAAe;CAC/C;AAED,SAAS,EAAuB,EAAE,oBAAsD;CACtF,IAAM,IAAS,GAAwB,EACjC,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,IAAe,EAAoB,SAAS,EAAc,OAAO,EACjE,IAAS,EAAO;AAEtB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAc;OACZ,CAAA,EACL,kBAAC,GAAD,EAAa,QAAQ,EAAc,QAAU,CAAA,CACzC;SACN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAc,OAAO,QAAQ;OAAQ;OAAG,kBAAC,GAAD,EAAW,IAAI,EAAc,WAAa,CAAA;OACjF;QACA;;IACF,CAAA;GAEL,EAAc,SACb,kBAAC,KAAD;IAAG,WAAU;cAAiD,EAAc;IAAU,CAAA;GAGxF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eACb,EAAU,EAAc,KAAK;KACzB,CAAA,EACP,kBAAC,QAAD,EAAA,UAAO,EAAY,EAAc,cAAc,EAAc,SAAS,EAAQ,CAAA,CAC1E;;GAEL,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAU,WAAU,+BAAgC,CAAA,EACpD,kBAAC,SAAD;MACE,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;MACzC,aAAY;MACZ,WAAU;MACV,CAAA,CACE;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU;MACV,eACE,EAAO,OAAO;OAAE,IAAI,EAAc;OAAI,OAAO;QAAE,UAAU;QAAU;QAAO;OAAE,CAAC;MAE/E,WAAW,EACT,6EACA,uFACA,kDACD;gBAVH,CAYE,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA,EAAA,UAClB;SACT,kBAAC,UAAD;MACE,MAAK;MACL,UAAU;MACV,eACE,EAAO,OAAO;OAAE,IAAI,EAAc;OAAI,OAAO;QAAE,UAAU;QAAW;QAAO;OAAE,CAAC;MAEhF,WAAW,EACT,6EACA,6FACA,kDACD;gBAVH,CAYE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA,EAAA,WACtB;QACL;OACF;;GAEJ;;;AAIV,SAAgB,IAA0B;CACxC,IAAM,CAAC,GAAQ,KAAa,EAAyC,MAAM,EACrE,IAAQ,EAAkB,EAAE,QAAQ,MAAW,QAAQ,KAAA,IAAY,GAAQ,CAAC,EAC5E,IAAiB,EAAM,QAAQ,EAAE;AAEvC,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAFd,CAIE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAe,KAAK,MACnB,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,EAAU,EAAI,MAAM;IACnC,WAAW,EACT,kEACA,MAAW,EAAI,QACX,kDACA,2DACL;cAEA,EAAI;IACE,EAXF,EAAI,MAWF,CACT;GACE,CAAA,EAEL,EAAM,YACL,kBAAC,GAAD,EAAa,OAAM,2BAA4B,CAAA,GAC7C,EAAM,UACR,kBAAC,GAAD,EAAW,eAAe,KAAK,EAAM,SAAS,EAAI,CAAA,GAChD,EAAe,WAAW,IAC5B,kBAAC,GAAD;GACE,cAAa;GACb,MAAM,kBAAC,GAAD,EAAW,WAAU,aAAc,CAAA;GACzC,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAe,KAAK,MACnB,kBAAC,GAAD,EAAwC,eAAe,GAAU,EAApC,EAAO,GAA6B,CACjE;GACE,CAAA,CAEG"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Featured Items Page
3
+ * @module coolandlovely/pages/admin
4
+ *
5
+ * Curation surface for featured style items. Lists the catalogue and lets admins
6
+ * feature or unfeature individual items for curated discovery surfaces.
7
+ */
8
+ export declare function AdminFeaturedItemsPage(): import("react").JSX.Element;
9
+ //# sourceMappingURL=AdminFeaturedItemsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminFeaturedItemsPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/admin/AdminFeaturedItemsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+EH,wBAAgB,sBAAsB,gCAgGrC"}
@@ -0,0 +1,139 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { PageLayout as t } from "../../components/PageLayout.js";
3
+ import { StatusBadge as n } from "../../components/StatusBadge.js";
4
+ import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
5
+ import { Thumbnail as a } from "../../components/Thumbnail.js";
6
+ import { useFeatureStyleItem as o, useStyleItems as s, useUnfeatureStyleItem as c } from "../../hooks/useCoolAndLovelyApi.js";
7
+ import { formatPrice as l, titleCase as u } from "../../utils/formatters.js";
8
+ import "../../hooks/index.js";
9
+ import { STYLE_CATEGORIES as d, categoryIcon as f } from "../../utils/icons.js";
10
+ import { EmptyState as p } from "../../components/EmptyState.js";
11
+ import { useState as m } from "react";
12
+ import { Shirt as h, Star as g } from "lucide-react";
13
+ import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
14
+ //#region src/coolandlovely/pages/admin/AdminFeaturedItemsPage.tsx
15
+ var b = 24;
16
+ function x({ item: t }) {
17
+ let r = o(), i = c(), s = r.isPending || i.isPending;
18
+ return /* @__PURE__ */ y("div", {
19
+ className: "flex items-center gap-4 rounded-lg border border-border-seam bg-bg-surface p-3",
20
+ children: [
21
+ /* @__PURE__ */ v(a, {
22
+ icon: f(t.category, 20),
23
+ seed: t.id,
24
+ fileId: t.image?.id,
25
+ alt: t.name,
26
+ className: "h-14 w-14 shrink-0 rounded-lg"
27
+ }),
28
+ /* @__PURE__ */ y("div", {
29
+ className: "min-w-0 flex-1",
30
+ children: [
31
+ /* @__PURE__ */ y("div", {
32
+ className: "flex items-center gap-2",
33
+ children: [/* @__PURE__ */ v("h3", {
34
+ className: "truncate text-sm font-semibold text-text-primary",
35
+ children: t.name
36
+ }), t.isFeatured && /* @__PURE__ */ v(g, { className: "h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text" })]
37
+ }),
38
+ /* @__PURE__ */ y("p", {
39
+ className: "truncate text-xs text-text-secondary",
40
+ children: [
41
+ t.brand?.name ?? "Unknown brand",
42
+ " · ",
43
+ u(t.category)
44
+ ]
45
+ }),
46
+ /* @__PURE__ */ v("p", {
47
+ className: "mt-0.5 text-sm font-medium text-text-primary",
48
+ children: l(t.price, t.currency)
49
+ })
50
+ ]
51
+ }),
52
+ /* @__PURE__ */ y("div", {
53
+ className: "flex shrink-0 items-center gap-2",
54
+ children: [/* @__PURE__ */ v(n, { status: t.status }), t.isFeatured ? /* @__PURE__ */ v("button", {
55
+ type: "button",
56
+ disabled: s,
57
+ onClick: () => i.mutate(t.id),
58
+ className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80", "disabled:cursor-not-allowed disabled:opacity-50"),
59
+ children: "Unfeature"
60
+ }) : /* @__PURE__ */ y("button", {
61
+ type: "button",
62
+ disabled: s,
63
+ onClick: () => r.mutate(t.id),
64
+ className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-warning-bg-subtle text-status-warning-text hover:bg-status-warning-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
65
+ children: [/* @__PURE__ */ v(g, { className: "h-4 w-4" }), " Feature"]
66
+ })]
67
+ })
68
+ ]
69
+ });
70
+ }
71
+ function S() {
72
+ let [n, a] = m("ALL"), [o, c] = m(0), l = s({
73
+ category: n === "ALL" ? void 0 : n,
74
+ pagination: {
75
+ limit: b,
76
+ offset: o
77
+ }
78
+ }), g = l.data?.items ?? [], S = l.data?.pageInfo.hasMore ?? !1;
79
+ return /* @__PURE__ */ y(t, {
80
+ title: "Admin — Featured Items",
81
+ description: "Curate style items for discovery surfaces",
82
+ children: [/* @__PURE__ */ y("div", {
83
+ className: "mb-6 flex flex-wrap items-center gap-2",
84
+ children: [/* @__PURE__ */ v("button", {
85
+ type: "button",
86
+ onClick: () => {
87
+ a("ALL"), c(0);
88
+ },
89
+ className: e("rounded-full px-3 py-1.5 text-xs font-medium transition-colors", n === "ALL" ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
90
+ children: "All categories"
91
+ }), d.map((t) => /* @__PURE__ */ y("button", {
92
+ type: "button",
93
+ onClick: () => {
94
+ a(t), c(0);
95
+ },
96
+ className: e("inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors", n === t ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
97
+ children: [
98
+ f(t, 14),
99
+ " ",
100
+ u(t)
101
+ ]
102
+ }, t))]
103
+ }), l.isLoading ? /* @__PURE__ */ v(i, { label: "Loading style items…" }) : l.isError ? /* @__PURE__ */ v(r, { onRetry: () => void l.refetch() }) : g.length === 0 ? /* @__PURE__ */ v(p, {
104
+ illustration: "empty-data",
105
+ icon: /* @__PURE__ */ v(h, { className: "h-10 w-10" }),
106
+ title: "No style items found",
107
+ description: "No items match this filter."
108
+ }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", {
109
+ className: "space-y-3",
110
+ children: g.map((e) => /* @__PURE__ */ v(x, { item: e }, e.id))
111
+ }), (o > 0 || S) && /* @__PURE__ */ y("div", {
112
+ className: "mt-6 flex items-center justify-center gap-2",
113
+ children: [
114
+ /* @__PURE__ */ v("button", {
115
+ type: "button",
116
+ disabled: o === 0,
117
+ onClick: () => c((e) => Math.max(0, e - b)),
118
+ 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",
119
+ children: "Previous"
120
+ }),
121
+ /* @__PURE__ */ y("span", {
122
+ className: "text-sm text-text-secondary",
123
+ children: ["Page ", o / b + 1]
124
+ }),
125
+ /* @__PURE__ */ v("button", {
126
+ type: "button",
127
+ disabled: !S,
128
+ onClick: () => c((e) => e + b),
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",
130
+ children: "Next"
131
+ })
132
+ ]
133
+ })] })]
134
+ });
135
+ }
136
+ //#endregion
137
+ export { S as AdminFeaturedItemsPage };
138
+
139
+ //# sourceMappingURL=AdminFeaturedItemsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminFeaturedItemsPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/admin/AdminFeaturedItemsPage.tsx"],"sourcesContent":["/**\n * Admin Featured Items Page\n * @module coolandlovely/pages/admin\n *\n * Curation surface for featured style items. Lists the catalogue and lets admins\n * feature or unfeature individual items for curated discovery surfaces.\n */\n\nimport { useState } from 'react';\nimport { Shirt, Star } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { useStyleItems, useFeatureStyleItem, useUnfeatureStyleItem } from '../../hooks';\nimport { categoryIcon, STYLE_CATEGORIES } from '../../utils/icons';\nimport { formatPrice, titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooStyleCategory, CooStyleItem } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nfunction FeaturedItemRow({ item }: { item: CooStyleItem }) {\n const feature = useFeatureStyleItem();\n const unfeature = useUnfeatureStyleItem();\n const isBusy = feature.isPending || unfeature.isPending;\n\n return (\n <div className=\"flex items-center gap-4 rounded-lg border border-border-seam bg-bg-surface p-3\">\n <Thumbnail\n icon={categoryIcon(item.category, 20)}\n seed={item.id}\n fileId={item.image?.id}\n alt={item.name}\n className=\"h-14 w-14 shrink-0 rounded-lg\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{item.name}</h3>\n {item.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">\n {item.brand?.name ?? 'Unknown brand'} · {titleCase(item.category)}\n </p>\n <p className=\"mt-0.5 text-sm font-medium text-text-primary\">\n {formatPrice(item.price, item.currency)}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusBadge status={item.status} />\n {item.isFeatured ? (\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => unfeature.mutate(item.id)}\n className={cn(\n 'inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium',\n 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80',\n 'disabled:cursor-not-allowed disabled:opacity-50'\n )}\n >\n Unfeature\n </button>\n ) : (\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => feature.mutate(item.id)}\n className={cn(\n 'inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium',\n 'bg-status-warning-bg-subtle text-status-warning-text hover:bg-status-warning-bg-subtle/80',\n 'disabled:cursor-not-allowed disabled:opacity-50'\n )}\n >\n <Star className=\"h-4 w-4\" /> Feature\n </button>\n )}\n </div>\n </div>\n );\n}\n\nexport function AdminFeaturedItemsPage() {\n const [category, setCategory] = useState<CooStyleCategory | 'ALL'>('ALL');\n const [offset, setOffset] = useState(0);\n\n const query = useStyleItems({\n category: category === 'ALL' ? undefined : category,\n pagination: { limit: PAGE_SIZE, offset },\n });\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title=\"Admin — Featured Items\"\n description=\"Curate style items for discovery surfaces\"\n >\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setCategory('ALL');\n setOffset(0);\n }}\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={() => {\n setCategory(c);\n setOffset(0);\n }}\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 style items…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Shirt className=\"h-10 w-10\" />}\n title=\"No style items found\"\n description=\"No items match this filter.\"\n />\n ) : (\n <>\n <div className=\"space-y-3\">\n {items.map((item) => (\n <FeaturedItemRow key={item.id} item={item} />\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 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\n </button>\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Admin pages index
3
+ * @module coolandlovely/pages/admin
4
+ */
5
+ export { AdminBrandsPage } from './AdminBrandsPage';
6
+ export { AdminCollaborationsPage } from './AdminCollaborationsPage';
7
+ export { AdminFeaturedItemsPage } from './AdminFeaturedItemsPage';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/admin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -2,12 +2,13 @@
2
2
  * Discover Page
3
3
  * @module coolandlovely/pages/discover
4
4
  *
5
- * A profile-tuned discovery feed computed entirely client-side over the REAL
6
- * catalogue + social lists (cooListLooks / cooListStyleItems) and the caller's
7
- * own style profile (cooMyStyleProfile) + follow graph (cooMyFollowing). It only
8
- * ranks what is actually on the platform against the taste the shopper declared
9
- * — no fabricated recommendations, no black-box "AI" (that is roadmap). When the
10
- * shopper has no profile yet, it invites them to build one.
5
+ * A profile-tuned discovery feed plus a first-class search experience over the
6
+ * catalogue (`cooSearchStyleItems`) and published looks (`cooSearchLooks`).
7
+ *
8
+ * The "For you" tab ranks the real catalogue + social lists against the
9
+ * caller's style profile — no fabricated recommendations, no black-box "AI"
10
+ * (that is roadmap). The "Items" / "Looks" tabs drive server-side search with
11
+ * filters and pagination.
11
12
  */
12
13
  export declare function DiscoverPage(): import("react").JSX.Element;
13
14
  //# sourceMappingURL=DiscoverPage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DiscoverPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/discover/DiscoverPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiDH,wBAAgB,YAAY,gCA8O3B"}
1
+ {"version":3,"file":"DiscoverPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/discover/DiscoverPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA4GH,wBAAgB,YAAY,gCA4jB3B"}