@anthusai/papyrus 1.4.0 → 1.5.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.
@@ -1,6 +1,7 @@
1
1
  import { resolveActiveSiteBrand } from "../../../lib/site-brand-tenant";
2
2
  import { notFound, redirect } from "next/navigation";
3
3
  import { toPublicNewsroomPath } from "../../../lib/newsroom-base-path";
4
+ import { contentItemBoardPathSegment } from "../../../lib/newsroom-board";
4
5
  import { NewsroomBoardView } from "../../../components/newsroom-board-view";
5
6
  import { NewsDeskPage } from "../../../components/news-desk-page";
6
7
  import type { NewsDeskTab } from "../../../components/topic-steering-workspace";
@@ -43,10 +44,11 @@ const NEWS_DESK_ROUTE_SECTIONS = new Set<NewsDeskTab>([
43
44
 
44
45
  export default async function NewsDeskSectionPage({ params, searchParams }: NewsDeskSectionPageProps) {
45
46
  const { section } = await params;
46
- if (section === "work") redirect(toPublicNewsroomPath("/newsroom/articles"));
47
+ if (section === "work") redirect(toPublicNewsroomPath(`/newsroom/${contentItemBoardPathSegment()}`));
47
48
  if (section === "users") redirect(toPublicNewsroomPath("/newsroom/administration/users"));
48
49
  if (section === "doctrine") redirect(toPublicNewsroomPath("/newsroom/administration/policies"));
49
50
  if (section === "desks" && resolveActiveSiteBrand().newsroomLayout !== "organizer") redirect(toPublicNewsroomPath("/newsroom/topics"));
51
+ if (section === contentItemBoardPathSegment() && resolveActiveSiteBrand().newsroomLayout === "organizer") return <NewsroomBoardView subject="contentItem" />;
50
52
  if (!NEWS_DESK_ROUTE_SECTIONS.has(section as NewsDeskTab)) notFound();
51
53
  if (section === "assignments" && resolveActiveSiteBrand().newsroomLayout === "organizer") {
52
54
  const resolved = await searchParams;
@@ -1,3 +1,6 @@
1
+ import { redirect } from "next/navigation";
2
+ import { contentItemBoardPathSegment } from "../../../lib/newsroom-board";
3
+ import { toPublicNewsroomPath } from "../../../lib/newsroom-base-path";
1
4
  import { NewsroomBoardView } from "../../../components/newsroom-board-view";
2
5
  import { resolveActiveSiteBrand } from "../../../lib/site-brand-tenant";
3
6
  import { NewsroomArticlesView } from "../../../components/newsroom-articles-view";
@@ -11,6 +14,10 @@ type NewsroomArticlesPageProps = {
11
14
  export default async function NewsroomArticlesPage({ searchParams }: NewsroomArticlesPageProps) {
12
15
  const resolved = await searchParams;
13
16
  const demoValue = Array.isArray(resolved?.demo) ? resolved?.demo[0] : resolved?.demo;
14
- if (resolveActiveSiteBrand().newsroomLayout === "organizer" && demoValue !== "1") return <NewsroomBoardView subject="contentItem" />;
17
+ if (resolveActiveSiteBrand().newsroomLayout === "organizer" && demoValue !== "1") {
18
+ const boardPathSegment = contentItemBoardPathSegment();
19
+ if (boardPathSegment !== "articles") redirect(toPublicNewsroomPath(`/newsroom/${boardPathSegment}`));
20
+ return <NewsroomBoardView subject="contentItem" />;
21
+ }
15
22
  return <NewsroomArticlesView demo={demoValue === "1"} />;
16
23
  }
@@ -93,19 +93,16 @@ export function NewsroomBoardView({ subject }: { subject: NewsroomBoardSubject }
93
93
  const issue = issues.find(item => item.id === selected);
94
94
  if (!allowed) return <NewsDeskAccessGate shell={session?.shell ?? null} />;
95
95
  return <NewsroomOpsShell activeTab={subject === "contentItem" ? "articles" : "assignments"} appTitle={SITE_BRAND.appTitle} backLabel={SITE_BRAND.backToHomeLabel} pageTitle={pageTitle}
96
- headerActions={subject === "contentItem" ? (
97
- <Link className={cn(buttonVariants({ size: "sm" }))} data-newsroom-article-new href={getNewsroomNavHref(newsroomHref("articles", "new"))}>
98
- <PlusIcon aria-hidden="true" />
99
- {`New ${getContentItemName().singular.toLowerCase()}`}
100
- </Link>
101
- ) : undefined}>
96
+ headerActions={<>
97
+ <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>
98
+ {subject === "contentItem" && (
99
+ <Link className={cn(buttonVariants({ size: "sm" }))} data-newsroom-article-new href={getNewsroomNavHref(newsroomHref("articles", "new"))}>
100
+ <PlusIcon aria-hidden="true" />
101
+ {`New ${getContentItemName().singular.toLowerCase()}`}
102
+ </Link>
103
+ )}
104
+ </>}>
102
105
  <div className="organizer-native-board papyrus-newsprint-board" data-color-mode={theme}>
103
- <div className="organizer-board-toolbar">
104
- <div role="group" aria-label="Record presentation"><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>
105
- </div>
106
- <p className="organizer-board-note">{subject === "contentItem"
107
- ? `Each ${getContentItemName().singular.toLowerCase()} appears once, in the column for its stage. Open one to use its existing editor.`
108
- : "Each assignment appears once, in the column for its status. Open one to use its existing workflow controls."}</p>
109
106
  {error && <NewsroomOpsStatusBanner tone="error">{error}</NewsroomOpsStatusBanner>}
110
107
  <p role="status" className="organizer-board-note">{loading ? "Loading records…" : `${issues.length} records loaded.`}</p>
111
108
  <div className="organizer-board-body" data-detail-open={Boolean(issue)}>
@@ -21,7 +21,7 @@ const ICONS = { overview: LayoutDashboard, articles: ClipboardList, references:
21
21
  function Navigation({ activePath, demo, admin, surface, onNavigate }: { activePath: string; demo: boolean; admin: boolean; surface: string; onNavigate?: () => void }) {
22
22
  const id = useId();
23
23
  const all = ORGANIZER_NAV_GROUPS.flatMap(group => group.items);
24
- const active = all.filter(item => activePath === item.path || (item.path !== "/newsroom" && activePath.startsWith(item.path + "/"))).sort((a, b) => b.path.length - a.path.length)[0]?.key;
24
+ const active = all.filter(item => [item.path, ...(item.alsoActiveOnPaths ?? [])].some(candidate => activePath === candidate || (candidate !== "/newsroom" && activePath.startsWith(candidate + "/")))).sort((a, b) => b.path.length - a.path.length)[0]?.key;
25
25
  return <nav className="organizer-destinations" aria-label={surface === "sidebar" ? "Newsroom" : "All workspaces"}>
26
26
  {ORGANIZER_NAV_GROUPS.map((group, index) => <section key={group.title} aria-labelledby={`${id}-${index}`}>
27
27
  <h3 id={`${id}-${index}`}>{group.title}</h3>
@@ -37,10 +37,9 @@ body:has(.newsroom-organizer)>[data-staging-banner]{flex-shrink:0}
37
37
  .organizer-bottom-nav{display:none}
38
38
  .organizer-main:has(.organizer-native-board){display:flex;overflow:hidden}
39
39
  .organizer-native-board{display:flex;flex-direction:column;flex:1;min-height:0;min-width:0;gap:12px}
40
- .organizer-board-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;flex-wrap:wrap}
41
- .organizer-board-toolbar [role=group]{display:flex;gap:8px;flex-wrap:wrap}
42
- .organizer-board-toolbar button{min-height:44px;padding:8px 12px;border:1px solid var(--org-line)}
43
- .organizer-board-toolbar button[aria-pressed=true]{background:var(--org-ink);color:var(--org-paper)}
40
+ .organizer-board-view-toggle{display:flex;gap:4px;flex-shrink:0}
41
+ .organizer-board-view-toggle button{min-height:36px;padding:4px 12px;border:1px solid var(--org-line)}
42
+ .organizer-board-view-toggle button[aria-pressed=true]{background:var(--org-ink);color:var(--org-paper)}
44
43
  .organizer-board-note{font-size:11px;line-height:1.5;margin:0;color:var(--org-muted)}
45
44
  .organizer-board-body{position:relative;display:grid;grid-template-columns:minmax(0,1fr);flex:1;min-width:0;min-height:0;gap:16px}
46
45
  .organizer-board-body[data-detail-open=true]{grid-template-columns:minmax(0,1fr) minmax(280px,360px)}
@@ -17,6 +17,10 @@ export interface NativeBoardIssue extends KanbanIssue {
17
17
  href: string;
18
18
  }
19
19
 
20
+ export function contentItemBoardPathSegment(): string {
21
+ return getContentItemName().plural.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "articles";
22
+ }
23
+
20
24
  export function contentItemBoardType(): string {
21
25
  return getContentItemName().singular.toLowerCase();
22
26
  }
@@ -1,14 +1,15 @@
1
+ import { contentItemBoardPathSegment } from "./newsroom-board";
1
2
  import { getContentItemName } from "./site-brand";
2
3
 
3
4
  const contentItemPluralLowercase = getContentItemName().plural.toLowerCase();
4
5
  export type OrganizerDestination = {
5
6
  key: string; label: string; description: string; path: string;
6
- access?: "admin"; primary?: boolean; unavailable?: string;
7
+ access?: "admin"; primary?: boolean; unavailable?: string; alsoActiveOnPaths?: readonly string[];
7
8
  };
8
9
  export const ORGANIZER_NAV_GROUPS: readonly { title: string; items: readonly OrganizerDestination[] }[] = [
9
10
  { title: "Editorial work", items: [
10
11
  { key: "overview", label: "Overview", description: "See the desk and choose where to begin.", path: "/newsroom", primary: true },
11
- { key: "articles", label: getContentItemName().plural, description: `Follow ${contentItemPluralLowercase} across their stages and open one to write or revise it.`, path: "/newsroom/articles", primary: true },
12
+ { key: "articles", label: getContentItemName().plural, description: `Follow ${contentItemPluralLowercase} across their stages and open one to write or revise it.`, path: `/newsroom/${contentItemBoardPathSegment()}`, alsoActiveOnPaths: ["/newsroom/articles"], primary: true },
12
13
  { key: "assignments", label: "Assignments", description: "Follow research and reporting work across its statuses and claim it.", path: "/newsroom/assignments" },
13
14
  { key: "messages", label: "Messages", description: "Read and reply to newsroom conversations.", path: "/newsroom/messages", primary: true },
14
15
  { key: "insights", label: "Insights", description: "Discuss findings and research threads.", path: "/newsroom/insights" },
package/package.json CHANGED
@@ -350,7 +350,7 @@
350
350
  "optional": true
351
351
  }
352
352
  },
353
- "version": "1.4.0",
353
+ "version": "1.5.0",
354
354
  "dependencies": {
355
355
  "@aws-amplify/adapter-nextjs": "^1.7.3",
356
356
  "@aws-crypto/sha256-js": "5.2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.4.0",
2
+ "version": "1.5.0",
3
3
  "routes": [
4
4
  {
5
5
  "file": "app/%5F_preview/[[...path]]/route.ts",