@burdenoff/microfe-theculturepost 2026.704.2 → 2026.705.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/theculturepost/TheCulturePostRoot.js +63 -45
  2. package/dist/theculturepost/TheCulturePostRoot.js.map +1 -1
  3. package/dist/theculturepost/TheCulturePostRoutes.js +17 -9
  4. package/dist/theculturepost/TheCulturePostRoutes.js.map +1 -1
  5. package/dist/theculturepost/components/AddCitationDialog.js +131 -102
  6. package/dist/theculturepost/components/AddCitationDialog.js.map +1 -1
  7. package/dist/theculturepost/components/ArchiveItemCard.js +56 -43
  8. package/dist/theculturepost/components/ArchiveItemCard.js.map +1 -1
  9. package/dist/theculturepost/components/AudioPlayer.js +74 -0
  10. package/dist/theculturepost/components/AudioPlayer.js.map +1 -0
  11. package/dist/theculturepost/components/CitationList.js +215 -105
  12. package/dist/theculturepost/components/CitationList.js.map +1 -1
  13. package/dist/theculturepost/components/CollectionCard.js +32 -32
  14. package/dist/theculturepost/components/CollectionCard.js.map +1 -1
  15. package/dist/theculturepost/components/CreateArchiveItemDialog.js +133 -93
  16. package/dist/theculturepost/components/CreateArchiveItemDialog.js.map +1 -1
  17. package/dist/theculturepost/components/CreateCollectionDialog.js +91 -70
  18. package/dist/theculturepost/components/CreateCollectionDialog.js.map +1 -1
  19. package/dist/theculturepost/components/CreateStoryDialog.js +311 -132
  20. package/dist/theculturepost/components/CreateStoryDialog.js.map +1 -1
  21. package/dist/theculturepost/components/DecorativeCover.js +78 -0
  22. package/dist/theculturepost/components/DecorativeCover.js.map +1 -0
  23. package/dist/theculturepost/components/DecorativeDivider.js +45 -0
  24. package/dist/theculturepost/components/DecorativeDivider.js.map +1 -0
  25. package/dist/theculturepost/components/DiscussionCard.js +75 -60
  26. package/dist/theculturepost/components/DiscussionCard.js.map +1 -1
  27. package/dist/theculturepost/components/HistoryTimeline.js +115 -0
  28. package/dist/theculturepost/components/HistoryTimeline.js.map +1 -0
  29. package/dist/theculturepost/components/LifecycleStepper.js +33 -0
  30. package/dist/theculturepost/components/LifecycleStepper.js.map +1 -0
  31. package/dist/theculturepost/components/OpenDiscussionDialog.js +66 -56
  32. package/dist/theculturepost/components/OpenDiscussionDialog.js.map +1 -1
  33. package/dist/theculturepost/components/OralHistoryCard.js +6 -73
  34. package/dist/theculturepost/components/OverlayBadge.js +22 -0
  35. package/dist/theculturepost/components/OverlayBadge.js.map +1 -0
  36. package/dist/theculturepost/components/PageHero.js +62 -0
  37. package/dist/theculturepost/components/PageHero.js.map +1 -0
  38. package/dist/theculturepost/components/RecordOralHistoryDialog.js +306 -131
  39. package/dist/theculturepost/components/RecordOralHistoryDialog.js.map +1 -1
  40. package/dist/theculturepost/components/ReplyThread.js +268 -139
  41. package/dist/theculturepost/components/ReplyThread.js.map +1 -1
  42. package/dist/theculturepost/components/StatusBadge.js +9 -9
  43. package/dist/theculturepost/components/StatusBadge.js.map +1 -1
  44. package/dist/theculturepost/components/StoryCard.js +54 -49
  45. package/dist/theculturepost/components/StoryCard.js.map +1 -1
  46. package/dist/theculturepost/components/TopicCard.js +60 -41
  47. package/dist/theculturepost/components/TopicCard.js.map +1 -1
  48. package/dist/theculturepost/components/Waveform.js +31 -0
  49. package/dist/theculturepost/components/Waveform.js.map +1 -0
  50. package/dist/theculturepost/components/categoryMeta.js +20 -0
  51. package/dist/theculturepost/components/categoryMeta.js.map +1 -0
  52. package/dist/theculturepost/components/index.js +41 -0
  53. package/dist/theculturepost/components/viz/BarChart.js +53 -0
  54. package/dist/theculturepost/components/viz/BarChart.js.map +1 -0
  55. package/dist/theculturepost/components/viz/Donut.js +58 -0
  56. package/dist/theculturepost/components/viz/Donut.js.map +1 -0
  57. package/dist/theculturepost/components/viz/StatTile.js +35 -0
  58. package/dist/theculturepost/components/viz/StatTile.js.map +1 -0
  59. package/dist/theculturepost/components/viz/index.js +3 -0
  60. package/dist/theculturepost/pages/HomePage.js +247 -124
  61. package/dist/theculturepost/pages/HomePage.js.map +1 -1
  62. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js +319 -127
  63. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js.map +1 -1
  64. package/dist/theculturepost/pages/archive/ArchivePage.js +265 -180
  65. package/dist/theculturepost/pages/archive/ArchivePage.js.map +1 -1
  66. package/dist/theculturepost/pages/archive/CollectionDetailPage.js +158 -143
  67. package/dist/theculturepost/pages/archive/CollectionDetailPage.js.map +1 -1
  68. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js +155 -93
  69. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js.map +1 -1
  70. package/dist/theculturepost/pages/discussions/DiscussionsPage.js +16 -16
  71. package/dist/theculturepost/pages/discussions/DiscussionsPage.js.map +1 -1
  72. package/dist/theculturepost/pages/governance/GovernancePage.js +293 -0
  73. package/dist/theculturepost/pages/governance/GovernancePage.js.map +1 -0
  74. package/dist/theculturepost/pages/stories/StoriesPage.js +18 -18
  75. package/dist/theculturepost/pages/stories/StoriesPage.js.map +1 -1
  76. package/dist/theculturepost/pages/stories/StoryDetailPage.js +603 -285
  77. package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
  78. package/dist/theculturepost/pages/timeline/TimelinePage.js +162 -0
  79. package/dist/theculturepost/pages/timeline/TimelinePage.js.map +1 -0
  80. package/dist/theculturepost/pages/topics/TopicDetailPage.js +239 -163
  81. package/dist/theculturepost/pages/topics/TopicDetailPage.js.map +1 -1
  82. package/dist/theculturepost/pages/topics/TopicsPage.js +76 -74
  83. package/dist/theculturepost/pages/topics/TopicsPage.js.map +1 -1
  84. package/dist/theculturepost/styles/injectDisplayFont.js +11 -0
  85. package/dist/theculturepost/styles/injectDisplayFont.js.map +1 -0
  86. package/dist/theculturepost/utils/formatters.js +6 -1
  87. package/dist/theculturepost/utils/formatters.js.map +1 -1
  88. package/package.json +1 -1
  89. package/dist/theculturepost/components/OralHistoryCard.js.map +0 -1
@@ -1,129 +1,239 @@
1
- import { ErrorView as e, LoadingView as t } from "./StateViews.js";
2
- import { formatDate as n, formatEnumLabel as r } from "../utils/formatters.js";
3
- import { StatusBadge as i } from "./StatusBadge.js";
4
- import { EmptyState as a } from "./EmptyState.js";
5
- import { useTcpCitations as o, useTcpVerifyCitation as s } from "../hooks/useTcpApi.js";
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { ErrorView as t, LoadingView as n } from "./StateViews.js";
3
+ import { formatDate as r, formatEnumLabel as i } from "../utils/formatters.js";
4
+ import { StatusBadge as a } from "./StatusBadge.js";
5
+ import { EmptyState as o } from "./EmptyState.js";
6
+ import { useTcpCitations as s, useTcpVerifyCitation as c } from "../hooks/useTcpApi.js";
6
7
  import "../hooks/index.js";
7
- import { describeError as c, notify as l } from "../lib/toast.js";
8
- import { BookMarked as u, ExternalLink as d, Quote as f, ShieldCheck as p } from "lucide-react";
9
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
- import { Select as g, SelectContent as _, SelectItem as v, SelectTrigger as y, SelectValue as b, Spinner as x } from "@burdenoff/fe-libs/ui";
8
+ import { describeError as l, notify as u } from "../lib/toast.js";
9
+ import { createElement as d } from "react";
10
+ import { AlertCircle as f, BookMarked as p, BookOpen as m, Box as h, CheckCircle2 as g, ExternalLink as _, FileText as v, Globe as y, GraduationCap as b, HelpCircle as x, Landmark as S, Mic as C, Newspaper as w, Quote as T, ShieldCheck as E, XCircle as D } from "lucide-react";
11
+ import { jsx as O, jsxs as k } from "react/jsx-runtime";
12
+ import { Spinner as A } from "@burdenoff/fe-libs/ui";
11
13
  //#region src/theculturepost/components/CitationList.tsx
12
- var S = [
13
- "UNVERIFIED",
14
+ var j = {
15
+ ACADEMIC: b,
16
+ ARCHIVAL_RECORD: S,
17
+ ARTIFACT: h,
18
+ BOOK: m,
19
+ INTERVIEW: C,
20
+ PERIODICAL: w,
21
+ PRIMARY_DOCUMENT: v,
22
+ WEB: y
23
+ };
24
+ function M(e) {
25
+ return j[e] ?? v;
26
+ }
27
+ var N = {
28
+ CORROBORATED: {
29
+ icon: g,
30
+ accent: "success",
31
+ label: "Corroborated",
32
+ blurb: "Strong agreement among reliable sources."
33
+ },
34
+ DISPUTED: {
35
+ icon: f,
36
+ accent: "warning",
37
+ label: "Disputed",
38
+ blurb: "Conflicting or insufficient evidence."
39
+ },
40
+ RETRACTED: {
41
+ icon: D,
42
+ accent: "destructive",
43
+ label: "Retracted",
44
+ blurb: "The source has since been withdrawn."
45
+ },
46
+ UNVERIFIED: {
47
+ icon: x,
48
+ accent: "secondary",
49
+ label: "Unverified",
50
+ blurb: "Not yet reviewed by an editor."
51
+ }
52
+ }, P = [
14
53
  "CORROBORATED",
15
54
  "DISPUTED",
16
- "RETRACTED"
17
- ];
18
- function C({ citation: e }) {
19
- let t = s(), a = async (n) => {
20
- try {
21
- await t.mutateAsync({
22
- id: e.id,
23
- verification: n
24
- }), l.success("Verification updated", r(n));
55
+ "RETRACTED",
56
+ "UNVERIFIED"
57
+ ], F = {
58
+ success: "bg-status-success-bg-subtle text-status-success-text shadow-elevation-1",
59
+ warning: "bg-status-warning-bg-subtle text-status-warning-text shadow-elevation-1",
60
+ destructive: "bg-status-error-bg-subtle text-status-error-text shadow-elevation-1",
61
+ secondary: "bg-bg-sunken text-text-primary shadow-elevation-1"
62
+ };
63
+ function I({ value: t, onChange: n, pending: r = !1, compact: i = !1, className: a }) {
64
+ return /* @__PURE__ */ O("div", {
65
+ role: "radiogroup",
66
+ "aria-label": "Verification status",
67
+ className: e("grid grid-cols-2 gap-1 rounded-xl border border-border-seam bg-bg-surface p-1", i ? "grid-cols-4" : "sm:inline-grid sm:auto-cols-max sm:grid-flow-col", a),
68
+ children: P.map((a) => {
69
+ let o = N[a], s = o.icon, c = a === t;
70
+ return /* @__PURE__ */ k("button", {
71
+ type: "button",
72
+ role: "radio",
73
+ "aria-checked": c,
74
+ "aria-label": o.label,
75
+ title: o.label,
76
+ disabled: r,
77
+ onClick: () => n(a),
78
+ className: e("inline-flex min-h-9 items-center justify-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-medium transition-colors disabled:opacity-60", c ? F[o.accent] : "text-text-tertiary hover:bg-bg-sunken hover:text-text-primary"),
79
+ children: [/* @__PURE__ */ O(s, {
80
+ className: "h-3.5 w-3.5 shrink-0",
81
+ "aria-hidden": "true"
82
+ }), /* @__PURE__ */ O("span", {
83
+ className: i ? "sr-only" : "hidden sm:inline",
84
+ children: o.label
85
+ })]
86
+ }, a);
87
+ })
88
+ });
89
+ }
90
+ function L({ citation: t, compact: n }) {
91
+ let o = c(), s = async (e) => {
92
+ if (e !== t.verification) try {
93
+ await o.mutateAsync({
94
+ id: t.id,
95
+ verification: e
96
+ }), u.success("Verification updated", i(e));
25
97
  } catch (e) {
26
- l.error("Could not update verification", c(e));
98
+ u.error("Could not update verification", l(e));
27
99
  }
28
- }, o = [
29
- e.author,
30
- e.publisher,
31
- n(e.publishedOn)
100
+ }, f = [
101
+ t.author,
102
+ t.publisher,
103
+ r(t.publishedOn)
32
104
  ].filter((e) => e && e !== "—").join(" · ");
33
- return /* @__PURE__ */ h("li", {
34
- className: "rounded-xl border border-border-seam bg-bg-surface p-4",
35
- children: [
36
- /* @__PURE__ */ h("div", {
37
- className: "flex items-start justify-between gap-3",
38
- children: [/* @__PURE__ */ h("div", {
39
- className: "min-w-0",
40
- children: [
41
- /* @__PURE__ */ h("div", {
42
- className: "mb-1.5 flex flex-wrap items-center gap-1.5",
43
- children: [/* @__PURE__ */ m(i, { status: e.sourceType }), /* @__PURE__ */ m(i, { status: e.verification })]
44
- }),
45
- /* @__PURE__ */ m("p", {
46
- className: "text-sm font-semibold text-text-primary",
47
- children: e.title
48
- }),
49
- o && /* @__PURE__ */ m("p", {
50
- className: "mt-0.5 text-xs text-text-tertiary",
51
- children: o
52
- })
53
- ]
54
- }), e.url && /* @__PURE__ */ h("a", {
55
- href: e.url,
56
- target: "_blank",
57
- rel: "noreferrer noopener",
58
- className: "inline-flex shrink-0 items-center gap-1 text-xs font-medium text-accent hover:underline",
59
- children: ["Source ", /* @__PURE__ */ m(d, { className: "h-3 w-3" })]
60
- })]
61
- }),
62
- e.excerpt && /* @__PURE__ */ h("blockquote", {
63
- className: "mt-3 flex gap-2 rounded-lg bg-bg-sunken/60 px-3 py-2 text-sm italic text-text-secondary",
64
- children: [/* @__PURE__ */ m(f, {
65
- className: "h-3.5 w-3.5 shrink-0 text-text-tertiary",
66
- "aria-hidden": "true"
67
- }), /* @__PURE__ */ m("span", {
68
- className: "not-italic",
69
- children: e.excerpt
70
- })]
71
- }),
72
- e.provenance && /* @__PURE__ */ h("p", {
73
- className: "mt-2 text-xs text-text-tertiary",
105
+ return /* @__PURE__ */ k("li", {
106
+ className: e("rounded-2xl border border-border-seam bg-bg-surface transition-colors hover:border-border-strong", n ? "p-3.5" : "p-4 sm:p-5"),
107
+ children: [/* @__PURE__ */ k("div", {
108
+ className: e("flex gap-3", n ? "items-start" : "sm:gap-4"),
109
+ children: [/* @__PURE__ */ O("span", {
110
+ className: e("grid shrink-0 place-items-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary", n ? "h-9 w-9" : "h-12 w-12"),
111
+ "aria-hidden": "true",
112
+ children: d(M(t.sourceType), { className: n ? "h-4 w-4" : "h-6 w-6" })
113
+ }), /* @__PURE__ */ k("div", {
114
+ className: "min-w-0 flex-1",
74
115
  children: [
75
- /* @__PURE__ */ m("span", {
76
- className: "font-medium text-text-secondary",
77
- children: "Provenance:"
116
+ /* @__PURE__ */ k("div", {
117
+ className: "mb-1 flex flex-wrap items-center gap-1.5",
118
+ children: [/* @__PURE__ */ O(a, {
119
+ status: t.sourceType,
120
+ icon: d(M(t.sourceType), { className: "h-3 w-3" })
121
+ }), n && /* @__PURE__ */ O(a, { status: t.verification })]
122
+ }),
123
+ /* @__PURE__ */ O("p", {
124
+ className: e("font-display font-semibold leading-snug text-text-primary", n ? "text-sm" : "text-base"),
125
+ children: t.title
78
126
  }),
79
- " ",
80
- e.provenance
127
+ f && /* @__PURE__ */ O("p", {
128
+ className: "mt-0.5 text-xs text-text-tertiary",
129
+ children: f
130
+ }),
131
+ t.excerpt && !n && /* @__PURE__ */ k("blockquote", {
132
+ className: "mt-3 flex gap-2 rounded-lg bg-bg-sunken/60 px-3 py-2 text-sm text-text-secondary",
133
+ children: [/* @__PURE__ */ O(T, {
134
+ className: "h-3.5 w-3.5 shrink-0 text-text-tertiary",
135
+ "aria-hidden": "true"
136
+ }), /* @__PURE__ */ O("span", { children: t.excerpt })]
137
+ }),
138
+ t.provenance && !n && /* @__PURE__ */ k("p", {
139
+ className: "mt-2 text-xs text-text-tertiary",
140
+ children: [
141
+ /* @__PURE__ */ O("span", {
142
+ className: "font-medium text-text-secondary",
143
+ children: "Provenance:"
144
+ }),
145
+ " ",
146
+ t.provenance
147
+ ]
148
+ }),
149
+ t.url && /* @__PURE__ */ k("a", {
150
+ href: t.url,
151
+ target: "_blank",
152
+ rel: "noreferrer noopener",
153
+ className: "mt-2 inline-flex max-w-full items-center gap-1 text-xs font-medium text-accent hover:underline",
154
+ children: [/* @__PURE__ */ O("span", {
155
+ className: "truncate",
156
+ children: n ? "View source" : t.url
157
+ }), /* @__PURE__ */ O(_, {
158
+ className: "h-3 w-3 shrink-0",
159
+ "aria-hidden": "true"
160
+ })]
161
+ })
81
162
  ]
82
- }),
83
- /* @__PURE__ */ h("div", {
84
- className: "mt-3 flex items-center gap-2 border-t border-border-seam pt-3",
163
+ })]
164
+ }), /* @__PURE__ */ k("div", {
165
+ className: e("mt-3 flex flex-wrap items-center gap-x-3 gap-y-2 border-t border-border-seam pt-3", n ? "" : "sm:justify-between"),
166
+ children: [/* @__PURE__ */ k("span", {
167
+ className: "inline-flex items-center gap-1.5 text-xs font-medium text-text-secondary",
85
168
  children: [
86
- /* @__PURE__ */ m(p, {
87
- className: "h-4 w-4 text-text-tertiary",
169
+ /* @__PURE__ */ O(E, {
170
+ className: "h-3.5 w-3.5 text-text-tertiary",
88
171
  "aria-hidden": "true"
89
172
  }),
90
- /* @__PURE__ */ m("span", {
91
- className: "text-xs text-text-secondary",
92
- children: "Verification"
93
- }),
94
- /* @__PURE__ */ h(g, {
95
- value: e.verification,
96
- onValueChange: (e) => a(e),
97
- children: [/* @__PURE__ */ m(y, {
98
- className: "h-8 w-40",
99
- "aria-label": "Change verification status",
100
- children: /* @__PURE__ */ m(b, {})
101
- }), /* @__PURE__ */ m(_, { children: S.map((e) => /* @__PURE__ */ m(v, {
102
- value: e,
103
- children: r(e)
104
- }, e)) })]
105
- }),
106
- t.isPending && /* @__PURE__ */ m(x, { size: "sm" })
173
+ "Verification",
174
+ o.isPending && /* @__PURE__ */ O(A, { size: "sm" })
107
175
  ]
176
+ }), /* @__PURE__ */ O(I, {
177
+ value: t.verification,
178
+ onChange: s,
179
+ pending: o.isPending,
180
+ compact: n
181
+ })]
182
+ })]
183
+ });
184
+ }
185
+ function R() {
186
+ return /* @__PURE__ */ k("div", {
187
+ className: "mt-4 rounded-2xl border border-border-seam bg-bg-sunken/50 p-4 sm:p-5",
188
+ children: [/* @__PURE__ */ k("div", {
189
+ className: "flex items-start gap-3",
190
+ children: [/* @__PURE__ */ O("span", {
191
+ className: "grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary",
192
+ "aria-hidden": "true",
193
+ children: /* @__PURE__ */ O(E, { className: "h-5 w-5" })
194
+ }), /* @__PURE__ */ O("p", {
195
+ className: "text-sm text-text-secondary",
196
+ children: "Every source is evaluated for accuracy, relevance, and cultural sensitivity through a transparent, multi-step review by editors and subject-area contributors."
197
+ })]
198
+ }), /* @__PURE__ */ O("dl", {
199
+ className: "mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4",
200
+ children: P.map((t) => {
201
+ let n = N[t], r = n.icon;
202
+ return /* @__PURE__ */ k("div", {
203
+ className: "flex items-start gap-2",
204
+ children: [/* @__PURE__ */ O(r, {
205
+ className: e("mt-0.5 h-4 w-4 shrink-0", n.accent === "success" && "text-status-success-text", n.accent === "warning" && "text-status-warning-text", n.accent === "destructive" && "text-status-error-text", n.accent === "secondary" && "text-text-tertiary"),
206
+ "aria-hidden": "true"
207
+ }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("dt", {
208
+ className: "text-xs font-semibold text-text-primary",
209
+ children: n.label
210
+ }), /* @__PURE__ */ O("dd", {
211
+ className: "mt-0.5 text-xs text-text-tertiary",
212
+ children: n.blurb
213
+ })] })]
214
+ }, t);
108
215
  })
109
- ]
216
+ })]
110
217
  });
111
218
  }
112
- function w({ storyId: n }) {
113
- let { data: r, isLoading: i, isError: s, refetch: c } = o(n);
114
- return i ? /* @__PURE__ */ m(t, { label: "Loading citations…" }) : s ? /* @__PURE__ */ m(e, {
219
+ function z({ storyId: r, compact: i = !1 }) {
220
+ let { data: a, isLoading: c, isError: l, refetch: u } = s(r);
221
+ return c ? /* @__PURE__ */ O(n, { label: "Loading citations…" }) : l ? /* @__PURE__ */ O(t, {
115
222
  message: "We couldn't load citations.",
116
- onRetry: () => c()
117
- }) : !r || r.length === 0 ? /* @__PURE__ */ m(a, {
118
- icon: /* @__PURE__ */ m(u, { className: "h-9 w-9" }),
223
+ onRetry: () => u()
224
+ }) : !a || a.length === 0 ? /* @__PURE__ */ O(o, {
225
+ icon: /* @__PURE__ */ O(p, { className: "h-9 w-9" }),
119
226
  title: "No citations yet",
120
227
  description: "Add verifiable sources to ground this story in the record."
121
- }) : /* @__PURE__ */ m("ul", {
122
- className: "flex flex-col gap-3",
123
- children: r.map((e) => /* @__PURE__ */ m(C, { citation: e }, e.id))
124
- });
228
+ }) : /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("ul", {
229
+ className: e("flex flex-col", i ? "gap-3" : "gap-3 sm:gap-4"),
230
+ children: a.map((e) => /* @__PURE__ */ O(L, {
231
+ citation: e,
232
+ compact: i
233
+ }, e.id))
234
+ }), !i && /* @__PURE__ */ O(R, {})] });
125
235
  }
126
236
  //#endregion
127
- export { w as CitationList };
237
+ export { z as CitationList, P as VERIFICATIONS, N as VERIFICATION_META, M as sourceTypeIcon };
128
238
 
129
239
  //# sourceMappingURL=CitationList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CitationList.js","names":[],"sources":["../../../src/theculturepost/components/CitationList.tsx"],"sourcesContent":["/**\n * CitationList — the sources attached to a story, with verification controls.\n * @module theculturepost/components\n *\n * Reads `tcpListCitations(storyId)`; each entry can be moved through its\n * verification state (`tcpVerifyCitation`) — the archival integrity control at\n * the heart of a serious cultural publication.\n */\n\nimport { BookMarked, ExternalLink, Quote, ShieldCheck } from 'lucide-react';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { LoadingView, ErrorView } from './StateViews';\nimport { EmptyState } from './EmptyState';\nimport { useTcpCitations, useTcpVerifyCitation } from '../hooks';\nimport { formatDate, formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { TcpCitation, TcpVerificationStatus } from '../types';\n\nconst VERIFICATIONS: TcpVerificationStatus[] = [\n 'UNVERIFIED',\n 'CORROBORATED',\n 'DISPUTED',\n 'RETRACTED',\n];\n\ninterface CitationListProps {\n storyId: string;\n}\n\nfunction CitationRow({ citation }: { citation: TcpCitation }) {\n const verify = useTcpVerifyCitation();\n\n const changeVerification = async (verification: TcpVerificationStatus) => {\n try {\n await verify.mutateAsync({ id: citation.id, verification });\n notify.success('Verification updated', formatEnumLabel(verification));\n } catch (err) {\n notify.error('Could not update verification', describeError(err));\n }\n };\n\n const meta = [citation.author, citation.publisher, formatDate(citation.publishedOn)]\n .filter((v) => v && v !== '—')\n .join(' · ');\n\n return (\n <li className=\"rounded-xl border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"mb-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={citation.sourceType} />\n <StatusBadge status={citation.verification} />\n </div>\n <p className=\"text-sm font-semibold text-text-primary\">{citation.title}</p>\n {meta && <p className=\"mt-0.5 text-xs text-text-tertiary\">{meta}</p>}\n </div>\n {citation.url && (\n <a\n href={citation.url}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex shrink-0 items-center gap-1 text-xs font-medium text-accent hover:underline\"\n >\n Source <ExternalLink className=\"h-3 w-3\" />\n </a>\n )}\n </div>\n\n {citation.excerpt && (\n <blockquote className=\"mt-3 flex gap-2 rounded-lg bg-bg-sunken/60 px-3 py-2 text-sm italic text-text-secondary\">\n <Quote className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary\" aria-hidden=\"true\" />\n <span className=\"not-italic\">{citation.excerpt}</span>\n </blockquote>\n )}\n\n {citation.provenance && (\n <p className=\"mt-2 text-xs text-text-tertiary\">\n <span className=\"font-medium text-text-secondary\">Provenance:</span> {citation.provenance}\n </p>\n )}\n\n <div className=\"mt-3 flex items-center gap-2 border-t border-border-seam pt-3\">\n <ShieldCheck className=\"h-4 w-4 text-text-tertiary\" aria-hidden=\"true\" />\n <span className=\"text-xs text-text-secondary\">Verification</span>\n <Select\n value={citation.verification}\n onValueChange={(v) => changeVerification(v as TcpVerificationStatus)}\n >\n <SelectTrigger className=\"h-8 w-40\" aria-label=\"Change verification status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {VERIFICATIONS.map((v) => (\n <SelectItem key={v} value={v}>\n {formatEnumLabel(v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {verify.isPending && <Spinner size=\"sm\" />}\n </div>\n </li>\n );\n}\n\nexport function CitationList({ storyId }: CitationListProps) {\n const { data: citations, isLoading, isError, refetch } = useTcpCitations(storyId);\n\n if (isLoading) return <LoadingView label=\"Loading citations…\" />;\n if (isError) return <ErrorView message=\"We couldn't load citations.\" onRetry={() => refetch()} />;\n\n if (!citations || citations.length === 0) {\n return (\n <EmptyState\n icon={<BookMarked className=\"h-9 w-9\" />}\n title=\"No citations yet\"\n description=\"Add verifiable sources to ground this story in the record.\"\n />\n );\n }\n\n return (\n <ul className=\"flex flex-col gap-3\">\n {citations.map((citation) => (\n <CitationRow key={citation.id} citation={citation} />\n ))}\n </ul>\n );\n}\n"],"mappings":";;;;;;;;;;;AA0BA,IAAM,IAAyC;CAC7C;CACA;CACA;CACA;CACD;AAMD,SAAS,EAAY,EAAE,eAAuC;CAC5D,IAAM,IAAS,GAAsB,EAE/B,IAAqB,OAAO,MAAwC;AACxE,MAAI;AAEF,GADA,MAAM,EAAO,YAAY;IAAE,IAAI,EAAS;IAAI;IAAc,CAAC,EAC3D,EAAO,QAAQ,wBAAwB,EAAgB,EAAa,CAAC;WAC9D,GAAK;AACZ,KAAO,MAAM,iCAAiC,EAAc,EAAI,CAAC;;IAI/D,IAAO;EAAC,EAAS;EAAQ,EAAS;EAAW,EAAW,EAAS,YAAY;EAAC,CACjF,QAAQ,MAAM,KAAK,MAAM,IAAI,CAC7B,KAAK,MAAM;AAEd,QACE,kBAAC,MAAD;EAAI,WAAU;YAAd;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAa,QAAQ,EAAS,YAAc,CAAA,EAC5C,kBAAC,GAAD,EAAa,QAAQ,EAAS,cAAgB,CAAA,CAC1C;;MACN,kBAAC,KAAD;OAAG,WAAU;iBAA2C,EAAS;OAAU,CAAA;MAC1E,KAAQ,kBAAC,KAAD;OAAG,WAAU;iBAAqC;OAAS,CAAA;MAChE;QACL,EAAS,OACR,kBAAC,KAAD;KACE,MAAM,EAAS;KACf,QAAO;KACP,KAAI;KACJ,WAAU;eAJZ,CAKC,WACQ,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,CACzC;OAEF;;GAEL,EAAS,WACR,kBAAC,cAAD;IAAY,WAAU;cAAtB,CACE,kBAAC,GAAD;KAAO,WAAU;KAA0C,eAAY;KAAS,CAAA,EAChF,kBAAC,QAAD;KAAM,WAAU;eAAc,EAAS;KAAe,CAAA,CAC3C;;GAGd,EAAS,cACR,kBAAC,KAAD;IAAG,WAAU;cAAb;KACE,kBAAC,QAAD;MAAM,WAAU;gBAAkC;MAAkB,CAAA;;KAAE,EAAS;KAC7E;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAa,WAAU;MAA6B,eAAY;MAAS,CAAA;KACzE,kBAAC,QAAD;MAAM,WAAU;gBAA8B;MAAmB,CAAA;KACjE,kBAAC,GAAD;MACE,OAAO,EAAS;MAChB,gBAAgB,MAAM,EAAmB,EAA2B;gBAFtE,CAIE,kBAAC,GAAD;OAAe,WAAU;OAAW,cAAW;iBAC7C,kBAAC,GAAD,EAAe,CAAA;OACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAc,KAAK,MAClB,kBAAC,GAAD;OAAoB,OAAO;iBACxB,EAAgB,EAAE;OACR,EAFI,EAEJ,CACb,EACY,CAAA,CACT;;KACR,EAAO,aAAa,kBAAC,GAAD,EAAS,MAAK,MAAO,CAAA;KACtC;;GACH;;;AAIT,SAAgB,EAAa,EAAE,cAA8B;CAC3D,IAAM,EAAE,MAAM,GAAW,cAAW,YAAS,eAAY,EAAgB,EAAQ;AAejF,QAbI,IAAkB,kBAAC,GAAD,EAAa,OAAM,sBAAuB,CAAA,GAC5D,IAAgB,kBAAC,GAAD;EAAW,SAAQ;EAA8B,eAAe,GAAS;EAAI,CAAA,GAE7F,CAAC,KAAa,EAAU,WAAW,IAEnC,kBAAC,GAAD;EACE,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;EACxC,OAAM;EACN,aAAY;EACZ,CAAA,GAKJ,kBAAC,MAAD;EAAI,WAAU;YACX,EAAU,KAAK,MACd,kBAAC,GAAD,EAAyC,aAAY,EAAnC,EAAS,GAA0B,CACrD;EACC,CAAA"}
1
+ {"version":3,"file":"CitationList.js","names":[],"sources":["../../../src/theculturepost/components/CitationList.tsx"],"sourcesContent":["/**\n * CitationList — the verifiable sources that ground a story (tcp-05).\n * @module theculturepost/components\n *\n * Reads `tcpListCitations(storyId)`; each entry can be moved through its\n * verification state (`tcpVerifyCitation`) via a **segmented control** — the\n * archival-integrity control at the heart of a serious cultural publication.\n *\n * Each source shows a source-type icon, its bibliographic detail, an optional\n * excerpt + link, and (in the full variant) closes with a static methodology\n * legend. There is deliberately NO \"evidence trail\" mapping citations to\n * individual claims — the backend exposes no such linkage, so we never fake it.\n *\n * Two layouts, same data:\n * - default (full): roomy cards + labelled segments + methodology legend.\n * - `compact`: narrow-rail cards + icon-only segments, no legend.\n */\n\nimport {\n BookOpen,\n GraduationCap,\n Landmark,\n FileText,\n Globe,\n Newspaper,\n Mic,\n Box,\n ExternalLink,\n Quote,\n ShieldCheck,\n CheckCircle2,\n AlertCircle,\n XCircle,\n HelpCircle,\n BookMarked,\n type LucideIcon,\n} from 'lucide-react';\nimport { createElement } from 'react';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { LoadingView, ErrorView } from './StateViews';\nimport { EmptyState } from './EmptyState';\nimport { useTcpCitations, useTcpVerifyCitation } from '../hooks';\nimport { cn } from '../utils/cn';\nimport { formatDate, formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { TcpCitation, TcpSourceType, TcpVerificationStatus } from '../types';\n\n// ---------------------------------------------------------------------------\n// Source-type + verification metadata (exported for reuse in the editorial table)\n// ---------------------------------------------------------------------------\n\n/** A distinct lucide motif per source type (icon, not hue — per the design rules). */\nexport const SOURCE_TYPE_ICON: Record<TcpSourceType, LucideIcon> = {\n ACADEMIC: GraduationCap,\n ARCHIVAL_RECORD: Landmark,\n ARTIFACT: Box,\n BOOK: BookOpen,\n INTERVIEW: Mic,\n PERIODICAL: Newspaper,\n PRIMARY_DOCUMENT: FileText,\n WEB: Globe,\n};\n\n/** Resolve a source-type's motif icon, falling back to a neutral document. */\nexport function sourceTypeIcon(type: TcpSourceType): LucideIcon {\n return SOURCE_TYPE_ICON[type] ?? FileText;\n}\n\ntype VerificationAccent = 'success' | 'warning' | 'destructive' | 'secondary';\n\ninterface VerificationMetaEntry {\n icon: LucideIcon;\n accent: VerificationAccent;\n label: string;\n /** Static, factual description of what the state means — shown in the legend. */\n blurb: string;\n}\n\n/** Ordered, tone-mapped metadata for each verification state. */\nexport const VERIFICATION_META: Record<TcpVerificationStatus, VerificationMetaEntry> = {\n CORROBORATED: {\n icon: CheckCircle2,\n accent: 'success',\n label: 'Corroborated',\n blurb: 'Strong agreement among reliable sources.',\n },\n DISPUTED: {\n icon: AlertCircle,\n accent: 'warning',\n label: 'Disputed',\n blurb: 'Conflicting or insufficient evidence.',\n },\n RETRACTED: {\n icon: XCircle,\n accent: 'destructive',\n label: 'Retracted',\n blurb: 'The source has since been withdrawn.',\n },\n UNVERIFIED: {\n icon: HelpCircle,\n accent: 'secondary',\n label: 'Unverified',\n blurb: 'Not yet reviewed by an editor.',\n },\n};\n\n/** The order the segmented control presents states in. */\nexport const VERIFICATIONS: TcpVerificationStatus[] = [\n 'CORROBORATED',\n 'DISPUTED',\n 'RETRACTED',\n 'UNVERIFIED',\n];\n\n/** Active-segment styling per accent — semantic tokens only. */\nconst SEGMENT_ACTIVE: Record<VerificationAccent, string> = {\n success: 'bg-status-success-bg-subtle text-status-success-text shadow-elevation-1',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text shadow-elevation-1',\n destructive: 'bg-status-error-bg-subtle text-status-error-text shadow-elevation-1',\n secondary: 'bg-bg-sunken text-text-primary shadow-elevation-1',\n};\n\n// ---------------------------------------------------------------------------\n// Segmented verification control\n// ---------------------------------------------------------------------------\n\ninterface VerificationSegmentsProps {\n value: TcpVerificationStatus;\n onChange: (value: TcpVerificationStatus) => void;\n pending?: boolean;\n /** Icon-only segments (for the narrow reading rail). */\n compact?: boolean;\n className?: string;\n}\n\nfunction VerificationSegments({\n value,\n onChange,\n pending = false,\n compact = false,\n className,\n}: VerificationSegmentsProps) {\n return (\n <div\n role=\"radiogroup\"\n aria-label=\"Verification status\"\n className={cn(\n 'grid grid-cols-2 gap-1 rounded-xl border border-border-seam bg-bg-surface p-1',\n compact ? 'grid-cols-4' : 'sm:inline-grid sm:auto-cols-max sm:grid-flow-col',\n className\n )}\n >\n {VERIFICATIONS.map((v) => {\n const meta = VERIFICATION_META[v];\n const Icon = meta.icon;\n const active = v === value;\n return (\n <button\n key={v}\n type=\"button\"\n role=\"radio\"\n aria-checked={active}\n aria-label={meta.label}\n title={meta.label}\n disabled={pending}\n onClick={() => onChange(v)}\n className={cn(\n 'inline-flex min-h-9 items-center justify-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-medium transition-colors disabled:opacity-60',\n active\n ? SEGMENT_ACTIVE[meta.accent]\n : 'text-text-tertiary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n <Icon className=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className={compact ? 'sr-only' : 'hidden sm:inline'}>{meta.label}</span>\n </button>\n );\n })}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Citation row\n// ---------------------------------------------------------------------------\n\nfunction CitationRow({ citation, compact }: { citation: TcpCitation; compact: boolean }) {\n const verify = useTcpVerifyCitation();\n\n const changeVerification = async (verification: TcpVerificationStatus) => {\n if (verification === citation.verification) return;\n try {\n await verify.mutateAsync({ id: citation.id, verification });\n notify.success('Verification updated', formatEnumLabel(verification));\n } catch (err) {\n notify.error('Could not update verification', describeError(err));\n }\n };\n\n const meta = [citation.author, citation.publisher, formatDate(citation.publishedOn)]\n .filter((v) => v && v !== '—')\n .join(' · ');\n\n return (\n <li\n className={cn(\n 'rounded-2xl border border-border-seam bg-bg-surface transition-colors hover:border-border-strong',\n compact ? 'p-3.5' : 'p-4 sm:p-5'\n )}\n >\n <div className={cn('flex gap-3', compact ? 'items-start' : 'sm:gap-4')}>\n {/* Source-type motif tile */}\n <span\n className={cn(\n 'grid shrink-0 place-items-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary',\n compact ? 'h-9 w-9' : 'h-12 w-12'\n )}\n aria-hidden=\"true\"\n >\n {createElement(sourceTypeIcon(citation.sourceType), {\n className: compact ? 'h-4 w-4' : 'h-6 w-6',\n })}\n </span>\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"mb-1 flex flex-wrap items-center gap-1.5\">\n <StatusBadge\n status={citation.sourceType}\n icon={createElement(sourceTypeIcon(citation.sourceType), { className: 'h-3 w-3' })}\n />\n {compact && <StatusBadge status={citation.verification} />}\n </div>\n\n <p\n className={cn(\n 'font-display font-semibold leading-snug text-text-primary',\n compact ? 'text-sm' : 'text-base'\n )}\n >\n {citation.title}\n </p>\n {meta && <p className=\"mt-0.5 text-xs text-text-tertiary\">{meta}</p>}\n\n {citation.excerpt && !compact && (\n <blockquote className=\"mt-3 flex gap-2 rounded-lg bg-bg-sunken/60 px-3 py-2 text-sm text-text-secondary\">\n <Quote className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary\" aria-hidden=\"true\" />\n <span>{citation.excerpt}</span>\n </blockquote>\n )}\n\n {citation.provenance && !compact && (\n <p className=\"mt-2 text-xs text-text-tertiary\">\n <span className=\"font-medium text-text-secondary\">Provenance:</span>{' '}\n {citation.provenance}\n </p>\n )}\n\n {citation.url && (\n <a\n href={citation.url}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"mt-2 inline-flex max-w-full items-center gap-1 text-xs font-medium text-accent hover:underline\"\n >\n <span className=\"truncate\">{compact ? 'View source' : citation.url}</span>\n <ExternalLink className=\"h-3 w-3 shrink-0\" aria-hidden=\"true\" />\n </a>\n )}\n </div>\n </div>\n\n {/* Verification control */}\n <div\n className={cn(\n 'mt-3 flex flex-wrap items-center gap-x-3 gap-y-2 border-t border-border-seam pt-3',\n compact ? '' : 'sm:justify-between'\n )}\n >\n <span className=\"inline-flex items-center gap-1.5 text-xs font-medium text-text-secondary\">\n <ShieldCheck className=\"h-3.5 w-3.5 text-text-tertiary\" aria-hidden=\"true\" />\n Verification\n {verify.isPending && <Spinner size=\"sm\" />}\n </span>\n <VerificationSegments\n value={citation.verification}\n onChange={changeVerification}\n pending={verify.isPending}\n compact={compact}\n />\n </div>\n </li>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Methodology legend (static — no fabricated data)\n// ---------------------------------------------------------------------------\n\nfunction MethodologyLegend() {\n return (\n <div className=\"mt-4 rounded-2xl border border-border-seam bg-bg-sunken/50 p-4 sm:p-5\">\n <div className=\"flex items-start gap-3\">\n <span\n className=\"grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary\"\n aria-hidden=\"true\"\n >\n <ShieldCheck className=\"h-5 w-5\" />\n </span>\n <p className=\"text-sm text-text-secondary\">\n Every source is evaluated for accuracy, relevance, and cultural sensitivity through a\n transparent, multi-step review by editors and subject-area contributors.\n </p>\n </div>\n <dl className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n {VERIFICATIONS.map((v) => {\n const m = VERIFICATION_META[v];\n const Icon = m.icon;\n return (\n <div key={v} className=\"flex items-start gap-2\">\n <Icon\n className={cn(\n 'mt-0.5 h-4 w-4 shrink-0',\n m.accent === 'success' && 'text-status-success-text',\n m.accent === 'warning' && 'text-status-warning-text',\n m.accent === 'destructive' && 'text-status-error-text',\n m.accent === 'secondary' && 'text-text-tertiary'\n )}\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs font-semibold text-text-primary\">{m.label}</dt>\n <dd className=\"mt-0.5 text-xs text-text-tertiary\">{m.blurb}</dd>\n </div>\n </div>\n );\n })}\n </dl>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// CitationList\n// ---------------------------------------------------------------------------\n\ninterface CitationListProps {\n storyId: string;\n /** Narrow reading-rail rendering: smaller cards, icon-only segments, no legend. */\n compact?: boolean;\n}\n\nexport function CitationList({ storyId, compact = false }: CitationListProps) {\n const { data: citations, isLoading, isError, refetch } = useTcpCitations(storyId);\n\n if (isLoading) return <LoadingView label=\"Loading citations…\" />;\n if (isError) return <ErrorView message=\"We couldn't load citations.\" onRetry={() => refetch()} />;\n\n if (!citations || citations.length === 0) {\n return (\n <EmptyState\n icon={<BookMarked className=\"h-9 w-9\" />}\n title=\"No citations yet\"\n description=\"Add verifiable sources to ground this story in the record.\"\n />\n );\n }\n\n return (\n <div>\n <ul className={cn('flex flex-col', compact ? 'gap-3' : 'gap-3 sm:gap-4')}>\n {citations.map((citation) => (\n <CitationRow key={citation.id} citation={citation} compact={compact} />\n ))}\n </ul>\n {!compact && <MethodologyLegend />}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,56 +1,56 @@
1
1
  import { CoverImage as e } from "./CoverImage.js";
2
2
  import { formatNumber as t } from "../utils/formatters.js";
3
- import { Layers as n, Library as r } from "lucide-react";
4
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
- import { GlassCard as o } from "@burdenoff/fe-libs/ui";
3
+ import { OverlayBadge as n } from "./OverlayBadge.js";
4
+ import { Layers as r, Library as i } from "lucide-react";
5
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
+ import { GlassCard as s } from "@burdenoff/fe-libs/ui";
6
7
  //#region src/theculturepost/components/CollectionCard.tsx
7
- function s({ collection: s, onOpen: c }) {
8
- return /* @__PURE__ */ a(o, {
8
+ function c({ collection: c, onOpen: l }) {
9
+ return /* @__PURE__ */ o(s, {
9
10
  treatment: "plain",
10
11
  className: "flex flex-col overflow-hidden",
11
- children: [/* @__PURE__ */ i("button", {
12
+ children: [/* @__PURE__ */ o("button", {
12
13
  type: "button",
13
- onClick: c,
14
+ onClick: l,
14
15
  className: "group relative block aspect-[3/2] w-full overflow-hidden text-left",
15
- "aria-label": `Open ${s.name}`,
16
- children: /* @__PURE__ */ i(e, {
17
- fileId: s.cover?.id,
18
- alt: s.name,
16
+ "aria-label": `Open ${c.name}`,
17
+ children: [/* @__PURE__ */ a(e, {
18
+ fileId: c.cover?.id,
19
+ alt: c.name,
19
20
  rounded: "rounded-none",
20
21
  className: "h-full w-full transition-transform duration-500 group-hover:scale-[1.03]",
21
- icon: /* @__PURE__ */ i(r, { className: "h-8 w-8" })
22
- })
23
- }), /* @__PURE__ */ a("div", {
22
+ icon: /* @__PURE__ */ a(i, { className: "h-8 w-8" })
23
+ }), /* @__PURE__ */ o(n, {
24
+ position: "top-right",
25
+ icon: /* @__PURE__ */ a(r, { className: "h-3 w-3" }),
26
+ children: [
27
+ t(c.itemCount),
28
+ " ",
29
+ c.itemCount === 1 ? "item" : "items"
30
+ ]
31
+ })]
32
+ }), /* @__PURE__ */ o("div", {
24
33
  className: "flex flex-1 flex-col p-5",
25
34
  children: [
26
- /* @__PURE__ */ i("button", {
35
+ /* @__PURE__ */ a("button", {
27
36
  type: "button",
28
- onClick: c,
29
- className: "text-left text-base font-semibold text-text-primary hover:underline",
30
- children: s.name
37
+ onClick: l,
38
+ className: "font-display text-left text-lg font-semibold text-text-primary hover:underline",
39
+ children: c.name
31
40
  }),
32
- s.tradition && /* @__PURE__ */ i("p", {
41
+ c.tradition && /* @__PURE__ */ a("p", {
33
42
  className: "mt-0.5 text-sm text-text-secondary",
34
- children: s.tradition
43
+ children: c.tradition
35
44
  }),
36
- s.description && /* @__PURE__ */ i("p", {
45
+ c.description && /* @__PURE__ */ a("p", {
37
46
  className: "mt-2 line-clamp-2 text-sm text-text-secondary",
38
- children: s.description
39
- }),
40
- /* @__PURE__ */ a("div", {
41
- className: "mt-auto flex items-center gap-1.5 pt-4 text-xs text-text-tertiary",
42
- children: [
43
- /* @__PURE__ */ i(n, { className: "h-3.5 w-3.5" }),
44
- t(s.itemCount),
45
- " ",
46
- s.itemCount === 1 ? "item" : "items"
47
- ]
47
+ children: c.description
48
48
  })
49
49
  ]
50
50
  })]
51
51
  });
52
52
  }
53
53
  //#endregion
54
- export { s as CollectionCard };
54
+ export { c as CollectionCard };
55
55
 
56
56
  //# sourceMappingURL=CollectionCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionCard.js","names":[],"sources":["../../../src/theculturepost/components/CollectionCard.tsx"],"sourcesContent":["/**\n * CollectionCard — a tile for an archive collection.\n * @module theculturepost/components\n *\n * Cover image, name, tradition, description, item count. Links to the collection\n * detail page.\n */\n\nimport { Library, Layers } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { CoverImage } from './CoverImage';\nimport { formatNumber } from '../utils/formatters';\nimport type { TcpCollection } from '../types';\n\ninterface CollectionCardProps {\n collection: TcpCollection;\n onOpen: () => void;\n}\n\nexport function CollectionCard({ collection, onOpen }: CollectionCardProps) {\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col overflow-hidden\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group relative block aspect-[3/2] w-full overflow-hidden text-left\"\n aria-label={`Open ${collection.name}`}\n >\n <CoverImage\n fileId={collection.cover?.id}\n alt={collection.name}\n rounded=\"rounded-none\"\n className=\"h-full w-full transition-transform duration-500 group-hover:scale-[1.03]\"\n icon={<Library className=\"h-8 w-8\" />}\n />\n </button>\n\n <div className=\"flex flex-1 flex-col p-5\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"text-left text-base font-semibold text-text-primary hover:underline\"\n >\n {collection.name}\n </button>\n {collection.tradition && (\n <p className=\"mt-0.5 text-sm text-text-secondary\">{collection.tradition}</p>\n )}\n {collection.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">{collection.description}</p>\n )}\n <div className=\"mt-auto flex items-center gap-1.5 pt-4 text-xs text-text-tertiary\">\n <Layers className=\"h-3.5 w-3.5\" />\n {formatNumber(collection.itemCount)} {collection.itemCount === 1 ? 'item' : 'items'}\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,EAAe,EAAE,eAAY,aAA+B;AAC1E,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC,CACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;GACV,cAAY,QAAQ,EAAW;aAE/B,kBAAC,GAAD;IACE,QAAQ,EAAW,OAAO;IAC1B,KAAK,EAAW;IAChB,SAAQ;IACR,WAAU;IACV,MAAM,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;IACrC,CAAA;GACK,CAAA,EAET,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,EAAW;KACL,CAAA;IACR,EAAW,aACV,kBAAC,KAAD;KAAG,WAAU;eAAsC,EAAW;KAAc,CAAA;IAE7E,EAAW,eACV,kBAAC,KAAD;KAAG,WAAU;eAAiD,EAAW;KAAgB,CAAA;IAE3F,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;MACjC,EAAa,EAAW,UAAU;MAAC;MAAE,EAAW,cAAc,IAAI,SAAS;MACxE;;IACF;KACI"}
1
+ {"version":3,"file":"CollectionCard.js","names":[],"sources":["../../../src/theculturepost/components/CollectionCard.tsx"],"sourcesContent":["/**\n * CollectionCard — a tile for an archive collection.\n * @module theculturepost/components\n *\n * Cover image (collections DO carry a cover file) with the real item count as an\n * {@link OverlayBadge}; serif name, tradition (as region), description. Links to\n * the collection detail page.\n */\n\nimport { Library, Layers } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { CoverImage } from './CoverImage';\nimport { OverlayBadge } from './OverlayBadge';\nimport { formatNumber } from '../utils/formatters';\nimport type { TcpCollection } from '../types';\n\ninterface CollectionCardProps {\n collection: TcpCollection;\n onOpen: () => void;\n}\n\nexport function CollectionCard({ collection, onOpen }: CollectionCardProps) {\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col overflow-hidden\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group relative block aspect-[3/2] w-full overflow-hidden text-left\"\n aria-label={`Open ${collection.name}`}\n >\n <CoverImage\n fileId={collection.cover?.id}\n alt={collection.name}\n rounded=\"rounded-none\"\n className=\"h-full w-full transition-transform duration-500 group-hover:scale-[1.03]\"\n icon={<Library className=\"h-8 w-8\" />}\n />\n <OverlayBadge position=\"top-right\" icon={<Layers className=\"h-3 w-3\" />}>\n {formatNumber(collection.itemCount)} {collection.itemCount === 1 ? 'item' : 'items'}\n </OverlayBadge>\n </button>\n\n <div className=\"flex flex-1 flex-col p-5\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"font-display text-left text-lg font-semibold text-text-primary hover:underline\"\n >\n {collection.name}\n </button>\n {collection.tradition && (\n <p className=\"mt-0.5 text-sm text-text-secondary\">{collection.tradition}</p>\n )}\n {collection.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">{collection.description}</p>\n )}\n </div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;AAqBA,SAAgB,EAAe,EAAE,eAAY,aAA+B;AAC1E,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC,CACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;GACV,cAAY,QAAQ,EAAW;aAJjC,CAME,kBAAC,GAAD;IACE,QAAQ,EAAW,OAAO;IAC1B,KAAK,EAAW;IAChB,SAAQ;IACR,WAAU;IACV,MAAM,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;IACrC,CAAA,EACF,kBAAC,GAAD;IAAc,UAAS;IAAY,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;cAAvE;KACG,EAAa,EAAW,UAAU;KAAC;KAAE,EAAW,cAAc,IAAI,SAAS;KAC/D;MACR;MAET,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,EAAW;KACL,CAAA;IACR,EAAW,aACV,kBAAC,KAAD;KAAG,WAAU;eAAsC,EAAW;KAAc,CAAA;IAE7E,EAAW,eACV,kBAAC,KAAD;KAAG,WAAU;eAAiD,EAAW;KAAgB,CAAA;IAEvF;KACI"}