@the-portland-company/shell 0.32.2 → 0.34.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 +535 -68
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +0 -1
- package/dist/app-chrome.d.ts +0 -1
- package/dist/app-chrome.js +2 -3
- package/dist/app-chrome.js.map +1 -1
- package/dist/{chunk-PWBYDQCO.js → chunk-CVBEBCID.js} +584 -71
- package/dist/chunk-CVBEBCID.js.map +1 -0
- package/dist/index.cjs +4 -1927
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -295
- package/dist/index.d.ts +2 -295
- package/dist/index.js +11 -1809
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +536 -67
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +56 -5
- package/dist/native.d.ts +56 -5
- package/dist/native.js +1 -2
- package/package.json +1 -1
- package/dist/chunk-NV6WHTEZ.js +0 -54
- package/dist/chunk-NV6WHTEZ.js.map +0 -1
- package/dist/chunk-PWBYDQCO.js.map +0 -1
- package/dist/topbarIconSlot-FunwEKO9.d.cts +0 -10
- package/dist/topbarIconSlot-FunwEKO9.d.ts +0 -10
|
@@ -1,9 +1,59 @@
|
|
|
1
|
-
import { useTopBarIconSlotGlyph, resolveBrandGlyph } from './chunk-NV6WHTEZ.js';
|
|
2
1
|
import { readThemeCookie, THEME_STORAGE_KEY, isThemePreference, applyThemePreference, readPendingThemeChoice, writeThemeCookie, clearPendingThemeChoice, markThemeChoicePending } from './chunk-LL7RCJQN.js';
|
|
3
2
|
import { createContext, useState, useEffect, useContext, useRef, useCallback, useMemo } from 'react';
|
|
3
|
+
import { FiUser, FiSettings, FiMessageSquare, FiMail, FiInfo, FiSun, FiMoon, FiHelpCircle, FiLifeBuoy, FiAlertCircle, FiAlertTriangle, FiBell, FiGrid, FiZap, FiBatteryCharging, FiBattery } from 'react-icons/fi';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
|
+
function useTopBarIconSlotGlyph(slot) {
|
|
8
|
+
const [name, setName] = useState(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (typeof window === "undefined") return;
|
|
11
|
+
const read = () => {
|
|
12
|
+
const raw = window.getComputedStyle(document.documentElement).getPropertyValue(`--topbar-icon-${slot}`).trim().replace(/^['"]|['"]$/g, "");
|
|
13
|
+
setName(raw.length > 0 ? raw : null);
|
|
14
|
+
};
|
|
15
|
+
read();
|
|
16
|
+
const observer = new MutationObserver(read);
|
|
17
|
+
observer.observe(document.head, { childList: true, subtree: true });
|
|
18
|
+
return () => observer.disconnect();
|
|
19
|
+
}, [slot]);
|
|
20
|
+
return name;
|
|
21
|
+
}
|
|
22
|
+
var GLYPHS = {
|
|
23
|
+
// Communications Fuel reads as a LEVEL, so its glyph is a battery. `bolt`/
|
|
24
|
+
// `zap` stay mapped for older Brand values.
|
|
25
|
+
"battery-charging": FiBatteryCharging,
|
|
26
|
+
battery: FiBattery,
|
|
27
|
+
fuel: FiBatteryCharging,
|
|
28
|
+
bolt: FiZap,
|
|
29
|
+
zap: FiZap,
|
|
30
|
+
grid: FiGrid,
|
|
31
|
+
apps: FiGrid,
|
|
32
|
+
bell: FiBell,
|
|
33
|
+
notification: FiBell,
|
|
34
|
+
"alert-triangle": FiAlertTriangle,
|
|
35
|
+
alert: FiAlertTriangle,
|
|
36
|
+
"alert-circle": FiAlertCircle,
|
|
37
|
+
"life-buoy": FiLifeBuoy,
|
|
38
|
+
"life-ring": FiLifeBuoy,
|
|
39
|
+
support: FiLifeBuoy,
|
|
40
|
+
"help-circle": FiHelpCircle,
|
|
41
|
+
moon: FiMoon,
|
|
42
|
+
sun: FiSun,
|
|
43
|
+
info: FiInfo,
|
|
44
|
+
mail: FiMail,
|
|
45
|
+
"message-square": FiMessageSquare,
|
|
46
|
+
settings: FiSettings,
|
|
47
|
+
user: FiUser
|
|
48
|
+
};
|
|
49
|
+
function resolveBrandGlyph(name) {
|
|
50
|
+
if (!name) return null;
|
|
51
|
+
return GLYPHS[name.trim().toLowerCase()] ?? null;
|
|
52
|
+
}
|
|
53
|
+
var CORE_APP_ORIGIN = "https://app.politogyvrm.com";
|
|
54
|
+
var CORE_BILLING_PATH = "/organization-settings/subscription-billing/text-email";
|
|
55
|
+
var CORE_BILLING_URL = `${CORE_APP_ORIGIN}${CORE_BILLING_PATH}`;
|
|
56
|
+
var QUARANTINED_CONTACTS_URL = `${CORE_APP_ORIGIN}/relationship/contacts/quarantined`;
|
|
7
57
|
function topBarIconButtonClass(tone = "default") {
|
|
8
58
|
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)]";
|
|
9
59
|
return [
|
|
@@ -186,11 +236,22 @@ function TopBarPopover({
|
|
|
186
236
|
badge,
|
|
187
237
|
tone,
|
|
188
238
|
onOpen,
|
|
239
|
+
openToken,
|
|
189
240
|
children,
|
|
190
241
|
panelClassName
|
|
191
242
|
}) {
|
|
192
243
|
const [open, setOpen] = useState(false);
|
|
193
244
|
const rootRef = useRef(null);
|
|
245
|
+
const lastOpenTokenRef = useRef(openToken);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (openToken === void 0) return;
|
|
248
|
+
if (openToken === lastOpenTokenRef.current) return;
|
|
249
|
+
lastOpenTokenRef.current = openToken;
|
|
250
|
+
setOpen((v) => {
|
|
251
|
+
if (!v) onOpen?.();
|
|
252
|
+
return true;
|
|
253
|
+
});
|
|
254
|
+
}, [openToken, onOpen]);
|
|
194
255
|
useEffect(() => {
|
|
195
256
|
if (!open) return;
|
|
196
257
|
const onDocClick = (e) => {
|
|
@@ -274,31 +335,60 @@ function LayoutGlyph({ size = 20, className }) {
|
|
|
274
335
|
/* @__PURE__ */ jsx("line", { x1: "9", y1: "21", x2: "9", y2: "9" })
|
|
275
336
|
] });
|
|
276
337
|
}
|
|
338
|
+
function PlusGlyph({ size = 14, className }) {
|
|
339
|
+
return /* @__PURE__ */ 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: [
|
|
340
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
|
|
341
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
342
|
+
] });
|
|
343
|
+
}
|
|
344
|
+
function XGlyph({ size = 14, className }) {
|
|
345
|
+
return /* @__PURE__ */ 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: [
|
|
346
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
347
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
348
|
+
] });
|
|
349
|
+
}
|
|
350
|
+
function CheckGlyph({ size = 14, className }) {
|
|
351
|
+
return /* @__PURE__ */ 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__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) });
|
|
352
|
+
}
|
|
353
|
+
var LOW_FUEL_HINT_DISMISSED_KEY = "politogy_low_fuel_hint_dismissed";
|
|
277
354
|
function FuelRow({
|
|
278
355
|
label,
|
|
279
356
|
channel,
|
|
280
|
-
loading
|
|
357
|
+
loading,
|
|
358
|
+
addHref,
|
|
359
|
+
addLabel
|
|
281
360
|
}) {
|
|
282
361
|
const remaining = channel?.remainingUnits ?? 0;
|
|
283
362
|
const total = channel ? Math.max(channel.rawBalanceUnits, channel.remainingUnits) : 0;
|
|
284
363
|
const ratio = total > 0 ? Math.min(100, Math.max(0, remaining / total * 100)) : 0;
|
|
285
364
|
const low = channel?.lowFuel ?? false;
|
|
286
365
|
return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
|
|
287
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
366
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
|
|
288
367
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-[color:var(--foreground)]", children: label }),
|
|
289
|
-
|
|
290
|
-
"
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
368
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
369
|
+
loading ? /* @__PURE__ */ jsx(ValueSkeleton, { className: "h-3.5 w-14" }) : /* @__PURE__ */ jsxs(
|
|
370
|
+
"span",
|
|
371
|
+
{
|
|
372
|
+
className: [
|
|
373
|
+
"text-xs font-semibold tabular-nums",
|
|
374
|
+
low ? "text-[color:var(--color-race-red)]" : "text-[color:var(--muted)]"
|
|
375
|
+
].join(" "),
|
|
376
|
+
children: [
|
|
377
|
+
remaining.toLocaleString(),
|
|
378
|
+
" left"
|
|
379
|
+
]
|
|
380
|
+
}
|
|
381
|
+
),
|
|
382
|
+
addHref ? /* @__PURE__ */ jsx(
|
|
383
|
+
"a",
|
|
384
|
+
{
|
|
385
|
+
href: addHref,
|
|
386
|
+
"aria-label": addLabel,
|
|
387
|
+
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)]",
|
|
388
|
+
children: /* @__PURE__ */ jsx(PlusGlyph, {})
|
|
389
|
+
}
|
|
390
|
+
) : null
|
|
391
|
+
] })
|
|
302
392
|
] }),
|
|
303
393
|
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-[color:var(--panel)]", children: loading ? /* @__PURE__ */ jsx("div", { className: "h-full w-full animate-pulse bg-[color:var(--line)]" }) : /* @__PURE__ */ jsx(
|
|
304
394
|
"div",
|
|
@@ -312,6 +402,71 @@ function FuelRow({
|
|
|
312
402
|
) })
|
|
313
403
|
] });
|
|
314
404
|
}
|
|
405
|
+
function LowFuelHint({ lowFuel }) {
|
|
406
|
+
const [dismissed, setDismissed] = useState(() => {
|
|
407
|
+
if (typeof window === "undefined") return false;
|
|
408
|
+
try {
|
|
409
|
+
return window.sessionStorage.getItem(LOW_FUEL_HINT_DISMISSED_KEY) === "1";
|
|
410
|
+
} catch {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
useEffect(() => {
|
|
415
|
+
if (lowFuel) return;
|
|
416
|
+
try {
|
|
417
|
+
window.sessionStorage.removeItem(LOW_FUEL_HINT_DISMISSED_KEY);
|
|
418
|
+
} catch {
|
|
419
|
+
}
|
|
420
|
+
}, [lowFuel]);
|
|
421
|
+
if (!lowFuel || dismissed) return null;
|
|
422
|
+
const dismiss = () => {
|
|
423
|
+
try {
|
|
424
|
+
window.sessionStorage.setItem(LOW_FUEL_HINT_DISMISSED_KEY, "1");
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
setDismissed(true);
|
|
428
|
+
};
|
|
429
|
+
return /* @__PURE__ */ jsxs(
|
|
430
|
+
"div",
|
|
431
|
+
{
|
|
432
|
+
role: "status",
|
|
433
|
+
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)]",
|
|
434
|
+
onClick: (e) => e.stopPropagation(),
|
|
435
|
+
children: [
|
|
436
|
+
/* @__PURE__ */ jsx(
|
|
437
|
+
"span",
|
|
438
|
+
{
|
|
439
|
+
"aria-hidden": true,
|
|
440
|
+
className: "absolute -top-[5px] right-[11px] h-2.5 w-2.5 rotate-45 bg-[color:var(--color-race-red)]"
|
|
441
|
+
}
|
|
442
|
+
),
|
|
443
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
444
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
445
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "You\u2019re low on fuel" }),
|
|
446
|
+
/* @__PURE__ */ jsx(
|
|
447
|
+
"a",
|
|
448
|
+
{
|
|
449
|
+
href: CORE_BILLING_URL,
|
|
450
|
+
className: "mt-1 inline-block text-sm font-bold underline hover:opacity-90",
|
|
451
|
+
children: "Buy More Fuel"
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
] }),
|
|
455
|
+
/* @__PURE__ */ jsx(
|
|
456
|
+
"button",
|
|
457
|
+
{
|
|
458
|
+
type: "button",
|
|
459
|
+
"aria-label": "Dismiss low fuel notice",
|
|
460
|
+
onClick: dismiss,
|
|
461
|
+
className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-white/90 transition hover:bg-white/20",
|
|
462
|
+
children: /* @__PURE__ */ jsx(XGlyph, {})
|
|
463
|
+
}
|
|
464
|
+
)
|
|
465
|
+
] })
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
}
|
|
315
470
|
function FuelAction({ orgId }) {
|
|
316
471
|
const { fetchJson } = useNavChrome("FuelAction");
|
|
317
472
|
const [fuel, setFuel] = useState(null);
|
|
@@ -337,21 +492,154 @@ function FuelAction({ orgId }) {
|
|
|
337
492
|
};
|
|
338
493
|
}, [fetchJson, orgId]);
|
|
339
494
|
const low = !!fuel && (fuel.signal.lowFuel || fuel.broadcast.lowFuel);
|
|
340
|
-
return
|
|
341
|
-
|
|
495
|
+
return (
|
|
496
|
+
// The nudge anchors to the trigger, so it sits OUTSIDE the popover panel in
|
|
497
|
+
// its own relative wrapper (same shape the VRM widget used).
|
|
498
|
+
/* @__PURE__ */ jsxs("div", { className: "relative inline-flex", children: [
|
|
499
|
+
/* @__PURE__ */ jsx(
|
|
500
|
+
TopBarPopover,
|
|
501
|
+
{
|
|
502
|
+
label: "Communications Fuel",
|
|
503
|
+
description: "Remaining SMS and email sending balance for the active organization.",
|
|
504
|
+
slot: "comms-fuel",
|
|
505
|
+
fallbackIcon: BatteryGlyph,
|
|
506
|
+
tone: low ? "warning" : "default",
|
|
507
|
+
children: /* @__PURE__ */ jsxs("div", { className: "px-4 py-3", children: [
|
|
508
|
+
/* @__PURE__ */ jsx("p", { className: "mb-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Communications Fuel" }),
|
|
509
|
+
error || !loading && !fuel ? /* @__PURE__ */ jsx("p", { className: "text-sm text-[color:var(--muted)]", children: "Fuel balance is unavailable right now." }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
510
|
+
/* @__PURE__ */ jsx(
|
|
511
|
+
FuelRow,
|
|
512
|
+
{
|
|
513
|
+
label: "SMS Signal",
|
|
514
|
+
channel: fuel?.signal ?? null,
|
|
515
|
+
loading,
|
|
516
|
+
addHref: CORE_BILLING_URL,
|
|
517
|
+
addLabel: "Add SMS Signal fuel"
|
|
518
|
+
}
|
|
519
|
+
),
|
|
520
|
+
/* @__PURE__ */ jsx(
|
|
521
|
+
FuelRow,
|
|
522
|
+
{
|
|
523
|
+
label: "Email Broadcast",
|
|
524
|
+
channel: fuel?.broadcast ?? null,
|
|
525
|
+
loading,
|
|
526
|
+
addHref: CORE_BILLING_URL,
|
|
527
|
+
addLabel: "Add Email Broadcast fuel"
|
|
528
|
+
}
|
|
529
|
+
)
|
|
530
|
+
] })
|
|
531
|
+
] })
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
/* @__PURE__ */ jsx(LowFuelHint, { lowFuel: low })
|
|
535
|
+
] })
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
function notifStateKey(orgId) {
|
|
539
|
+
return `politogy_chrome_notif_state:${orgId ?? "none"}`;
|
|
540
|
+
}
|
|
541
|
+
function readNotifState(orgId) {
|
|
542
|
+
const empty = { readAlertIds: [], lastSeenAt: null, toastedReplyIds: [] };
|
|
543
|
+
if (typeof window === "undefined") return empty;
|
|
544
|
+
try {
|
|
545
|
+
const raw = window.localStorage.getItem(notifStateKey(orgId));
|
|
546
|
+
if (!raw) return empty;
|
|
547
|
+
const parsed = JSON.parse(raw);
|
|
548
|
+
return {
|
|
549
|
+
readAlertIds: Array.isArray(parsed?.readAlertIds) ? parsed.readAlertIds.filter((id) => typeof id === "string") : [],
|
|
550
|
+
lastSeenAt: typeof parsed?.lastSeenAt === "string" ? parsed.lastSeenAt : null,
|
|
551
|
+
toastedReplyIds: Array.isArray(parsed?.toastedReplyIds) ? parsed.toastedReplyIds.filter((id) => typeof id === "string") : []
|
|
552
|
+
};
|
|
553
|
+
} catch {
|
|
554
|
+
return empty;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function writeNotifState(orgId, state) {
|
|
558
|
+
if (typeof window === "undefined") return;
|
|
559
|
+
try {
|
|
560
|
+
window.localStorage.setItem(notifStateKey(orgId), JSON.stringify(state));
|
|
561
|
+
} catch {
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
var TEN_DLC_REDIRECTED_KEY = "politogy_10dlc_redirected";
|
|
565
|
+
function formatPhone(raw) {
|
|
566
|
+
const digits = raw.replace(/\D/g, "");
|
|
567
|
+
const ten = digits.length === 11 && digits.startsWith("1") ? digits.slice(1) : digits;
|
|
568
|
+
if (ten.length === 10) return `(${ten.slice(0, 3)}) ${ten.slice(3, 6)}-${ten.slice(6)}`;
|
|
569
|
+
return raw;
|
|
570
|
+
}
|
|
571
|
+
function ImportRow({ imp }) {
|
|
572
|
+
const pct = imp.percent == null ? null : Math.max(0, Math.min(100, imp.percent));
|
|
573
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
574
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
575
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm text-[color:var(--foreground)]", children: imp.label }),
|
|
576
|
+
pct != null ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs tabular-nums text-[color:var(--muted)]", children: [
|
|
577
|
+
Math.round(pct),
|
|
578
|
+
"%"
|
|
579
|
+
] }) : null
|
|
580
|
+
] }),
|
|
581
|
+
pct != null ? /* @__PURE__ */ jsx("div", { className: "mt-1 h-1 w-full overflow-hidden rounded-full bg-[color:var(--panel)]", children: /* @__PURE__ */ jsx(
|
|
582
|
+
"div",
|
|
583
|
+
{
|
|
584
|
+
className: "h-full rounded-full bg-[color:var(--color-race-red)] transition-[width]",
|
|
585
|
+
style: { width: `${pct}%` }
|
|
586
|
+
}
|
|
587
|
+
) }) : null
|
|
588
|
+
] });
|
|
589
|
+
}
|
|
590
|
+
function NewReplyToast({
|
|
591
|
+
reply,
|
|
592
|
+
onActivate,
|
|
593
|
+
onClose,
|
|
594
|
+
ttlMs = 12e3
|
|
595
|
+
}) {
|
|
596
|
+
const [visible, setVisible] = useState(false);
|
|
597
|
+
const onCloseRef = useRef(onClose);
|
|
598
|
+
onCloseRef.current = onClose;
|
|
599
|
+
useEffect(() => {
|
|
600
|
+
setVisible(true);
|
|
601
|
+
const fadeAt = Math.max(0, ttlMs - 450);
|
|
602
|
+
const t1 = window.setTimeout(() => setVisible(false), fadeAt);
|
|
603
|
+
const t2 = window.setTimeout(() => onCloseRef.current(), ttlMs);
|
|
604
|
+
return () => {
|
|
605
|
+
window.clearTimeout(t1);
|
|
606
|
+
window.clearTimeout(t2);
|
|
607
|
+
};
|
|
608
|
+
}, [ttlMs]);
|
|
609
|
+
const activate = () => {
|
|
610
|
+
setVisible(false);
|
|
611
|
+
onActivate();
|
|
612
|
+
};
|
|
613
|
+
return /* @__PURE__ */ jsxs(
|
|
614
|
+
"div",
|
|
342
615
|
{
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
616
|
+
role: "button",
|
|
617
|
+
tabIndex: 0,
|
|
618
|
+
"aria-label": "Open new reply",
|
|
619
|
+
onClick: activate,
|
|
620
|
+
onKeyDown: (e) => {
|
|
621
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
622
|
+
e.preventDefault();
|
|
623
|
+
activate();
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
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)]",
|
|
627
|
+
style: {
|
|
628
|
+
opacity: visible ? 1 : 0,
|
|
629
|
+
transform: visible ? "translateY(0px)" : "translateY(-4px)",
|
|
630
|
+
transition: "opacity 250ms ease, transform 250ms ease"
|
|
631
|
+
},
|
|
632
|
+
children: [
|
|
633
|
+
/* @__PURE__ */ jsx(
|
|
634
|
+
"span",
|
|
635
|
+
{
|
|
636
|
+
"aria-hidden": true,
|
|
637
|
+
className: "absolute right-[18px] top-[-7px] h-0 w-0 border-x-[8px] border-x-transparent border-b-[8px] border-b-[#161616]"
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold", children: reply.name }),
|
|
641
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 truncate text-xs opacity-85", children: reply.preview })
|
|
642
|
+
]
|
|
355
643
|
}
|
|
356
644
|
);
|
|
357
645
|
}
|
|
@@ -360,6 +648,13 @@ function NotificationsAction({ orgId }) {
|
|
|
360
648
|
const [payload, setPayload] = useState(null);
|
|
361
649
|
const [loading, setLoading] = useState(true);
|
|
362
650
|
const [error, setError] = useState(false);
|
|
651
|
+
const [notif, setNotif] = useState(() => readNotifState(orgId));
|
|
652
|
+
const [toastReply, setToastReply] = useState(null);
|
|
653
|
+
const [openToken, setOpenToken] = useState(0);
|
|
654
|
+
useEffect(() => {
|
|
655
|
+
setNotif(readNotifState(orgId));
|
|
656
|
+
setToastReply(null);
|
|
657
|
+
}, [orgId]);
|
|
363
658
|
useEffect(() => {
|
|
364
659
|
let cancelled = false;
|
|
365
660
|
setLoading(true);
|
|
@@ -378,51 +673,226 @@ function NotificationsAction({ orgId }) {
|
|
|
378
673
|
cancelled = true;
|
|
379
674
|
};
|
|
380
675
|
}, [fetchJson, orgId]);
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
badge: loading ? 0 : count,
|
|
390
|
-
children: /* @__PURE__ */ jsxs("div", { className: "px-4 py-3", children: [
|
|
391
|
-
/* @__PURE__ */ jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Notifications" }),
|
|
392
|
-
loading ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 py-1", children: [
|
|
393
|
-
/* @__PURE__ */ jsx(ValueSkeleton, { className: "h-4 w-full" }),
|
|
394
|
-
/* @__PURE__ */ jsx(ValueSkeleton, { className: "h-4 w-3/4" })
|
|
395
|
-
] }) : error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-[color:var(--muted)]", children: "Notifications are unavailable right now." }) : count === 0 ? /* @__PURE__ */ jsx("p", { className: "py-1 text-sm text-[color:var(--muted)]", children: "You\u2019re all caught up." }) : /* @__PURE__ */ jsxs("ul", { className: "flex max-h-80 flex-col gap-1 overflow-y-auto", children: [
|
|
396
|
-
payload?.alerts.map((a) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
397
|
-
"a",
|
|
398
|
-
{
|
|
399
|
-
href: a.href ?? "#",
|
|
400
|
-
className: "block rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]",
|
|
401
|
-
children: [
|
|
402
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: a.title }),
|
|
403
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-[color:var(--muted)]", children: a.description })
|
|
404
|
-
]
|
|
405
|
-
}
|
|
406
|
-
) }, a.id)),
|
|
407
|
-
payload?.replies.map((r) => /* @__PURE__ */ jsxs("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
408
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: r.name }),
|
|
409
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-[color:var(--muted)]", children: r.preview })
|
|
410
|
-
] }, r.id))
|
|
411
|
-
] })
|
|
412
|
-
] })
|
|
676
|
+
useEffect(() => {
|
|
677
|
+
if (!payload || typeof window === "undefined") return;
|
|
678
|
+
const alert = payload.alerts.find((a) => a.kind === "10dlc_registration" && a.href);
|
|
679
|
+
if (!alert?.href) return;
|
|
680
|
+
try {
|
|
681
|
+
if (window.sessionStorage.getItem(TEN_DLC_REDIRECTED_KEY) === "1") return;
|
|
682
|
+
} catch {
|
|
683
|
+
return;
|
|
413
684
|
}
|
|
685
|
+
let target;
|
|
686
|
+
try {
|
|
687
|
+
target = new URL(alert.href, window.location.href);
|
|
688
|
+
} catch {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (target.origin !== window.location.origin) return;
|
|
692
|
+
try {
|
|
693
|
+
window.sessionStorage.setItem(TEN_DLC_REDIRECTED_KEY, "1");
|
|
694
|
+
} catch {
|
|
695
|
+
}
|
|
696
|
+
window.location.assign(alert.href);
|
|
697
|
+
}, [payload]);
|
|
698
|
+
const markRead = useCallback(
|
|
699
|
+
(id) => {
|
|
700
|
+
setNotif((prev) => {
|
|
701
|
+
if (prev.readAlertIds.includes(id)) return prev;
|
|
702
|
+
const next = { ...prev, readAlertIds: [...prev.readAlertIds, id] };
|
|
703
|
+
writeNotifState(orgId, next);
|
|
704
|
+
return next;
|
|
705
|
+
});
|
|
706
|
+
},
|
|
707
|
+
[orgId]
|
|
708
|
+
);
|
|
709
|
+
const markUnread = useCallback(
|
|
710
|
+
(id) => {
|
|
711
|
+
setNotif((prev) => {
|
|
712
|
+
if (!prev.readAlertIds.includes(id)) return prev;
|
|
713
|
+
const next = { ...prev, readAlertIds: prev.readAlertIds.filter((x) => x !== id) };
|
|
714
|
+
writeNotifState(orgId, next);
|
|
715
|
+
return next;
|
|
716
|
+
});
|
|
717
|
+
},
|
|
718
|
+
[orgId]
|
|
719
|
+
);
|
|
720
|
+
const onOpen = useCallback(() => {
|
|
721
|
+
setNotif((prev) => {
|
|
722
|
+
const next = { ...prev, lastSeenAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
723
|
+
writeNotifState(orgId, next);
|
|
724
|
+
return next;
|
|
725
|
+
});
|
|
726
|
+
}, [orgId]);
|
|
727
|
+
const alerts = payload?.alerts ?? [];
|
|
728
|
+
const replies = payload?.replies ?? [];
|
|
729
|
+
const imports = payload?.imports ?? [];
|
|
730
|
+
const quarantined = payload?.quarantined ?? [];
|
|
731
|
+
useEffect(() => {
|
|
732
|
+
if (!payload || toastReply) return;
|
|
733
|
+
const candidate = replies.filter((r) => {
|
|
734
|
+
if (!r.createdAt) return false;
|
|
735
|
+
if (notif.lastSeenAt && r.createdAt <= notif.lastSeenAt) return false;
|
|
736
|
+
return !notif.toastedReplyIds.includes(r.id);
|
|
737
|
+
}).sort((a, b) => a.createdAt < b.createdAt ? 1 : -1)[0];
|
|
738
|
+
if (!candidate) return;
|
|
739
|
+
setToastReply(candidate);
|
|
740
|
+
setNotif((prev) => {
|
|
741
|
+
const next = {
|
|
742
|
+
...prev,
|
|
743
|
+
toastedReplyIds: [candidate.id, ...prev.toastedReplyIds].slice(0, 50)
|
|
744
|
+
};
|
|
745
|
+
writeNotifState(orgId, next);
|
|
746
|
+
return next;
|
|
747
|
+
});
|
|
748
|
+
}, [payload, replies, toastReply, notif.lastSeenAt, notif.toastedReplyIds, orgId]);
|
|
749
|
+
const onToastActivate = useCallback(() => {
|
|
750
|
+
const href = toastReply?.href;
|
|
751
|
+
setToastReply(null);
|
|
752
|
+
if (href && typeof window !== "undefined") {
|
|
753
|
+
window.location.assign(href);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
setOpenToken((t) => t + 1);
|
|
757
|
+
}, [toastReply]);
|
|
758
|
+
const unreadAlertCount = alerts.filter((a) => !notif.readAlertIds.includes(a.id)).length;
|
|
759
|
+
const unseenReplyCount = replies.filter((r) => {
|
|
760
|
+
if (!notif.lastSeenAt) return true;
|
|
761
|
+
if (!r.createdAt) return false;
|
|
762
|
+
return r.createdAt > notif.lastSeenAt;
|
|
763
|
+
}).length;
|
|
764
|
+
const badgeCount = unreadAlertCount + unseenReplyCount;
|
|
765
|
+
const isEmpty = alerts.length === 0 && replies.length === 0 && imports.length === 0 && quarantined.length === 0;
|
|
766
|
+
return (
|
|
767
|
+
// Relative wrapper so the new-reply toast anchors to the bell trigger,
|
|
768
|
+
// outside the popover panel (same pattern as the fuel low-fuel nudge).
|
|
769
|
+
/* @__PURE__ */ jsxs("div", { className: "relative inline-flex", children: [
|
|
770
|
+
/* @__PURE__ */ jsx(
|
|
771
|
+
TopBarPopover,
|
|
772
|
+
{
|
|
773
|
+
label: "Alerts & Notifications",
|
|
774
|
+
description: "System alerts and recent activity for the active organization.",
|
|
775
|
+
slot: "notifications",
|
|
776
|
+
fallbackIcon: BellGlyph,
|
|
777
|
+
badge: loading ? 0 : badgeCount,
|
|
778
|
+
onOpen,
|
|
779
|
+
openToken,
|
|
780
|
+
children: /* @__PURE__ */ jsxs("div", { className: "px-4 py-3", children: [
|
|
781
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Notifications" }),
|
|
782
|
+
loading ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 py-1", children: [
|
|
783
|
+
/* @__PURE__ */ jsx(ValueSkeleton, { className: "h-4 w-full" }),
|
|
784
|
+
/* @__PURE__ */ jsx(ValueSkeleton, { className: "h-4 w-3/4" })
|
|
785
|
+
] }) : error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-[color:var(--muted)]", children: "Notifications are unavailable right now." }) : isEmpty ? /* @__PURE__ */ jsx("p", { className: "py-1 text-sm text-[color:var(--muted)]", children: "You\u2019re all caught up." }) : /* @__PURE__ */ jsxs("div", { className: "flex max-h-80 flex-col gap-2 overflow-y-auto", children: [
|
|
786
|
+
alerts.length > 0 || replies.length > 0 ? /* @__PURE__ */ jsxs("ul", { className: "flex flex-col gap-1", children: [
|
|
787
|
+
alerts.map((a) => {
|
|
788
|
+
const isRead = notif.readAlertIds.includes(a.id);
|
|
789
|
+
return /* @__PURE__ */ jsx("li", { className: isRead ? "opacity-50" : void 0, children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1 rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
790
|
+
/* @__PURE__ */ jsxs(
|
|
791
|
+
"a",
|
|
792
|
+
{
|
|
793
|
+
href: a.href ?? "#",
|
|
794
|
+
onClick: () => markRead(a.id),
|
|
795
|
+
className: "block min-w-0 flex-1",
|
|
796
|
+
children: [
|
|
797
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: a.title }),
|
|
798
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-[color:var(--muted)]", children: a.description })
|
|
799
|
+
]
|
|
800
|
+
}
|
|
801
|
+
),
|
|
802
|
+
/* @__PURE__ */ jsx(
|
|
803
|
+
"button",
|
|
804
|
+
{
|
|
805
|
+
type: "button",
|
|
806
|
+
"aria-label": isRead ? "Mark as unread" : "Mark as read",
|
|
807
|
+
onClick: () => isRead ? markUnread(a.id) : markRead(a.id),
|
|
808
|
+
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)]",
|
|
809
|
+
children: /* @__PURE__ */ jsx(CheckGlyph, {})
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
] }) }, a.id);
|
|
813
|
+
}),
|
|
814
|
+
replies.map((r) => /* @__PURE__ */ jsxs("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
815
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: r.name }),
|
|
816
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-[color:var(--muted)]", children: r.preview })
|
|
817
|
+
] }, r.id))
|
|
818
|
+
] }) : null,
|
|
819
|
+
imports.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
|
|
820
|
+
/* @__PURE__ */ jsx("p", { className: "mb-1 px-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Imports" }),
|
|
821
|
+
/* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1", children: imports.map((imp) => /* @__PURE__ */ jsx("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: imp.href ? /* @__PURE__ */ jsx("a", { href: imp.href, className: "block", children: /* @__PURE__ */ jsx(ImportRow, { imp }) }) : /* @__PURE__ */ jsx(ImportRow, { imp }) }, imp.id)) })
|
|
822
|
+
] }) : null,
|
|
823
|
+
quarantined.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
|
|
824
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between px-2", children: [
|
|
825
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Quarantined Contacts" }),
|
|
826
|
+
/* @__PURE__ */ jsx(
|
|
827
|
+
"a",
|
|
828
|
+
{
|
|
829
|
+
href: QUARANTINED_CONTACTS_URL,
|
|
830
|
+
className: "text-[11px] font-semibold text-[color:var(--color-race-red)] hover:underline",
|
|
831
|
+
children: "View all"
|
|
832
|
+
}
|
|
833
|
+
)
|
|
834
|
+
] }),
|
|
835
|
+
/* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1", children: quarantined.map((q) => /* @__PURE__ */ jsxs("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
836
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
837
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: formatPhone(q.phone) }),
|
|
838
|
+
/* @__PURE__ */ 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 })
|
|
839
|
+
] }),
|
|
840
|
+
q.latestAt ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[color:var(--muted)]", children: new Date(q.latestAt).toLocaleString() }) : null
|
|
841
|
+
] }, q.phone)) })
|
|
842
|
+
] }) : null
|
|
843
|
+
] })
|
|
844
|
+
] })
|
|
845
|
+
}
|
|
846
|
+
),
|
|
847
|
+
toastReply ? /* @__PURE__ */ jsx(
|
|
848
|
+
NewReplyToast,
|
|
849
|
+
{
|
|
850
|
+
reply: toastReply,
|
|
851
|
+
onActivate: onToastActivate,
|
|
852
|
+
onClose: () => setToastReply(null)
|
|
853
|
+
}
|
|
854
|
+
) : null
|
|
855
|
+
] })
|
|
414
856
|
);
|
|
415
857
|
}
|
|
416
|
-
function
|
|
858
|
+
function composeSupportMessage(input) {
|
|
859
|
+
const ctx = [`Page: ${input.pageTitle || "Unknown"}`, `URL: ${input.url}`];
|
|
860
|
+
if (input.orgName) ctx.push(`Org: ${input.orgName}`);
|
|
861
|
+
const reply = [input.replyEmail?.trim(), input.replyPhone?.trim()].filter(Boolean).join(" / ");
|
|
862
|
+
if (reply) ctx.push(`Reply-to: ${reply}`);
|
|
863
|
+
return `${input.note.trim()}
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
${ctx.join("\n")}`;
|
|
867
|
+
}
|
|
868
|
+
function SupportAction({ orgId } = {}) {
|
|
417
869
|
const { fetchJson } = useNavChrome("SupportAction");
|
|
870
|
+
const { me, loaded } = useChromeMe();
|
|
871
|
+
const { orgs } = useChromeOrgs();
|
|
418
872
|
const [open, setOpen] = useState(false);
|
|
419
873
|
const [message, setMessage] = useState("");
|
|
874
|
+
const [email, setEmail] = useState("");
|
|
875
|
+
const [phone, setPhone] = useState("");
|
|
876
|
+
const [showEmailError, setShowEmailError] = useState(false);
|
|
420
877
|
const [state, setState] = useState("idle");
|
|
878
|
+
const meEmail = me?.email ?? null;
|
|
879
|
+
const mePhone = me?.phone ?? null;
|
|
880
|
+
const missingEmail = loaded && !meEmail;
|
|
881
|
+
const missingPhone = loaded && !mePhone;
|
|
882
|
+
const activeOrg = (orgId ? orgs?.find((o) => o.id === orgId) : null) ?? (orgs && orgs.length === 1 ? orgs[0] : null);
|
|
883
|
+
const orgName = activeOrg?.name ?? null;
|
|
421
884
|
const close = useCallback(() => {
|
|
422
885
|
setOpen(false);
|
|
423
886
|
setState("idle");
|
|
424
887
|
setMessage("");
|
|
888
|
+
setShowEmailError(false);
|
|
425
889
|
}, []);
|
|
890
|
+
useEffect(() => {
|
|
891
|
+
if (!open) return;
|
|
892
|
+
setEmail(meEmail ?? "");
|
|
893
|
+
setPhone(mePhone ?? "");
|
|
894
|
+
setShowEmailError(false);
|
|
895
|
+
}, [open, meEmail, mePhone]);
|
|
426
896
|
useEffect(() => {
|
|
427
897
|
if (!open) return;
|
|
428
898
|
const onKey = (e) => {
|
|
@@ -433,12 +903,28 @@ function SupportAction() {
|
|
|
433
903
|
}, [open, close]);
|
|
434
904
|
const submit = async () => {
|
|
435
905
|
if (!message.trim() || state === "sending") return;
|
|
906
|
+
const resolvedEmail = (missingEmail ? email : meEmail ?? "").trim();
|
|
907
|
+
const resolvedPhone = (missingPhone ? phone : mePhone ?? "").trim();
|
|
908
|
+
if (missingEmail && !resolvedEmail) {
|
|
909
|
+
setShowEmailError(true);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
setShowEmailError(false);
|
|
913
|
+
const collectedContact = missingEmail || missingPhone;
|
|
914
|
+
const fullMessage = composeSupportMessage({
|
|
915
|
+
note: message,
|
|
916
|
+
pageTitle: typeof document !== "undefined" ? document.title : "",
|
|
917
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
918
|
+
orgName,
|
|
919
|
+
replyEmail: collectedContact ? resolvedEmail : void 0,
|
|
920
|
+
replyPhone: collectedContact ? resolvedPhone : void 0
|
|
921
|
+
});
|
|
436
922
|
setState("sending");
|
|
437
923
|
try {
|
|
438
924
|
await fetchJson("support", {
|
|
439
925
|
method: "POST",
|
|
440
926
|
headers: { "Content-Type": "application/json" },
|
|
441
|
-
body: JSON.stringify({ message:
|
|
927
|
+
body: JSON.stringify({ message: fullMessage })
|
|
442
928
|
});
|
|
443
929
|
setState("sent");
|
|
444
930
|
} catch {
|
|
@@ -491,6 +977,33 @@ function SupportAction() {
|
|
|
491
977
|
) })
|
|
492
978
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
493
979
|
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-[color:var(--muted)]", children: "Describe the problem or question. Your account details ride along automatically." }),
|
|
980
|
+
missingEmail ? /* @__PURE__ */ jsxs("label", { className: "mt-4 block", children: [
|
|
981
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1 block text-xs font-semibold text-[color:var(--foreground)]", children: "Your email" }),
|
|
982
|
+
/* @__PURE__ */ jsx(
|
|
983
|
+
"input",
|
|
984
|
+
{
|
|
985
|
+
type: "email",
|
|
986
|
+
value: email,
|
|
987
|
+
onChange: (e) => setEmail(e.target.value),
|
|
988
|
+
placeholder: "you@example.com",
|
|
989
|
+
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)]"
|
|
990
|
+
}
|
|
991
|
+
),
|
|
992
|
+
showEmailError ? /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-[color:var(--color-race-red)]", children: "Enter an email so support can reply." }) : null
|
|
993
|
+
] }) : null,
|
|
994
|
+
missingPhone ? /* @__PURE__ */ jsxs("label", { className: "mt-3 block", children: [
|
|
995
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1 block text-xs font-semibold text-[color:var(--foreground)]", children: "Your phone \u2014 optional" }),
|
|
996
|
+
/* @__PURE__ */ jsx(
|
|
997
|
+
"input",
|
|
998
|
+
{
|
|
999
|
+
type: "tel",
|
|
1000
|
+
value: phone,
|
|
1001
|
+
onChange: (e) => setPhone(e.target.value),
|
|
1002
|
+
placeholder: "(503) 555-1212",
|
|
1003
|
+
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)]"
|
|
1004
|
+
}
|
|
1005
|
+
)
|
|
1006
|
+
] }) : null,
|
|
494
1007
|
/* @__PURE__ */ jsx(
|
|
495
1008
|
"textarea",
|
|
496
1009
|
{
|
|
@@ -679,10 +1192,10 @@ function useThemePreference() {
|
|
|
679
1192
|
|
|
680
1193
|
// src/native/standardNav.ts
|
|
681
1194
|
var CAMPAIGN_FEATURE_AVAILABILITY = {
|
|
682
|
-
a2p:
|
|
683
|
-
phonebank:
|
|
684
|
-
canvass:
|
|
685
|
-
signs:
|
|
1195
|
+
a2p: false,
|
|
1196
|
+
phonebank: false,
|
|
1197
|
+
canvass: false,
|
|
1198
|
+
signs: false,
|
|
686
1199
|
tribes: true,
|
|
687
1200
|
polling: true
|
|
688
1201
|
};
|
|
@@ -1899,6 +2412,6 @@ function NavAppShell({
|
|
|
1899
2412
|
] });
|
|
1900
2413
|
}
|
|
1901
2414
|
|
|
1902
|
-
export { CAMPAIGN_FEATURE_AVAILABILITY, FuelAction, NavAppShell, NavBetaBanner, NavChromeProvider, NavDisplayAction, NavSidebar, NavTabStrip, NavThemeControl, NavTopBar, NotificationsAction, SupportAction, TopBarCountBadge, TopBarPanelArrow, TopBarPopover, TopBarTooltip, TopBarWidgetSlot, envChipClasses, isNavItemActive, standardPolitogyNav, topBarIconButtonClass, useChromeMe, useChromeOrgs, useNavChromeFetcher, useThemePreference };
|
|
1903
|
-
//# sourceMappingURL=chunk-
|
|
1904
|
-
//# sourceMappingURL=chunk-
|
|
2415
|
+
export { CAMPAIGN_FEATURE_AVAILABILITY, CORE_APP_ORIGIN, CORE_BILLING_PATH, FuelAction, NavAppShell, NavBetaBanner, NavChromeProvider, NavDisplayAction, NavSidebar, NavTabStrip, NavThemeControl, NavTopBar, NotificationsAction, SupportAction, TopBarCountBadge, TopBarPanelArrow, TopBarPopover, TopBarTooltip, TopBarWidgetSlot, envChipClasses, isNavItemActive, standardPolitogyNav, topBarIconButtonClass, useChromeMe, useChromeOrgs, useNavChromeFetcher, useThemePreference };
|
|
2416
|
+
//# sourceMappingURL=chunk-CVBEBCID.js.map
|
|
2417
|
+
//# sourceMappingURL=chunk-CVBEBCID.js.map
|