@the-portland-company/shell 0.80.1 → 0.81.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 +20 -0
- package/dist/active-org.cjs +9 -0
- package/dist/active-org.cjs.map +1 -1
- package/dist/active-org.d.cts +7 -1
- package/dist/active-org.d.ts +7 -1
- package/dist/active-org.js +1 -1
- package/dist/app-chrome.cjs +73 -34
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +31 -5
- package/dist/app-chrome.d.ts +31 -5
- package/dist/app-chrome.js +61 -35
- package/dist/app-chrome.js.map +1 -1
- package/dist/{chunk-FBUF7LUE.js → chunk-5LO5W7W5.js} +11 -3
- package/dist/chunk-5LO5W7W5.js.map +1 -0
- package/dist/{chunk-TUK2IEL5.js → chunk-L73FZKGE.js} +5 -4
- package/dist/chunk-L73FZKGE.js.map +1 -0
- package/dist/{chunk-DXJOADNV.js → chunk-MDF2W6CY.js} +8 -6
- package/dist/chunk-MDF2W6CY.js.map +1 -0
- package/dist/{index-fydj5EU2.d.cts → index-_Y7jOKPN.d.cts} +8 -3
- package/dist/{index-KGYMc_1i.d.ts → index-bYu6DxNt.d.ts} +8 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/native.cjs +15 -4
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.js +3 -3
- package/dist/url-state.cjs.map +1 -1
- package/dist/url-state.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DXJOADNV.js.map +0 -1
- package/dist/chunk-FBUF7LUE.js.map +0 -1
- package/dist/chunk-TUK2IEL5.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## 0.81.0
|
|
2
|
+
|
|
3
|
+
An organization switch without a reload keeps the mode switcher and the address bar right, and no shell link builds on the retired VRM core. Spec cited: Reference/UI-Platform "Retirement of app.politogyvrm.com" and "URL state"; Specs/Left-Navigation §7.
|
|
4
|
+
|
|
5
|
+
- **Cause (mode switcher).** After a switch the pill showed the new organization at once, but the entitlement read (`me` with `x-contacts-org`) ran before the app had re-exchanged its token. The chrome API answers for the verified token org, so the old organization's modes were cached under the new one's key: on an organization that holds Campaign Mode the Campaign button stayed dimmed ("isn't active for this organization") until a reload (reported by Ben; reproduced 2026-10-12 on the Dashboard, `account: <old org>` in the response).
|
|
6
|
+
- **Fix.** `ChromeState` gains `orgPending`; `useChromeMode` waits for it to clear and reads entitlements with the new token.
|
|
7
|
+
- **Cause (address bar).** The shell wrote the new `org_id` tail, then the app's `onReexchange` revalidated its tree and the Next router restored the URL it last navigated to, with the previous tail.
|
|
8
|
+
- **Fix.** The tail is stamped again when the token settles on the new organization, after that refresh.
|
|
9
|
+
- **Links.** My Profile and Add account go to Politogy ID (`auth.politogyvrm.com/my-profile`); Organization settings goes to the Settings app with the 7-char `org_id`; Privacy, Terms and the Beta terms go to Politogy ID; the fuel top-up link goes to the Settings Subscription & Billing tab. New export `settingsBaseUrl()` and `footerLinks()`. `CORE_APP_ORIGIN` stays only for `resolveOrgLogoUrl` (the storage proxy is data service, not UI).
|
|
10
|
+
- Tests: `__tests__/orgSwitchPending.test.tsx`, `__tests__/retiredHostLinks.test.tsx`.
|
|
11
|
+
|
|
12
|
+
## 0.80.2
|
|
13
|
+
|
|
14
|
+
The mode switcher goes to each mode's landing page. Spec cited: Specs/Left-Navigation §7 and §11.1.
|
|
15
|
+
|
|
16
|
+
- **Cause.** The Relationship and Campaign buttons still pointed at the retired VRM core (`app.politogyvrm.com/relationship/dashboard`, `/campaign/dashboard`). Its redirect lands on the Dashboard app without a mode, and that app then chooses by the `politogy_active_mode` cookie. From a Campaign page the cookie says campaign, so clicking Relationship landed back on Battleground (measured 2026-10-12 on an organization that holds Campaign Mode).
|
|
17
|
+
- **Fix.** Relationship goes to `https://dashboard.politogyvrm.com/dashboard?mode=relationship`; Campaign goes to `https://battleground.politogyvrm.com/battleground?mode=campaign`. Both carry the active organization, and move to their `-staging` twins from a staging host. New export `modeLandingHref`.
|
|
18
|
+
- **The `dashboard` registry row is pinned to `mode: 'relationship'`.** It was unscoped, so its frame followed the user's last mode selection: after a visit to Campaign Mode the Relationship Dashboard drew the red Campaign rail and the "Campaign mode" label over the Relationship rows (reported by Ben, reproduced 2026-10-12; Left-Navigation §11.1). Pinned, the Dashboard always draws the Relationship frame and writes `relationship` to the active-mode cookie, like `email`.
|
|
19
|
+
- Tests: `__tests__/modeLandingHref.test.ts`.
|
|
20
|
+
|
|
1
21
|
## 0.80.1
|
|
2
22
|
|
|
3
23
|
The Dashboard row shows its active marker on the Dashboard. Spec cited: Specs/Left-Navigation §8.1 and §11.3 ("Exactly one row is active").
|
package/dist/active-org.cjs
CHANGED
|
@@ -66,6 +66,8 @@ var LEGACY_ORG_TAIL_LENGTH = 6;
|
|
|
66
66
|
var TAIL_RE = /^[0-9a-f]{6,7}$/i;
|
|
67
67
|
var AUTH_BASE_URL = "https://auth.politogyvrm.com";
|
|
68
68
|
var AUTH_BASE_URL_STAGING = "https://auth-staging.politogyvrm.com";
|
|
69
|
+
var SETTINGS_BASE_URL = "https://settings.politogyvrm.com";
|
|
70
|
+
var SETTINGS_BASE_URL_STAGING = "https://settings-staging.politogyvrm.com";
|
|
69
71
|
function orgIdTail(orgId) {
|
|
70
72
|
return isOrgId(orgId) ? orgId.trim().toLowerCase().slice(-ORG_ID_TAIL_LENGTH) : null;
|
|
71
73
|
}
|
|
@@ -137,6 +139,12 @@ function authBaseUrl() {
|
|
|
137
139
|
}
|
|
138
140
|
return AUTH_BASE_URL;
|
|
139
141
|
}
|
|
142
|
+
function settingsBaseUrl() {
|
|
143
|
+
if (typeof window !== "undefined" && window.location.hostname.toLowerCase().includes("staging")) {
|
|
144
|
+
return SETTINGS_BASE_URL_STAGING;
|
|
145
|
+
}
|
|
146
|
+
return SETTINGS_BASE_URL;
|
|
147
|
+
}
|
|
140
148
|
var ActiveOrgSwitchError = class extends Error {
|
|
141
149
|
constructor(status, message) {
|
|
142
150
|
super(message);
|
|
@@ -202,6 +210,7 @@ exports.readActiveOrgCookie = readActiveOrgCookie;
|
|
|
202
210
|
exports.readActiveOrgFromUrl = readActiveOrgFromUrl;
|
|
203
211
|
exports.readOrgSelectorFromUrl = readOrgSelectorFromUrl;
|
|
204
212
|
exports.resolveActiveOrg = resolveActiveOrg;
|
|
213
|
+
exports.settingsBaseUrl = settingsBaseUrl;
|
|
205
214
|
exports.switchActiveOrg = switchActiveOrg;
|
|
206
215
|
exports.syncOrgIdInUrl = syncOrgIdInUrl;
|
|
207
216
|
exports.withActiveOrgParam = withActiveOrgParam;
|
package/dist/active-org.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/active-org.ts"],"names":[],"mappings":"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","file":"active-org.cjs","sourcesContent":["// Universal active-organization selection — the ONE cross-app contract.\n//\n// A user belongs to many orgs; ONE active-org choice must follow them across\n// every Politogy app + subdomain so all apps resolve the SAME org (and campaign\n// apps don't land on a rel-only org and lock themselves). Layers cooperate,\n// mirroring the theme-preference contract:\n// 1. URL `?org=<uuid>` — the authoritative per-request selector. The shared\n// chrome stamps it onto every route so it is bookmarkable + shareable, and\n// each app forwards it to the chrome API as `x-contacts-org`.\n// 2. `.politogyvrm.com` cookie `politogy_active_org` — cross-subdomain vehicle\n// (auth session cookies are host-only) that remembers the LAST USED org so\n// a bare URL in the next app opens on the same org.\n//\n// The stored/most-authoritative value is the FULL org UUID: contacts-api\n// (auth/vrmMembership.selectRequestedMembership) matches the selector by EXACT\n// id first, then falls back to a short suffix that can COLLIDE across a user's\n// orgs — so the full UUID is the only collision-free selector.\n//\n// Server-safe (no 'use client'): constants/resolve helpers are importable from\n// server layouts; the cookie read/write helpers no-op on the server.\n\nexport const ACTIVE_ORG_COOKIE = 'politogy_active_org'\nexport const ACTIVE_ORG_QUERY_PARAM = 'org'\n\n// Loose UUID shape check — we only ever store ids that came from the user's own\n// authoritative membership list, so this just rejects obvious garbage.\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport function isOrgId(v: unknown): v is string {\n return typeof v === 'string' && UUID_RE.test(v.trim())\n}\n\n/**\n * Read the shared active-org cookie (browser only; null on server or unset).\n * @deprecated v2 apps read the org from the token claims (`orgClaimsFromToken`).\n * The cookie stays written during rollout for apps not yet on the token path.\n */\nexport function readActiveOrgCookie(): string | null {\n if (typeof document === 'undefined') return null\n try {\n const m = document.cookie.match(\n new RegExp(`(?:^|;\\\\s*)${ACTIVE_ORG_COOKIE}=([^;]+)`),\n )\n const v = m ? decodeURIComponent(m[1]) : null\n return isOrgId(v) ? v : null\n } catch {\n return null\n }\n}\n\n// Write the cross-subdomain cookie with the SAME scoping rules as the theme\n// cookie: on a *.politogyvrm.com host scope to the parent domain so every app\n// subdomain reads it; on any other host (localhost, *.pages.dev preview) fall\n// back to a host-only cookie. 1-year, SameSite=Lax, Secure on https.\nexport function writeActiveOrgCookie(orgId: string): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return\n if (!isOrgId(orgId)) return\n const host = window.location.hostname\n const domainAttr = /(^|\\.)politogyvrm\\.com$/i.test(host) ? '; Domain=.politogyvrm.com' : ''\n const secure = window.location.protocol === 'https:' ? '; Secure' : ''\n document.cookie = `${ACTIVE_ORG_COOKIE}=${encodeURIComponent(\n orgId,\n )}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`\n}\n\n/**\n * Read the `?org=` selector from a URL search string (full UUID only). Accepts a\n * `location.search`-style string or a full URL.\n */\nexport function readActiveOrgFromUrl(search: string | null | undefined): string | null {\n if (!search) return null\n try {\n const qs = search.includes('?') ? search.slice(search.indexOf('?') + 1) : search\n const v = new URLSearchParams(qs).get(ACTIVE_ORG_QUERY_PARAM)\n return isOrgId(v) ? v : null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve the active org with the cross-app precedence:\n * 1. `?org=<uuid>` in the current URL (explicit per-request selector)\n * 2. the `.politogyvrm.com` cookie (last used, cross-subdomain)\n * 3. null → let the app fall back to the chrome-API default (earliest-joined)\n * Pass `search` on the server (from the request URL); it defaults to\n * `window.location.search` in the browser.\n * @deprecated v2 apps use the token `org_id` claim plus `readOrgSelectorFromUrl`\n * as a per-tab override only.\n */\nexport function resolveActiveOrg(search?: string | null): string | null {\n const fromUrl = readActiveOrgFromUrl(\n search ?? (typeof window !== 'undefined' ? window.location.search : null),\n )\n if (fromUrl) return fromUrl\n return readActiveOrgCookie()\n}\n\n/** The canonical `?org=` query fragment for a full org id (no leading `?`/`&`). */\nexport function activeOrgParam(orgId: string | null | undefined): string {\n return isOrgId(orgId) ? `${ACTIVE_ORG_QUERY_PARAM}=${orgId}` : ''\n}\n\n/**\n * Return `url` with `?org=<uuid>` set/replaced. Used by the shared chrome to\n * stamp the active org onto every route so it is always present in the address\n * bar. Leaves the URL unchanged when `orgId` is not a valid id.\n */\nexport function withActiveOrgParam(url: string, orgId: string | null | undefined): string {\n if (!isOrgId(orgId)) return url\n try {\n // Support both absolute and relative URLs.\n const base = typeof window !== 'undefined' ? window.location.origin : 'https://politogyvrm.com'\n const u = new URL(url, base)\n u.searchParams.set(ACTIVE_ORG_QUERY_PARAM, orgId as string)\n // Preserve relative form when the input was relative.\n return /^https?:\\/\\//i.test(url) ? u.toString() : `${u.pathname}${u.search}${u.hash}`\n } catch {\n return url\n }\n}\n\n// ---------------------------------------------------------------------------\n// v2: auth-owned active org (Global-Rules v38 \"Auth / Active Organization\",\n// UI-Platform v46 \"URL state\"). The IdP owns the shared active org on the SSO\n// session; tokens carry `org_id` (full uuid) + `org_role`. The URL carries the\n// 7-char `org_id` tail as a PER-TAB override only: it never changes the shared\n// active org. Full uuids and the legacy 6-char tail are accepted during rollout.\n// ---------------------------------------------------------------------------\n\nexport const ORG_ID_QUERY_PARAM = 'org_id'\nexport const ORG_ID_TAIL_LENGTH = 7\nconst LEGACY_ORG_TAIL_LENGTH = 6\nconst TAIL_RE = /^[0-9a-f]{6,7}$/i\n\nconst AUTH_BASE_URL = 'https://auth.politogyvrm.com'\nconst AUTH_BASE_URL_STAGING = 'https://auth-staging.politogyvrm.com'\n\n/** The URL form of an org id: the last 7 chars of the uuid, lower case. */\nexport function orgIdTail(orgId: string | null | undefined): string | null {\n return isOrgId(orgId) ? (orgId as string).trim().toLowerCase().slice(-ORG_ID_TAIL_LENGTH) : null\n}\n\n/** A full uuid, the 7-char tail, or the legacy 6-char tail. */\nexport function isOrgSelector(v: unknown): v is string {\n return typeof v === 'string' && (UUID_RE.test(v.trim()) || TAIL_RE.test(v.trim()))\n}\n\n/**\n * Resolve a selector against the user's own org list: exact uuid first, then a\n * unique 7-char tail, then a unique 6-char tail. Ambiguous or no match → null\n * (the IdP selector rules, so the shell never picks an org the IdP would not).\n */\nexport function matchOrgSelector<T extends { id: string }>(\n selector: string | null | undefined,\n orgs: readonly T[],\n): T | null {\n if (!isOrgSelector(selector)) return null\n const sel = selector.trim().toLowerCase()\n if (UUID_RE.test(sel)) return orgs.find((o) => o.id.toLowerCase() === sel) ?? null\n if (sel.length !== ORG_ID_TAIL_LENGTH && sel.length !== LEGACY_ORG_TAIL_LENGTH) return null\n const hits = orgs.filter((o) => o.id.toLowerCase().endsWith(sel))\n return hits.length === 1 ? hits[0] : null\n}\n\n/**\n * Read the per-tab org override from a URL: `org_id` (spec), else the legacy\n * `org`. Returns the raw selector (uuid or tail), or null. Never a source for\n * the shared active org.\n */\nexport function readOrgSelectorFromUrl(search: string | null | undefined): string | null {\n if (!search) return null\n try {\n const qs = search.includes('?') ? search.slice(search.indexOf('?') + 1) : search\n const params = new URLSearchParams(qs)\n for (const key of [ORG_ID_QUERY_PARAM, ACTIVE_ORG_QUERY_PARAM]) {\n const v = params.get(key)\n if (isOrgSelector(v)) return v.trim().toLowerCase()\n }\n return null\n } catch {\n return null\n }\n}\n\n/**\n * Return `url` with `org_id=<7-char tail>` set and any legacy `org=` removed.\n * Leaves the URL unchanged when `orgId` is not a full uuid.\n */\nexport function withOrgIdParam(url: string, orgId: string | null | undefined): string {\n const tail = orgIdTail(orgId)\n if (!tail) return url\n try {\n const base = typeof window !== 'undefined' ? window.location.origin : 'https://politogyvrm.com'\n const u = new URL(url, base)\n u.searchParams.delete(ACTIVE_ORG_QUERY_PARAM)\n u.searchParams.set(ORG_ID_QUERY_PARAM, tail)\n return /^https?:\\/\\//i.test(url) ? u.toString() : `${u.pathname}${u.search}${u.hash}`\n } catch {\n return url\n }\n}\n\nexport type OrgClaims = { orgId: string; orgRole: string | null }\n\n/** Pick `org_id` / `org_role` out of a token claim set. */\nexport function orgClaimsFrom(claims: unknown): OrgClaims | null {\n if (!claims || typeof claims !== 'object') return null\n const c = claims as { org_id?: unknown; org_role?: unknown }\n if (!isOrgId(c.org_id)) return null\n return {\n orgId: (c.org_id as string).trim().toLowerCase(),\n orgRole: typeof c.org_role === 'string' ? c.org_role : null,\n }\n}\n\n/**\n * Read `org_id` / `org_role` from a JWT payload WITHOUT verifying it. For\n * display and routing only: the app server verifies the token (JWKS) and every\n * authorization decision keys on the verified claim, never on this.\n */\nexport function orgClaimsFromToken(token: string | null | undefined): OrgClaims | null {\n if (typeof token !== 'string') return null\n const part = token.split('.')[1]\n if (!part) return null\n try {\n const b64 = part.replace(/-/g, '+').replace(/_/g, '/')\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4)\n const json =\n typeof atob === 'function'\n ? decodeURIComponent(\n Array.from(atob(padded), (ch) => '%' + ch.charCodeAt(0).toString(16).padStart(2, '0')).join(''),\n )\n : ''\n return orgClaimsFrom(JSON.parse(json))\n } catch {\n return null\n }\n}\n\n/** The IdP base for the current environment (staging host → staging IdP). */\nexport function authBaseUrl(): string {\n if (typeof window !== 'undefined' && window.location.hostname.toLowerCase().includes('staging')) {\n return AUTH_BASE_URL_STAGING\n }\n return AUTH_BASE_URL\n}\n\nexport class ActiveOrgSwitchError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'ActiveOrgSwitchError'\n }\n}\n\n/**\n * Switch the SHARED active org on the IdP: `POST {auth}/api/active-org`. The\n * IdP checks membership against VRM Core and updates the SSO session; the\n * caller then re-exchanges (server) for tokens carrying the new `org_id`.\n * Also mirrors the legacy cookie so apps not yet on v2 follow the switch.\n * Throws `ActiveOrgSwitchError` (403 = not a member, 401 = no SSO session).\n */\nexport async function switchActiveOrg(\n orgId: string,\n opts: { authBaseUrl?: string; fetch?: typeof fetch } = {},\n): Promise<OrgClaims> {\n if (!isOrgId(orgId)) throw new ActiveOrgSwitchError(400, 'org_id must be a full uuid')\n const doFetch = opts.fetch ?? fetch\n const res = await doFetch(`${opts.authBaseUrl ?? authBaseUrl()}/api/active-org`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ org_id: orgId }),\n })\n if (!res.ok) {\n let msg = `active-org switch failed (${res.status})`\n try {\n const body = (await res.json()) as { error?: string }\n if (body?.error) msg = body.error\n } catch {\n /* no body */\n }\n throw new ActiveOrgSwitchError(res.status, msg)\n }\n const claims = orgClaimsFrom(await res.json().catch(() => null))\n if (!claims) throw new ActiveOrgSwitchError(502, 'active-org switch returned no org_id')\n writeActiveOrgCookie(claims.orgId)\n return claims\n}\n\n/**\n * URL-sync rule for the address-bar org selector. Returns the new href, or\n * null when nothing should be written. A bare URL (no `org_id`, no legacy\n * `org`) stays bare: the shell must never inject `org_id` on its own, because\n * a self-injected selector then acts as a per-tab override and beats the token\n * org and the cross-app `politogy_active_org` trigger. Only a selector the user\n * brought (or a real switch put there) is canonicalized to the current org.\n */\nexport function syncOrgIdInUrl(href: string, orgId: string | null | undefined): string | null {\n const tail = orgIdTail(orgId)\n if (!tail) return null\n let u: URL\n try {\n u = new URL(href)\n } catch {\n return null\n }\n const hasLegacy = u.searchParams.has(ACTIVE_ORG_QUERY_PARAM)\n const current = u.searchParams.get(ORG_ID_QUERY_PARAM)\n if (!hasLegacy && current === null) return null\n if (!hasLegacy && current === tail) return null\n u.searchParams.delete(ACTIVE_ORG_QUERY_PARAM)\n u.searchParams.set(ORG_ID_QUERY_PARAM, tail)\n return u.toString()\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/active-org.ts"],"names":[],"mappings":"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","file":"active-org.cjs","sourcesContent":["// Universal active-organization selection — the ONE cross-app contract.\n//\n// A user belongs to many orgs; ONE active-org choice must follow them across\n// every Politogy app + subdomain so all apps resolve the SAME org (and campaign\n// apps don't land on a rel-only org and lock themselves). Layers cooperate,\n// mirroring the theme-preference contract:\n// 1. URL `?org=<uuid>` — the authoritative per-request selector. The shared\n// chrome stamps it onto every route so it is bookmarkable + shareable, and\n// each app forwards it to the chrome API as `x-contacts-org`.\n// 2. `.politogyvrm.com` cookie `politogy_active_org` — cross-subdomain vehicle\n// (auth session cookies are host-only) that remembers the LAST USED org so\n// a bare URL in the next app opens on the same org.\n//\n// The stored/most-authoritative value is the FULL org UUID: contacts-api\n// (auth/vrmMembership.selectRequestedMembership) matches the selector by EXACT\n// id first, then falls back to a short suffix that can COLLIDE across a user's\n// orgs — so the full UUID is the only collision-free selector.\n//\n// Server-safe (no 'use client'): constants/resolve helpers are importable from\n// server layouts; the cookie read/write helpers no-op on the server.\n\nexport const ACTIVE_ORG_COOKIE = 'politogy_active_org'\nexport const ACTIVE_ORG_QUERY_PARAM = 'org'\n\n// Loose UUID shape check — we only ever store ids that came from the user's own\n// authoritative membership list, so this just rejects obvious garbage.\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport function isOrgId(v: unknown): v is string {\n return typeof v === 'string' && UUID_RE.test(v.trim())\n}\n\n/**\n * Read the shared active-org cookie (browser only; null on server or unset).\n * @deprecated v2 apps read the org from the token claims (`orgClaimsFromToken`).\n * The cookie stays written during rollout for apps not yet on the token path.\n */\nexport function readActiveOrgCookie(): string | null {\n if (typeof document === 'undefined') return null\n try {\n const m = document.cookie.match(\n new RegExp(`(?:^|;\\\\s*)${ACTIVE_ORG_COOKIE}=([^;]+)`),\n )\n const v = m ? decodeURIComponent(m[1]) : null\n return isOrgId(v) ? v : null\n } catch {\n return null\n }\n}\n\n// Write the cross-subdomain cookie with the SAME scoping rules as the theme\n// cookie: on a *.politogyvrm.com host scope to the parent domain so every app\n// subdomain reads it; on any other host (localhost, *.pages.dev preview) fall\n// back to a host-only cookie. 1-year, SameSite=Lax, Secure on https.\nexport function writeActiveOrgCookie(orgId: string): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return\n if (!isOrgId(orgId)) return\n const host = window.location.hostname\n const domainAttr = /(^|\\.)politogyvrm\\.com$/i.test(host) ? '; Domain=.politogyvrm.com' : ''\n const secure = window.location.protocol === 'https:' ? '; Secure' : ''\n document.cookie = `${ACTIVE_ORG_COOKIE}=${encodeURIComponent(\n orgId,\n )}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`\n}\n\n/**\n * Read the `?org=` selector from a URL search string (full UUID only). Accepts a\n * `location.search`-style string or a full URL.\n */\nexport function readActiveOrgFromUrl(search: string | null | undefined): string | null {\n if (!search) return null\n try {\n const qs = search.includes('?') ? search.slice(search.indexOf('?') + 1) : search\n const v = new URLSearchParams(qs).get(ACTIVE_ORG_QUERY_PARAM)\n return isOrgId(v) ? v : null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve the active org with the cross-app precedence:\n * 1. `?org=<uuid>` in the current URL (explicit per-request selector)\n * 2. the `.politogyvrm.com` cookie (last used, cross-subdomain)\n * 3. null → let the app fall back to the chrome-API default (earliest-joined)\n * Pass `search` on the server (from the request URL); it defaults to\n * `window.location.search` in the browser.\n * @deprecated v2 apps use the token `org_id` claim plus `readOrgSelectorFromUrl`\n * as a per-tab override only.\n */\nexport function resolveActiveOrg(search?: string | null): string | null {\n const fromUrl = readActiveOrgFromUrl(\n search ?? (typeof window !== 'undefined' ? window.location.search : null),\n )\n if (fromUrl) return fromUrl\n return readActiveOrgCookie()\n}\n\n/** The canonical `?org=` query fragment for a full org id (no leading `?`/`&`). */\nexport function activeOrgParam(orgId: string | null | undefined): string {\n return isOrgId(orgId) ? `${ACTIVE_ORG_QUERY_PARAM}=${orgId}` : ''\n}\n\n/**\n * Return `url` with `?org=<uuid>` set/replaced. Used by the shared chrome to\n * stamp the active org onto every route so it is always present in the address\n * bar. Leaves the URL unchanged when `orgId` is not a valid id.\n */\nexport function withActiveOrgParam(url: string, orgId: string | null | undefined): string {\n if (!isOrgId(orgId)) return url\n try {\n // Support both absolute and relative URLs.\n const base = typeof window !== 'undefined' ? window.location.origin : 'https://politogyvrm.com'\n const u = new URL(url, base)\n u.searchParams.set(ACTIVE_ORG_QUERY_PARAM, orgId as string)\n // Preserve relative form when the input was relative.\n return /^https?:\\/\\//i.test(url) ? u.toString() : `${u.pathname}${u.search}${u.hash}`\n } catch {\n return url\n }\n}\n\n// ---------------------------------------------------------------------------\n// v2: auth-owned active org (Global-Rules v38 \"Auth / Active Organization\",\n// UI-Platform v46 \"URL state\"). The IdP owns the shared active org on the SSO\n// session; tokens carry `org_id` (full uuid) + `org_role`. The URL carries the\n// 7-char `org_id` tail as a PER-TAB override only: it never changes the shared\n// active org. Full uuids and the legacy 6-char tail are accepted during rollout.\n// ---------------------------------------------------------------------------\n\nexport const ORG_ID_QUERY_PARAM = 'org_id'\nexport const ORG_ID_TAIL_LENGTH = 7\nconst LEGACY_ORG_TAIL_LENGTH = 6\nconst TAIL_RE = /^[0-9a-f]{6,7}$/i\n\nconst AUTH_BASE_URL = 'https://auth.politogyvrm.com'\nconst AUTH_BASE_URL_STAGING = 'https://auth-staging.politogyvrm.com'\nconst SETTINGS_BASE_URL = 'https://settings.politogyvrm.com'\nconst SETTINGS_BASE_URL_STAGING = 'https://settings-staging.politogyvrm.com'\n\n/** The URL form of an org id: the last 7 chars of the uuid, lower case. */\nexport function orgIdTail(orgId: string | null | undefined): string | null {\n return isOrgId(orgId) ? (orgId as string).trim().toLowerCase().slice(-ORG_ID_TAIL_LENGTH) : null\n}\n\n/** A full uuid, the 7-char tail, or the legacy 6-char tail. */\nexport function isOrgSelector(v: unknown): v is string {\n return typeof v === 'string' && (UUID_RE.test(v.trim()) || TAIL_RE.test(v.trim()))\n}\n\n/**\n * Resolve a selector against the user's own org list: exact uuid first, then a\n * unique 7-char tail, then a unique 6-char tail. Ambiguous or no match → null\n * (the IdP selector rules, so the shell never picks an org the IdP would not).\n */\nexport function matchOrgSelector<T extends { id: string }>(\n selector: string | null | undefined,\n orgs: readonly T[],\n): T | null {\n if (!isOrgSelector(selector)) return null\n const sel = selector.trim().toLowerCase()\n if (UUID_RE.test(sel)) return orgs.find((o) => o.id.toLowerCase() === sel) ?? null\n if (sel.length !== ORG_ID_TAIL_LENGTH && sel.length !== LEGACY_ORG_TAIL_LENGTH) return null\n const hits = orgs.filter((o) => o.id.toLowerCase().endsWith(sel))\n return hits.length === 1 ? hits[0] : null\n}\n\n/**\n * Read the per-tab org override from a URL: `org_id` (spec), else the legacy\n * `org`. Returns the raw selector (uuid or tail), or null. Never a source for\n * the shared active org.\n */\nexport function readOrgSelectorFromUrl(search: string | null | undefined): string | null {\n if (!search) return null\n try {\n const qs = search.includes('?') ? search.slice(search.indexOf('?') + 1) : search\n const params = new URLSearchParams(qs)\n for (const key of [ORG_ID_QUERY_PARAM, ACTIVE_ORG_QUERY_PARAM]) {\n const v = params.get(key)\n if (isOrgSelector(v)) return v.trim().toLowerCase()\n }\n return null\n } catch {\n return null\n }\n}\n\n/**\n * Return `url` with `org_id=<7-char tail>` set and any legacy `org=` removed.\n * Leaves the URL unchanged when `orgId` is not a full uuid.\n */\nexport function withOrgIdParam(url: string, orgId: string | null | undefined): string {\n const tail = orgIdTail(orgId)\n if (!tail) return url\n try {\n const base = typeof window !== 'undefined' ? window.location.origin : 'https://politogyvrm.com'\n const u = new URL(url, base)\n u.searchParams.delete(ACTIVE_ORG_QUERY_PARAM)\n u.searchParams.set(ORG_ID_QUERY_PARAM, tail)\n return /^https?:\\/\\//i.test(url) ? u.toString() : `${u.pathname}${u.search}${u.hash}`\n } catch {\n return url\n }\n}\n\nexport type OrgClaims = { orgId: string; orgRole: string | null }\n\n/** Pick `org_id` / `org_role` out of a token claim set. */\nexport function orgClaimsFrom(claims: unknown): OrgClaims | null {\n if (!claims || typeof claims !== 'object') return null\n const c = claims as { org_id?: unknown; org_role?: unknown }\n if (!isOrgId(c.org_id)) return null\n return {\n orgId: (c.org_id as string).trim().toLowerCase(),\n orgRole: typeof c.org_role === 'string' ? c.org_role : null,\n }\n}\n\n/**\n * Read `org_id` / `org_role` from a JWT payload WITHOUT verifying it. For\n * display and routing only: the app server verifies the token (JWKS) and every\n * authorization decision keys on the verified claim, never on this.\n */\nexport function orgClaimsFromToken(token: string | null | undefined): OrgClaims | null {\n if (typeof token !== 'string') return null\n const part = token.split('.')[1]\n if (!part) return null\n try {\n const b64 = part.replace(/-/g, '+').replace(/_/g, '/')\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4)\n const json =\n typeof atob === 'function'\n ? decodeURIComponent(\n Array.from(atob(padded), (ch) => '%' + ch.charCodeAt(0).toString(16).padStart(2, '0')).join(''),\n )\n : ''\n return orgClaimsFrom(JSON.parse(json))\n } catch {\n return null\n }\n}\n\n/** The IdP base for the current environment (staging host → staging IdP). */\nexport function authBaseUrl(): string {\n if (typeof window !== 'undefined' && window.location.hostname.toLowerCase().includes('staging')) {\n return AUTH_BASE_URL_STAGING\n }\n return AUTH_BASE_URL\n}\n\n/**\n * The Settings app base for the current environment (staging host → staging\n * Settings). Organization settings, onboarding, activation and subscription &\n * billing live here (Reference/UI-Platform \"Retirement of app.politogyvrm.com\").\n */\nexport function settingsBaseUrl(): string {\n if (typeof window !== 'undefined' && window.location.hostname.toLowerCase().includes('staging')) {\n return SETTINGS_BASE_URL_STAGING\n }\n return SETTINGS_BASE_URL\n}\n\nexport class ActiveOrgSwitchError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'ActiveOrgSwitchError'\n }\n}\n\n/**\n * Switch the SHARED active org on the IdP: `POST {auth}/api/active-org`. The\n * IdP checks membership against VRM Core and updates the SSO session; the\n * caller then re-exchanges (server) for tokens carrying the new `org_id`.\n * Also mirrors the legacy cookie so apps not yet on v2 follow the switch.\n * Throws `ActiveOrgSwitchError` (403 = not a member, 401 = no SSO session).\n */\nexport async function switchActiveOrg(\n orgId: string,\n opts: { authBaseUrl?: string; fetch?: typeof fetch } = {},\n): Promise<OrgClaims> {\n if (!isOrgId(orgId)) throw new ActiveOrgSwitchError(400, 'org_id must be a full uuid')\n const doFetch = opts.fetch ?? fetch\n const res = await doFetch(`${opts.authBaseUrl ?? authBaseUrl()}/api/active-org`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ org_id: orgId }),\n })\n if (!res.ok) {\n let msg = `active-org switch failed (${res.status})`\n try {\n const body = (await res.json()) as { error?: string }\n if (body?.error) msg = body.error\n } catch {\n /* no body */\n }\n throw new ActiveOrgSwitchError(res.status, msg)\n }\n const claims = orgClaimsFrom(await res.json().catch(() => null))\n if (!claims) throw new ActiveOrgSwitchError(502, 'active-org switch returned no org_id')\n writeActiveOrgCookie(claims.orgId)\n return claims\n}\n\n/**\n * URL-sync rule for the address-bar org selector. Returns the new href, or\n * null when nothing should be written. A bare URL (no `org_id`, no legacy\n * `org`) stays bare: the shell must never inject `org_id` on its own, because\n * a self-injected selector then acts as a per-tab override and beats the token\n * org and the cross-app `politogy_active_org` trigger. Only a selector the user\n * brought (or a real switch put there) is canonicalized to the current org.\n */\nexport function syncOrgIdInUrl(href: string, orgId: string | null | undefined): string | null {\n const tail = orgIdTail(orgId)\n if (!tail) return null\n let u: URL\n try {\n u = new URL(href)\n } catch {\n return null\n }\n const hasLegacy = u.searchParams.has(ACTIVE_ORG_QUERY_PARAM)\n const current = u.searchParams.get(ORG_ID_QUERY_PARAM)\n if (!hasLegacy && current === null) return null\n if (!hasLegacy && current === tail) return null\n u.searchParams.delete(ACTIVE_ORG_QUERY_PARAM)\n u.searchParams.set(ORG_ID_QUERY_PARAM, tail)\n return u.toString()\n}\n"]}
|
package/dist/active-org.d.cts
CHANGED
|
@@ -71,6 +71,12 @@ declare function orgClaimsFrom(claims: unknown): OrgClaims | null;
|
|
|
71
71
|
declare function orgClaimsFromToken(token: string | null | undefined): OrgClaims | null;
|
|
72
72
|
/** The IdP base for the current environment (staging host → staging IdP). */
|
|
73
73
|
declare function authBaseUrl(): string;
|
|
74
|
+
/**
|
|
75
|
+
* The Settings app base for the current environment (staging host → staging
|
|
76
|
+
* Settings). Organization settings, onboarding, activation and subscription &
|
|
77
|
+
* billing live here (Reference/UI-Platform "Retirement of app.politogyvrm.com").
|
|
78
|
+
*/
|
|
79
|
+
declare function settingsBaseUrl(): string;
|
|
74
80
|
declare class ActiveOrgSwitchError extends Error {
|
|
75
81
|
readonly status: number;
|
|
76
82
|
constructor(status: number, message: string);
|
|
@@ -96,4 +102,4 @@ declare function switchActiveOrg(orgId: string, opts?: {
|
|
|
96
102
|
*/
|
|
97
103
|
declare function syncOrgIdInUrl(href: string, orgId: string | null | undefined): string | null;
|
|
98
104
|
|
|
99
|
-
export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, ActiveOrgSwitchError, ORG_ID_QUERY_PARAM, ORG_ID_TAIL_LENGTH, type OrgClaims, activeOrgParam, authBaseUrl, isOrgId, isOrgSelector, matchOrgSelector, orgClaimsFrom, orgClaimsFromToken, orgIdTail, readActiveOrgCookie, readActiveOrgFromUrl, readOrgSelectorFromUrl, resolveActiveOrg, switchActiveOrg, syncOrgIdInUrl, withActiveOrgParam, withOrgIdParam, writeActiveOrgCookie };
|
|
105
|
+
export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, ActiveOrgSwitchError, ORG_ID_QUERY_PARAM, ORG_ID_TAIL_LENGTH, type OrgClaims, activeOrgParam, authBaseUrl, isOrgId, isOrgSelector, matchOrgSelector, orgClaimsFrom, orgClaimsFromToken, orgIdTail, readActiveOrgCookie, readActiveOrgFromUrl, readOrgSelectorFromUrl, resolveActiveOrg, settingsBaseUrl, switchActiveOrg, syncOrgIdInUrl, withActiveOrgParam, withOrgIdParam, writeActiveOrgCookie };
|
package/dist/active-org.d.ts
CHANGED
|
@@ -71,6 +71,12 @@ declare function orgClaimsFrom(claims: unknown): OrgClaims | null;
|
|
|
71
71
|
declare function orgClaimsFromToken(token: string | null | undefined): OrgClaims | null;
|
|
72
72
|
/** The IdP base for the current environment (staging host → staging IdP). */
|
|
73
73
|
declare function authBaseUrl(): string;
|
|
74
|
+
/**
|
|
75
|
+
* The Settings app base for the current environment (staging host → staging
|
|
76
|
+
* Settings). Organization settings, onboarding, activation and subscription &
|
|
77
|
+
* billing live here (Reference/UI-Platform "Retirement of app.politogyvrm.com").
|
|
78
|
+
*/
|
|
79
|
+
declare function settingsBaseUrl(): string;
|
|
74
80
|
declare class ActiveOrgSwitchError extends Error {
|
|
75
81
|
readonly status: number;
|
|
76
82
|
constructor(status: number, message: string);
|
|
@@ -96,4 +102,4 @@ declare function switchActiveOrg(orgId: string, opts?: {
|
|
|
96
102
|
*/
|
|
97
103
|
declare function syncOrgIdInUrl(href: string, orgId: string | null | undefined): string | null;
|
|
98
104
|
|
|
99
|
-
export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, ActiveOrgSwitchError, ORG_ID_QUERY_PARAM, ORG_ID_TAIL_LENGTH, type OrgClaims, activeOrgParam, authBaseUrl, isOrgId, isOrgSelector, matchOrgSelector, orgClaimsFrom, orgClaimsFromToken, orgIdTail, readActiveOrgCookie, readActiveOrgFromUrl, readOrgSelectorFromUrl, resolveActiveOrg, switchActiveOrg, syncOrgIdInUrl, withActiveOrgParam, withOrgIdParam, writeActiveOrgCookie };
|
|
105
|
+
export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, ActiveOrgSwitchError, ORG_ID_QUERY_PARAM, ORG_ID_TAIL_LENGTH, type OrgClaims, activeOrgParam, authBaseUrl, isOrgId, isOrgSelector, matchOrgSelector, orgClaimsFrom, orgClaimsFromToken, orgIdTail, readActiveOrgCookie, readActiveOrgFromUrl, readOrgSelectorFromUrl, resolveActiveOrg, settingsBaseUrl, switchActiveOrg, syncOrgIdInUrl, withActiveOrgParam, withOrgIdParam, writeActiveOrgCookie };
|
package/dist/active-org.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, ActiveOrgSwitchError, ORG_ID_QUERY_PARAM, ORG_ID_TAIL_LENGTH, activeOrgParam, authBaseUrl, isOrgId, isOrgSelector, matchOrgSelector, orgClaimsFrom, orgClaimsFromToken, orgIdTail, readActiveOrgCookie, readActiveOrgFromUrl, readOrgSelectorFromUrl, resolveActiveOrg, switchActiveOrg, syncOrgIdInUrl, withActiveOrgParam, withOrgIdParam, writeActiveOrgCookie } from './chunk-
|
|
1
|
+
export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, ActiveOrgSwitchError, ORG_ID_QUERY_PARAM, ORG_ID_TAIL_LENGTH, activeOrgParam, authBaseUrl, isOrgId, isOrgSelector, matchOrgSelector, orgClaimsFrom, orgClaimsFromToken, orgIdTail, readActiveOrgCookie, readActiveOrgFromUrl, readOrgSelectorFromUrl, resolveActiveOrg, settingsBaseUrl, switchActiveOrg, syncOrgIdInUrl, withActiveOrgParam, withOrgIdParam, writeActiveOrgCookie } from './chunk-5LO5W7W5.js';
|
|
2
2
|
//# sourceMappingURL=active-org.js.map
|
|
3
3
|
//# sourceMappingURL=active-org.js.map
|
package/dist/app-chrome.cjs
CHANGED
|
@@ -141,6 +141,8 @@ var LEGACY_ORG_TAIL_LENGTH = 6;
|
|
|
141
141
|
var TAIL_RE = /^[0-9a-f]{6,7}$/i;
|
|
142
142
|
var AUTH_BASE_URL = "https://auth.politogyvrm.com";
|
|
143
143
|
var AUTH_BASE_URL_STAGING = "https://auth-staging.politogyvrm.com";
|
|
144
|
+
var SETTINGS_BASE_URL = "https://settings.politogyvrm.com";
|
|
145
|
+
var SETTINGS_BASE_URL_STAGING = "https://settings-staging.politogyvrm.com";
|
|
144
146
|
function orgIdTail(orgId) {
|
|
145
147
|
return isOrgId(orgId) ? orgId.trim().toLowerCase().slice(-ORG_ID_TAIL_LENGTH) : null;
|
|
146
148
|
}
|
|
@@ -184,6 +186,12 @@ function authBaseUrl() {
|
|
|
184
186
|
}
|
|
185
187
|
return AUTH_BASE_URL;
|
|
186
188
|
}
|
|
189
|
+
function settingsBaseUrl() {
|
|
190
|
+
if (typeof window !== "undefined" && window.location.hostname.toLowerCase().includes("staging")) {
|
|
191
|
+
return SETTINGS_BASE_URL_STAGING;
|
|
192
|
+
}
|
|
193
|
+
return SETTINGS_BASE_URL;
|
|
194
|
+
}
|
|
187
195
|
var ActiveOrgSwitchError = class extends Error {
|
|
188
196
|
constructor(status, message) {
|
|
189
197
|
super(message);
|
|
@@ -488,8 +496,9 @@ function notificationsFeedToPayload(feed, orgId) {
|
|
|
488
496
|
};
|
|
489
497
|
}
|
|
490
498
|
var CORE_APP_ORIGIN = "https://app.politogyvrm.com";
|
|
491
|
-
var
|
|
492
|
-
var
|
|
499
|
+
var SETTINGS_APP_ORIGIN = "https://settings.politogyvrm.com";
|
|
500
|
+
var CORE_BILLING_PATH = "/organization-settings/subscription-billing";
|
|
501
|
+
var CORE_BILLING_URL = `${SETTINGS_APP_ORIGIN}${CORE_BILLING_PATH}`;
|
|
493
502
|
var QUARANTINED_CONTACTS_URL = `https://contacts.politogyvrm.com/duplicates`;
|
|
494
503
|
var SPECS_ORIGIN = "https://specs.politogyvrm.com";
|
|
495
504
|
function withOrgParam(url, orgId) {
|
|
@@ -3748,7 +3757,9 @@ function NavTopBarActionButton({ action }) {
|
|
|
3748
3757
|
var BETA_BANNER_DEFAULTS = {
|
|
3749
3758
|
text: "Our product is in Beta. Thank you for participating in the Pilot Program. ",
|
|
3750
3759
|
linkLabel: "You can click here to see the Terms & Conditions for Beta users.",
|
|
3751
|
-
|
|
3760
|
+
// Legal pages live on Politogy ID (Reference/UI-Platform "Retirement of
|
|
3761
|
+
// app.politogyvrm.com"); resolved per render so staging stays in staging.
|
|
3762
|
+
href: () => `${authBaseUrl()}/beta-terms`
|
|
3752
3763
|
};
|
|
3753
3764
|
var BETA_BANNER_DISMISS_KEY = "politogy_beta_banner_dismissed";
|
|
3754
3765
|
function clearBetaBannerDismissal() {
|
|
@@ -3761,7 +3772,7 @@ function clearBetaBannerDismissal() {
|
|
|
3761
3772
|
function NavBetaBanner({ text, linkLabel, href, dismissible = true }) {
|
|
3762
3773
|
const body = text ?? BETA_BANNER_DEFAULTS.text;
|
|
3763
3774
|
const label = linkLabel ?? BETA_BANNER_DEFAULTS.linkLabel;
|
|
3764
|
-
const target = href ?? BETA_BANNER_DEFAULTS.href;
|
|
3775
|
+
const target = href ?? BETA_BANNER_DEFAULTS.href();
|
|
3765
3776
|
const [dismissed, setDismissed] = react.useState(false);
|
|
3766
3777
|
react.useEffect(() => {
|
|
3767
3778
|
if (!dismissible) return;
|
|
@@ -4552,7 +4563,11 @@ var POLITOGY_APPS = {
|
|
|
4552
4563
|
// Relationship Dashboard, the Relationship Mode landing page, and nothing
|
|
4553
4564
|
// else (Specs/Relationship-Dashboard §1A "Relationship Mode only"); Settings
|
|
4554
4565
|
// hosts org settings & billing (Specs/Organization-Settings).
|
|
4555
|
-
|
|
4566
|
+
// PINNED to Relationship: left unscoped, its frame took the user's last
|
|
4567
|
+
// mode selection, so after a visit to Campaign Mode the Dashboard drew the
|
|
4568
|
+
// red Campaign rail and "Campaign mode" label over the Relationship rows
|
|
4569
|
+
// (Left-Navigation §11.1 "the rail never shows a mix of two modes").
|
|
4570
|
+
dashboard: { title: "Dashboard", origin: "https://dashboard.politogyvrm.com", storagePrefix: "dashboard", mode: "relationship" },
|
|
4556
4571
|
// Battleground (the Campaign Mode landing page) and District Data are
|
|
4557
4572
|
// Campaign Mode features, each its own app on its own host (Specs/Left-
|
|
4558
4573
|
// Navigation §9.2, §13, §14; Reference/UI-Platform, decided 2026-10-12).
|
|
@@ -4895,16 +4910,6 @@ function FrameBody({
|
|
|
4895
4910
|
}
|
|
4896
4911
|
var ORG_STORAGE_KEY = "vrm.activeOrg";
|
|
4897
4912
|
var MODE_STORAGE_KEY = "vrm.activeMode";
|
|
4898
|
-
var CORE_BASE_URL = "https://app.politogyvrm.com";
|
|
4899
|
-
var CORE_BASE_URL_STAGING = "https://staging-app.politogyvrm.com";
|
|
4900
|
-
function getCoreBaseUrl() {
|
|
4901
|
-
if (typeof window !== "undefined") {
|
|
4902
|
-
if (window.location.hostname.toLowerCase().includes("staging")) {
|
|
4903
|
-
return CORE_BASE_URL_STAGING;
|
|
4904
|
-
}
|
|
4905
|
-
}
|
|
4906
|
-
return CORE_BASE_URL;
|
|
4907
|
-
}
|
|
4908
4913
|
var ChromeStateContext = react.createContext(null);
|
|
4909
4914
|
function useChromeState() {
|
|
4910
4915
|
return react.useContext(ChromeStateContext);
|
|
@@ -4959,7 +4964,13 @@ function TokenChromeStateProvider({
|
|
|
4959
4964
|
);
|
|
4960
4965
|
}, [overrideOrg, tokenOrg.orgId]);
|
|
4961
4966
|
react.useEffect(() => {
|
|
4962
|
-
if (pendingOrg
|
|
4967
|
+
if (!pendingOrg || tokenOrg.orgId !== pendingOrg) return;
|
|
4968
|
+
setPendingOrg(null);
|
|
4969
|
+
try {
|
|
4970
|
+
const next = syncOrgIdInUrl(window.location.href, pendingOrg);
|
|
4971
|
+
if (next) window.history.replaceState(window.history.state, "", next);
|
|
4972
|
+
} catch {
|
|
4973
|
+
}
|
|
4963
4974
|
}, [pendingOrg, tokenOrg.orgId]);
|
|
4964
4975
|
const setOrgId = react.useCallback((id) => {
|
|
4965
4976
|
const current = tokenOrgRef.current;
|
|
@@ -5022,10 +5033,11 @@ function TokenChromeStateProvider({
|
|
|
5022
5033
|
setOrgId,
|
|
5023
5034
|
orgs,
|
|
5024
5035
|
orgsLoading: !tokenOrg.orgs && orgsFetching && !orgsError && !fetchedOrgs,
|
|
5036
|
+
orgPending: pendingOrg !== null,
|
|
5025
5037
|
navDisplay,
|
|
5026
5038
|
setNavDisplay
|
|
5027
5039
|
}),
|
|
5028
|
-
[orgId, setOrgId, orgs, tokenOrg.orgs, orgsFetching, orgsError, fetchedOrgs, navDisplay, setNavDisplay]
|
|
5040
|
+
[orgId, setOrgId, orgs, tokenOrg.orgs, orgsFetching, orgsError, fetchedOrgs, pendingOrg, navDisplay, setNavDisplay]
|
|
5029
5041
|
);
|
|
5030
5042
|
return /* @__PURE__ */ jsxRuntime.jsx(ChromeStateContext.Provider, { value, children });
|
|
5031
5043
|
}
|
|
@@ -5168,6 +5180,7 @@ function ChromeStateProvider({
|
|
|
5168
5180
|
rawRole: o.rawRole ?? null
|
|
5169
5181
|
})),
|
|
5170
5182
|
orgsLoading: false,
|
|
5183
|
+
orgPending: false,
|
|
5171
5184
|
navDisplay,
|
|
5172
5185
|
setNavDisplay
|
|
5173
5186
|
};
|
|
@@ -5186,6 +5199,7 @@ function ChromeStateProvider({
|
|
|
5186
5199
|
// Loading until the fetch settles. On error we stop loading so the frame
|
|
5187
5200
|
// falls back to the app label rather than shimmering forever.
|
|
5188
5201
|
orgsLoading: orgsFetching && !orgsError && !orgs,
|
|
5202
|
+
orgPending: false,
|
|
5189
5203
|
navDisplay,
|
|
5190
5204
|
setNavDisplay
|
|
5191
5205
|
};
|
|
@@ -5392,9 +5406,15 @@ function useIsSuperAdmin() {
|
|
|
5392
5406
|
const state = useChromeState();
|
|
5393
5407
|
return (state?.orgs ?? []).some((o) => /super/i.test(o.rawRole ?? ""));
|
|
5394
5408
|
}
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5409
|
+
function footerLinks() {
|
|
5410
|
+
const auth = authBaseUrl();
|
|
5411
|
+
return [
|
|
5412
|
+
{ label: "Privacy", href: `${auth}/privacy-policy`, external: true },
|
|
5413
|
+
{ label: "Terms", href: `${auth}/terms-and-conditions`, external: true },
|
|
5414
|
+
...FOOTER_LINKS_STATIC
|
|
5415
|
+
];
|
|
5416
|
+
}
|
|
5417
|
+
var FOOTER_LINKS_STATIC = [
|
|
5398
5418
|
{
|
|
5399
5419
|
label: "Subscriptions",
|
|
5400
5420
|
href: "https://politogyvrm.com/subscription-cancellation-policy",
|
|
@@ -5403,7 +5423,7 @@ var FOOTER_LINKS = [
|
|
|
5403
5423
|
];
|
|
5404
5424
|
function useChromeFooter(meta) {
|
|
5405
5425
|
return react.useMemo(
|
|
5406
|
-
() => ({ links:
|
|
5426
|
+
() => ({ links: footerLinks(), ...meta }),
|
|
5407
5427
|
[meta.envLabel, meta.version, meta.commit, meta.commitDate, meta.commitAuthor]
|
|
5408
5428
|
);
|
|
5409
5429
|
}
|
|
@@ -5424,15 +5444,17 @@ function useChromeUserMenu({
|
|
|
5424
5444
|
}) {
|
|
5425
5445
|
const state = useChromeState();
|
|
5426
5446
|
const activeOrg = state?.orgs.find((o) => o.id === state.orgId) ?? null;
|
|
5427
|
-
const
|
|
5428
|
-
const
|
|
5447
|
+
const tail = orgIdTail(activeOrg?.id);
|
|
5448
|
+
const orgSuffix = tail ? `?${ORG_ID_QUERY_PARAM}=${tail}` : "";
|
|
5449
|
+
const auth = authBaseUrl();
|
|
5450
|
+
const settings = settingsBaseUrl();
|
|
5429
5451
|
return {
|
|
5430
|
-
// My Profile is global — NOT org-suffixed
|
|
5431
|
-
myProfileHref: `${
|
|
5432
|
-
organizationSettingsHref: `${
|
|
5452
|
+
// My Profile is global — NOT org-suffixed.
|
|
5453
|
+
myProfileHref: `${auth}/my-profile`,
|
|
5454
|
+
organizationSettingsHref: `${settings}/organization-settings${orgSuffix}`,
|
|
5433
5455
|
roles: (state?.orgs ?? []).filter((o) => o.rawRole || o.role).map((o) => ({ role: o.rawRole ?? o.role, orgName: o.name })),
|
|
5434
5456
|
accounts: [{ id: userId, name: userName, email, avatarUrl: null, active: true }],
|
|
5435
|
-
addAccountHref: `${
|
|
5457
|
+
addAccountHref: `${auth}/my-profile#accounts`,
|
|
5436
5458
|
// No theme control: the product is light only (Reference/UI-Platform
|
|
5437
5459
|
// "Theme — light only"; shell 0.73.0 stopped applying dark). The account
|
|
5438
5460
|
// menu rendered an "Appearance" radio with one option until 0.77.0.
|
|
@@ -5444,14 +5466,19 @@ var MODE_DEFS = [
|
|
|
5444
5466
|
id: "relationship",
|
|
5445
5467
|
label: "Relationship Mode",
|
|
5446
5468
|
icon: lu.LuContactRound,
|
|
5447
|
-
|
|
5469
|
+
// The mode's landing page (Left-Navigation §11.1), with the mode stated in
|
|
5470
|
+
// the URL. These used to point at the retired VRM core
|
|
5471
|
+
// (`app.politogyvrm.com/relationship/dashboard`), whose redirect dropped
|
|
5472
|
+
// the mode: the Dashboard app then chose by the active-mode cookie, so
|
|
5473
|
+
// "Relationship" from a Campaign page landed back on Battleground.
|
|
5474
|
+
href: "https://dashboard.politogyvrm.com/dashboard?mode=relationship",
|
|
5448
5475
|
orgQuery: true
|
|
5449
5476
|
},
|
|
5450
5477
|
{
|
|
5451
5478
|
id: "campaign",
|
|
5452
5479
|
label: "Campaign Mode",
|
|
5453
5480
|
icon: lu.LuMegaphone,
|
|
5454
|
-
href:
|
|
5481
|
+
href: "https://battleground.politogyvrm.com/battleground?mode=campaign",
|
|
5455
5482
|
orgQuery: true
|
|
5456
5483
|
},
|
|
5457
5484
|
{
|
|
@@ -5464,12 +5491,21 @@ var MODE_DEFS = [
|
|
|
5464
5491
|
orgQuery: true
|
|
5465
5492
|
}
|
|
5466
5493
|
];
|
|
5494
|
+
function modeLandingHref(id, opts = {}) {
|
|
5495
|
+
const def = MODE_DEFS.find((m) => m.id === id);
|
|
5496
|
+
if (!def) return null;
|
|
5497
|
+
const staging = (opts.hostname ?? "").toLowerCase().includes("staging");
|
|
5498
|
+
const href = staging ? def.href.replace(/^https:\/\/(dashboard|battleground)\.politogyvrm\.com/, "https://$1-staging.politogyvrm.com") : def.href;
|
|
5499
|
+
if (!opts.orgId) return href;
|
|
5500
|
+
return `${href}${href.includes("?") ? "&" : "?"}org=${opts.orgId}`;
|
|
5501
|
+
}
|
|
5467
5502
|
function useChromeMode(arg = {}) {
|
|
5468
5503
|
const { fallbackModeId = "relationship", pinnedModeId } = typeof arg === "string" ? { fallbackModeId: arg, pinnedModeId: void 0 } : arg;
|
|
5469
5504
|
const ctx = react.useContext(ChromeStateContext);
|
|
5470
5505
|
const dedupedFetch = useNavChromeDedupedFetch();
|
|
5471
5506
|
const { me, loaded: meLoaded } = useChromeMe();
|
|
5472
5507
|
const orgId = ctx?.orgId ?? null;
|
|
5508
|
+
const orgPending = ctx?.orgPending ?? false;
|
|
5473
5509
|
const [entitled, setEntitled] = react.useState(null);
|
|
5474
5510
|
const [selectedModeId, setSelectedModeId] = react.useState(null);
|
|
5475
5511
|
react.useEffect(() => {
|
|
@@ -5481,6 +5517,7 @@ function useChromeMode(arg = {}) {
|
|
|
5481
5517
|
}, []);
|
|
5482
5518
|
react.useEffect(() => {
|
|
5483
5519
|
if (!dedupedFetch || !orgId || !meLoaded) return;
|
|
5520
|
+
if (orgPending) return;
|
|
5484
5521
|
const bootMe = me;
|
|
5485
5522
|
if (bootMe && bootMe.account === orgId && bootMe.modes !== void 0) {
|
|
5486
5523
|
setEntitled(bootMe.modes ?? null);
|
|
@@ -5496,12 +5533,11 @@ function useChromeMode(arg = {}) {
|
|
|
5496
5533
|
return () => {
|
|
5497
5534
|
cancelled = true;
|
|
5498
5535
|
};
|
|
5499
|
-
}, [dedupedFetch, me, meLoaded, orgId]);
|
|
5536
|
+
}, [dedupedFetch, me, meLoaded, orgId, orgPending]);
|
|
5500
5537
|
const options = MODE_DEFS.filter((m) => entitled?.[m.id]);
|
|
5501
5538
|
const pinned = pinnedModeId ? MODE_DEFS.find((m) => m.id === pinnedModeId) ?? null : null;
|
|
5502
5539
|
const selected = selectedModeId ? options.find((m) => m.id === selectedModeId) ?? null : null;
|
|
5503
5540
|
const current = pinned ?? selected ?? options[0] ?? MODE_DEFS.find((m) => m.id === fallbackModeId) ?? MODE_DEFS[0];
|
|
5504
|
-
const suffix = orgId ? `?org=${orgId}` : "";
|
|
5505
5541
|
return {
|
|
5506
5542
|
label: current.label,
|
|
5507
5543
|
icon: current.icon,
|
|
@@ -5523,10 +5559,11 @@ function useChromeMode(arg = {}) {
|
|
|
5523
5559
|
localStorage.setItem(MODE_STORAGE_KEY, id);
|
|
5524
5560
|
} catch {
|
|
5525
5561
|
}
|
|
5526
|
-
const href = def.href.replace(CORE_BASE_URL, getCoreBaseUrl());
|
|
5527
|
-
const sep = href.includes("?") ? "&" : "?";
|
|
5528
5562
|
window.location.assign(
|
|
5529
|
-
|
|
5563
|
+
modeLandingHref(id, {
|
|
5564
|
+
orgId: def.orgQuery ? orgId : null,
|
|
5565
|
+
hostname: window.location.hostname
|
|
5566
|
+
}) ?? def.href
|
|
5530
5567
|
);
|
|
5531
5568
|
} : void 0
|
|
5532
5569
|
};
|
|
@@ -5538,7 +5575,9 @@ exports.POLITOGY_APPS = POLITOGY_APPS;
|
|
|
5538
5575
|
exports.PolitogyAppChrome = PolitogyAppChrome;
|
|
5539
5576
|
exports.PolitogyAppFrame = PolitogyAppFrame;
|
|
5540
5577
|
exports.deriveBreadcrumbs = deriveBreadcrumbs;
|
|
5578
|
+
exports.footerLinks = footerLinks;
|
|
5541
5579
|
exports.looksLikeEmail = looksLikeEmail;
|
|
5580
|
+
exports.modeLandingHref = modeLandingHref;
|
|
5542
5581
|
exports.resolveIdentity = resolveIdentity;
|
|
5543
5582
|
exports.standardNavIcons = standardNavIcons;
|
|
5544
5583
|
exports.useChromeFooter = useChromeFooter;
|