@the-portland-company/shell 0.24.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 +86 -0
- package/dist/app-chrome.cjs +1385 -3
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +111 -3
- package/dist/app-chrome.d.ts +111 -3
- package/dist/app-chrome.js +234 -6
- package/dist/app-chrome.js.map +1 -1
- package/dist/app-icons.cjs +50 -0
- package/dist/app-icons.cjs.map +1 -0
- package/dist/app-icons.d.cts +46 -0
- package/dist/app-icons.d.ts +46 -0
- package/dist/app-icons.js +45 -0
- package/dist/app-icons.js.map +1 -0
- package/dist/chrome.css +41 -144
- package/dist/{chunk-ZZWM7BZZ.js → chunk-LL7RCJQN.js} +31 -3
- package/dist/chunk-LL7RCJQN.js.map +1 -0
- package/dist/{chunk-YWFWSAVT.js → chunk-QCA2PGKA.js} +25 -7
- package/dist/chunk-QCA2PGKA.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/native.cjs +50 -4
- package/dist/native.cjs.map +1 -1
- package/dist/native.js +2 -2
- package/dist/theme-preference.cjs +32 -0
- package/dist/theme-preference.cjs.map +1 -1
- package/dist/theme-preference.d.cts +12 -1
- package/dist/theme-preference.d.ts +12 -1
- package/dist/theme-preference.js +1 -1
- package/package.json +6 -1
- package/dist/chunk-YWFWSAVT.js.map +0 -1
- package/dist/chunk-ZZWM7BZZ.js.map +0 -1
package/dist/app-chrome.d.cts
CHANGED
|
@@ -1,9 +1,102 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
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
|
|
|
7
|
+
declare function deriveBreadcrumbs(items: NavItem[], pathname: string, opts?: {
|
|
8
|
+
search?: string;
|
|
9
|
+
origin?: string;
|
|
10
|
+
}): NavBreadcrumb[];
|
|
11
|
+
|
|
12
|
+
type ResolvedIdentity = {
|
|
13
|
+
userName: string | null;
|
|
14
|
+
email: string | null;
|
|
15
|
+
avatarUrl: string | null;
|
|
16
|
+
loaded: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** True when a "name" is really just an email address. */
|
|
19
|
+
declare function looksLikeEmail(value: string | null | undefined): boolean;
|
|
20
|
+
declare function resolveIdentity(input: {
|
|
21
|
+
apiName?: string | null;
|
|
22
|
+
fallbackName?: string | null;
|
|
23
|
+
email?: string | null;
|
|
24
|
+
avatarUrl?: string | null;
|
|
25
|
+
loaded?: boolean;
|
|
26
|
+
}): ResolvedIdentity;
|
|
27
|
+
|
|
28
|
+
/** Every app that renders the shared chrome. Keyed by its subdomain. */
|
|
29
|
+
type PolitogyAppKey = 'vrm' | 'contacts' | 'forms' | 'surveys' | 'polls' | 'petitions';
|
|
30
|
+
/**
|
|
31
|
+
* The per-app registry — the ONLY place app identity lives. A new app adds a
|
|
32
|
+
* row here and inherits every chrome behavior; it never re-derives one.
|
|
33
|
+
*/
|
|
34
|
+
declare const POLITOGY_APPS: Record<PolitogyAppKey, {
|
|
35
|
+
title: string;
|
|
36
|
+
origin: string;
|
|
37
|
+
storagePrefix: string;
|
|
38
|
+
}>;
|
|
39
|
+
type PolitogyAppSession = {
|
|
40
|
+
/** Stable IdP subject (`sub`). */
|
|
41
|
+
userId: string;
|
|
42
|
+
email: string | null;
|
|
43
|
+
/** The app's own role string — feeds DevNotes admin gating. */
|
|
44
|
+
role: string;
|
|
45
|
+
/** The app's own workspace label, used only until core orgs resolve. */
|
|
46
|
+
accountName: string;
|
|
47
|
+
accountId: string;
|
|
48
|
+
/**
|
|
49
|
+
* Pre-hydration display name ONLY. The chrome API is the authority; an email
|
|
50
|
+
* passed here is discarded rather than rendered as a name.
|
|
51
|
+
*/
|
|
52
|
+
userName?: string | null;
|
|
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
|
+
};
|
|
77
|
+
type PolitogyBuildMeta = {
|
|
78
|
+
envLabel?: string;
|
|
79
|
+
version?: string;
|
|
80
|
+
commit?: string;
|
|
81
|
+
commitDate?: string;
|
|
82
|
+
commitAuthor?: string;
|
|
83
|
+
};
|
|
84
|
+
declare function PolitogyAppFrame({ app, pathname, search, linkComponent, session, onSignOut, buildMeta, data, children, }: {
|
|
85
|
+
app: PolitogyAppKey;
|
|
86
|
+
/** Current path from the app's router (Next: usePathname()). */
|
|
87
|
+
pathname: string;
|
|
88
|
+
/** Current query string, when the app routes by query (forms' ?tab=). */
|
|
89
|
+
search?: string;
|
|
90
|
+
/** The app's routing primitive (Next: <Link>). */
|
|
91
|
+
linkComponent: NavLinkComponent;
|
|
92
|
+
session: PolitogyAppSession;
|
|
93
|
+
onSignOut: () => void;
|
|
94
|
+
buildMeta?: PolitogyBuildMeta;
|
|
95
|
+
/** Optional DATA plumbing (never styling) — see PolitogyFrameData. */
|
|
96
|
+
data?: PolitogyFrameData;
|
|
97
|
+
children: ReactNode;
|
|
98
|
+
}): react_jsx_runtime.JSX.Element;
|
|
99
|
+
|
|
7
100
|
/**
|
|
8
101
|
* The canonical icon map for standardPolitogyNav — ONE definition so every
|
|
9
102
|
* app renders identical glyphs (including the Organization Settings branch:
|
|
@@ -83,6 +176,11 @@ declare function useChromeFooter(meta: {
|
|
|
83
176
|
commitDate?: string;
|
|
84
177
|
commitAuthor?: string;
|
|
85
178
|
}): NavFooter;
|
|
179
|
+
declare function useChromeIdentity(fallback?: {
|
|
180
|
+
userName?: string | null;
|
|
181
|
+
email?: string | null;
|
|
182
|
+
}): ResolvedIdentity;
|
|
183
|
+
|
|
86
184
|
/**
|
|
87
185
|
* The My Account menu config — profile/org links into VRM core, real roles
|
|
88
186
|
* from the user's core organizations, the signed-in account, and the shared
|
|
@@ -93,6 +191,16 @@ declare function useChromeUserMenu({ userId, userName, email, }: {
|
|
|
93
191
|
userName: string | null;
|
|
94
192
|
email: string | null;
|
|
95
193
|
}): NavUserMenu;
|
|
96
|
-
|
|
194
|
+
/**
|
|
195
|
+
* The active mode pill / selector.
|
|
196
|
+
*
|
|
197
|
+
* The current mode is the USER'S OWN SELECTION (persisted cross-app in
|
|
198
|
+
* localStorage under `vrm.activeMode`), narrowed to what the active org is
|
|
199
|
+
* entitled to — never a literal an app hardcodes. Apps used to call
|
|
200
|
+
* `useChromeMode('relationship')`, which pinned the pill to Relationship even
|
|
201
|
+
* for an org entitled to Campaign or Petition (HITL 2026-07-23). The optional
|
|
202
|
+
* argument now only supplies the pre-entitlement fallback for first paint.
|
|
203
|
+
*/
|
|
204
|
+
declare function useChromeMode(fallbackModeId?: 'relationship' | 'campaign' | 'petition'): NavMode;
|
|
97
205
|
|
|
98
|
-
export { type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, PolitogyAppChrome, standardNavIcons, useChromeFooter, 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,9 +1,102 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
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
|
|
|
7
|
+
declare function deriveBreadcrumbs(items: NavItem[], pathname: string, opts?: {
|
|
8
|
+
search?: string;
|
|
9
|
+
origin?: string;
|
|
10
|
+
}): NavBreadcrumb[];
|
|
11
|
+
|
|
12
|
+
type ResolvedIdentity = {
|
|
13
|
+
userName: string | null;
|
|
14
|
+
email: string | null;
|
|
15
|
+
avatarUrl: string | null;
|
|
16
|
+
loaded: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** True when a "name" is really just an email address. */
|
|
19
|
+
declare function looksLikeEmail(value: string | null | undefined): boolean;
|
|
20
|
+
declare function resolveIdentity(input: {
|
|
21
|
+
apiName?: string | null;
|
|
22
|
+
fallbackName?: string | null;
|
|
23
|
+
email?: string | null;
|
|
24
|
+
avatarUrl?: string | null;
|
|
25
|
+
loaded?: boolean;
|
|
26
|
+
}): ResolvedIdentity;
|
|
27
|
+
|
|
28
|
+
/** Every app that renders the shared chrome. Keyed by its subdomain. */
|
|
29
|
+
type PolitogyAppKey = 'vrm' | 'contacts' | 'forms' | 'surveys' | 'polls' | 'petitions';
|
|
30
|
+
/**
|
|
31
|
+
* The per-app registry — the ONLY place app identity lives. A new app adds a
|
|
32
|
+
* row here and inherits every chrome behavior; it never re-derives one.
|
|
33
|
+
*/
|
|
34
|
+
declare const POLITOGY_APPS: Record<PolitogyAppKey, {
|
|
35
|
+
title: string;
|
|
36
|
+
origin: string;
|
|
37
|
+
storagePrefix: string;
|
|
38
|
+
}>;
|
|
39
|
+
type PolitogyAppSession = {
|
|
40
|
+
/** Stable IdP subject (`sub`). */
|
|
41
|
+
userId: string;
|
|
42
|
+
email: string | null;
|
|
43
|
+
/** The app's own role string — feeds DevNotes admin gating. */
|
|
44
|
+
role: string;
|
|
45
|
+
/** The app's own workspace label, used only until core orgs resolve. */
|
|
46
|
+
accountName: string;
|
|
47
|
+
accountId: string;
|
|
48
|
+
/**
|
|
49
|
+
* Pre-hydration display name ONLY. The chrome API is the authority; an email
|
|
50
|
+
* passed here is discarded rather than rendered as a name.
|
|
51
|
+
*/
|
|
52
|
+
userName?: string | null;
|
|
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
|
+
};
|
|
77
|
+
type PolitogyBuildMeta = {
|
|
78
|
+
envLabel?: string;
|
|
79
|
+
version?: string;
|
|
80
|
+
commit?: string;
|
|
81
|
+
commitDate?: string;
|
|
82
|
+
commitAuthor?: string;
|
|
83
|
+
};
|
|
84
|
+
declare function PolitogyAppFrame({ app, pathname, search, linkComponent, session, onSignOut, buildMeta, data, children, }: {
|
|
85
|
+
app: PolitogyAppKey;
|
|
86
|
+
/** Current path from the app's router (Next: usePathname()). */
|
|
87
|
+
pathname: string;
|
|
88
|
+
/** Current query string, when the app routes by query (forms' ?tab=). */
|
|
89
|
+
search?: string;
|
|
90
|
+
/** The app's routing primitive (Next: <Link>). */
|
|
91
|
+
linkComponent: NavLinkComponent;
|
|
92
|
+
session: PolitogyAppSession;
|
|
93
|
+
onSignOut: () => void;
|
|
94
|
+
buildMeta?: PolitogyBuildMeta;
|
|
95
|
+
/** Optional DATA plumbing (never styling) — see PolitogyFrameData. */
|
|
96
|
+
data?: PolitogyFrameData;
|
|
97
|
+
children: ReactNode;
|
|
98
|
+
}): react_jsx_runtime.JSX.Element;
|
|
99
|
+
|
|
7
100
|
/**
|
|
8
101
|
* The canonical icon map for standardPolitogyNav — ONE definition so every
|
|
9
102
|
* app renders identical glyphs (including the Organization Settings branch:
|
|
@@ -83,6 +176,11 @@ declare function useChromeFooter(meta: {
|
|
|
83
176
|
commitDate?: string;
|
|
84
177
|
commitAuthor?: string;
|
|
85
178
|
}): NavFooter;
|
|
179
|
+
declare function useChromeIdentity(fallback?: {
|
|
180
|
+
userName?: string | null;
|
|
181
|
+
email?: string | null;
|
|
182
|
+
}): ResolvedIdentity;
|
|
183
|
+
|
|
86
184
|
/**
|
|
87
185
|
* The My Account menu config — profile/org links into VRM core, real roles
|
|
88
186
|
* from the user's core organizations, the signed-in account, and the shared
|
|
@@ -93,6 +191,16 @@ declare function useChromeUserMenu({ userId, userName, email, }: {
|
|
|
93
191
|
userName: string | null;
|
|
94
192
|
email: string | null;
|
|
95
193
|
}): NavUserMenu;
|
|
96
|
-
|
|
194
|
+
/**
|
|
195
|
+
* The active mode pill / selector.
|
|
196
|
+
*
|
|
197
|
+
* The current mode is the USER'S OWN SELECTION (persisted cross-app in
|
|
198
|
+
* localStorage under `vrm.activeMode`), narrowed to what the active org is
|
|
199
|
+
* entitled to — never a literal an app hardcodes. Apps used to call
|
|
200
|
+
* `useChromeMode('relationship')`, which pinned the pill to Relationship even
|
|
201
|
+
* for an org entitled to Campaign or Petition (HITL 2026-07-23). The optional
|
|
202
|
+
* argument now only supplies the pre-entitlement fallback for first paint.
|
|
203
|
+
*/
|
|
204
|
+
declare function useChromeMode(fallbackModeId?: 'relationship' | 'campaign' | 'petition'): NavMode;
|
|
97
205
|
|
|
98
|
-
export { type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, PolitogyAppChrome, standardNavIcons, useChromeFooter, 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
|
@@ -1,12 +1,215 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { NavChromeProvider, FuelAction, NavDisplayAction, TopBarWidgetSlot, SupportAction, NotificationsAction, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-
|
|
2
|
+
import { standardPolitogyNav, NavAppShell, NavChromeProvider, FuelAction, NavDisplayAction, TopBarWidgetSlot, SupportAction, NotificationsAction, useChromeMe, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-QCA2PGKA.js';
|
|
3
3
|
import './chunk-NV6WHTEZ.js';
|
|
4
|
-
import './chunk-
|
|
5
|
-
import { createContext,
|
|
4
|
+
import './chunk-LL7RCJQN.js';
|
|
5
|
+
import { createContext, useMemo, useContext, useState, useEffect, useCallback } from 'react';
|
|
6
6
|
import { DevNotesMenu, createDevNotesClient, DevNotesProvider } from '@the-portland-company/devnotes';
|
|
7
7
|
import { FiShare2, FiDollarSign, FiCreditCard, FiSettings, FiBriefcase, FiBarChart2, FiUser, FiClock, FiSend, FiFileText, FiEdit3, FiUpload, FiList, FiGrid, FiLifeBuoy, FiActivity, FiLayout, FiMessageSquare, FiMail, FiUsers, FiHome, FiFlag } from 'react-icons/fi';
|
|
8
8
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
9
|
|
|
10
|
+
// src/lib/breadcrumbs.ts
|
|
11
|
+
function deriveBreadcrumbs(items, pathname, opts) {
|
|
12
|
+
const search = opts?.search ?? (typeof window !== "undefined" ? window.location.search : "");
|
|
13
|
+
const origin = opts?.origin ?? (typeof window !== "undefined" ? window.location.origin : "");
|
|
14
|
+
const matches = (href) => {
|
|
15
|
+
if (!href) return false;
|
|
16
|
+
let path = href;
|
|
17
|
+
let query = "";
|
|
18
|
+
if (/^https?:\/\//i.test(href)) {
|
|
19
|
+
try {
|
|
20
|
+
const u = new URL(href);
|
|
21
|
+
if (origin && u.origin !== origin) return false;
|
|
22
|
+
path = u.pathname;
|
|
23
|
+
query = u.search;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
const qi = href.indexOf("?");
|
|
29
|
+
if (qi >= 0) {
|
|
30
|
+
path = href.slice(0, qi) || "/";
|
|
31
|
+
query = href.slice(qi);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (path !== pathname) return false;
|
|
35
|
+
if (!query) return true;
|
|
36
|
+
const want = new URLSearchParams(query);
|
|
37
|
+
const have = new URLSearchParams(search);
|
|
38
|
+
for (const [k, v] of want) if (have.get(k) !== v) return false;
|
|
39
|
+
return true;
|
|
40
|
+
};
|
|
41
|
+
const walk = (nodes, trail2) => {
|
|
42
|
+
for (const node of nodes) {
|
|
43
|
+
const here = [...trail2, node];
|
|
44
|
+
if (node.children?.length) {
|
|
45
|
+
const deeper = walk(node.children, here);
|
|
46
|
+
if (deeper) return deeper;
|
|
47
|
+
}
|
|
48
|
+
if (matches(node.href)) return here;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
};
|
|
52
|
+
const trail = walk(items, []);
|
|
53
|
+
const dashboard = items.find((i) => i.label === "Dashboard");
|
|
54
|
+
const crumbs = [{ label: "Dashboard", href: dashboard?.href }];
|
|
55
|
+
if (!trail) return crumbs;
|
|
56
|
+
for (const node of trail) {
|
|
57
|
+
if (node.label === "Dashboard") continue;
|
|
58
|
+
crumbs.push({ label: node.label, href: node.href });
|
|
59
|
+
}
|
|
60
|
+
const last = crumbs[crumbs.length - 1];
|
|
61
|
+
if (last) delete last.href;
|
|
62
|
+
return crumbs;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/lib/identity.ts
|
|
66
|
+
function looksLikeEmail(value) {
|
|
67
|
+
return typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
|
|
68
|
+
}
|
|
69
|
+
function resolveIdentity(input) {
|
|
70
|
+
const apiName = looksLikeEmail(input.apiName) ? null : input.apiName ?? null;
|
|
71
|
+
const fallbackName = looksLikeEmail(input.fallbackName) ? null : input.fallbackName ?? null;
|
|
72
|
+
return {
|
|
73
|
+
userName: apiName ?? fallbackName,
|
|
74
|
+
email: input.email ?? null,
|
|
75
|
+
avatarUrl: input.avatarUrl ?? null,
|
|
76
|
+
loaded: input.loaded ?? false
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
var POLITOGY_APPS = {
|
|
80
|
+
vrm: { title: "Politogy VRM", origin: "https://app.politogyvrm.com", storagePrefix: "vrm" },
|
|
81
|
+
contacts: { title: "Contacts", origin: "https://contacts.politogyvrm.com", storagePrefix: "contacts" },
|
|
82
|
+
forms: { title: "Forms", origin: "https://forms.politogyvrm.com", storagePrefix: "forms" },
|
|
83
|
+
surveys: { title: "Surveys", origin: "https://surveys.politogyvrm.com", storagePrefix: "surveys" },
|
|
84
|
+
polls: { title: "Polls", origin: "https://polls.politogyvrm.com", storagePrefix: "polls" },
|
|
85
|
+
petitions: { title: "Petitions", origin: "https://petitions.politogyvrm.com", storagePrefix: "petitions" }
|
|
86
|
+
};
|
|
87
|
+
function PolitogyAppFrame({
|
|
88
|
+
app,
|
|
89
|
+
pathname,
|
|
90
|
+
search,
|
|
91
|
+
linkComponent,
|
|
92
|
+
session,
|
|
93
|
+
onSignOut,
|
|
94
|
+
buildMeta,
|
|
95
|
+
data,
|
|
96
|
+
children
|
|
97
|
+
}) {
|
|
98
|
+
const meta = POLITOGY_APPS[app];
|
|
99
|
+
return /* @__PURE__ */ jsx(
|
|
100
|
+
PolitogyAppChrome,
|
|
101
|
+
{
|
|
102
|
+
storagePrefix: meta.storagePrefix,
|
|
103
|
+
fetchJson: data?.fetchJson,
|
|
104
|
+
devNotes: data?.devNotes,
|
|
105
|
+
org: data?.org,
|
|
106
|
+
identity: {
|
|
107
|
+
userId: session.userId,
|
|
108
|
+
userName: session.userName ?? null,
|
|
109
|
+
email: session.email,
|
|
110
|
+
role: session.role
|
|
111
|
+
},
|
|
112
|
+
children: /* @__PURE__ */ jsx(
|
|
113
|
+
FrameBody,
|
|
114
|
+
{
|
|
115
|
+
app,
|
|
116
|
+
pathname,
|
|
117
|
+
search,
|
|
118
|
+
linkComponent,
|
|
119
|
+
session,
|
|
120
|
+
onSignOut,
|
|
121
|
+
buildMeta,
|
|
122
|
+
data,
|
|
123
|
+
children
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
function FrameBody({
|
|
130
|
+
app,
|
|
131
|
+
pathname,
|
|
132
|
+
search,
|
|
133
|
+
linkComponent,
|
|
134
|
+
session,
|
|
135
|
+
onSignOut,
|
|
136
|
+
buildMeta,
|
|
137
|
+
data,
|
|
138
|
+
children
|
|
139
|
+
}) {
|
|
140
|
+
const meta = POLITOGY_APPS[app];
|
|
141
|
+
const chrome = useChromeState();
|
|
142
|
+
const identity = useChromeIdentity({ userName: session.userName, email: session.email });
|
|
143
|
+
const userMenu = useChromeUserMenu({
|
|
144
|
+
userId: session.userId,
|
|
145
|
+
userName: identity.userName,
|
|
146
|
+
email: identity.email
|
|
147
|
+
});
|
|
148
|
+
const footer = useChromeFooter(buildMeta ?? {});
|
|
149
|
+
const mode = useChromeMode();
|
|
150
|
+
const coreOrgs = chrome?.orgs ?? [];
|
|
151
|
+
const activeCoreOrg = coreOrgs.find((o) => o.id === chrome?.orgId) ?? null;
|
|
152
|
+
const navDisplay = chrome?.navDisplay ?? "both";
|
|
153
|
+
const navItems = useMemo(
|
|
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]
|
|
162
|
+
);
|
|
163
|
+
const breadcrumbs = useMemo(
|
|
164
|
+
() => deriveBreadcrumbs(navItems, pathname, { search, origin: meta.origin }),
|
|
165
|
+
[navItems, pathname, search, meta.origin]
|
|
166
|
+
);
|
|
167
|
+
return /* @__PURE__ */ jsx(
|
|
168
|
+
NavAppShell,
|
|
169
|
+
{
|
|
170
|
+
sidebar: {
|
|
171
|
+
pathname,
|
|
172
|
+
brand: {
|
|
173
|
+
logoSrc: "/brand/logos/relationship-mode-logo.png",
|
|
174
|
+
logoAlt: "Politogy",
|
|
175
|
+
badgeLabel: "P",
|
|
176
|
+
title: "Politogy"
|
|
177
|
+
},
|
|
178
|
+
nav: {
|
|
179
|
+
items: navDisplay === "tabs" ? navItems.map(({ children: _sub, ...item }) => item) : navItems
|
|
180
|
+
},
|
|
181
|
+
org: activeCoreOrg ? {
|
|
182
|
+
name: activeCoreOrg.name,
|
|
183
|
+
id: activeCoreOrg.id,
|
|
184
|
+
logoUrl: activeCoreOrg.logoUrl,
|
|
185
|
+
brandColor: activeCoreOrg.brandColor,
|
|
186
|
+
organizations: coreOrgs,
|
|
187
|
+
onSwitch: chrome?.setOrgId
|
|
188
|
+
} : { name: session.accountName, id: session.accountId },
|
|
189
|
+
mode,
|
|
190
|
+
footer,
|
|
191
|
+
linkComponent
|
|
192
|
+
},
|
|
193
|
+
betaBanner: {},
|
|
194
|
+
topBar: {
|
|
195
|
+
title: meta.title,
|
|
196
|
+
breadcrumbs,
|
|
197
|
+
user: {
|
|
198
|
+
name: identity.userName,
|
|
199
|
+
email: identity.email,
|
|
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,
|
|
203
|
+
role: session.role
|
|
204
|
+
},
|
|
205
|
+
userMenu,
|
|
206
|
+
onSignOut,
|
|
207
|
+
actions: /* @__PURE__ */ jsx(ChromeTopBarActions, {})
|
|
208
|
+
},
|
|
209
|
+
children
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
}
|
|
10
213
|
var standardNavIcons = {
|
|
11
214
|
home: FiHome,
|
|
12
215
|
users: FiUsers,
|
|
@@ -31,6 +234,7 @@ var standardNavIcons = {
|
|
|
31
234
|
share: FiShare2
|
|
32
235
|
};
|
|
33
236
|
var ORG_STORAGE_KEY = "vrm.activeOrg";
|
|
237
|
+
var MODE_STORAGE_KEY = "vrm.activeMode";
|
|
34
238
|
var NAV_DISPLAY_KEY = "vrm.navDisplay";
|
|
35
239
|
var CORE_BASE_URL = "https://app.politogyvrm.com";
|
|
36
240
|
var ChromeStateContext = createContext(null);
|
|
@@ -215,6 +419,16 @@ function useChromeFooter(meta) {
|
|
|
215
419
|
[isSuperAdmin, meta.envLabel, meta.version, meta.commit, meta.commitDate, meta.commitAuthor]
|
|
216
420
|
);
|
|
217
421
|
}
|
|
422
|
+
function useChromeIdentity(fallback) {
|
|
423
|
+
const { me, loaded } = useChromeMe();
|
|
424
|
+
return resolveIdentity({
|
|
425
|
+
apiName: me?.userName,
|
|
426
|
+
fallbackName: fallback?.userName,
|
|
427
|
+
email: fallback?.email,
|
|
428
|
+
avatarUrl: me?.avatarUrl,
|
|
429
|
+
loaded
|
|
430
|
+
});
|
|
431
|
+
}
|
|
218
432
|
function useChromeUserMenu({
|
|
219
433
|
userId,
|
|
220
434
|
userName,
|
|
@@ -258,11 +472,19 @@ var MODE_DEFS = [
|
|
|
258
472
|
orgQuery: true
|
|
259
473
|
}
|
|
260
474
|
];
|
|
261
|
-
function useChromeMode(
|
|
475
|
+
function useChromeMode(fallbackModeId = "relationship") {
|
|
262
476
|
const ctx = useContext(ChromeStateContext);
|
|
263
477
|
const nav = useNavChromeFetcher();
|
|
264
478
|
const orgId = ctx?.orgId ?? null;
|
|
265
479
|
const [entitled, setEntitled] = useState(null);
|
|
480
|
+
const [selectedModeId, setSelectedModeId] = useState(null);
|
|
481
|
+
useEffect(() => {
|
|
482
|
+
try {
|
|
483
|
+
const stored = localStorage.getItem(MODE_STORAGE_KEY);
|
|
484
|
+
if (stored && MODE_DEFS.some((m) => m.id === stored)) setSelectedModeId(stored);
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}, []);
|
|
266
488
|
useEffect(() => {
|
|
267
489
|
if (!nav || !orgId) return;
|
|
268
490
|
let cancelled = false;
|
|
@@ -276,8 +498,9 @@ function useChromeMode(currentModeId = "relationship") {
|
|
|
276
498
|
cancelled = true;
|
|
277
499
|
};
|
|
278
500
|
}, [nav, orgId]);
|
|
279
|
-
const current = MODE_DEFS.find((m) => m.id === currentModeId) ?? MODE_DEFS[0];
|
|
280
501
|
const options = MODE_DEFS.filter((m) => entitled?.[m.id]);
|
|
502
|
+
const selected = selectedModeId ? options.find((m) => m.id === selectedModeId) ?? null : null;
|
|
503
|
+
const current = selected ?? options[0] ?? MODE_DEFS.find((m) => m.id === fallbackModeId) ?? MODE_DEFS[0];
|
|
281
504
|
const suffix = orgId ? `?org=${orgId.slice(-6)}` : "";
|
|
282
505
|
return {
|
|
283
506
|
label: current.label,
|
|
@@ -286,6 +509,11 @@ function useChromeMode(currentModeId = "relationship") {
|
|
|
286
509
|
onSelect: options.length > 1 ? (id) => {
|
|
287
510
|
const def = MODE_DEFS.find((m) => m.id === id);
|
|
288
511
|
if (!def) return;
|
|
512
|
+
setSelectedModeId(id);
|
|
513
|
+
try {
|
|
514
|
+
localStorage.setItem(MODE_STORAGE_KEY, id);
|
|
515
|
+
} catch {
|
|
516
|
+
}
|
|
289
517
|
const sep = def.href.includes("?") ? "&" : "?";
|
|
290
518
|
window.location.assign(
|
|
291
519
|
def.orgQuery && suffix ? `${def.href}${sep}${suffix.slice(1)}` : def.href
|
|
@@ -294,6 +522,6 @@ function useChromeMode(currentModeId = "relationship") {
|
|
|
294
522
|
};
|
|
295
523
|
}
|
|
296
524
|
|
|
297
|
-
export { ChromeTopBarActions, PolitogyAppChrome, standardNavIcons, useChromeFooter, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
|
|
525
|
+
export { ChromeTopBarActions, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
|
|
298
526
|
//# sourceMappingURL=app-chrome.js.map
|
|
299
527
|
//# sourceMappingURL=app-chrome.js.map
|