@the-portland-company/shell 0.25.0 → 0.26.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/CHANGELOG.md +16 -0
- package/dist/app-chrome.cjs +17 -3
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +28 -3
- package/dist/app-chrome.d.ts +28 -3
- package/dist/app-chrome.js +17 -3
- package/dist/app-chrome.js.map +1 -1
- package/package.json +1 -1
package/dist/app-chrome.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { NavItem, NavBreadcrumb, NavLinkComponent,
|
|
3
|
+
import { NavItem, NavBreadcrumb, NavLinkComponent, NavChromeFetcher, NavOrgOption, StandardNavIconKey, NavIcon, NavFooter, NavMode, NavDisplayMode, NavUserMenu } from './native.cjs';
|
|
4
4
|
import './topbarIconSlot-FunwEKO9.cjs';
|
|
5
5
|
import './theme-preference.cjs';
|
|
6
6
|
|
|
@@ -51,6 +51,29 @@ type PolitogyAppSession = {
|
|
|
51
51
|
*/
|
|
52
52
|
userName?: string | null;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Optional DATA plumbing for apps whose session/auth differs — never styling.
|
|
56
|
+
*
|
|
57
|
+
* Everything here is about WHERE data comes from (which fetcher, which org
|
|
58
|
+
* state, which routes an app owns), not what the chrome looks like. Adding a
|
|
59
|
+
* visual option to this type would re-open the drift this component exists to
|
|
60
|
+
* close.
|
|
61
|
+
*/
|
|
62
|
+
type PolitogyFrameData = {
|
|
63
|
+
/** Chrome-API fetcher override for apps that authenticate client-side. */
|
|
64
|
+
fetchJson?: NavChromeFetcher;
|
|
65
|
+
/** DevNotes wiring override for those same direct-auth apps. */
|
|
66
|
+
devNotes?: ChromeDevNotesConfig;
|
|
67
|
+
/** Controlled org state for apps that own active-org + switching. */
|
|
68
|
+
org?: ControlledOrgState;
|
|
69
|
+
/** App-owned children for a standard nav entry this app owns (Contacts). */
|
|
70
|
+
contactsItem?: {
|
|
71
|
+
href?: string;
|
|
72
|
+
children?: NavItem[];
|
|
73
|
+
};
|
|
74
|
+
/** Avatar fallback when the profile has no uploaded photo (memoji, default). */
|
|
75
|
+
fallbackAvatarUrl?: string | null;
|
|
76
|
+
};
|
|
54
77
|
type PolitogyBuildMeta = {
|
|
55
78
|
envLabel?: string;
|
|
56
79
|
version?: string;
|
|
@@ -58,7 +81,7 @@ type PolitogyBuildMeta = {
|
|
|
58
81
|
commitDate?: string;
|
|
59
82
|
commitAuthor?: string;
|
|
60
83
|
};
|
|
61
|
-
declare function PolitogyAppFrame({ app, pathname, search, linkComponent, session, onSignOut, buildMeta, children, }: {
|
|
84
|
+
declare function PolitogyAppFrame({ app, pathname, search, linkComponent, session, onSignOut, buildMeta, data, children, }: {
|
|
62
85
|
app: PolitogyAppKey;
|
|
63
86
|
/** Current path from the app's router (Next: usePathname()). */
|
|
64
87
|
pathname: string;
|
|
@@ -69,6 +92,8 @@ declare function PolitogyAppFrame({ app, pathname, search, linkComponent, sessio
|
|
|
69
92
|
session: PolitogyAppSession;
|
|
70
93
|
onSignOut: () => void;
|
|
71
94
|
buildMeta?: PolitogyBuildMeta;
|
|
95
|
+
/** Optional DATA plumbing (never styling) — see PolitogyFrameData. */
|
|
96
|
+
data?: PolitogyFrameData;
|
|
72
97
|
children: ReactNode;
|
|
73
98
|
}): react_jsx_runtime.JSX.Element;
|
|
74
99
|
|
|
@@ -178,4 +203,4 @@ declare function useChromeUserMenu({ userId, userName, email, }: {
|
|
|
178
203
|
*/
|
|
179
204
|
declare function useChromeMode(fallbackModeId?: 'relationship' | 'campaign' | 'petition'): NavMode;
|
|
180
205
|
|
|
181
|
-
export { type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppSession, type PolitogyBuildMeta, type ResolvedIdentity, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
|
|
206
|
+
export { type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppSession, type PolitogyBuildMeta, type PolitogyFrameData, type ResolvedIdentity, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
|
package/dist/app-chrome.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { NavItem, NavBreadcrumb, NavLinkComponent,
|
|
3
|
+
import { NavItem, NavBreadcrumb, NavLinkComponent, NavChromeFetcher, NavOrgOption, StandardNavIconKey, NavIcon, NavFooter, NavMode, NavDisplayMode, NavUserMenu } from './native.js';
|
|
4
4
|
import './topbarIconSlot-FunwEKO9.js';
|
|
5
5
|
import './theme-preference.js';
|
|
6
6
|
|
|
@@ -51,6 +51,29 @@ type PolitogyAppSession = {
|
|
|
51
51
|
*/
|
|
52
52
|
userName?: string | null;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Optional DATA plumbing for apps whose session/auth differs — never styling.
|
|
56
|
+
*
|
|
57
|
+
* Everything here is about WHERE data comes from (which fetcher, which org
|
|
58
|
+
* state, which routes an app owns), not what the chrome looks like. Adding a
|
|
59
|
+
* visual option to this type would re-open the drift this component exists to
|
|
60
|
+
* close.
|
|
61
|
+
*/
|
|
62
|
+
type PolitogyFrameData = {
|
|
63
|
+
/** Chrome-API fetcher override for apps that authenticate client-side. */
|
|
64
|
+
fetchJson?: NavChromeFetcher;
|
|
65
|
+
/** DevNotes wiring override for those same direct-auth apps. */
|
|
66
|
+
devNotes?: ChromeDevNotesConfig;
|
|
67
|
+
/** Controlled org state for apps that own active-org + switching. */
|
|
68
|
+
org?: ControlledOrgState;
|
|
69
|
+
/** App-owned children for a standard nav entry this app owns (Contacts). */
|
|
70
|
+
contactsItem?: {
|
|
71
|
+
href?: string;
|
|
72
|
+
children?: NavItem[];
|
|
73
|
+
};
|
|
74
|
+
/** Avatar fallback when the profile has no uploaded photo (memoji, default). */
|
|
75
|
+
fallbackAvatarUrl?: string | null;
|
|
76
|
+
};
|
|
54
77
|
type PolitogyBuildMeta = {
|
|
55
78
|
envLabel?: string;
|
|
56
79
|
version?: string;
|
|
@@ -58,7 +81,7 @@ type PolitogyBuildMeta = {
|
|
|
58
81
|
commitDate?: string;
|
|
59
82
|
commitAuthor?: string;
|
|
60
83
|
};
|
|
61
|
-
declare function PolitogyAppFrame({ app, pathname, search, linkComponent, session, onSignOut, buildMeta, children, }: {
|
|
84
|
+
declare function PolitogyAppFrame({ app, pathname, search, linkComponent, session, onSignOut, buildMeta, data, children, }: {
|
|
62
85
|
app: PolitogyAppKey;
|
|
63
86
|
/** Current path from the app's router (Next: usePathname()). */
|
|
64
87
|
pathname: string;
|
|
@@ -69,6 +92,8 @@ declare function PolitogyAppFrame({ app, pathname, search, linkComponent, sessio
|
|
|
69
92
|
session: PolitogyAppSession;
|
|
70
93
|
onSignOut: () => void;
|
|
71
94
|
buildMeta?: PolitogyBuildMeta;
|
|
95
|
+
/** Optional DATA plumbing (never styling) — see PolitogyFrameData. */
|
|
96
|
+
data?: PolitogyFrameData;
|
|
72
97
|
children: ReactNode;
|
|
73
98
|
}): react_jsx_runtime.JSX.Element;
|
|
74
99
|
|
|
@@ -178,4 +203,4 @@ declare function useChromeUserMenu({ userId, userName, email, }: {
|
|
|
178
203
|
*/
|
|
179
204
|
declare function useChromeMode(fallbackModeId?: 'relationship' | 'campaign' | 'petition'): NavMode;
|
|
180
205
|
|
|
181
|
-
export { type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppSession, type PolitogyBuildMeta, type ResolvedIdentity, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
|
|
206
|
+
export { type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppSession, type PolitogyBuildMeta, type PolitogyFrameData, type ResolvedIdentity, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
|
package/dist/app-chrome.js
CHANGED
|
@@ -92,6 +92,7 @@ function PolitogyAppFrame({
|
|
|
92
92
|
session,
|
|
93
93
|
onSignOut,
|
|
94
94
|
buildMeta,
|
|
95
|
+
data,
|
|
95
96
|
children
|
|
96
97
|
}) {
|
|
97
98
|
const meta = POLITOGY_APPS[app];
|
|
@@ -99,6 +100,9 @@ function PolitogyAppFrame({
|
|
|
99
100
|
PolitogyAppChrome,
|
|
100
101
|
{
|
|
101
102
|
storagePrefix: meta.storagePrefix,
|
|
103
|
+
fetchJson: data?.fetchJson,
|
|
104
|
+
devNotes: data?.devNotes,
|
|
105
|
+
org: data?.org,
|
|
102
106
|
identity: {
|
|
103
107
|
userId: session.userId,
|
|
104
108
|
userName: session.userName ?? null,
|
|
@@ -115,6 +119,7 @@ function PolitogyAppFrame({
|
|
|
115
119
|
session,
|
|
116
120
|
onSignOut,
|
|
117
121
|
buildMeta,
|
|
122
|
+
data,
|
|
118
123
|
children
|
|
119
124
|
}
|
|
120
125
|
)
|
|
@@ -129,6 +134,7 @@ function FrameBody({
|
|
|
129
134
|
session,
|
|
130
135
|
onSignOut,
|
|
131
136
|
buildMeta,
|
|
137
|
+
data,
|
|
132
138
|
children
|
|
133
139
|
}) {
|
|
134
140
|
const meta = POLITOGY_APPS[app];
|
|
@@ -145,8 +151,14 @@ function FrameBody({
|
|
|
145
151
|
const activeCoreOrg = coreOrgs.find((o) => o.id === chrome?.orgId) ?? null;
|
|
146
152
|
const navDisplay = chrome?.navDisplay ?? "both";
|
|
147
153
|
const navItems = useMemo(
|
|
148
|
-
() => standardPolitogyNav({
|
|
149
|
-
|
|
154
|
+
() => standardPolitogyNav({
|
|
155
|
+
coreBaseUrl: POLITOGY_APPS.vrm.origin,
|
|
156
|
+
icons: standardNavIcons,
|
|
157
|
+
// An app that OWNS a standard nav entry (Contacts) nests its own routes
|
|
158
|
+
// under it. This is routing data, not chrome configuration.
|
|
159
|
+
contactsItem: data?.contactsItem
|
|
160
|
+
}),
|
|
161
|
+
[data?.contactsItem]
|
|
150
162
|
);
|
|
151
163
|
const breadcrumbs = useMemo(
|
|
152
164
|
() => deriveBreadcrumbs(navItems, pathname, { search, origin: meta.origin }),
|
|
@@ -185,7 +197,9 @@ function FrameBody({
|
|
|
185
197
|
user: {
|
|
186
198
|
name: identity.userName,
|
|
187
199
|
email: identity.email,
|
|
188
|
-
|
|
200
|
+
// The chrome API's avatar wins; the app may supply a fallback chain
|
|
201
|
+
// (e.g. a stored memoji) for users with no uploaded photo.
|
|
202
|
+
avatarUrl: identity.avatarUrl ?? data?.fallbackAvatarUrl ?? null,
|
|
189
203
|
role: session.role
|
|
190
204
|
},
|
|
191
205
|
userMenu,
|
package/dist/app-chrome.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/breadcrumbs.ts","../src/lib/identity.ts","../src/app-chrome/frame.tsx","../src/app-chrome/index.tsx"],"names":["trail","useMemo","jsx"],"mappings":"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","file":"app-chrome.js","sourcesContent":["// Breadcrumb derivation — pure, dependency-free logic (no React, no DevNotes),\n// so it is importable from a server module and testable in isolation.\n//\n// The standard is Dashboard > Sub > Item (HITL 2026-07-23), derived from the\n// same nav tree the sidebar renders. Apps previously hand-built their own\n// trails and disagreed: forms rendered \"Forms > Dashboard\" while polls rendered\n// a lone \"Polls\" crumb under a \"Polls\" title (\"Polls / Polls\").\n\nimport type { NavBreadcrumb, NavItem } from '../native/index'\n\nexport function deriveBreadcrumbs(\n items: NavItem[],\n pathname: string,\n opts?: { search?: string; origin?: string },\n): NavBreadcrumb[] {\n const search = opts?.search ?? (typeof window !== 'undefined' ? window.location.search : '')\n const origin = opts?.origin ?? (typeof window !== 'undefined' ? window.location.origin : '')\n\n // Same-origin comparison: cross-app hrefs are absolute, local ones relative.\n const matches = (href?: string): boolean => {\n if (!href) return false\n let path = href\n let query = ''\n if (/^https?:\\/\\//i.test(href)) {\n try {\n const u = new URL(href)\n if (origin && u.origin !== origin) return false\n path = u.pathname\n query = u.search\n } catch {\n return false\n }\n } else {\n const qi = href.indexOf('?')\n if (qi >= 0) {\n path = href.slice(0, qi) || '/'\n query = href.slice(qi)\n }\n }\n if (path !== pathname) return false\n if (!query) return true\n const want = new URLSearchParams(query)\n const have = new URLSearchParams(search)\n for (const [k, v] of want) if (have.get(k) !== v) return false\n return true\n }\n\n // Depth-first walk for the deepest item owning the current route.\n const walk = (nodes: NavItem[], trail: NavItem[]): NavItem[] | null => {\n for (const node of nodes) {\n const here = [...trail, node]\n if (node.children?.length) {\n const deeper = walk(node.children, here)\n if (deeper) return deeper\n }\n if (matches(node.href)) return here\n }\n return null\n }\n\n const trail = walk(items, [])\n // Always anchored at Dashboard; the last crumb is the current location and\n // carries no href (the shell marks it aria-current=\"page\").\n const dashboard = items.find((i) => i.label === 'Dashboard')\n const crumbs: NavBreadcrumb[] = [{ label: 'Dashboard', href: dashboard?.href }]\n if (!trail) return crumbs\n for (const node of trail) {\n if (node.label === 'Dashboard') continue\n crumbs.push({ label: node.label, href: node.href })\n }\n const last = crumbs[crumbs.length - 1]\n if (last) delete (last as { href?: string }).href\n return crumbs\n}\n","// Identity resolution — pure logic, shared by the chrome hook.\n//\n// The chrome API (VRM-core profile) is the AUTHORITY for a user's display name;\n// what an app knows from its own session token is a pre-hydration fallback. An\n// email is never a name: forms passed `user.name ?? user.email`, so a user with\n// no name set had their email rendered as their name in the top bar and the\n// shell's \"add your name\" affordance never fired (HITL 2026-07-23).\n\nexport type ResolvedIdentity = {\n userName: string | null\n email: string | null\n avatarUrl: string | null\n loaded: boolean\n}\n\n/** True when a \"name\" is really just an email address. */\nexport function looksLikeEmail(value: string | null | undefined): boolean {\n return typeof value === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value.trim())\n}\n\nexport function resolveIdentity(input: {\n apiName?: string | null\n fallbackName?: string | null\n email?: string | null\n avatarUrl?: string | null\n loaded?: boolean\n}): ResolvedIdentity {\n const apiName = looksLikeEmail(input.apiName) ? null : input.apiName ?? null\n const fallbackName = looksLikeEmail(input.fallbackName) ? null : input.fallbackName ?? null\n return {\n userName: apiName ?? fallbackName,\n email: input.email ?? null,\n avatarUrl: input.avatarUrl ?? null,\n loaded: input.loaded ?? false,\n }\n}\n","// ---------------------------------------------------------------------------\n// PolitogyAppFrame — ONE component that renders the entire app frame.\n//\n// Why this exists (HITL directive 2026-07-23): shipping the same npm package at\n// the same version is necessary but NOT sufficient. The package exposed\n// PRIMITIVES (NavAppShell + a dozen hooks) and every app hand-assembled them in\n// its own ~150-line Shell.tsx. Identical inputs were never guaranteed, so the\n// outputs diverged — measured on production: forms rendered its user's EMAIL as\n// their name, polls' breadcrumb read \"Polls / Polls\", forms and surveys pinned\n// the mode pill to a hardcoded \"relationship\", and each app declared its own\n// chrome CSS variables (surveys' accent was #C8102E where everyone else was\n// #E63946).\n//\n// The fix is to stop exporting assembly as an app responsibility. An app now\n// renders:\n//\n// <PolitogyAppFrame app=\"forms\" pathname={pathname} linkComponent={Link}\n// session={{ userId, email, role, accountName, accountId }}\n// onSignOut={signOut}>\n// {children}\n// </PolitogyAppFrame>\n//\n// and passes NOTHING about the chrome: no nav, no breadcrumbs, no title, no\n// mode, no user menu, no footer, no theme. Those are derived here, identically,\n// for every app. Anything an app cannot pass is something it cannot drift on.\n// ---------------------------------------------------------------------------\n\n'use client'\n\nimport { useMemo, type ReactNode } from 'react'\n\nimport {\n NavAppShell,\n standardPolitogyNav,\n type NavLinkComponent,\n} from '../native/index'\nimport {\n ChromeTopBarActions,\n PolitogyAppChrome,\n standardNavIcons,\n useChromeFooter,\n useChromeIdentity,\n useChromeMode,\n useChromeState,\n useChromeUserMenu,\n} from './index'\nimport { deriveBreadcrumbs } from '../lib/breadcrumbs'\n\n/** Every app that renders the shared chrome. Keyed by its subdomain. */\nexport type PolitogyAppKey = 'vrm' | 'contacts' | 'forms' | 'surveys' | 'polls' | 'petitions'\n\n/**\n * The per-app registry — the ONLY place app identity lives. A new app adds a\n * row here and inherits every chrome behavior; it never re-derives one.\n */\nexport const POLITOGY_APPS: Record<\n PolitogyAppKey,\n { title: string; origin: string; storagePrefix: string }\n> = {\n vrm: { title: 'Politogy VRM', origin: 'https://app.politogyvrm.com', storagePrefix: 'vrm' },\n contacts: { title: 'Contacts', origin: 'https://contacts.politogyvrm.com', storagePrefix: 'contacts' },\n forms: { title: 'Forms', origin: 'https://forms.politogyvrm.com', storagePrefix: 'forms' },\n surveys: { title: 'Surveys', origin: 'https://surveys.politogyvrm.com', storagePrefix: 'surveys' },\n polls: { title: 'Polls', origin: 'https://polls.politogyvrm.com', storagePrefix: 'polls' },\n petitions: { title: 'Petitions', origin: 'https://petitions.politogyvrm.com', storagePrefix: 'petitions' },\n}\n\nexport type PolitogyAppSession = {\n /** Stable IdP subject (`sub`). */\n userId: string\n email: string | null\n /** The app's own role string — feeds DevNotes admin gating. */\n role: string\n /** The app's own workspace label, used only until core orgs resolve. */\n accountName: string\n accountId: string\n /**\n * Pre-hydration display name ONLY. The chrome API is the authority; an email\n * passed here is discarded rather than rendered as a name.\n */\n userName?: string | null\n}\n\nexport type PolitogyBuildMeta = {\n envLabel?: string\n version?: string\n commit?: string\n commitDate?: string\n commitAuthor?: string\n}\n\nexport function PolitogyAppFrame({\n app,\n pathname,\n search,\n linkComponent,\n session,\n onSignOut,\n buildMeta,\n children,\n}: {\n app: PolitogyAppKey\n /** Current path from the app's router (Next: usePathname()). */\n pathname: string\n /** Current query string, when the app routes by query (forms' ?tab=). */\n search?: string\n /** The app's routing primitive (Next: <Link>). */\n linkComponent: NavLinkComponent\n session: PolitogyAppSession\n onSignOut: () => void\n buildMeta?: PolitogyBuildMeta\n children: ReactNode\n}) {\n const meta = POLITOGY_APPS[app]\n return (\n <PolitogyAppChrome\n storagePrefix={meta.storagePrefix}\n identity={{\n userId: session.userId,\n userName: session.userName ?? null,\n email: session.email,\n role: session.role,\n }}\n >\n <FrameBody\n app={app}\n pathname={pathname}\n search={search}\n linkComponent={linkComponent}\n session={session}\n onSignOut={onSignOut}\n buildMeta={buildMeta}\n >\n {children}\n </FrameBody>\n </PolitogyAppChrome>\n )\n}\n\nfunction FrameBody({\n app,\n pathname,\n search,\n linkComponent,\n session,\n onSignOut,\n buildMeta,\n children,\n}: {\n app: PolitogyAppKey\n pathname: string\n search?: string\n linkComponent: NavLinkComponent\n session: PolitogyAppSession\n onSignOut: () => void\n buildMeta?: PolitogyBuildMeta\n children: ReactNode\n}) {\n const meta = POLITOGY_APPS[app]\n const chrome = useChromeState()\n\n // Identity: chrome API wins, app claims are a fallback, an email is never a\n // name (that is what forms was rendering in the top right).\n const identity = useChromeIdentity({ userName: session.userName, email: session.email })\n const userMenu = useChromeUserMenu({\n userId: session.userId,\n userName: identity.userName,\n email: identity.email,\n })\n const footer = useChromeFooter(buildMeta ?? {})\n // Entitlement + user-selection driven. No app passes a mode.\n const mode = useChromeMode()\n\n const coreOrgs = chrome?.orgs ?? []\n const activeCoreOrg = coreOrgs.find((o) => o.id === chrome?.orgId) ?? null\n const navDisplay = chrome?.navDisplay ?? 'both'\n\n const navItems = useMemo(\n () => standardPolitogyNav({ coreBaseUrl: POLITOGY_APPS.vrm.origin, icons: standardNavIcons }),\n [],\n )\n\n // Dashboard › Sub › Item, derived from the same tree the sidebar renders.\n const breadcrumbs = useMemo(\n () => deriveBreadcrumbs(navItems, pathname, { search, origin: meta.origin }),\n [navItems, pathname, search, meta.origin],\n )\n\n return (\n <NavAppShell\n sidebar={{\n pathname,\n brand: {\n logoSrc: '/brand/logos/relationship-mode-logo.png',\n logoAlt: 'Politogy',\n badgeLabel: 'P',\n title: 'Politogy',\n },\n nav: {\n items:\n navDisplay === 'tabs'\n ? navItems.map(({ children: _sub, ...item }) => item)\n : navItems,\n },\n org: activeCoreOrg\n ? {\n name: activeCoreOrg.name,\n id: activeCoreOrg.id,\n logoUrl: activeCoreOrg.logoUrl,\n brandColor: activeCoreOrg.brandColor,\n organizations: coreOrgs,\n onSwitch: chrome?.setOrgId,\n }\n : { name: session.accountName, id: session.accountId },\n mode,\n footer,\n linkComponent,\n }}\n betaBanner={{}}\n topBar={{\n title: meta.title,\n breadcrumbs,\n user: {\n name: identity.userName,\n email: identity.email,\n avatarUrl: identity.avatarUrl,\n role: session.role,\n },\n userMenu,\n onSignOut,\n actions: <ChromeTopBarActions />,\n }}\n >\n {children}\n </NavAppShell>\n )\n}\n","// ---------------------------------------------------------------------------\n// App chrome wiring — the ONE shared implementation of the per-app glue that\n// previously lived as a copied ChromeWidgets.tsx in every Next.js feature app\n// (forms / surveys / polls / contacts). Copies drifted (org dropdown, badges);\n// this module makes that class of drift impossible: apps configure, never\n// re-implement (Reference/UI-Platform: consume via npm, never vendored).\n//\n// Provides:\n// <PolitogyAppChrome> — NavChromeProvider (same-origin proxy fetcher) +\n// chrome state (active core org, nav display) +\n// DevNotes provider, in one wrapper.\n// <ChromeTopBarActions> — the canonical top-bar cluster, in canonical order\n// (Fuel · Nav display · DevNotes · Support · Alerts\n// LAST, immediately left of My Account).\n// useChromeState / useChromeUserMenu / useIsSuperAdmin / useChromeFooter.\n//\n// The app still owns: its session (the /api/chrome proxy), page frames, build\n// metadata (env/commit/version), tab strips, and its own icons.\n// ---------------------------------------------------------------------------\n\n'use client'\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react'\nimport {\n NavChromeProvider,\n FuelAction,\n NotificationsAction,\n SupportAction,\n NavDisplayAction,\n TopBarWidgetSlot,\n useChromeOrgs,\n useThemePreference,\n type NavChromeFetcher,\n type NavDisplayMode,\n type NavOrgOption,\n type NavUserMenu,\n type NavFooter,\n type NavFooterLink,\n} from '../native/index'\nimport {\n DevNotesProvider,\n DevNotesMenu,\n createDevNotesClient,\n type DevNotesConfig,\n} from '@the-portland-company/devnotes'\nimport {\n FiActivity,\n FiBarChart2,\n FiBriefcase,\n FiClock,\n FiCreditCard,\n FiDollarSign,\n FiEdit3,\n FiFileText,\n FiFlag,\n FiGrid,\n FiHome,\n FiLayout,\n FiLifeBuoy,\n FiList,\n FiMail,\n FiMessageSquare,\n FiSend,\n FiSettings,\n FiShare2,\n FiUpload,\n FiUser,\n FiUsers,\n} from 'react-icons/fi'\nimport { useChromeMe, useNavChromeFetcher } from '../native/index'\nimport type { StandardNavIconKey } from '../native/standardNav'\nimport type { NavBreadcrumb, NavIcon, NavItem, NavMode } from '../native/index'\n\n/**\n * The canonical icon map for standardPolitogyNav — ONE definition so every\n * app renders identical glyphs (including the Organization Settings branch:\n * briefcase / settings / creditCard / dollarSign / share).\n */\nexport const standardNavIcons: Record<StandardNavIconKey, NavIcon> = {\n home: FiHome,\n users: FiUsers,\n mail: FiMail,\n messageSquare: FiMessageSquare,\n layout: FiLayout,\n activity: FiActivity,\n lifeBuoy: FiLifeBuoy,\n grid: FiGrid,\n list: FiList,\n upload: FiUpload,\n edit: FiEdit3,\n fileText: FiFileText,\n send: FiSend,\n clock: FiClock,\n user: FiUser,\n barChart: FiBarChart2,\n briefcase: FiBriefcase,\n settings: FiSettings,\n creditCard: FiCreditCard,\n dollarSign: FiDollarSign,\n share: FiShare2,\n}\n\nconst ORG_STORAGE_KEY = 'vrm.activeOrg'\n// The user's mode choice, shared by every app on the origin's localStorage.\nconst MODE_STORAGE_KEY = 'vrm.activeMode'\nconst NAV_DISPLAY_KEY = 'vrm.navDisplay'\nconst CORE_BASE_URL = 'https://app.politogyvrm.com'\n\nexport type ChromeIdentity = {\n userId: string\n userName: string | null\n email: string | null\n /** The APP's own role string (any casing) — feeds DevNotes admin gating. */\n role: string\n}\n\ntype ChromeState = {\n orgId: string | null\n setOrgId: (id: string) => void\n orgs: (NavOrgOption & { rawRole?: string | null })[]\n navDisplay: NavDisplayMode\n setNavDisplay: (m: NavDisplayMode) => void\n}\n\nconst ChromeStateContext = createContext<ChromeState | null>(null)\n\nexport function useChromeState(): ChromeState | null {\n return useContext(ChromeStateContext)\n}\n\nexport type ControlledOrgState = {\n orgId: string | null\n onSwitch: (id: string) => void\n /** Override the org list (e.g. the app's own session-scoped list). */\n orgs?: (NavOrgOption & { rawRole?: string | null })[]\n}\n\nfunction ChromeStateProvider({\n controlledOrg,\n children,\n}: {\n controlledOrg?: ControlledOrgState\n children: ReactNode\n}) {\n const { orgs } = useChromeOrgs()\n const [orgId, setOrgIdState] = useState<string | null>(null)\n const [navDisplay, setNavDisplayState] = useState<NavDisplayMode>('both')\n\n // Boot: restore persisted choices.\n useEffect(() => {\n try {\n const storedOrg = localStorage.getItem(ORG_STORAGE_KEY)\n if (storedOrg) setOrgIdState(storedOrg)\n const storedNav = localStorage.getItem(NAV_DISPLAY_KEY)\n if (storedNav === 'menu' || storedNav === 'tabs' || storedNav === 'both') {\n setNavDisplayState(storedNav)\n }\n } catch {\n /* storage unavailable */\n }\n }, [])\n\n // Once orgs resolve: validate the stored org, default to the first.\n useEffect(() => {\n if (!orgs || orgs.length === 0) return\n setOrgIdState((prev) => (prev && orgs.some((o) => o.id === prev) ? prev : orgs[0].id))\n }, [orgs])\n\n const setOrgId = useCallback((id: string) => {\n setOrgIdState(id)\n try {\n localStorage.setItem(ORG_STORAGE_KEY, id)\n } catch {\n /* best effort */\n }\n }, [])\n\n const setNavDisplay = useCallback((m: NavDisplayMode) => {\n setNavDisplayState(m)\n try {\n localStorage.setItem(NAV_DISPLAY_KEY, m)\n } catch {\n /* best effort */\n }\n }, [])\n\n const value = useMemo<ChromeState>(() => {\n // CONTROLLED mode (Contacts): the APP owns the active org + switching —\n // its switch does a full session re-scope. The shared widgets read the\n // app's state so there is exactly one org truth (unification directive).\n if (controlledOrg) {\n return {\n orgId: controlledOrg.orgId,\n setOrgId: controlledOrg.onSwitch,\n orgs:\n controlledOrg.orgs ??\n (orgs ?? []).map((o) => ({\n id: o.id,\n name: o.name,\n logoUrl: o.logoUrl,\n brandColor: o.brandColor,\n role: o.role,\n rawRole: o.rawRole ?? null,\n })),\n navDisplay,\n setNavDisplay,\n }\n }\n return {\n orgId,\n setOrgId,\n orgs: (orgs ?? []).map((o) => ({\n id: o.id,\n name: o.name,\n logoUrl: o.logoUrl,\n brandColor: o.brandColor,\n role: o.role,\n rawRole: o.rawRole ?? null,\n })),\n navDisplay,\n setNavDisplay,\n }\n }, [controlledOrg, orgId, setOrgId, orgs, navDisplay, setNavDisplay])\n\n return <ChromeStateContext.Provider value={value}>{children}</ChromeStateContext.Provider>\n}\n\nexport type ChromeDevNotesConfig = {\n /** Override the DevNotes API base (default: `${proxyBasePath}/devnotes`). */\n basePath?: string\n /** Client-side token supplier (default: empty — same-origin proxy injects). */\n getAuthToken?: () => Promise<string> | string\n /** Custom fetch (e.g. credentials:'include' against a cross-origin API). */\n fetch?: typeof globalThis.fetch\n}\n\nfunction AppDevNotes({\n storagePrefix,\n proxyBasePath,\n devNotes,\n identity,\n children,\n}: {\n storagePrefix: string\n proxyBasePath: string\n devNotes?: ChromeDevNotesConfig\n identity: ChromeIdentity\n children: ReactNode\n}) {\n const adapter = useMemo(\n () =>\n createDevNotesClient({\n basePath: devNotes?.basePath ?? `${proxyBasePath}/devnotes`,\n // Default: the same-origin proxy attaches the session's Bearer.\n getAuthToken: devNotes?.getAuthToken ?? (async () => ''),\n ...(devNotes?.fetch ? { fetch: devNotes.fetch } : {}),\n }),\n [proxyBasePath, devNotes?.basePath, devNotes?.getAuthToken, devNotes?.fetch],\n )\n\n const user = useMemo(\n () => ({\n id: identity.userId,\n email: identity.email ?? '',\n fullName: identity.userName ?? undefined,\n }),\n [identity.userId, identity.email, identity.userName],\n )\n\n const config = useMemo<DevNotesConfig>(\n () => ({\n storagePrefix,\n role: identity.role.toLowerCase() === 'admin' ? 'admin' : 'reporter',\n disableAi: true,\n getPagePath: () => window.location.pathname,\n }),\n [storagePrefix, identity.role],\n )\n\n return (\n <DevNotesProvider adapter={adapter} user={user} config={config}>\n {children}\n </DevNotesProvider>\n )\n}\n\n/**\n * The app-chrome root. Wrap the authenticated frame once:\n *\n * <PolitogyAppChrome storagePrefix=\"forms\" identity={{userId, userName, email, role}}>\n * <ShellFrame …/>\n * </PolitogyAppChrome>\n */\nexport function PolitogyAppChrome({\n storagePrefix,\n proxyBasePath = '/api/chrome',\n fetchJson: fetchJsonOverride,\n devNotes,\n org,\n identity,\n children,\n}: {\n storagePrefix: string\n proxyBasePath?: string\n /**\n * Override the same-origin proxy fetcher for apps that talk to the chrome\n * API directly with their own auth (Contacts sends a client-side Bearer).\n * Paths stay relative ('orgs', 'me', …) — the override owns base + auth.\n */\n fetchJson?: NavChromeFetcher\n /** DevNotes wiring override for direct-auth apps (Contacts). */\n devNotes?: ChromeDevNotesConfig\n /** Controlled org state — the app owns active-org + switching (Contacts). */\n org?: ControlledOrgState\n identity: ChromeIdentity\n children: ReactNode\n}) {\n const proxyFetchJson = useMemo<NavChromeFetcher>(\n () =>\n async (path, init) => {\n const res = await fetch(`${proxyBasePath}/${path}`, {\n method: init?.method ?? 'GET',\n headers: init?.headers,\n body: init?.body,\n })\n if (!res.ok) throw new Error(`chrome ${path}: ${res.status}`)\n if (res.status === 204) return null\n const text = await res.text()\n return text ? JSON.parse(text) : null\n },\n [proxyBasePath],\n )\n const fetchJson = fetchJsonOverride ?? proxyFetchJson\n\n return (\n <NavChromeProvider fetchJson={fetchJson}>\n <ChromeStateProvider controlledOrg={org}>\n <AppDevNotes\n storagePrefix={storagePrefix}\n proxyBasePath={proxyBasePath}\n devNotes={devNotes}\n identity={identity}\n >\n {children}\n </AppDevNotes>\n </ChromeStateProvider>\n </NavChromeProvider>\n )\n}\n\n/** The standard top-bar action cluster. The Alerts bell renders LAST so it\n * sits farthest right, immediately left of the My Account badge. */\nexport function ChromeTopBarActions() {\n const state = useChromeState()\n if (!state) return null\n return (\n <>\n <FuelAction orgId={state.orgId} />\n <NavDisplayAction value={state.navDisplay} onChange={state.setNavDisplay} />\n <TopBarWidgetSlot\n title=\"DevNotes\"\n description=\"Capture bugs and notes, filed straight to Forge.\"\n >\n <DevNotesMenu />\n </TopBarWidgetSlot>\n <SupportAction />\n <NotificationsAction orgId={state.orgId} />\n </>\n )\n}\n\n/** True when any core-org membership carries a super-admin tier. */\nexport function useIsSuperAdmin(): boolean {\n const state = useChromeState()\n return (state?.orgs ?? []).some((o) => /super/i.test(o.rawRole ?? ''))\n}\n\nconst FOOTER_LINKS: NavFooterLink[] = [\n { label: 'Privacy', href: `${CORE_BASE_URL}/privacy-policy`, external: true },\n { label: 'Terms', href: `${CORE_BASE_URL}/terms-and-conditions`, external: true },\n {\n label: 'Subscriptions',\n href: 'https://politogyvrm.com/subscription-cancellation-policy',\n external: true,\n },\n]\n\n/**\n * Sidebar footer: legal links for everyone; build metadata (env / commit /\n * version / date / author) ONLY for Super Administrators\n * (Reference/UI-Platform). Pass the app's build-time metadata in.\n */\nexport function useChromeFooter(meta: {\n envLabel?: string\n version?: string\n commit?: string\n commitDate?: string\n commitAuthor?: string\n}): NavFooter {\n const isSuperAdmin = useIsSuperAdmin()\n return useMemo<NavFooter>(\n () => (isSuperAdmin ? { links: FOOTER_LINKS, ...meta } : { links: FOOTER_LINKS }),\n [isSuperAdmin, meta.envLabel, meta.version, meta.commit, meta.commitDate, meta.commitAuthor],\n )\n}\n\n// ---------------------------------------------------------------------------\n// Identity — resolved by the PACKAGE, from the chrome API.\n//\n// Apps used to pass a display name straight from their own session claims, and\n// forms passed `user.name ?? user.email` — so a user with no name set had their\n// EMAIL rendered as their name, and the chrome API's real profile name could\n// never win (HITL 2026-07-23). The chrome API is the authority; what the app\n// passes is a pre-hydration fallback only, and an email is never a name.\n// ---------------------------------------------------------------------------\n\nexport function useChromeIdentity(fallback?: {\n userName?: string | null\n email?: string | null\n}): ResolvedIdentity {\n const { me, loaded } = useChromeMe()\n return resolveIdentity({\n apiName: me?.userName,\n fallbackName: fallback?.userName,\n email: fallback?.email,\n avatarUrl: me?.avatarUrl,\n loaded,\n })\n}\n\n// Breadcrumbs live in ../lib/breadcrumbs (pure, dependency-free) and are\n// re-exported here so apps keep a single import surface.\nexport { deriveBreadcrumbs } from '../lib/breadcrumbs'\nexport { looksLikeEmail, resolveIdentity, type ResolvedIdentity } from '../lib/identity'\nimport { resolveIdentity, type ResolvedIdentity } from '../lib/identity'\n\n// ---------------------------------------------------------------------------\n// Breadcrumbs — derived by the PACKAGE from the nav tree + the current path.\n//\n// Every app hand-built its own trail, so they disagreed: forms rendered\n// \"Forms › Dashboard\" while polls rendered \"Polls\" under a \"Polls\" title\n// (\"Polls / Polls\"). The standard is Dashboard › Sub › Item (HITL 2026-07-23),\n// derived from the same nav tree the sidebar renders, so no app can drift.\n// ---------------------------------------------------------------------------\n\n/**\n * The My Account menu config — profile/org links into VRM core, real roles\n * from the user's core organizations, the signed-in account, and the shared\n * Light/System/Dark theme control (auth-derived, cross-app).\n */\nexport function useChromeUserMenu({\n userId,\n userName,\n email,\n}: {\n userId: string\n userName: string | null\n email: string | null\n}): NavUserMenu {\n const state = useChromeState()\n const theme = useThemePreference()\n const activeOrg = state?.orgs.find((o) => o.id === state.orgId) ?? null\n const orgSuffix = activeOrg ? `?org=${activeOrg.id.slice(-6)}` : ''\n return {\n // My Profile is global — NOT org-suffixed (main-app ShellChromeBridge).\n myProfileHref: `${CORE_BASE_URL}/my-profile`,\n organizationSettingsHref: `${CORE_BASE_URL}/organization-settings${orgSuffix}`,\n roles: (state?.orgs ?? [])\n .filter((o) => o.rawRole || o.role)\n .map((o) => ({ role: (o.rawRole ?? o.role) as string, orgName: o.name })),\n accounts: [{ id: userId, name: userName, email, avatarUrl: null, active: true }],\n addAccountHref: `${CORE_BASE_URL}/my-profile#accounts`,\n theme: { value: theme.value, onChange: theme.setValue },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Mode selector — entitlement-driven (HITL 2026-07-23: an org with multiple\n// active modes selects them from the left nav). Entitlements come from the\n// chrome API: GET me with the active org header → modes booleans\n// {relationship, campaign, petition, polling}. Selecting a mode navigates to\n// that mode's VRM-core dashboard carrying the active org.\n// ---------------------------------------------------------------------------\n\nconst MODE_DEFS: {\n id: string\n label: string\n icon: NavIcon\n /** Absolute destination, or a VRM-core path prefixed with CORE_BASE_URL. */\n href: string\n orgQuery: boolean\n}[] = [\n {\n id: 'relationship',\n label: 'Relationship Mode',\n icon: FiUsers,\n href: `${CORE_BASE_URL}/relationship/dashboard`,\n orgQuery: true,\n },\n {\n id: 'campaign',\n label: 'Campaign Mode',\n icon: FiBarChart2,\n href: `${CORE_BASE_URL}/campaign/dashboard`,\n orgQuery: true,\n },\n {\n // Petition Mode lives in its own standalone app (HITL 2026-07-23).\n id: 'petition',\n label: 'Petition Mode',\n icon: FiFlag,\n href: 'https://petitions.politogyvrm.com/',\n orgQuery: true,\n },\n]\n\n/**\n * The active mode pill / selector.\n *\n * The current mode is the USER'S OWN SELECTION (persisted cross-app in\n * localStorage under `vrm.activeMode`), narrowed to what the active org is\n * entitled to — never a literal an app hardcodes. Apps used to call\n * `useChromeMode('relationship')`, which pinned the pill to Relationship even\n * for an org entitled to Campaign or Petition (HITL 2026-07-23). The optional\n * argument now only supplies the pre-entitlement fallback for first paint.\n */\nexport function useChromeMode(\n fallbackModeId: 'relationship' | 'campaign' | 'petition' = 'relationship',\n): NavMode {\n const ctx = useContext(ChromeStateContext)\n const nav = useNavChromeFetcher()\n const orgId = ctx?.orgId ?? null\n const [entitled, setEntitled] = useState<Record<string, boolean> | null>(null)\n const [selectedModeId, setSelectedModeId] = useState<string | null>(null)\n\n // Restore the user's last mode choice (shared key across every app).\n useEffect(() => {\n try {\n const stored = localStorage.getItem(MODE_STORAGE_KEY)\n if (stored && MODE_DEFS.some((m) => m.id === stored)) setSelectedModeId(stored)\n } catch {\n /* storage unavailable */\n }\n }, [])\n\n useEffect(() => {\n if (!nav || !orgId) return\n let cancelled = false\n nav('me', { headers: { 'x-contacts-org': orgId } })\n .then((data) => {\n if (cancelled) return\n const modes = (data as { modes?: Record<string, boolean> | null })?.modes ?? null\n setEntitled(modes)\n })\n .catch(() => {\n /* entitlements unavailable — single-mode pill stands */\n })\n return () => {\n cancelled = true\n }\n }, [nav, orgId])\n\n const options = MODE_DEFS.filter((m) => entitled?.[m.id])\n // Resolution order: the user's selection (if the org is still entitled to it)\n // → the first entitled mode → the app's fallback. Entitlements are async, so\n // before they land the fallback holds the pill's shape (no layout shift).\n const selected = selectedModeId\n ? options.find((m) => m.id === selectedModeId) ?? null\n : null\n const current =\n selected ??\n options[0] ??\n MODE_DEFS.find((m) => m.id === fallbackModeId) ??\n MODE_DEFS[0]\n const suffix = orgId ? `?org=${orgId.slice(-6)}` : ''\n\n return {\n label: current.label,\n icon: current.icon,\n options:\n options.length > 1\n ? options.map((o) => ({ id: o.id, label: o.label, icon: o.icon }))\n : undefined,\n onSelect:\n options.length > 1\n ? (id: string) => {\n const def = MODE_DEFS.find((m) => m.id === id)\n if (!def) return\n // Remember the choice BEFORE navigating so the destination app —\n // and every app after it — opens in the mode the user picked.\n setSelectedModeId(id)\n try {\n localStorage.setItem(MODE_STORAGE_KEY, id)\n } catch {\n /* storage unavailable */\n }\n const sep = def.href.includes('?') ? '&' : '?'\n window.location.assign(\n def.orgQuery && suffix ? `${def.href}${sep}${suffix.slice(1)}` : def.href,\n )\n }\n : undefined,\n }\n}\n\n// The one-component app frame (see ./frame.tsx). Re-exported here so apps have\n// a single import surface: `@the-portland-company/shell/app-chrome`.\nexport {\n PolitogyAppFrame,\n POLITOGY_APPS,\n type PolitogyAppKey,\n type PolitogyAppSession,\n type PolitogyBuildMeta,\n} from './frame'\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/breadcrumbs.ts","../src/lib/identity.ts","../src/app-chrome/frame.tsx","../src/app-chrome/index.tsx"],"names":["trail","useMemo","jsx"],"mappings":"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","file":"app-chrome.js","sourcesContent":["// Breadcrumb derivation — pure, dependency-free logic (no React, no DevNotes),\n// so it is importable from a server module and testable in isolation.\n//\n// The standard is Dashboard > Sub > Item (HITL 2026-07-23), derived from the\n// same nav tree the sidebar renders. Apps previously hand-built their own\n// trails and disagreed: forms rendered \"Forms > Dashboard\" while polls rendered\n// a lone \"Polls\" crumb under a \"Polls\" title (\"Polls / Polls\").\n\nimport type { NavBreadcrumb, NavItem } from '../native/index'\n\nexport function deriveBreadcrumbs(\n items: NavItem[],\n pathname: string,\n opts?: { search?: string; origin?: string },\n): NavBreadcrumb[] {\n const search = opts?.search ?? (typeof window !== 'undefined' ? window.location.search : '')\n const origin = opts?.origin ?? (typeof window !== 'undefined' ? window.location.origin : '')\n\n // Same-origin comparison: cross-app hrefs are absolute, local ones relative.\n const matches = (href?: string): boolean => {\n if (!href) return false\n let path = href\n let query = ''\n if (/^https?:\\/\\//i.test(href)) {\n try {\n const u = new URL(href)\n if (origin && u.origin !== origin) return false\n path = u.pathname\n query = u.search\n } catch {\n return false\n }\n } else {\n const qi = href.indexOf('?')\n if (qi >= 0) {\n path = href.slice(0, qi) || '/'\n query = href.slice(qi)\n }\n }\n if (path !== pathname) return false\n if (!query) return true\n const want = new URLSearchParams(query)\n const have = new URLSearchParams(search)\n for (const [k, v] of want) if (have.get(k) !== v) return false\n return true\n }\n\n // Depth-first walk for the deepest item owning the current route.\n const walk = (nodes: NavItem[], trail: NavItem[]): NavItem[] | null => {\n for (const node of nodes) {\n const here = [...trail, node]\n if (node.children?.length) {\n const deeper = walk(node.children, here)\n if (deeper) return deeper\n }\n if (matches(node.href)) return here\n }\n return null\n }\n\n const trail = walk(items, [])\n // Always anchored at Dashboard; the last crumb is the current location and\n // carries no href (the shell marks it aria-current=\"page\").\n const dashboard = items.find((i) => i.label === 'Dashboard')\n const crumbs: NavBreadcrumb[] = [{ label: 'Dashboard', href: dashboard?.href }]\n if (!trail) return crumbs\n for (const node of trail) {\n if (node.label === 'Dashboard') continue\n crumbs.push({ label: node.label, href: node.href })\n }\n const last = crumbs[crumbs.length - 1]\n if (last) delete (last as { href?: string }).href\n return crumbs\n}\n","// Identity resolution — pure logic, shared by the chrome hook.\n//\n// The chrome API (VRM-core profile) is the AUTHORITY for a user's display name;\n// what an app knows from its own session token is a pre-hydration fallback. An\n// email is never a name: forms passed `user.name ?? user.email`, so a user with\n// no name set had their email rendered as their name in the top bar and the\n// shell's \"add your name\" affordance never fired (HITL 2026-07-23).\n\nexport type ResolvedIdentity = {\n userName: string | null\n email: string | null\n avatarUrl: string | null\n loaded: boolean\n}\n\n/** True when a \"name\" is really just an email address. */\nexport function looksLikeEmail(value: string | null | undefined): boolean {\n return typeof value === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value.trim())\n}\n\nexport function resolveIdentity(input: {\n apiName?: string | null\n fallbackName?: string | null\n email?: string | null\n avatarUrl?: string | null\n loaded?: boolean\n}): ResolvedIdentity {\n const apiName = looksLikeEmail(input.apiName) ? null : input.apiName ?? null\n const fallbackName = looksLikeEmail(input.fallbackName) ? null : input.fallbackName ?? null\n return {\n userName: apiName ?? fallbackName,\n email: input.email ?? null,\n avatarUrl: input.avatarUrl ?? null,\n loaded: input.loaded ?? false,\n }\n}\n","// ---------------------------------------------------------------------------\n// PolitogyAppFrame — ONE component that renders the entire app frame.\n//\n// Why this exists (HITL directive 2026-07-23): shipping the same npm package at\n// the same version is necessary but NOT sufficient. The package exposed\n// PRIMITIVES (NavAppShell + a dozen hooks) and every app hand-assembled them in\n// its own ~150-line Shell.tsx. Identical inputs were never guaranteed, so the\n// outputs diverged — measured on production: forms rendered its user's EMAIL as\n// their name, polls' breadcrumb read \"Polls / Polls\", forms and surveys pinned\n// the mode pill to a hardcoded \"relationship\", and each app declared its own\n// chrome CSS variables (surveys' accent was #C8102E where everyone else was\n// #E63946).\n//\n// The fix is to stop exporting assembly as an app responsibility. An app now\n// renders:\n//\n// <PolitogyAppFrame app=\"forms\" pathname={pathname} linkComponent={Link}\n// session={{ userId, email, role, accountName, accountId }}\n// onSignOut={signOut}>\n// {children}\n// </PolitogyAppFrame>\n//\n// and passes NOTHING about the chrome: no nav, no breadcrumbs, no title, no\n// mode, no user menu, no footer, no theme. Those are derived here, identically,\n// for every app. Anything an app cannot pass is something it cannot drift on.\n// ---------------------------------------------------------------------------\n\n'use client'\n\nimport { useMemo, type ReactNode } from 'react'\n\nimport {\n NavAppShell,\n standardPolitogyNav,\n type NavLinkComponent,\n} from '../native/index'\nimport {\n ChromeTopBarActions,\n PolitogyAppChrome,\n standardNavIcons,\n useChromeFooter,\n useChromeIdentity,\n useChromeMode,\n useChromeState,\n useChromeUserMenu,\n type ChromeDevNotesConfig,\n type ControlledOrgState,\n} from './index'\nimport type { NavChromeFetcher, NavItem } from '../native/index'\nimport { deriveBreadcrumbs } from '../lib/breadcrumbs'\n\n/** Every app that renders the shared chrome. Keyed by its subdomain. */\nexport type PolitogyAppKey = 'vrm' | 'contacts' | 'forms' | 'surveys' | 'polls' | 'petitions'\n\n/**\n * The per-app registry — the ONLY place app identity lives. A new app adds a\n * row here and inherits every chrome behavior; it never re-derives one.\n */\nexport const POLITOGY_APPS: Record<\n PolitogyAppKey,\n { title: string; origin: string; storagePrefix: string }\n> = {\n vrm: { title: 'Politogy VRM', origin: 'https://app.politogyvrm.com', storagePrefix: 'vrm' },\n contacts: { title: 'Contacts', origin: 'https://contacts.politogyvrm.com', storagePrefix: 'contacts' },\n forms: { title: 'Forms', origin: 'https://forms.politogyvrm.com', storagePrefix: 'forms' },\n surveys: { title: 'Surveys', origin: 'https://surveys.politogyvrm.com', storagePrefix: 'surveys' },\n polls: { title: 'Polls', origin: 'https://polls.politogyvrm.com', storagePrefix: 'polls' },\n petitions: { title: 'Petitions', origin: 'https://petitions.politogyvrm.com', storagePrefix: 'petitions' },\n}\n\nexport type PolitogyAppSession = {\n /** Stable IdP subject (`sub`). */\n userId: string\n email: string | null\n /** The app's own role string — feeds DevNotes admin gating. */\n role: string\n /** The app's own workspace label, used only until core orgs resolve. */\n accountName: string\n accountId: string\n /**\n * Pre-hydration display name ONLY. The chrome API is the authority; an email\n * passed here is discarded rather than rendered as a name.\n */\n userName?: string | null\n}\n\n/**\n * Optional DATA plumbing for apps whose session/auth differs — never styling.\n *\n * Everything here is about WHERE data comes from (which fetcher, which org\n * state, which routes an app owns), not what the chrome looks like. Adding a\n * visual option to this type would re-open the drift this component exists to\n * close.\n */\nexport type PolitogyFrameData = {\n /** Chrome-API fetcher override for apps that authenticate client-side. */\n fetchJson?: NavChromeFetcher\n /** DevNotes wiring override for those same direct-auth apps. */\n devNotes?: ChromeDevNotesConfig\n /** Controlled org state for apps that own active-org + switching. */\n org?: ControlledOrgState\n /** App-owned children for a standard nav entry this app owns (Contacts). */\n contactsItem?: { href?: string; children?: NavItem[] }\n /** Avatar fallback when the profile has no uploaded photo (memoji, default). */\n fallbackAvatarUrl?: string | null\n}\n\nexport type PolitogyBuildMeta = {\n envLabel?: string\n version?: string\n commit?: string\n commitDate?: string\n commitAuthor?: string\n}\n\nexport function PolitogyAppFrame({\n app,\n pathname,\n search,\n linkComponent,\n session,\n onSignOut,\n buildMeta,\n data,\n children,\n}: {\n app: PolitogyAppKey\n /** Current path from the app's router (Next: usePathname()). */\n pathname: string\n /** Current query string, when the app routes by query (forms' ?tab=). */\n search?: string\n /** The app's routing primitive (Next: <Link>). */\n linkComponent: NavLinkComponent\n session: PolitogyAppSession\n onSignOut: () => void\n buildMeta?: PolitogyBuildMeta\n /** Optional DATA plumbing (never styling) — see PolitogyFrameData. */\n data?: PolitogyFrameData\n children: ReactNode\n}) {\n const meta = POLITOGY_APPS[app]\n return (\n <PolitogyAppChrome\n storagePrefix={meta.storagePrefix}\n fetchJson={data?.fetchJson}\n devNotes={data?.devNotes}\n org={data?.org}\n identity={{\n userId: session.userId,\n userName: session.userName ?? null,\n email: session.email,\n role: session.role,\n }}\n >\n <FrameBody\n app={app}\n pathname={pathname}\n search={search}\n linkComponent={linkComponent}\n session={session}\n onSignOut={onSignOut}\n buildMeta={buildMeta}\n data={data}\n >\n {children}\n </FrameBody>\n </PolitogyAppChrome>\n )\n}\n\nfunction FrameBody({\n app,\n pathname,\n search,\n linkComponent,\n session,\n onSignOut,\n buildMeta,\n data,\n children,\n}: {\n app: PolitogyAppKey\n pathname: string\n search?: string\n linkComponent: NavLinkComponent\n session: PolitogyAppSession\n onSignOut: () => void\n buildMeta?: PolitogyBuildMeta\n data?: PolitogyFrameData\n children: ReactNode\n}) {\n const meta = POLITOGY_APPS[app]\n const chrome = useChromeState()\n\n // Identity: chrome API wins, app claims are a fallback, an email is never a\n // name (that is what forms was rendering in the top right).\n const identity = useChromeIdentity({ userName: session.userName, email: session.email })\n const userMenu = useChromeUserMenu({\n userId: session.userId,\n userName: identity.userName,\n email: identity.email,\n })\n const footer = useChromeFooter(buildMeta ?? {})\n // Entitlement + user-selection driven. No app passes a mode.\n const mode = useChromeMode()\n\n const coreOrgs = chrome?.orgs ?? []\n const activeCoreOrg = coreOrgs.find((o) => o.id === chrome?.orgId) ?? null\n const navDisplay = chrome?.navDisplay ?? 'both'\n\n const navItems = useMemo(\n () =>\n standardPolitogyNav({\n coreBaseUrl: POLITOGY_APPS.vrm.origin,\n icons: standardNavIcons,\n // An app that OWNS a standard nav entry (Contacts) nests its own routes\n // under it. This is routing data, not chrome configuration.\n contactsItem: data?.contactsItem,\n }),\n [data?.contactsItem],\n )\n\n // Dashboard › Sub › Item, derived from the same tree the sidebar renders.\n const breadcrumbs = useMemo(\n () => deriveBreadcrumbs(navItems, pathname, { search, origin: meta.origin }),\n [navItems, pathname, search, meta.origin],\n )\n\n return (\n <NavAppShell\n sidebar={{\n pathname,\n brand: {\n logoSrc: '/brand/logos/relationship-mode-logo.png',\n logoAlt: 'Politogy',\n badgeLabel: 'P',\n title: 'Politogy',\n },\n nav: {\n items:\n navDisplay === 'tabs'\n ? navItems.map(({ children: _sub, ...item }) => item)\n : navItems,\n },\n org: activeCoreOrg\n ? {\n name: activeCoreOrg.name,\n id: activeCoreOrg.id,\n logoUrl: activeCoreOrg.logoUrl,\n brandColor: activeCoreOrg.brandColor,\n organizations: coreOrgs,\n onSwitch: chrome?.setOrgId,\n }\n : { name: session.accountName, id: session.accountId },\n mode,\n footer,\n linkComponent,\n }}\n betaBanner={{}}\n topBar={{\n title: meta.title,\n breadcrumbs,\n user: {\n name: identity.userName,\n email: identity.email,\n // The chrome API's avatar wins; the app may supply a fallback chain\n // (e.g. a stored memoji) for users with no uploaded photo.\n avatarUrl: identity.avatarUrl ?? data?.fallbackAvatarUrl ?? null,\n role: session.role,\n },\n userMenu,\n onSignOut,\n actions: <ChromeTopBarActions />,\n }}\n >\n {children}\n </NavAppShell>\n )\n}\n","// ---------------------------------------------------------------------------\n// App chrome wiring — the ONE shared implementation of the per-app glue that\n// previously lived as a copied ChromeWidgets.tsx in every Next.js feature app\n// (forms / surveys / polls / contacts). Copies drifted (org dropdown, badges);\n// this module makes that class of drift impossible: apps configure, never\n// re-implement (Reference/UI-Platform: consume via npm, never vendored).\n//\n// Provides:\n// <PolitogyAppChrome> — NavChromeProvider (same-origin proxy fetcher) +\n// chrome state (active core org, nav display) +\n// DevNotes provider, in one wrapper.\n// <ChromeTopBarActions> — the canonical top-bar cluster, in canonical order\n// (Fuel · Nav display · DevNotes · Support · Alerts\n// LAST, immediately left of My Account).\n// useChromeState / useChromeUserMenu / useIsSuperAdmin / useChromeFooter.\n//\n// The app still owns: its session (the /api/chrome proxy), page frames, build\n// metadata (env/commit/version), tab strips, and its own icons.\n// ---------------------------------------------------------------------------\n\n'use client'\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react'\nimport {\n NavChromeProvider,\n FuelAction,\n NotificationsAction,\n SupportAction,\n NavDisplayAction,\n TopBarWidgetSlot,\n useChromeOrgs,\n useThemePreference,\n type NavChromeFetcher,\n type NavDisplayMode,\n type NavOrgOption,\n type NavUserMenu,\n type NavFooter,\n type NavFooterLink,\n} from '../native/index'\nimport {\n DevNotesProvider,\n DevNotesMenu,\n createDevNotesClient,\n type DevNotesConfig,\n} from '@the-portland-company/devnotes'\nimport {\n FiActivity,\n FiBarChart2,\n FiBriefcase,\n FiClock,\n FiCreditCard,\n FiDollarSign,\n FiEdit3,\n FiFileText,\n FiFlag,\n FiGrid,\n FiHome,\n FiLayout,\n FiLifeBuoy,\n FiList,\n FiMail,\n FiMessageSquare,\n FiSend,\n FiSettings,\n FiShare2,\n FiUpload,\n FiUser,\n FiUsers,\n} from 'react-icons/fi'\nimport { useChromeMe, useNavChromeFetcher } from '../native/index'\nimport type { StandardNavIconKey } from '../native/standardNav'\nimport type { NavBreadcrumb, NavIcon, NavItem, NavMode } from '../native/index'\n\n/**\n * The canonical icon map for standardPolitogyNav — ONE definition so every\n * app renders identical glyphs (including the Organization Settings branch:\n * briefcase / settings / creditCard / dollarSign / share).\n */\nexport const standardNavIcons: Record<StandardNavIconKey, NavIcon> = {\n home: FiHome,\n users: FiUsers,\n mail: FiMail,\n messageSquare: FiMessageSquare,\n layout: FiLayout,\n activity: FiActivity,\n lifeBuoy: FiLifeBuoy,\n grid: FiGrid,\n list: FiList,\n upload: FiUpload,\n edit: FiEdit3,\n fileText: FiFileText,\n send: FiSend,\n clock: FiClock,\n user: FiUser,\n barChart: FiBarChart2,\n briefcase: FiBriefcase,\n settings: FiSettings,\n creditCard: FiCreditCard,\n dollarSign: FiDollarSign,\n share: FiShare2,\n}\n\nconst ORG_STORAGE_KEY = 'vrm.activeOrg'\n// The user's mode choice, shared by every app on the origin's localStorage.\nconst MODE_STORAGE_KEY = 'vrm.activeMode'\nconst NAV_DISPLAY_KEY = 'vrm.navDisplay'\nconst CORE_BASE_URL = 'https://app.politogyvrm.com'\n\nexport type ChromeIdentity = {\n userId: string\n userName: string | null\n email: string | null\n /** The APP's own role string (any casing) — feeds DevNotes admin gating. */\n role: string\n}\n\ntype ChromeState = {\n orgId: string | null\n setOrgId: (id: string) => void\n orgs: (NavOrgOption & { rawRole?: string | null })[]\n navDisplay: NavDisplayMode\n setNavDisplay: (m: NavDisplayMode) => void\n}\n\nconst ChromeStateContext = createContext<ChromeState | null>(null)\n\nexport function useChromeState(): ChromeState | null {\n return useContext(ChromeStateContext)\n}\n\nexport type ControlledOrgState = {\n orgId: string | null\n onSwitch: (id: string) => void\n /** Override the org list (e.g. the app's own session-scoped list). */\n orgs?: (NavOrgOption & { rawRole?: string | null })[]\n}\n\nfunction ChromeStateProvider({\n controlledOrg,\n children,\n}: {\n controlledOrg?: ControlledOrgState\n children: ReactNode\n}) {\n const { orgs } = useChromeOrgs()\n const [orgId, setOrgIdState] = useState<string | null>(null)\n const [navDisplay, setNavDisplayState] = useState<NavDisplayMode>('both')\n\n // Boot: restore persisted choices.\n useEffect(() => {\n try {\n const storedOrg = localStorage.getItem(ORG_STORAGE_KEY)\n if (storedOrg) setOrgIdState(storedOrg)\n const storedNav = localStorage.getItem(NAV_DISPLAY_KEY)\n if (storedNav === 'menu' || storedNav === 'tabs' || storedNav === 'both') {\n setNavDisplayState(storedNav)\n }\n } catch {\n /* storage unavailable */\n }\n }, [])\n\n // Once orgs resolve: validate the stored org, default to the first.\n useEffect(() => {\n if (!orgs || orgs.length === 0) return\n setOrgIdState((prev) => (prev && orgs.some((o) => o.id === prev) ? prev : orgs[0].id))\n }, [orgs])\n\n const setOrgId = useCallback((id: string) => {\n setOrgIdState(id)\n try {\n localStorage.setItem(ORG_STORAGE_KEY, id)\n } catch {\n /* best effort */\n }\n }, [])\n\n const setNavDisplay = useCallback((m: NavDisplayMode) => {\n setNavDisplayState(m)\n try {\n localStorage.setItem(NAV_DISPLAY_KEY, m)\n } catch {\n /* best effort */\n }\n }, [])\n\n const value = useMemo<ChromeState>(() => {\n // CONTROLLED mode (Contacts): the APP owns the active org + switching —\n // its switch does a full session re-scope. The shared widgets read the\n // app's state so there is exactly one org truth (unification directive).\n if (controlledOrg) {\n return {\n orgId: controlledOrg.orgId,\n setOrgId: controlledOrg.onSwitch,\n orgs:\n controlledOrg.orgs ??\n (orgs ?? []).map((o) => ({\n id: o.id,\n name: o.name,\n logoUrl: o.logoUrl,\n brandColor: o.brandColor,\n role: o.role,\n rawRole: o.rawRole ?? null,\n })),\n navDisplay,\n setNavDisplay,\n }\n }\n return {\n orgId,\n setOrgId,\n orgs: (orgs ?? []).map((o) => ({\n id: o.id,\n name: o.name,\n logoUrl: o.logoUrl,\n brandColor: o.brandColor,\n role: o.role,\n rawRole: o.rawRole ?? null,\n })),\n navDisplay,\n setNavDisplay,\n }\n }, [controlledOrg, orgId, setOrgId, orgs, navDisplay, setNavDisplay])\n\n return <ChromeStateContext.Provider value={value}>{children}</ChromeStateContext.Provider>\n}\n\nexport type ChromeDevNotesConfig = {\n /** Override the DevNotes API base (default: `${proxyBasePath}/devnotes`). */\n basePath?: string\n /** Client-side token supplier (default: empty — same-origin proxy injects). */\n getAuthToken?: () => Promise<string> | string\n /** Custom fetch (e.g. credentials:'include' against a cross-origin API). */\n fetch?: typeof globalThis.fetch\n}\n\nfunction AppDevNotes({\n storagePrefix,\n proxyBasePath,\n devNotes,\n identity,\n children,\n}: {\n storagePrefix: string\n proxyBasePath: string\n devNotes?: ChromeDevNotesConfig\n identity: ChromeIdentity\n children: ReactNode\n}) {\n const adapter = useMemo(\n () =>\n createDevNotesClient({\n basePath: devNotes?.basePath ?? `${proxyBasePath}/devnotes`,\n // Default: the same-origin proxy attaches the session's Bearer.\n getAuthToken: devNotes?.getAuthToken ?? (async () => ''),\n ...(devNotes?.fetch ? { fetch: devNotes.fetch } : {}),\n }),\n [proxyBasePath, devNotes?.basePath, devNotes?.getAuthToken, devNotes?.fetch],\n )\n\n const user = useMemo(\n () => ({\n id: identity.userId,\n email: identity.email ?? '',\n fullName: identity.userName ?? undefined,\n }),\n [identity.userId, identity.email, identity.userName],\n )\n\n const config = useMemo<DevNotesConfig>(\n () => ({\n storagePrefix,\n role: identity.role.toLowerCase() === 'admin' ? 'admin' : 'reporter',\n disableAi: true,\n getPagePath: () => window.location.pathname,\n }),\n [storagePrefix, identity.role],\n )\n\n return (\n <DevNotesProvider adapter={adapter} user={user} config={config}>\n {children}\n </DevNotesProvider>\n )\n}\n\n/**\n * The app-chrome root. Wrap the authenticated frame once:\n *\n * <PolitogyAppChrome storagePrefix=\"forms\" identity={{userId, userName, email, role}}>\n * <ShellFrame …/>\n * </PolitogyAppChrome>\n */\nexport function PolitogyAppChrome({\n storagePrefix,\n proxyBasePath = '/api/chrome',\n fetchJson: fetchJsonOverride,\n devNotes,\n org,\n identity,\n children,\n}: {\n storagePrefix: string\n proxyBasePath?: string\n /**\n * Override the same-origin proxy fetcher for apps that talk to the chrome\n * API directly with their own auth (Contacts sends a client-side Bearer).\n * Paths stay relative ('orgs', 'me', …) — the override owns base + auth.\n */\n fetchJson?: NavChromeFetcher\n /** DevNotes wiring override for direct-auth apps (Contacts). */\n devNotes?: ChromeDevNotesConfig\n /** Controlled org state — the app owns active-org + switching (Contacts). */\n org?: ControlledOrgState\n identity: ChromeIdentity\n children: ReactNode\n}) {\n const proxyFetchJson = useMemo<NavChromeFetcher>(\n () =>\n async (path, init) => {\n const res = await fetch(`${proxyBasePath}/${path}`, {\n method: init?.method ?? 'GET',\n headers: init?.headers,\n body: init?.body,\n })\n if (!res.ok) throw new Error(`chrome ${path}: ${res.status}`)\n if (res.status === 204) return null\n const text = await res.text()\n return text ? JSON.parse(text) : null\n },\n [proxyBasePath],\n )\n const fetchJson = fetchJsonOverride ?? proxyFetchJson\n\n return (\n <NavChromeProvider fetchJson={fetchJson}>\n <ChromeStateProvider controlledOrg={org}>\n <AppDevNotes\n storagePrefix={storagePrefix}\n proxyBasePath={proxyBasePath}\n devNotes={devNotes}\n identity={identity}\n >\n {children}\n </AppDevNotes>\n </ChromeStateProvider>\n </NavChromeProvider>\n )\n}\n\n/** The standard top-bar action cluster. The Alerts bell renders LAST so it\n * sits farthest right, immediately left of the My Account badge. */\nexport function ChromeTopBarActions() {\n const state = useChromeState()\n if (!state) return null\n return (\n <>\n <FuelAction orgId={state.orgId} />\n <NavDisplayAction value={state.navDisplay} onChange={state.setNavDisplay} />\n <TopBarWidgetSlot\n title=\"DevNotes\"\n description=\"Capture bugs and notes, filed straight to Forge.\"\n >\n <DevNotesMenu />\n </TopBarWidgetSlot>\n <SupportAction />\n <NotificationsAction orgId={state.orgId} />\n </>\n )\n}\n\n/** True when any core-org membership carries a super-admin tier. */\nexport function useIsSuperAdmin(): boolean {\n const state = useChromeState()\n return (state?.orgs ?? []).some((o) => /super/i.test(o.rawRole ?? ''))\n}\n\nconst FOOTER_LINKS: NavFooterLink[] = [\n { label: 'Privacy', href: `${CORE_BASE_URL}/privacy-policy`, external: true },\n { label: 'Terms', href: `${CORE_BASE_URL}/terms-and-conditions`, external: true },\n {\n label: 'Subscriptions',\n href: 'https://politogyvrm.com/subscription-cancellation-policy',\n external: true,\n },\n]\n\n/**\n * Sidebar footer: legal links for everyone; build metadata (env / commit /\n * version / date / author) ONLY for Super Administrators\n * (Reference/UI-Platform). Pass the app's build-time metadata in.\n */\nexport function useChromeFooter(meta: {\n envLabel?: string\n version?: string\n commit?: string\n commitDate?: string\n commitAuthor?: string\n}): NavFooter {\n const isSuperAdmin = useIsSuperAdmin()\n return useMemo<NavFooter>(\n () => (isSuperAdmin ? { links: FOOTER_LINKS, ...meta } : { links: FOOTER_LINKS }),\n [isSuperAdmin, meta.envLabel, meta.version, meta.commit, meta.commitDate, meta.commitAuthor],\n )\n}\n\n// ---------------------------------------------------------------------------\n// Identity — resolved by the PACKAGE, from the chrome API.\n//\n// Apps used to pass a display name straight from their own session claims, and\n// forms passed `user.name ?? user.email` — so a user with no name set had their\n// EMAIL rendered as their name, and the chrome API's real profile name could\n// never win (HITL 2026-07-23). The chrome API is the authority; what the app\n// passes is a pre-hydration fallback only, and an email is never a name.\n// ---------------------------------------------------------------------------\n\nexport function useChromeIdentity(fallback?: {\n userName?: string | null\n email?: string | null\n}): ResolvedIdentity {\n const { me, loaded } = useChromeMe()\n return resolveIdentity({\n apiName: me?.userName,\n fallbackName: fallback?.userName,\n email: fallback?.email,\n avatarUrl: me?.avatarUrl,\n loaded,\n })\n}\n\n// Breadcrumbs live in ../lib/breadcrumbs (pure, dependency-free) and are\n// re-exported here so apps keep a single import surface.\nexport { deriveBreadcrumbs } from '../lib/breadcrumbs'\nexport { looksLikeEmail, resolveIdentity, type ResolvedIdentity } from '../lib/identity'\nimport { resolveIdentity, type ResolvedIdentity } from '../lib/identity'\n\n// ---------------------------------------------------------------------------\n// Breadcrumbs — derived by the PACKAGE from the nav tree + the current path.\n//\n// Every app hand-built its own trail, so they disagreed: forms rendered\n// \"Forms › Dashboard\" while polls rendered \"Polls\" under a \"Polls\" title\n// (\"Polls / Polls\"). The standard is Dashboard › Sub › Item (HITL 2026-07-23),\n// derived from the same nav tree the sidebar renders, so no app can drift.\n// ---------------------------------------------------------------------------\n\n/**\n * The My Account menu config — profile/org links into VRM core, real roles\n * from the user's core organizations, the signed-in account, and the shared\n * Light/System/Dark theme control (auth-derived, cross-app).\n */\nexport function useChromeUserMenu({\n userId,\n userName,\n email,\n}: {\n userId: string\n userName: string | null\n email: string | null\n}): NavUserMenu {\n const state = useChromeState()\n const theme = useThemePreference()\n const activeOrg = state?.orgs.find((o) => o.id === state.orgId) ?? null\n const orgSuffix = activeOrg ? `?org=${activeOrg.id.slice(-6)}` : ''\n return {\n // My Profile is global — NOT org-suffixed (main-app ShellChromeBridge).\n myProfileHref: `${CORE_BASE_URL}/my-profile`,\n organizationSettingsHref: `${CORE_BASE_URL}/organization-settings${orgSuffix}`,\n roles: (state?.orgs ?? [])\n .filter((o) => o.rawRole || o.role)\n .map((o) => ({ role: (o.rawRole ?? o.role) as string, orgName: o.name })),\n accounts: [{ id: userId, name: userName, email, avatarUrl: null, active: true }],\n addAccountHref: `${CORE_BASE_URL}/my-profile#accounts`,\n theme: { value: theme.value, onChange: theme.setValue },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Mode selector — entitlement-driven (HITL 2026-07-23: an org with multiple\n// active modes selects them from the left nav). Entitlements come from the\n// chrome API: GET me with the active org header → modes booleans\n// {relationship, campaign, petition, polling}. Selecting a mode navigates to\n// that mode's VRM-core dashboard carrying the active org.\n// ---------------------------------------------------------------------------\n\nconst MODE_DEFS: {\n id: string\n label: string\n icon: NavIcon\n /** Absolute destination, or a VRM-core path prefixed with CORE_BASE_URL. */\n href: string\n orgQuery: boolean\n}[] = [\n {\n id: 'relationship',\n label: 'Relationship Mode',\n icon: FiUsers,\n href: `${CORE_BASE_URL}/relationship/dashboard`,\n orgQuery: true,\n },\n {\n id: 'campaign',\n label: 'Campaign Mode',\n icon: FiBarChart2,\n href: `${CORE_BASE_URL}/campaign/dashboard`,\n orgQuery: true,\n },\n {\n // Petition Mode lives in its own standalone app (HITL 2026-07-23).\n id: 'petition',\n label: 'Petition Mode',\n icon: FiFlag,\n href: 'https://petitions.politogyvrm.com/',\n orgQuery: true,\n },\n]\n\n/**\n * The active mode pill / selector.\n *\n * The current mode is the USER'S OWN SELECTION (persisted cross-app in\n * localStorage under `vrm.activeMode`), narrowed to what the active org is\n * entitled to — never a literal an app hardcodes. Apps used to call\n * `useChromeMode('relationship')`, which pinned the pill to Relationship even\n * for an org entitled to Campaign or Petition (HITL 2026-07-23). The optional\n * argument now only supplies the pre-entitlement fallback for first paint.\n */\nexport function useChromeMode(\n fallbackModeId: 'relationship' | 'campaign' | 'petition' = 'relationship',\n): NavMode {\n const ctx = useContext(ChromeStateContext)\n const nav = useNavChromeFetcher()\n const orgId = ctx?.orgId ?? null\n const [entitled, setEntitled] = useState<Record<string, boolean> | null>(null)\n const [selectedModeId, setSelectedModeId] = useState<string | null>(null)\n\n // Restore the user's last mode choice (shared key across every app).\n useEffect(() => {\n try {\n const stored = localStorage.getItem(MODE_STORAGE_KEY)\n if (stored && MODE_DEFS.some((m) => m.id === stored)) setSelectedModeId(stored)\n } catch {\n /* storage unavailable */\n }\n }, [])\n\n useEffect(() => {\n if (!nav || !orgId) return\n let cancelled = false\n nav('me', { headers: { 'x-contacts-org': orgId } })\n .then((data) => {\n if (cancelled) return\n const modes = (data as { modes?: Record<string, boolean> | null })?.modes ?? null\n setEntitled(modes)\n })\n .catch(() => {\n /* entitlements unavailable — single-mode pill stands */\n })\n return () => {\n cancelled = true\n }\n }, [nav, orgId])\n\n const options = MODE_DEFS.filter((m) => entitled?.[m.id])\n // Resolution order: the user's selection (if the org is still entitled to it)\n // → the first entitled mode → the app's fallback. Entitlements are async, so\n // before they land the fallback holds the pill's shape (no layout shift).\n const selected = selectedModeId\n ? options.find((m) => m.id === selectedModeId) ?? null\n : null\n const current =\n selected ??\n options[0] ??\n MODE_DEFS.find((m) => m.id === fallbackModeId) ??\n MODE_DEFS[0]\n const suffix = orgId ? `?org=${orgId.slice(-6)}` : ''\n\n return {\n label: current.label,\n icon: current.icon,\n options:\n options.length > 1\n ? options.map((o) => ({ id: o.id, label: o.label, icon: o.icon }))\n : undefined,\n onSelect:\n options.length > 1\n ? (id: string) => {\n const def = MODE_DEFS.find((m) => m.id === id)\n if (!def) return\n // Remember the choice BEFORE navigating so the destination app —\n // and every app after it — opens in the mode the user picked.\n setSelectedModeId(id)\n try {\n localStorage.setItem(MODE_STORAGE_KEY, id)\n } catch {\n /* storage unavailable */\n }\n const sep = def.href.includes('?') ? '&' : '?'\n window.location.assign(\n def.orgQuery && suffix ? `${def.href}${sep}${suffix.slice(1)}` : def.href,\n )\n }\n : undefined,\n }\n}\n\n// The one-component app frame (see ./frame.tsx). Re-exported here so apps have\n// a single import surface: `@the-portland-company/shell/app-chrome`.\nexport {\n PolitogyAppFrame,\n POLITOGY_APPS,\n type PolitogyAppKey,\n type PolitogyAppSession,\n type PolitogyBuildMeta,\n type PolitogyFrameData,\n} from './frame'\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@the-portland-company/shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Shared chrome (header, sidebar, footer, theme, auth) for politogy apps. Drop into any Vite SPA to inherit the politogy look and signed-in user.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|