@anthusai/papyrus 1.6.0 → 1.8.0
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/app/newsroom/sections/[sectionId]/page.tsx +3 -0
- package/components/news-desk-taxonomy-client.ts +1 -0
- package/components/newsroom-board-view.tsx +44 -25
- package/components/newsroom-organizer.css +6 -3
- package/components/topic-steering-workspace.tsx +1 -1
- package/lib/newsroom-board.ts +25 -0
- package/lib/prospectus-account-client.ts +64 -0
- package/lib/site-stack.ts +1 -1
- package/package.json +2 -1
- package/routes.manifest.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { notFound } from "next/navigation";
|
|
2
|
+
import { resolveActiveSiteBrand } from "../../../../lib/site-brand-tenant";
|
|
3
|
+
import { NewsroomBoardView } from "../../../../components/newsroom-board-view";
|
|
2
4
|
import { NewsDeskPage } from "../../../../components/news-desk-page";
|
|
3
5
|
|
|
4
6
|
export const dynamic = "force-dynamic";
|
|
@@ -13,5 +15,6 @@ export default async function NewsroomDeepSectionPage({ params, searchParams }:
|
|
|
13
15
|
const normalizedSectionId = decodeURIComponent(sectionId ?? "").trim();
|
|
14
16
|
if (!normalizedSectionId) notFound();
|
|
15
17
|
|
|
18
|
+
if (resolveActiveSiteBrand().newsroomLayout === "organizer") return <NewsroomBoardView subject="desk" deskId={normalizedSectionId} />;
|
|
16
19
|
return <NewsDeskPage sectionPageId={normalizedSectionId} searchParams={searchParams} />;
|
|
17
20
|
}
|
|
@@ -2236,6 +2236,7 @@ type PublishedItemBoardRow = {
|
|
|
2236
2236
|
};
|
|
2237
2237
|
|
|
2238
2238
|
export async function loadNewsroomPublishedContentItemVersions(): Promise<ContentItemVersionRecord[]> {
|
|
2239
|
+
if (isLocalPreviewEnabled()) return [];
|
|
2239
2240
|
configureAmplifyClient();
|
|
2240
2241
|
const client = generateClient<Schema>() as unknown as {
|
|
2241
2242
|
graphql: (options: Record<string, unknown>) => Promise<GraphQLConnectionResponse<PublishedItemBoardRow>>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
|
-
import { IssueCollection, IssueDetailPanel, useBoardColumnState } from "@kanbus/ui";
|
|
5
|
+
import { IssueCollection, IssueDetailPanel, IssueTypeFilter, issueTypeFromFilter, useBoardColumnState } from "@kanbus/ui";
|
|
6
6
|
import "@kanbus/ui/styles/kanban.css";
|
|
7
7
|
import "./newsroom-newsprint-board.css";
|
|
8
8
|
import { PlusIcon } from "lucide-react";
|
|
@@ -15,11 +15,15 @@ import {
|
|
|
15
15
|
canReadNewsroomBoard,
|
|
16
16
|
contentItemBoardConfig,
|
|
17
17
|
contentItemBoardType,
|
|
18
|
+
deskBoardConfig,
|
|
19
|
+
deskContainsAssignment,
|
|
20
|
+
deskContainsContentItem,
|
|
18
21
|
projectAssignmentBoard,
|
|
19
22
|
projectContentItemBoard,
|
|
20
23
|
type NativeBoardIssue,
|
|
21
24
|
} from "../lib/newsroom-board";
|
|
22
|
-
import type { AssignmentRecord } from "../lib/category-repository";
|
|
25
|
+
import type { AssignmentRecord, NewsroomSectionRecord } from "../lib/category-repository";
|
|
26
|
+
import { displayNewsroomSectionShortTitle } from "../lib/newsroom-sections";
|
|
23
27
|
import { getContentItemName, getContentItemStageDefinitions, SITE_BRAND } from "../lib/site-brand";
|
|
24
28
|
import { getNewsroomNavHref } from "../lib/newsroom-nav";
|
|
25
29
|
import { loadNewsroomAssignmentPage, loadNewsroomPublishedContentItemVersions } from "./news-desk-taxonomy-client";
|
|
@@ -46,10 +50,10 @@ async function loadEveryNewsroomAssignment() {
|
|
|
46
50
|
return assignments;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
export type NewsroomBoardSubject = "contentItem" | "assignment";
|
|
53
|
+
export type NewsroomBoardSubject = "contentItem" | "assignment" | "desk";
|
|
50
54
|
|
|
51
55
|
/** Read-only board of one record kind over authenticated CMS records; existing workspaces own every mutation. */
|
|
52
|
-
export function NewsroomBoardView({ subject }: { subject: NewsroomBoardSubject }) {
|
|
56
|
+
export function NewsroomBoardView({ subject, deskId }: { subject: NewsroomBoardSubject; deskId?: string }) {
|
|
53
57
|
const session = useOptionalNewsDeskClient();
|
|
54
58
|
const allowed = canReadNewsroomBoard(session?.shell.phase);
|
|
55
59
|
const theme = useResolvedPapyrusTheme();
|
|
@@ -63,13 +67,20 @@ export function NewsroomBoardView({ subject }: { subject: NewsroomBoardSubject }
|
|
|
63
67
|
const [supportingRecordsError, setSupportingRecordsError] = useState<string | null>(null);
|
|
64
68
|
const generation = useRef(0);
|
|
65
69
|
const boardBodyRef = useRef<HTMLDivElement | null>(null);
|
|
66
|
-
const
|
|
67
|
-
const
|
|
70
|
+
const [typeFilter, setTypeFilter] = useState<string | null>("all");
|
|
71
|
+
const showsContentItems = subject !== "assignment";
|
|
72
|
+
const contentItemColumnKeyPrefix = `${contentItemBoardType()}/`;
|
|
73
|
+
const boardType = subject === "assignment" ? ASSIGNMENT_BOARD_TYPE : contentItemBoardType();
|
|
74
|
+
const deskAssignmentIssues = useMemo(() => subject === "desk" && deskId
|
|
75
|
+
? projectAssignmentBoard(assignments.filter(assignment => deskContainsAssignment(assignment, deskId))) : [], [assignments, deskId, subject]);
|
|
76
|
+
const columnStateConfig = useMemo(() => subject === "contentItem" ? contentItemBoardConfig([])
|
|
77
|
+
: subject === "desk" ? deskBoardConfig(deskAssignmentIssues)
|
|
78
|
+
: assignmentBoardConfig(assignmentBoardIssues), [assignmentBoardIssues, deskAssignmentIssues, subject]);
|
|
68
79
|
const columnState = useBoardColumnState({ config: columnStateConfig, storageKey: `papyrus.cms.columns.v1.${SITE_BRAND.id}.${subject}` });
|
|
69
|
-
const openedCollapsedUntilOpenedStages = useMemo(() =>
|
|
70
|
-
.filter(definition => definition.collapsedUntilOpened && !columnState.collapsedColumns.has(`${
|
|
71
|
-
.map(definition => definition.key), [
|
|
72
|
-
const pagedContentItems = usePagedContentItemVersions({ enabled: allowed &&
|
|
80
|
+
const openedCollapsedUntilOpenedStages = useMemo(() => !showsContentItems ? [] : getContentItemStageDefinitions()
|
|
81
|
+
.filter(definition => definition.collapsedUntilOpened && !columnState.collapsedColumns.has(`${contentItemColumnKeyPrefix}${definition.key}`))
|
|
82
|
+
.map(definition => definition.key), [contentItemColumnKeyPrefix, columnState.collapsedColumns, showsContentItems]);
|
|
83
|
+
const pagedContentItems = usePagedContentItemVersions({ enabled: allowed && showsContentItems, openedCollapsedUntilOpenedStages });
|
|
73
84
|
useEffect(() => {
|
|
74
85
|
setLowMotion(readLocalReaderSettings().motion === "low");
|
|
75
86
|
return subscribeReaderSettingsChanges(settings => setLowMotion(settings.motion === "low"));
|
|
@@ -78,7 +89,7 @@ export function NewsroomBoardView({ subject }: { subject: NewsroomBoardSubject }
|
|
|
78
89
|
const request = ++generation.current;
|
|
79
90
|
setAssignmentBoardIssues([]); setPublishedVersions([]); setAssignments([]); setSelected(null); setSupportingRecordsError(null);
|
|
80
91
|
if (!allowed) { setSupportingRecordsLoading(false); return; }
|
|
81
|
-
const loadSupportingRecords = () => subject
|
|
92
|
+
const loadSupportingRecords = () => subject !== "assignment"
|
|
82
93
|
? Promise.all([loadNewsroomPublishedContentItemVersions(), loadEveryNewsroomAssignment()]).then(([published, everyAssignment]) => {
|
|
83
94
|
if (generation.current === request) { setPublishedVersions(published); setAssignments(everyAssignment); }
|
|
84
95
|
})
|
|
@@ -99,13 +110,19 @@ export function NewsroomBoardView({ subject }: { subject: NewsroomBoardSubject }
|
|
|
99
110
|
document.addEventListener("visibilitychange", refreshWhenVisible);
|
|
100
111
|
return () => { generation.current = request + 1; window.clearInterval(interval); document.removeEventListener("visibilitychange", refreshWhenVisible); };
|
|
101
112
|
}, [allowed, subject]);
|
|
102
|
-
const issues = useMemo(() =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
113
|
+
const issues = useMemo(() => {
|
|
114
|
+
if (subject === "assignment") return assignmentBoardIssues;
|
|
115
|
+
if (subject === "contentItem") return projectContentItemBoard({ contentItemVersions: [...pagedContentItems.versions, ...publishedVersions], assignments });
|
|
116
|
+
const versionsOnDesk = [...pagedContentItems.versions, ...publishedVersions].filter(version => deskId && deskContainsContentItem(version, deskId));
|
|
117
|
+
return [...projectContentItemBoard({ contentItemVersions: versionsOnDesk, assignments: assignments.filter(assignment => deskId && deskContainsAssignment(assignment, deskId)) }), ...deskAssignmentIssues];
|
|
118
|
+
}, [assignmentBoardIssues, assignments, deskAssignmentIssues, deskId, pagedContentItems.versions, publishedVersions, subject]);
|
|
119
|
+
const loading = supportingRecordsLoading || (showsContentItems && pagedContentItems.loading);
|
|
120
|
+
const error = supportingRecordsError ?? (showsContentItems ? pagedContentItems.error : null);
|
|
121
|
+
const config = useMemo(() => subject === "contentItem" ? contentItemBoardConfig(issues) : subject === "desk" ? deskBoardConfig(issues) : assignmentBoardConfig(issues), [issues, subject]);
|
|
122
|
+
const columnStageFromElement = (element: Element | null) => {
|
|
123
|
+
const columnKey = element?.closest("[data-column-key]")?.getAttribute("data-column-key");
|
|
124
|
+
return columnKey?.startsWith(contentItemColumnKeyPrefix) ? columnKey.slice(contentItemColumnKeyPrefix.length) : null;
|
|
125
|
+
};
|
|
109
126
|
const loadMoreWhenColumnIsNearItsEnd = (columnScroll: HTMLElement) => {
|
|
110
127
|
const distanceToEnd = columnScroll.scrollHeight - columnScroll.scrollTop - columnScroll.clientHeight;
|
|
111
128
|
const stage = columnStageFromElement(columnScroll);
|
|
@@ -113,19 +130,21 @@ export function NewsroomBoardView({ subject }: { subject: NewsroomBoardSubject }
|
|
|
113
130
|
};
|
|
114
131
|
const handleBoardScroll = (event: React.UIEvent<HTMLDivElement>) => {
|
|
115
132
|
const target = event.target as HTMLElement;
|
|
116
|
-
if (
|
|
133
|
+
if (showsContentItems && target.classList?.contains("kb-column-scroll")) loadMoreWhenColumnIsNearItsEnd(target);
|
|
117
134
|
};
|
|
118
135
|
useEffect(() => {
|
|
119
|
-
if (
|
|
136
|
+
if (!showsContentItems) return;
|
|
120
137
|
boardBodyRef.current?.querySelectorAll<HTMLElement>(".kb-column-scroll").forEach(loadMoreWhenColumnIsNearItsEnd);
|
|
121
138
|
});
|
|
122
|
-
const
|
|
139
|
+
const deskTitle = session?.shell.dashboard?.newsroomSections?.find(section => section.id === deskId)?.title ?? (deskId ? displayNewsroomSectionShortTitle({ id: deskId } as NewsroomSectionRecord) : "Desk");
|
|
140
|
+
const pageTitle = subject === "contentItem" ? getContentItemName().plural : subject === "desk" ? deskTitle : "Assignments";
|
|
123
141
|
const issue = issues.find(item => item.id === selected);
|
|
124
142
|
if (!allowed) return <NewsDeskAccessGate shell={session?.shell ?? null} />;
|
|
125
|
-
return <NewsroomOpsShell activeTab={subject === "contentItem" ? "articles" : "assignments"} appTitle={SITE_BRAND.appTitle} backLabel={SITE_BRAND.backToHomeLabel} pageTitle={pageTitle}
|
|
143
|
+
return <NewsroomOpsShell activeTab={subject === "contentItem" ? "articles" : subject === "desk" ? "desks" : "assignments"} appTitle={SITE_BRAND.appTitle} backLabel={SITE_BRAND.backToHomeLabel} pageTitle={pageTitle}
|
|
126
144
|
headerActions={<>
|
|
145
|
+
<span role="status" className="organizer-board-note">{loading ? "Loading records…" : `${issues.length} records loaded.`}</span>
|
|
127
146
|
<div role="group" aria-label="Record presentation" className="organizer-board-view-toggle"><button type="button" aria-pressed={view === "board"} onClick={() => setView("board")}>Board</button><button type="button" aria-pressed={view === "list"} onClick={() => setView("list")}>List</button></div>
|
|
128
|
-
{
|
|
147
|
+
{showsContentItems && (
|
|
129
148
|
<Link className={cn(buttonVariants({ size: "sm" }))} data-newsroom-article-new href={getNewsroomNavHref(newsroomHref("articles", "new"))}>
|
|
130
149
|
<PlusIcon aria-hidden="true" />
|
|
131
150
|
{`New ${getContentItemName().singular.toLowerCase()}`}
|
|
@@ -134,9 +153,9 @@ export function NewsroomBoardView({ subject }: { subject: NewsroomBoardSubject }
|
|
|
134
153
|
</>}>
|
|
135
154
|
<div className="organizer-native-board papyrus-newsprint-board" data-color-mode={theme}>
|
|
136
155
|
{error && <NewsroomOpsStatusBanner tone="error">{error}</NewsroomOpsStatusBanner>}
|
|
137
|
-
<
|
|
156
|
+
{subject === "desk" && <IssueTypeFilter config={config} value={typeFilter} onChange={setTypeFilter} name="Record type" className="organizer-board-type-filter" />}
|
|
138
157
|
<div className="organizer-board-body" data-detail-open={Boolean(issue)} ref={boardBodyRef} onScrollCapture={handleBoardScroll}>
|
|
139
|
-
<IssueCollection issues={issues} config={config} query={{ type: boardType }} view={view}
|
|
158
|
+
<IssueCollection issues={issues} config={config} query={{ type: subject === "desk" ? issueTypeFromFilter(typeFilter) : boardType }} view={view}
|
|
140
159
|
motion={lowMotion ? STATIC_MOTION : undefined}
|
|
141
160
|
collapsedColumns={columnState.collapsedColumns} onToggleCollapse={columnState.toggleColumn} initialScroll="start"
|
|
142
161
|
selectedIssueId={selected} detailOpen={Boolean(issue)} onSelectIssue={item => setSelected(item.id)} ariaLabel={pageTitle}
|
|
@@ -20,7 +20,7 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
|
|
|
20
20
|
.organizer-destinations a[aria-current=page]{border-bottom:2px solid var(--org-ink);color:var(--org-ink)}
|
|
21
21
|
.newsroom-organizer :is(a,button):focus-visible,.organizer-menu :is(a,button):focus-visible{outline:2px solid currentColor;outline-offset:-3px}
|
|
22
22
|
.organizer-workspace{display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden;padding:0 16px}
|
|
23
|
-
.organizer-topbar{height:
|
|
23
|
+
.organizer-topbar{height:48px;min-height:48px;display:flex;align-items:center;gap:16px;border-bottom:1px solid var(--org-ink)}
|
|
24
24
|
.organizer-masthead{font:900 28px Newsroom Display,Georgia,serif;white-space:nowrap;max-width:30vw;overflow:hidden;text-overflow:ellipsis}
|
|
25
25
|
.organizer-topbar h1{font:400 12px Newsroom UI,system-ui;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0}
|
|
26
26
|
.organizer-toggle{display:flex;align-items:center;justify-content:center;width:36px;min-height:36px;padding:0;border:1px solid var(--org-line);flex-shrink:0}
|
|
@@ -36,11 +36,13 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
|
|
|
36
36
|
.organizer-account-popup [role=menuitem]{min-height:40px;font-size:12px}.organizer-account-popup .reader-auth{display:contents}.organizer-account-popup .reader-auth__error{display:block;white-space:normal;font-size:11px;padding:8px}
|
|
37
37
|
.organizer-bottom-nav{display:none}
|
|
38
38
|
.organizer-main:has(.organizer-native-board){display:flex;overflow:hidden}
|
|
39
|
-
.organizer-native-board{display:flex;flex-direction:column;flex:1;min-height:0;min-width:0;gap:
|
|
39
|
+
.organizer-native-board{display:flex;flex-direction:column;flex:1;min-height:0;min-width:0;gap:8px}
|
|
40
40
|
.organizer-board-view-toggle{display:flex;gap:4px;flex-shrink:0}
|
|
41
41
|
.organizer-board-view-toggle button{min-height:36px;padding:4px 12px;border:1px solid var(--org-line)}
|
|
42
42
|
.organizer-board-view-toggle button[aria-pressed=true]{background:var(--org-ink);color:var(--org-paper)}
|
|
43
|
-
.organizer-board-note{font-size:11px;line-height:1.5;margin:0;color:var(--org-muted)}
|
|
43
|
+
.organizer-board-note{font-size:11px;line-height:1.5;margin:0;color:var(--org-muted);white-space:nowrap}
|
|
44
|
+
.organizer-header-actions a[data-newsroom-article-new]{color:var(--primary-foreground)}
|
|
45
|
+
@media(min-width:901px){.newsroom-organizer[data-sidebar-collapsed=false] .organizer-masthead{display:none}}
|
|
44
46
|
.organizer-board-body{position:relative;display:grid;grid-template-columns:minmax(0,1fr);flex:1;min-width:0;min-height:0;gap:16px}
|
|
45
47
|
.organizer-board-body[data-detail-open=true]{grid-template-columns:minmax(0,1fr) minmax(280px,360px)}
|
|
46
48
|
.organizer-board-body>.kb-issue-collection{height:100%;min-height:0;overflow:auto}
|
|
@@ -50,6 +52,7 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
|
|
|
50
52
|
.organizer-menu .organizer-destinations{overflow-y:auto}.organizer-menu .organizer-destinations section{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.organizer-menu .organizer-destinations h3{grid-column:1/-1;font-size:12px;text-transform:none;margin:0 0 4px}
|
|
51
53
|
.organizer-menu .organizer-destinations a{border:1px solid var(--org-line);padding:12px;color:var(--org-ink)}
|
|
52
54
|
@media(max-width:900px){
|
|
55
|
+
.organizer-board-note{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
|
|
53
56
|
.newsroom-organizer,.newsroom-organizer[data-sidebar-collapsed=true]{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr) auto}
|
|
54
57
|
.organizer-sidebar,.organizer-toggle{display:none}.organizer-workspace{padding:0 16px}.organizer-topbar{gap:8px;height:55px;min-height:55px}
|
|
55
58
|
.organizer-masthead{font-size:24px;max-width:36vw}.organizer-topbar h1{font-size:10px}.organizer-account-header{display:flex}.organizer-header-actions{gap:4px;max-width:35%}
|
|
@@ -13297,7 +13297,7 @@ function buildUiAnalysisReindexAssignmentPlan({
|
|
|
13297
13297
|
createdBy: actorLabel,
|
|
13298
13298
|
createdAt: now,
|
|
13299
13299
|
updatedAt: now,
|
|
13300
|
-
newsroomFeedKey: "
|
|
13300
|
+
newsroomFeedKey: "operations",
|
|
13301
13301
|
metadata,
|
|
13302
13302
|
};
|
|
13303
13303
|
const event: AssignmentEventRecord = {
|
package/lib/newsroom-board.ts
CHANGED
|
@@ -114,6 +114,31 @@ export function assignmentBoardConfig(issues: readonly NativeBoardIssue[]): Kanb
|
|
|
114
114
|
.map(status => ({ key: `${ASSIGNMENT_BOARD_TYPE}/${status}`, name: assignmentStatusLabel(status) })));
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function matchesDesk(candidate: string | null | undefined, deskId: string): boolean {
|
|
118
|
+
return Boolean(candidate) && candidate!.trim().toLowerCase() === deskId.trim().toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function deskContainsContentItem(version: ContentItemVersionRecord, deskId: string): boolean {
|
|
122
|
+
return matchesDesk(version.section, deskId);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function deskContainsAssignment(assignment: AssignmentRecord, deskId: string): boolean {
|
|
126
|
+
return matchesDesk(assignment.sectionId, deskId) || matchesDesk(assignment.sectionKey, deskId);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** A desk board shows stories and assignments side by side; each keeps its own columns and the type filter narrows to one. */
|
|
130
|
+
export function deskBoardConfig(issues: readonly NativeBoardIssue[]): KanbanConfig {
|
|
131
|
+
const storiesConfig = contentItemBoardConfig(issues.filter(issue => issue.kind === "contentItem"));
|
|
132
|
+
const assignmentsConfig = assignmentBoardConfig(issues.filter(issue => issue.kind === "assignment"));
|
|
133
|
+
return {
|
|
134
|
+
...storiesConfig,
|
|
135
|
+
types: [...(storiesConfig.types ?? []), ...(assignmentsConfig.types ?? [])],
|
|
136
|
+
statuses: [...storiesConfig.statuses, ...assignmentsConfig.statuses],
|
|
137
|
+
categories: storiesConfig.categories,
|
|
138
|
+
workflows: { ...storiesConfig.workflows, ...assignmentsConfig.workflows },
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
117
142
|
export function canReadNewsroomBoard(phase: string | undefined): boolean {
|
|
118
143
|
return phase === "ready";
|
|
119
144
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export interface ProspectusPublishingAccountChoice {
|
|
2
|
+
accountId: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
role: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type ProspectusAccountState =
|
|
8
|
+
| { status: "unavailable" }
|
|
9
|
+
| { status: "signedOut"; signInUrl: string }
|
|
10
|
+
| {
|
|
11
|
+
status: "signedIn";
|
|
12
|
+
publishingAccounts: ProspectusPublishingAccountChoice[];
|
|
13
|
+
selectedAccountId: string | null;
|
|
14
|
+
cyclotronLabAccess: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type FetchImplementation = (input: string, init?: RequestInit) => Promise<Response>;
|
|
18
|
+
|
|
19
|
+
const SESSION_PATH = "/api/auth/prospectus/session";
|
|
20
|
+
const SELECTION_PATH = "/api/auth/prospectus/selection";
|
|
21
|
+
const SIGN_IN_PATH = "/api/auth/prospectus/sign-in";
|
|
22
|
+
|
|
23
|
+
export function prospectusSignInUrl(returnPath: string): string {
|
|
24
|
+
return `${SIGN_IN_PATH}?returnTo=${encodeURIComponent(returnPath)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function loadProspectusAccountState(
|
|
28
|
+
returnPath: string,
|
|
29
|
+
fetchImplementation: FetchImplementation = fetch,
|
|
30
|
+
): Promise<ProspectusAccountState> {
|
|
31
|
+
let response: Response;
|
|
32
|
+
try {
|
|
33
|
+
response = await fetchImplementation(SESSION_PATH, { credentials: "same-origin", cache: "no-store" });
|
|
34
|
+
} catch {
|
|
35
|
+
return { status: "unavailable" };
|
|
36
|
+
}
|
|
37
|
+
if (!response.ok) return { status: "unavailable" };
|
|
38
|
+
const body = await response.json().catch(() => null) as
|
|
39
|
+
| { signedIn?: boolean; publishingAccounts?: ProspectusPublishingAccountChoice[]; selectedAccountId?: string | null; cyclotronLabAccess?: boolean }
|
|
40
|
+
| null;
|
|
41
|
+
if (!body) return { status: "unavailable" };
|
|
42
|
+
if (!body.signedIn) return { status: "signedOut", signInUrl: prospectusSignInUrl(returnPath) };
|
|
43
|
+
return {
|
|
44
|
+
status: "signedIn",
|
|
45
|
+
publishingAccounts: Array.isArray(body.publishingAccounts) ? body.publishingAccounts : [],
|
|
46
|
+
selectedAccountId: body.selectedAccountId ?? null,
|
|
47
|
+
cyclotronLabAccess: body.cyclotronLabAccess === true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function selectProspectusAccount(
|
|
52
|
+
accountId: string,
|
|
53
|
+
fetchImplementation: FetchImplementation = fetch,
|
|
54
|
+
): Promise<{ selectedAccountId: string; role: string } | { error: string }> {
|
|
55
|
+
const response = await fetchImplementation(SELECTION_PATH, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
credentials: "same-origin",
|
|
58
|
+
headers: { "content-type": "application/json" },
|
|
59
|
+
body: JSON.stringify({ accountId }),
|
|
60
|
+
});
|
|
61
|
+
const body = await response.json().catch(() => null) as { selectedAccountId?: string; role?: string; message?: string } | null;
|
|
62
|
+
if (!response.ok || !body?.selectedAccountId) return { error: body?.message ?? "That account could not be selected." };
|
|
63
|
+
return { selectedAccountId: body.selectedAccountId, role: body.role ?? "" };
|
|
64
|
+
}
|
package/lib/site-stack.ts
CHANGED
|
@@ -149,7 +149,7 @@ export const PILOBOL_US_THEME_PACK_TOKENS: ThemePackTokens = {
|
|
|
149
149
|
};
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
|
-
* Anthus palette, read off the live Gatsby site at `AnthusAI/Anth.us`
|
|
152
|
+
* Anthus palette, read off the live Gatsby site at `AnthusAI/Anth.us-legacy-Gatsby`
|
|
153
153
|
* (`src/styles/variables.scss` `:root`). Colors only — Shadcn owns typography.
|
|
154
154
|
* Mapping from that file's names to the pack's slots:
|
|
155
155
|
*
|
package/package.json
CHANGED
|
@@ -220,6 +220,7 @@
|
|
|
220
220
|
"./lib/presentation-footer": "./lib/presentation-footer.ts",
|
|
221
221
|
"./lib/pretext-layout": "./lib/pretext-layout.ts",
|
|
222
222
|
"./lib/priming-proof-report": "./lib/priming-proof-report.ts",
|
|
223
|
+
"./lib/prospectus-account-client": "./lib/prospectus-account-client.ts",
|
|
223
224
|
"./lib/prospectus-publication-access": "./lib/prospectus-publication-access.ts",
|
|
224
225
|
"./lib/prospectus-session": "./lib/prospectus-session.ts",
|
|
225
226
|
"./lib/prospectus-sign-in-routes": "./lib/prospectus-sign-in-routes.ts",
|
|
@@ -351,7 +352,7 @@
|
|
|
351
352
|
"optional": true
|
|
352
353
|
}
|
|
353
354
|
},
|
|
354
|
-
"version": "1.
|
|
355
|
+
"version": "1.8.0",
|
|
355
356
|
"dependencies": {
|
|
356
357
|
"@aws-amplify/adapter-nextjs": "^1.7.3",
|
|
357
358
|
"@aws-crypto/sha256-js": "5.2.0",
|
package/routes.manifest.json
CHANGED