@burdenoff/microfe-theculturepost 2026.802.1 → 2026.830.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/theculturepost/components/CitationList.d.ts +2 -1
- package/dist/theculturepost/components/CitationList.d.ts.map +1 -1
- package/dist/theculturepost/components/CitationList.js +114 -95
- package/dist/theculturepost/components/CitationList.js.map +1 -1
- package/dist/theculturepost/components/SourceScanLink.d.ts +25 -0
- package/dist/theculturepost/components/SourceScanLink.d.ts.map +1 -0
- package/dist/theculturepost/components/SourceScanLink.js +36 -0
- package/dist/theculturepost/components/SourceScanLink.js.map +1 -0
- package/dist/theculturepost/components/index.d.ts +1 -0
- package/dist/theculturepost/components/index.d.ts.map +1 -1
- package/dist/theculturepost/components/index.js +1 -0
- package/dist/theculturepost/hooks/useTr.js +2 -2
- package/dist/theculturepost/hooks/useTr.js.map +1 -1
- package/dist/theculturepost/pages/HomePage.d.ts.map +1 -1
- package/dist/theculturepost/pages/HomePage.js +145 -127
- package/dist/theculturepost/pages/HomePage.js.map +1 -1
- package/dist/theculturepost/pages/governance/GovernancePage.d.ts +3 -1
- package/dist/theculturepost/pages/governance/GovernancePage.d.ts.map +1 -1
- package/dist/theculturepost/pages/governance/GovernancePage.js +122 -99
- package/dist/theculturepost/pages/governance/GovernancePage.js.map +1 -1
- package/dist/theculturepost/pages/stories/StoryDetailPage.d.ts +3 -0
- package/dist/theculturepost/pages/stories/StoryDetailPage.d.ts.map +1 -1
- package/dist/theculturepost/pages/stories/StoryDetailPage.js +202 -132
- package/dist/theculturepost/pages/stories/StoryDetailPage.js.map +1 -1
- package/dist/theculturepost/translations/ar.d.ts +43 -0
- package/dist/theculturepost/translations/ar.d.ts.map +1 -1
- package/dist/theculturepost/translations/ar.js +43 -0
- package/dist/theculturepost/translations/ar.js.map +1 -1
- package/dist/theculturepost/translations/en.d.ts +43 -0
- package/dist/theculturepost/translations/en.d.ts.map +1 -1
- package/dist/theculturepost/translations/en.js +43 -0
- package/dist/theculturepost/translations/en.js.map +1 -1
- package/dist/theculturepost/translations/es.d.ts +43 -0
- package/dist/theculturepost/translations/es.d.ts.map +1 -1
- package/dist/theculturepost/translations/es.js +43 -0
- package/dist/theculturepost/translations/es.js.map +1 -1
- package/dist/theculturepost/translations/hi.d.ts +43 -0
- package/dist/theculturepost/translations/hi.d.ts.map +1 -1
- package/dist/theculturepost/translations/hi.js +43 -0
- package/dist/theculturepost/translations/hi.js.map +1 -1
- package/dist/theculturepost/translations/ta.d.ts +43 -0
- package/dist/theculturepost/translations/ta.d.ts.map +1 -1
- package/dist/theculturepost/translations/ta.js +43 -0
- package/dist/theculturepost/translations/ta.js.map +1 -1
- package/dist/theculturepost/translations/zh.d.ts +43 -0
- package/dist/theculturepost/translations/zh.d.ts.map +1 -1
- package/dist/theculturepost/translations/zh.js +43 -0
- package/dist/theculturepost/translations/zh.js.map +1 -1
- package/package.json +2 -2
|
@@ -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';\nimport {\n ArrowLeft,\n Bookmark,\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 useTcpToggleStoryBookmark,\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 const toggleBookmark = useTcpToggleStoryBookmark();\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={story.isBookmarked ? 'default' : 'outline'}\n size=\"sm\"\n onClick={async () => {\n await impact('light');\n try {\n const bookmarked = await toggleBookmark.mutateAsync(story.id);\n await nativeToast(\n bookmarked ? 'Saved to your reading list' : 'Removed from your reading list'\n );\n } catch (error) {\n notify.error(describeError(error));\n }\n }}\n disabled={toggleBookmark.isPending}\n aria-pressed={story.isBookmarked}\n aria-label={story.isBookmarked ? 'Remove from reading list' : 'Save to reading list'}\n data-tour=\"theculturepost-story-bookmark\"\n >\n <Bookmark className={cn('mr-1.5 h-4 w-4', story.isBookmarked && 'fill-current')} />{' '}\n {story.isBookmarked ? 'Saved' : 'Save'}\n </Button>\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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"}
|
|
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 * - CITATIONS (tcp-05): the story's full evidence base — roomy source cards\n * carrying excerpt, provenance, source link and scan, a labelled\n * verification control per source, and the methodology legend.\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';\nimport {\n ArrowLeft,\n Bookmark,\n BookOpen,\n ChevronDown,\n Clock,\n ExternalLink,\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 { SourceScanLink } from '../../components/SourceScanLink';\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 useTcpToggleStoryBookmark,\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' | 'citations' | 'editorial';\n\n/** Views that are addressable via `?view=` (reading is the bare-URL default). */\nconst DEEP_LINKED_VIEWS: readonly StoryView[] = ['citations', '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 requestedView = searchParams.get('view');\n const view: StoryView = DEEP_LINKED_VIEWS.find((v) => v === requestedView) ?? 'reading';\n const setView = (next: StoryView) => {\n setSearchParams(\n (prev) => {\n const params = new URLSearchParams(prev);\n if (next === 'reading') params.delete('view');\n else params.set('view', next);\n return params;\n },\n { replace: true }\n );\n };\n\n const { data: story, isLoading, isError, refetch } = useTcpStory(id);\n const toggleBookmark = useTcpToggleStoryBookmark();\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: 'citations',\n label: 'Citations',\n icon: <Scroll className=\"h-4 w-4\" />,\n count: story.citations.length,\n },\n { value: 'editorial', label: 'Editorial', icon: <GitBranch className=\"h-4 w-4\" /> },\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={story.isBookmarked ? 'default' : 'outline'}\n size=\"sm\"\n onClick={async () => {\n await impact('light');\n try {\n const bookmarked = await toggleBookmark.mutateAsync(story.id);\n await nativeToast(\n bookmarked ? 'Saved to your reading list' : 'Removed from your reading list'\n );\n } catch (error) {\n notify.error(describeError(error));\n }\n }}\n disabled={toggleBookmark.isPending}\n aria-pressed={story.isBookmarked}\n aria-label={story.isBookmarked ? 'Remove from reading list' : 'Save to reading list'}\n data-tour=\"theculturepost-story-bookmark\"\n >\n <Bookmark className={cn('mr-1.5 h-4 w-4', story.isBookmarked && 'fill-current')} />{' '}\n {story.isBookmarked ? 'Saved' : 'Save'}\n </Button>\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('citations')}\n onOpenDiscussion={() => setDiscussionOpen(true)}\n onViewAllDiscussions={() => navigateTo('discussions')}\n onOpenDiscussionItem={(discussion) => navigateTo(`discussions/${discussion.id}`)}\n />\n ) : view === 'citations' ? (\n <CitationsView story={story} onAddCitation={() => setCitationOpen(true)} />\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// Citations view (tcp-05)\n// ===========================================================================\n\n/**\n * The story's full evidence base. Renders the roomy {@link CitationList} — the\n * variant carrying each source's excerpt, provenance, link, scan and a labelled\n * verification control, closing with the methodology legend. The editorial\n * table only summarises those sources; this is where they are read and judged.\n *\n * There is deliberately NO \"evidence trail\" column mapping each source to the\n * specific claim it supports: `TcpCitation` carries no claim/anchor field, so\n * that linkage does not exist to render.\n */\nfunction CitationsView({ story, onAddCitation }: { story: TcpStory; onAddCitation: () => void }) {\n return (\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex items-start 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\">\n Verifiable sources that ground this story\n </h2>\n <p className=\"mt-0.5 max-w-prose text-sm text-text-secondary\">\n Every source is reviewed for credibility and context. Set each one’s\n verification state as the evidence is checked.\n </p>\n </div>\n </div>\n <Button onClick={onAddCitation}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add citation\n </Button>\n </div>\n\n <CitationList storyId={story.id} className=\"mt-5\" />\n </GlassCard>\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 published = formatDate(citation.publishedOn);\n const meta = [citation.author, citation.publisher, published !== '—' ? published : null]\n .filter((v) => v && v.trim())\n .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 {citation.provenance && (\n <p\n className=\"mt-0.5 line-clamp-1 text-xs text-text-tertiary\"\n title={citation.provenance}\n >\n <span className=\"font-medium text-text-secondary\">Provenance:</span>{' '}\n {citation.provenance}\n </p>\n )}\n {(citation.url || citation.sourceFile) && (\n <div className=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1\">\n {citation.url && (\n <a\n href={citation.url}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline\"\n >\n Open source\n <ExternalLink className=\"h-3 w-3 shrink-0\" aria-hidden=\"true\" />\n </a>\n )}\n <SourceScanLink fileId={citation.sourceFile?.id} label=\"View scan\" />\n </div>\n )}\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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"}
|
|
@@ -8,6 +8,48 @@ export declare const arTranslations: {
|
|
|
8
8
|
discussions: string;
|
|
9
9
|
archive: string;
|
|
10
10
|
};
|
|
11
|
+
home: {
|
|
12
|
+
hero: {
|
|
13
|
+
eyebrow: string;
|
|
14
|
+
title: string;
|
|
15
|
+
description: string;
|
|
16
|
+
readStories: string;
|
|
17
|
+
};
|
|
18
|
+
loading: string;
|
|
19
|
+
welcome: {
|
|
20
|
+
title: string;
|
|
21
|
+
description: string;
|
|
22
|
+
draftStory: string;
|
|
23
|
+
curateTopic: string;
|
|
24
|
+
};
|
|
25
|
+
topics: {
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
allTopics: string;
|
|
29
|
+
emptyTitle: string;
|
|
30
|
+
emptyDescription: string;
|
|
31
|
+
};
|
|
32
|
+
stories: {
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
allStories: string;
|
|
36
|
+
emptyTitle: string;
|
|
37
|
+
emptyDescription: string;
|
|
38
|
+
};
|
|
39
|
+
discussions: {
|
|
40
|
+
title: string;
|
|
41
|
+
viewAll: string;
|
|
42
|
+
emptyTitle: string;
|
|
43
|
+
emptyDescription: string;
|
|
44
|
+
};
|
|
45
|
+
archive: {
|
|
46
|
+
title: string;
|
|
47
|
+
viewAll: string;
|
|
48
|
+
emptyTitle: string;
|
|
49
|
+
emptyDescription: string;
|
|
50
|
+
};
|
|
51
|
+
interfaith: string;
|
|
52
|
+
};
|
|
11
53
|
stories: {
|
|
12
54
|
title: string;
|
|
13
55
|
description: string;
|
|
@@ -34,6 +76,7 @@ export declare const arTranslations: {
|
|
|
34
76
|
newCollection: string;
|
|
35
77
|
newArtifact: string;
|
|
36
78
|
newRecording: string;
|
|
79
|
+
openCollection: string;
|
|
37
80
|
noCollections: string;
|
|
38
81
|
};
|
|
39
82
|
common: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ar.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/translations/ar.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"ar.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/translations/ar.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8F1B,CAAC"}
|
|
@@ -9,6 +9,48 @@ var e = {
|
|
|
9
9
|
discussions: "المناقشات",
|
|
10
10
|
archive: "الأرشيف"
|
|
11
11
|
},
|
|
12
|
+
home: {
|
|
13
|
+
hero: {
|
|
14
|
+
eyebrow: "TheCulturePost",
|
|
15
|
+
title: "ثقافة، دين، قيم وتاريخ — يُروى بعناية",
|
|
16
|
+
description: "مساحة للقصص المحترمة والمحادثات المدروسة التي تربطنا عبر التقاليد والزمن — مبنية على السجل.",
|
|
17
|
+
readStories: "اقرأ القصص"
|
|
18
|
+
},
|
|
19
|
+
loading: "جارٍ تحميل الأحدث…",
|
|
20
|
+
welcome: {
|
|
21
|
+
title: "مرحباً بك في TheCulturePost",
|
|
22
|
+
description: "هذه المنشورات في بدايتها. قم باختيار موضوع، أو صياغة قصة، أو ابدأ ببناء الأرشيف.",
|
|
23
|
+
draftStory: "صياغة قصة",
|
|
24
|
+
curateTopic: "اختيار موضوع"
|
|
25
|
+
},
|
|
26
|
+
topics: {
|
|
27
|
+
title: "مواضيع مميزة",
|
|
28
|
+
description: "مواضيع مختارة تمتد عبر الثقافة والإيمان والتاريخ والتراث.",
|
|
29
|
+
allTopics: "جميع المواضيع",
|
|
30
|
+
emptyTitle: "لا توجد مواضيع بعد",
|
|
31
|
+
emptyDescription: "اختر الموضوع الأول لتنظيم المنشور."
|
|
32
|
+
},
|
|
33
|
+
stories: {
|
|
34
|
+
title: "أحدث القصص المنشورة",
|
|
35
|
+
description: "قطع نشرت للتو من غرفة الأخبار.",
|
|
36
|
+
allStories: "جميع القصص",
|
|
37
|
+
emptyTitle: "لا توجد قصص منشورة بعد",
|
|
38
|
+
emptyDescription: "صاغ قصة ونقلها عبر دورة النشر التحريرية للنشر."
|
|
39
|
+
},
|
|
40
|
+
discussions: {
|
|
41
|
+
title: "مناقشات نشطة",
|
|
42
|
+
viewAll: "عرض الكل",
|
|
43
|
+
emptyTitle: "لا توجد مناقشات مفتوحة",
|
|
44
|
+
emptyDescription: "افتح مناقشة لدعوة وجهات نظر مدروسة."
|
|
45
|
+
},
|
|
46
|
+
archive: {
|
|
47
|
+
title: "من الأرشيف",
|
|
48
|
+
viewAll: "عرض الكل",
|
|
49
|
+
emptyTitle: "الأرشيف فارغ",
|
|
50
|
+
emptyDescription: "أنشئ مجموعة لبدء الحفاظ على التراث."
|
|
51
|
+
},
|
|
52
|
+
interfaith: "بين الأديان"
|
|
53
|
+
},
|
|
12
54
|
stories: {
|
|
13
55
|
title: "القصص",
|
|
14
56
|
description: "مقالات ودراسات وشروحات وقصص مصورة وخطوط زمنية — عبر دورة النشر الكاملة.",
|
|
@@ -35,6 +77,7 @@ var e = {
|
|
|
35
77
|
newCollection: "مجموعة جديدة",
|
|
36
78
|
newArtifact: "إضافة قطعة",
|
|
37
79
|
newRecording: "تسجيل",
|
|
80
|
+
openCollection: "فتح {{name}}",
|
|
38
81
|
noCollections: "لا توجد مجموعات بعد"
|
|
39
82
|
},
|
|
40
83
|
common: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ar.js","names":[],"sources":["../../../src/theculturepost/translations/ar.ts"],"sourcesContent":["export const arTranslations = {\n nav: {\n home: 'الرئيسية',\n overview: 'نظرة عامة',\n stories: 'القصص',\n topics: 'المواضيع',\n timeline: 'الخط الزمني',\n discussions: 'المناقشات',\n archive: 'الأرشيف',\n },\n stories: {\n title: 'القصص',\n description: 'مقالات ودراسات وشروحات وقصص مصورة وخطوط زمنية — عبر دورة النشر الكاملة.',\n newStory: 'قصة جديدة',\n noStories: 'لا توجد قصص بعد',\n filterStatus: 'تصفية حسب الحالة',\n filterTopic: 'تصفية حسب الموضوع',\n },\n topics: {\n title: 'المواضيع',\n description: 'مواضيع مختارة تغطي الثقافة والدين والتاريخ والتراث والفكر.',\n newTopic: 'موضوع جديد',\n noTopics: 'لا توجد مواضيع بعد',\n },\n discussions: {\n title: 'المناقشات',\n description:\n 'محادثات محترمة وmoderated — بما في ذلك مساحات بين الأديان ترحب بالعديد من التقاليد.',\n openDiscussion: 'فتح مناقشة',\n noDiscussions: 'لا توجد مناقشات مفتوحة',\n },\n archive: {\n title: 'الأرشيف',\n description: 'أرشيف حي من المخطوطات والصور والتسجيلات والخرائط والأشياء — مجمعة في مجموعات.',\n newCollection: 'مجموعة جديدة',\n newArtifact: 'إضافة قطعة',\n newRecording: 'تسجيل',\n noCollections: 'لا توجد مجموعات بعد',\n },\n common: {\n loading: 'جارٍ التحميل…',\n error: 'حدث خطأ',\n retry: 'إعادة المحاولة',\n save: 'حفظ',\n cancel: 'إلغاء',\n edit: 'تعديل',\n share: 'مشاركة',\n back: 'رجوع',\n },\n};\n"],"mappings":";AAAA,IAAa,IAAiB;CAC5B,KAAK;EACH,MAAM;EACN,UAAU;EACV,SAAS;EACT,QAAQ;EACR,UAAU;EACV,aAAa;EACb,SAAS;EACV;CACD,SAAS;EACP,OAAO;EACP,aAAa;EACb,UAAU;EACV,WAAW;EACX,cAAc;EACd,aAAa;EACd;CACD,QAAQ;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,UAAU;EACX;CACD,aAAa;EACX,OAAO;EACP,aACE;EACF,gBAAgB;EAChB,eAAe;EAChB;CACD,SAAS;EACP,OAAO;EACP,aAAa;EACb,eAAe;EACf,aAAa;EACb,cAAc;EACd,eAAe;EAChB;CACD,QAAQ;EACN,SAAS;EACT,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACF"}
|
|
1
|
+
{"version":3,"file":"ar.js","names":[],"sources":["../../../src/theculturepost/translations/ar.ts"],"sourcesContent":["export const arTranslations = {\n nav: {\n home: 'الرئيسية',\n overview: 'نظرة عامة',\n stories: 'القصص',\n topics: 'المواضيع',\n timeline: 'الخط الزمني',\n discussions: 'المناقشات',\n archive: 'الأرشيف',\n },\n home: {\n hero: {\n eyebrow: 'TheCulturePost',\n title: 'ثقافة، دين، قيم وتاريخ — يُروى بعناية',\n description:\n 'مساحة للقصص المحترمة والمحادثات المدروسة التي تربطنا عبر التقاليد والزمن — مبنية على السجل.',\n readStories: 'اقرأ القصص',\n },\n loading: 'جارٍ تحميل الأحدث…',\n welcome: {\n title: 'مرحباً بك في TheCulturePost',\n description:\n 'هذه المنشورات في بدايتها. قم باختيار موضوع، أو صياغة قصة، أو ابدأ ببناء الأرشيف.',\n draftStory: 'صياغة قصة',\n curateTopic: 'اختيار موضوع',\n },\n topics: {\n title: 'مواضيع مميزة',\n description: 'مواضيع مختارة تمتد عبر الثقافة والإيمان والتاريخ والتراث.',\n allTopics: 'جميع المواضيع',\n emptyTitle: 'لا توجد مواضيع بعد',\n emptyDescription: 'اختر الموضوع الأول لتنظيم المنشور.',\n },\n stories: {\n title: 'أحدث القصص المنشورة',\n description: 'قطع نشرت للتو من غرفة الأخبار.',\n allStories: 'جميع القصص',\n emptyTitle: 'لا توجد قصص منشورة بعد',\n emptyDescription: 'صاغ قصة ونقلها عبر دورة النشر التحريرية للنشر.',\n },\n discussions: {\n title: 'مناقشات نشطة',\n viewAll: 'عرض الكل',\n emptyTitle: 'لا توجد مناقشات مفتوحة',\n emptyDescription: 'افتح مناقشة لدعوة وجهات نظر مدروسة.',\n },\n archive: {\n title: 'من الأرشيف',\n viewAll: 'عرض الكل',\n emptyTitle: 'الأرشيف فارغ',\n emptyDescription: 'أنشئ مجموعة لبدء الحفاظ على التراث.',\n },\n interfaith: 'بين الأديان',\n },\n stories: {\n title: 'القصص',\n description: 'مقالات ودراسات وشروحات وقصص مصورة وخطوط زمنية — عبر دورة النشر الكاملة.',\n newStory: 'قصة جديدة',\n noStories: 'لا توجد قصص بعد',\n filterStatus: 'تصفية حسب الحالة',\n filterTopic: 'تصفية حسب الموضوع',\n },\n topics: {\n title: 'المواضيع',\n description: 'مواضيع مختارة تغطي الثقافة والدين والتاريخ والتراث والفكر.',\n newTopic: 'موضوع جديد',\n noTopics: 'لا توجد مواضيع بعد',\n },\n discussions: {\n title: 'المناقشات',\n description:\n 'محادثات محترمة وmoderated — بما في ذلك مساحات بين الأديان ترحب بالعديد من التقاليد.',\n openDiscussion: 'فتح مناقشة',\n noDiscussions: 'لا توجد مناقشات مفتوحة',\n },\n archive: {\n title: 'الأرشيف',\n description: 'أرشيف حي من المخطوطات والصور والتسجيلات والخرائط والأشياء — مجمعة في مجموعات.',\n newCollection: 'مجموعة جديدة',\n newArtifact: 'إضافة قطعة',\n newRecording: 'تسجيل',\n openCollection: 'فتح {{name}}',\n noCollections: 'لا توجد مجموعات بعد',\n },\n common: {\n loading: 'جارٍ التحميل…',\n error: 'حدث خطأ',\n retry: 'إعادة المحاولة',\n save: 'حفظ',\n cancel: 'إلغاء',\n edit: 'تعديل',\n share: 'مشاركة',\n back: 'رجوع',\n },\n};\n"],"mappings":";AAAA,IAAa,IAAiB;CAC5B,KAAK;EACH,MAAM;EACN,UAAU;EACV,SAAS;EACT,QAAQ;EACR,UAAU;EACV,aAAa;EACb,SAAS;EACV;CACD,MAAM;EACJ,MAAM;GACJ,SAAS;GACT,OAAO;GACP,aACE;GACF,aAAa;GACd;EACD,SAAS;EACT,SAAS;GACP,OAAO;GACP,aACE;GACF,YAAY;GACZ,aAAa;GACd;EACD,QAAQ;GACN,OAAO;GACP,aAAa;GACb,WAAW;GACX,YAAY;GACZ,kBAAkB;GACnB;EACD,SAAS;GACP,OAAO;GACP,aAAa;GACb,YAAY;GACZ,YAAY;GACZ,kBAAkB;GACnB;EACD,aAAa;GACX,OAAO;GACP,SAAS;GACT,YAAY;GACZ,kBAAkB;GACnB;EACD,SAAS;GACP,OAAO;GACP,SAAS;GACT,YAAY;GACZ,kBAAkB;GACnB;EACD,YAAY;EACb;CACD,SAAS;EACP,OAAO;EACP,aAAa;EACb,UAAU;EACV,WAAW;EACX,cAAc;EACd,aAAa;EACd;CACD,QAAQ;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,UAAU;EACX;CACD,aAAa;EACX,OAAO;EACP,aACE;EACF,gBAAgB;EAChB,eAAe;EAChB;CACD,SAAS;EACP,OAAO;EACP,aAAa;EACb,eAAe;EACf,aAAa;EACb,cAAc;EACd,gBAAgB;EAChB,eAAe;EAChB;CACD,QAAQ;EACN,SAAS;EACT,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACF"}
|
|
@@ -8,6 +8,48 @@ export declare const enTranslations: {
|
|
|
8
8
|
discussions: string;
|
|
9
9
|
archive: string;
|
|
10
10
|
};
|
|
11
|
+
home: {
|
|
12
|
+
hero: {
|
|
13
|
+
eyebrow: string;
|
|
14
|
+
title: string;
|
|
15
|
+
description: string;
|
|
16
|
+
readStories: string;
|
|
17
|
+
};
|
|
18
|
+
loading: string;
|
|
19
|
+
welcome: {
|
|
20
|
+
title: string;
|
|
21
|
+
description: string;
|
|
22
|
+
draftStory: string;
|
|
23
|
+
curateTopic: string;
|
|
24
|
+
};
|
|
25
|
+
topics: {
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
allTopics: string;
|
|
29
|
+
emptyTitle: string;
|
|
30
|
+
emptyDescription: string;
|
|
31
|
+
};
|
|
32
|
+
stories: {
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
allStories: string;
|
|
36
|
+
emptyTitle: string;
|
|
37
|
+
emptyDescription: string;
|
|
38
|
+
};
|
|
39
|
+
discussions: {
|
|
40
|
+
title: string;
|
|
41
|
+
viewAll: string;
|
|
42
|
+
emptyTitle: string;
|
|
43
|
+
emptyDescription: string;
|
|
44
|
+
};
|
|
45
|
+
archive: {
|
|
46
|
+
title: string;
|
|
47
|
+
viewAll: string;
|
|
48
|
+
emptyTitle: string;
|
|
49
|
+
emptyDescription: string;
|
|
50
|
+
};
|
|
51
|
+
interfaith: string;
|
|
52
|
+
};
|
|
11
53
|
stories: {
|
|
12
54
|
title: string;
|
|
13
55
|
description: string;
|
|
@@ -34,6 +76,7 @@ export declare const enTranslations: {
|
|
|
34
76
|
newCollection: string;
|
|
35
77
|
newArtifact: string;
|
|
36
78
|
newRecording: string;
|
|
79
|
+
openCollection: string;
|
|
37
80
|
noCollections: string;
|
|
38
81
|
};
|
|
39
82
|
common: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/translations/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/translations/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgG1B,CAAC"}
|
|
@@ -9,6 +9,48 @@ var e = {
|
|
|
9
9
|
discussions: "Discussions",
|
|
10
10
|
archive: "Archive"
|
|
11
11
|
},
|
|
12
|
+
home: {
|
|
13
|
+
hero: {
|
|
14
|
+
eyebrow: "TheCulturePost",
|
|
15
|
+
title: "Culture, religion, values & history — told with care",
|
|
16
|
+
description: "A space for respectful stories and thoughtful conversations that connect us across traditions and time — grounded in the record.",
|
|
17
|
+
readStories: "Read stories"
|
|
18
|
+
},
|
|
19
|
+
loading: "Loading the latest…",
|
|
20
|
+
welcome: {
|
|
21
|
+
title: "Welcome to TheCulturePost",
|
|
22
|
+
description: "This publication is just getting started. Curate a topic, draft a story, or begin building the archive.",
|
|
23
|
+
draftStory: "Draft a story",
|
|
24
|
+
curateTopic: "Curate a topic"
|
|
25
|
+
},
|
|
26
|
+
topics: {
|
|
27
|
+
title: "Featured topics",
|
|
28
|
+
description: "Curated subjects across culture, faith, history and heritage.",
|
|
29
|
+
allTopics: "All topics",
|
|
30
|
+
emptyTitle: "No topics yet",
|
|
31
|
+
emptyDescription: "Curate the first topic to organise the publication."
|
|
32
|
+
},
|
|
33
|
+
stories: {
|
|
34
|
+
title: "Latest published stories",
|
|
35
|
+
description: "Freshly published pieces from the newsroom.",
|
|
36
|
+
allStories: "All stories",
|
|
37
|
+
emptyTitle: "No published stories yet",
|
|
38
|
+
emptyDescription: "Draft a story and move it through the editorial lifecycle to publish."
|
|
39
|
+
},
|
|
40
|
+
discussions: {
|
|
41
|
+
title: "Active discussions",
|
|
42
|
+
viewAll: "View all",
|
|
43
|
+
emptyTitle: "No open discussions",
|
|
44
|
+
emptyDescription: "Open a discussion to invite thoughtful perspectives."
|
|
45
|
+
},
|
|
46
|
+
archive: {
|
|
47
|
+
title: "From the archive",
|
|
48
|
+
viewAll: "View all",
|
|
49
|
+
emptyTitle: "The archive is empty",
|
|
50
|
+
emptyDescription: "Create a collection to begin preserving heritage."
|
|
51
|
+
},
|
|
52
|
+
interfaith: "Interfaith"
|
|
53
|
+
},
|
|
12
54
|
stories: {
|
|
13
55
|
title: "Stories",
|
|
14
56
|
description: "Articles, essays, explainers, photo stories and timelines — across the full editorial lifecycle.",
|
|
@@ -35,6 +77,7 @@ var e = {
|
|
|
35
77
|
newCollection: "New collection",
|
|
36
78
|
newArtifact: "Add artifact",
|
|
37
79
|
newRecording: "Record",
|
|
80
|
+
openCollection: "Open {{name}}",
|
|
38
81
|
noCollections: "No collections yet"
|
|
39
82
|
},
|
|
40
83
|
common: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.js","names":[],"sources":["../../../src/theculturepost/translations/en.ts"],"sourcesContent":["export const enTranslations = {\n nav: {\n home: 'Home',\n overview: 'Overview',\n stories: 'Stories',\n topics: 'Topics',\n timeline: 'Timeline',\n discussions: 'Discussions',\n archive: 'Archive',\n },\n stories: {\n title: 'Stories',\n description:\n 'Articles, essays, explainers, photo stories and timelines — across the full editorial lifecycle.',\n newStory: 'New story',\n noStories: 'No stories yet',\n filterStatus: 'Filter by status',\n filterTopic: 'Filter by topic',\n },\n topics: {\n title: 'Topics',\n description: 'Curated subjects spanning culture, faith, history, heritage and thought.',\n newTopic: 'New topic',\n noTopics: 'No topics yet',\n },\n discussions: {\n title: 'Discussions',\n description:\n 'Respectful, moderated conversations — including interfaith spaces that welcome many traditions.',\n openDiscussion: 'Open discussion',\n noDiscussions: 'No open discussions',\n },\n archive: {\n title: 'Archive',\n description:\n 'A living archive of manuscripts, photographs, recordings, maps and objects — grouped into collections.',\n newCollection: 'New collection',\n newArtifact: 'Add artifact',\n newRecording: 'Record',\n noCollections: 'No collections yet',\n },\n common: {\n loading: 'Loading…',\n error: 'An error occurred',\n retry: 'Retry',\n save: 'Save',\n cancel: 'Cancel',\n edit: 'Edit',\n share: 'Share',\n back: 'Back',\n },\n};\n"],"mappings":";AAAA,IAAa,IAAiB;CAC5B,KAAK;EACH,MAAM;EACN,UAAU;EACV,SAAS;EACT,QAAQ;EACR,UAAU;EACV,aAAa;EACb,SAAS;EACV;CACD,SAAS;EACP,OAAO;EACP,aACE;EACF,UAAU;EACV,WAAW;EACX,cAAc;EACd,aAAa;EACd;CACD,QAAQ;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,UAAU;EACX;CACD,aAAa;EACX,OAAO;EACP,aACE;EACF,gBAAgB;EAChB,eAAe;EAChB;CACD,SAAS;EACP,OAAO;EACP,aACE;EACF,eAAe;EACf,aAAa;EACb,cAAc;EACd,eAAe;EAChB;CACD,QAAQ;EACN,SAAS;EACT,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACF"}
|
|
1
|
+
{"version":3,"file":"en.js","names":[],"sources":["../../../src/theculturepost/translations/en.ts"],"sourcesContent":["export const enTranslations = {\n nav: {\n home: 'Home',\n overview: 'Overview',\n stories: 'Stories',\n topics: 'Topics',\n timeline: 'Timeline',\n discussions: 'Discussions',\n archive: 'Archive',\n },\n home: {\n hero: {\n eyebrow: 'TheCulturePost',\n title: 'Culture, religion, values & history — told with care',\n description:\n 'A space for respectful stories and thoughtful conversations that connect us across traditions and time — grounded in the record.',\n readStories: 'Read stories',\n },\n loading: 'Loading the latest…',\n welcome: {\n title: 'Welcome to TheCulturePost',\n description:\n 'This publication is just getting started. Curate a topic, draft a story, or begin building the archive.',\n draftStory: 'Draft a story',\n curateTopic: 'Curate a topic',\n },\n topics: {\n title: 'Featured topics',\n description: 'Curated subjects across culture, faith, history and heritage.',\n allTopics: 'All topics',\n emptyTitle: 'No topics yet',\n emptyDescription: 'Curate the first topic to organise the publication.',\n },\n stories: {\n title: 'Latest published stories',\n description: 'Freshly published pieces from the newsroom.',\n allStories: 'All stories',\n emptyTitle: 'No published stories yet',\n emptyDescription: 'Draft a story and move it through the editorial lifecycle to publish.',\n },\n discussions: {\n title: 'Active discussions',\n viewAll: 'View all',\n emptyTitle: 'No open discussions',\n emptyDescription: 'Open a discussion to invite thoughtful perspectives.',\n },\n archive: {\n title: 'From the archive',\n viewAll: 'View all',\n emptyTitle: 'The archive is empty',\n emptyDescription: 'Create a collection to begin preserving heritage.',\n },\n interfaith: 'Interfaith',\n },\n stories: {\n title: 'Stories',\n description:\n 'Articles, essays, explainers, photo stories and timelines — across the full editorial lifecycle.',\n newStory: 'New story',\n noStories: 'No stories yet',\n filterStatus: 'Filter by status',\n filterTopic: 'Filter by topic',\n },\n topics: {\n title: 'Topics',\n description: 'Curated subjects spanning culture, faith, history, heritage and thought.',\n newTopic: 'New topic',\n noTopics: 'No topics yet',\n },\n discussions: {\n title: 'Discussions',\n description:\n 'Respectful, moderated conversations — including interfaith spaces that welcome many traditions.',\n openDiscussion: 'Open discussion',\n noDiscussions: 'No open discussions',\n },\n archive: {\n title: 'Archive',\n description:\n 'A living archive of manuscripts, photographs, recordings, maps and objects — grouped into collections.',\n newCollection: 'New collection',\n newArtifact: 'Add artifact',\n newRecording: 'Record',\n openCollection: 'Open {{name}}',\n noCollections: 'No collections yet',\n },\n common: {\n loading: 'Loading…',\n error: 'An error occurred',\n retry: 'Retry',\n save: 'Save',\n cancel: 'Cancel',\n edit: 'Edit',\n share: 'Share',\n back: 'Back',\n },\n};\n"],"mappings":";AAAA,IAAa,IAAiB;CAC5B,KAAK;EACH,MAAM;EACN,UAAU;EACV,SAAS;EACT,QAAQ;EACR,UAAU;EACV,aAAa;EACb,SAAS;EACV;CACD,MAAM;EACJ,MAAM;GACJ,SAAS;GACT,OAAO;GACP,aACE;GACF,aAAa;GACd;EACD,SAAS;EACT,SAAS;GACP,OAAO;GACP,aACE;GACF,YAAY;GACZ,aAAa;GACd;EACD,QAAQ;GACN,OAAO;GACP,aAAa;GACb,WAAW;GACX,YAAY;GACZ,kBAAkB;GACnB;EACD,SAAS;GACP,OAAO;GACP,aAAa;GACb,YAAY;GACZ,YAAY;GACZ,kBAAkB;GACnB;EACD,aAAa;GACX,OAAO;GACP,SAAS;GACT,YAAY;GACZ,kBAAkB;GACnB;EACD,SAAS;GACP,OAAO;GACP,SAAS;GACT,YAAY;GACZ,kBAAkB;GACnB;EACD,YAAY;EACb;CACD,SAAS;EACP,OAAO;EACP,aACE;EACF,UAAU;EACV,WAAW;EACX,cAAc;EACd,aAAa;EACd;CACD,QAAQ;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,UAAU;EACX;CACD,aAAa;EACX,OAAO;EACP,aACE;EACF,gBAAgB;EAChB,eAAe;EAChB;CACD,SAAS;EACP,OAAO;EACP,aACE;EACF,eAAe;EACf,aAAa;EACb,cAAc;EACd,gBAAgB;EAChB,eAAe;EAChB;CACD,QAAQ;EACN,SAAS;EACT,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACF"}
|
|
@@ -8,6 +8,48 @@ export declare const esTranslations: {
|
|
|
8
8
|
discussions: string;
|
|
9
9
|
archive: string;
|
|
10
10
|
};
|
|
11
|
+
home: {
|
|
12
|
+
hero: {
|
|
13
|
+
eyebrow: string;
|
|
14
|
+
title: string;
|
|
15
|
+
description: string;
|
|
16
|
+
readStories: string;
|
|
17
|
+
};
|
|
18
|
+
loading: string;
|
|
19
|
+
welcome: {
|
|
20
|
+
title: string;
|
|
21
|
+
description: string;
|
|
22
|
+
draftStory: string;
|
|
23
|
+
curateTopic: string;
|
|
24
|
+
};
|
|
25
|
+
topics: {
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
allTopics: string;
|
|
29
|
+
emptyTitle: string;
|
|
30
|
+
emptyDescription: string;
|
|
31
|
+
};
|
|
32
|
+
stories: {
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
allStories: string;
|
|
36
|
+
emptyTitle: string;
|
|
37
|
+
emptyDescription: string;
|
|
38
|
+
};
|
|
39
|
+
discussions: {
|
|
40
|
+
title: string;
|
|
41
|
+
viewAll: string;
|
|
42
|
+
emptyTitle: string;
|
|
43
|
+
emptyDescription: string;
|
|
44
|
+
};
|
|
45
|
+
archive: {
|
|
46
|
+
title: string;
|
|
47
|
+
viewAll: string;
|
|
48
|
+
emptyTitle: string;
|
|
49
|
+
emptyDescription: string;
|
|
50
|
+
};
|
|
51
|
+
interfaith: string;
|
|
52
|
+
};
|
|
11
53
|
stories: {
|
|
12
54
|
title: string;
|
|
13
55
|
description: string;
|
|
@@ -34,6 +76,7 @@ export declare const esTranslations: {
|
|
|
34
76
|
newCollection: string;
|
|
35
77
|
newArtifact: string;
|
|
36
78
|
newRecording: string;
|
|
79
|
+
openCollection: string;
|
|
37
80
|
noCollections: string;
|
|
38
81
|
};
|
|
39
82
|
common: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/translations/es.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../../src/theculturepost/translations/es.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiG1B,CAAC"}
|