@anthusai/papyrus 5.43.0-next.42 → 5.43.0-next.45
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/components/conversation-thread.tsx +19 -5
- package/components/conversations-memory-source.ts +46 -0
- package/components/conversations-network-source.ts +40 -0
- package/components/conversations-page.tsx +14 -0
- package/components/conversations-source.ts +24 -0
- package/components/conversations-view.tsx +319 -0
- package/components/news-desk-taxonomy-client.ts +73 -2
- package/components/reference-feed.tsx +53 -26
- package/components/story-reporting-trail-client.ts +4 -3
- package/components/story-reporting-trail.tsx +8 -0
- package/components/topic-steering-workspace.tsx +2 -620
- package/lib/conversation-feed.ts +96 -0
- package/lib/story-reporting-trail.ts +28 -2
- package/package.json +7 -1
- package/routes.manifest.json +1 -1
|
@@ -44,7 +44,6 @@ import {
|
|
|
44
44
|
loadEditorReferenceReviewDetails,
|
|
45
45
|
loadEditionForumThreads,
|
|
46
46
|
loadNewsroomAssignmentPage,
|
|
47
|
-
loadNewsroomMessagePage,
|
|
48
47
|
loadNewsroomReferencePage,
|
|
49
48
|
loadNewsroomSemanticNodePage,
|
|
50
49
|
appendForumThreadMessageRecord,
|
|
@@ -68,14 +67,11 @@ import {
|
|
|
68
67
|
loadCyclotronPrimingRequests,
|
|
69
68
|
requestCyclotronReviewRate,
|
|
70
69
|
} from "./news-desk-taxonomy-client";
|
|
71
|
-
import { listConsoleThreads } from "../lib/console-chat-client";
|
|
72
70
|
import {
|
|
73
71
|
effectiveAssignmentsIndexFilters,
|
|
74
|
-
effectiveMessagesIndexFilters,
|
|
75
72
|
effectiveReferencesIndexFilters,
|
|
76
73
|
normalizeReferenceIndexOrder,
|
|
77
74
|
readAssignmentsIndexFilters,
|
|
78
|
-
readMessagesIndexFilters,
|
|
79
75
|
readReferencesIndexFilters,
|
|
80
76
|
referencesStatusFromUrl,
|
|
81
77
|
referencesStatusToUrl,
|
|
@@ -102,6 +98,7 @@ import { NewsroomOpsOverview } from "./newsroom-ops-overview";
|
|
|
102
98
|
import { NewsroomCurationBootstrap } from "./newsroom-curation-bootstrap";
|
|
103
99
|
import { NewsroomReferencesView } from "./newsroom-references-view";
|
|
104
100
|
import { ConversationProvider } from "./conversation-provider";
|
|
101
|
+
import { ConversationsPage } from "./conversations-page";
|
|
105
102
|
import { NewsroomAssignmentsView } from "./newsroom-assignments-view";
|
|
106
103
|
import { NewsroomTopicsView } from "./newsroom-topics-view";
|
|
107
104
|
import { MarkdownContext } from "./markdown-context";
|
|
@@ -221,7 +218,6 @@ import {
|
|
|
221
218
|
import {
|
|
222
219
|
buildForumThreadUrl,
|
|
223
220
|
getForumMessageAnchorId,
|
|
224
|
-
isForumThreadId,
|
|
225
221
|
parseForumMessageAnchorFromHash,
|
|
226
222
|
pushForumThreadUrl,
|
|
227
223
|
readCurrentForumRoute,
|
|
@@ -318,20 +314,6 @@ type NewsroomCardRecord = {
|
|
|
318
314
|
templateRole?: NewsroomCardTemplateRole;
|
|
319
315
|
title: ReactNode;
|
|
320
316
|
};
|
|
321
|
-
type ConsoleThreadSummary = {
|
|
322
|
-
id: string;
|
|
323
|
-
threadKind: string;
|
|
324
|
-
status: string;
|
|
325
|
-
title: string;
|
|
326
|
-
summary?: string | null;
|
|
327
|
-
primaryAnchorKey?: string | null;
|
|
328
|
-
createdByLabel?: string | null;
|
|
329
|
-
messageCount?: number | null;
|
|
330
|
-
lastMessageAt?: string | null;
|
|
331
|
-
createdAt: string;
|
|
332
|
-
updatedAt: string;
|
|
333
|
-
newsroomFeedKey?: string | null;
|
|
334
|
-
};
|
|
335
317
|
type NewsroomDetailAction = {
|
|
336
318
|
ariaLabel?: string;
|
|
337
319
|
icon?: ReactNode;
|
|
@@ -3217,16 +3199,7 @@ function NewsDeskDashboard({
|
|
|
3217
3199
|
</NewsroomCurationBootstrap>
|
|
3218
3200
|
) : null}
|
|
3219
3201
|
{activeTab === "messages" ? (
|
|
3220
|
-
<
|
|
3221
|
-
assignments={assignments}
|
|
3222
|
-
graph={graph}
|
|
3223
|
-
initialForumThreadId={initialSelection.forumThread}
|
|
3224
|
-
initialMessageId={initialSelection.message}
|
|
3225
|
-
isDemo={Boolean(dashboard.isDemo)}
|
|
3226
|
-
messages={messages}
|
|
3227
|
-
newsroomSections={newsroomSections}
|
|
3228
|
-
summary={summary}
|
|
3229
|
-
/>
|
|
3202
|
+
<ConversationsPage authorLabel={authState.label} demo={Boolean(dashboard.isDemo)} />
|
|
3230
3203
|
) : null}
|
|
3231
3204
|
{activeTab === "assignments" ? (
|
|
3232
3205
|
<AssignmentDeskView
|
|
@@ -6847,574 +6820,6 @@ function NewsroomDeskSectionLede({
|
|
|
6847
6820
|
);
|
|
6848
6821
|
}
|
|
6849
6822
|
|
|
6850
|
-
function MessagesDeskView({
|
|
6851
|
-
assignments,
|
|
6852
|
-
graph,
|
|
6853
|
-
initialForumThreadId = null,
|
|
6854
|
-
initialMessageId,
|
|
6855
|
-
isDemo = false,
|
|
6856
|
-
messages,
|
|
6857
|
-
newsroomSections,
|
|
6858
|
-
summary,
|
|
6859
|
-
}: {
|
|
6860
|
-
assignments: AssignmentRecord[];
|
|
6861
|
-
graph: SemanticGraph;
|
|
6862
|
-
initialForumThreadId?: string | null;
|
|
6863
|
-
initialMessageId?: string | null;
|
|
6864
|
-
isDemo?: boolean;
|
|
6865
|
-
messages: MessageRecord[];
|
|
6866
|
-
newsroomSections: NewsroomSectionRecord[];
|
|
6867
|
-
summary?: NewsroomSummaryRecord | null;
|
|
6868
|
-
}) {
|
|
6869
|
-
const resolvedInitialForumThreadId = initialForumThreadId
|
|
6870
|
-
?? (isForumThreadId(initialMessageId) ? initialMessageId : null);
|
|
6871
|
-
const forumMessageAnchorId = useForumMessageAnchorId();
|
|
6872
|
-
const [kindFilter, setKindFilter] = useState(() => {
|
|
6873
|
-
if (resolvedInitialForumThreadId) return "__forum";
|
|
6874
|
-
if (typeof window === "undefined") return "";
|
|
6875
|
-
return readMessagesIndexFilters(new URLSearchParams(window.location.search)).kind;
|
|
6876
|
-
});
|
|
6877
|
-
const [domainFilter, setDomainFilter] = useState(() => {
|
|
6878
|
-
if (typeof window === "undefined") return "";
|
|
6879
|
-
return readMessagesIndexFilters(new URLSearchParams(window.location.search)).domain;
|
|
6880
|
-
});
|
|
6881
|
-
const [selectedMessageId, setSelectedMessageId] = useState(initialMessageId ?? "");
|
|
6882
|
-
const [isMessageDetailOpen, setIsMessageDetailOpen] = useState(Boolean(initialMessageId));
|
|
6883
|
-
const [consoleThreads, setConsoleThreads] = useState<ConsoleThreadSummary[]>([]);
|
|
6884
|
-
const [consoleThreadsError, setConsoleThreadsError] = useState<string | null>(null);
|
|
6885
|
-
const [selectedForumEditionId, setSelectedForumEditionId] = useState("");
|
|
6886
|
-
const [selectedForumSectionId, setSelectedForumSectionId] = useState("");
|
|
6887
|
-
const [forumScopeFilter, setForumScopeFilter] = useState<"all" | "edition" | "section">("all");
|
|
6888
|
-
const [forumActivityFilter, setForumActivityFilter] = useState<"active" | "all">("active");
|
|
6889
|
-
const [forumThreads, setForumThreads] = useState<ForumThreadWithMessages[]>([]);
|
|
6890
|
-
const [forumThreadsLoading, setForumThreadsLoading] = useState(false);
|
|
6891
|
-
const [forumThreadsError, setForumThreadsError] = useState<string | null>(null);
|
|
6892
|
-
const [selectedForumThreadId, setSelectedForumThreadId] = useState("");
|
|
6893
|
-
const [forumView, setForumView] = useState<ForumViewState>({ mode: "index" });
|
|
6894
|
-
const [forumComposeSummary, setForumComposeSummary] = useState("");
|
|
6895
|
-
const [forumComposeContent, setForumComposeContent] = useState("");
|
|
6896
|
-
const [forumReplyParentId, setForumReplyParentId] = useState("");
|
|
6897
|
-
const [forumComposeError, setForumComposeError] = useState<string | null>(null);
|
|
6898
|
-
const [newForumThreadDraft, setNewForumThreadDraft] = useState<ForumNewThreadDraft>(createDefaultForumNewThreadDraft(""));
|
|
6899
|
-
const [newForumThreadOpen, setNewForumThreadOpen] = useState(false);
|
|
6900
|
-
const [newForumThreadError, setNewForumThreadError] = useState<string | null>(null);
|
|
6901
|
-
const [deletingForumMessageId, setDeletingForumMessageId] = useState("");
|
|
6902
|
-
const messageKind = kindFilter === "__chat_detail" ? "console_chat_turn" : kindFilter;
|
|
6903
|
-
const isForumMode = kindFilter === "__forum";
|
|
6904
|
-
const syncMessagesIndexUrl = useCallback((nextKind: string, nextDomain: string, replace = true) => {
|
|
6905
|
-
if (isDemo || isForumMode || isMessageDetailOpen) return;
|
|
6906
|
-
syncBrowserNewsroomIndexUrl(
|
|
6907
|
-
"messages",
|
|
6908
|
-
effectiveMessagesIndexFilters({ kind: nextKind, domain: nextDomain }),
|
|
6909
|
-
{ replace },
|
|
6910
|
-
);
|
|
6911
|
-
}, [isDemo, isForumMode, isMessageDetailOpen]);
|
|
6912
|
-
useEffect(() => {
|
|
6913
|
-
if (isDemo || isForumMode || isMessageDetailOpen) return;
|
|
6914
|
-
syncMessagesIndexUrl(kindFilter, domainFilter, true);
|
|
6915
|
-
}, [domainFilter, isDemo, isForumMode, isMessageDetailOpen, kindFilter, syncMessagesIndexUrl]);
|
|
6916
|
-
const feed = useNewsroomPagedRows({
|
|
6917
|
-
initialItems: messages,
|
|
6918
|
-
enabled: !isDemo && kindFilter !== "__chat_sessions" && !isForumMode,
|
|
6919
|
-
resetKey: `messages:${kindFilter}:${domainFilter}`,
|
|
6920
|
-
loadPage: (nextToken) => loadNewsroomMessagePage({
|
|
6921
|
-
kind: messageKind,
|
|
6922
|
-
domain: domainFilter,
|
|
6923
|
-
nextToken,
|
|
6924
|
-
}),
|
|
6925
|
-
});
|
|
6926
|
-
useEffect(() => {
|
|
6927
|
-
if (isDemo || kindFilter !== "__chat_sessions") return;
|
|
6928
|
-
let active = true;
|
|
6929
|
-
void listConsoleThreads(100)
|
|
6930
|
-
.then((threads) => {
|
|
6931
|
-
if (!active) return;
|
|
6932
|
-
setConsoleThreads(threads);
|
|
6933
|
-
setConsoleThreadsError(null);
|
|
6934
|
-
})
|
|
6935
|
-
.catch((error) => {
|
|
6936
|
-
if (!active) return;
|
|
6937
|
-
setConsoleThreadsError(error instanceof Error ? error.message : "Unable to load chat sessions.");
|
|
6938
|
-
});
|
|
6939
|
-
return () => {
|
|
6940
|
-
active = false;
|
|
6941
|
-
};
|
|
6942
|
-
}, [isDemo, kindFilter]);
|
|
6943
|
-
const [messageOverviewEditions, setMessageOverviewEditions] = useState<ResolvedOverviewEdition[]>([]);
|
|
6944
|
-
useEffect(() => {
|
|
6945
|
-
if (isDemo) {
|
|
6946
|
-
setMessageOverviewEditions([]);
|
|
6947
|
-
return;
|
|
6948
|
-
}
|
|
6949
|
-
let active = true;
|
|
6950
|
-
void loadEditorOverviewEditionData()
|
|
6951
|
-
.then((data) => {
|
|
6952
|
-
if (!active) return;
|
|
6953
|
-
setMessageOverviewEditions(resolveOverviewEditions({
|
|
6954
|
-
editions: data.editions,
|
|
6955
|
-
editionSlots: data.editionSlots,
|
|
6956
|
-
assignments: data.assignments,
|
|
6957
|
-
}));
|
|
6958
|
-
})
|
|
6959
|
-
.catch(() => {
|
|
6960
|
-
if (!active) return;
|
|
6961
|
-
setMessageOverviewEditions(resolveOverviewEditions({
|
|
6962
|
-
editions: [],
|
|
6963
|
-
editionSlots: [],
|
|
6964
|
-
assignments,
|
|
6965
|
-
}));
|
|
6966
|
-
});
|
|
6967
|
-
return () => {
|
|
6968
|
-
active = false;
|
|
6969
|
-
};
|
|
6970
|
-
}, [assignments, isDemo]);
|
|
6971
|
-
const availableSections = useMemo(
|
|
6972
|
-
() => sortNewsroomSections(newsroomSections).filter((section) => section.enabled !== false),
|
|
6973
|
-
[newsroomSections],
|
|
6974
|
-
);
|
|
6975
|
-
useEffect(() => {
|
|
6976
|
-
if (!messageOverviewEditions.length) {
|
|
6977
|
-
setSelectedForumEditionId("");
|
|
6978
|
-
return;
|
|
6979
|
-
}
|
|
6980
|
-
if (selectedForumEditionId && messageOverviewEditions.some((edition) => edition.editionId === selectedForumEditionId)) {
|
|
6981
|
-
return;
|
|
6982
|
-
}
|
|
6983
|
-
const preferred = messageOverviewEditions.find((edition) => edition.isNearestUpcoming) ?? messageOverviewEditions[0];
|
|
6984
|
-
setSelectedForumEditionId(preferred.editionId);
|
|
6985
|
-
}, [messageOverviewEditions, selectedForumEditionId]);
|
|
6986
|
-
useEffect(() => {
|
|
6987
|
-
if (!availableSections.length) {
|
|
6988
|
-
setSelectedForumSectionId("");
|
|
6989
|
-
setNewForumThreadDraft((current) => ({ ...current, sectionId: "" }));
|
|
6990
|
-
return;
|
|
6991
|
-
}
|
|
6992
|
-
if (!selectedForumSectionId || !availableSections.some((section) => section.id === selectedForumSectionId)) {
|
|
6993
|
-
setSelectedForumSectionId(availableSections[0].id);
|
|
6994
|
-
}
|
|
6995
|
-
setNewForumThreadDraft((current) => (
|
|
6996
|
-
current.sectionId
|
|
6997
|
-
? current
|
|
6998
|
-
: { ...current, sectionId: availableSections[0].id }
|
|
6999
|
-
));
|
|
7000
|
-
}, [availableSections, selectedForumSectionId]);
|
|
7001
|
-
const refreshForumThreads = useCallback(async () => {
|
|
7002
|
-
if (!selectedForumEditionId) {
|
|
7003
|
-
setForumThreads([]);
|
|
7004
|
-
setForumThreadsError(null);
|
|
7005
|
-
return;
|
|
7006
|
-
}
|
|
7007
|
-
setForumThreadsLoading(true);
|
|
7008
|
-
try {
|
|
7009
|
-
const sectionIdForQuery = forumScopeFilter === "section" ? selectedForumSectionId : "";
|
|
7010
|
-
const sectionForQuery = availableSections.find((section) => section.id === sectionIdForQuery) ?? null;
|
|
7011
|
-
const result = await loadEditionForumThreads({
|
|
7012
|
-
editionId: selectedForumEditionId,
|
|
7013
|
-
sectionId: sectionIdForQuery || undefined,
|
|
7014
|
-
sectionKey: sectionForQuery?.id ?? undefined,
|
|
7015
|
-
includeMessages: true,
|
|
7016
|
-
status: forumActivityFilter === "active" ? "active" : "",
|
|
7017
|
-
});
|
|
7018
|
-
const merged = [...result.editionThreads, ...result.sectionThreads]
|
|
7019
|
-
.sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")));
|
|
7020
|
-
setForumThreads(merged);
|
|
7021
|
-
setForumThreadsError(null);
|
|
7022
|
-
setSelectedForumThreadId((current) => (current && merged.some((thread) => thread.id === current) ? current : ""));
|
|
7023
|
-
} catch (error) {
|
|
7024
|
-
setForumThreads([]);
|
|
7025
|
-
setForumThreadsError(error instanceof Error ? error.message : "Could not load edition forum threads.");
|
|
7026
|
-
} finally {
|
|
7027
|
-
setForumThreadsLoading(false);
|
|
7028
|
-
}
|
|
7029
|
-
}, [availableSections, forumActivityFilter, forumScopeFilter, selectedForumEditionId, selectedForumSectionId]);
|
|
7030
|
-
useEffect(() => {
|
|
7031
|
-
if (!isForumMode || isDemo) return;
|
|
7032
|
-
void refreshForumThreads();
|
|
7033
|
-
}, [isDemo, isForumMode, refreshForumThreads]);
|
|
7034
|
-
useEffect(() => {
|
|
7035
|
-
if (!isForumMode) return;
|
|
7036
|
-
const threadId = resolvedInitialForumThreadId ?? readCurrentForumRoute().threadId;
|
|
7037
|
-
if (!threadId || forumThreadsLoading) return;
|
|
7038
|
-
if (!forumThreads.some((thread) => thread.id === threadId)) return;
|
|
7039
|
-
if (selectedForumThreadId === threadId && forumView.mode === "thread") return;
|
|
7040
|
-
setSelectedForumThreadId(threadId);
|
|
7041
|
-
setForumView({ mode: "thread", threadId });
|
|
7042
|
-
}, [
|
|
7043
|
-
forumThreads,
|
|
7044
|
-
forumThreadsLoading,
|
|
7045
|
-
forumView.mode,
|
|
7046
|
-
isForumMode,
|
|
7047
|
-
resolvedInitialForumThreadId,
|
|
7048
|
-
selectedForumThreadId,
|
|
7049
|
-
]);
|
|
7050
|
-
|
|
7051
|
-
useEffect(() => {
|
|
7052
|
-
if (!isForumMode) return;
|
|
7053
|
-
const handlePopState = () => {
|
|
7054
|
-
const route = readCurrentForumRoute();
|
|
7055
|
-
if (route.threadId) {
|
|
7056
|
-
setSelectedForumThreadId(route.threadId);
|
|
7057
|
-
setForumView({ mode: "thread", threadId: route.threadId });
|
|
7058
|
-
return;
|
|
7059
|
-
}
|
|
7060
|
-
setSelectedForumThreadId("");
|
|
7061
|
-
setForumView({ mode: "index" });
|
|
7062
|
-
};
|
|
7063
|
-
window.addEventListener("popstate", handlePopState);
|
|
7064
|
-
return () => window.removeEventListener("popstate", handlePopState);
|
|
7065
|
-
}, [isForumMode]);
|
|
7066
|
-
const feedMessages = isDemo ? messages : feed.items;
|
|
7067
|
-
const messageKindCounts = summary?.facets?.messages?.byKind ?? summary?.messageKindCounts ?? countMessagesBy(messages, "messageKind");
|
|
7068
|
-
const messageDomainCounts = summary?.facets?.messages?.byDomain ?? summary?.messageDomainCounts ?? countMessagesBy(messages, "messageDomain");
|
|
7069
|
-
const messageKinds = useMemo(() => sortedCountOptions(messageKindCounts).map((option) => option.key), [messageKindCounts]);
|
|
7070
|
-
const messageDomains = useMemo(() => sortedCountOptions(messageDomainCounts).map((option) => option.key), [messageDomainCounts]);
|
|
7071
|
-
const chatSessionMessages = useMemo(() => consoleThreads.map(consoleThreadToMessageRecord), [consoleThreads]);
|
|
7072
|
-
const forumSectionKeyByThreadId = useMemo(() => {
|
|
7073
|
-
const map = new Map<string, string>();
|
|
7074
|
-
for (const thread of forumThreads) {
|
|
7075
|
-
const sectionKey = normalizeForumThreadSectionKey(thread);
|
|
7076
|
-
if (sectionKey) map.set(thread.id, sectionKey);
|
|
7077
|
-
}
|
|
7078
|
-
return map;
|
|
7079
|
-
}, [forumThreads]);
|
|
7080
|
-
const filteredForumThreads = useMemo(() => forumThreads.filter((thread) => {
|
|
7081
|
-
if (forumScopeFilter === "edition" && thread.scope !== "edition") return false;
|
|
7082
|
-
if (forumScopeFilter === "section" && thread.scope !== "section") return false;
|
|
7083
|
-
if (forumScopeFilter === "section" && selectedForumSectionId) {
|
|
7084
|
-
const selectedSection = availableSections.find((section) => section.id === selectedForumSectionId) ?? null;
|
|
7085
|
-
if (selectedSection) {
|
|
7086
|
-
const threadSectionKey = forumSectionKeyByThreadId.get(thread.id) ?? "";
|
|
7087
|
-
if (threadSectionKey && threadSectionKey !== selectedSection.id) return false;
|
|
7088
|
-
}
|
|
7089
|
-
}
|
|
7090
|
-
if (forumActivityFilter === "active" && thread.status !== "active") return false;
|
|
7091
|
-
return true;
|
|
7092
|
-
}), [availableSections, forumActivityFilter, forumScopeFilter, forumSectionKeyByThreadId, forumThreads, selectedForumSectionId]);
|
|
7093
|
-
const selectedForumThread = filteredForumThreads.find((thread) => thread.id === selectedForumThreadId) ?? null;
|
|
7094
|
-
const visibleMessages = useMemo(() => {
|
|
7095
|
-
if (kindFilter === "__chat_sessions") return chatSessionMessages;
|
|
7096
|
-
return feedMessages
|
|
7097
|
-
.filter((message) => !messageKind || message.messageKind === messageKind)
|
|
7098
|
-
.filter((message) => !domainFilter || message.messageDomain === domainFilter)
|
|
7099
|
-
.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
7100
|
-
}, [chatSessionMessages, domainFilter, feedMessages, kindFilter, messageKind]);
|
|
7101
|
-
const selectedMessage = visibleMessages.find((message) => message.id === selectedMessageId)
|
|
7102
|
-
?? (initialMessageId ? messages.find((message) => message.id === initialMessageId) : null)
|
|
7103
|
-
?? visibleMessages[0]
|
|
7104
|
-
?? null;
|
|
7105
|
-
const selected = selectedMessage ? graph.resolve("message", selectedMessage.id) : null;
|
|
7106
|
-
const selectedForumMessage = selectedForumThread
|
|
7107
|
-
? [...(selectedForumThread.messages ?? [])]
|
|
7108
|
-
.sort((left, right) => Number(left.sequenceNumber ?? 0) - Number(right.sequenceNumber ?? 0))
|
|
7109
|
-
.at(-1) ?? null
|
|
7110
|
-
: null;
|
|
7111
|
-
const selectedEntity = isForumMode
|
|
7112
|
-
? (selectedForumMessage ? graph.resolve("message", selectedForumMessage.id) : null)
|
|
7113
|
-
: selected;
|
|
7114
|
-
const messageKnowledgeQuery = useNewsroomKnowledgeContext(selectedMessage ? {
|
|
7115
|
-
anchor: { kind: "message", id: selectedMessage.id },
|
|
7116
|
-
title: selectedMessage.summary ?? "Stored message payload",
|
|
7117
|
-
subtitle: selectedMessage.messageKind,
|
|
7118
|
-
} : null);
|
|
7119
|
-
const forumKnowledgeQuery = useNewsroomKnowledgeContext(selectedForumMessage ? {
|
|
7120
|
-
anchor: { kind: "message", id: selectedForumMessage.id },
|
|
7121
|
-
title: selectedForumThread?.title ?? selectedForumMessage.summary ?? "Forum thread",
|
|
7122
|
-
subtitle: selectedForumMessage.messageKind,
|
|
7123
|
-
} : null);
|
|
7124
|
-
const cards = visibleMessages.map((message, index) => messageToNewsroomCard(message, index));
|
|
7125
|
-
const totalMessageCount = summaryCountFromRecord(summary, "messages") || messages.length;
|
|
7126
|
-
const metrics = [
|
|
7127
|
-
{ key: "", label: "All", count: totalMessageCount },
|
|
7128
|
-
...messageDomains.slice(0, 4).map((domain) => ({ key: domain, label: domain, count: messageDomainCounts[domain] ?? 0 })),
|
|
7129
|
-
];
|
|
7130
|
-
const selectMessage = (id: string) => {
|
|
7131
|
-
setSelectedMessageId(id);
|
|
7132
|
-
setIsMessageDetailOpen(true);
|
|
7133
|
-
pushNewsroomDetailUrl("messages", id, isDemo);
|
|
7134
|
-
};
|
|
7135
|
-
const openForumThread = (threadId: string, options?: { messageId?: string | null; replace?: boolean }) => {
|
|
7136
|
-
setSelectedForumThreadId(threadId);
|
|
7137
|
-
setForumView({ mode: "thread", threadId });
|
|
7138
|
-
setIsMessageDetailOpen(true);
|
|
7139
|
-
pushForumThreadUrl(threadId, {
|
|
7140
|
-
demo: isDemo,
|
|
7141
|
-
messageId: options?.messageId,
|
|
7142
|
-
replace: options?.replace,
|
|
7143
|
-
});
|
|
7144
|
-
};
|
|
7145
|
-
|
|
7146
|
-
const closeForumThread = () => {
|
|
7147
|
-
setSelectedForumThreadId("");
|
|
7148
|
-
setForumView({ mode: "index" });
|
|
7149
|
-
pushForumThreadUrl(null, { demo: isDemo, replace: true });
|
|
7150
|
-
};
|
|
7151
|
-
const submitNewForumThread = async () => {
|
|
7152
|
-
if (!selectedForumEditionId) return;
|
|
7153
|
-
const title = newForumThreadDraft.title.trim();
|
|
7154
|
-
const content = newForumThreadDraft.content.trim();
|
|
7155
|
-
if (!title) {
|
|
7156
|
-
setNewForumThreadError("Thread title is required.");
|
|
7157
|
-
return;
|
|
7158
|
-
}
|
|
7159
|
-
if (!content) {
|
|
7160
|
-
setNewForumThreadError("Thread body is required.");
|
|
7161
|
-
return;
|
|
7162
|
-
}
|
|
7163
|
-
try {
|
|
7164
|
-
let threadId = "";
|
|
7165
|
-
if (newForumThreadDraft.scope === "section") {
|
|
7166
|
-
const sectionId = String(newForumThreadDraft.sectionId || selectedForumSectionId || "").trim();
|
|
7167
|
-
const section = availableSections.find((entry) => entry.id === sectionId) ?? null;
|
|
7168
|
-
if (!section) {
|
|
7169
|
-
setNewForumThreadError("Section is required for section threads.");
|
|
7170
|
-
return;
|
|
7171
|
-
}
|
|
7172
|
-
const created = await createSectionForumThreadRecord({
|
|
7173
|
-
editionId: selectedForumEditionId,
|
|
7174
|
-
sectionId: section.id,
|
|
7175
|
-
sectionKey: section.id,
|
|
7176
|
-
sectionTitle: section.title,
|
|
7177
|
-
title,
|
|
7178
|
-
actorLabel: "human-editor",
|
|
7179
|
-
});
|
|
7180
|
-
threadId = created.thread.id;
|
|
7181
|
-
} else {
|
|
7182
|
-
const ensured = await ensureEditionForumThreadRecord({
|
|
7183
|
-
editionId: selectedForumEditionId,
|
|
7184
|
-
actorLabel: "human-editor",
|
|
7185
|
-
});
|
|
7186
|
-
threadId = ensured.thread.id;
|
|
7187
|
-
}
|
|
7188
|
-
await appendForumThreadMessageRecord({
|
|
7189
|
-
threadId,
|
|
7190
|
-
summary: title,
|
|
7191
|
-
content,
|
|
7192
|
-
role: "human",
|
|
7193
|
-
authorLabel: "human-editor",
|
|
7194
|
-
});
|
|
7195
|
-
await refreshForumThreads();
|
|
7196
|
-
setNewForumThreadDraft(createDefaultForumNewThreadDraft(selectedForumSectionId || availableSections[0]?.id || ""));
|
|
7197
|
-
setNewForumThreadError(null);
|
|
7198
|
-
setNewForumThreadOpen(false);
|
|
7199
|
-
openForumThread(threadId, { replace: true });
|
|
7200
|
-
} catch (error) {
|
|
7201
|
-
setNewForumThreadError(error instanceof Error ? error.message : "Could not create thread.");
|
|
7202
|
-
}
|
|
7203
|
-
};
|
|
7204
|
-
const submitForumMessage = async () => {
|
|
7205
|
-
if (!selectedForumThread) return;
|
|
7206
|
-
const summary = forumComposeSummary.trim() || forumComposeContent.trim().slice(0, 120) || "Forum reply";
|
|
7207
|
-
const content = forumComposeContent.trim();
|
|
7208
|
-
if (!content) {
|
|
7209
|
-
setForumComposeError("Message content is required.");
|
|
7210
|
-
return;
|
|
7211
|
-
}
|
|
7212
|
-
setForumComposeError(null);
|
|
7213
|
-
try {
|
|
7214
|
-
await appendForumThreadMessageRecord({
|
|
7215
|
-
threadId: selectedForumThread.id,
|
|
7216
|
-
summary,
|
|
7217
|
-
content,
|
|
7218
|
-
role: "human",
|
|
7219
|
-
authorLabel: "human-editor",
|
|
7220
|
-
parentMessageId: forumReplyParentId || undefined,
|
|
7221
|
-
});
|
|
7222
|
-
setForumComposeSummary("");
|
|
7223
|
-
setForumComposeContent("");
|
|
7224
|
-
setForumReplyParentId("");
|
|
7225
|
-
await refreshForumThreads();
|
|
7226
|
-
setForumView({ mode: "thread", threadId: selectedForumThread.id });
|
|
7227
|
-
} catch (error) {
|
|
7228
|
-
setForumComposeError(error instanceof Error ? error.message : "Could not append message.");
|
|
7229
|
-
}
|
|
7230
|
-
};
|
|
7231
|
-
const deleteForumMessage = async (threadId: string, messageId: string) => {
|
|
7232
|
-
setDeletingForumMessageId(messageId);
|
|
7233
|
-
try {
|
|
7234
|
-
await deleteForumThreadMessageRecord({ threadId, messageId });
|
|
7235
|
-
if (forumReplyParentId === messageId) setForumReplyParentId("");
|
|
7236
|
-
await refreshForumThreads();
|
|
7237
|
-
setForumComposeError(null);
|
|
7238
|
-
} catch (error) {
|
|
7239
|
-
setForumComposeError(error instanceof Error ? error.message : "Could not delete message.");
|
|
7240
|
-
} finally {
|
|
7241
|
-
setDeletingForumMessageId("");
|
|
7242
|
-
}
|
|
7243
|
-
};
|
|
7244
|
-
return (
|
|
7245
|
-
<>
|
|
7246
|
-
<NewsroomListDetailShell
|
|
7247
|
-
animatedDetail
|
|
7248
|
-
sectionKey="messages"
|
|
7249
|
-
canExpandDetail={Boolean(isForumMode ? (forumView.mode === "thread" && forumView.threadId) : selectedMessage)}
|
|
7250
|
-
detailOpen={isForumMode ? (forumView.mode === "thread" && Boolean(forumView.threadId)) : isMessageDetailOpen}
|
|
7251
|
-
selectionScrollKey={(isForumMode ? (forumView.mode === "thread" ? forumView.threadId : null) : selectedMessage?.id) ?? null}
|
|
7252
|
-
utilityActions={[isForumMode ? forumKnowledgeQuery.action : messageKnowledgeQuery.action]}
|
|
7253
|
-
lede={(
|
|
7254
|
-
<section className="news-desk-lede news-desk-assignment-lede" aria-labelledby="message-management-title">
|
|
7255
|
-
<div>
|
|
7256
|
-
<h2 id="message-management-title">{formatDeskSectionHeadline("messages")}</h2>
|
|
7257
|
-
<p>{formatDeskSectionLede("messages")}</p>
|
|
7258
|
-
</div>
|
|
7259
|
-
</section>
|
|
7260
|
-
)}
|
|
7261
|
-
list={(
|
|
7262
|
-
<section className="category-steering-section category-steering-section--lead" aria-label="Messages">
|
|
7263
|
-
{isForumMode ? (
|
|
7264
|
-
<ForumThreadIndex
|
|
7265
|
-
threads={filteredForumThreads}
|
|
7266
|
-
sections={availableSections}
|
|
7267
|
-
isLoading={forumThreadsLoading}
|
|
7268
|
-
error={forumThreadsError}
|
|
7269
|
-
emptyLabel="No forum threads for this edition/filter."
|
|
7270
|
-
toolbar={(
|
|
7271
|
-
<div className="news-desk-forum-toolbar">
|
|
7272
|
-
<select
|
|
7273
|
-
aria-label="Forum edition"
|
|
7274
|
-
value={selectedForumEditionId}
|
|
7275
|
-
onChange={(event) => setSelectedForumEditionId(event.target.value)}
|
|
7276
|
-
>
|
|
7277
|
-
{messageOverviewEditions.length ? messageOverviewEditions.map((edition) => (
|
|
7278
|
-
<option key={edition.editionId} value={edition.editionId}>{edition.label}</option>
|
|
7279
|
-
)) : <option value="">No editions found</option>}
|
|
7280
|
-
</select>
|
|
7281
|
-
<select
|
|
7282
|
-
aria-label="Forum scope"
|
|
7283
|
-
value={forumScopeFilter}
|
|
7284
|
-
onChange={(event) => setForumScopeFilter((event.target.value as "all" | "edition" | "section"))}
|
|
7285
|
-
>
|
|
7286
|
-
<option value="all">All scopes</option>
|
|
7287
|
-
<option value="edition">Edition</option>
|
|
7288
|
-
<option value="section">Section</option>
|
|
7289
|
-
</select>
|
|
7290
|
-
<select
|
|
7291
|
-
aria-label="Forum section"
|
|
7292
|
-
value={selectedForumSectionId}
|
|
7293
|
-
onChange={(event) => {
|
|
7294
|
-
setSelectedForumSectionId(event.target.value);
|
|
7295
|
-
setNewForumThreadDraft((current) => ({ ...current, sectionId: event.target.value }));
|
|
7296
|
-
}}
|
|
7297
|
-
disabled={!availableSections.length}
|
|
7298
|
-
>
|
|
7299
|
-
{availableSections.length ? availableSections.map((section) => (
|
|
7300
|
-
<option key={section.id} value={section.id}>{section.title}</option>
|
|
7301
|
-
)) : <option value="">No sections</option>}
|
|
7302
|
-
</select>
|
|
7303
|
-
<select
|
|
7304
|
-
aria-label="Forum status"
|
|
7305
|
-
value={forumActivityFilter}
|
|
7306
|
-
onChange={(event) => setForumActivityFilter((event.target.value as "active" | "all"))}
|
|
7307
|
-
>
|
|
7308
|
-
<option value="active">Active</option>
|
|
7309
|
-
<option value="all">All</option>
|
|
7310
|
-
</select>
|
|
7311
|
-
<button type="button" disabled={!selectedForumEditionId} onClick={() => setNewForumThreadOpen((value) => !value)}>
|
|
7312
|
-
{newForumThreadOpen ? "Close New Thread" : "New Thread"}
|
|
7313
|
-
</button>
|
|
7314
|
-
<button type="button" onClick={() => setKindFilter("")}>Back to Messages</button>
|
|
7315
|
-
</div>
|
|
7316
|
-
)}
|
|
7317
|
-
composer={newForumThreadOpen ? (
|
|
7318
|
-
<ForumThreadComposer
|
|
7319
|
-
draft={newForumThreadDraft}
|
|
7320
|
-
sections={availableSections}
|
|
7321
|
-
error={newForumThreadError}
|
|
7322
|
-
onChange={setNewForumThreadDraft}
|
|
7323
|
-
onCancel={() => {
|
|
7324
|
-
setNewForumThreadOpen(false);
|
|
7325
|
-
setNewForumThreadError(null);
|
|
7326
|
-
}}
|
|
7327
|
-
onSubmit={() => void submitNewForumThread()}
|
|
7328
|
-
/>
|
|
7329
|
-
) : null}
|
|
7330
|
-
onOpenThread={openForumThread}
|
|
7331
|
-
/>
|
|
7332
|
-
) : (
|
|
7333
|
-
<NewsroomCardGrid
|
|
7334
|
-
cards={cards}
|
|
7335
|
-
emptyLabel="No private messages recorded"
|
|
7336
|
-
filterLabel="Message kind"
|
|
7337
|
-
filterOptions={[
|
|
7338
|
-
{ key: "__forum", label: "Forum threads", count: forumThreads.length || undefined },
|
|
7339
|
-
{ key: "", label: "All kinds", count: totalMessageCount },
|
|
7340
|
-
{ key: "__chat_sessions", label: "Chat sessions", count: consoleThreads.length || undefined },
|
|
7341
|
-
{ key: "__chat_detail", label: "Chat detail", count: messageKindCounts.console_chat_turn ?? 0 },
|
|
7342
|
-
...messageKinds.map((kind) => ({ key: kind, label: kind, count: messageKindCounts[kind] ?? 0 })),
|
|
7343
|
-
]}
|
|
7344
|
-
filterValue={kindFilter}
|
|
7345
|
-
metricValue={domainFilter}
|
|
7346
|
-
metrics={metrics}
|
|
7347
|
-
footerLabel={consoleThreadsError ?? feed.error ?? undefined}
|
|
7348
|
-
hasMore={!isDemo && kindFilter !== "__chat_sessions" && feed.hasMore}
|
|
7349
|
-
isLoadingMore={feed.isLoadingMore}
|
|
7350
|
-
onFilterChange={(value) => {
|
|
7351
|
-
setKindFilter(value);
|
|
7352
|
-
syncMessagesIndexUrl(value, domainFilter, true);
|
|
7353
|
-
}}
|
|
7354
|
-
onLoadMore={feed.loadMore}
|
|
7355
|
-
onMetricChange={(value) => {
|
|
7356
|
-
setDomainFilter(value);
|
|
7357
|
-
syncMessagesIndexUrl(kindFilter, value, true);
|
|
7358
|
-
}}
|
|
7359
|
-
onSelect={selectMessage}
|
|
7360
|
-
selectedId={selectedMessage?.id ?? null}
|
|
7361
|
-
/>
|
|
7362
|
-
)}
|
|
7363
|
-
</section>
|
|
7364
|
-
)}
|
|
7365
|
-
onCloseDetail={() => {
|
|
7366
|
-
if (isForumMode) {
|
|
7367
|
-
closeForumThread();
|
|
7368
|
-
return;
|
|
7369
|
-
}
|
|
7370
|
-
setIsMessageDetailOpen(false);
|
|
7371
|
-
syncMessagesIndexUrl(kindFilter, domainFilter, true);
|
|
7372
|
-
}}
|
|
7373
|
-
detail={isForumMode ? (
|
|
7374
|
-
(forumView.mode === "thread" && selectedForumThread ? (
|
|
7375
|
-
<ForumThreadView
|
|
7376
|
-
thread={selectedForumThread}
|
|
7377
|
-
sections={availableSections}
|
|
7378
|
-
replyParentId={forumReplyParentId}
|
|
7379
|
-
composeSummary={forumComposeSummary}
|
|
7380
|
-
composeContent={forumComposeContent}
|
|
7381
|
-
composeError={forumComposeError}
|
|
7382
|
-
focusMessageId={forumMessageAnchorId}
|
|
7383
|
-
isDemo={isDemo}
|
|
7384
|
-
onBack={closeForumThread}
|
|
7385
|
-
onReplyTarget={(messageId) => {
|
|
7386
|
-
setForumReplyParentId(messageId);
|
|
7387
|
-
pushForumThreadUrl(selectedForumThread.id, { demo: isDemo, messageId, replace: true });
|
|
7388
|
-
}}
|
|
7389
|
-
onDeleteMessage={(messageId) => {
|
|
7390
|
-
if (!selectedForumThread || deletingForumMessageId === messageId) return;
|
|
7391
|
-
void deleteForumMessage(selectedForumThread.id, messageId);
|
|
7392
|
-
}}
|
|
7393
|
-
onClearReplyTarget={() => {
|
|
7394
|
-
setForumReplyParentId("");
|
|
7395
|
-
pushForumThreadUrl(selectedForumThread.id, { demo: isDemo, replace: true });
|
|
7396
|
-
}}
|
|
7397
|
-
onSummaryChange={setForumComposeSummary}
|
|
7398
|
-
onContentChange={setForumComposeContent}
|
|
7399
|
-
onSubmit={() => void submitForumMessage()}
|
|
7400
|
-
deletingMessageId={deletingForumMessageId}
|
|
7401
|
-
/>
|
|
7402
|
-
) : forumView.mode === "thread" ? (
|
|
7403
|
-
<section className="category-steering-section">
|
|
7404
|
-
<SectionHeader title="Thread Unavailable" detail="Forum" />
|
|
7405
|
-
<EmptyRow label="That thread is not available under the current filters. Return to Threads and adjust filters." />
|
|
7406
|
-
<button type="button" onClick={closeForumThread}>Back to Threads</button>
|
|
7407
|
-
</section>
|
|
7408
|
-
) : <SemanticDetailPanel graph={graph} selected={selectedEntity} knowledgeQuery={forumKnowledgeQuery} />)
|
|
7409
|
-
) : (selectedMessage
|
|
7410
|
-
? <MessageDetailPanel graph={graph} message={selectedMessage} selected={selected} knowledgeQuery={messageKnowledgeQuery} />
|
|
7411
|
-
: <SemanticDetailPanel graph={graph} selected={selected} knowledgeQuery={messageKnowledgeQuery} />)}
|
|
7412
|
-
/>
|
|
7413
|
-
{isForumMode ? forumKnowledgeQuery.dialog : messageKnowledgeQuery.dialog}
|
|
7414
|
-
</>
|
|
7415
|
-
);
|
|
7416
|
-
}
|
|
7417
|
-
|
|
7418
6823
|
function useForumMessageAnchorId(): string | null {
|
|
7419
6824
|
const [messageId, setMessageId] = useState<string | null>(null);
|
|
7420
6825
|
useEffect(() => {
|
|
@@ -13103,29 +12508,6 @@ function messageToNewsroomCard(message: MessageRecord, index: number): NewsroomC
|
|
|
13103
12508
|
};
|
|
13104
12509
|
}
|
|
13105
12510
|
|
|
13106
|
-
function consoleThreadToMessageRecord(thread: ConsoleThreadSummary): MessageRecord {
|
|
13107
|
-
const updatedAt = thread.updatedAt ?? thread.lastMessageAt ?? thread.createdAt;
|
|
13108
|
-
return {
|
|
13109
|
-
id: thread.id,
|
|
13110
|
-
messageKind: "console_thread",
|
|
13111
|
-
messageDomain: "conversation",
|
|
13112
|
-
status: thread.status,
|
|
13113
|
-
summary: thread.summary ?? thread.title,
|
|
13114
|
-
source: "papyrus-console",
|
|
13115
|
-
authorLabel: thread.createdByLabel ?? null,
|
|
13116
|
-
createdAt: thread.createdAt,
|
|
13117
|
-
updatedAt,
|
|
13118
|
-
newsroomFeedKey: thread.newsroomFeedKey ?? "consoleChat",
|
|
13119
|
-
metadata: {
|
|
13120
|
-
threadId: thread.id,
|
|
13121
|
-
threadKind: thread.threadKind,
|
|
13122
|
-
messageCount: thread.messageCount ?? 0,
|
|
13123
|
-
lastMessageAt: thread.lastMessageAt ?? null,
|
|
13124
|
-
primaryAnchorKey: thread.primaryAnchorKey ?? null,
|
|
13125
|
-
},
|
|
13126
|
-
};
|
|
13127
|
-
}
|
|
13128
|
-
|
|
13129
12511
|
function assignmentToNewsroomCard(assignment: AssignmentRecord, index: number, reportingDecision: ReportingPacketDecisionSummary | null = null): NewsroomCardRecord {
|
|
13130
12512
|
const analysisPlan = assignmentAnalysisReindexMetadata(assignment);
|
|
13131
12513
|
const subtitle = assignmentCardSubtitle(assignment);
|