@the-portland-company/shell 0.86.0 → 0.87.1
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 +15 -0
- package/dist/app-chrome.cjs +98 -4
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +14 -1
- package/dist/app-chrome.d.ts +14 -1
- package/dist/app-chrome.js +38 -5
- package/dist/app-chrome.js.map +1 -1
- package/dist/chunk-GVQKZI5X.js +92 -0
- package/dist/chunk-GVQKZI5X.js.map +1 -0
- package/dist/url-state.js +2 -90
- package/dist/url-state.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## 0.87.1
|
|
2
|
+
|
|
3
|
+
Release fix only; same code as 0.87.0, which never reached npm. The 0.87.0 version bump regenerated `package-lock.json` with a newer npm that dropped two optional `yaml@2.9.1` entries, so the publish gate's `npm ci` refused it. The lock file is restored from 0.86.0 with only its version changed.
|
|
4
|
+
|
|
5
|
+
## 0.87.0
|
|
6
|
+
|
|
7
|
+
The top-bar search works in Relationship Mode. Spec cited: Reference/UI-Platform "Top bar — required elements" (Relationship search returns the Account's Contacts only) and Data-System/UVP §7.1 (a result opens the Contact Card).
|
|
8
|
+
|
|
9
|
+
- **Why.** Reported by Ben 2026-10-18: clicking the search box in Contacts did nothing. The frame passed only a placeholder, so `NavTopBar` drew a read-only input in every app.
|
|
10
|
+
- **Relationship Mode.** The box takes typing; Enter opens Contacts → All Contacts filtered to the query: `?mode=relationship&page=contacts&tab=contacts§ion=table&org_id=<tail>&q=<query>`. Same origin from inside Contacts, the `-staging` twin from a staging host. No app wiring and no new prop (an app never passes chrome configuration).
|
|
11
|
+
- **Campaign and Petition.** Unchanged: decorative until their search is built (Campaign voter lookup is Campaign-Voter-Record §3).
|
|
12
|
+
- **New export** `relationshipSearchHref(query, { orgId, fromApp, hostname })`.
|
|
13
|
+
- **Needs** Contacts to read `q` into its table search (politogy-contacts, same day).
|
|
14
|
+
- Tests: `__tests__/relationshipSearch.test.ts`, `__tests__/topbarSearch.test.tsx`.
|
|
15
|
+
|
|
1
16
|
## 0.86.0
|
|
2
17
|
|
|
3
18
|
Campaign Mode gets a Tribes row. Spec cited: Specs/Tribes §2.3–§2.4 and Specs/Left-Navigation §9.2 (decided 2026-10-16 by Ben: Campaign and Relationship Tribes are separate; Campaign Tribes live in Campaign Mode).
|
package/dist/app-chrome.cjs
CHANGED
|
@@ -4549,6 +4549,67 @@ function LoadingScreen({
|
|
|
4549
4549
|
}
|
|
4550
4550
|
);
|
|
4551
4551
|
}
|
|
4552
|
+
|
|
4553
|
+
// src/url-state.ts
|
|
4554
|
+
var URL_STATE_MODES = ["relationship", "campaign", "petition"];
|
|
4555
|
+
var URL_STATE_KEYS = ["mode", "page", "tab", "section", "org_id"];
|
|
4556
|
+
var SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
4557
|
+
function isUrlStateMode(v) {
|
|
4558
|
+
return typeof v === "string" && URL_STATE_MODES.includes(v);
|
|
4559
|
+
}
|
|
4560
|
+
function searchOf(input) {
|
|
4561
|
+
if (input instanceof URLSearchParams) return new URLSearchParams(input);
|
|
4562
|
+
return new URLSearchParams();
|
|
4563
|
+
}
|
|
4564
|
+
var slugOrNull = (v) => v && SLUG_RE.test(v) ? v : null;
|
|
4565
|
+
function shortOrgId(v) {
|
|
4566
|
+
if (!v) return null;
|
|
4567
|
+
const tail = orgIdTail(v);
|
|
4568
|
+
if (tail) return tail;
|
|
4569
|
+
const t = String(v).trim().toLowerCase();
|
|
4570
|
+
return /^[0-9a-f]{6,7}$/.test(t) ? t : null;
|
|
4571
|
+
}
|
|
4572
|
+
function readUrlState(input) {
|
|
4573
|
+
const params = searchOf(input);
|
|
4574
|
+
const mode = params.get("mode");
|
|
4575
|
+
const orgRaw = params.get("org_id");
|
|
4576
|
+
const feature = new URLSearchParams();
|
|
4577
|
+
for (const [k, v] of params) if (!URL_STATE_KEYS.includes(k)) feature.append(k, v);
|
|
4578
|
+
return {
|
|
4579
|
+
mode: isUrlStateMode(mode) ? mode : null,
|
|
4580
|
+
page: slugOrNull(params.get("page")),
|
|
4581
|
+
tab: slugOrNull(params.get("tab")),
|
|
4582
|
+
section: slugOrNull(params.get("section")),
|
|
4583
|
+
org_id: shortOrgId(orgRaw),
|
|
4584
|
+
feature
|
|
4585
|
+
};
|
|
4586
|
+
}
|
|
4587
|
+
function buildSearch(state) {
|
|
4588
|
+
const out = new URLSearchParams();
|
|
4589
|
+
for (const key of URL_STATE_KEYS) {
|
|
4590
|
+
const v = state[key];
|
|
4591
|
+
if (v) out.set(key, v);
|
|
4592
|
+
}
|
|
4593
|
+
for (const [k, v] of state.feature) out.append(k, v);
|
|
4594
|
+
const s = out.toString();
|
|
4595
|
+
return s ? `?${s}` : "";
|
|
4596
|
+
}
|
|
4597
|
+
function writeUrlState(current, patch) {
|
|
4598
|
+
const state = readUrlState(current);
|
|
4599
|
+
if (patch.mode !== void 0) state.mode = patch.mode;
|
|
4600
|
+
if (patch.page !== void 0) state.page = patch.page;
|
|
4601
|
+
if (patch.tab !== void 0) state.tab = patch.tab;
|
|
4602
|
+
if (patch.section !== void 0) state.section = patch.section;
|
|
4603
|
+
if (patch.org_id !== void 0) state.org_id = shortOrgId(patch.org_id);
|
|
4604
|
+
if (patch.feature) {
|
|
4605
|
+
for (const [k, v] of Object.entries(patch.feature)) {
|
|
4606
|
+
if (URL_STATE_KEYS.includes(k)) continue;
|
|
4607
|
+
if (v === null) state.feature.delete(k);
|
|
4608
|
+
else state.feature.set(k, v);
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
return buildSearch(state);
|
|
4612
|
+
}
|
|
4552
4613
|
function parseCssUrl(value) {
|
|
4553
4614
|
if (!value) return null;
|
|
4554
4615
|
const m = /^url\(\s*(['"]?)(.*?)\1\s*\)$/i.exec(value.trim());
|
|
@@ -4686,6 +4747,22 @@ var MODE_SEARCH_PLACEHOLDER = {
|
|
|
4686
4747
|
campaign: "Search voters, walk lists, polls",
|
|
4687
4748
|
petition: "Search petitions, sheets, circulators"
|
|
4688
4749
|
};
|
|
4750
|
+
function relationshipSearchHref(query, opts = {}) {
|
|
4751
|
+
const q = query.trim();
|
|
4752
|
+
if (!q) return null;
|
|
4753
|
+
const search = writeUrlState("", {
|
|
4754
|
+
mode: "relationship",
|
|
4755
|
+
page: "contacts",
|
|
4756
|
+
tab: "contacts",
|
|
4757
|
+
section: "table",
|
|
4758
|
+
org_id: opts.orgId ?? null,
|
|
4759
|
+
feature: { q }
|
|
4760
|
+
});
|
|
4761
|
+
if (opts.fromApp === "contacts") return `/${search}`;
|
|
4762
|
+
const staging = (opts.hostname ?? "").toLowerCase().includes("staging");
|
|
4763
|
+
const origin = staging ? "https://contacts-staging.politogyvrm.com" : POLITOGY_APPS.contacts.origin;
|
|
4764
|
+
return `${origin}/${search}`;
|
|
4765
|
+
}
|
|
4689
4766
|
function PolitogyAppFrame({
|
|
4690
4767
|
app,
|
|
4691
4768
|
pathname,
|
|
@@ -4847,6 +4924,8 @@ function FrameBody({
|
|
|
4847
4924
|
}, [cookieMode]);
|
|
4848
4925
|
const coreOrgs = chrome?.orgs ?? [];
|
|
4849
4926
|
const activeCoreOrg = coreOrgs.find((o) => o.id === chrome?.orgId) ?? null;
|
|
4927
|
+
const searchMode = appMode ?? mode.id ?? "relationship";
|
|
4928
|
+
const [searchText, setSearchText] = react.useState("");
|
|
4850
4929
|
const navDisplay = chrome?.navDisplay ?? "both";
|
|
4851
4930
|
const navMode = appMode ?? (app !== CORE_APP && isStandardNavMode(mode.id) ? mode.id : "relationship");
|
|
4852
4931
|
const activeOrgId = chrome?.orgId ?? null;
|
|
@@ -4959,11 +5038,25 @@ function FrameBody({
|
|
|
4959
5038
|
userMenu,
|
|
4960
5039
|
onSignOut: handleSignOut,
|
|
4961
5040
|
// Reference/UI-Platform "Top bar — required elements": Universal
|
|
4962
|
-
// search is the only thing on the left end of the bar.
|
|
4963
|
-
//
|
|
4964
|
-
//
|
|
5041
|
+
// search is the only thing on the left end of the bar. In
|
|
5042
|
+
// Relationship Mode it searches the Account's Contacts: Enter opens
|
|
5043
|
+
// Contacts → All Contacts filtered to the query, from every app, with
|
|
5044
|
+
// no app wiring (an app never passes chrome configuration). Campaign
|
|
5045
|
+
// and Petition search are not built yet and stay decorative.
|
|
4965
5046
|
search: {
|
|
4966
|
-
placeholder: data?.searchPlaceholder ?? MODE_SEARCH_PLACEHOLDER[
|
|
5047
|
+
placeholder: data?.searchPlaceholder ?? MODE_SEARCH_PLACEHOLDER[searchMode] ?? "Search",
|
|
5048
|
+
...searchMode === "relationship" ? {
|
|
5049
|
+
value: searchText,
|
|
5050
|
+
onChange: setSearchText,
|
|
5051
|
+
onSubmit: (value) => {
|
|
5052
|
+
const href = relationshipSearchHref(value, {
|
|
5053
|
+
orgId: activeCoreOrg?.id ?? session.accountId ?? null,
|
|
5054
|
+
fromApp: app,
|
|
5055
|
+
hostname: typeof window === "undefined" ? "" : window.location.hostname
|
|
5056
|
+
});
|
|
5057
|
+
if (href) window.location.assign(href);
|
|
5058
|
+
}
|
|
5059
|
+
} : {}
|
|
4967
5060
|
},
|
|
4968
5061
|
// No Build notes control (Reference/UI-Platform, decided 2026-10-03).
|
|
4969
5062
|
// The action cluster is IDENTICAL everywhere — core included. Page
|
|
@@ -5692,6 +5785,7 @@ exports.footerLinks = footerLinks;
|
|
|
5692
5785
|
exports.looksLikeEmail = looksLikeEmail;
|
|
5693
5786
|
exports.modeLandingHref = modeLandingHref;
|
|
5694
5787
|
exports.orgSwitchLanding = orgSwitchLanding;
|
|
5788
|
+
exports.relationshipSearchHref = relationshipSearchHref;
|
|
5695
5789
|
exports.resolveIdentity = resolveIdentity;
|
|
5696
5790
|
exports.standardNavIcons = standardNavIcons;
|
|
5697
5791
|
exports.useChromeFooter = useChromeFooter;
|