@the-portland-company/shell 0.33.0 → 0.35.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/dist/app-chrome.cjs +554 -71
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +53 -1
- package/dist/app-chrome.d.ts +53 -1
- package/dist/app-chrome.js +25 -9
- package/dist/app-chrome.js.map +1 -1
- package/dist/{chunk-MTVP6EDM.js → chunk-CVBEBCID.js} +533 -66
- package/dist/chunk-CVBEBCID.js.map +1 -0
- package/dist/native.cjs +532 -63
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +45 -3
- package/dist/native.d.ts +45 -3
- package/dist/native.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MTVP6EDM.js.map +0 -1
package/dist/app-chrome.cjs
CHANGED
|
@@ -146,6 +146,10 @@ var d=document.documentElement;
|
|
|
146
146
|
if(resolved==="dark"){d.classList.add("dark");}else{d.classList.remove("dark");}
|
|
147
147
|
try{d.style.colorScheme=resolved;}catch(e){}
|
|
148
148
|
}catch(e){}})();`;
|
|
149
|
+
var CORE_APP_ORIGIN = "https://app.politogyvrm.com";
|
|
150
|
+
var CORE_BILLING_PATH = "/organization-settings/subscription-billing/text-email";
|
|
151
|
+
var CORE_BILLING_URL = `${CORE_APP_ORIGIN}${CORE_BILLING_PATH}`;
|
|
152
|
+
var QUARANTINED_CONTACTS_URL = `${CORE_APP_ORIGIN}/relationship/contacts/quarantined`;
|
|
149
153
|
function topBarIconButtonClass(tone = "default") {
|
|
150
154
|
const toneClass = tone === "danger" || tone === "warning" ? "text-[color:var(--color-race-red)] hover:text-[color:var(--color-race-red)]" : "text-[color:var(--topbar-icon-color,var(--muted))] hover:text-[color:var(--topbar-icon-hover-color,var(--foreground))] hover:bg-[color:var(--topbar-icon-hover-bg,transparent)]";
|
|
151
155
|
return [
|
|
@@ -328,11 +332,22 @@ function TopBarPopover({
|
|
|
328
332
|
badge,
|
|
329
333
|
tone,
|
|
330
334
|
onOpen,
|
|
335
|
+
openToken,
|
|
331
336
|
children,
|
|
332
337
|
panelClassName
|
|
333
338
|
}) {
|
|
334
339
|
const [open, setOpen] = react.useState(false);
|
|
335
340
|
const rootRef = react.useRef(null);
|
|
341
|
+
const lastOpenTokenRef = react.useRef(openToken);
|
|
342
|
+
react.useEffect(() => {
|
|
343
|
+
if (openToken === void 0) return;
|
|
344
|
+
if (openToken === lastOpenTokenRef.current) return;
|
|
345
|
+
lastOpenTokenRef.current = openToken;
|
|
346
|
+
setOpen((v) => {
|
|
347
|
+
if (!v) onOpen?.();
|
|
348
|
+
return true;
|
|
349
|
+
});
|
|
350
|
+
}, [openToken, onOpen]);
|
|
336
351
|
react.useEffect(() => {
|
|
337
352
|
if (!open) return;
|
|
338
353
|
const onDocClick = (e) => {
|
|
@@ -416,31 +431,60 @@ function LayoutGlyph({ size = 20, className }) {
|
|
|
416
431
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "21", x2: "9", y2: "9" })
|
|
417
432
|
] });
|
|
418
433
|
}
|
|
434
|
+
function PlusGlyph({ size = 14, className }) {
|
|
435
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
436
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
|
|
437
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
438
|
+
] });
|
|
439
|
+
}
|
|
440
|
+
function XGlyph({ size = 14, className }) {
|
|
441
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
442
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
443
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
444
|
+
] });
|
|
445
|
+
}
|
|
446
|
+
function CheckGlyph({ size = 14, className }) {
|
|
447
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) });
|
|
448
|
+
}
|
|
449
|
+
var LOW_FUEL_HINT_DISMISSED_KEY = "politogy_low_fuel_hint_dismissed";
|
|
419
450
|
function FuelRow({
|
|
420
451
|
label,
|
|
421
452
|
channel,
|
|
422
|
-
loading
|
|
453
|
+
loading,
|
|
454
|
+
addHref,
|
|
455
|
+
addLabel
|
|
423
456
|
}) {
|
|
424
457
|
const remaining = channel?.remainingUnits ?? 0;
|
|
425
458
|
const total = channel ? Math.max(channel.rawBalanceUnits, channel.remainingUnits) : 0;
|
|
426
459
|
const ratio = total > 0 ? Math.min(100, Math.max(0, remaining / total * 100)) : 0;
|
|
427
460
|
const low = channel?.lowFuel ?? false;
|
|
428
461
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
|
|
429
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
462
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
|
|
430
463
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-[color:var(--foreground)]", children: label }),
|
|
431
|
-
|
|
432
|
-
"
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
464
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
465
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-3.5 w-14" }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
466
|
+
"span",
|
|
467
|
+
{
|
|
468
|
+
className: [
|
|
469
|
+
"text-xs font-semibold tabular-nums",
|
|
470
|
+
low ? "text-[color:var(--color-race-red)]" : "text-[color:var(--muted)]"
|
|
471
|
+
].join(" "),
|
|
472
|
+
children: [
|
|
473
|
+
remaining.toLocaleString(),
|
|
474
|
+
" left"
|
|
475
|
+
]
|
|
476
|
+
}
|
|
477
|
+
),
|
|
478
|
+
addHref ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
479
|
+
"a",
|
|
480
|
+
{
|
|
481
|
+
href: addHref,
|
|
482
|
+
"aria-label": addLabel,
|
|
483
|
+
className: "flex h-5 w-5 items-center justify-center rounded-full border border-[color:var(--line)] text-[color:var(--muted)] transition hover:border-[color:var(--foreground)] hover:text-[color:var(--foreground)]",
|
|
484
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(PlusGlyph, {})
|
|
485
|
+
}
|
|
486
|
+
) : null
|
|
487
|
+
] })
|
|
444
488
|
] }),
|
|
445
489
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-[color:var(--panel)]", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full animate-pulse bg-[color:var(--line)]" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
446
490
|
"div",
|
|
@@ -454,6 +498,71 @@ function FuelRow({
|
|
|
454
498
|
) })
|
|
455
499
|
] });
|
|
456
500
|
}
|
|
501
|
+
function LowFuelHint({ lowFuel }) {
|
|
502
|
+
const [dismissed, setDismissed] = react.useState(() => {
|
|
503
|
+
if (typeof window === "undefined") return false;
|
|
504
|
+
try {
|
|
505
|
+
return window.sessionStorage.getItem(LOW_FUEL_HINT_DISMISSED_KEY) === "1";
|
|
506
|
+
} catch {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
react.useEffect(() => {
|
|
511
|
+
if (lowFuel) return;
|
|
512
|
+
try {
|
|
513
|
+
window.sessionStorage.removeItem(LOW_FUEL_HINT_DISMISSED_KEY);
|
|
514
|
+
} catch {
|
|
515
|
+
}
|
|
516
|
+
}, [lowFuel]);
|
|
517
|
+
if (!lowFuel || dismissed) return null;
|
|
518
|
+
const dismiss = () => {
|
|
519
|
+
try {
|
|
520
|
+
window.sessionStorage.setItem(LOW_FUEL_HINT_DISMISSED_KEY, "1");
|
|
521
|
+
} catch {
|
|
522
|
+
}
|
|
523
|
+
setDismissed(true);
|
|
524
|
+
};
|
|
525
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
526
|
+
"div",
|
|
527
|
+
{
|
|
528
|
+
role: "status",
|
|
529
|
+
className: "absolute right-0 top-[calc(100%+10px)] z-50 w-60 rounded-lg border border-[color:var(--color-race-red)] bg-[color:var(--color-race-red)] px-3 py-2 text-white shadow-[var(--shadow-soft)]",
|
|
530
|
+
onClick: (e) => e.stopPropagation(),
|
|
531
|
+
children: [
|
|
532
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
533
|
+
"span",
|
|
534
|
+
{
|
|
535
|
+
"aria-hidden": true,
|
|
536
|
+
className: "absolute -top-[5px] right-[11px] h-2.5 w-2.5 rotate-45 bg-[color:var(--color-race-red)]"
|
|
537
|
+
}
|
|
538
|
+
),
|
|
539
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
|
|
540
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
541
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "You\u2019re low on fuel" }),
|
|
542
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
543
|
+
"a",
|
|
544
|
+
{
|
|
545
|
+
href: CORE_BILLING_URL,
|
|
546
|
+
className: "mt-1 inline-block text-sm font-bold underline hover:opacity-90",
|
|
547
|
+
children: "Buy More Fuel"
|
|
548
|
+
}
|
|
549
|
+
)
|
|
550
|
+
] }),
|
|
551
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
552
|
+
"button",
|
|
553
|
+
{
|
|
554
|
+
type: "button",
|
|
555
|
+
"aria-label": "Dismiss low fuel notice",
|
|
556
|
+
onClick: dismiss,
|
|
557
|
+
className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-white/90 transition hover:bg-white/20",
|
|
558
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(XGlyph, {})
|
|
559
|
+
}
|
|
560
|
+
)
|
|
561
|
+
] })
|
|
562
|
+
]
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
}
|
|
457
566
|
function FuelAction({ orgId }) {
|
|
458
567
|
const { fetchJson } = useNavChrome("FuelAction");
|
|
459
568
|
const [fuel, setFuel] = react.useState(null);
|
|
@@ -479,21 +588,154 @@ function FuelAction({ orgId }) {
|
|
|
479
588
|
};
|
|
480
589
|
}, [fetchJson, orgId]);
|
|
481
590
|
const low = !!fuel && (fuel.signal.lowFuel || fuel.broadcast.lowFuel);
|
|
482
|
-
return
|
|
483
|
-
|
|
591
|
+
return (
|
|
592
|
+
// The nudge anchors to the trigger, so it sits OUTSIDE the popover panel in
|
|
593
|
+
// its own relative wrapper (same shape the VRM widget used).
|
|
594
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative inline-flex", children: [
|
|
595
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
596
|
+
TopBarPopover,
|
|
597
|
+
{
|
|
598
|
+
label: "Communications Fuel",
|
|
599
|
+
description: "Remaining SMS and email sending balance for the active organization.",
|
|
600
|
+
slot: "comms-fuel",
|
|
601
|
+
fallbackIcon: BatteryGlyph,
|
|
602
|
+
tone: low ? "warning" : "default",
|
|
603
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
|
|
604
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Communications Fuel" }),
|
|
605
|
+
error || !loading && !fuel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[color:var(--muted)]", children: "Fuel balance is unavailable right now." }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
606
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
607
|
+
FuelRow,
|
|
608
|
+
{
|
|
609
|
+
label: "SMS Signal",
|
|
610
|
+
channel: fuel?.signal ?? null,
|
|
611
|
+
loading,
|
|
612
|
+
addHref: CORE_BILLING_URL,
|
|
613
|
+
addLabel: "Add SMS Signal fuel"
|
|
614
|
+
}
|
|
615
|
+
),
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
617
|
+
FuelRow,
|
|
618
|
+
{
|
|
619
|
+
label: "Email Broadcast",
|
|
620
|
+
channel: fuel?.broadcast ?? null,
|
|
621
|
+
loading,
|
|
622
|
+
addHref: CORE_BILLING_URL,
|
|
623
|
+
addLabel: "Add Email Broadcast fuel"
|
|
624
|
+
}
|
|
625
|
+
)
|
|
626
|
+
] })
|
|
627
|
+
] })
|
|
628
|
+
}
|
|
629
|
+
),
|
|
630
|
+
/* @__PURE__ */ jsxRuntime.jsx(LowFuelHint, { lowFuel: low })
|
|
631
|
+
] })
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
function notifStateKey(orgId) {
|
|
635
|
+
return `politogy_chrome_notif_state:${orgId ?? "none"}`;
|
|
636
|
+
}
|
|
637
|
+
function readNotifState(orgId) {
|
|
638
|
+
const empty = { readAlertIds: [], lastSeenAt: null, toastedReplyIds: [] };
|
|
639
|
+
if (typeof window === "undefined") return empty;
|
|
640
|
+
try {
|
|
641
|
+
const raw = window.localStorage.getItem(notifStateKey(orgId));
|
|
642
|
+
if (!raw) return empty;
|
|
643
|
+
const parsed = JSON.parse(raw);
|
|
644
|
+
return {
|
|
645
|
+
readAlertIds: Array.isArray(parsed?.readAlertIds) ? parsed.readAlertIds.filter((id) => typeof id === "string") : [],
|
|
646
|
+
lastSeenAt: typeof parsed?.lastSeenAt === "string" ? parsed.lastSeenAt : null,
|
|
647
|
+
toastedReplyIds: Array.isArray(parsed?.toastedReplyIds) ? parsed.toastedReplyIds.filter((id) => typeof id === "string") : []
|
|
648
|
+
};
|
|
649
|
+
} catch {
|
|
650
|
+
return empty;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function writeNotifState(orgId, state) {
|
|
654
|
+
if (typeof window === "undefined") return;
|
|
655
|
+
try {
|
|
656
|
+
window.localStorage.setItem(notifStateKey(orgId), JSON.stringify(state));
|
|
657
|
+
} catch {
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
var TEN_DLC_REDIRECTED_KEY = "politogy_10dlc_redirected";
|
|
661
|
+
function formatPhone(raw) {
|
|
662
|
+
const digits = raw.replace(/\D/g, "");
|
|
663
|
+
const ten = digits.length === 11 && digits.startsWith("1") ? digits.slice(1) : digits;
|
|
664
|
+
if (ten.length === 10) return `(${ten.slice(0, 3)}) ${ten.slice(3, 6)}-${ten.slice(6)}`;
|
|
665
|
+
return raw;
|
|
666
|
+
}
|
|
667
|
+
function ImportRow({ imp }) {
|
|
668
|
+
const pct = imp.percent == null ? null : Math.max(0, Math.min(100, imp.percent));
|
|
669
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
670
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
671
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm text-[color:var(--foreground)]", children: imp.label }),
|
|
672
|
+
pct != null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-xs tabular-nums text-[color:var(--muted)]", children: [
|
|
673
|
+
Math.round(pct),
|
|
674
|
+
"%"
|
|
675
|
+
] }) : null
|
|
676
|
+
] }),
|
|
677
|
+
pct != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 h-1 w-full overflow-hidden rounded-full bg-[color:var(--panel)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
678
|
+
"div",
|
|
679
|
+
{
|
|
680
|
+
className: "h-full rounded-full bg-[color:var(--color-race-red)] transition-[width]",
|
|
681
|
+
style: { width: `${pct}%` }
|
|
682
|
+
}
|
|
683
|
+
) }) : null
|
|
684
|
+
] });
|
|
685
|
+
}
|
|
686
|
+
function NewReplyToast({
|
|
687
|
+
reply,
|
|
688
|
+
onActivate,
|
|
689
|
+
onClose,
|
|
690
|
+
ttlMs = 12e3
|
|
691
|
+
}) {
|
|
692
|
+
const [visible, setVisible] = react.useState(false);
|
|
693
|
+
const onCloseRef = react.useRef(onClose);
|
|
694
|
+
onCloseRef.current = onClose;
|
|
695
|
+
react.useEffect(() => {
|
|
696
|
+
setVisible(true);
|
|
697
|
+
const fadeAt = Math.max(0, ttlMs - 450);
|
|
698
|
+
const t1 = window.setTimeout(() => setVisible(false), fadeAt);
|
|
699
|
+
const t2 = window.setTimeout(() => onCloseRef.current(), ttlMs);
|
|
700
|
+
return () => {
|
|
701
|
+
window.clearTimeout(t1);
|
|
702
|
+
window.clearTimeout(t2);
|
|
703
|
+
};
|
|
704
|
+
}, [ttlMs]);
|
|
705
|
+
const activate = () => {
|
|
706
|
+
setVisible(false);
|
|
707
|
+
onActivate();
|
|
708
|
+
};
|
|
709
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
710
|
+
"div",
|
|
484
711
|
{
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
712
|
+
role: "button",
|
|
713
|
+
tabIndex: 0,
|
|
714
|
+
"aria-label": "Open new reply",
|
|
715
|
+
onClick: activate,
|
|
716
|
+
onKeyDown: (e) => {
|
|
717
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
718
|
+
e.preventDefault();
|
|
719
|
+
activate();
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
className: "absolute right-[-6px] top-[calc(100%+12px)] z-50 min-w-[240px] max-w-[320px] cursor-pointer rounded-xl bg-[#161616] px-4 py-3 text-white shadow-[var(--shadow-soft)]",
|
|
723
|
+
style: {
|
|
724
|
+
opacity: visible ? 1 : 0,
|
|
725
|
+
transform: visible ? "translateY(0px)" : "translateY(-4px)",
|
|
726
|
+
transition: "opacity 250ms ease, transform 250ms ease"
|
|
727
|
+
},
|
|
728
|
+
children: [
|
|
729
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
730
|
+
"span",
|
|
731
|
+
{
|
|
732
|
+
"aria-hidden": true,
|
|
733
|
+
className: "absolute right-[18px] top-[-7px] h-0 w-0 border-x-[8px] border-x-transparent border-b-[8px] border-b-[#161616]"
|
|
734
|
+
}
|
|
735
|
+
),
|
|
736
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-semibold", children: reply.name }),
|
|
737
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 truncate text-xs opacity-85", children: reply.preview })
|
|
738
|
+
]
|
|
497
739
|
}
|
|
498
740
|
);
|
|
499
741
|
}
|
|
@@ -502,6 +744,13 @@ function NotificationsAction({ orgId }) {
|
|
|
502
744
|
const [payload, setPayload] = react.useState(null);
|
|
503
745
|
const [loading, setLoading] = react.useState(true);
|
|
504
746
|
const [error, setError] = react.useState(false);
|
|
747
|
+
const [notif, setNotif] = react.useState(() => readNotifState(orgId));
|
|
748
|
+
const [toastReply, setToastReply] = react.useState(null);
|
|
749
|
+
const [openToken, setOpenToken] = react.useState(0);
|
|
750
|
+
react.useEffect(() => {
|
|
751
|
+
setNotif(readNotifState(orgId));
|
|
752
|
+
setToastReply(null);
|
|
753
|
+
}, [orgId]);
|
|
505
754
|
react.useEffect(() => {
|
|
506
755
|
let cancelled = false;
|
|
507
756
|
setLoading(true);
|
|
@@ -520,51 +769,226 @@ function NotificationsAction({ orgId }) {
|
|
|
520
769
|
cancelled = true;
|
|
521
770
|
};
|
|
522
771
|
}, [fetchJson, orgId]);
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
772
|
+
react.useEffect(() => {
|
|
773
|
+
if (!payload || typeof window === "undefined") return;
|
|
774
|
+
const alert = payload.alerts.find((a) => a.kind === "10dlc_registration" && a.href);
|
|
775
|
+
if (!alert?.href) return;
|
|
776
|
+
try {
|
|
777
|
+
if (window.sessionStorage.getItem(TEN_DLC_REDIRECTED_KEY) === "1") return;
|
|
778
|
+
} catch {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
let target;
|
|
782
|
+
try {
|
|
783
|
+
target = new URL(alert.href, window.location.href);
|
|
784
|
+
} catch {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (target.origin !== window.location.origin) return;
|
|
788
|
+
try {
|
|
789
|
+
window.sessionStorage.setItem(TEN_DLC_REDIRECTED_KEY, "1");
|
|
790
|
+
} catch {
|
|
791
|
+
}
|
|
792
|
+
window.location.assign(alert.href);
|
|
793
|
+
}, [payload]);
|
|
794
|
+
const markRead = react.useCallback(
|
|
795
|
+
(id) => {
|
|
796
|
+
setNotif((prev) => {
|
|
797
|
+
if (prev.readAlertIds.includes(id)) return prev;
|
|
798
|
+
const next = { ...prev, readAlertIds: [...prev.readAlertIds, id] };
|
|
799
|
+
writeNotifState(orgId, next);
|
|
800
|
+
return next;
|
|
801
|
+
});
|
|
802
|
+
},
|
|
803
|
+
[orgId]
|
|
804
|
+
);
|
|
805
|
+
const markUnread = react.useCallback(
|
|
806
|
+
(id) => {
|
|
807
|
+
setNotif((prev) => {
|
|
808
|
+
if (!prev.readAlertIds.includes(id)) return prev;
|
|
809
|
+
const next = { ...prev, readAlertIds: prev.readAlertIds.filter((x) => x !== id) };
|
|
810
|
+
writeNotifState(orgId, next);
|
|
811
|
+
return next;
|
|
812
|
+
});
|
|
813
|
+
},
|
|
814
|
+
[orgId]
|
|
815
|
+
);
|
|
816
|
+
const onOpen = react.useCallback(() => {
|
|
817
|
+
setNotif((prev) => {
|
|
818
|
+
const next = { ...prev, lastSeenAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
819
|
+
writeNotifState(orgId, next);
|
|
820
|
+
return next;
|
|
821
|
+
});
|
|
822
|
+
}, [orgId]);
|
|
823
|
+
const alerts = payload?.alerts ?? [];
|
|
824
|
+
const replies = payload?.replies ?? [];
|
|
825
|
+
const imports = payload?.imports ?? [];
|
|
826
|
+
const quarantined = payload?.quarantined ?? [];
|
|
827
|
+
react.useEffect(() => {
|
|
828
|
+
if (!payload || toastReply) return;
|
|
829
|
+
const candidate = replies.filter((r) => {
|
|
830
|
+
if (!r.createdAt) return false;
|
|
831
|
+
if (notif.lastSeenAt && r.createdAt <= notif.lastSeenAt) return false;
|
|
832
|
+
return !notif.toastedReplyIds.includes(r.id);
|
|
833
|
+
}).sort((a, b) => a.createdAt < b.createdAt ? 1 : -1)[0];
|
|
834
|
+
if (!candidate) return;
|
|
835
|
+
setToastReply(candidate);
|
|
836
|
+
setNotif((prev) => {
|
|
837
|
+
const next = {
|
|
838
|
+
...prev,
|
|
839
|
+
toastedReplyIds: [candidate.id, ...prev.toastedReplyIds].slice(0, 50)
|
|
840
|
+
};
|
|
841
|
+
writeNotifState(orgId, next);
|
|
842
|
+
return next;
|
|
843
|
+
});
|
|
844
|
+
}, [payload, replies, toastReply, notif.lastSeenAt, notif.toastedReplyIds, orgId]);
|
|
845
|
+
const onToastActivate = react.useCallback(() => {
|
|
846
|
+
const href = toastReply?.href;
|
|
847
|
+
setToastReply(null);
|
|
848
|
+
if (href && typeof window !== "undefined") {
|
|
849
|
+
window.location.assign(href);
|
|
850
|
+
return;
|
|
555
851
|
}
|
|
852
|
+
setOpenToken((t) => t + 1);
|
|
853
|
+
}, [toastReply]);
|
|
854
|
+
const unreadAlertCount = alerts.filter((a) => !notif.readAlertIds.includes(a.id)).length;
|
|
855
|
+
const unseenReplyCount = replies.filter((r) => {
|
|
856
|
+
if (!notif.lastSeenAt) return true;
|
|
857
|
+
if (!r.createdAt) return false;
|
|
858
|
+
return r.createdAt > notif.lastSeenAt;
|
|
859
|
+
}).length;
|
|
860
|
+
const badgeCount = unreadAlertCount + unseenReplyCount;
|
|
861
|
+
const isEmpty = alerts.length === 0 && replies.length === 0 && imports.length === 0 && quarantined.length === 0;
|
|
862
|
+
return (
|
|
863
|
+
// Relative wrapper so the new-reply toast anchors to the bell trigger,
|
|
864
|
+
// outside the popover panel (same pattern as the fuel low-fuel nudge).
|
|
865
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative inline-flex", children: [
|
|
866
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
867
|
+
TopBarPopover,
|
|
868
|
+
{
|
|
869
|
+
label: "Alerts & Notifications",
|
|
870
|
+
description: "System alerts and recent activity for the active organization.",
|
|
871
|
+
slot: "notifications",
|
|
872
|
+
fallbackIcon: BellGlyph,
|
|
873
|
+
badge: loading ? 0 : badgeCount,
|
|
874
|
+
onOpen,
|
|
875
|
+
openToken,
|
|
876
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
|
|
877
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Notifications" }),
|
|
878
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 py-1", children: [
|
|
879
|
+
/* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-4 w-full" }),
|
|
880
|
+
/* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-4 w-3/4" })
|
|
881
|
+
] }) : error ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[color:var(--muted)]", children: "Notifications are unavailable right now." }) : isEmpty ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-1 text-sm text-[color:var(--muted)]", children: "You\u2019re all caught up." }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-h-80 flex-col gap-2 overflow-y-auto", children: [
|
|
882
|
+
alerts.length > 0 || replies.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "flex flex-col gap-1", children: [
|
|
883
|
+
alerts.map((a) => {
|
|
884
|
+
const isRead = notif.readAlertIds.includes(a.id);
|
|
885
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { className: isRead ? "opacity-50" : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-1 rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
886
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
887
|
+
"a",
|
|
888
|
+
{
|
|
889
|
+
href: a.href ?? "#",
|
|
890
|
+
onClick: () => markRead(a.id),
|
|
891
|
+
className: "block min-w-0 flex-1",
|
|
892
|
+
children: [
|
|
893
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: a.title }),
|
|
894
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-[color:var(--muted)]", children: a.description })
|
|
895
|
+
]
|
|
896
|
+
}
|
|
897
|
+
),
|
|
898
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
899
|
+
"button",
|
|
900
|
+
{
|
|
901
|
+
type: "button",
|
|
902
|
+
"aria-label": isRead ? "Mark as unread" : "Mark as read",
|
|
903
|
+
onClick: () => isRead ? markUnread(a.id) : markRead(a.id),
|
|
904
|
+
className: "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[color:var(--muted)] transition hover:bg-[color:var(--panel-elevated)] hover:text-[color:var(--foreground)]",
|
|
905
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(CheckGlyph, {})
|
|
906
|
+
}
|
|
907
|
+
)
|
|
908
|
+
] }) }, a.id);
|
|
909
|
+
}),
|
|
910
|
+
replies.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
911
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: r.name }),
|
|
912
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-xs text-[color:var(--muted)]", children: r.preview })
|
|
913
|
+
] }, r.id))
|
|
914
|
+
] }) : null,
|
|
915
|
+
imports.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
916
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Imports" }),
|
|
917
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1", children: imports.map((imp) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: imp.href ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: imp.href, className: "block", children: /* @__PURE__ */ jsxRuntime.jsx(ImportRow, { imp }) }) : /* @__PURE__ */ jsxRuntime.jsx(ImportRow, { imp }) }, imp.id)) })
|
|
918
|
+
] }) : null,
|
|
919
|
+
quarantined.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
920
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center justify-between px-2", children: [
|
|
921
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Quarantined Contacts" }),
|
|
922
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
923
|
+
"a",
|
|
924
|
+
{
|
|
925
|
+
href: QUARANTINED_CONTACTS_URL,
|
|
926
|
+
className: "text-[11px] font-semibold text-[color:var(--color-race-red)] hover:underline",
|
|
927
|
+
children: "View all"
|
|
928
|
+
}
|
|
929
|
+
)
|
|
930
|
+
] }),
|
|
931
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1", children: quarantined.map((q) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
932
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
933
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: formatPhone(q.phone) }),
|
|
934
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--panel)] px-1 text-[10px] tabular-nums text-[color:var(--muted)]", children: q.count })
|
|
935
|
+
] }),
|
|
936
|
+
q.latestAt ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-[color:var(--muted)]", children: new Date(q.latestAt).toLocaleString() }) : null
|
|
937
|
+
] }, q.phone)) })
|
|
938
|
+
] }) : null
|
|
939
|
+
] })
|
|
940
|
+
] })
|
|
941
|
+
}
|
|
942
|
+
),
|
|
943
|
+
toastReply ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
944
|
+
NewReplyToast,
|
|
945
|
+
{
|
|
946
|
+
reply: toastReply,
|
|
947
|
+
onActivate: onToastActivate,
|
|
948
|
+
onClose: () => setToastReply(null)
|
|
949
|
+
}
|
|
950
|
+
) : null
|
|
951
|
+
] })
|
|
556
952
|
);
|
|
557
953
|
}
|
|
558
|
-
function
|
|
954
|
+
function composeSupportMessage(input) {
|
|
955
|
+
const ctx = [`Page: ${input.pageTitle || "Unknown"}`, `URL: ${input.url}`];
|
|
956
|
+
if (input.orgName) ctx.push(`Org: ${input.orgName}`);
|
|
957
|
+
const reply = [input.replyEmail?.trim(), input.replyPhone?.trim()].filter(Boolean).join(" / ");
|
|
958
|
+
if (reply) ctx.push(`Reply-to: ${reply}`);
|
|
959
|
+
return `${input.note.trim()}
|
|
960
|
+
|
|
961
|
+
---
|
|
962
|
+
${ctx.join("\n")}`;
|
|
963
|
+
}
|
|
964
|
+
function SupportAction({ orgId } = {}) {
|
|
559
965
|
const { fetchJson } = useNavChrome("SupportAction");
|
|
966
|
+
const { me, loaded } = useChromeMe();
|
|
967
|
+
const { orgs } = useChromeOrgs();
|
|
560
968
|
const [open, setOpen] = react.useState(false);
|
|
561
969
|
const [message, setMessage] = react.useState("");
|
|
970
|
+
const [email, setEmail] = react.useState("");
|
|
971
|
+
const [phone, setPhone] = react.useState("");
|
|
972
|
+
const [showEmailError, setShowEmailError] = react.useState(false);
|
|
562
973
|
const [state, setState] = react.useState("idle");
|
|
974
|
+
const meEmail = me?.email ?? null;
|
|
975
|
+
const mePhone = me?.phone ?? null;
|
|
976
|
+
const missingEmail = loaded && !meEmail;
|
|
977
|
+
const missingPhone = loaded && !mePhone;
|
|
978
|
+
const activeOrg = (orgId ? orgs?.find((o) => o.id === orgId) : null) ?? (orgs && orgs.length === 1 ? orgs[0] : null);
|
|
979
|
+
const orgName = activeOrg?.name ?? null;
|
|
563
980
|
const close = react.useCallback(() => {
|
|
564
981
|
setOpen(false);
|
|
565
982
|
setState("idle");
|
|
566
983
|
setMessage("");
|
|
984
|
+
setShowEmailError(false);
|
|
567
985
|
}, []);
|
|
986
|
+
react.useEffect(() => {
|
|
987
|
+
if (!open) return;
|
|
988
|
+
setEmail(meEmail ?? "");
|
|
989
|
+
setPhone(mePhone ?? "");
|
|
990
|
+
setShowEmailError(false);
|
|
991
|
+
}, [open, meEmail, mePhone]);
|
|
568
992
|
react.useEffect(() => {
|
|
569
993
|
if (!open) return;
|
|
570
994
|
const onKey = (e) => {
|
|
@@ -575,12 +999,28 @@ function SupportAction() {
|
|
|
575
999
|
}, [open, close]);
|
|
576
1000
|
const submit = async () => {
|
|
577
1001
|
if (!message.trim() || state === "sending") return;
|
|
1002
|
+
const resolvedEmail = (missingEmail ? email : meEmail ?? "").trim();
|
|
1003
|
+
const resolvedPhone = (missingPhone ? phone : mePhone ?? "").trim();
|
|
1004
|
+
if (missingEmail && !resolvedEmail) {
|
|
1005
|
+
setShowEmailError(true);
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
setShowEmailError(false);
|
|
1009
|
+
const collectedContact = missingEmail || missingPhone;
|
|
1010
|
+
const fullMessage = composeSupportMessage({
|
|
1011
|
+
note: message,
|
|
1012
|
+
pageTitle: typeof document !== "undefined" ? document.title : "",
|
|
1013
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
1014
|
+
orgName,
|
|
1015
|
+
replyEmail: collectedContact ? resolvedEmail : void 0,
|
|
1016
|
+
replyPhone: collectedContact ? resolvedPhone : void 0
|
|
1017
|
+
});
|
|
578
1018
|
setState("sending");
|
|
579
1019
|
try {
|
|
580
1020
|
await fetchJson("support", {
|
|
581
1021
|
method: "POST",
|
|
582
1022
|
headers: { "Content-Type": "application/json" },
|
|
583
|
-
body: JSON.stringify({ message:
|
|
1023
|
+
body: JSON.stringify({ message: fullMessage })
|
|
584
1024
|
});
|
|
585
1025
|
setState("sent");
|
|
586
1026
|
} catch {
|
|
@@ -633,6 +1073,33 @@ function SupportAction() {
|
|
|
633
1073
|
) })
|
|
634
1074
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
635
1075
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-[color:var(--muted)]", children: "Describe the problem or question. Your account details ride along automatically." }),
|
|
1076
|
+
missingEmail ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-4 block", children: [
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block text-xs font-semibold text-[color:var(--foreground)]", children: "Your email" }),
|
|
1078
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1079
|
+
"input",
|
|
1080
|
+
{
|
|
1081
|
+
type: "email",
|
|
1082
|
+
value: email,
|
|
1083
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1084
|
+
placeholder: "you@example.com",
|
|
1085
|
+
className: "w-full rounded-lg border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-race-red)]"
|
|
1086
|
+
}
|
|
1087
|
+
),
|
|
1088
|
+
showEmailError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-xs text-[color:var(--color-race-red)]", children: "Enter an email so support can reply." }) : null
|
|
1089
|
+
] }) : null,
|
|
1090
|
+
missingPhone ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-3 block", children: [
|
|
1091
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block text-xs font-semibold text-[color:var(--foreground)]", children: "Your phone \u2014 optional" }),
|
|
1092
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1093
|
+
"input",
|
|
1094
|
+
{
|
|
1095
|
+
type: "tel",
|
|
1096
|
+
value: phone,
|
|
1097
|
+
onChange: (e) => setPhone(e.target.value),
|
|
1098
|
+
placeholder: "(503) 555-1212",
|
|
1099
|
+
className: "w-full rounded-lg border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-race-red)]"
|
|
1100
|
+
}
|
|
1101
|
+
)
|
|
1102
|
+
] }) : null,
|
|
636
1103
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
637
1104
|
"textarea",
|
|
638
1105
|
{
|
|
@@ -2012,6 +2479,7 @@ function resolveIdentity(input) {
|
|
|
2012
2479
|
loaded: input.loaded ?? false
|
|
2013
2480
|
};
|
|
2014
2481
|
}
|
|
2482
|
+
var CORE_APP = "vrm";
|
|
2015
2483
|
var POLITOGY_APPS = {
|
|
2016
2484
|
vrm: { title: "Politogy VRM", origin: "https://app.politogyvrm.com", storagePrefix: "vrm" },
|
|
2017
2485
|
contacts: { title: "Contacts", origin: "https://contacts.politogyvrm.com", storagePrefix: "contacts" },
|
|
@@ -2087,21 +2555,24 @@ function FrameBody({
|
|
|
2087
2555
|
}) {
|
|
2088
2556
|
const meta = POLITOGY_APPS[app];
|
|
2089
2557
|
const chrome = useChromeState();
|
|
2558
|
+
const core = app === CORE_APP ? data?.core : void 0;
|
|
2090
2559
|
const identity = useChromeIdentity({ userName: session.userName, email: session.email });
|
|
2091
|
-
const
|
|
2560
|
+
const derivedUserMenu = useChromeUserMenu({
|
|
2092
2561
|
userId: session.userId,
|
|
2093
2562
|
userName: identity.userName,
|
|
2094
2563
|
email: identity.email
|
|
2095
2564
|
});
|
|
2565
|
+
const userMenu = core?.userMenu ?? derivedUserMenu;
|
|
2096
2566
|
const footer = useChromeFooter(buildMeta ?? {});
|
|
2097
2567
|
const appMode = meta.mode;
|
|
2098
|
-
const
|
|
2568
|
+
const derivedMode = useChromeMode(appMode ? { pinnedModeId: appMode } : void 0);
|
|
2569
|
+
const mode = core?.mode ?? derivedMode;
|
|
2099
2570
|
const coreOrgs = chrome?.orgs ?? [];
|
|
2100
2571
|
const activeCoreOrg = coreOrgs.find((o) => o.id === chrome?.orgId) ?? null;
|
|
2101
2572
|
const navDisplay = chrome?.navDisplay ?? "both";
|
|
2102
2573
|
const navMode = appMode ?? "relationship";
|
|
2103
2574
|
const activeOrgId = chrome?.orgId ?? null;
|
|
2104
|
-
const
|
|
2575
|
+
const derivedNavItems = react.useMemo(
|
|
2105
2576
|
() => standardPolitogyNav({
|
|
2106
2577
|
coreBaseUrl: POLITOGY_APPS.vrm.origin,
|
|
2107
2578
|
icons: standardNavIcons,
|
|
@@ -2113,17 +2584,19 @@ function FrameBody({
|
|
|
2113
2584
|
}),
|
|
2114
2585
|
[data?.contactsItem, navMode, activeOrgId]
|
|
2115
2586
|
);
|
|
2116
|
-
const
|
|
2587
|
+
const navItems = core?.nav ?? derivedNavItems;
|
|
2588
|
+
const derivedBreadcrumbs = react.useMemo(
|
|
2117
2589
|
() => deriveBreadcrumbs(navItems, pathname, { search, origin: meta.origin }),
|
|
2118
2590
|
[navItems, pathname, search, meta.origin]
|
|
2119
2591
|
);
|
|
2592
|
+
const breadcrumbs = core ? core.page?.breadcrumbs : derivedBreadcrumbs;
|
|
2120
2593
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2121
2594
|
NavAppShell,
|
|
2122
2595
|
{
|
|
2123
2596
|
sidebar: {
|
|
2124
2597
|
pathname,
|
|
2125
2598
|
brand: {
|
|
2126
|
-
logoSrc: "/brand/logos/relationship-mode-logo.png",
|
|
2599
|
+
logoSrc: core?.brandLogoSrc ?? "/brand/logos/relationship-mode-logo.png",
|
|
2127
2600
|
logoAlt: "Politogy",
|
|
2128
2601
|
badgeLabel: "P",
|
|
2129
2602
|
title: "Politogy"
|
|
@@ -2144,8 +2617,11 @@ function FrameBody({
|
|
|
2144
2617
|
linkComponent
|
|
2145
2618
|
},
|
|
2146
2619
|
betaBanner: {},
|
|
2620
|
+
contentWrapper: core?.contentWrapper,
|
|
2147
2621
|
topBar: {
|
|
2148
|
-
|
|
2622
|
+
// Core's pages publish their own identity; every other app is one
|
|
2623
|
+
// product surface and takes its registry title.
|
|
2624
|
+
title: core?.page?.title ?? meta.title,
|
|
2149
2625
|
breadcrumbs,
|
|
2150
2626
|
user: {
|
|
2151
2627
|
name: identity.userName,
|
|
@@ -2157,7 +2633,13 @@ function FrameBody({
|
|
|
2157
2633
|
},
|
|
2158
2634
|
userMenu,
|
|
2159
2635
|
onSignOut,
|
|
2160
|
-
|
|
2636
|
+
// The action cluster is IDENTICAL everywhere — core included. Page
|
|
2637
|
+
// actions sit BEFORE it, never inside it, so the Alerts bell keeps its
|
|
2638
|
+
// required position: farthest right, immediately before My Account.
|
|
2639
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2640
|
+
core?.page?.actions,
|
|
2641
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChromeTopBarActions, {})
|
|
2642
|
+
] })
|
|
2161
2643
|
},
|
|
2162
2644
|
children
|
|
2163
2645
|
}
|
|
@@ -2303,6 +2785,7 @@ function AppDevNotes({
|
|
|
2303
2785
|
}),
|
|
2304
2786
|
[storagePrefix, identity.role]
|
|
2305
2787
|
);
|
|
2788
|
+
if (devNotes?.providedByApp) return /* @__PURE__ */ jsxRuntime.jsx(DevNotesSurface, { children });
|
|
2306
2789
|
return /* @__PURE__ */ jsxRuntime.jsx(devnotes.DevNotesProvider, { adapter, user, config, children: /* @__PURE__ */ jsxRuntime.jsx(DevNotesSurface, { children }) });
|
|
2307
2790
|
}
|
|
2308
2791
|
var DevNotesTasksContext = react.createContext({
|
|
@@ -2369,7 +2852,7 @@ function ChromeTopBarActions() {
|
|
|
2369
2852
|
children: /* @__PURE__ */ jsxRuntime.jsx(ChromeDevNotesMenu, {})
|
|
2370
2853
|
}
|
|
2371
2854
|
),
|
|
2372
|
-
/* @__PURE__ */ jsxRuntime.jsx(SupportAction, {}),
|
|
2855
|
+
/* @__PURE__ */ jsxRuntime.jsx(SupportAction, { orgId: state.orgId }),
|
|
2373
2856
|
/* @__PURE__ */ jsxRuntime.jsx(NotificationsAction, { orgId: state.orgId })
|
|
2374
2857
|
] });
|
|
2375
2858
|
}
|