@burdenoff/microfe-theculturepost 2026.704.2 → 2026.705.2
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.
- package/dist/theculturepost/TheCulturePostRoot.js +63 -45
- package/dist/theculturepost/TheCulturePostRoot.js.map +1 -1
- package/dist/theculturepost/TheCulturePostRoutes.js +17 -9
- package/dist/theculturepost/TheCulturePostRoutes.js.map +1 -1
- package/dist/theculturepost/components/AddCitationDialog.js +131 -102
- package/dist/theculturepost/components/AddCitationDialog.js.map +1 -1
- package/dist/theculturepost/components/ArchiveItemCard.js +56 -43
- package/dist/theculturepost/components/ArchiveItemCard.js.map +1 -1
- package/dist/theculturepost/components/AudioPlayer.js +74 -0
- package/dist/theculturepost/components/AudioPlayer.js.map +1 -0
- package/dist/theculturepost/components/CitationList.js +215 -105
- package/dist/theculturepost/components/CitationList.js.map +1 -1
- package/dist/theculturepost/components/CollectionCard.js +32 -32
- package/dist/theculturepost/components/CollectionCard.js.map +1 -1
- package/dist/theculturepost/components/CreateArchiveItemDialog.js +133 -93
- package/dist/theculturepost/components/CreateArchiveItemDialog.js.map +1 -1
- package/dist/theculturepost/components/CreateCollectionDialog.js +91 -70
- package/dist/theculturepost/components/CreateCollectionDialog.js.map +1 -1
- package/dist/theculturepost/components/CreateStoryDialog.js +311 -132
- package/dist/theculturepost/components/CreateStoryDialog.js.map +1 -1
- package/dist/theculturepost/components/DecorativeCover.js +78 -0
- package/dist/theculturepost/components/DecorativeCover.js.map +1 -0
- package/dist/theculturepost/components/DecorativeDivider.js +45 -0
- package/dist/theculturepost/components/DecorativeDivider.js.map +1 -0
- package/dist/theculturepost/components/DiscussionCard.js +75 -60
- package/dist/theculturepost/components/DiscussionCard.js.map +1 -1
- package/dist/theculturepost/components/HistoryTimeline.js +115 -0
- package/dist/theculturepost/components/HistoryTimeline.js.map +1 -0
- package/dist/theculturepost/components/LifecycleStepper.js +33 -0
- package/dist/theculturepost/components/LifecycleStepper.js.map +1 -0
- package/dist/theculturepost/components/OpenDiscussionDialog.js +66 -56
- package/dist/theculturepost/components/OpenDiscussionDialog.js.map +1 -1
- package/dist/theculturepost/components/OralHistoryCard.js +6 -73
- package/dist/theculturepost/components/OverlayBadge.js +22 -0
- package/dist/theculturepost/components/OverlayBadge.js.map +1 -0
- package/dist/theculturepost/components/PageHero.js +62 -0
- package/dist/theculturepost/components/PageHero.js.map +1 -0
- package/dist/theculturepost/components/RecordOralHistoryDialog.js +306 -131
- package/dist/theculturepost/components/RecordOralHistoryDialog.js.map +1 -1
- package/dist/theculturepost/components/ReplyThread.js +268 -139
- package/dist/theculturepost/components/ReplyThread.js.map +1 -1
- package/dist/theculturepost/components/StatusBadge.js +9 -9
- package/dist/theculturepost/components/StatusBadge.js.map +1 -1
- package/dist/theculturepost/components/StoryCard.js +54 -49
- package/dist/theculturepost/components/StoryCard.js.map +1 -1
- package/dist/theculturepost/components/TopicCard.js +60 -41
- package/dist/theculturepost/components/TopicCard.js.map +1 -1
- package/dist/theculturepost/components/Waveform.js +31 -0
- package/dist/theculturepost/components/Waveform.js.map +1 -0
- package/dist/theculturepost/components/categoryMeta.js +20 -0
- package/dist/theculturepost/components/categoryMeta.js.map +1 -0
- package/dist/theculturepost/components/index.js +41 -0
- package/dist/theculturepost/components/viz/BarChart.js +53 -0
- package/dist/theculturepost/components/viz/BarChart.js.map +1 -0
- package/dist/theculturepost/components/viz/Donut.js +58 -0
- package/dist/theculturepost/components/viz/Donut.js.map +1 -0
- package/dist/theculturepost/components/viz/StatTile.js +35 -0
- package/dist/theculturepost/components/viz/StatTile.js.map +1 -0
- package/dist/theculturepost/components/viz/index.js +3 -0
- package/dist/theculturepost/hooks/useFiles.js +1 -1
- package/dist/theculturepost/hooks/useFiles.js.map +1 -1
- package/dist/theculturepost/pages/HomePage.js +247 -124
- package/dist/theculturepost/pages/HomePage.js.map +1 -1
- package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js +319 -127
- package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/archive/ArchivePage.js +265 -180
- package/dist/theculturepost/pages/archive/ArchivePage.js.map +1 -1
- package/dist/theculturepost/pages/archive/CollectionDetailPage.js +158 -143
- package/dist/theculturepost/pages/archive/CollectionDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js +155 -93
- package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/discussions/DiscussionsPage.js +16 -16
- package/dist/theculturepost/pages/discussions/DiscussionsPage.js.map +1 -1
- package/dist/theculturepost/pages/governance/GovernancePage.js +293 -0
- package/dist/theculturepost/pages/governance/GovernancePage.js.map +1 -0
- package/dist/theculturepost/pages/stories/StoriesPage.js +18 -18
- package/dist/theculturepost/pages/stories/StoriesPage.js.map +1 -1
- package/dist/theculturepost/pages/stories/StoryDetailPage.js +603 -285
- package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/timeline/TimelinePage.js +162 -0
- package/dist/theculturepost/pages/timeline/TimelinePage.js.map +1 -0
- package/dist/theculturepost/pages/topics/TopicDetailPage.js +239 -163
- package/dist/theculturepost/pages/topics/TopicDetailPage.js.map +1 -1
- package/dist/theculturepost/pages/topics/TopicsPage.js +76 -74
- package/dist/theculturepost/pages/topics/TopicsPage.js.map +1 -1
- package/dist/theculturepost/styles/injectDisplayFont.js +11 -0
- package/dist/theculturepost/styles/injectDisplayFont.js.map +1 -0
- package/dist/theculturepost/utils/formatters.js +6 -1
- package/dist/theculturepost/utils/formatters.js.map +1 -1
- package/package.json +1 -1
- package/dist/theculturepost/components/OralHistoryCard.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useTheCulturePost as e } from "../../context/TheCulturePostContext.js";
|
|
2
|
-
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
-
import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
|
|
4
2
|
import "../../context/index.js";
|
|
3
|
+
import { PageHero as t } from "../../components/PageHero.js";
|
|
4
|
+
import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
|
|
5
5
|
import { formatEnumLabel as i } from "../../utils/formatters.js";
|
|
6
6
|
import { TopicCard as a } from "../../components/TopicCard.js";
|
|
7
7
|
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
@@ -9,13 +9,15 @@ import { useTcpTopics as s } from "../../hooks/useTcpApi.js";
|
|
|
9
9
|
import { useCursorPager as c } from "../../hooks/useCursorPager.js";
|
|
10
10
|
import "../../hooks/index.js";
|
|
11
11
|
import { Pagination as l } from "../../components/Pagination.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
12
|
+
import { FilterTabs as u } from "../../components/FilterTabs.js";
|
|
13
|
+
import { CreateTopicDialog as d } from "../../components/CreateTopicDialog.js";
|
|
14
|
+
import "../../components/index.js";
|
|
15
|
+
import { useState as f } from "react";
|
|
16
|
+
import { Compass as p, Plus as m } from "lucide-react";
|
|
17
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
18
|
+
import { Button as _ } from "@burdenoff/fe-libs/ui";
|
|
17
19
|
//#region src/theculturepost/pages/topics/TopicsPage.tsx
|
|
18
|
-
var
|
|
20
|
+
var v = 12, y = [
|
|
19
21
|
"CULTURE_AND_IDENTITY",
|
|
20
22
|
"RELIGION_AND_FAITH",
|
|
21
23
|
"HISTORY",
|
|
@@ -25,77 +27,77 @@ var C = 12, w = [
|
|
|
25
27
|
"ORAL_HISTORIES",
|
|
26
28
|
"PHILOSOPHY_AND_THOUGHT",
|
|
27
29
|
"VALUES_AND_ETHICS"
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
], b = (e) => i(e).replace(/ And /g, " & "), x = [{
|
|
31
|
+
value: "ALL",
|
|
32
|
+
label: "All topics"
|
|
33
|
+
}, ...y.map((e) => ({
|
|
34
|
+
value: e,
|
|
35
|
+
label: b(e)
|
|
36
|
+
}))];
|
|
37
|
+
function S() {
|
|
38
|
+
let { navigateTo: i } = e(), y = c(), [b, S] = f("ALL"), [C, w] = f(!1), { data: T, isLoading: E, isError: D, refetch: O } = s({
|
|
39
|
+
category: b === "ALL" ? void 0 : b,
|
|
32
40
|
pagination: {
|
|
33
|
-
limit:
|
|
34
|
-
cursor:
|
|
41
|
+
limit: v,
|
|
42
|
+
cursor: y.cursor
|
|
35
43
|
}
|
|
36
|
-
}),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
children: [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
44
|
+
}), k = T?.items ?? [], A = (e) => {
|
|
45
|
+
S(e), y.reset();
|
|
46
|
+
}, j = /* @__PURE__ */ g(_, {
|
|
47
|
+
onClick: () => w(!0),
|
|
48
|
+
children: [/* @__PURE__ */ h(m, { className: "mr-1.5 h-4 w-4" }), " New topic"]
|
|
49
|
+
});
|
|
50
|
+
return /* @__PURE__ */ g("div", {
|
|
51
|
+
className: "h-full overflow-y-auto",
|
|
52
|
+
children: [/* @__PURE__ */ g("div", {
|
|
53
|
+
className: "mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6",
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ h(t, {
|
|
56
|
+
eyebrow: "Explore",
|
|
57
|
+
title: "Topics",
|
|
58
|
+
description: "Curated subjects spanning culture, faith, history, heritage and thought.",
|
|
59
|
+
actions: j
|
|
60
|
+
}),
|
|
61
|
+
/* @__PURE__ */ h(u, {
|
|
62
|
+
options: x,
|
|
63
|
+
value: b,
|
|
64
|
+
onChange: A,
|
|
65
|
+
className: "max-w-full"
|
|
66
|
+
}),
|
|
67
|
+
E ? /* @__PURE__ */ h(r, { label: "Loading topics…" }) : D ? /* @__PURE__ */ h(n, {
|
|
68
|
+
message: "We couldn't load topics.",
|
|
69
|
+
onRetry: () => O()
|
|
70
|
+
}) : k.length === 0 && y.page === 0 ? /* @__PURE__ */ h(o, {
|
|
71
|
+
illustration: "empty-search",
|
|
72
|
+
icon: /* @__PURE__ */ h(p, { className: "h-10 w-10" }),
|
|
73
|
+
title: b === "ALL" ? "No topics yet" : "No topics in this category",
|
|
74
|
+
description: b === "ALL" ? "Curate the first topic to organise stories around a shared subject." : "Try another category, or curate a new topic.",
|
|
75
|
+
action: j
|
|
76
|
+
}) : /* @__PURE__ */ g("div", {
|
|
77
|
+
className: "flex flex-col gap-6",
|
|
78
|
+
children: [/* @__PURE__ */ h("div", {
|
|
79
|
+
className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3",
|
|
80
|
+
children: k.map((e) => /* @__PURE__ */ h(a, {
|
|
81
|
+
topic: e,
|
|
82
|
+
onOpen: () => i(`topics/${e.id}`)
|
|
83
|
+
}, e.id))
|
|
84
|
+
}), /* @__PURE__ */ h(l, {
|
|
85
|
+
page: y.page,
|
|
86
|
+
pageSize: v,
|
|
87
|
+
itemCount: k.length,
|
|
88
|
+
hasMore: T?.pageInfo.hasMore ?? !1,
|
|
89
|
+
onPageChange: (e) => y.onPageChange(e, T?.pageInfo.cursor)
|
|
90
|
+
})]
|
|
76
91
|
})
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}), /* @__PURE__ */ h(l, {
|
|
84
|
-
page: E.page,
|
|
85
|
-
pageSize: C,
|
|
86
|
-
itemCount: F.length,
|
|
87
|
-
hasMore: j?.pageInfo.hasMore ?? !1,
|
|
88
|
-
onPageChange: (e) => E.onPageChange(e, j?.pageInfo.cursor)
|
|
89
|
-
})] }),
|
|
90
|
-
/* @__PURE__ */ h(u, {
|
|
91
|
-
open: k,
|
|
92
|
-
onOpenChange: A,
|
|
93
|
-
onCreated: (e) => T(`topics/${e.id}`)
|
|
94
|
-
})
|
|
95
|
-
]
|
|
92
|
+
]
|
|
93
|
+
}), /* @__PURE__ */ h(d, {
|
|
94
|
+
open: C,
|
|
95
|
+
onOpenChange: w,
|
|
96
|
+
onCreated: (e) => i(`topics/${e.id}`)
|
|
97
|
+
})]
|
|
96
98
|
});
|
|
97
99
|
}
|
|
98
100
|
//#endregion
|
|
99
|
-
export {
|
|
101
|
+
export { S as TopicsPage };
|
|
100
102
|
|
|
101
103
|
//# sourceMappingURL=TopicsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TopicsPage.js","names":[],"sources":["../../../../src/theculturepost/pages/topics/TopicsPage.tsx"],"sourcesContent":["/**\n * TopicsPage — browse curated
|
|
1
|
+
{"version":3,"file":"TopicsPage.js","names":[],"sources":["../../../../src/theculturepost/pages/topics/TopicsPage.tsx"],"sourcesContent":["/**\n * TopicsPage — the topics hub: browse curated subjects.\n * @module theculturepost/pages/topics\n *\n * An editorial {@link PageHero} header, a category pill-filter bar (every\n * {@link TcpTopicCategory}, plus \"All\"), and the image-forward {@link TopicCard}\n * grid. Reads `tcpListTopics` (optionally filtered by category), cursor-paginated.\n * The schema exposes no per-topic imagery or list story counts, so cards use the\n * token-only DecorativeCover — never a fabricated photo or count.\n */\n\nimport { useState } from 'react';\nimport { Compass, Plus } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport {\n PageHero,\n FilterTabs,\n type FilterOption,\n TopicCard,\n CreateTopicDialog,\n EmptyState,\n LoadingView,\n ErrorView,\n Pagination,\n} from '../../components';\nimport { useTcpTopics, useCursorPager } from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport type { TcpTopicCategory } from '../../types';\n\nconst PAGE_SIZE = 12;\n\nconst CATEGORIES: TcpTopicCategory[] = [\n 'CULTURE_AND_IDENTITY',\n 'RELIGION_AND_FAITH',\n 'HISTORY',\n 'HERITAGE_AND_ARCHIVES',\n 'FESTIVALS_AND_RITUALS',\n 'MIGRATION_AND_DIASPORA',\n 'ORAL_HISTORIES',\n 'PHILOSOPHY_AND_THOUGHT',\n 'VALUES_AND_ETHICS',\n];\n\ntype CategoryFilter = 'ALL' | TcpTopicCategory;\n\n/** Nicer pill label: \"Culture And Identity\" → \"Culture & Identity\". */\nconst pillLabel = (category: TcpTopicCategory): string =>\n formatEnumLabel(category).replace(/ And /g, ' & ');\n\nconst CATEGORY_FILTERS: FilterOption<CategoryFilter>[] = [\n { value: 'ALL', label: 'All topics' },\n ...CATEGORIES.map((c) => ({ value: c, label: pillLabel(c) })),\n];\n\nexport function TopicsPage() {\n const { navigateTo } = useTheCulturePost();\n const pager = useCursorPager();\n const [category, setCategory] = useState<CategoryFilter>('ALL');\n const [createOpen, setCreateOpen] = useState(false);\n\n const { data, isLoading, isError, refetch } = useTcpTopics({\n category: category === 'ALL' ? undefined : category,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n\n const topics = data?.items ?? [];\n\n const changeCategory = (next: CategoryFilter) => {\n setCategory(next);\n pager.reset();\n };\n\n const newTopicButton = (\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New topic\n </Button>\n );\n\n return (\n <div className=\"h-full overflow-y-auto\">\n <div className=\"mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6\">\n <PageHero\n eyebrow=\"Explore\"\n title=\"Topics\"\n description=\"Curated subjects spanning culture, faith, history, heritage and thought.\"\n actions={newTopicButton}\n />\n\n <FilterTabs\n options={CATEGORY_FILTERS}\n value={category}\n onChange={changeCategory}\n className=\"max-w-full\"\n />\n\n {isLoading ? (\n <LoadingView label=\"Loading topics…\" />\n ) : isError ? (\n <ErrorView message=\"We couldn't load topics.\" onRetry={() => refetch()} />\n ) : topics.length === 0 && pager.page === 0 ? (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Compass className=\"h-10 w-10\" />}\n title={category === 'ALL' ? 'No topics yet' : 'No topics in this category'}\n description={\n category === 'ALL'\n ? 'Curate the first topic to organise stories around a shared subject.'\n : 'Try another category, or curate a new topic.'\n }\n action={newTopicButton}\n />\n ) : (\n <div className=\"flex flex-col gap-6\">\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {topics.map((topic) => (\n <TopicCard\n key={topic.id}\n topic={topic}\n onOpen={() => navigateTo(`topics/${topic.id}`)}\n />\n ))}\n </div>\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={topics.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo.cursor)}\n />\n </div>\n )}\n </div>\n\n <CreateTopicDialog\n open={createOpen}\n onOpenChange={setCreateOpen}\n onCreated={(topic) => navigateTo(`topics/${topic.id}`)}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8BA,IAAM,IAAY,IAEZ,IAAiC;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAKK,KAAa,MACjB,EAAgB,EAAS,CAAC,QAAQ,UAAU,MAAM,EAE9C,IAAmD,CACvD;CAAE,OAAO;CAAO,OAAO;CAAc,EACrC,GAAG,EAAW,KAAK,OAAO;CAAE,OAAO;CAAG,OAAO,EAAU,EAAE;CAAE,EAAE,CAC9D;AAED,SAAgB,IAAa;CAC3B,IAAM,EAAE,kBAAe,GAAmB,EACpC,IAAQ,GAAgB,EACxB,CAAC,GAAU,KAAe,EAAyB,MAAM,EACzD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAE7C,EAAE,SAAM,cAAW,YAAS,eAAY,EAAa;EACzD,UAAU,MAAa,QAAQ,KAAA,IAAY;EAC3C,YAAY;GAAE,OAAO;GAAW,QAAQ,EAAM;GAAQ;EACvD,CAAC,EAEI,IAAS,GAAM,SAAS,EAAE,EAE1B,KAAkB,MAAyB;AAE/C,EADA,EAAY,EAAK,EACjB,EAAM,OAAO;IAGT,IACJ,kBAAC,GAAD;EAAQ,eAAe,EAAc,GAAK;YAA1C,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,aAC5B;;AAGX,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,SAAQ;KACR,OAAM;KACN,aAAY;KACZ,SAAS;KACT,CAAA;IAEF,kBAAC,GAAD;KACE,SAAS;KACT,OAAO;KACP,UAAU;KACV,WAAU;KACV,CAAA;IAED,IACC,kBAAC,GAAD,EAAa,OAAM,mBAAoB,CAAA,GACrC,IACF,kBAAC,GAAD;KAAW,SAAQ;KAA2B,eAAe,GAAS;KAAI,CAAA,GACxE,EAAO,WAAW,KAAK,EAAM,SAAS,IACxC,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,GAAD,EAAS,WAAU,aAAc,CAAA;KACvC,OAAO,MAAa,QAAQ,kBAAkB;KAC9C,aACE,MAAa,QACT,wEACA;KAEN,QAAQ;KACR,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAO,KAAK,MACX,kBAAC,GAAD;OAES;OACP,cAAc,EAAW,UAAU,EAAM,KAAK;OAC9C,EAHK,EAAM,GAGX,CACF;MACE,CAAA,EACN,kBAAC,GAAD;MACE,MAAM,EAAM;MACZ,UAAU;MACV,WAAW,EAAO;MAClB,SAAS,GAAM,SAAS,WAAW;MACnC,eAAe,MAAW,EAAM,aAAa,GAAQ,GAAM,SAAS,OAAO;MAC3E,CAAA,CACE;;IAEJ;MAEN,kBAAC,GAAD;GACE,MAAM;GACN,cAAc;GACd,YAAY,MAAU,EAAW,UAAU,EAAM,KAAK;GACtD,CAAA,CACE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/theculturepost/styles/injectDisplayFont.ts
|
|
2
|
+
var e = "tcp-display-font", t = ".font-display{font-family:Georgia,'Iowan Old Style','Palatino Linotype',Palatino,'Times New Roman',serif;font-feature-settings:'liga' 1,'kern' 1;letter-spacing:-0.01em;}";
|
|
3
|
+
function n() {
|
|
4
|
+
if (typeof document > "u" || document.getElementById(e)) return;
|
|
5
|
+
let n = document.createElement("style");
|
|
6
|
+
n.id = e, n.textContent = t, document.head.appendChild(n);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { n as injectDisplayFont };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=injectDisplayFont.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectDisplayFont.js","names":[],"sources":["../../../src/theculturepost/styles/injectDisplayFont.ts"],"sourcesContent":["/**\n * injectDisplayFont — guarantee the `.font-display` utility exists in the built\n * MFE, regardless of the host shell's Tailwind content globs or CSS pipeline.\n * @module theculturepost/styles\n *\n * MFEs ship no component CSS to the shell (the shell owns Tailwind + tokens), and\n * `.font-display` is a custom class Tailwind will not emit. Vite's lib build also\n * strips plain CSS imports out of the JS. So instead of relying on a bundled\n * stylesheet, we inject a single, idempotent <style> rule at runtime the first\n * time the MFE mounts. It sets ONLY the font family (colour stays token-driven on\n * each element). Import for side-effect from the MFE root.\n */\n\nconst STYLE_ID = 'tcp-display-font';\n\nconst CSS = `.font-display{font-family:Georgia,'Iowan Old Style','Palatino Linotype',Palatino,'Times New Roman',serif;font-feature-settings:'liga' 1,'kern' 1;letter-spacing:-0.01em;}`;\n\nexport function injectDisplayFont(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = CSS;\n document.head.appendChild(style);\n}\n"],"mappings":";AAaA,IAAM,IAAW,oBAEX,IAAM;AAEZ,SAAgB,IAA0B;AAExC,KADI,OAAO,WAAa,OACpB,SAAS,eAAe,EAAS,CAAE;CACvC,IAAM,IAAQ,SAAS,cAAc,QAAQ;AAG7C,CAFA,EAAM,KAAK,GACX,EAAM,cAAc,GACpB,SAAS,KAAK,YAAY,EAAM"}
|
|
@@ -42,7 +42,12 @@ function i(e) {
|
|
|
42
42
|
function a(e) {
|
|
43
43
|
return e ? e.toLowerCase().replace(/_/g, " ").replace(/\b\w/g, (e) => e.toUpperCase()) : "—";
|
|
44
44
|
}
|
|
45
|
+
function o(e) {
|
|
46
|
+
if (!e) return "?";
|
|
47
|
+
let t = e.trim().split(/\s+/).filter(Boolean);
|
|
48
|
+
return t.length === 0 ? "?" : t.length === 1 ? t[0].slice(0, 2).toUpperCase() : (t[0][0] + t[t.length - 1][0]).toUpperCase();
|
|
49
|
+
}
|
|
45
50
|
//#endregion
|
|
46
|
-
export { e as formatDate, t as formatDateTime, i as formatDuration, a as formatEnumLabel, r as formatNumber, n as formatRelativeTime };
|
|
51
|
+
export { e as formatDate, t as formatDateTime, i as formatDuration, a as formatEnumLabel, r as formatNumber, n as formatRelativeTime, o as initialsOf };
|
|
47
52
|
|
|
48
53
|
//# sourceMappingURL=formatters.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatters.js","names":[],"sources":["../../../src/theculturepost/utils/formatters.ts"],"sourcesContent":["/**\n * Formatting utilities for the TheCulturePost microfrontend\n * @module theculturepost/utils/formatters\n */\n\n/** Format a date string or Date into a human-readable date. */\nexport function formatDate(\n date: string | Date | null | undefined,\n options?: Partial<Intl.DateTimeFormatOptions>\n): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n ...options,\n }).format(d);\n}\n\n/** Format a date-time into a date + time string. */\nexport function formatDateTime(\n date: string | Date | null | undefined,\n options?: Partial<Intl.DateTimeFormatOptions>\n): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n ...options,\n }).format(d);\n}\n\n/** Format a date into a relative time string (e.g. \"2 hours ago\", \"in 3 days\"). */\nexport function formatRelativeTime(date: string | Date | null | undefined): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n\n const now = new Date();\n const diffMs = d.getTime() - now.getTime();\n const future = diffMs >= 0;\n const absSeconds = Math.floor(Math.abs(diffMs) / 1000);\n const minutes = Math.floor(absSeconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(days / 30);\n const years = Math.floor(days / 365);\n\n const phrase = (value: number, unit: string): string => {\n const label = `${value} ${unit}${value !== 1 ? 's' : ''}`;\n return future ? `in ${label}` : `${label} ago`;\n };\n\n if (absSeconds < 60) return future ? 'soon' : 'just now';\n if (minutes < 60) return phrase(minutes, 'min');\n if (hours < 24) return phrase(hours, 'hour');\n if (days < 7) return phrase(days, 'day');\n if (weeks < 4) return phrase(weeks, 'week');\n if (months < 12) return phrase(months, 'month');\n return phrase(years, 'year');\n}\n\n/** Format a number with locale grouping. */\nexport function formatNumber(\n value: number | null | undefined,\n options?: Partial<Intl.NumberFormatOptions>\n): string {\n if (value == null || Number.isNaN(value)) return '—';\n return new Intl.NumberFormat(undefined, options).format(value);\n}\n\n/** Format a duration in seconds into a compact \"h m s\" / \"m s\" string. */\nexport function formatDuration(totalSeconds: number | null | undefined): string {\n if (totalSeconds == null || Number.isNaN(totalSeconds) || totalSeconds <= 0) return '—';\n const secs = Math.floor(totalSeconds);\n const hours = Math.floor(secs / 3600);\n const minutes = Math.floor((secs % 3600) / 60);\n const seconds = secs % 60;\n if (hours > 0) return `${hours}h ${minutes}m`;\n if (minutes > 0) return `${minutes}m ${seconds}s`;\n return `${seconds}s`;\n}\n\n/** Turn an ENUM_LIKE_TOKEN into a human \"Enum Like Token\" label. */\nexport function formatEnumLabel(value: string | null | undefined): string {\n if (!value) return '—';\n return value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n"],"mappings":";AAMA,SAAgB,EACd,GACA,GACQ;AACR,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAI,OAAO,KAAS,WAAW,IAAI,KAAK,EAAK,GAAG;AAEtD,QADI,OAAO,MAAM,EAAE,SAAS,CAAC,GAAS,MAC/B,IAAI,KAAK,eAAe,KAAA,GAAW;EACxC,KAAK;EACL,OAAO;EACP,MAAM;EACN,GAAG;EACJ,CAAC,CAAC,OAAO,EAAE;;AAId,SAAgB,EACd,GACA,GACQ;AACR,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAI,OAAO,KAAS,WAAW,IAAI,KAAK,EAAK,GAAG;AAEtD,QADI,OAAO,MAAM,EAAE,SAAS,CAAC,GAAS,MAC/B,IAAI,KAAK,eAAe,KAAA,GAAW;EACxC,KAAK;EACL,OAAO;EACP,MAAM;EACN,MAAM;EACN,QAAQ;EACR,GAAG;EACJ,CAAC,CAAC,OAAO,EAAE;;AAId,SAAgB,EAAmB,GAAgD;AACjF,KAAI,CAAC,EAAM,QAAO;CAClB,IAAM,IAAI,OAAO,KAAS,WAAW,IAAI,KAAK,EAAK,GAAG;AACtD,KAAI,OAAO,MAAM,EAAE,SAAS,CAAC,CAAE,QAAO;CAEtC,IAAM,oBAAM,IAAI,MAAM,EAChB,IAAS,EAAE,SAAS,GAAG,EAAI,SAAS,EACpC,IAAS,KAAU,GACnB,IAAa,KAAK,MAAM,KAAK,IAAI,EAAO,GAAG,IAAK,EAChD,IAAU,KAAK,MAAM,IAAa,GAAG,EACrC,IAAQ,KAAK,MAAM,IAAU,GAAG,EAChC,IAAO,KAAK,MAAM,IAAQ,GAAG,EAC7B,IAAQ,KAAK,MAAM,IAAO,EAAE,EAC5B,IAAS,KAAK,MAAM,IAAO,GAAG,EAC9B,IAAQ,KAAK,MAAM,IAAO,IAAI,EAE9B,KAAU,GAAe,MAAyB;EACtD,IAAM,IAAQ,GAAG,EAAM,GAAG,IAAO,MAAU,IAAU,KAAN;AAC/C,SAAO,IAAS,MAAM,MAAU,GAAG,EAAM;;AAS3C,QANI,IAAa,KAAW,IAAS,SAAS,aAC1C,IAAU,KAAW,EAAO,GAAS,MAAM,GAC3C,IAAQ,KAAW,EAAO,GAAO,OAAO,GACxC,IAAO,IAAU,EAAO,GAAM,MAAM,GACpC,IAAQ,IAAU,EAAO,GAAO,OAAO,GACvC,IAAS,KAAW,EAAO,GAAQ,QAAQ,GACxC,EAAO,GAAO,OAAO;;AAI9B,SAAgB,EACd,GACA,GACQ;AAER,QADI,KAAS,QAAQ,OAAO,MAAM,EAAM,GAAS,MAC1C,IAAI,KAAK,aAAa,KAAA,GAAW,EAAQ,CAAC,OAAO,EAAM;;AAIhE,SAAgB,EAAe,GAAiD;AAC9E,KAAI,KAAgB,QAAQ,OAAO,MAAM,EAAa,IAAI,KAAgB,EAAG,QAAO;CACpF,IAAM,IAAO,KAAK,MAAM,EAAa,EAC/B,IAAQ,KAAK,MAAM,IAAO,KAAK,EAC/B,IAAU,KAAK,MAAO,IAAO,OAAQ,GAAG,EACxC,IAAU,IAAO;AAGvB,QAFI,IAAQ,IAAU,GAAG,EAAM,IAAI,EAAQ,KACvC,IAAU,IAAU,GAAG,EAAQ,IAAI,EAAQ,KACxC,GAAG,EAAQ;;AAIpB,SAAgB,EAAgB,GAA0C;AAExE,QADK,IACE,EACJ,aAAa,CACb,QAAQ,MAAM,IAAI,CAClB,QAAQ,UAAU,MAAS,EAAK,aAAa,CAAC,GAJ9B"}
|
|
1
|
+
{"version":3,"file":"formatters.js","names":[],"sources":["../../../src/theculturepost/utils/formatters.ts"],"sourcesContent":["/**\n * Formatting utilities for the TheCulturePost microfrontend\n * @module theculturepost/utils/formatters\n */\n\n/** Format a date string or Date into a human-readable date. */\nexport function formatDate(\n date: string | Date | null | undefined,\n options?: Partial<Intl.DateTimeFormatOptions>\n): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n ...options,\n }).format(d);\n}\n\n/** Format a date-time into a date + time string. */\nexport function formatDateTime(\n date: string | Date | null | undefined,\n options?: Partial<Intl.DateTimeFormatOptions>\n): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n ...options,\n }).format(d);\n}\n\n/** Format a date into a relative time string (e.g. \"2 hours ago\", \"in 3 days\"). */\nexport function formatRelativeTime(date: string | Date | null | undefined): string {\n if (!date) return '—';\n const d = typeof date === 'string' ? new Date(date) : date;\n if (Number.isNaN(d.getTime())) return '—';\n\n const now = new Date();\n const diffMs = d.getTime() - now.getTime();\n const future = diffMs >= 0;\n const absSeconds = Math.floor(Math.abs(diffMs) / 1000);\n const minutes = Math.floor(absSeconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(days / 30);\n const years = Math.floor(days / 365);\n\n const phrase = (value: number, unit: string): string => {\n const label = `${value} ${unit}${value !== 1 ? 's' : ''}`;\n return future ? `in ${label}` : `${label} ago`;\n };\n\n if (absSeconds < 60) return future ? 'soon' : 'just now';\n if (minutes < 60) return phrase(minutes, 'min');\n if (hours < 24) return phrase(hours, 'hour');\n if (days < 7) return phrase(days, 'day');\n if (weeks < 4) return phrase(weeks, 'week');\n if (months < 12) return phrase(months, 'month');\n return phrase(years, 'year');\n}\n\n/** Format a number with locale grouping. */\nexport function formatNumber(\n value: number | null | undefined,\n options?: Partial<Intl.NumberFormatOptions>\n): string {\n if (value == null || Number.isNaN(value)) return '—';\n return new Intl.NumberFormat(undefined, options).format(value);\n}\n\n/** Format a duration in seconds into a compact \"h m s\" / \"m s\" string. */\nexport function formatDuration(totalSeconds: number | null | undefined): string {\n if (totalSeconds == null || Number.isNaN(totalSeconds) || totalSeconds <= 0) return '—';\n const secs = Math.floor(totalSeconds);\n const hours = Math.floor(secs / 3600);\n const minutes = Math.floor((secs % 3600) / 60);\n const seconds = secs % 60;\n if (hours > 0) return `${hours}h ${minutes}m`;\n if (minutes > 0) return `${minutes}m ${seconds}s`;\n return `${seconds}s`;\n}\n\n/** Turn an ENUM_LIKE_TOKEN into a human \"Enum Like Token\" label. */\nexport function formatEnumLabel(value: string | null | undefined): string {\n if (!value) return '—';\n return value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n\n/** Two-letter monogram from a name (e.g. \"Amma Lakshmi\" -> \"AL\"). */\nexport function initialsOf(name: string | null | undefined): string {\n if (!name) return '?';\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '?';\n if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase();\n return (parts[0]![0]! + parts[parts.length - 1]![0]!).toUpperCase();\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"OralHistoryCard.js","names":[],"sources":["../../../src/theculturepost/components/OralHistoryCard.tsx"],"sourcesContent":["/**\n * OralHistoryCard — a row for a recorded oral history.\n * @module theculturepost/components\n *\n * Narrator (a plain String — used directly), language, access level, duration.\n * A resolvable recording exposes a \"Listen / download\" link via the file id.\n */\n\nimport { AudioLines, Mic, Clock, Languages, Download } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { useFileUrl } from '../hooks/useFiles';\nimport { formatDuration, formatEnumLabel } from '../utils/formatters';\nimport type { TcpOralHistory } from '../types';\n\ninterface OralHistoryCardProps {\n oralHistory: TcpOralHistory;\n}\n\nexport function OralHistoryCard({ oralHistory }: OralHistoryCardProps) {\n const { data: recordingUrl } = useFileUrl(oralHistory.recording?.id);\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex items-start gap-4 p-4\">\n <div className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary\">\n <AudioLines className=\"h-5 w-5\" />\n </div>\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{oralHistory.title}</h3>\n <StatusBadge status={oralHistory.accessLevel} />\n </div>\n\n {oralHistory.summary && (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{oralHistory.summary}</p>\n )}\n\n <div className=\"mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-tertiary\">\n {oralHistory.narratorName && (\n <span className=\"inline-flex items-center gap-1\">\n <Mic className=\"h-3.5 w-3.5\" /> {oralHistory.narratorName}\n </span>\n )}\n {oralHistory.durationSecs > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(oralHistory.durationSecs)}\n </span>\n )}\n {oralHistory.language && (\n <span className=\"inline-flex items-center gap-1\">\n <Languages className=\"h-3.5 w-3.5\" /> {formatEnumLabel(oralHistory.language)}\n </span>\n )}\n {recordingUrl && (\n <a\n href={recordingUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 font-medium text-accent hover:underline\"\n >\n <Download className=\"h-3.5 w-3.5\" /> Listen\n </a>\n )}\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;AAmBA,SAAgB,EAAgB,EAAE,kBAAqC;CACrE,IAAM,EAAE,MAAM,MAAiB,EAAW,EAAY,WAAW,GAAG;AAEpE,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;GAC9B,CAAA,EAEN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAoD,EAAY;MAAW,CAAA,EACzF,kBAAC,GAAD,EAAa,QAAQ,EAAY,aAAe,CAAA,CAC5C;;IAEL,EAAY,WACX,kBAAC,KAAD;KAAG,WAAU;eAAiD,EAAY;KAAY,CAAA;IAGxF,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,EAAY,gBACX,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAK,WAAU,eAAgB,CAAA;;QAAE,EAAY;QACxC;;MAER,EAAY,eAAe,KAC1B,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;;QAAE,EAAe,EAAY,aAAa;QACtE;;MAER,EAAY,YACX,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAW,WAAU,eAAgB,CAAA;;QAAE,EAAgB,EAAY,SAAS;QACvE;;MAER,KACC,kBAAC,KAAD;OACE,MAAM;OACN,QAAO;OACP,KAAI;OACJ,WAAU;iBAJZ,CAME,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EAAA,UAClC;;MAEF;;IACF;KACI"}
|