@anthusai/papyrus 5.42.0-next.2 → 5.42.0-next.4
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/amplify/data/schema.js +2 -0
- package/amplify/data/schema.ts +2 -0
- package/amplify/functions/monitoring-view/schema.js +6 -0
- package/amplify/functions/monitoring-view/schema.ts +6 -0
- package/amplify/functions/monitoring-view/wire.js +1 -0
- package/amplify/functions/monitoring-view/wire.ts +1 -0
- package/amplify/functions/proposal-actions/handler.py +137 -0
- package/amplify/functions/proposal-actions/resource.js +15 -0
- package/amplify/functions/proposal-actions/resource.ts +22 -0
- package/amplify/functions/proposal-actions/schema.js +106 -0
- package/amplify/functions/proposal-actions/schema.ts +108 -0
- package/amplify/functions/proposal-actions/wire.js +10 -0
- package/amplify/functions/proposal-actions/wire.ts +13 -0
- package/amplify/plugins/reader-steering/function/handler.py +12 -69
- package/amplify/plugins/reader-steering/papyrus.plugin.js +5 -5
- package/amplify/plugins/reader-steering/papyrus.plugin.ts +5 -5
- package/amplify/plugins/reader-steering/schema.js +0 -95
- package/amplify/plugins/reader-steering/schema.ts +0 -95
- package/amplify/site-backend.js +4 -0
- package/amplify/site-backend.ts +4 -0
- package/app/globals.css +2 -35
- package/app/tailwind.css +2 -0
- package/components/monitoring-overview-view.tsx +7 -5
- package/components/monitoring-panel.tsx +33 -3
- package/components/newsroom-references-view.tsx +2 -3
- package/components/proposals-panel.tsx +110 -0
- package/components/proposals-view.tsx +93 -0
- package/components/topic-steering-workspace.tsx +13 -2
- package/lib/monitoring-client.ts +11 -2
- package/lib/monitoring-overview-fixture.ts +1 -1
- package/lib/monitoring-overview.ts +1 -1
- package/lib/proposals-admin.ts +115 -0
- package/lib/relevance-decisions.ts +9 -0
- package/package.json +4 -1
- package/routes.manifest.json +1 -1
- /package/amplify/{plugins/reader-steering/function → functions/proposal-actions}/proposals.py +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import {
|
|
3
|
+
countProposals,
|
|
4
|
+
filterProposals,
|
|
5
|
+
kindLabel,
|
|
6
|
+
proposalMoves,
|
|
7
|
+
proposerLabel,
|
|
8
|
+
sortProposals,
|
|
9
|
+
stateLabel,
|
|
10
|
+
type ProposalFilter,
|
|
11
|
+
type ProposalMove,
|
|
12
|
+
type ProposalRow,
|
|
13
|
+
} from "../lib/proposals-admin";
|
|
14
|
+
|
|
15
|
+
const FILTERS: Array<{ key: ProposalFilter; label: string }> = [
|
|
16
|
+
{ key: "open", label: "Open" },
|
|
17
|
+
{ key: "draft", label: "Drafts" },
|
|
18
|
+
{ key: "in_steering", label: "In steering" },
|
|
19
|
+
{ key: "editor_review", label: "Editor review" },
|
|
20
|
+
{ key: "decided", label: "Decided" },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export type ProposalsViewProps = {
|
|
24
|
+
rows: ProposalRow[];
|
|
25
|
+
/** True when the site has the reader-steering plugin; without it nothing reader-facing is shown. */
|
|
26
|
+
readers: boolean;
|
|
27
|
+
filter: ProposalFilter;
|
|
28
|
+
busy: boolean;
|
|
29
|
+
status: string;
|
|
30
|
+
reason: string;
|
|
31
|
+
onFilter: (filter: ProposalFilter) => void;
|
|
32
|
+
onReason: (reason: string) => void;
|
|
33
|
+
onMove: (proposalId: string, move: ProposalMove) => void;
|
|
34
|
+
onRefresh: () => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function ProposalsView(props: ProposalsViewProps) {
|
|
38
|
+
const sorted = useMemo(() => sortProposals(props.rows), [props.rows]);
|
|
39
|
+
const counts = useMemo(() => countProposals(sorted), [sorted]);
|
|
40
|
+
const shown = filterProposals(sorted, props.filter);
|
|
41
|
+
return (
|
|
42
|
+
<section aria-labelledby="newsroom-proposals-title" data-news-desk-admin-panel="proposals">
|
|
43
|
+
<h3 id="newsroom-proposals-title">Proposals</h3>
|
|
44
|
+
<p>Story drafts and other changes that AI proposes. Editors pin, hold and decide them. Only editors and admins can see this.</p>
|
|
45
|
+
<p role="tablist" aria-label="Proposal views">
|
|
46
|
+
{FILTERS.filter((entry) => props.readers || entry.key !== "in_steering").map((entry) => (
|
|
47
|
+
<span key={entry.key}>
|
|
48
|
+
<button type="button" role="tab" aria-selected={props.filter === entry.key} onClick={() => props.onFilter(entry.key)}>
|
|
49
|
+
{entry.label} ({counts[entry.key]})
|
|
50
|
+
</button>{" "}
|
|
51
|
+
</span>
|
|
52
|
+
))}
|
|
53
|
+
<button type="button" onClick={props.onRefresh} disabled={props.busy}>Refresh</button>
|
|
54
|
+
</p>
|
|
55
|
+
<label>
|
|
56
|
+
Reason for the next move (optional){" "}
|
|
57
|
+
<input type="text" value={props.reason} maxLength={500} onChange={(event) => props.onReason(event.target.value)} />
|
|
58
|
+
</label>
|
|
59
|
+
<p role="status" aria-live="polite">{props.status}</p>
|
|
60
|
+
{shown.length === 0 ? (
|
|
61
|
+
<p>Nothing here.</p>
|
|
62
|
+
) : (
|
|
63
|
+
<ul>
|
|
64
|
+
{shown.map((row) => (
|
|
65
|
+
<li key={row.id} data-proposal-row={row.id}>
|
|
66
|
+
<article aria-label={row.title}>
|
|
67
|
+
<p>
|
|
68
|
+
<strong>{kindLabel(row.kind)}</strong>: {row.title} ({stateLabel(row.state, props.readers)}
|
|
69
|
+
{row.held ? ", on hold" : ""}) from {proposerLabel(row.proposer)} <time dateTime={row.createdAt}>{row.createdAt}</time>
|
|
70
|
+
</p>
|
|
71
|
+
{row.summary ? <p>{row.summary}</p> : null}
|
|
72
|
+
{row.body ? (
|
|
73
|
+
<details>
|
|
74
|
+
<summary>Read the draft</summary>
|
|
75
|
+
<pre style={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}>{row.body}</pre>
|
|
76
|
+
</details>
|
|
77
|
+
) : null}
|
|
78
|
+
<p>
|
|
79
|
+
{proposalMoves(row, props.readers).map((move) => (
|
|
80
|
+
<span key={move.label}>
|
|
81
|
+
<button type="button" disabled={props.busy} onClick={() => props.onMove(row.id, move)}>{move.label}</button>{" "}
|
|
82
|
+
</span>
|
|
83
|
+
))}
|
|
84
|
+
</p>
|
|
85
|
+
</article>
|
|
86
|
+
</li>
|
|
87
|
+
))}
|
|
88
|
+
</ul>
|
|
89
|
+
)}
|
|
90
|
+
</section>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
@@ -17,6 +17,7 @@ import { EditorialChecksAdminPanel } from "./editorial-checks-admin-panel";
|
|
|
17
17
|
import { AutomationPanel } from "./automation-panel";
|
|
18
18
|
import { MonitoringPanel } from "./monitoring-panel";
|
|
19
19
|
import { ModerationPanel } from "./moderation-panel";
|
|
20
|
+
import { ProposalsPanel } from "./proposals-panel";
|
|
20
21
|
import { NewsletterPanel } from "./newsletter-panel";
|
|
21
22
|
import { SignupEntriesPanel } from "./signup-entries-panel";
|
|
22
23
|
import { pluginSettings, type PluginOutputs } from "../lib/plugin-settings";
|
|
@@ -256,7 +257,7 @@ type AssignmentAction = "claim" | "release" | "complete" | "cancel" | "reopen" |
|
|
|
256
257
|
type ReportingPacketReviewDecision = "select" | "merge" | "brief" | "hold" | "kill";
|
|
257
258
|
type AssignmentDeskViewMode = "queue" | "budget";
|
|
258
259
|
type UserRoleAction = "grant" | "revoke";
|
|
259
|
-
type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "automations" | "delegations" | "signups" | "newsletter" | "moderation" | "editorial-checks" | "monitoring";
|
|
260
|
+
type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "automations" | "delegations" | "signups" | "newsletter" | "moderation" | "proposals" | "editorial-checks" | "monitoring";
|
|
260
261
|
export type NewsDeskTab = "overview" | "desks" | "topics" | "concepts" | "references" | "messages" | "assignments" | "administration" | "search";
|
|
261
262
|
type LexicalRuleScope = "publication" | "corpus" | "classifier" | "category";
|
|
262
263
|
type AnalysisReindexMode = "online-update" | "classifier-retrain" | "scoped-topic-rebuild" | "entity-graph-rebuild" | "generated-analysis-rebuild";
|
|
@@ -4398,6 +4399,15 @@ function AdministrationDeskView({
|
|
|
4398
4399
|
<span>Subscribers, issues and delivery for the site's email</span>
|
|
4399
4400
|
</Link>
|
|
4400
4401
|
) : null}
|
|
4402
|
+
<Link
|
|
4403
|
+
href={administrationPanelHref("proposals", isDemo)}
|
|
4404
|
+
data-news-desk-admin-nav="proposals"
|
|
4405
|
+
data-active={administrationPanel === "proposals" || undefined}
|
|
4406
|
+
onClick={() => onPanelChange("proposals")}
|
|
4407
|
+
>
|
|
4408
|
+
<strong>Proposals</strong>
|
|
4409
|
+
<span>Story drafts and changes that AI proposes</span>
|
|
4410
|
+
</Link>
|
|
4401
4411
|
{readerPluginEnabled() ? (
|
|
4402
4412
|
<Link
|
|
4403
4413
|
href={administrationPanelHref("moderation", isDemo)}
|
|
@@ -4413,6 +4423,7 @@ function AdministrationDeskView({
|
|
|
4413
4423
|
<div className="news-desk-settings-panel">
|
|
4414
4424
|
{administrationPanel === "signups" ? <SignupEntriesPanel /> : null}
|
|
4415
4425
|
{administrationPanel === "newsletter" ? <NewsletterPanel isAdmin={canManageUsers} isDemo={isDemo} /> : null}
|
|
4426
|
+
{administrationPanel === "proposals" ? <ProposalsPanel /> : null}
|
|
4416
4427
|
{administrationPanel === "moderation" ? <ModerationPanel /> : null}
|
|
4417
4428
|
{administrationPanel === "editorial-checks" ? <EditorialChecksAdminPanel /> : null}
|
|
4418
4429
|
{administrationPanel === "automations" ? <AutomationPanel isDemo={isDemo} /> : null}
|
|
@@ -12890,7 +12901,7 @@ function readerPluginEnabled(): boolean {
|
|
|
12890
12901
|
}
|
|
12891
12902
|
|
|
12892
12903
|
function normalizeAdministrationPanel(value: string | null | undefined, demo = false): AdministrationPanel {
|
|
12893
|
-
if (value === "policies" || value === "sections" || value === "procedures" || value === "automations" || value === "delegations" || value === "editorial-checks" || value === "monitoring") return value;
|
|
12904
|
+
if (value === "policies" || value === "sections" || value === "procedures" || value === "automations" || value === "delegations" || value === "editorial-checks" || value === "monitoring" || value === "proposals") return value;
|
|
12894
12905
|
if (value === "signups" && signupPluginEnabled()) return value;
|
|
12895
12906
|
if (value === "newsletter" && newsletterPluginEnabled(demo)) return value;
|
|
12896
12907
|
if (value === "moderation" && readerPluginEnabled()) return value;
|
package/lib/monitoring-client.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
MonitoringResult,
|
|
11
11
|
} from "./monitoring-overview";
|
|
12
12
|
|
|
13
|
-
type QueryField = "monitoringOverview" | "monitoringEmailSubmission" | "monitoringResolveEmailArticleLink" | "intakeEmailSubmissionReferences";
|
|
13
|
+
type QueryField = "monitoringOverview" | "monitoringEmailSubmission" | "monitoringResolveEmailArticleLink" | "intakeEmailSubmissionReferences" | "monitoringQueueEmailReference";
|
|
14
14
|
type GraphQLResult = { data?: Partial<Record<QueryField, unknown>>; errors?: { message?: string; errorType?: string }[] };
|
|
15
15
|
|
|
16
16
|
async function query<T>(field: QueryField, queryText: string, variables: Record<string, string | number>): Promise<T | { error: "forbidden" } | { state: "unavailable" }> {
|
|
@@ -74,10 +74,19 @@ export async function importMonitoringEmailCandidates(
|
|
|
74
74
|
} catch {
|
|
75
75
|
return { imported: references.length, references, error: "The import response could not be read. Completed links are safe to retry." };
|
|
76
76
|
}
|
|
77
|
-
if (!response?.ok || !Array.isArray(response.references)) {
|
|
77
|
+
if (!response?.ok || !Array.isArray(response.references) || response.references.length !== batch.length) {
|
|
78
78
|
return { imported: references.length, references, error: "The import was not accepted. Check your editor access and retry." };
|
|
79
79
|
}
|
|
80
80
|
references.push(...response.references);
|
|
81
|
+
for (const [index, reference] of response.references.entries()) {
|
|
82
|
+
const queued = await query<{ queued: boolean }>("monitoringQueueEmailReference",
|
|
83
|
+
`mutation MonitoringQueueEmailReference($input: AWSJSON!) { monitoringQueueEmailReference(input: $input) }`,
|
|
84
|
+
{ input: JSON.stringify({ receiptId, ...batch[index], referenceId: reference.id }) });
|
|
85
|
+
if (!("queued" in queued) || !queued.queued) {
|
|
86
|
+
return { imported: references.length, references,
|
|
87
|
+
error: "References were registered, but predictions could not be queued. Retry these selected links safely." };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
81
90
|
}
|
|
82
91
|
return { imported: references.length, references };
|
|
83
92
|
}
|
|
@@ -14,7 +14,7 @@ export const FIXTURE_MONITORING_OVERVIEW: MonitoringOverview = {
|
|
|
14
14
|
],
|
|
15
15
|
routerRuns: [{ runId: "q2", startedAt: "2026-10-21T02:37:00.000000Z", mode: "live", status: "stopped", stoppedBy: "request_ceiling_reached",
|
|
16
16
|
counts: { examined: 3, decided: 1, delivered: 1, deliveryFailed: 1, routed: 1 }, errorCount: 0 }],
|
|
17
|
-
router: { mode: "live", feedback: true, enabled: true
|
|
17
|
+
router: { mode: "live", feedback: true, enabled: true },
|
|
18
18
|
usage: { day: "2026-10-21", decisionsToday: 2, decisionsTotal: 3, historyComplete: true },
|
|
19
19
|
hiddenNotRelevant: 1,
|
|
20
20
|
items: [
|
|
@@ -121,7 +121,7 @@ export type MonitoringOverview = {
|
|
|
121
121
|
feeds: MonitoringFeed[];
|
|
122
122
|
runs: MonitoringRun[];
|
|
123
123
|
routerRuns: MonitoringRouterRun[];
|
|
124
|
-
router: { mode: string; feedback: boolean; enabled: boolean
|
|
124
|
+
router: { mode: string; feedback: boolean; enabled: boolean } | null;
|
|
125
125
|
usage: {
|
|
126
126
|
day: string;
|
|
127
127
|
decisionsToday: number;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The data side of the Administration "Proposals" panel, which every site has (the queue is core; the reader-steering
|
|
3
|
+
* plugin only adds reader votes). Rows come from the staff-readable `Proposal` model; an editor moves one with the
|
|
4
|
+
* `transitionProposal` mutation. Pure functions, no network, so the rules the panel shows can be tested.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type ProposalRow = {
|
|
8
|
+
id: string;
|
|
9
|
+
kind: string;
|
|
10
|
+
title: string;
|
|
11
|
+
summary?: string | null;
|
|
12
|
+
body?: string | null;
|
|
13
|
+
state: string;
|
|
14
|
+
proposer?: string | null;
|
|
15
|
+
held?: boolean | null;
|
|
16
|
+
pinned?: boolean | null;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ProposalFilter = "open" | "draft" | "in_steering" | "editor_review" | "decided";
|
|
21
|
+
/** A button: the states it moves through in order (one call each) or, for pin and hold, the `editProposal` action it runs. */
|
|
22
|
+
export type ProposalMove = { label: string; path?: string[]; edit?: "pin" | "unpin" | "hold" | "release"; state?: string };
|
|
23
|
+
|
|
24
|
+
const STATE_LABELS: Record<string, string> = {
|
|
25
|
+
draft: "Draft",
|
|
26
|
+
in_steering: "In steering",
|
|
27
|
+
editor_review: "Editor review",
|
|
28
|
+
approved: "Approved",
|
|
29
|
+
declined: "Declined",
|
|
30
|
+
overridden_by_editor: "Decided by an editor",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const KIND_LABELS: Record<string, string> = {
|
|
34
|
+
story_draft: "Story draft",
|
|
35
|
+
new_concept: "New concept",
|
|
36
|
+
rename: "Rename",
|
|
37
|
+
merge: "Merge",
|
|
38
|
+
split: "Split",
|
|
39
|
+
new_column: "New column",
|
|
40
|
+
element_symbol: "Element symbol",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const DECIDED = new Set(["approved", "declined", "overridden_by_editor"]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* What an editor may do next; the function enforces the same table (overriding the community needs a reason and is not offered
|
|
47
|
+
* here). On a site without reader-steering nobody steers, so a draft goes straight to editor review (two moves) and the
|
|
48
|
+
* "in steering" wording never shows.
|
|
49
|
+
*/
|
|
50
|
+
function stateMoves(state: string, readers: boolean): ProposalMove[] {
|
|
51
|
+
if (state === "draft") {
|
|
52
|
+
return readers
|
|
53
|
+
? [{ label: "Send to steering", path: ["in_steering"] }, { label: "Decline", path: ["declined"] }]
|
|
54
|
+
: [{ label: "Move to editor review", path: ["in_steering", "editor_review"] }, { label: "Decline", path: ["declined"] }];
|
|
55
|
+
}
|
|
56
|
+
if (state === "in_steering") return [{ label: "Move to editor review", path: ["editor_review"] }];
|
|
57
|
+
if (state === "editor_review") return [{ label: "Approve", path: ["approved"] }, { label: "Decline", path: ["declined"] }];
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const PROPOSAL_FIELDS = "id kind title summary body state proposer held pinned createdAt";
|
|
62
|
+
export const LIST_PROPOSALS = `query Proposals($nextToken: String) { listProposals(limit: 500, nextToken: $nextToken) { items { ${PROPOSAL_FIELDS} } nextToken } }`;
|
|
63
|
+
export const EDIT_PROPOSAL = `mutation Edit($proposalId: String!, $action: String!) { editProposal(proposalId: $proposalId, action: $action) { status id error message } }`;
|
|
64
|
+
export const TRANSITION_PROPOSAL = `mutation Move($proposalId: String!, $state: String!, $reason: String) { transitionProposal(proposalId: $proposalId, state: $state, reason: $reason) { status id error message } }`;
|
|
65
|
+
|
|
66
|
+
export function stateLabel(state: string, readers = true): string {
|
|
67
|
+
if (!readers && state === "in_steering") return "Waiting for review";
|
|
68
|
+
return STATE_LABELS[state] ?? state;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function kindLabel(kind: string): string {
|
|
72
|
+
return KIND_LABELS[kind] ?? kind;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** AI-filed proposals say "AI"; a reader's proposal says "Reader" (their id is never shown). */
|
|
76
|
+
export function proposerLabel(proposer: string | null | undefined): string {
|
|
77
|
+
return proposer === "ai" ? "AI" : proposer ? "Reader" : "Unknown";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function proposalMoves(row: Pick<ProposalRow, "state" | "held" | "pinned">, readers = true): ProposalMove[] {
|
|
81
|
+
const edits: ProposalMove[] = [
|
|
82
|
+
{ label: row.pinned ? "Unpin" : "Pin", edit: row.pinned ? "unpin" : "pin" },
|
|
83
|
+
...(DECIDED.has(row.state) && !row.held ? [] : [{ label: row.held ? "Release hold" : "Hold", edit: (row.held ? "release" : "hold") as "release" | "hold" }]),
|
|
84
|
+
];
|
|
85
|
+
// A proposal on hold refuses every state change until it is released.
|
|
86
|
+
return [...(row.held ? [] : stateMoves(row.state, readers)), ...edits];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function sortProposals(rows: ProposalRow[]): ProposalRow[] {
|
|
90
|
+
return [...rows].sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function filterProposals(rows: ProposalRow[], filter: ProposalFilter): ProposalRow[] {
|
|
94
|
+
if (filter === "open") return rows.filter((row) => !DECIDED.has(row.state));
|
|
95
|
+
if (filter === "decided") return rows.filter((row) => DECIDED.has(row.state));
|
|
96
|
+
return rows.filter((row) => row.state === filter);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function countProposals(rows: ProposalRow[]): Record<ProposalFilter, number> {
|
|
100
|
+
return {
|
|
101
|
+
open: filterProposals(rows, "open").length,
|
|
102
|
+
draft: filterProposals(rows, "draft").length,
|
|
103
|
+
in_steering: filterProposals(rows, "in_steering").length,
|
|
104
|
+
editor_review: filterProposals(rows, "editor_review").length,
|
|
105
|
+
decided: filterProposals(rows, "decided").length,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type ProposalReceipt = { status?: string | null; id?: string | null; error?: string | null; message?: string | null };
|
|
110
|
+
|
|
111
|
+
export function proposalReceiptMessage(receipt: ProposalReceipt | null | undefined): string {
|
|
112
|
+
if (!receipt) return "The action did not answer.";
|
|
113
|
+
if (receipt.status === "ok") return "Done.";
|
|
114
|
+
return receipt.message || receipt.error || "The action was refused.";
|
|
115
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SemanticRelationRecord } from "./category-repository";
|
|
2
|
+
import { normalizeReferenceCurationStatus } from "./reference-policy";
|
|
2
3
|
|
|
3
4
|
export const RELEVANCE_RELATION = "relevance_decision_is";
|
|
4
5
|
|
|
@@ -162,3 +163,11 @@ export function curationForRelevanceReview(decision: RelevanceDecisionView, revi
|
|
|
162
163
|
shareable: review.shareable,
|
|
163
164
|
};
|
|
164
165
|
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* A pending reference with a current cyclotron suggestion is waiting for an editor's answer,
|
|
169
|
+
* whether or not the cyclotron flagged the decision itself for review.
|
|
170
|
+
*/
|
|
171
|
+
export function awaitsEditorReview(curationStatus: unknown, decision: RelevanceDecisionView | undefined): boolean {
|
|
172
|
+
return Boolean(decision) && normalizeReferenceCurationStatus(curationStatus) === "pending";
|
|
173
|
+
}
|
package/package.json
CHANGED
|
@@ -127,6 +127,8 @@
|
|
|
127
127
|
"./components/presentation-header": "./components/presentation-header.tsx",
|
|
128
128
|
"./components/presentation-rhythm-hrule": "./components/presentation-rhythm-hrule.tsx",
|
|
129
129
|
"./components/priming-proof-page": "./components/priming-proof-page.tsx",
|
|
130
|
+
"./components/proposals-panel": "./components/proposals-panel.tsx",
|
|
131
|
+
"./components/proposals-view": "./components/proposals-view.tsx",
|
|
130
132
|
"./components/prospectus-account-chooser": "./components/prospectus-account-chooser.tsx",
|
|
131
133
|
"./components/publication-cyclotron-panel": "./components/publication-cyclotron-panel.tsx",
|
|
132
134
|
"./components/reader-auth-control": "./components/reader-auth-control.tsx",
|
|
@@ -289,6 +291,7 @@
|
|
|
289
291
|
"./lib/presentation-footer": "./lib/presentation-footer.ts",
|
|
290
292
|
"./lib/pretext-layout": "./lib/pretext-layout.ts",
|
|
291
293
|
"./lib/priming-proof-report": "./lib/priming-proof-report.ts",
|
|
294
|
+
"./lib/proposals-admin": "./lib/proposals-admin.ts",
|
|
292
295
|
"./lib/prospectus-account-client": "./lib/prospectus-account-client.ts",
|
|
293
296
|
"./lib/prospectus-publication-access": "./lib/prospectus-publication-access.ts",
|
|
294
297
|
"./lib/prospectus-session": "./lib/prospectus-session.ts",
|
|
@@ -463,7 +466,7 @@
|
|
|
463
466
|
"optional": true
|
|
464
467
|
}
|
|
465
468
|
},
|
|
466
|
-
"version": "5.42.0-next.
|
|
469
|
+
"version": "5.42.0-next.4",
|
|
467
470
|
"dependencies": {
|
|
468
471
|
"@aws-amplify/adapter-nextjs": "^1.7.3",
|
|
469
472
|
"@aws-crypto/sha256-js": "5.2.0",
|
package/routes.manifest.json
CHANGED
/package/amplify/{plugins/reader-steering/function → functions/proposal-actions}/proposals.py
RENAMED
|
File without changes
|