@the-portland-company/shell 0.80.0 → 0.80.2

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 CHANGED
@@ -1,3 +1,20 @@
1
+ ## 0.80.2
2
+
3
+ The mode switcher goes to each mode's landing page. Spec cited: Specs/Left-Navigation §7 and §11.1.
4
+
5
+ - **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).
6
+ - **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`.
7
+ - **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`.
8
+ - Tests: `__tests__/modeLandingHref.test.ts`.
9
+
10
+ ## 0.80.1
11
+
12
+ 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").
13
+
14
+ - **Cause.** The Relationship tree linked Dashboard to the host root (`https://dashboard.politogyvrm.com/`). The app serves the page at `/dashboard` and the root only redirects there; `isNavItemActive` treats a `/` target as active on `/` alone, so no row was active on the Relationship Dashboard (measured 2026-10-12).
15
+ - **Fix.** The row links to `https://dashboard.politogyvrm.com/dashboard`. Other apps follow the same link without the extra redirect.
16
+ - Tests: `__tests__/standardNav.test.ts`.
17
+
1
18
  ## 0.80.0
2
19
 
3
20
  The URL-state helper. Specs cited: Reference/UI-Platform "URL state" (decided 2026-10-06, Spencer) and Global Rules → URL State. Until now the page said the helper "ships in a shell minor release" and no app could carry `section=` without concatenating by hand.
@@ -2324,7 +2324,12 @@ function standardPolitogyNav(opts) {
2324
2324
  { kind: "section", label: "Relationship" },
2325
2325
  // Dashboard moved to the standalone Dashboard app (dashboard.politogyvrm.com,
2326
2326
  // Specs/Relationship-Dashboard) with the retirement of app.politogyvrm.com.
2327
- { label: "Dashboard", icon: icon("layoutDashboard"), href: withOrg(DASHBOARD_ORIGIN) },
2327
+ // The row links to the page itself (`/dashboard`), not the host root.
2328
+ // The root only redirects there, and `isNavItemActive` treats a `/`
2329
+ // target as active on `/` alone, so a root link left the Dashboard row
2330
+ // without its active marker on the Dashboard (Left-Navigation §8.1,
2331
+ // §11.3 "exactly one row is active").
2332
+ { label: "Dashboard", icon: icon("layoutDashboard"), href: withOrg(`${DASHBOARD_ORIGIN}dashboard`) },
2328
2333
  contacts,
2329
2334
  {
2330
2335
  label: "Email broadcast",
@@ -4547,7 +4552,11 @@ var POLITOGY_APPS = {
4547
4552
  // Relationship Dashboard, the Relationship Mode landing page, and nothing
4548
4553
  // else (Specs/Relationship-Dashboard §1A "Relationship Mode only"); Settings
4549
4554
  // hosts org settings & billing (Specs/Organization-Settings).
4550
- dashboard: { title: "Dashboard", origin: "https://dashboard.politogyvrm.com", storagePrefix: "dashboard" },
4555
+ // PINNED to Relationship: left unscoped, its frame took the user's last
4556
+ // mode selection, so after a visit to Campaign Mode the Dashboard drew the
4557
+ // red Campaign rail and "Campaign mode" label over the Relationship rows
4558
+ // (Left-Navigation §11.1 "the rail never shows a mix of two modes").
4559
+ dashboard: { title: "Dashboard", origin: "https://dashboard.politogyvrm.com", storagePrefix: "dashboard", mode: "relationship" },
4551
4560
  // Battleground (the Campaign Mode landing page) and District Data are
4552
4561
  // Campaign Mode features, each its own app on its own host (Specs/Left-
4553
4562
  // Navigation §9.2, §13, §14; Reference/UI-Platform, decided 2026-10-12).
@@ -5439,14 +5448,19 @@ var MODE_DEFS = [
5439
5448
  id: "relationship",
5440
5449
  label: "Relationship Mode",
5441
5450
  icon: lu.LuContactRound,
5442
- href: `${CORE_BASE_URL}/relationship/dashboard`,
5451
+ // The mode's landing page (Left-Navigation §11.1), with the mode stated in
5452
+ // the URL. These used to point at the retired VRM core
5453
+ // (`app.politogyvrm.com/relationship/dashboard`), whose redirect dropped
5454
+ // the mode: the Dashboard app then chose by the active-mode cookie, so
5455
+ // "Relationship" from a Campaign page landed back on Battleground.
5456
+ href: "https://dashboard.politogyvrm.com/dashboard?mode=relationship",
5443
5457
  orgQuery: true
5444
5458
  },
5445
5459
  {
5446
5460
  id: "campaign",
5447
5461
  label: "Campaign Mode",
5448
5462
  icon: lu.LuMegaphone,
5449
- href: `${CORE_BASE_URL}/campaign/dashboard`,
5463
+ href: "https://battleground.politogyvrm.com/battleground?mode=campaign",
5450
5464
  orgQuery: true
5451
5465
  },
5452
5466
  {
@@ -5459,6 +5473,14 @@ var MODE_DEFS = [
5459
5473
  orgQuery: true
5460
5474
  }
5461
5475
  ];
5476
+ function modeLandingHref(id, opts = {}) {
5477
+ const def = MODE_DEFS.find((m) => m.id === id);
5478
+ if (!def) return null;
5479
+ const staging = (opts.hostname ?? "").toLowerCase().includes("staging");
5480
+ const href = staging ? def.href.replace(/^https:\/\/(dashboard|battleground)\.politogyvrm\.com/, "https://$1-staging.politogyvrm.com") : def.href;
5481
+ if (!opts.orgId) return href;
5482
+ return `${href}${href.includes("?") ? "&" : "?"}org=${opts.orgId}`;
5483
+ }
5462
5484
  function useChromeMode(arg = {}) {
5463
5485
  const { fallbackModeId = "relationship", pinnedModeId } = typeof arg === "string" ? { fallbackModeId: arg, pinnedModeId: void 0 } : arg;
5464
5486
  const ctx = react.useContext(ChromeStateContext);
@@ -5496,7 +5518,6 @@ function useChromeMode(arg = {}) {
5496
5518
  const pinned = pinnedModeId ? MODE_DEFS.find((m) => m.id === pinnedModeId) ?? null : null;
5497
5519
  const selected = selectedModeId ? options.find((m) => m.id === selectedModeId) ?? null : null;
5498
5520
  const current = pinned ?? selected ?? options[0] ?? MODE_DEFS.find((m) => m.id === fallbackModeId) ?? MODE_DEFS[0];
5499
- const suffix = orgId ? `?org=${orgId}` : "";
5500
5521
  return {
5501
5522
  label: current.label,
5502
5523
  icon: current.icon,
@@ -5518,10 +5539,11 @@ function useChromeMode(arg = {}) {
5518
5539
  localStorage.setItem(MODE_STORAGE_KEY, id);
5519
5540
  } catch {
5520
5541
  }
5521
- const href = def.href.replace(CORE_BASE_URL, getCoreBaseUrl());
5522
- const sep = href.includes("?") ? "&" : "?";
5523
5542
  window.location.assign(
5524
- def.orgQuery && suffix ? `${href}${sep}${suffix.slice(1)}` : href
5543
+ modeLandingHref(id, {
5544
+ orgId: def.orgQuery ? orgId : null,
5545
+ hostname: window.location.hostname
5546
+ }) ?? def.href
5525
5547
  );
5526
5548
  } : void 0
5527
5549
  };
@@ -5534,6 +5556,7 @@ exports.PolitogyAppChrome = PolitogyAppChrome;
5534
5556
  exports.PolitogyAppFrame = PolitogyAppFrame;
5535
5557
  exports.deriveBreadcrumbs = deriveBreadcrumbs;
5536
5558
  exports.looksLikeEmail = looksLikeEmail;
5559
+ exports.modeLandingHref = modeLandingHref;
5537
5560
  exports.resolveIdentity = resolveIdentity;
5538
5561
  exports.standardNavIcons = standardNavIcons;
5539
5562
  exports.useChromeFooter = useChromeFooter;