@the-portland-company/shell 0.73.2 → 0.75.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/app-chrome.cjs +157 -40
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +1 -2
- package/dist/app-chrome.d.ts +1 -2
- package/dist/app-chrome.js +10 -3
- package/dist/app-chrome.js.map +1 -1
- package/dist/chunk-MY6G3LMN.js +125 -0
- package/dist/chunk-MY6G3LMN.js.map +1 -0
- package/dist/{chunk-5DHPCG7W.js → chunk-QUXD4PZ7.js} +68 -43
- package/dist/chunk-QUXD4PZ7.js.map +1 -0
- package/dist/index.cjs +84 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -2
- package/dist/index.d.ts +61 -2
- package/dist/index.js +3 -20
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +150 -40
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +25 -4
- package/dist/native.d.ts +25 -4
- package/dist/native.js +2 -2
- package/package.json +1 -1
- package/dist/FormColumn-DsNP6NVG.d.cts +0 -21
- package/dist/FormColumn-DsNP6NVG.d.ts +0 -21
- package/dist/chunk-5DHPCG7W.js.map +0 -1
- package/dist/chunk-LPSBSAF3.js +0 -26
- package/dist/chunk-LPSBSAF3.js.map +0 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useMemo, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
// src/components/FormColumn.tsx
|
|
6
|
+
function FormColumn({ children, className }) {
|
|
7
|
+
return /* @__PURE__ */ jsx("div", { "data-shell-form-column": "", className: clsx("w-full max-w-[960px]", className), children });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/lib/deprecation.ts
|
|
11
|
+
var warned = /* @__PURE__ */ new Set();
|
|
12
|
+
function isDev() {
|
|
13
|
+
try {
|
|
14
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function warnDeprecated(key, message) {
|
|
20
|
+
if (!isDev() || warned.has(key)) return;
|
|
21
|
+
warned.add(key);
|
|
22
|
+
console.warn(`[@the-portland-company/shell] ${message}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/speculation/emitRules.ts
|
|
26
|
+
function buildSpeculationRules(currentApp, registry) {
|
|
27
|
+
const others = registry.filter(
|
|
28
|
+
(entry) => entry.id !== currentApp && entry.visible !== false
|
|
29
|
+
);
|
|
30
|
+
if (others.length === 0) return null;
|
|
31
|
+
const payload = {
|
|
32
|
+
prerender: [
|
|
33
|
+
{
|
|
34
|
+
urls: others.map((entry) => entry.path),
|
|
35
|
+
eagerness: "moderate"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
return JSON.stringify(payload);
|
|
40
|
+
}
|
|
41
|
+
var DESTRUCTIVE_LABEL_PATTERNS = ["sign out", "signout", "log out", "logout", "delete"];
|
|
42
|
+
var DESTRUCTIVE_PATH_PATTERNS = ["/signout", "/sign-out", "/logout", "/log-out"];
|
|
43
|
+
function isDestructive(item) {
|
|
44
|
+
const label = item.label.toLowerCase();
|
|
45
|
+
if (DESTRUCTIVE_LABEL_PATTERNS.some((p) => label.includes(p))) return true;
|
|
46
|
+
const href = (item.href ?? "").toLowerCase();
|
|
47
|
+
return DESTRUCTIVE_PATH_PATTERNS.some((p) => href.includes(p));
|
|
48
|
+
}
|
|
49
|
+
function collectSiblingAppUrls(items, currentOrigin) {
|
|
50
|
+
const urls = /* @__PURE__ */ new Set();
|
|
51
|
+
const walk = (list) => {
|
|
52
|
+
for (const item of list) {
|
|
53
|
+
if (item.kind === "section") continue;
|
|
54
|
+
if (item.href && !isDestructive(item)) {
|
|
55
|
+
try {
|
|
56
|
+
const url = new URL(item.href, currentOrigin);
|
|
57
|
+
if (url.origin !== currentOrigin && /^https?:$/.test(url.protocol)) {
|
|
58
|
+
urls.add(url.href);
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (item.children?.length) walk(item.children);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
walk(items);
|
|
67
|
+
return Array.from(urls);
|
|
68
|
+
}
|
|
69
|
+
function buildNavSpeculationRules(items, currentOrigin = typeof window !== "undefined" ? window.location.origin : "") {
|
|
70
|
+
const urls = collectSiblingAppUrls(items, currentOrigin);
|
|
71
|
+
if (urls.length === 0) return null;
|
|
72
|
+
return {
|
|
73
|
+
prerender: [{ source: "list", urls, eagerness: "moderate" }]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
var SCRIPT_ID = "politogy-speculation-rules";
|
|
77
|
+
var PREFETCH_ATTR = "data-politogy-speculation-prefetch";
|
|
78
|
+
function SpeculationRulesScript({ items }) {
|
|
79
|
+
const origin = typeof window !== "undefined" ? window.location.origin : "";
|
|
80
|
+
const rules = useMemo(() => buildNavSpeculationRules(items, origin), [items, origin]);
|
|
81
|
+
const json = rules ? JSON.stringify(rules) : null;
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!json || typeof document === "undefined") return;
|
|
84
|
+
let el = document.getElementById(SCRIPT_ID);
|
|
85
|
+
if (!el) {
|
|
86
|
+
el = document.createElement("script");
|
|
87
|
+
el.id = SCRIPT_ID;
|
|
88
|
+
el.type = "speculationrules";
|
|
89
|
+
document.head.appendChild(el);
|
|
90
|
+
}
|
|
91
|
+
el.textContent = json;
|
|
92
|
+
return () => {
|
|
93
|
+
};
|
|
94
|
+
}, [json]);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
97
|
+
const supportsSpecRules = typeof HTMLScriptElement !== "undefined" && typeof HTMLScriptElement.supports === "function" && HTMLScriptElement.supports(
|
|
98
|
+
"speculationrules"
|
|
99
|
+
);
|
|
100
|
+
if (supportsSpecRules) return;
|
|
101
|
+
const urls = collectSiblingAppUrls(items, origin);
|
|
102
|
+
if (urls.length === 0) return;
|
|
103
|
+
const schedule = typeof window.requestIdleCallback === "function" ? window.requestIdleCallback : (cb) => setTimeout(cb, 2e3);
|
|
104
|
+
const added = [];
|
|
105
|
+
schedule(() => {
|
|
106
|
+
for (const url of urls) {
|
|
107
|
+
if (document.querySelector(`link[${PREFETCH_ATTR}][href="${url}"]`)) continue;
|
|
108
|
+
const link = document.createElement("link");
|
|
109
|
+
link.rel = "prefetch";
|
|
110
|
+
link.href = url;
|
|
111
|
+
link.setAttribute(PREFETCH_ATTR, "");
|
|
112
|
+
document.head.appendChild(link);
|
|
113
|
+
added.push(link);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return () => {
|
|
117
|
+
added.forEach((l) => l.remove());
|
|
118
|
+
};
|
|
119
|
+
}, [items, origin]);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { FormColumn, SpeculationRulesScript, buildNavSpeculationRules, buildSpeculationRules, collectSiblingAppUrls, warnDeprecated };
|
|
124
|
+
//# sourceMappingURL=chunk-MY6G3LMN.js.map
|
|
125
|
+
//# sourceMappingURL=chunk-MY6G3LMN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/FormColumn.tsx","../src/lib/deprecation.ts","../src/speculation/emitRules.ts","../src/speculation/SpeculationRulesScript.tsx"],"names":[],"mappings":"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","file":"chunk-MY6G3LMN.js","sourcesContent":["import type { ReactNode } from 'react';\nimport clsx from 'clsx';\n\ntype FormColumnProps = {\n children: ReactNode;\n className?: string;\n};\n\n/**\n * The ONE constrained layout inside the frame's fluid `<main>` (Brand guideline\n * \"Content width is fluid\"): a single left-aligned column, 960px max, for form\n * and settings pages — single-record edit forms, Organization Settings tabs,\n * My Profile, activation and setup flows. Data surfaces (tables, dashboards,\n * maps) never use it; they take the full fluid width.\n *\n * It adds no gutter of its own — `<main>` already owns the 24px / 16px gutter —\n * and it is never centered. There is no width prop on purpose: a per-page\n * max-width is exactly what the guideline forbids.\n */\nexport default function FormColumn({ children, className }: FormColumnProps) {\n return (\n <div data-shell-form-column=\"\" className={clsx('w-full max-w-[960px]', className)}>\n {children}\n </div>\n );\n}\n","// One-time console warnings for retired shell API, in development only.\n\nconst warned = new Set<string>()\n\nfunction isDev(): boolean {\n try {\n return typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production'\n } catch {\n return false\n }\n}\n\n/** Warn once per `key` in dev; silent in production builds. */\nexport function warnDeprecated(key: string, message: string): void {\n if (!isDev() || warned.has(key)) return\n warned.add(key)\n console.warn(`[@the-portland-company/shell] ${message}`)\n}\n\n/** Test hook: forget which warnings have fired. */\nexport function resetDeprecationWarnings(): void {\n warned.clear()\n}\n","import type { AppRegistryEntry } from '../types'\nimport type { NavItem } from '../native/index'\n\n/**\n * Build a Speculation Rules JSON payload that prerenders other apps in the\n * registry on moderate eagerness. Returns null when there are no eligible\n * other apps to prerender (e.g., this is the only app in the registry).\n *\n * @deprecated Legacy `AppRegistryEntry[]`-based registry is unused by any\n * current consumer. Cross-app sidebar nav now ships real `NavItem[]` trees\n * (`standardPolitogyNav`) — use `buildNavSpeculationRules` instead, which this\n * now delegates to.\n *\n * The host app is expected to render this as\n * `<script type=\"speculationrules\">{json}</script>` in <head> at runtime.\n */\nexport function buildSpeculationRules(\n currentApp: string,\n registry: AppRegistryEntry[],\n): string | null {\n const others = registry.filter(\n (entry) => entry.id !== currentApp && entry.visible !== false,\n )\n if (others.length === 0) return null\n const payload = {\n prerender: [\n {\n urls: others.map((entry) => entry.path),\n eagerness: 'moderate' as const,\n },\n ],\n }\n return JSON.stringify(payload)\n}\n\n/**\n * Labels the shell will never prerender or prefetch, even if a consumer's nav\n * tree somehow carries an `href` for them (sign-out is normally a footer\n * `onClick`, never a `NavItem.href` — this is a second guard, not the primary\n * one). Case-insensitive substring match against the row's label.\n */\nconst DESTRUCTIVE_LABEL_PATTERNS = ['sign out', 'signout', 'log out', 'logout', 'delete']\n\n/** URL substrings that mark a link as destructive/non-idempotent regardless of label. */\nconst DESTRUCTIVE_PATH_PATTERNS = ['/signout', '/sign-out', '/logout', '/log-out']\n\nfunction isDestructive(item: { label: string; href?: string }): boolean {\n const label = item.label.toLowerCase()\n if (DESTRUCTIVE_LABEL_PATTERNS.some((p) => label.includes(p))) return true\n const href = (item.href ?? '').toLowerCase()\n return DESTRUCTIVE_PATH_PATTERNS.some((p) => href.includes(p))\n}\n\n/**\n * Walk a `NavItem[]` tree (as produced by `standardPolitogyNav`) and collect\n * every absolute, cross-origin, non-destructive, GET-safe link — the\n * candidate set for prerender/prefetch. Sections and action rows (`onClick`,\n * no `href`) are skipped; children are walked too (sub-menu rows like\n * \"Command Center\" / \"New Poll\" are real cross-app destinations).\n */\nexport function collectSiblingAppUrls(items: NavItem[], currentOrigin: string): string[] {\n const urls = new Set<string>()\n const walk = (list: NavItem[]) => {\n for (const item of list) {\n if (item.kind === 'section') continue\n if (item.href && !isDestructive(item)) {\n try {\n const url = new URL(item.href, currentOrigin)\n if (url.origin !== currentOrigin && /^https?:$/.test(url.protocol)) {\n urls.add(url.href)\n }\n } catch {\n // Malformed href — skip rather than throw during nav render.\n }\n }\n if (item.children?.length) walk(item.children)\n }\n }\n walk(items)\n return Array.from(urls)\n}\n\nexport interface NavSpeculationRules {\n prerender: [{ source: 'list'; urls: string[]; eagerness: 'moderate' }]\n}\n\n/**\n * Build the Speculation Rules payload for a consumer's real sidebar nav tree:\n * every sibling `*.politogyvrm.com` app link, `eagerness: \"moderate\"` (the\n * browser's own heuristic — prerender starts on hover/pointerdown of a\n * matching `<a>`, not on page load). Sign-out and other destructive rows are\n * excluded by `collectSiblingAppUrls`. Returns `null` when there is nothing\n * to speculate on (e.g., an empty or single-app tree).\n *\n * Each target origin must opt in by sending\n * `Supports-Loading-Mode: credentialed-prerender` on its responses, per the\n * Speculation Rules cross-origin prerender contract — without it the browser\n * silently falls back to a normal navigation. (Other apps are adding this\n * header separately.)\n */\nexport function buildNavSpeculationRules(\n items: NavItem[],\n currentOrigin: string = typeof window !== 'undefined' ? window.location.origin : '',\n): NavSpeculationRules | null {\n const urls = collectSiblingAppUrls(items, currentOrigin)\n if (urls.length === 0) return null\n return {\n prerender: [{ source: 'list', urls, eagerness: 'moderate' }],\n }\n}\n","import { useEffect, useMemo } from 'react'\nimport type { NavItem } from '../native/index'\nimport { buildNavSpeculationRules, collectSiblingAppUrls } from './emitRules'\n\nconst SCRIPT_ID = 'politogy-speculation-rules'\nconst PREFETCH_ATTR = 'data-politogy-speculation-prefetch'\n\n/**\n * Renders the cross-app Speculation Rules `<script>` for the current\n * consumer's sidebar nav tree, and mounts in every `NavAppShell` — so it is\n * never an opt-in an app has to remember. Browsers that support Speculation\n * Rules prerender a sibling `*.politogyvrm.com` app the moment the user\n * hovers or pointers-down on its sidebar link (`eagerness: \"moderate\"`);\n * browsers that don't fall back, after the page goes idle, to plain\n * `<link rel=\"prefetch\">` tags for the same URLs, so the next click is still\n * warmed by a DNS/TLS/HTML fetch even without a prerendered document.\n *\n * Each sibling origin must send `Supports-Loading-Mode:\n * credentialed-prerender` on its responses for the prerender to actually\n * happen while the user is authenticated (cross-origin prerender is opt-in\n * on the destination). Without it, browsers degrade this to a normal\n * navigation — the hover/click still works, it's just not pre-warmed.\n *\n * Renders nothing visible; this is a side-effecting `<script>` injector.\n */\nexport function SpeculationRulesScript({ items }: { items: NavItem[] }) {\n const origin = typeof window !== 'undefined' ? window.location.origin : ''\n const rules = useMemo(() => buildNavSpeculationRules(items, origin), [items, origin])\n const json = rules ? JSON.stringify(rules) : null\n\n // Native Speculation Rules — covers the hover/pointerdown prerender case in\n // supporting browsers (Chromium). Declarative: no listeners needed, the\n // browser applies its own \"moderate\" heuristic to matching <a> elements.\n useEffect(() => {\n if (!json || typeof document === 'undefined') return\n let el = document.getElementById(SCRIPT_ID) as HTMLScriptElement | null\n if (!el) {\n el = document.createElement('script')\n el.id = SCRIPT_ID\n el.type = 'speculationrules'\n document.head.appendChild(el)\n }\n el.textContent = json\n return () => {\n // Leave the tag in place across route changes within the same app —\n // only strip it if this effect instance's app unmounts entirely with no\n // sibling to replace it.\n }\n }, [json])\n\n // Idle-prefetch fallback for browsers without Speculation Rules support\n // (or where the destination hasn't sent the opt-in header): warm the\n // connection/document via <link rel=\"prefetch\"> once the main thread is\n // idle, so it never competes with first-paint work.\n useEffect(() => {\n if (typeof document === 'undefined' || typeof window === 'undefined') return\n const supportsSpecRules =\n typeof HTMLScriptElement !== 'undefined' &&\n typeof (HTMLScriptElement as unknown as { supports?: (t: string) => boolean }).supports ===\n 'function' &&\n (HTMLScriptElement as unknown as { supports: (t: string) => boolean }).supports(\n 'speculationrules',\n )\n if (supportsSpecRules) return\n const urls = collectSiblingAppUrls(items, origin)\n if (urls.length === 0) return\n\n const schedule: (cb: () => void) => void =\n typeof (window as unknown as { requestIdleCallback?: (cb: () => void) => void })\n .requestIdleCallback === 'function'\n ? (window as unknown as { requestIdleCallback: (cb: () => void) => void })\n .requestIdleCallback\n : (cb: () => void) => setTimeout(cb, 2000)\n\n const added: HTMLLinkElement[] = []\n schedule(() => {\n for (const url of urls) {\n if (document.querySelector(`link[${PREFETCH_ATTR}][href=\"${url}\"]`)) continue\n const link = document.createElement('link')\n link.rel = 'prefetch'\n link.href = url\n link.setAttribute(PREFETCH_ATTR, '')\n document.head.appendChild(link)\n added.push(link)\n }\n })\n return () => {\n added.forEach((l) => l.remove())\n }\n }, [items, origin])\n\n return null\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { warnDeprecated } from './chunk-
|
|
1
|
+
import { warnDeprecated, SpeculationRulesScript } from './chunk-MY6G3LMN.js';
|
|
2
2
|
import { readThemeCookie, THEME_STORAGE_KEY, isThemePreference, applyThemePreference, readPendingThemeChoice, writeThemeCookie, clearPendingThemeChoice, markThemeChoicePending } from './chunk-BHRVZPZ7.js';
|
|
3
3
|
import { readRailPreference, writeRailPreference } from './chunk-7YVJ7HQG.js';
|
|
4
4
|
import { createContext, useRef, useState, useEffect, useContext, useCallback, useMemo, Fragment as Fragment$1, useSyncExternalStore } from 'react';
|
|
@@ -1794,6 +1794,9 @@ var PHONEBANK_ORIGIN = "https://phonebank.politogyvrm.com/";
|
|
|
1794
1794
|
var CANVASS_ORIGIN = "https://canvass.politogyvrm.com/";
|
|
1795
1795
|
var SIGNS_ORIGIN = "https://signs.politogyvrm.com/";
|
|
1796
1796
|
var POLLS_ORIGIN = "https://polls.politogyvrm.com/";
|
|
1797
|
+
var SETTINGS_ORIGIN = "https://settings.politogyvrm.com/";
|
|
1798
|
+
var DASHBOARD_ORIGIN = "https://dashboard.politogyvrm.com/";
|
|
1799
|
+
var AUTH_ORIGIN = "https://auth.politogyvrm.com/";
|
|
1797
1800
|
var PETITION_MODE_ORIGIN = "https://petition-mode.politogyvrm.com/";
|
|
1798
1801
|
function applyHiddenItems(items, hidden) {
|
|
1799
1802
|
if (!hidden || hidden.length === 0) return items;
|
|
@@ -1825,29 +1828,23 @@ var APP_EMAIL_CHILDREN = [
|
|
|
1825
1828
|
{ label: "Statistics", icon: "barChart", href: `${EMAIL_APP}/statistics` },
|
|
1826
1829
|
{ label: "Deliverability", icon: "activity", href: `${EMAIL_APP}/deliverability` }
|
|
1827
1830
|
];
|
|
1828
|
-
var EMAIL_V1_LEGACY = { label: "Email V1 (legacy)", icon: "clock", path: "/relationship/email/dashboard" };
|
|
1829
|
-
var CTA_CHILDREN = [
|
|
1830
|
-
{ label: "Dashboard", icon: "grid", path: "/relationship/cta-database/dashboard" },
|
|
1831
|
-
{ label: "Create & Edit", icon: "edit", path: "/relationship/cta-creator/create-and-edit" },
|
|
1832
|
-
{ label: "Submissions", icon: "fileText", path: "/relationship/cta-submissions/submissions" }
|
|
1833
|
-
];
|
|
1834
1831
|
var ORG_SETTINGS_CHILDREN = [
|
|
1835
|
-
{ label: "General", icon: "settings", path: "
|
|
1832
|
+
{ label: "General", icon: "settings", path: "organization-settings" },
|
|
1836
1833
|
{
|
|
1837
1834
|
label: "Subscription & Billing",
|
|
1838
1835
|
icon: "creditCard",
|
|
1839
|
-
path: "
|
|
1836
|
+
path: "organization-settings/subscription-billing",
|
|
1840
1837
|
children: [
|
|
1841
|
-
{ label: "Current Plan", icon: "fileText", path: "
|
|
1842
|
-
{ label: "SMS, MMS & Email Usage", icon: "activity", path: "
|
|
1843
|
-
{ label: "Wallet", icon: "dollarSign", path: "
|
|
1844
|
-
{ label: "10DLC", icon: "messageSquare", path: "
|
|
1845
|
-
{ label: "Billing History", icon: "clock", path: "
|
|
1838
|
+
{ label: "Current Plan", icon: "fileText", path: "organization-settings/subscription-billing/current-plan" },
|
|
1839
|
+
{ label: "SMS, MMS & Email Usage", icon: "activity", path: "organization-settings/subscription-billing/text-email" },
|
|
1840
|
+
{ label: "Wallet", icon: "dollarSign", path: "organization-settings/subscription-billing/wallet" },
|
|
1841
|
+
{ label: "10DLC", icon: "messageSquare", path: "organization-settings/subscription-billing/10dlc" },
|
|
1842
|
+
{ label: "Billing History", icon: "clock", path: "organization-settings/subscription-billing/billing-history" }
|
|
1846
1843
|
]
|
|
1847
1844
|
},
|
|
1848
|
-
{ label: "Email", icon: "mail", path: "
|
|
1849
|
-
{ label: "Users", icon: "users", path: "
|
|
1850
|
-
{ label: "Shared Links", icon: "share", path: "
|
|
1845
|
+
{ label: "Email", icon: "mail", path: "organization-settings/email-settings" },
|
|
1846
|
+
{ label: "Users", icon: "users", path: "organization-settings/users" },
|
|
1847
|
+
{ label: "Shared Links", icon: "share", path: "organization-settings/shared-links" }
|
|
1851
1848
|
];
|
|
1852
1849
|
var APP_FORMS_CHILDREN = [
|
|
1853
1850
|
{ label: "Forms", icon: "list", href: "https://forms.politogyvrm.com/?tab=forms" },
|
|
@@ -1863,6 +1860,10 @@ var APP_POLLS_CHILDREN = [
|
|
|
1863
1860
|
{ label: "Command Center", icon: "grid", href: "https://polls.politogyvrm.com/" },
|
|
1864
1861
|
{ label: "New Poll", icon: "edit", href: "https://polls.politogyvrm.com/polls/new" }
|
|
1865
1862
|
];
|
|
1863
|
+
var APP_CTAS_CHILDREN = [
|
|
1864
|
+
{ label: "CTAs", icon: "list", href: "https://ctas.politogyvrm.com/" },
|
|
1865
|
+
{ label: "Canvas", icon: "edit", href: "https://ctas.politogyvrm.com/?tab=canvas" }
|
|
1866
|
+
];
|
|
1866
1867
|
var APP_A2P_CHILDREN = [
|
|
1867
1868
|
{ label: "Dashboard", icon: "grid", path: "dashboard" },
|
|
1868
1869
|
{ label: "Compose", icon: "edit", path: "compose" },
|
|
@@ -1890,35 +1891,39 @@ var APP_SIGNS_CHILDREN = [
|
|
|
1890
1891
|
{ label: "Reports", icon: "barChart", path: "reports" }
|
|
1891
1892
|
];
|
|
1892
1893
|
function standardPolitogyNav(opts) {
|
|
1893
|
-
const base = opts.coreBaseUrl.replace(/\/+$/, "");
|
|
1894
|
-
const abs = (path) => `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
1895
1894
|
const icon = (key) => opts.icons?.[key] ?? standardNavIcons[key];
|
|
1896
1895
|
const orgParam = opts.activeOrgId ? `org=${opts.activeOrgId}` : "";
|
|
1897
1896
|
const withOrg = (url) => orgParam ? `${url}${url.includes("?") ? "&" : "?"}${orgParam}` : url;
|
|
1898
|
-
const leaves = (
|
|
1899
|
-
const branches = (
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1897
|
+
const leaves = (origin, children) => appLeaves(origin, children);
|
|
1898
|
+
const branches = (origin, children) => {
|
|
1899
|
+
const o = origin.replace(/\/+$/, "");
|
|
1900
|
+
return children.map((c) => ({
|
|
1901
|
+
label: c.label,
|
|
1902
|
+
icon: icon(c.icon),
|
|
1903
|
+
href: withOrg(`${o}/${c.path}`),
|
|
1904
|
+
children: c.children ? leaves(origin, c.children) : void 0
|
|
1905
|
+
}));
|
|
1906
|
+
};
|
|
1905
1907
|
const appLeaves = (origin, children, modeParam) => {
|
|
1906
|
-
const
|
|
1908
|
+
const base = origin.replace(/\/+$/, "");
|
|
1907
1909
|
return children.map((c) => {
|
|
1908
|
-
const url = c.path ? `${
|
|
1910
|
+
const url = c.path ? `${base}/${c.path}` : `${base}/`;
|
|
1909
1911
|
const withMode = modeParam ? `${url}${url.includes("?") ? "&" : "?"}mode=${modeParam}` : url;
|
|
1910
1912
|
return { label: c.label, icon: icon(c.icon), href: withOrg(withMode) };
|
|
1911
1913
|
});
|
|
1912
1914
|
};
|
|
1913
1915
|
const accountRows = () => [
|
|
1914
1916
|
{ kind: "section", label: "Account" },
|
|
1915
|
-
// My Profile
|
|
1916
|
-
|
|
1917
|
+
// My Profile moved to the Politogy ID account app (auth.politogyvrm.com)
|
|
1918
|
+
// with the retirement of app.politogyvrm.com.
|
|
1919
|
+
{ label: "My profile", icon: icon("circleUser"), href: withOrg(`${AUTH_ORIGIN}my-profile`) },
|
|
1917
1920
|
{
|
|
1918
1921
|
label: "Organization settings",
|
|
1919
1922
|
icon: icon("settings"),
|
|
1920
|
-
|
|
1921
|
-
|
|
1923
|
+
// Organization settings moved to the standalone Settings app
|
|
1924
|
+
// (settings.politogyvrm.com, Specs/Organization-Settings v12).
|
|
1925
|
+
href: withOrg(`${SETTINGS_ORIGIN}organization-settings`),
|
|
1926
|
+
children: branches(SETTINGS_ORIGIN, ORG_SETTINGS_CHILDREN)
|
|
1922
1927
|
}
|
|
1923
1928
|
];
|
|
1924
1929
|
const buildRelationship = () => {
|
|
@@ -1940,16 +1945,15 @@ function standardPolitogyNav(opts) {
|
|
|
1940
1945
|
};
|
|
1941
1946
|
return [
|
|
1942
1947
|
{ kind: "section", label: "Relationship" },
|
|
1943
|
-
|
|
1948
|
+
// Dashboard moved to the standalone Dashboard app (dashboard.politogyvrm.com,
|
|
1949
|
+
// Specs/Relationship-Dashboard) with the retirement of app.politogyvrm.com.
|
|
1950
|
+
{ label: "Dashboard", icon: icon("layoutDashboard"), href: withOrg(DASHBOARD_ORIGIN) },
|
|
1944
1951
|
contacts,
|
|
1945
1952
|
{
|
|
1946
1953
|
label: "Email broadcast",
|
|
1947
1954
|
icon: icon("mail"),
|
|
1948
1955
|
href: withOrg(`${EMAIL_APP}/dashboard`),
|
|
1949
|
-
children:
|
|
1950
|
-
...APP_EMAIL_CHILDREN.map((c) => ({ label: c.label, icon: icon(c.icon), href: withOrg(c.href) })),
|
|
1951
|
-
...leaves([EMAIL_V1_LEGACY])
|
|
1952
|
-
]
|
|
1956
|
+
children: APP_EMAIL_CHILDREN.map((c) => ({ label: c.label, icon: icon(c.icon), href: withOrg(c.href) }))
|
|
1953
1957
|
},
|
|
1954
1958
|
{
|
|
1955
1959
|
// SMS/MMS Blast is the a2p app entered in RELATIONSHIP mode (a2p is
|
|
@@ -1962,8 +1966,14 @@ function standardPolitogyNav(opts) {
|
|
|
1962
1966
|
children: appLeaves(A2P_ORIGIN, APP_A2P_CHILDREN, "relationship")
|
|
1963
1967
|
},
|
|
1964
1968
|
{ kind: "section", label: "Collect" },
|
|
1965
|
-
//
|
|
1966
|
-
|
|
1969
|
+
// Calls to action moved to the standalone CTAs app (ctas./runtime.
|
|
1970
|
+
// politogyvrm.com, Specs/CTAs) with the retirement of app.politogyvrm.com.
|
|
1971
|
+
{
|
|
1972
|
+
label: "Calls to action",
|
|
1973
|
+
icon: icon("mousePointerClick"),
|
|
1974
|
+
href: withOrg("https://ctas.politogyvrm.com/"),
|
|
1975
|
+
children: APP_CTAS_CHILDREN.map((c) => ({ label: c.label, icon: icon(c.icon), href: withOrg(c.href) }))
|
|
1976
|
+
},
|
|
1967
1977
|
{
|
|
1968
1978
|
label: "Forms",
|
|
1969
1979
|
icon: icon("fileText"),
|
|
@@ -1976,6 +1986,16 @@ function standardPolitogyNav(opts) {
|
|
|
1976
1986
|
href: withOrg("https://surveys.politogyvrm.com/surveys"),
|
|
1977
1987
|
children: APP_SURVEYS_CHILDREN.map((c) => ({ label: c.label, icon: icon(c.icon), href: withOrg(c.href) }))
|
|
1978
1988
|
},
|
|
1989
|
+
// Polls (polls.politogyvrm.com) — kept in the default Collect section;
|
|
1990
|
+
// apps that want it hidden pass `hiddenItems: ['Polls']`. Regression:
|
|
1991
|
+
// dropped entirely in 03c4dc2 when app.politogyvrm.com retired; restored
|
|
1992
|
+
// here (this row is independent of the retired app).
|
|
1993
|
+
{
|
|
1994
|
+
label: "Polls",
|
|
1995
|
+
icon: icon("chartNoAxes"),
|
|
1996
|
+
href: withOrg(POLLS_ORIGIN),
|
|
1997
|
+
children: APP_POLLS_CHILDREN.map((c) => ({ label: c.label, icon: icon(c.icon), href: withOrg(c.href) }))
|
|
1998
|
+
},
|
|
1979
1999
|
...accountRows()
|
|
1980
2000
|
];
|
|
1981
2001
|
};
|
|
@@ -1989,8 +2009,12 @@ function standardPolitogyNav(opts) {
|
|
|
1989
2009
|
// Battleground is the Campaign landing page; there is no Campaign
|
|
1990
2010
|
// Dashboard row, and Voter Contact and Tribes have no row.
|
|
1991
2011
|
{ kind: "section", label: "Race data" },
|
|
1992
|
-
|
|
1993
|
-
|
|
2012
|
+
// Battleground moved to the Dashboard app (dashboard.politogyvrm.com)
|
|
2013
|
+
// until a dedicated Battleground host is decided (UI-Platform route table).
|
|
2014
|
+
{ label: "Battleground", icon: icon("crosshair"), href: withOrg(`${DASHBOARD_ORIGIN}battleground`) },
|
|
2015
|
+
// District data moved to the Dashboard app (dashboard.politogyvrm.com)
|
|
2016
|
+
// per Reference/UI-Platform v44.
|
|
2017
|
+
{ label: "District data", icon: icon("landmark"), href: withOrg(`${DASHBOARD_ORIGIN}districts`) },
|
|
1994
2018
|
{ kind: "section", label: "Research" },
|
|
1995
2019
|
{
|
|
1996
2020
|
label: "Polling",
|
|
@@ -3577,6 +3601,7 @@ function NavAppShell({
|
|
|
3577
3601
|
"data-politogy-mode": modeId ?? modeIdFromLabel(sidebar.mode?.label),
|
|
3578
3602
|
className: "min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]",
|
|
3579
3603
|
children: [
|
|
3604
|
+
/* @__PURE__ */ jsx(SpeculationRulesScript, { items: sidebar.nav.items }),
|
|
3580
3605
|
/* @__PURE__ */ jsx(
|
|
3581
3606
|
NavSidebar,
|
|
3582
3607
|
{
|
|
@@ -3622,5 +3647,5 @@ function NavAppShell({
|
|
|
3622
3647
|
}
|
|
3623
3648
|
|
|
3624
3649
|
export { BETA_BANNER_DISMISS_KEY, CAMPAIGN_FEATURE_AVAILABILITY, CORE_APP_ORIGIN, CORE_BILLING_PATH, FuelAction, NavAppShell, NavBetaBanner, NavChromeProvider, NavDisplayAction, NavSidebar, NavTabStrip, NavThemeControl, NavTopBar, NotificationsAction, SPECS_ORIGIN, SpecOverlayAction, SupportAction, TopBarCountBadge, TopBarPanelArrow, TopBarPopover, TopBarTooltip, TopBarWidgetSlot, clearBetaBannerDismissal, envChipClasses, isNavItemActive, modeIdFromLabel, resolveOrgLogoUrl, shortEnvLabel, shortUserName, standardNavIcons, standardPolitogyNav, topBarIconButtonClass, useChromeMe, useChromeOrgs, useNavChromeFetcher, useThemePreference };
|
|
3625
|
-
//# sourceMappingURL=chunk-
|
|
3626
|
-
//# sourceMappingURL=chunk-
|
|
3650
|
+
//# sourceMappingURL=chunk-QUXD4PZ7.js.map
|
|
3651
|
+
//# sourceMappingURL=chunk-QUXD4PZ7.js.map
|