@burdenoff/microfe-theculturepost 2026.705.2 → 2026.709.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.
Files changed (234) hide show
  1. package/dist/dev/main.d.ts +2 -0
  2. package/dist/dev/main.d.ts.map +1 -0
  3. package/dist/generated/global-operations.d.ts +2 -0
  4. package/dist/generated/global-operations.d.ts.map +1 -0
  5. package/dist/generated/global-types.d.ts +18199 -0
  6. package/dist/generated/global-types.d.ts.map +1 -0
  7. package/dist/generated/wspace-operations.d.ts +2 -0
  8. package/dist/generated/wspace-operations.d.ts.map +1 -0
  9. package/dist/generated/wspace-types.d.ts +43046 -0
  10. package/dist/generated/wspace-types.d.ts.map +1 -0
  11. package/dist/index.d.ts +9 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +8 -1
  14. package/dist/services/assistantApi.d.ts +15 -0
  15. package/dist/services/assistantApi.d.ts.map +1 -0
  16. package/dist/services/assistantApi.js +204 -0
  17. package/dist/services/assistantApi.js.map +1 -0
  18. package/dist/services/createSandboxAssistantTransport.d.ts +26 -0
  19. package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -0
  20. package/dist/services/createSandboxAssistantTransport.js +126 -0
  21. package/dist/services/createSandboxAssistantTransport.js.map +1 -0
  22. package/dist/theculturepost/TheCulturePostRoot.d.ts +5 -0
  23. package/dist/theculturepost/TheCulturePostRoot.d.ts.map +1 -0
  24. package/dist/theculturepost/TheCulturePostRoutes.d.ts +4 -0
  25. package/dist/theculturepost/TheCulturePostRoutes.d.ts.map +1 -0
  26. package/dist/theculturepost/TheCulturePostRoutes.js +73 -40
  27. package/dist/theculturepost/TheCulturePostRoutes.js.map +1 -1
  28. package/dist/theculturepost/components/AddCitationDialog.d.ts +11 -0
  29. package/dist/theculturepost/components/AddCitationDialog.d.ts.map +1 -0
  30. package/dist/theculturepost/components/ArchiveItemCard.d.ts +9 -0
  31. package/dist/theculturepost/components/ArchiveItemCard.d.ts.map +1 -0
  32. package/dist/theculturepost/components/AudioPlayer.d.ts +22 -0
  33. package/dist/theculturepost/components/AudioPlayer.d.ts.map +1 -0
  34. package/dist/theculturepost/components/AuthorLabel.d.ts +16 -0
  35. package/dist/theculturepost/components/AuthorLabel.d.ts.map +1 -0
  36. package/dist/theculturepost/components/AuthorLabel.js +1 -1
  37. package/dist/theculturepost/components/CitationList.d.ts +27 -0
  38. package/dist/theculturepost/components/CitationList.d.ts.map +1 -0
  39. package/dist/theculturepost/components/CollectionCard.d.ts +9 -0
  40. package/dist/theculturepost/components/CollectionCard.d.ts.map +1 -0
  41. package/dist/theculturepost/components/CoverImage.d.ts +13 -0
  42. package/dist/theculturepost/components/CoverImage.d.ts.map +1 -0
  43. package/dist/theculturepost/components/CreateArchiveItemDialog.d.ts +12 -0
  44. package/dist/theculturepost/components/CreateArchiveItemDialog.d.ts.map +1 -0
  45. package/dist/theculturepost/components/CreateCollectionDialog.d.ts +10 -0
  46. package/dist/theculturepost/components/CreateCollectionDialog.d.ts.map +1 -0
  47. package/dist/theculturepost/components/CreateStoryDialog.d.ts +12 -0
  48. package/dist/theculturepost/components/CreateStoryDialog.d.ts.map +1 -0
  49. package/dist/theculturepost/components/CreateTopicDialog.d.ts +10 -0
  50. package/dist/theculturepost/components/CreateTopicDialog.d.ts.map +1 -0
  51. package/dist/theculturepost/components/DecorativeCover.d.ts +17 -0
  52. package/dist/theculturepost/components/DecorativeCover.d.ts.map +1 -0
  53. package/dist/theculturepost/components/DecorativeDivider.d.ts +14 -0
  54. package/dist/theculturepost/components/DecorativeDivider.d.ts.map +1 -0
  55. package/dist/theculturepost/components/DiscussionCard.d.ts +9 -0
  56. package/dist/theculturepost/components/DiscussionCard.d.ts.map +1 -0
  57. package/dist/theculturepost/components/EmptyState.d.ts +18 -0
  58. package/dist/theculturepost/components/EmptyState.d.ts.map +1 -0
  59. package/dist/theculturepost/components/FileImage.d.ts +18 -0
  60. package/dist/theculturepost/components/FileImage.d.ts.map +1 -0
  61. package/dist/theculturepost/components/FileUploadField.d.ts +29 -0
  62. package/dist/theculturepost/components/FileUploadField.d.ts.map +1 -0
  63. package/dist/theculturepost/components/FilterTabs.d.ts +18 -0
  64. package/dist/theculturepost/components/FilterTabs.d.ts.map +1 -0
  65. package/dist/theculturepost/components/HistoryTimeline.d.ts +20 -0
  66. package/dist/theculturepost/components/HistoryTimeline.d.ts.map +1 -0
  67. package/dist/theculturepost/components/LifecycleStepper.d.ts +11 -0
  68. package/dist/theculturepost/components/LifecycleStepper.d.ts.map +1 -0
  69. package/dist/theculturepost/components/OpenDiscussionDialog.d.ts +12 -0
  70. package/dist/theculturepost/components/OpenDiscussionDialog.d.ts.map +1 -0
  71. package/dist/theculturepost/components/OralHistoryCard.d.ts +8 -0
  72. package/dist/theculturepost/components/OralHistoryCard.d.ts.map +1 -0
  73. package/dist/theculturepost/components/OverlayBadge.d.ts +15 -0
  74. package/dist/theculturepost/components/OverlayBadge.d.ts.map +1 -0
  75. package/dist/theculturepost/components/PageHero.d.ts +26 -0
  76. package/dist/theculturepost/components/PageHero.d.ts.map +1 -0
  77. package/dist/theculturepost/components/PageLayout.d.ts +16 -0
  78. package/dist/theculturepost/components/PageLayout.d.ts.map +1 -0
  79. package/dist/theculturepost/components/Pagination.d.ts +22 -0
  80. package/dist/theculturepost/components/Pagination.d.ts.map +1 -0
  81. package/dist/theculturepost/components/RecordOralHistoryDialog.d.ts +47 -0
  82. package/dist/theculturepost/components/RecordOralHistoryDialog.d.ts.map +1 -0
  83. package/dist/theculturepost/components/ReplyThread.d.ts +20 -0
  84. package/dist/theculturepost/components/ReplyThread.d.ts.map +1 -0
  85. package/dist/theculturepost/components/ReplyThread.js +1 -1
  86. package/dist/theculturepost/components/StateViews.d.ts +24 -0
  87. package/dist/theculturepost/components/StateViews.d.ts.map +1 -0
  88. package/dist/theculturepost/components/StatusBadge.d.ts +11 -0
  89. package/dist/theculturepost/components/StatusBadge.d.ts.map +1 -0
  90. package/dist/theculturepost/components/StoryCard.d.ts +11 -0
  91. package/dist/theculturepost/components/StoryCard.d.ts.map +1 -0
  92. package/dist/theculturepost/components/Tabs.d.ts +17 -0
  93. package/dist/theculturepost/components/Tabs.d.ts.map +1 -0
  94. package/dist/theculturepost/components/TagInput.d.ts +20 -0
  95. package/dist/theculturepost/components/TagInput.d.ts.map +1 -0
  96. package/dist/theculturepost/components/TopicCard.d.ts +9 -0
  97. package/dist/theculturepost/components/TopicCard.d.ts.map +1 -0
  98. package/dist/theculturepost/components/Waveform.d.ts +23 -0
  99. package/dist/theculturepost/components/Waveform.d.ts.map +1 -0
  100. package/dist/theculturepost/components/categoryMeta.d.ts +14 -0
  101. package/dist/theculturepost/components/categoryMeta.d.ts.map +1 -0
  102. package/dist/theculturepost/components/index.d.ts +43 -0
  103. package/dist/theculturepost/components/index.d.ts.map +1 -0
  104. package/dist/theculturepost/components/kindIcon.d.ts +7 -0
  105. package/dist/theculturepost/components/kindIcon.d.ts.map +1 -0
  106. package/dist/theculturepost/components/viz/BarChart.d.ts +23 -0
  107. package/dist/theculturepost/components/viz/BarChart.d.ts.map +1 -0
  108. package/dist/theculturepost/components/viz/Donut.d.ts +25 -0
  109. package/dist/theculturepost/components/viz/Donut.d.ts.map +1 -0
  110. package/dist/theculturepost/components/viz/StatTile.d.ts +14 -0
  111. package/dist/theculturepost/components/viz/StatTile.d.ts.map +1 -0
  112. package/dist/theculturepost/components/viz/index.d.ts +11 -0
  113. package/dist/theculturepost/components/viz/index.d.ts.map +1 -0
  114. package/dist/theculturepost/context/TheCulturePostContext.d.ts +16 -0
  115. package/dist/theculturepost/context/TheCulturePostContext.d.ts.map +1 -0
  116. package/dist/theculturepost/context/index.d.ts +6 -0
  117. package/dist/theculturepost/context/index.d.ts.map +1 -0
  118. package/dist/theculturepost/hooks/index.d.ts +10 -0
  119. package/dist/theculturepost/hooks/index.d.ts.map +1 -0
  120. package/dist/theculturepost/hooks/index.js +2 -0
  121. package/dist/theculturepost/hooks/useCursorPager.d.ts +32 -0
  122. package/dist/theculturepost/hooks/useCursorPager.d.ts.map +1 -0
  123. package/dist/theculturepost/hooks/useFiles.d.ts +31 -0
  124. package/dist/theculturepost/hooks/useFiles.d.ts.map +1 -0
  125. package/dist/theculturepost/hooks/usePageAnalytics.d.ts +9 -0
  126. package/dist/theculturepost/hooks/usePageAnalytics.d.ts.map +1 -0
  127. package/dist/theculturepost/hooks/usePageAnalytics.js +34 -0
  128. package/dist/theculturepost/hooks/usePageAnalytics.js.map +1 -0
  129. package/dist/theculturepost/hooks/useTcpApi.d.ts +74 -0
  130. package/dist/theculturepost/hooks/useTcpApi.d.ts.map +1 -0
  131. package/dist/theculturepost/hooks/useTr.d.ts +8 -0
  132. package/dist/theculturepost/hooks/useTr.d.ts.map +1 -0
  133. package/dist/theculturepost/hooks/useTr.js +14 -0
  134. package/dist/theculturepost/hooks/useTr.js.map +1 -0
  135. package/dist/theculturepost/index.d.ts +9 -0
  136. package/dist/theculturepost/index.d.ts.map +1 -0
  137. package/dist/theculturepost/index.js +2 -1
  138. package/dist/theculturepost/lib/graphql-cache.d.ts +26 -0
  139. package/dist/theculturepost/lib/graphql-cache.d.ts.map +1 -0
  140. package/dist/theculturepost/lib/toast.d.ts +23 -0
  141. package/dist/theculturepost/lib/toast.d.ts.map +1 -0
  142. package/dist/theculturepost/pages/HomePage.d.ts +16 -0
  143. package/dist/theculturepost/pages/HomePage.d.ts.map +1 -0
  144. package/dist/theculturepost/pages/HomePage.js +83 -68
  145. package/dist/theculturepost/pages/HomePage.js.map +1 -1
  146. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.d.ts +12 -0
  147. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.d.ts.map +1 -0
  148. package/dist/theculturepost/pages/archive/ArchiveItemDetailPage.js +21 -21
  149. package/dist/theculturepost/pages/archive/ArchivePage.d.ts +7 -0
  150. package/dist/theculturepost/pages/archive/ArchivePage.d.ts.map +1 -0
  151. package/dist/theculturepost/pages/archive/ArchivePage.js +144 -125
  152. package/dist/theculturepost/pages/archive/ArchivePage.js.map +1 -1
  153. package/dist/theculturepost/pages/archive/CollectionDetailPage.d.ts +12 -0
  154. package/dist/theculturepost/pages/archive/CollectionDetailPage.d.ts.map +1 -0
  155. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.d.ts +12 -0
  156. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.d.ts.map +1 -0
  157. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js +78 -70
  158. package/dist/theculturepost/pages/discussions/DiscussionDetailPage.js.map +1 -1
  159. package/dist/theculturepost/pages/discussions/DiscussionsPage.d.ts +10 -0
  160. package/dist/theculturepost/pages/discussions/DiscussionsPage.d.ts.map +1 -0
  161. package/dist/theculturepost/pages/discussions/DiscussionsPage.js +63 -56
  162. package/dist/theculturepost/pages/discussions/DiscussionsPage.js.map +1 -1
  163. package/dist/theculturepost/pages/governance/GovernancePage.d.ts +20 -0
  164. package/dist/theculturepost/pages/governance/GovernancePage.d.ts.map +1 -0
  165. package/dist/theculturepost/pages/stories/StoriesPage.d.ts +9 -0
  166. package/dist/theculturepost/pages/stories/StoriesPage.d.ts.map +1 -0
  167. package/dist/theculturepost/pages/stories/StoriesPage.js +65 -58
  168. package/dist/theculturepost/pages/stories/StoriesPage.js.map +1 -1
  169. package/dist/theculturepost/pages/stories/StoryDetailPage.d.ts +18 -0
  170. package/dist/theculturepost/pages/stories/StoryDetailPage.d.ts.map +1 -0
  171. package/dist/theculturepost/pages/stories/StoryDetailPage.js +249 -229
  172. package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
  173. package/dist/theculturepost/pages/timeline/TimelinePage.d.ts +17 -0
  174. package/dist/theculturepost/pages/timeline/TimelinePage.d.ts.map +1 -0
  175. package/dist/theculturepost/pages/topics/TopicDetailPage.d.ts +12 -0
  176. package/dist/theculturepost/pages/topics/TopicDetailPage.d.ts.map +1 -0
  177. package/dist/theculturepost/pages/topics/TopicsPage.d.ts +12 -0
  178. package/dist/theculturepost/pages/topics/TopicsPage.d.ts.map +1 -0
  179. package/dist/theculturepost/pages/topics/TopicsPage.js +61 -52
  180. package/dist/theculturepost/pages/topics/TopicsPage.js.map +1 -1
  181. package/dist/theculturepost/styles/injectDisplayFont.d.ts +14 -0
  182. package/dist/theculturepost/styles/injectDisplayFont.d.ts.map +1 -0
  183. package/dist/theculturepost/translations/ar.d.ts +50 -0
  184. package/dist/theculturepost/translations/ar.d.ts.map +1 -0
  185. package/dist/theculturepost/translations/ar.js +54 -0
  186. package/dist/theculturepost/translations/ar.js.map +1 -0
  187. package/dist/theculturepost/translations/en.d.ts +50 -0
  188. package/dist/theculturepost/translations/en.d.ts.map +1 -0
  189. package/dist/theculturepost/translations/en.js +54 -0
  190. package/dist/theculturepost/translations/en.js.map +1 -0
  191. package/dist/theculturepost/translations/es.d.ts +50 -0
  192. package/dist/theculturepost/translations/es.d.ts.map +1 -0
  193. package/dist/theculturepost/translations/es.js +54 -0
  194. package/dist/theculturepost/translations/es.js.map +1 -0
  195. package/dist/theculturepost/translations/hi.d.ts +50 -0
  196. package/dist/theculturepost/translations/hi.d.ts.map +1 -0
  197. package/dist/theculturepost/translations/hi.js +54 -0
  198. package/dist/theculturepost/translations/hi.js.map +1 -0
  199. package/dist/theculturepost/translations/index.d.ts +7 -0
  200. package/dist/theculturepost/translations/index.d.ts.map +1 -0
  201. package/dist/theculturepost/translations/ta.d.ts +50 -0
  202. package/dist/theculturepost/translations/ta.d.ts.map +1 -0
  203. package/dist/theculturepost/translations/ta.js +54 -0
  204. package/dist/theculturepost/translations/ta.js.map +1 -0
  205. package/dist/theculturepost/translations/zh.d.ts +50 -0
  206. package/dist/theculturepost/translations/zh.d.ts.map +1 -0
  207. package/dist/theculturepost/translations/zh.js +54 -0
  208. package/dist/theculturepost/translations/zh.js.map +1 -0
  209. package/dist/theculturepost/types/index.d.ts +333 -0
  210. package/dist/theculturepost/types/index.d.ts.map +1 -0
  211. package/dist/theculturepost/utils/cn.d.ts +4 -0
  212. package/dist/theculturepost/utils/cn.d.ts.map +1 -0
  213. package/dist/theculturepost/utils/formatters.d.ts +19 -0
  214. package/dist/theculturepost/utils/formatters.d.ts.map +1 -0
  215. package/dist/theculturepost/utils/i18n.d.ts +6 -0
  216. package/dist/theculturepost/utils/i18n.d.ts.map +1 -0
  217. package/dist/theculturepost/utils/index.d.ts +11 -0
  218. package/dist/theculturepost/utils/index.d.ts.map +1 -0
  219. package/dist/theculturepost/utils/index.js +6 -0
  220. package/dist/theculturepost/utils/nativeBridge.d.ts +47 -0
  221. package/dist/theculturepost/utils/nativeBridge.d.ts.map +1 -0
  222. package/dist/theculturepost/utils/nativeBridge.js +60 -0
  223. package/dist/theculturepost/utils/nativeBridge.js.map +1 -0
  224. package/dist/theculturepost/utils/navigation.d.ts +14 -0
  225. package/dist/theculturepost/utils/navigation.d.ts.map +1 -0
  226. package/dist/theculturepost/utils/slug.d.ts +6 -0
  227. package/dist/theculturepost/utils/slug.d.ts.map +1 -0
  228. package/dist/types/assistant.d.ts +56 -0
  229. package/dist/types/assistant.d.ts.map +1 -0
  230. package/dist/utils/pageContext.d.ts +27 -0
  231. package/dist/utils/pageContext.d.ts.map +1 -0
  232. package/dist/utils/pageContext.js +47 -0
  233. package/dist/utils/pageContext.js.map +1 -0
  234. package/package.json +29 -1
@@ -1 +1 @@
1
- {"version":3,"file":"StoryDetailPage.js","names":[],"sources":["../../../../src/theculturepost/pages/stories/StoryDetailPage.tsx"],"sourcesContent":["/**\n * StoryDetailPage — a single story in two complementary surfaces.\n * @module theculturepost/pages/stories\n *\n * A `?view=` toggle (with a sub-tab control) splits the page into:\n * - READING (tcp-03): a cover-banner hero, serif display title, a drop-cap\n * article body with ornamental section breaks, and a right rail of citations\n * (compact) + linked discussions.\n * - EDITORIAL (tcp-04): a visual lifecycle stepper with validated transitions\n * (Publish / Send back), plus citations as a verifiable-source table with an\n * inline verify control.\n *\n * Reads `tcpStory` (+ citations), `tcpListDiscussions(storyId)`. Writes\n * `tcpTransitionStory`, `tcpUpdateStory`, `tcpVerifyCitation`. Person references\n * are id-only, so we render \"You\" / \"Contributor\".\n */\n\nimport { Fragment, useMemo, useState, type ReactNode } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n BookOpen,\n ChevronDown,\n Clock,\n GitBranch,\n Languages,\n Lightbulb,\n Lock,\n MessagesSquare,\n Pencil,\n Plus,\n RotateCcw,\n Scroll,\n Send,\n ShieldCheck,\n Sparkles,\n Tag,\n Archive as ArchiveIcon,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveTable,\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { PageHero } from '../../components/PageHero';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { Tabs, type TabItem } from '../../components/Tabs';\nimport { LifecycleStepper } from '../../components/LifecycleStepper';\nimport { DecorativeDivider } from '../../components/DecorativeDivider';\nimport { AuthorLabel } from '../../components/AuthorLabel';\nimport {\n CitationList,\n sourceTypeIcon,\n VERIFICATION_META,\n VERIFICATIONS,\n} from '../../components/CitationList';\nimport { AddCitationDialog } from '../../components/AddCitationDialog';\nimport { OpenDiscussionDialog } from '../../components/OpenDiscussionDialog';\nimport { TagInput } from '../../components/TagInput';\nimport { FileUploadField } from '../../components/FileUploadField';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport {\n useTcpStory,\n useTcpTransitionStory,\n useTcpUpdateStory,\n useTcpTopics,\n useTcpDiscussions,\n useTcpCitations,\n useTcpVerifyCitation,\n FILE_FOLDERS,\n} from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { cn } from '../../utils/cn';\nimport {\n formatDate,\n formatEnumLabel,\n formatNumber,\n formatRelativeTime,\n} from '../../utils/formatters';\nimport { notify, describeError } from '../../lib/toast';\nimport type {\n TcpCitation,\n TcpDiscussion,\n TcpStory,\n TcpStoryFormat,\n TcpStoryStatus,\n TcpVerificationStatus,\n} from '../../types';\n\ntype StoryView = 'reading' | 'editorial';\n\nconst NEXT_STATUSES: Record<TcpStoryStatus, TcpStoryStatus[]> = {\n IDEA: ['DRAFT'],\n DRAFT: ['IN_REVIEW'],\n IN_REVIEW: ['PUBLISHED', 'DRAFT'],\n PUBLISHED: ['ARCHIVED'],\n ARCHIVED: ['PUBLISHED'],\n};\n\ninterface TransitionMetaEntry {\n label: string;\n icon: LucideIcon;\n primary?: boolean;\n}\n\nconst TRANSITION_META: Record<TcpStoryStatus, TransitionMetaEntry> = {\n IDEA: { label: 'Move to Idea', icon: Lightbulb },\n DRAFT: { label: 'Send back to Draft', icon: RotateCcw },\n IN_REVIEW: { label: 'Send to Review', icon: Send },\n PUBLISHED: { label: 'Publish', icon: Send, primary: true },\n ARCHIVED: { label: 'Archive', icon: ArchiveIcon },\n};\n\n/** Semantic-token colour for a verification icon (matches CitationList legend). */\nfunction verificationIconClass(status: TcpVerificationStatus): string {\n switch (VERIFICATION_META[status].accent) {\n case 'success':\n return 'text-status-success-text';\n case 'warning':\n return 'text-status-warning-text';\n case 'destructive':\n return 'text-status-error-text';\n default:\n return 'text-text-tertiary';\n }\n}\n\n// ===========================================================================\n// Page\n// ===========================================================================\n\nexport function StoryDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useTheCulturePost();\n const [searchParams, setSearchParams] = useSearchParams();\n\n const view: StoryView = searchParams.get('view') === 'editorial' ? 'editorial' : 'reading';\n const setView = (next: StoryView) => {\n setSearchParams(\n (prev) => {\n const params = new URLSearchParams(prev);\n if (next === 'editorial') params.set('view', 'editorial');\n else params.delete('view');\n return params;\n },\n { replace: true }\n );\n };\n\n const { data: story, isLoading, isError, refetch } = useTcpStory(id);\n\n const [citationOpen, setCitationOpen] = useState(false);\n const [discussionOpen, setDiscussionOpen] = useState(false);\n const [editOpen, setEditOpen] = useState(false);\n\n const discussionsQuery = useTcpDiscussions({ storyId: id, pagination: { limit: 20 } });\n const linkedDiscussions = discussionsQuery.data?.items ?? [];\n\n if (isLoading) {\n return (\n <div className=\"mx-auto max-w-6xl p-4 sm:p-6\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('stories')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Stories\n </Button>\n <LoadingView label=\"Loading story…\" />\n </div>\n );\n }\n\n if (isError || !story) {\n return (\n <div className=\"mx-auto max-w-6xl p-4 sm:p-6\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('stories')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Stories\n </Button>\n <ErrorView\n title=\"Story not found\"\n message=\"This story may have been archived, or you may not have access.\"\n onRetry={() => refetch()}\n />\n </div>\n );\n }\n\n const heroMeta = (\n <>\n <StatusBadge status={story.format} />\n <StatusBadge status={story.status} />\n {story.topic && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`topics/${story.topic!.id}`)}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Tag className=\"h-3 w-3\" aria-hidden=\"true\" /> {story.topic.name}\n </button>\n )}\n <span className=\"inline-flex items-center gap-1.5 text-xs text-text-tertiary\">\n <AuthorLabel actorId={story.authorId} prefix=\"By\" withIcon />\n </span>\n {story.readingMins > 0 && (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-tertiary\">\n <Clock className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {formatNumber(story.readingMins)} min\n read\n </span>\n )}\n {story.languages.length > 0 && (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-tertiary\">\n <Languages className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />{' '}\n {story.languages.map(formatEnumLabel).join(', ')}\n </span>\n )}\n </>\n );\n\n const tabs: TabItem<StoryView>[] = [\n { value: 'reading', label: 'Reading', icon: <BookOpen className=\"h-4 w-4\" /> },\n {\n value: 'editorial',\n label: 'Editorial',\n icon: <GitBranch className=\"h-4 w-4\" />,\n count: story.citations.length,\n },\n ];\n\n return (\n <div className=\"mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6\">\n <PageHero\n eyebrow=\"Story\"\n title={story.title}\n description={story.summary ?? undefined}\n coverFileId={story.cover?.id ?? undefined}\n backAction={{ label: 'Stories', onClick: () => navigateTo('stories') }}\n meta={heroMeta}\n actions={\n <Button variant=\"outline\" onClick={() => setEditOpen(true)}>\n <Pencil className=\"mr-1.5 h-4 w-4\" /> Edit\n </Button>\n }\n />\n\n <Tabs items={tabs} value={view} onChange={setView} />\n\n {view === 'reading' ? (\n <ReadingView\n story={story}\n discussions={linkedDiscussions}\n discussionsLoading={discussionsQuery.isLoading}\n onViewCitations={() => setView('editorial')}\n onOpenDiscussion={() => setDiscussionOpen(true)}\n onViewAllDiscussions={() => navigateTo('discussions')}\n onOpenDiscussionItem={(discussion) => navigateTo(`discussions/${discussion.id}`)}\n />\n ) : (\n <EditorialView story={story} onAddCitation={() => setCitationOpen(true)} />\n )}\n\n <AddCitationDialog open={citationOpen} onOpenChange={setCitationOpen} storyId={story.id} />\n <OpenDiscussionDialog\n open={discussionOpen}\n onOpenChange={setDiscussionOpen}\n storyId={story.id}\n onCreated={(discussion) => navigateTo(`discussions/${discussion.id}`)}\n />\n <EditStoryDialog open={editOpen} onOpenChange={setEditOpen} story={story} />\n </div>\n );\n}\n\n// ===========================================================================\n// Reading view (tcp-03)\n// ===========================================================================\n\ninterface ReadingViewProps {\n story: TcpStory;\n discussions: TcpDiscussion[];\n discussionsLoading: boolean;\n onViewCitations: () => void;\n onOpenDiscussion: () => void;\n onViewAllDiscussions: () => void;\n onOpenDiscussionItem: (discussion: TcpDiscussion) => void;\n}\n\nfunction ReadingView({\n story,\n discussions,\n discussionsLoading,\n onViewCitations,\n onOpenDiscussion,\n onViewAllDiscussions,\n onOpenDiscussionItem,\n}: ReadingViewProps) {\n return (\n <div className=\"grid grid-cols-1 gap-8 lg:grid-cols-[minmax(0,1fr)_360px]\">\n {/* Article */}\n <article className=\"min-w-0\">\n <StoryBody body={story.body} />\n\n {story.tags.length > 0 && (\n <div className=\"mt-8 flex flex-wrap gap-1.5 border-t border-border-seam pt-6\">\n {story.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n </article>\n\n {/* Right rail */}\n <aside className=\"flex flex-col gap-6 lg:sticky lg:top-4 lg:self-start\">\n <RailPanel\n title=\"Citations\"\n action={\n <button\n type=\"button\"\n onClick={onViewCitations}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n View all\n </button>\n }\n >\n <CitationList storyId={story.id} compact />\n </RailPanel>\n\n <RailPanel\n title=\"Linked discussions\"\n action={\n <button\n type=\"button\"\n onClick={onViewAllDiscussions}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n View all\n </button>\n }\n >\n {discussionsLoading ? (\n <LoadingView label=\"Loading discussions…\" className=\"min-h-[8rem]\" />\n ) : discussions.length === 0 ? (\n <EmptyState\n icon={<MessagesSquare className=\"h-8 w-8\" />}\n title=\"No discussions yet\"\n description=\"Open a discussion to invite perspectives on this story.\"\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={onOpenDiscussion}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Open discussion\n </Button>\n }\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {discussions.slice(0, 5).map((discussion) => (\n <LinkedDiscussion\n key={discussion.id}\n discussion={discussion}\n onOpen={() => onOpenDiscussionItem(discussion)}\n />\n ))}\n </div>\n )}\n </RailPanel>\n </aside>\n </div>\n );\n}\n\nfunction RailPanel({\n title,\n action,\n children,\n}: {\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}) {\n return (\n <section>\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h2 className=\"font-display text-lg font-semibold text-text-primary\">{title}</h2>\n {action}\n </div>\n {children}\n </section>\n );\n}\n\nfunction StoryBody({ body }: { body?: string | null }) {\n const paragraphs = useMemo(\n () =>\n (body ?? '')\n .split(/\\n{2,}/)\n .map((p) => p.trim())\n .filter(Boolean),\n [body]\n );\n\n if (paragraphs.length === 0) {\n return (\n <p className=\"rounded-xl border border-dashed border-border-seam px-4 py-10 text-center text-sm text-text-tertiary\">\n This story has no body yet. Use Edit to write it.\n </p>\n );\n }\n\n return (\n <div className=\"max-w-prose\">\n {paragraphs.map((paragraph, index) => (\n <Fragment key={index}>\n {index === 1 && <DecorativeDivider />}\n <p\n className={cn(\n 'text-[1.05rem] leading-8 text-text-primary',\n index > 0 && index !== 1 && 'mt-5',\n index === 0 &&\n 'first-letter:float-left first-letter:mr-3 first-letter:mt-1 first-letter:font-display first-letter:text-6xl first-letter:font-semibold first-letter:leading-[0.72] first-letter:text-accent'\n )}\n >\n {paragraph}\n </p>\n </Fragment>\n ))}\n </div>\n );\n}\n\nfunction LinkedDiscussion({\n discussion,\n onOpen,\n}: {\n discussion: TcpDiscussion;\n onOpen: () => void;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"w-full rounded-xl border border-border-seam bg-bg-surface p-3 text-left transition-colors hover:border-border-strong\"\n >\n <div className=\"flex items-start gap-2.5\">\n <span\n className=\"grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\"\n aria-hidden=\"true\"\n >\n <MessagesSquare className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{discussion.title}</p>\n {discussion.prompt && (\n <p className=\"mt-0.5 line-clamp-2 text-xs text-text-tertiary\">{discussion.prompt}</p>\n )}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-text-tertiary\">\n <span>\n {formatNumber(discussion.replyCount)}{' '}\n {discussion.replyCount === 1 ? 'reply' : 'replies'}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>{formatRelativeTime(discussion.createdAt)}</span>\n {discussion.isInterfaith && (\n <StatusBadge\n status=\"Interfaith\"\n icon={<Sparkles className=\"h-3 w-3\" />}\n className=\"ml-0.5\"\n />\n )}\n {discussion.isLocked && (\n <span className=\"inline-flex items-center gap-1 text-text-tertiary\">\n <Lock className=\"h-3 w-3\" aria-hidden=\"true\" /> Locked\n </span>\n )}\n </div>\n </div>\n </div>\n </button>\n );\n}\n\n// ===========================================================================\n// Editorial view (tcp-04)\n// ===========================================================================\n\nfunction EditorialView({ story, onAddCitation }: { story: TcpStory; onAddCitation: () => void }) {\n const transition = useTcpTransitionStory();\n const nextStatuses = NEXT_STATUSES[story.status] ?? [];\n\n const doTransition = async (status: TcpStoryStatus) => {\n try {\n await transition.mutateAsync({ id: story.id, status });\n notify.success('Story updated', `Moved to ${formatEnumLabel(status)}`);\n } catch (err) {\n notify.error('Could not update the story', describeError(err));\n }\n };\n\n return (\n <div className=\"flex flex-col gap-6\">\n {/* Lifecycle */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <div className=\"flex items-center gap-2.5\">\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 <GitBranch className=\"h-5 w-5\" />\n </span>\n <h2 className=\"font-display text-xl font-semibold text-text-primary\">\n Editorial lifecycle\n </h2>\n </div>\n\n <LifecycleStepper status={story.status} className=\"mt-6\" />\n\n <div className=\"mt-6 flex flex-col gap-4 border-t border-border-seam pt-5 md:flex-row md:items-center md:justify-between\">\n {nextStatuses.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {nextStatuses.map((status) => {\n const meta = TRANSITION_META[status];\n const Icon = meta.icon;\n return (\n <Button\n key={status}\n variant={meta.primary ? 'default' : 'outline'}\n onClick={() => doTransition(status)}\n disabled={transition.isPending}\n >\n <Icon className=\"mr-1.5 h-4 w-4\" aria-hidden=\"true\" />\n {meta.label}\n </Button>\n );\n })}\n </div>\n ) : (\n <p className=\"text-sm text-text-tertiary\">This story is at the end of its lifecycle.</p>\n )}\n\n <p className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <ShieldCheck className=\"h-4 w-4 shrink-0 text-text-tertiary\" aria-hidden=\"true\" />\n Reviewed with fact and sensitivity care before publishing.\n </p>\n </div>\n </GlassCard>\n\n {/* Citations */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2.5\">\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 <Scroll className=\"h-5 w-5\" />\n </span>\n <div>\n <h2 className=\"font-display text-xl font-semibold text-text-primary\">Citations</h2>\n <p className=\"text-sm text-text-secondary\">\n Verifiable sources that ground this story.\n </p>\n </div>\n </div>\n <Button variant=\"outline\" onClick={onAddCitation}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add citation\n </Button>\n </div>\n\n <EditorialCitationsTable storyId={story.id} className=\"mt-5\" />\n\n <p className=\"mt-4 flex items-center gap-2 border-t border-border-seam pt-4 text-xs text-text-tertiary\">\n <ShieldCheck className=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" />\n All sources are evaluated for accuracy, relevance, and cultural sensitivity.\n </p>\n </GlassCard>\n </div>\n );\n}\n\nfunction EditorialCitationsTable({ storyId, className }: { storyId: string; className?: string }) {\n const { data: citations, isLoading, isError, refetch } = useTcpCitations(storyId);\n\n const columns: ResponsiveTableColumn<TcpCitation>[] = [\n {\n key: 'source',\n header: 'Source',\n priority: 'primary',\n cell: (citation) => {\n const Icon = sourceTypeIcon(citation.sourceType);\n const meta = [citation.author, citation.publisher].filter((v) => v && v.trim()).join(' · ');\n return (\n <div className=\"flex min-w-0 items-start gap-3\">\n <span\n className=\"grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\"\n aria-hidden=\"true\"\n >\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary\">{citation.title}</p>\n {citation.excerpt && (\n <p className=\"mt-0.5 line-clamp-1 text-xs italic text-text-tertiary\">\n {citation.excerpt}\n </p>\n )}\n {meta && <p className=\"mt-0.5 text-xs text-text-tertiary\">{meta}</p>}\n </div>\n </div>\n );\n },\n },\n {\n key: 'type',\n header: 'Type',\n cell: (citation) => (\n <StatusBadge\n status={citation.sourceType}\n icon={(() => {\n const Icon = sourceTypeIcon(citation.sourceType);\n return <Icon className=\"h-3 w-3\" />;\n })()}\n />\n ),\n },\n {\n key: 'added',\n header: 'Added on',\n priority: 'hidden-on-mobile',\n cell: (citation) => (\n <span className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(citation.createdAt)}\n </span>\n ),\n },\n {\n key: 'verification',\n header: 'Status',\n cell: (citation) => {\n const meta = VERIFICATION_META[citation.verification];\n const Icon = meta.icon;\n return (\n <StatusBadge\n status={citation.verification}\n icon={<Icon className={cn('h-3 w-3', verificationIconClass(citation.verification))} />}\n />\n );\n },\n },\n {\n key: 'actions',\n header: 'Actions',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (citation) => <EditorialCitationActions citation={citation} />,\n },\n ];\n\n if (isLoading) return <LoadingView label=\"Loading citations…\" className={className} />;\n if (isError)\n return (\n <div className={className}>\n <ErrorView message=\"We couldn't load citations.\" onRetry={() => refetch()} />\n </div>\n );\n\n return (\n <ResponsiveTable<TcpCitation>\n className={className}\n columns={columns}\n data={citations ?? []}\n rowKey={(citation) => citation.id}\n emptyState={\n <EmptyState\n icon={<Scroll 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}\n\nfunction EditorialCitationActions({ citation }: { citation: TcpCitation }) {\n const verify = useTcpVerifyCitation();\n\n const change = 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 return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={verify.isPending}\n aria-label={`Change verification for ${citation.title}`}\n >\n Verify <ChevronDown className=\"ml-1 h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {VERIFICATIONS.map((status) => {\n const meta = VERIFICATION_META[status];\n const Icon = meta.icon;\n const current = status === citation.verification;\n return (\n <DropdownMenuItem\n key={status}\n onSelect={() => change(status)}\n className={cn('gap-2', current && 'font-semibold')}\n >\n <Icon className={cn('h-4 w-4', verificationIconClass(status))} aria-hidden=\"true\" />\n {meta.label}\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\n// ===========================================================================\n// Edit story dialog (local — only used here)\n// ===========================================================================\n\nconst FORMATS: TcpStoryFormat[] = [\n 'ARTICLE',\n 'ESSAY',\n 'EXPLAINER',\n 'ORAL_HISTORY',\n 'PHOTO_STORY',\n 'TIMELINE',\n];\n\nfunction EditStoryDialog({\n open,\n onOpenChange,\n story,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n story: TcpStory;\n}) {\n const updateStory = useTcpUpdateStory();\n const topicsQuery = useTcpTopics({ pagination: { limit: 100 } });\n\n const [title, setTitle] = useState(story.title);\n const [summary, setSummary] = useState(story.summary ?? '');\n const [body, setBody] = useState(story.body ?? '');\n const [format, setFormat] = useState<TcpStoryFormat>(story.format);\n const [pickedTopic, setPickedTopic] = useState(story.topicId ?? '');\n const [languages, setLanguages] = useState<string[]>(story.languages);\n const [tags, setTags] = useState<string[]>(story.tags);\n const [readingMins, setReadingMins] = useState(\n story.readingMins ? String(story.readingMins) : ''\n );\n const [coverFileId, setCoverFileId] = useState<string | null>(story.cover?.id ?? null);\n const [error, setError] = useState<string | null>(null);\n\n // Reset local state whenever a different story is opened for editing.\n const storyKey = useMemo(() => `${story.id}:${story.updatedAt}`, [story.id, story.updatedAt]);\n const [seededKey, setSeededKey] = useState(storyKey);\n if (seededKey !== storyKey) {\n setSeededKey(storyKey);\n setTitle(story.title);\n setSummary(story.summary ?? '');\n setBody(story.body ?? '');\n setFormat(story.format);\n setPickedTopic(story.topicId ?? '');\n setLanguages(story.languages);\n setTags(story.tags);\n setReadingMins(story.readingMins ? String(story.readingMins) : '');\n setCoverFileId(story.cover?.id ?? null);\n setError(null);\n }\n\n const submit = async () => {\n if (!title.trim()) {\n setError('A title is required.');\n return;\n }\n try {\n await updateStory.mutateAsync({\n id: story.id,\n input: {\n title: title.trim(),\n summary: summary.trim() || null,\n body: body.trim() || null,\n format,\n topicId: pickedTopic || null,\n languages,\n tags,\n readingMins: readingMins ? Number(readingMins) : null,\n coverFileId,\n },\n });\n notify.success('Story updated', title.trim());\n onOpenChange(false);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit story</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Refine the story. The URL slug is fixed once created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[62vh] flex-col gap-4 overflow-y-auto py-2\">\n <div className=\"grid gap-1.5\">\n <Label>Cover image</Label>\n <FileUploadField\n folder={FILE_FOLDERS.covers}\n accept=\"image/*\"\n label=\"Upload cover\"\n fileId={coverFileId}\n onUploaded={setCoverFileId}\n onCleared={() => setCoverFileId(null)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-title\">Title</Label>\n <Input id=\"edit-title\" value={title} onChange={(e) => setTitle(e.target.value)} />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label>Format</Label>\n <Select value={format} onValueChange={(v) => setFormat(v as TcpStoryFormat)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FORMATS.map((f) => (\n <SelectItem key={f} value={f}>\n {formatEnumLabel(f)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-reading\">Reading time (min)</Label>\n <Input\n id=\"edit-reading\"\n type=\"number\"\n min={0}\n value={readingMins}\n onChange={(e) => setReadingMins(e.target.value)}\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Topic</Label>\n <Select value={pickedTopic} onValueChange={setPickedTopic}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a topic (optional)\" />\n </SelectTrigger>\n <SelectContent>\n {(topicsQuery.data?.items ?? []).map((topic) => (\n <SelectItem key={topic.id} value={topic.id}>\n {topic.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-summary\">Summary</Label>\n <Textarea\n id=\"edit-summary\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-body\">Body</Label>\n <Textarea\n id=\"edit-body\"\n value={body}\n onChange={(e) => setBody(e.target.value)}\n rows={6}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-langs\">Languages</Label>\n <TagInput id=\"edit-langs\" value={languages} onChange={setLanguages} preserveCase />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-tags\">Tags</Label>\n <TagInput id=\"edit-tags\" value={tags} onChange={setTags} />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={updateStory.isPending}>\n Save changes\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"StoryDetailPage.js","names":[],"sources":["../../../../src/theculturepost/pages/stories/StoryDetailPage.tsx"],"sourcesContent":["/**\n * StoryDetailPage — a single story in two complementary surfaces.\n * @module theculturepost/pages/stories\n *\n * A `?view=` toggle (with a sub-tab control) splits the page into:\n * - READING (tcp-03): a cover-banner hero, serif display title, a drop-cap\n * article body with ornamental section breaks, and a right rail of citations\n * (compact) + linked discussions.\n * - EDITORIAL (tcp-04): a visual lifecycle stepper with validated transitions\n * (Publish / Send back), plus citations as a verifiable-source table with an\n * inline verify control.\n *\n * Reads `tcpStory` (+ citations), `tcpListDiscussions(storyId)`. Writes\n * `tcpTransitionStory`, `tcpUpdateStory`, `tcpVerifyCitation`. Person references\n * are id-only, so we render \"You\" / \"Contributor\".\n */\n\nimport { Fragment, useMemo, useState, type ReactNode } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n BookOpen,\n ChevronDown,\n Clock,\n GitBranch,\n Languages,\n Lightbulb,\n Lock,\n MessagesSquare,\n Pencil,\n Plus,\n RotateCcw,\n Scroll,\n Send,\n Share2,\n ShieldCheck,\n Sparkles,\n Tag,\n Archive as ArchiveIcon,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveTable,\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { PageHero } from '../../components/PageHero';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { Tabs, type TabItem } from '../../components/Tabs';\nimport { LifecycleStepper } from '../../components/LifecycleStepper';\nimport { DecorativeDivider } from '../../components/DecorativeDivider';\nimport { AuthorLabel } from '../../components/AuthorLabel';\nimport {\n CitationList,\n sourceTypeIcon,\n VERIFICATION_META,\n VERIFICATIONS,\n} from '../../components/CitationList';\nimport { AddCitationDialog } from '../../components/AddCitationDialog';\nimport { OpenDiscussionDialog } from '../../components/OpenDiscussionDialog';\nimport { TagInput } from '../../components/TagInput';\nimport { FileUploadField } from '../../components/FileUploadField';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport {\n useTcpStory,\n useTcpTransitionStory,\n useTcpUpdateStory,\n useTcpTopics,\n useTcpDiscussions,\n useTcpCitations,\n useTcpVerifyCitation,\n FILE_FOLDERS,\n} from '../../hooks';\nimport { useTheCulturePost } from '../../context';\nimport { cn } from '../../utils/cn';\nimport { share, impact, nativeToast } from '../../utils/nativeBridge';\nimport {\n formatDate,\n formatEnumLabel,\n formatNumber,\n formatRelativeTime,\n} from '../../utils/formatters';\nimport { notify, describeError } from '../../lib/toast';\nimport type {\n TcpCitation,\n TcpDiscussion,\n TcpStory,\n TcpStoryFormat,\n TcpStoryStatus,\n TcpVerificationStatus,\n} from '../../types';\n\ntype StoryView = 'reading' | 'editorial';\n\nconst NEXT_STATUSES: Record<TcpStoryStatus, TcpStoryStatus[]> = {\n IDEA: ['DRAFT'],\n DRAFT: ['IN_REVIEW'],\n IN_REVIEW: ['PUBLISHED', 'DRAFT'],\n PUBLISHED: ['ARCHIVED'],\n ARCHIVED: ['PUBLISHED'],\n};\n\ninterface TransitionMetaEntry {\n label: string;\n icon: LucideIcon;\n primary?: boolean;\n}\n\nconst TRANSITION_META: Record<TcpStoryStatus, TransitionMetaEntry> = {\n IDEA: { label: 'Move to Idea', icon: Lightbulb },\n DRAFT: { label: 'Send back to Draft', icon: RotateCcw },\n IN_REVIEW: { label: 'Send to Review', icon: Send },\n PUBLISHED: { label: 'Publish', icon: Send, primary: true },\n ARCHIVED: { label: 'Archive', icon: ArchiveIcon },\n};\n\n/** Semantic-token colour for a verification icon (matches CitationList legend). */\nfunction verificationIconClass(status: TcpVerificationStatus): string {\n switch (VERIFICATION_META[status].accent) {\n case 'success':\n return 'text-status-success-text';\n case 'warning':\n return 'text-status-warning-text';\n case 'destructive':\n return 'text-status-error-text';\n default:\n return 'text-text-tertiary';\n }\n}\n\n// ===========================================================================\n// Page\n// ===========================================================================\n\nexport function StoryDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useTheCulturePost();\n const [searchParams, setSearchParams] = useSearchParams();\n\n const view: StoryView = searchParams.get('view') === 'editorial' ? 'editorial' : 'reading';\n const setView = (next: StoryView) => {\n setSearchParams(\n (prev) => {\n const params = new URLSearchParams(prev);\n if (next === 'editorial') params.set('view', 'editorial');\n else params.delete('view');\n return params;\n },\n { replace: true }\n );\n };\n\n const { data: story, isLoading, isError, refetch } = useTcpStory(id);\n\n const [citationOpen, setCitationOpen] = useState(false);\n const [discussionOpen, setDiscussionOpen] = useState(false);\n const [editOpen, setEditOpen] = useState(false);\n\n const discussionsQuery = useTcpDiscussions({ storyId: id, pagination: { limit: 20 } });\n const linkedDiscussions = discussionsQuery.data?.items ?? [];\n\n if (isLoading) {\n return (\n <div className=\"mx-auto max-w-6xl p-4 sm:p-6\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('stories')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Stories\n </Button>\n <LoadingView label=\"Loading story…\" />\n </div>\n );\n }\n\n if (isError || !story) {\n return (\n <div className=\"mx-auto max-w-6xl p-4 sm:p-6\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('stories')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Stories\n </Button>\n <ErrorView\n title=\"Story not found\"\n message=\"This story may have been archived, or you may not have access.\"\n onRetry={() => refetch()}\n />\n </div>\n );\n }\n\n const heroMeta = (\n <>\n <StatusBadge status={story.format} />\n <StatusBadge status={story.status} />\n {story.topic && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`topics/${story.topic!.id}`)}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Tag className=\"h-3 w-3\" aria-hidden=\"true\" /> {story.topic.name}\n </button>\n )}\n <span className=\"inline-flex items-center gap-1.5 text-xs text-text-tertiary\">\n <AuthorLabel actorId={story.authorId} prefix=\"By\" withIcon />\n </span>\n {story.readingMins > 0 && (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-tertiary\">\n <Clock className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {formatNumber(story.readingMins)} min\n read\n </span>\n )}\n {story.languages.length > 0 && (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-tertiary\">\n <Languages className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />{' '}\n {story.languages.map(formatEnumLabel).join(', ')}\n </span>\n )}\n </>\n );\n\n const tabs: TabItem<StoryView>[] = [\n { value: 'reading', label: 'Reading', icon: <BookOpen className=\"h-4 w-4\" /> },\n {\n value: 'editorial',\n label: 'Editorial',\n icon: <GitBranch className=\"h-4 w-4\" />,\n count: story.citations.length,\n },\n ];\n\n return (\n <div className=\"mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6\">\n <PageHero\n eyebrow=\"Story\"\n title={story.title}\n description={story.summary ?? undefined}\n coverFileId={story.cover?.id ?? undefined}\n backAction={{ label: 'Stories', onClick: () => navigateTo('stories') }}\n meta={heroMeta}\n actions={\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={async () => {\n await impact('light');\n const ok = await share({\n title: story.title,\n text: story.summary ?? undefined,\n url: window.location.href,\n });\n if (ok) await nativeToast('Link shared');\n }}\n aria-label=\"Share story\"\n >\n <Share2 className=\"mr-1.5 h-4 w-4\" /> Share\n </Button>\n <Button\n variant=\"outline\"\n onClick={() => setEditOpen(true)}\n data-tour=\"theculturepost-story-edit\"\n >\n <Pencil className=\"mr-1.5 h-4 w-4\" /> Edit\n </Button>\n </div>\n }\n />\n\n <div data-tour=\"theculturepost-story-tabs\">\n <Tabs items={tabs} value={view} onChange={setView} />\n </div>\n\n {view === 'reading' ? (\n <ReadingView\n story={story}\n discussions={linkedDiscussions}\n discussionsLoading={discussionsQuery.isLoading}\n onViewCitations={() => setView('editorial')}\n onOpenDiscussion={() => setDiscussionOpen(true)}\n onViewAllDiscussions={() => navigateTo('discussions')}\n onOpenDiscussionItem={(discussion) => navigateTo(`discussions/${discussion.id}`)}\n />\n ) : (\n <EditorialView story={story} onAddCitation={() => setCitationOpen(true)} />\n )}\n\n <AddCitationDialog open={citationOpen} onOpenChange={setCitationOpen} storyId={story.id} />\n <OpenDiscussionDialog\n open={discussionOpen}\n onOpenChange={setDiscussionOpen}\n storyId={story.id}\n onCreated={(discussion) => navigateTo(`discussions/${discussion.id}`)}\n />\n <EditStoryDialog open={editOpen} onOpenChange={setEditOpen} story={story} />\n </div>\n );\n}\n\n// ===========================================================================\n// Reading view (tcp-03)\n// ===========================================================================\n\ninterface ReadingViewProps {\n story: TcpStory;\n discussions: TcpDiscussion[];\n discussionsLoading: boolean;\n onViewCitations: () => void;\n onOpenDiscussion: () => void;\n onViewAllDiscussions: () => void;\n onOpenDiscussionItem: (discussion: TcpDiscussion) => void;\n}\n\nfunction ReadingView({\n story,\n discussions,\n discussionsLoading,\n onViewCitations,\n onOpenDiscussion,\n onViewAllDiscussions,\n onOpenDiscussionItem,\n}: ReadingViewProps) {\n return (\n <div className=\"grid grid-cols-1 gap-8 lg:grid-cols-[minmax(0,1fr)_360px]\">\n {/* Article */}\n <article className=\"min-w-0\">\n <StoryBody body={story.body} />\n\n {story.tags.length > 0 && (\n <div className=\"mt-8 flex flex-wrap gap-1.5 border-t border-border-seam pt-6\">\n {story.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n </article>\n\n {/* Right rail */}\n <aside className=\"flex flex-col gap-6 lg:sticky lg:top-4 lg:self-start\">\n <RailPanel\n title=\"Citations\"\n action={\n <button\n type=\"button\"\n onClick={onViewCitations}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n View all\n </button>\n }\n >\n <CitationList storyId={story.id} compact />\n </RailPanel>\n\n <RailPanel\n title=\"Linked discussions\"\n action={\n <button\n type=\"button\"\n onClick={onViewAllDiscussions}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n View all\n </button>\n }\n >\n {discussionsLoading ? (\n <LoadingView label=\"Loading discussions…\" className=\"min-h-[8rem]\" />\n ) : discussions.length === 0 ? (\n <EmptyState\n icon={<MessagesSquare className=\"h-8 w-8\" />}\n title=\"No discussions yet\"\n description=\"Open a discussion to invite perspectives on this story.\"\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={onOpenDiscussion}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Open discussion\n </Button>\n }\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {discussions.slice(0, 5).map((discussion) => (\n <LinkedDiscussion\n key={discussion.id}\n discussion={discussion}\n onOpen={() => onOpenDiscussionItem(discussion)}\n />\n ))}\n </div>\n )}\n </RailPanel>\n </aside>\n </div>\n );\n}\n\nfunction RailPanel({\n title,\n action,\n children,\n}: {\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}) {\n return (\n <section>\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h2 className=\"font-display text-lg font-semibold text-text-primary\">{title}</h2>\n {action}\n </div>\n {children}\n </section>\n );\n}\n\nfunction StoryBody({ body }: { body?: string | null }) {\n const paragraphs = useMemo(\n () =>\n (body ?? '')\n .split(/\\n{2,}/)\n .map((p) => p.trim())\n .filter(Boolean),\n [body]\n );\n\n if (paragraphs.length === 0) {\n return (\n <p className=\"rounded-xl border border-dashed border-border-seam px-4 py-10 text-center text-sm text-text-tertiary\">\n This story has no body yet. Use Edit to write it.\n </p>\n );\n }\n\n return (\n <div className=\"max-w-prose\">\n {paragraphs.map((paragraph, index) => (\n <Fragment key={index}>\n {index === 1 && <DecorativeDivider />}\n <p\n className={cn(\n 'text-[1.05rem] leading-8 text-text-primary',\n index > 0 && index !== 1 && 'mt-5',\n index === 0 &&\n 'first-letter:float-left first-letter:mr-3 first-letter:mt-1 first-letter:font-display first-letter:text-6xl first-letter:font-semibold first-letter:leading-[0.72] first-letter:text-accent'\n )}\n >\n {paragraph}\n </p>\n </Fragment>\n ))}\n </div>\n );\n}\n\nfunction LinkedDiscussion({\n discussion,\n onOpen,\n}: {\n discussion: TcpDiscussion;\n onOpen: () => void;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"w-full rounded-xl border border-border-seam bg-bg-surface p-3 text-left transition-colors hover:border-border-strong\"\n >\n <div className=\"flex items-start gap-2.5\">\n <span\n className=\"grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\"\n aria-hidden=\"true\"\n >\n <MessagesSquare className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{discussion.title}</p>\n {discussion.prompt && (\n <p className=\"mt-0.5 line-clamp-2 text-xs text-text-tertiary\">{discussion.prompt}</p>\n )}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-text-tertiary\">\n <span>\n {formatNumber(discussion.replyCount)}{' '}\n {discussion.replyCount === 1 ? 'reply' : 'replies'}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>{formatRelativeTime(discussion.createdAt)}</span>\n {discussion.isInterfaith && (\n <StatusBadge\n status=\"Interfaith\"\n icon={<Sparkles className=\"h-3 w-3\" />}\n className=\"ml-0.5\"\n />\n )}\n {discussion.isLocked && (\n <span className=\"inline-flex items-center gap-1 text-text-tertiary\">\n <Lock className=\"h-3 w-3\" aria-hidden=\"true\" /> Locked\n </span>\n )}\n </div>\n </div>\n </div>\n </button>\n );\n}\n\n// ===========================================================================\n// Editorial view (tcp-04)\n// ===========================================================================\n\nfunction EditorialView({ story, onAddCitation }: { story: TcpStory; onAddCitation: () => void }) {\n const transition = useTcpTransitionStory();\n const nextStatuses = NEXT_STATUSES[story.status] ?? [];\n\n const doTransition = async (status: TcpStoryStatus) => {\n try {\n await transition.mutateAsync({ id: story.id, status });\n notify.success('Story updated', `Moved to ${formatEnumLabel(status)}`);\n } catch (err) {\n notify.error('Could not update the story', describeError(err));\n }\n };\n\n return (\n <div className=\"flex flex-col gap-6\">\n {/* Lifecycle */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <div className=\"flex items-center gap-2.5\">\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 <GitBranch className=\"h-5 w-5\" />\n </span>\n <h2 className=\"font-display text-xl font-semibold text-text-primary\">\n Editorial lifecycle\n </h2>\n </div>\n\n <LifecycleStepper status={story.status} className=\"mt-6\" />\n\n <div className=\"mt-6 flex flex-col gap-4 border-t border-border-seam pt-5 md:flex-row md:items-center md:justify-between\">\n {nextStatuses.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {nextStatuses.map((status) => {\n const meta = TRANSITION_META[status];\n const Icon = meta.icon;\n return (\n <Button\n key={status}\n variant={meta.primary ? 'default' : 'outline'}\n onClick={() => doTransition(status)}\n disabled={transition.isPending}\n >\n <Icon className=\"mr-1.5 h-4 w-4\" aria-hidden=\"true\" />\n {meta.label}\n </Button>\n );\n })}\n </div>\n ) : (\n <p className=\"text-sm text-text-tertiary\">This story is at the end of its lifecycle.</p>\n )}\n\n <p className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <ShieldCheck className=\"h-4 w-4 shrink-0 text-text-tertiary\" aria-hidden=\"true\" />\n Reviewed with fact and sensitivity care before publishing.\n </p>\n </div>\n </GlassCard>\n\n {/* Citations */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2.5\">\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 <Scroll className=\"h-5 w-5\" />\n </span>\n <div>\n <h2 className=\"font-display text-xl font-semibold text-text-primary\">Citations</h2>\n <p className=\"text-sm text-text-secondary\">\n Verifiable sources that ground this story.\n </p>\n </div>\n </div>\n <Button variant=\"outline\" onClick={onAddCitation}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add citation\n </Button>\n </div>\n\n <EditorialCitationsTable storyId={story.id} className=\"mt-5\" />\n\n <p className=\"mt-4 flex items-center gap-2 border-t border-border-seam pt-4 text-xs text-text-tertiary\">\n <ShieldCheck className=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" />\n All sources are evaluated for accuracy, relevance, and cultural sensitivity.\n </p>\n </GlassCard>\n </div>\n );\n}\n\nfunction EditorialCitationsTable({ storyId, className }: { storyId: string; className?: string }) {\n const { data: citations, isLoading, isError, refetch } = useTcpCitations(storyId);\n\n const columns: ResponsiveTableColumn<TcpCitation>[] = [\n {\n key: 'source',\n header: 'Source',\n priority: 'primary',\n cell: (citation) => {\n const Icon = sourceTypeIcon(citation.sourceType);\n const meta = [citation.author, citation.publisher].filter((v) => v && v.trim()).join(' · ');\n return (\n <div className=\"flex min-w-0 items-start gap-3\">\n <span\n className=\"grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\"\n aria-hidden=\"true\"\n >\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary\">{citation.title}</p>\n {citation.excerpt && (\n <p className=\"mt-0.5 line-clamp-1 text-xs italic text-text-tertiary\">\n {citation.excerpt}\n </p>\n )}\n {meta && <p className=\"mt-0.5 text-xs text-text-tertiary\">{meta}</p>}\n </div>\n </div>\n );\n },\n },\n {\n key: 'type',\n header: 'Type',\n cell: (citation) => (\n <StatusBadge\n status={citation.sourceType}\n icon={(() => {\n const Icon = sourceTypeIcon(citation.sourceType);\n return <Icon className=\"h-3 w-3\" />;\n })()}\n />\n ),\n },\n {\n key: 'added',\n header: 'Added on',\n priority: 'hidden-on-mobile',\n cell: (citation) => (\n <span className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(citation.createdAt)}\n </span>\n ),\n },\n {\n key: 'verification',\n header: 'Status',\n cell: (citation) => {\n const meta = VERIFICATION_META[citation.verification];\n const Icon = meta.icon;\n return (\n <StatusBadge\n status={citation.verification}\n icon={<Icon className={cn('h-3 w-3', verificationIconClass(citation.verification))} />}\n />\n );\n },\n },\n {\n key: 'actions',\n header: 'Actions',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (citation) => <EditorialCitationActions citation={citation} />,\n },\n ];\n\n if (isLoading) return <LoadingView label=\"Loading citations…\" className={className} />;\n if (isError)\n return (\n <div className={className}>\n <ErrorView message=\"We couldn't load citations.\" onRetry={() => refetch()} />\n </div>\n );\n\n return (\n <ResponsiveTable<TcpCitation>\n className={className}\n columns={columns}\n data={citations ?? []}\n rowKey={(citation) => citation.id}\n emptyState={\n <EmptyState\n icon={<Scroll 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}\n\nfunction EditorialCitationActions({ citation }: { citation: TcpCitation }) {\n const verify = useTcpVerifyCitation();\n\n const change = 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 return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={verify.isPending}\n aria-label={`Change verification for ${citation.title}`}\n >\n Verify <ChevronDown className=\"ml-1 h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {VERIFICATIONS.map((status) => {\n const meta = VERIFICATION_META[status];\n const Icon = meta.icon;\n const current = status === citation.verification;\n return (\n <DropdownMenuItem\n key={status}\n onSelect={() => change(status)}\n className={cn('gap-2', current && 'font-semibold')}\n >\n <Icon className={cn('h-4 w-4', verificationIconClass(status))} aria-hidden=\"true\" />\n {meta.label}\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\n// ===========================================================================\n// Edit story dialog (local — only used here)\n// ===========================================================================\n\nconst FORMATS: TcpStoryFormat[] = [\n 'ARTICLE',\n 'ESSAY',\n 'EXPLAINER',\n 'ORAL_HISTORY',\n 'PHOTO_STORY',\n 'TIMELINE',\n];\n\nfunction EditStoryDialog({\n open,\n onOpenChange,\n story,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n story: TcpStory;\n}) {\n const updateStory = useTcpUpdateStory();\n const topicsQuery = useTcpTopics({ pagination: { limit: 100 } });\n\n const [title, setTitle] = useState(story.title);\n const [summary, setSummary] = useState(story.summary ?? '');\n const [body, setBody] = useState(story.body ?? '');\n const [format, setFormat] = useState<TcpStoryFormat>(story.format);\n const [pickedTopic, setPickedTopic] = useState(story.topicId ?? '');\n const [languages, setLanguages] = useState<string[]>(story.languages);\n const [tags, setTags] = useState<string[]>(story.tags);\n const [readingMins, setReadingMins] = useState(\n story.readingMins ? String(story.readingMins) : ''\n );\n const [coverFileId, setCoverFileId] = useState<string | null>(story.cover?.id ?? null);\n const [error, setError] = useState<string | null>(null);\n\n // Reset local state whenever a different story is opened for editing.\n const storyKey = useMemo(() => `${story.id}:${story.updatedAt}`, [story.id, story.updatedAt]);\n const [seededKey, setSeededKey] = useState(storyKey);\n if (seededKey !== storyKey) {\n setSeededKey(storyKey);\n setTitle(story.title);\n setSummary(story.summary ?? '');\n setBody(story.body ?? '');\n setFormat(story.format);\n setPickedTopic(story.topicId ?? '');\n setLanguages(story.languages);\n setTags(story.tags);\n setReadingMins(story.readingMins ? String(story.readingMins) : '');\n setCoverFileId(story.cover?.id ?? null);\n setError(null);\n }\n\n const submit = async () => {\n if (!title.trim()) {\n setError('A title is required.');\n return;\n }\n try {\n await updateStory.mutateAsync({\n id: story.id,\n input: {\n title: title.trim(),\n summary: summary.trim() || null,\n body: body.trim() || null,\n format,\n topicId: pickedTopic || null,\n languages,\n tags,\n readingMins: readingMins ? Number(readingMins) : null,\n coverFileId,\n },\n });\n notify.success('Story updated', title.trim());\n onOpenChange(false);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit story</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Refine the story. The URL slug is fixed once created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[62vh] flex-col gap-4 overflow-y-auto py-2\">\n <div className=\"grid gap-1.5\">\n <Label>Cover image</Label>\n <FileUploadField\n folder={FILE_FOLDERS.covers}\n accept=\"image/*\"\n label=\"Upload cover\"\n fileId={coverFileId}\n onUploaded={setCoverFileId}\n onCleared={() => setCoverFileId(null)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-title\">Title</Label>\n <Input id=\"edit-title\" value={title} onChange={(e) => setTitle(e.target.value)} />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label>Format</Label>\n <Select value={format} onValueChange={(v) => setFormat(v as TcpStoryFormat)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FORMATS.map((f) => (\n <SelectItem key={f} value={f}>\n {formatEnumLabel(f)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-reading\">Reading time (min)</Label>\n <Input\n id=\"edit-reading\"\n type=\"number\"\n min={0}\n value={readingMins}\n onChange={(e) => setReadingMins(e.target.value)}\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Topic</Label>\n <Select value={pickedTopic} onValueChange={setPickedTopic}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a topic (optional)\" />\n </SelectTrigger>\n <SelectContent>\n {(topicsQuery.data?.items ?? []).map((topic) => (\n <SelectItem key={topic.id} value={topic.id}>\n {topic.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-summary\">Summary</Label>\n <Textarea\n id=\"edit-summary\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-body\">Body</Label>\n <Textarea\n id=\"edit-body\"\n value={body}\n onChange={(e) => setBody(e.target.value)}\n rows={6}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-langs\">Languages</Label>\n <TagInput id=\"edit-langs\" value={languages} onChange={setLanguages} preserveCase />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"edit-tags\">Tags</Label>\n <TagInput id=\"edit-tags\" value={tags} onChange={setTags} />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={updateStory.isPending}>\n Save changes\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * TimelinePage — a data-honest chronology of dated archive material.
3
+ * @module theculturepost/pages/timeline
4
+ *
5
+ * The schema has no dedicated "historical event" entity, so this timeline is
6
+ * built strictly from REAL archive items that carry a recorded `datedAt`
7
+ * (`tcpListArchiveItems`). Items without a date are excluded; the rest are
8
+ * ordered chronologically by that date and rendered on the foundation
9
+ * {@link HistoryTimeline}. Era labels are derived from the item's own year (a
10
+ * documented bucketing of real data), never invented. Each entry links to the
11
+ * archive-item detail.
12
+ *
13
+ * NOTE: this is a NEW page — it exports {@link TimelinePage} only. Route/nav
14
+ * registration is owned by the integration step (do not wire it here).
15
+ */
16
+ export declare function TimelinePage(): import("react/jsx-runtime").JSX.Element;
17
+ //# sourceMappingURL=TimelinePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimelinePage.d.ts","sourceRoot":"","sources":["../../../../src/theculturepost/pages/timeline/TimelinePage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiFH,wBAAgB,YAAY,4CAyI3B"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * TopicDetailPage — a single topic and the stories curated under it.
3
+ * @module theculturepost/pages/topics
4
+ *
5
+ * An editorial {@link PageHero} banner carries the title, description and the
6
+ * Featured / category / tradition badges, plus the feature-toggle, edit and
7
+ * archive actions (`tcpUpdateTopic` / `tcpArchiveTopic`). Below, the topic's own
8
+ * `stories` are shown in a richer {@link StoryCard} grid with a real count and a
9
+ * client-side sort. Reads `tcpTopic` (with its stories) — no fabricated fields.
10
+ */
11
+ export declare function TopicDetailPage(): import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=TopicDetailPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/theculturepost/pages/topics/TopicDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA+FH,wBAAgB,eAAe,4CAkM9B"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * TopicsPage — the topics hub: browse curated subjects.
3
+ * @module theculturepost/pages/topics
4
+ *
5
+ * An editorial {@link PageHero} header, a category pill-filter bar (every
6
+ * {@link TcpTopicCategory}, plus "All"), and the image-forward {@link TopicCard}
7
+ * grid. Reads `tcpListTopics` (optionally filtered by category), cursor-paginated.
8
+ * The schema exposes no per-topic imagery or list story counts, so cards use the
9
+ * token-only DecorativeCover — never a fabricated photo or count.
10
+ */
11
+ export declare function TopicsPage(): import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=TopicsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicsPage.d.ts","sourceRoot":"","sources":["../../../../src/theculturepost/pages/topics/TopicsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA8CH,wBAAgB,UAAU,4CA4FzB"}
@@ -7,17 +7,18 @@ import { TopicCard as a } from "../../components/TopicCard.js";
7
7
  import { EmptyState as o } from "../../components/EmptyState.js";
8
8
  import { useTcpTopics as s } from "../../hooks/useTcpApi.js";
9
9
  import { useCursorPager as c } from "../../hooks/useCursorPager.js";
10
+ import { useTr as l } from "../../hooks/useTr.js";
10
11
  import "../../hooks/index.js";
11
- import { Pagination as l } from "../../components/Pagination.js";
12
- import { FilterTabs as u } from "../../components/FilterTabs.js";
13
- import { CreateTopicDialog as d } from "../../components/CreateTopicDialog.js";
12
+ import { Pagination as u } from "../../components/Pagination.js";
13
+ import { FilterTabs as d } from "../../components/FilterTabs.js";
14
+ import { CreateTopicDialog as f } from "../../components/CreateTopicDialog.js";
14
15
  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";
16
+ import { useState as p } from "react";
17
+ import { Compass as m, Plus as h } from "lucide-react";
18
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
19
+ import { Button as v } from "@burdenoff/fe-libs/ui";
19
20
  //#region src/theculturepost/pages/topics/TopicsPage.tsx
20
- var v = 12, y = [
21
+ var y = 12, b = [
21
22
  "CULTURE_AND_IDENTITY",
22
23
  "RELIGION_AND_FAITH",
23
24
  "HISTORY",
@@ -27,77 +28,85 @@ var v = 12, y = [
27
28
  "ORAL_HISTORIES",
28
29
  "PHILOSOPHY_AND_THOUGHT",
29
30
  "VALUES_AND_ETHICS"
30
- ], b = (e) => i(e).replace(/ And /g, " & "), x = [{
31
+ ], x = (e) => i(e).replace(/ And /g, " & "), S = [{
31
32
  value: "ALL",
32
33
  label: "All topics"
33
- }, ...y.map((e) => ({
34
+ }, ...b.map((e) => ({
34
35
  value: e,
35
- label: b(e)
36
+ label: x(e)
36
37
  }))];
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,
38
+ function C() {
39
+ let { navigateTo: i } = e(), b = l(), x = c(), [C, w] = p("ALL"), [T, E] = p(!1), { data: D, isLoading: O, isError: k, refetch: A } = s({
40
+ category: C === "ALL" ? void 0 : C,
40
41
  pagination: {
41
- limit: v,
42
- cursor: y.cursor
42
+ limit: y,
43
+ cursor: x.cursor
43
44
  }
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"]
45
+ }), j = D?.items ?? [], M = (e) => {
46
+ w(e), x.reset();
47
+ }, N = /* @__PURE__ */ _(v, {
48
+ onClick: () => E(!0),
49
+ "data-tour": "theculturepost-topics-create",
50
+ children: [
51
+ /* @__PURE__ */ g(h, { className: "mr-1.5 h-4 w-4" }),
52
+ " ",
53
+ b("topics.newTopic", "New topic")
54
+ ]
49
55
  });
50
- return /* @__PURE__ */ g("div", {
56
+ return /* @__PURE__ */ _("div", {
51
57
  className: "h-full overflow-y-auto",
52
- children: [/* @__PURE__ */ g("div", {
58
+ children: [/* @__PURE__ */ _("div", {
53
59
  className: "mx-auto flex max-w-6xl flex-col gap-6 p-4 sm:p-6",
54
60
  children: [
55
- /* @__PURE__ */ h(t, {
61
+ /* @__PURE__ */ g(t, {
56
62
  eyebrow: "Explore",
57
- title: "Topics",
58
- description: "Curated subjects spanning culture, faith, history, heritage and thought.",
59
- actions: j
63
+ title: b("topics.title", "Topics"),
64
+ description: b("topics.description", "Curated subjects spanning culture, faith, history, heritage and thought."),
65
+ actions: N
60
66
  }),
61
- /* @__PURE__ */ h(u, {
62
- options: x,
63
- value: b,
64
- onChange: A,
65
- className: "max-w-full"
67
+ /* @__PURE__ */ g("div", {
68
+ "data-tour": "theculturepost-topics-filters",
69
+ children: /* @__PURE__ */ g(d, {
70
+ options: S,
71
+ value: C,
72
+ onChange: M,
73
+ className: "max-w-full"
74
+ })
66
75
  }),
67
- E ? /* @__PURE__ */ h(r, { label: "Loading topics…" }) : D ? /* @__PURE__ */ h(n, {
76
+ O ? /* @__PURE__ */ g(r, { label: "Loading topics…" }) : k ? /* @__PURE__ */ g(n, {
68
77
  message: "We couldn't load topics.",
69
- onRetry: () => O()
70
- }) : k.length === 0 && y.page === 0 ? /* @__PURE__ */ h(o, {
78
+ onRetry: () => A()
79
+ }) : j.length === 0 && x.page === 0 ? /* @__PURE__ */ g(o, {
71
80
  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", {
81
+ icon: /* @__PURE__ */ g(m, { className: "h-10 w-10" }),
82
+ title: C === "ALL" ? "No topics yet" : "No topics in this category",
83
+ description: C === "ALL" ? "Curate the first topic to organise stories around a shared subject." : "Try another category, or curate a new topic.",
84
+ action: N
85
+ }) : /* @__PURE__ */ _("div", {
77
86
  className: "flex flex-col gap-6",
78
- children: [/* @__PURE__ */ h("div", {
87
+ children: [/* @__PURE__ */ g("div", {
79
88
  className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3",
80
- children: k.map((e) => /* @__PURE__ */ h(a, {
89
+ children: j.map((e) => /* @__PURE__ */ g(a, {
81
90
  topic: e,
82
91
  onOpen: () => i(`topics/${e.id}`)
83
92
  }, 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)
93
+ }), /* @__PURE__ */ g(u, {
94
+ page: x.page,
95
+ pageSize: y,
96
+ itemCount: j.length,
97
+ hasMore: D?.pageInfo.hasMore ?? !1,
98
+ onPageChange: (e) => x.onPageChange(e, D?.pageInfo.cursor)
90
99
  })]
91
100
  })
92
101
  ]
93
- }), /* @__PURE__ */ h(d, {
94
- open: C,
95
- onOpenChange: w,
102
+ }), /* @__PURE__ */ g(f, {
103
+ open: T,
104
+ onOpenChange: E,
96
105
  onCreated: (e) => i(`topics/${e.id}`)
97
106
  })]
98
107
  });
99
108
  }
100
109
  //#endregion
101
- export { S as TopicsPage };
110
+ export { C as TopicsPage };
102
111
 
103
112
  //# sourceMappingURL=TopicsPage.js.map
@@ -1 +1 @@
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"}
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, useTr } 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 tr = useTr();\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)} data-tour=\"theculturepost-topics-create\">\n <Plus className=\"mr-1.5 h-4 w-4\" /> {tr('topics.newTopic', '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={tr('topics.title', 'Topics')}\n description={tr(\n 'topics.description',\n 'Curated subjects spanning culture, faith, history, heritage and thought.'\n )}\n actions={newTopicButton}\n />\n\n <div data-tour=\"theculturepost-topics-filters\">\n <FilterTabs\n options={CATEGORY_FILTERS}\n value={category}\n onChange={changeCategory}\n className=\"max-w-full\"\n />\n </div>\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,IAAK,GAAO,EACZ,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;EAAE,aAAU;YAAtD;GACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA;;GAAE,EAAG,mBAAmB,YAAY;GAChE;;AAGX,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,SAAQ;KACR,OAAO,EAAG,gBAAgB,SAAS;KACnC,aAAa,EACX,sBACA,2EACD;KACD,SAAS;KACT,CAAA;IAEF,kBAAC,OAAD;KAAK,aAAU;eACb,kBAAC,GAAD;MACE,SAAS;MACT,OAAO;MACP,UAAU;MACV,WAAU;MACV,CAAA;KACE,CAAA;IAEL,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,14 @@
1
+ /**
2
+ * injectDisplayFont — guarantee the `.font-display` utility exists in the built
3
+ * MFE, regardless of the host shell's Tailwind content globs or CSS pipeline.
4
+ * @module theculturepost/styles
5
+ *
6
+ * MFEs ship no component CSS to the shell (the shell owns Tailwind + tokens), and
7
+ * `.font-display` is a custom class Tailwind will not emit. Vite's lib build also
8
+ * strips plain CSS imports out of the JS. So instead of relying on a bundled
9
+ * stylesheet, we inject a single, idempotent <style> rule at runtime the first
10
+ * time the MFE mounts. It sets ONLY the font family (colour stays token-driven on
11
+ * each element). Import for side-effect from the MFE root.
12
+ */
13
+ export declare function injectDisplayFont(): void;
14
+ //# sourceMappingURL=injectDisplayFont.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injectDisplayFont.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/styles/injectDisplayFont.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,wBAAgB,iBAAiB,IAAI,IAAI,CAOxC"}
@@ -0,0 +1,50 @@
1
+ export declare const arTranslations: {
2
+ nav: {
3
+ home: string;
4
+ overview: string;
5
+ stories: string;
6
+ topics: string;
7
+ timeline: string;
8
+ discussions: string;
9
+ archive: string;
10
+ };
11
+ stories: {
12
+ title: string;
13
+ description: string;
14
+ newStory: string;
15
+ noStories: string;
16
+ filterStatus: string;
17
+ filterTopic: string;
18
+ };
19
+ topics: {
20
+ title: string;
21
+ description: string;
22
+ newTopic: string;
23
+ noTopics: string;
24
+ };
25
+ discussions: {
26
+ title: string;
27
+ description: string;
28
+ openDiscussion: string;
29
+ noDiscussions: string;
30
+ };
31
+ archive: {
32
+ title: string;
33
+ description: string;
34
+ newCollection: string;
35
+ newArtifact: string;
36
+ newRecording: string;
37
+ noCollections: string;
38
+ };
39
+ common: {
40
+ loading: string;
41
+ error: string;
42
+ retry: string;
43
+ save: string;
44
+ cancel: string;
45
+ edit: string;
46
+ share: string;
47
+ back: string;
48
+ };
49
+ };
50
+ //# sourceMappingURL=ar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ar.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/translations/ar.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiD1B,CAAC"}