@the-portland-company/shell 0.2.1 → 0.3.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/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
- import { extendTheme, Tooltip, ChakraProvider, Menu, MenuButton, Box, HStack, Text, Portal, MenuList, MenuItem, Flex, MenuDivider, useDisclosure, useToast, IconButton, Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, VStack, Textarea, ModalFooter, Button, useColorMode, Avatar, Heading, Spacer, Link as Link$1, Image } from '@chakra-ui/react';
1
+ import { extendTheme, Tooltip, ChakraProvider, useColorModeValue, Flex, Menu, MenuButton, HStack, Avatar, VStack, Text, Box, Portal, MenuList, MenuItem, MenuDivider, Badge, IconButton, Link as Link$1, Collapse, Skeleton, Button, useClipboard, Icon, Image, useDisclosure, useToast, Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, Textarea, ModalFooter } from '@chakra-ui/react';
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import clsx2 from 'clsx';
4
- import { Link, useLocation } from 'react-router-dom';
4
+ import { Link, useLocation, useNavigate, NavLink } from 'react-router-dom';
5
5
  import { createContext, useEffect, useState, useMemo, useContext, useCallback } from 'react';
6
+ import { FiChevronDown, FiUser, FiRotateCcw, FiBriefcase, FiTrash2, FiUserPlus, FiLogOut, FiMenu, FiX } from 'react-icons/fi';
7
+ import { keyframes } from '@emotion/react';
6
8
 
7
9
  // src/theme/index.ts
8
10
 
@@ -1118,6 +1120,11 @@ var ShellMetaContext = createContext(null);
1118
1120
  var ShellOrgContext = createContext(null);
1119
1121
  var ShellLinkedAccountsContext = createContext(null);
1120
1122
  var ShellSupportProviderContext = createContext(null);
1123
+ var ShellModeContext = createContext(null);
1124
+ var ShellNavItemsContext = createContext(null);
1125
+ var ShellNavigationPreferenceContext = createContext(null);
1126
+ var ShellRoleContext = createContext(null);
1127
+ var ShellFooterContext = createContext(null);
1121
1128
  var ShellChromeContext = createContext(null);
1122
1129
  function toShellUser(session) {
1123
1130
  if (!session?.user) return null;
@@ -1186,7 +1193,7 @@ function ShellProvider({
1186
1193
  }),
1187
1194
  [devNotesMenu, myProfileHref]
1188
1195
  );
1189
- return /* @__PURE__ */ jsx(ShellAuthContext.Provider, { value, children: /* @__PURE__ */ jsx(ShellMetaContext.Provider, { value: meta, children: /* @__PURE__ */ jsx(ShellOrgContext.Provider, { value: organization ?? null, children: /* @__PURE__ */ jsx(ShellLinkedAccountsContext.Provider, { value: linkedAccounts ?? null, children: /* @__PURE__ */ jsx(ShellSupportProviderContext.Provider, { value: support ?? null, children: /* @__PURE__ */ jsx(ShellChromeContext.Provider, { value: chrome, children: /* @__PURE__ */ jsx(ChakraProvider, { theme: politogyTheme, children }) }) }) }) }) }) });
1196
+ return /* @__PURE__ */ jsx(ShellAuthContext.Provider, { value, children: /* @__PURE__ */ jsx(ShellMetaContext.Provider, { value: meta, children: /* @__PURE__ */ jsx(ShellOrgContext.Provider, { value: organization ?? null, children: /* @__PURE__ */ jsx(ShellLinkedAccountsContext.Provider, { value: linkedAccounts ?? null, children: /* @__PURE__ */ jsx(ShellSupportProviderContext.Provider, { value: support ?? null, children: /* @__PURE__ */ jsx(ShellModeContext.Provider, { value: null, children: /* @__PURE__ */ jsx(ShellNavItemsContext.Provider, { value: null, children: /* @__PURE__ */ jsx(ShellNavigationPreferenceContext.Provider, { value: null, children: /* @__PURE__ */ jsx(ShellRoleContext.Provider, { value: null, children: /* @__PURE__ */ jsx(ShellFooterContext.Provider, { value: null, children: /* @__PURE__ */ jsx(ShellChromeContext.Provider, { value: chrome, children: /* @__PURE__ */ jsx(ChakraProvider, { theme: politogyTheme, children }) }) }) }) }) }) }) }) }) }) }) });
1190
1197
  }
1191
1198
  function useShellAuth() {
1192
1199
  const ctx = useContext(ShellAuthContext);
@@ -1225,6 +1232,612 @@ function useShellChrome() {
1225
1232
  }
1226
1233
  return ctx;
1227
1234
  }
1235
+ function useShellMode() {
1236
+ return useContext(ShellModeContext);
1237
+ }
1238
+ function useShellNavItems() {
1239
+ return useContext(ShellNavItemsContext);
1240
+ }
1241
+ function useShellNavigationPreference() {
1242
+ return useContext(ShellNavigationPreferenceContext);
1243
+ }
1244
+ function useShellRole() {
1245
+ return useContext(ShellRoleContext);
1246
+ }
1247
+ function useShellFooter() {
1248
+ return useContext(ShellFooterContext);
1249
+ }
1250
+ function BrandLogo({ src, alt = "Brand", className, style }) {
1251
+ if (!src) return null;
1252
+ return /* @__PURE__ */ jsx(
1253
+ "img",
1254
+ {
1255
+ src,
1256
+ alt,
1257
+ className,
1258
+ style,
1259
+ draggable: false
1260
+ }
1261
+ );
1262
+ }
1263
+ var BrandLogo_default = BrandLogo;
1264
+
1265
+ // src/lib/userDisplay.ts
1266
+ function getUserDisplayName(user) {
1267
+ if (!user) return "User";
1268
+ return user.profile?.full_name || user.profile?.name || user.user_metadata?.full_name || user.user_metadata?.name || (typeof user.email === "string" ? user.email.split("@")[0] : "") || user.email || "User";
1269
+ }
1270
+ function avatarUrlFromUser(user) {
1271
+ if (!user) return void 0;
1272
+ const meta = user.raw.user_metadata;
1273
+ return meta?.avatar_url ?? void 0;
1274
+ }
1275
+ function UserMenu() {
1276
+ const { user, status, signOut } = useShellAuth();
1277
+ const {
1278
+ myProfileHref,
1279
+ organizationSettingsHref,
1280
+ showContinueOnboarding,
1281
+ onContinueOnboarding
1282
+ } = useShellChrome();
1283
+ const linked = useShellLinkedAccounts();
1284
+ const mode = useShellMode();
1285
+ const org = useShellOrganization();
1286
+ const role = useShellRole();
1287
+ const navigate = useNavigate();
1288
+ const menuListBg = useColorModeValue("white", "gray.900");
1289
+ const menuListBorderColor = useColorModeValue("gray.200", "gray.700");
1290
+ if (status !== "authenticated" || !user) return null;
1291
+ const displayName = getUserDisplayName(user.raw);
1292
+ const avatarUrl = avatarUrlFromUser(user);
1293
+ const modeConfig = mode?.getModeConfig() ?? null;
1294
+ const currentOrg = org?.currentOrg ?? null;
1295
+ const linkedAccounts = linked?.linkedAccounts ?? [];
1296
+ const activeRoleConfig = role?.activeRoleConfig ?? null;
1297
+ return /* @__PURE__ */ jsx(Flex, { alignItems: "center", children: /* @__PURE__ */ jsxs(Menu, { children: [
1298
+ /* @__PURE__ */ jsx(MenuButton, { py: 2, transition: "all 0.3s", _focus: { boxShadow: "none" }, children: /* @__PURE__ */ jsxs(HStack, { children: [
1299
+ /* @__PURE__ */ jsx(
1300
+ Avatar,
1301
+ {
1302
+ size: "sm",
1303
+ src: avatarUrl,
1304
+ name: displayName,
1305
+ bg: modeConfig?.primaryColor || "brand.500"
1306
+ }
1307
+ ),
1308
+ /* @__PURE__ */ jsxs(
1309
+ VStack,
1310
+ {
1311
+ display: { base: "none", md: "flex" },
1312
+ alignItems: "flex-start",
1313
+ spacing: "1px",
1314
+ ml: "2",
1315
+ children: [
1316
+ /* @__PURE__ */ jsx(HStack, { spacing: 2, children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", children: displayName }) }),
1317
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "gray.500", children: user.email })
1318
+ ]
1319
+ }
1320
+ ),
1321
+ /* @__PURE__ */ jsx(Box, { display: { base: "none", md: "flex" }, children: /* @__PURE__ */ jsx(FiChevronDown, {}) })
1322
+ ] }) }),
1323
+ /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(MenuList, { bg: menuListBg, borderColor: menuListBorderColor, zIndex: 1500, children: [
1324
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(FiUser, {}), onClick: () => navigate(myProfileHref), children: "My Profile" }),
1325
+ showContinueOnboarding && /* @__PURE__ */ jsx(
1326
+ MenuItem,
1327
+ {
1328
+ icon: /* @__PURE__ */ jsx(FiRotateCcw, {}),
1329
+ onClick: () => {
1330
+ if (onContinueOnboarding) onContinueOnboarding();
1331
+ else window.dispatchEvent(new CustomEvent("app:continue-onboarding"));
1332
+ },
1333
+ children: "Continue Onboarding"
1334
+ }
1335
+ ),
1336
+ organizationSettingsHref && /* @__PURE__ */ jsx(
1337
+ MenuItem,
1338
+ {
1339
+ icon: /* @__PURE__ */ jsx(FiBriefcase, {}),
1340
+ onClick: () => navigate(organizationSettingsHref),
1341
+ children: "Organization Settings"
1342
+ }
1343
+ ),
1344
+ /* @__PURE__ */ jsx(MenuDivider, {}),
1345
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "gray.500", px: 3, py: 1, fontWeight: "semibold", children: "ROLES & ORGANIZATIONS" }),
1346
+ /* @__PURE__ */ jsx(Box, { px: 3, py: 2, children: /* @__PURE__ */ jsxs(VStack, { align: "stretch", spacing: 1, children: [
1347
+ currentOrg && currentOrg.role && /* @__PURE__ */ jsxs(HStack, { children: [
1348
+ /* @__PURE__ */ jsx(
1349
+ Badge,
1350
+ {
1351
+ size: "sm",
1352
+ colorScheme: currentOrg.role === "owner" ? "purple" : currentOrg.role === "admin" ? "blue" : "gray",
1353
+ children: currentOrg.role.charAt(0).toUpperCase() + currentOrg.role.slice(1)
1354
+ }
1355
+ ),
1356
+ /* @__PURE__ */ jsxs(Text, { fontSize: "xs", color: "gray.500", children: [
1357
+ "in ",
1358
+ currentOrg.name
1359
+ ] })
1360
+ ] }),
1361
+ activeRoleConfig && !currentOrg?.role && /* @__PURE__ */ jsx(Badge, { colorScheme: activeRoleConfig.color, size: "sm", children: activeRoleConfig.name })
1362
+ ] }) }),
1363
+ linked && /* @__PURE__ */ jsxs(Fragment, { children: [
1364
+ /* @__PURE__ */ jsx(MenuDivider, {}),
1365
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "gray.500", px: 3, py: 1, fontWeight: "semibold", children: "ACCOUNTS" }),
1366
+ /* @__PURE__ */ jsx(Box, { px: 3, py: 2, children: /* @__PURE__ */ jsxs(HStack, { spacing: 2, children: [
1367
+ /* @__PURE__ */ jsx(Avatar, { size: "xs", src: avatarUrl, name: displayName }),
1368
+ /* @__PURE__ */ jsxs(VStack, { align: "start", spacing: 0, flex: 1, children: [
1369
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", fontWeight: "medium", color: "brand.600", children: displayName }),
1370
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "gray.400", children: "Active" })
1371
+ ] })
1372
+ ] }) }),
1373
+ linkedAccounts.map((account) => /* @__PURE__ */ jsx(
1374
+ MenuItem,
1375
+ {
1376
+ onClick: () => {
1377
+ void linked.switchToAccount(account.id);
1378
+ },
1379
+ _hover: { bg: "gray.50" },
1380
+ children: /* @__PURE__ */ jsxs(HStack, { spacing: 2, flex: 1, justify: "space-between", children: [
1381
+ /* @__PURE__ */ jsxs(HStack, { spacing: 2, children: [
1382
+ /* @__PURE__ */ jsx(
1383
+ Avatar,
1384
+ {
1385
+ size: "xs",
1386
+ src: account.avatarUrl ?? void 0,
1387
+ name: account.displayName ?? account.email
1388
+ }
1389
+ ),
1390
+ /* @__PURE__ */ jsxs(VStack, { align: "start", spacing: 0, children: [
1391
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", fontWeight: "medium", noOfLines: 1, children: account.displayName ?? account.email.split("@")[0] }),
1392
+ /* @__PURE__ */ jsx(Tooltip, { label: account.email, placement: "top", hasArrow: true, children: /* @__PURE__ */ jsx(
1393
+ Text,
1394
+ {
1395
+ fontSize: "xs",
1396
+ color: "gray.500",
1397
+ maxW: "120px",
1398
+ noOfLines: 1,
1399
+ textOverflow: "ellipsis",
1400
+ children: account.email
1401
+ }
1402
+ ) })
1403
+ ] })
1404
+ ] }),
1405
+ /* @__PURE__ */ jsx(
1406
+ IconButton,
1407
+ {
1408
+ as: "span",
1409
+ role: "button",
1410
+ tabIndex: 0,
1411
+ "aria-label": "Remove account",
1412
+ icon: /* @__PURE__ */ jsx(FiTrash2, {}),
1413
+ size: "xs",
1414
+ variant: "ghost",
1415
+ colorScheme: "red",
1416
+ onClick: (e) => {
1417
+ e.stopPropagation();
1418
+ linked.onRemoveAccount(account.id);
1419
+ }
1420
+ }
1421
+ )
1422
+ ] })
1423
+ },
1424
+ account.id
1425
+ )),
1426
+ /* @__PURE__ */ jsx(
1427
+ MenuItem,
1428
+ {
1429
+ icon: /* @__PURE__ */ jsx(FiUserPlus, {}),
1430
+ onClick: linked.onAddAccount,
1431
+ color: "brand.500",
1432
+ children: "Add Account"
1433
+ }
1434
+ )
1435
+ ] }),
1436
+ /* @__PURE__ */ jsx(MenuDivider, {}),
1437
+ /* @__PURE__ */ jsx(MenuItem, { icon: /* @__PURE__ */ jsx(FiLogOut, {}), onClick: () => void signOut(), children: "Sign out" })
1438
+ ] }) })
1439
+ ] }) });
1440
+ }
1441
+ function Header({ onOpen, id }) {
1442
+ const {
1443
+ pageTitle,
1444
+ breadcrumbs,
1445
+ headerActions,
1446
+ headerIconsSlot,
1447
+ brandLogoSrc
1448
+ } = useShellChrome();
1449
+ const mobileNavBg = useColorModeValue("white", "gray.900");
1450
+ const mobileNavBorderColor = useColorModeValue("gray.200", "gray.700");
1451
+ return /* @__PURE__ */ jsx(
1452
+ Flex,
1453
+ {
1454
+ id: id || "mobile-nav-container",
1455
+ ml: { base: 0, md: 60 },
1456
+ px: { base: 4, md: 4 },
1457
+ height: "20",
1458
+ alignItems: "center",
1459
+ bg: mobileNavBg,
1460
+ borderBottomWidth: "1px",
1461
+ borderBottomColor: mobileNavBorderColor,
1462
+ justifyContent: { base: "space-between", md: "space-between" },
1463
+ children: /* @__PURE__ */ jsxs(Flex, { w: "full", mx: "auto", align: "center", justify: "space-between", children: [
1464
+ /* @__PURE__ */ jsxs(HStack, { spacing: 4, minW: "0", children: [
1465
+ onOpen ? /* @__PURE__ */ jsx(
1466
+ IconButton,
1467
+ {
1468
+ display: { base: "flex", md: "none" },
1469
+ onClick: onOpen,
1470
+ variant: "outline",
1471
+ "aria-label": "open menu",
1472
+ icon: /* @__PURE__ */ jsx(FiMenu, {})
1473
+ }
1474
+ ) : null,
1475
+ brandLogoSrc ? /* @__PURE__ */ jsx(Box, { display: { base: "flex", md: "none" }, h: "32px", children: /* @__PURE__ */ jsx(BrandLogo_default, { src: brandLogoSrc, className: "h-full w-auto object-contain" }) }) : null,
1476
+ /* @__PURE__ */ jsxs(
1477
+ VStack,
1478
+ {
1479
+ align: "flex-start",
1480
+ spacing: 0,
1481
+ minW: "0",
1482
+ display: { base: "none", md: "flex" },
1483
+ children: [
1484
+ pageTitle ? /* @__PURE__ */ jsx(Text, { fontSize: "lg", fontWeight: "bold", noOfLines: 1, children: pageTitle }) : null,
1485
+ breadcrumbs && breadcrumbs.length > 0 ? /* @__PURE__ */ jsx(HStack, { spacing: 2, color: "gray.500", fontSize: "sm", mt: "1px", minW: "0", children: breadcrumbs.map((b, idx) => /* @__PURE__ */ jsxs(Box, { display: "inline-flex", alignItems: "center", minW: "0", children: [
1486
+ idx > 0 ? /* @__PURE__ */ jsx(Text, { color: "gray.400", mr: 2, children: " > " }) : null,
1487
+ b.href ? /* @__PURE__ */ jsx(
1488
+ Link$1,
1489
+ {
1490
+ as: NavLink,
1491
+ to: b.href,
1492
+ _hover: { textDecoration: "underline" },
1493
+ noOfLines: 1,
1494
+ children: b.label
1495
+ }
1496
+ ) : /* @__PURE__ */ jsx(Text, { noOfLines: 1, children: b.label })
1497
+ ] }, `${b.label}-${idx}`)) }) : null
1498
+ ]
1499
+ }
1500
+ )
1501
+ ] }),
1502
+ /* @__PURE__ */ jsxs(HStack, { spacing: 0, align: "center", children: [
1503
+ /* @__PURE__ */ jsxs(
1504
+ HStack,
1505
+ {
1506
+ spacing: 2,
1507
+ mr: "15px",
1508
+ color: "gray.600",
1509
+ sx: {
1510
+ '& button:not([role="menuitem"]):not([data-menu-item])': {
1511
+ width: "32px",
1512
+ height: "32px",
1513
+ minWidth: "32px",
1514
+ padding: 0
1515
+ },
1516
+ '& button:not([role="menuitem"]):not([data-menu-item]) svg': {
1517
+ width: "20px",
1518
+ height: "20px"
1519
+ }
1520
+ },
1521
+ children: [
1522
+ headerActions,
1523
+ headerIconsSlot
1524
+ ]
1525
+ }
1526
+ ),
1527
+ /* @__PURE__ */ jsx(UserMenu, {})
1528
+ ] })
1529
+ ] })
1530
+ }
1531
+ );
1532
+ }
1533
+ function BrandIcon({
1534
+ name,
1535
+ size,
1536
+ className,
1537
+ style,
1538
+ alt,
1539
+ basePath = "/brand/icons/"
1540
+ }) {
1541
+ const a11yLabel = alt ?? "";
1542
+ const isDecorative = a11yLabel === "";
1543
+ const inlineSize = size ? { width: size, height: size, ...style } : style;
1544
+ return /* @__PURE__ */ jsx(
1545
+ "img",
1546
+ {
1547
+ src: `${basePath}${name}.svg`,
1548
+ alt: a11yLabel,
1549
+ role: isDecorative ? "presentation" : void 0,
1550
+ "aria-hidden": isDecorative ? true : void 0,
1551
+ className,
1552
+ style: inlineSize,
1553
+ draggable: false
1554
+ }
1555
+ );
1556
+ }
1557
+ var BrandIcon_default = BrandIcon;
1558
+ var MODE_CAMPAIGN = "campaign";
1559
+ var MODE_PETITION = "petition";
1560
+ function NavItem({
1561
+ icon,
1562
+ brandIcon,
1563
+ children,
1564
+ href,
1565
+ action,
1566
+ subItems,
1567
+ badge,
1568
+ subLabel,
1569
+ isSubItem = false,
1570
+ isExpanded,
1571
+ onToggle,
1572
+ menuKey,
1573
+ isDisabled: navIsDisabled,
1574
+ disabled,
1575
+ ...rest
1576
+ }) {
1577
+ const location = useLocation();
1578
+ const navigate = useNavigate();
1579
+ const mode = useShellMode();
1580
+ const currentMode = mode?.currentMode ?? null;
1581
+ const modeConfig = mode?.getModeConfig() ?? null;
1582
+ const navPref = useShellNavigationPreference();
1583
+ const showSubMenu = navPref?.showSubMenu ?? true;
1584
+ const navHoverGradient = currentMode === MODE_CAMPAIGN ? "linear-gradient(90deg, #F2C94C 0%, #E63946 100%)" : currentMode === MODE_PETITION ? "linear-gradient(90deg, #F2C94C 0%, #007BFF 100%)" : "linear-gradient(90deg, #007BFF 0%, #E63946 100%)";
1585
+ const gradientSweepBefore = {
1586
+ content: '""',
1587
+ position: "absolute",
1588
+ top: 0,
1589
+ left: 0,
1590
+ bottom: 0,
1591
+ width: "0%",
1592
+ background: navHoverGradient,
1593
+ transition: "width 300ms ease",
1594
+ pointerEvents: "none",
1595
+ zIndex: 0
1596
+ };
1597
+ const isDisabled = Boolean(navIsDisabled || disabled);
1598
+ const effectiveHref = href || (Array.isArray(subItems) && subItems.length > 0 ? subItems[0].href : void 0);
1599
+ const effectivePathname = effectiveHref?.split("?")[0];
1600
+ const currentFullPath = location.pathname + location.search;
1601
+ const currentSearchParams = useMemo(
1602
+ () => new URLSearchParams(location.search),
1603
+ [location.search]
1604
+ );
1605
+ const matchesSubItem = (itemHref) => {
1606
+ if (itemHref === currentFullPath) return true;
1607
+ const itemPath = itemHref.split("?")[0];
1608
+ const itemSearch = itemHref.includes("?") ? itemHref.split("?")[1] : "";
1609
+ if (itemPath === location.pathname && !itemSearch) {
1610
+ return true;
1611
+ }
1612
+ if (location.pathname.startsWith(`${itemPath}/`)) {
1613
+ const itemSegments = itemPath.split("/").filter(Boolean);
1614
+ const locationSegments = location.pathname.split("/").filter(Boolean);
1615
+ const itemLastSegment = itemSegments[itemSegments.length - 1];
1616
+ const locationLastSegment = locationSegments[locationSegments.length - 1];
1617
+ if (itemLastSegment && itemLastSegment === locationLastSegment) {
1618
+ return true;
1619
+ }
1620
+ }
1621
+ if (itemPath === location.pathname && itemSearch) {
1622
+ const itemSearchParams = new URLSearchParams(itemSearch);
1623
+ const itemTab = itemSearchParams.get("tab") || itemSearchParams.get("view");
1624
+ if (!itemTab) return true;
1625
+ return itemTab === currentSearchParams.get("tab");
1626
+ }
1627
+ if (itemPath && !itemHref.includes("?") && itemPath !== effectivePathname) {
1628
+ const segment = itemPath.split("/").pop();
1629
+ if (segment && location.pathname.endsWith("/" + segment) && location.pathname.startsWith(itemPath.split("/").slice(0, -1).join("/"))) {
1630
+ return true;
1631
+ }
1632
+ }
1633
+ return false;
1634
+ };
1635
+ const isActive = isSubItem ? matchesSubItem(effectiveHref || "") : (() => {
1636
+ if (!effectivePathname) return false;
1637
+ if (location.pathname === effectivePathname) return true;
1638
+ if (location.pathname.startsWith(`${effectivePathname}/`)) return true;
1639
+ const dashboardOverviewMatch = effectivePathname.match(
1640
+ /^\/[^/]+\/[^/]*\/dashboard\/overview$/
1641
+ );
1642
+ if (dashboardOverviewMatch) {
1643
+ const dashboardRoot = effectivePathname.replace(/\/overview$/, "");
1644
+ return location.pathname === dashboardRoot || location.pathname.startsWith(`${dashboardRoot}/`) || location.pathname.startsWith(`${dashboardRoot}&`);
1645
+ }
1646
+ return effectivePathname !== `/${location.pathname.split("/")[1]}/dashboard` && location.pathname.startsWith(effectivePathname + "/");
1647
+ })();
1648
+ const hasSubItems = showSubMenu && Array.isArray(subItems) && subItems.length > 0;
1649
+ const hasActiveChild = hasSubItems && subItems.some((item) => matchesSubItem(item.href || ""));
1650
+ const isParentActive = isActive || hasActiveChild;
1651
+ const isSubMenuOpen = isExpanded !== void 0 ? isExpanded : hasSubItems && subItems.some((item) => matchesSubItem(item.href || ""));
1652
+ const expandedRowBg = isParentActive ? modeConfig?.primaryColor || "brand.500" : isSubMenuOpen ? "gray.100" : "transparent";
1653
+ const expandedRowTextColor = isParentActive ? "white" : isSubMenuOpen ? "gray.900" : "gray.600";
1654
+ const disabledRowBg = isSubItem ? "gray.50" : "transparent";
1655
+ const disabledRowTextColor = "gray.400";
1656
+ if (hasSubItems) {
1657
+ const headerProps = {
1658
+ align: "center",
1659
+ px: "4",
1660
+ py: "2.5",
1661
+ mx: "4",
1662
+ w: "auto",
1663
+ borderTopRadius: "lg",
1664
+ borderBottomRadius: isSubMenuOpen ? 0 : "lg",
1665
+ role: "group",
1666
+ cursor: isDisabled ? "not-allowed" : "pointer",
1667
+ opacity: 1,
1668
+ pointerEvents: isDisabled ? "none" : "auto",
1669
+ "data-nav-item-active": String(isParentActive),
1670
+ position: "relative",
1671
+ overflow: "hidden",
1672
+ bg: isDisabled ? disabledRowBg : expandedRowBg,
1673
+ color: isDisabled ? disabledRowTextColor : expandedRowTextColor,
1674
+ _before: isDisabled ? void 0 : gradientSweepBefore,
1675
+ _hover: {
1676
+ color: isDisabled ? disabledRowTextColor : "white",
1677
+ _before: isDisabled ? void 0 : { width: "100%" }
1678
+ },
1679
+ "aria-disabled": isDisabled ? "true" : "false",
1680
+ onClick: (e) => {
1681
+ if (isDisabled) return;
1682
+ if (effectiveHref) e.preventDefault();
1683
+ e.stopPropagation();
1684
+ onToggle?.(menuKey || null);
1685
+ }
1686
+ };
1687
+ return /* @__PURE__ */ jsxs(Box, { children: [
1688
+ /* @__PURE__ */ jsxs(Flex, { ...headerProps, ...rest, children: [
1689
+ brandIcon ? /* @__PURE__ */ jsx(
1690
+ Box,
1691
+ {
1692
+ mr: "4",
1693
+ display: "inline-flex",
1694
+ alignItems: "center",
1695
+ position: "relative",
1696
+ zIndex: 1,
1697
+ filter: isParentActive ? "brightness(0) invert(1)" : void 0,
1698
+ _groupHover: isDisabled ? void 0 : { filter: "brightness(0) invert(1)" },
1699
+ children: /* @__PURE__ */ jsx(BrandIcon_default, { name: brandIcon, size: 18 })
1700
+ }
1701
+ ) : icon ? /* @__PURE__ */ jsx(
1702
+ Box,
1703
+ {
1704
+ mr: "4",
1705
+ fontSize: "16",
1706
+ position: "relative",
1707
+ zIndex: 1,
1708
+ _groupHover: {
1709
+ color: isDisabled ? disabledRowTextColor : "white"
1710
+ },
1711
+ as: icon
1712
+ }
1713
+ ) : null,
1714
+ /* @__PURE__ */ jsx(Box, { flex: "1", position: "relative", zIndex: 1, children }),
1715
+ /* @__PURE__ */ jsx(
1716
+ Box,
1717
+ {
1718
+ as: FiChevronDown,
1719
+ position: "relative",
1720
+ zIndex: 1,
1721
+ transform: isSubMenuOpen ? "rotate(180deg)" : void 0,
1722
+ transition: "transform 0.2s"
1723
+ }
1724
+ )
1725
+ ] }),
1726
+ /* @__PURE__ */ jsx(Collapse, { in: isSubMenuOpen, animateOpacity: true, children: /* @__PURE__ */ jsx(
1727
+ Box,
1728
+ {
1729
+ px: "0",
1730
+ mx: "4",
1731
+ w: "auto",
1732
+ mb: "2",
1733
+ py: "1",
1734
+ borderTopRadius: 0,
1735
+ borderBottomRadius: "md",
1736
+ bg: isDisabled ? "gray.100" : "gray.200",
1737
+ children: subItems.map((item) => /* @__PURE__ */ jsx(
1738
+ NavItem,
1739
+ {
1740
+ icon: item.icon,
1741
+ brandIcon: item.brandIcon,
1742
+ href: item.href,
1743
+ isSubItem: true,
1744
+ isDisabled: item.disabled,
1745
+ subLabel: item.subLabel,
1746
+ children: item.name
1747
+ },
1748
+ item.name
1749
+ ))
1750
+ }
1751
+ ) })
1752
+ ] });
1753
+ }
1754
+ const handleClick = (e) => {
1755
+ if (isDisabled) {
1756
+ e.preventDefault();
1757
+ return;
1758
+ }
1759
+ if (action) {
1760
+ e.preventDefault();
1761
+ action();
1762
+ return;
1763
+ }
1764
+ if (effectiveHref) {
1765
+ e.preventDefault();
1766
+ navigate(effectiveHref);
1767
+ }
1768
+ };
1769
+ return /* @__PURE__ */ jsx(Box, { as: "a", href: effectiveHref || "#", onClick: handleClick, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsxs(
1770
+ Flex,
1771
+ {
1772
+ align: "center",
1773
+ px: "4",
1774
+ py: "2.5",
1775
+ mx: isSubItem ? "0" : "4",
1776
+ w: isSubItem ? "full" : "auto",
1777
+ borderRadius: isSubItem ? "0" : "lg",
1778
+ role: "group",
1779
+ cursor: isDisabled ? "not-allowed" : "pointer",
1780
+ opacity: 1,
1781
+ pointerEvents: isDisabled ? "none" : "auto",
1782
+ "data-nav-item-active": String(isActive),
1783
+ position: "relative",
1784
+ overflow: "hidden",
1785
+ bg: isDisabled ? disabledRowBg : isActive ? isSubItem ? "gray.100" : modeConfig?.primaryColor || "brand.500" : "transparent",
1786
+ color: isDisabled ? disabledRowTextColor : isActive ? isSubItem ? modeConfig?.primaryColor || "brand.500" : "white" : "gray.600",
1787
+ _before: isDisabled ? void 0 : gradientSweepBefore,
1788
+ _hover: {
1789
+ color: isDisabled ? disabledRowTextColor : "white",
1790
+ _before: isDisabled ? void 0 : { width: "100%" }
1791
+ },
1792
+ "aria-disabled": isDisabled ? "true" : "false",
1793
+ onClick: handleClick,
1794
+ children: [
1795
+ brandIcon ? /* @__PURE__ */ jsx(
1796
+ Box,
1797
+ {
1798
+ mr: "4",
1799
+ display: "inline-flex",
1800
+ alignItems: "center",
1801
+ position: "relative",
1802
+ zIndex: 1,
1803
+ filter: isActive && !isSubItem ? "brightness(0) invert(1)" : void 0,
1804
+ _groupHover: isDisabled ? void 0 : { filter: "brightness(0) invert(1)" },
1805
+ children: /* @__PURE__ */ jsx(BrandIcon_default, { name: brandIcon, size: 18 })
1806
+ }
1807
+ ) : icon ? /* @__PURE__ */ jsx(
1808
+ Box,
1809
+ {
1810
+ mr: "4",
1811
+ fontSize: "16",
1812
+ position: "relative",
1813
+ zIndex: 1,
1814
+ _groupHover: {
1815
+ color: isDisabled ? disabledRowTextColor : "white"
1816
+ },
1817
+ as: icon
1818
+ }
1819
+ ) : null,
1820
+ /* @__PURE__ */ jsxs(Box, { flex: "1", position: "relative", zIndex: 1, children: [
1821
+ /* @__PURE__ */ jsx(Box, { children }),
1822
+ subLabel ? /* @__PURE__ */ jsx(
1823
+ Text,
1824
+ {
1825
+ mt: 0.5,
1826
+ fontSize: "10px",
1827
+ lineHeight: "1.3",
1828
+ color: isDisabled ? "gray.400" : "gray.400",
1829
+ children: subLabel
1830
+ }
1831
+ ) : null,
1832
+ badge && /* @__PURE__ */ jsx(Badge, { colorScheme: "orange", size: "xs", mt: 1, fontSize: "xs", display: "block", children: badge })
1833
+ ] })
1834
+ ]
1835
+ }
1836
+ ) });
1837
+ }
1838
+ var NavItem_default = NavItem;
1839
+
1840
+ // src/lib/orgColors.ts
1228
1841
  var HEX_COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
1229
1842
  function normalizeHexColor(value) {
1230
1843
  const trimmed = value?.trim();
@@ -1233,6 +1846,34 @@ function normalizeHexColor(value) {
1233
1846
  const [r, g, b] = trimmed.slice(1).split("");
1234
1847
  return `#${r}${r}${g}${g}${b}${b}`;
1235
1848
  }
1849
+ function hexToRgba(hex, alpha) {
1850
+ const normalized = normalizeHexColor(hex);
1851
+ if (!normalized) return null;
1852
+ const red = parseInt(normalized.slice(1, 3), 16);
1853
+ const green = parseInt(normalized.slice(3, 5), 16);
1854
+ const blue = parseInt(normalized.slice(5, 7), 16);
1855
+ return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
1856
+ }
1857
+ function lightenHexColor(hex, amount) {
1858
+ const normalized = normalizeHexColor(hex);
1859
+ if (!normalized) return null;
1860
+ const clamp = (value) => Math.max(0, Math.min(255, Math.round(value)));
1861
+ const blend = (channel) => clamp(channel + (255 - channel) * amount);
1862
+ const red = parseInt(normalized.slice(1, 3), 16);
1863
+ const green = parseInt(normalized.slice(3, 5), 16);
1864
+ const blue = parseInt(normalized.slice(5, 7), 16);
1865
+ return `#${blend(red).toString(16).padStart(2, "0")}${blend(green).toString(16).padStart(2, "0")}${blend(blue).toString(16).padStart(2, "0")}`;
1866
+ }
1867
+ function getOrganizationBrandColor(organization) {
1868
+ if (!organization) return null;
1869
+ const direct = normalizeHexColor(organization.brandColor);
1870
+ if (direct) return direct;
1871
+ const brandColors = Array.isArray(organization.settings?.brand_colors) ? organization.settings?.brand_colors : [];
1872
+ const firstBrandColor = brandColors?.find(
1873
+ (entry) => typeof entry?.value === "string" && normalizeHexColor(entry.value)
1874
+ );
1875
+ return normalizeHexColor(firstBrandColor?.value ?? null);
1876
+ }
1236
1877
  function getOrganizationBadgeLetters(name) {
1237
1878
  const normalizedName = String(name || "").trim().replace(/[^a-zA-Z0-9\s]+/g, " ");
1238
1879
  const parts = normalizedName.split(/\s+/).filter(Boolean);
@@ -1267,60 +1908,698 @@ function OrgBadge({ name, logoUrl, brandColor, size }) {
1267
1908
  }
1268
1909
  );
1269
1910
  }
1270
- function OrgSelector() {
1911
+ function OrganizationIdCopy({ orgId }) {
1912
+ const { hasCopied, onCopy } = useClipboard(orgId);
1913
+ const copiedAnimation = keyframes`
1914
+ 0% { opacity: 0; transform: translateY(6px); }
1915
+ 20% { opacity: 1; transform: translateY(0); }
1916
+ 100% { opacity: 0; transform: translateY(-8px); }
1917
+ `;
1918
+ return /* @__PURE__ */ jsx(Tooltip, { label: orgId, placement: "top", hasArrow: true, children: /* @__PURE__ */ jsxs(
1919
+ Flex,
1920
+ {
1921
+ align: "center",
1922
+ cursor: "pointer",
1923
+ onClick: onCopy,
1924
+ _hover: { bg: "gray.100" },
1925
+ borderRadius: "md",
1926
+ px: 2,
1927
+ py: 1,
1928
+ position: "relative",
1929
+ transition: "all 0.2s",
1930
+ children: [
1931
+ /* @__PURE__ */ jsxs(Text, { fontSize: "xs", color: hasCopied ? "green.500" : "gray.600", fontWeight: "normal", children: [
1932
+ "...",
1933
+ orgId.slice(-8)
1934
+ ] }),
1935
+ hasCopied && /* @__PURE__ */ jsx(
1936
+ Box,
1937
+ {
1938
+ position: "absolute",
1939
+ bottom: "100%",
1940
+ mb: "6px",
1941
+ px: "2",
1942
+ py: "1",
1943
+ fontSize: "xs",
1944
+ bg: "gray.700",
1945
+ color: "white",
1946
+ borderRadius: "md",
1947
+ animation: `${copiedAnimation} 0.9s ease-out`,
1948
+ pointerEvents: "none",
1949
+ children: "Copied!"
1950
+ }
1951
+ )
1952
+ ]
1953
+ }
1954
+ ) });
1955
+ }
1956
+ function OrgPill() {
1271
1957
  const org = useShellOrganization();
1958
+ const { user } = useShellAuth();
1959
+ const currentOrg = org?.currentOrg ?? null;
1960
+ const organizations = org?.organizations ?? [];
1961
+ const switchOrganization = org?.switchOrganization;
1962
+ const loadingOrgs = org?.loadingOrgs ?? false;
1963
+ const unselectedOrganizations = useMemo(
1964
+ () => organizations.filter((o) => o.id !== currentOrg?.id),
1965
+ [organizations, currentOrg?.id]
1966
+ );
1967
+ const selectedOrgBrandColor = useMemo(
1968
+ () => currentOrg ? getOrganizationBrandColor(currentOrg) : null,
1969
+ [currentOrg]
1970
+ );
1971
+ const selectedOrgColor = selectedOrgBrandColor || "var(--mode-primary-color, #ea6a3a)";
1972
+ const selectedOrgMenuBg = selectedOrgBrandColor && lightenHexColor(selectedOrgBrandColor, 0.12) || "#4a4a4a";
1272
1973
  if (!org) return null;
1273
- const { currentOrg, organizations, switchOrganization, createOrgHref } = org;
1274
- const others = organizations.filter((o) => o.id !== currentOrg?.id);
1275
- return /* @__PURE__ */ jsxs(Menu, { placement: "bottom-start", children: [
1276
- /* @__PURE__ */ jsx(
1277
- MenuButton,
1278
- {
1279
- as: Box,
1280
- cursor: "pointer",
1281
- px: 2,
1282
- py: 1,
1283
- borderRadius: "md",
1284
- _hover: { bg: "gray.100" },
1285
- _active: { bg: "gray.200" },
1286
- "aria-label": "Switch organization",
1287
- children: /* @__PURE__ */ jsxs(HStack, { spacing: 2, align: "center", children: [
1288
- currentOrg ? /* @__PURE__ */ jsxs(Fragment, { children: [
1974
+ return /* @__PURE__ */ jsxs(Box, { mx: "4", mb: "3", children: [
1975
+ /* @__PURE__ */ jsxs(HStack, { justify: "space-between", align: "center", mb: "2", children: [
1976
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "gray.500", fontWeight: "semibold", children: "ORGANIZATION" }),
1977
+ currentOrg?.id && currentOrg.id !== user?.id && /* @__PURE__ */ jsx(OrganizationIdCopy, { orgId: currentOrg.id })
1978
+ ] }),
1979
+ loadingOrgs && !currentOrg ? /* @__PURE__ */ jsx(Skeleton, { height: "32px", width: "full", borderRadius: "md" }) : /* @__PURE__ */ jsxs(Menu, { gutter: 0, children: [
1980
+ /* @__PURE__ */ jsx(
1981
+ MenuButton,
1982
+ {
1983
+ as: Button,
1984
+ rightIcon: /* @__PURE__ */ jsx(FiChevronDown, {}),
1985
+ size: "sm",
1986
+ width: "full",
1987
+ bg: currentOrg?.id ? selectedOrgColor : "gray.100",
1988
+ borderColor: currentOrg?.id ? selectedOrgColor : "gray.300",
1989
+ color: currentOrg?.id ? "white" : "gray.700",
1990
+ _hover: {
1991
+ bg: currentOrg?.id ? selectedOrgColor : "gray.200",
1992
+ color: currentOrg?.id ? "white" : "gray.900"
1993
+ },
1994
+ _active: {
1995
+ bg: currentOrg?.id ? selectedOrgColor : "gray.200",
1996
+ color: currentOrg?.id ? "white" : "gray.900"
1997
+ },
1998
+ _expanded: {
1999
+ bg: currentOrg?.id ? selectedOrgColor : "gray.200",
2000
+ color: currentOrg?.id ? "white" : "gray.900"
2001
+ },
2002
+ textAlign: "left",
2003
+ justifyContent: "flex-start",
2004
+ isDisabled: loadingOrgs && organizations.length === 0,
2005
+ sx: currentOrg?.id ? {
2006
+ color: "white",
2007
+ ".chakra-button__icon": { color: "white" }
2008
+ } : void 0,
2009
+ children: /* @__PURE__ */ jsxs(HStack, { spacing: "2.5", minW: 0, align: "center", children: [
1289
2010
  /* @__PURE__ */ jsx(
1290
2011
  OrgBadge,
1291
2012
  {
1292
- name: currentOrg.name,
1293
- logoUrl: currentOrg.logoUrl,
1294
- brandColor: currentOrg.brandColor,
1295
- size: "24px"
2013
+ name: currentOrg?.name,
2014
+ logoUrl: currentOrg?.logoUrl,
2015
+ brandColor: selectedOrgBrandColor,
2016
+ size: "28px"
1296
2017
  }
1297
2018
  ),
1298
- /* @__PURE__ */ jsx(Text, { fontSize: "sm", fontWeight: "600", color: "gray.800", noOfLines: 1, children: currentOrg.name })
1299
- ] }) : /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "gray.500", children: "Select organization" }),
1300
- /* @__PURE__ */ jsx(Text, { as: "span", fontSize: "xs", color: "gray.500", "aria-hidden": "true", children: "\u25BE" })
1301
- ] })
1302
- }
1303
- ),
1304
- /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(MenuList, { zIndex: 1500, children: [
1305
- others.length === 0 && /* @__PURE__ */ jsx(MenuItem, { isDisabled: true, children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "gray.500", children: "No other organizations" }) }),
1306
- others.map((o) => /* @__PURE__ */ jsx(
1307
- MenuItem,
1308
- {
1309
- onClick: () => {
1310
- void switchOrganization(o.id);
1311
- },
1312
- children: /* @__PURE__ */ jsxs(HStack, { spacing: 2, align: "center", children: [
1313
- /* @__PURE__ */ jsx(OrgBadge, { name: o.name, logoUrl: o.logoUrl, brandColor: o.brandColor, size: "20px" }),
1314
- /* @__PURE__ */ jsx(Flex, { direction: "column", children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", fontWeight: "600", children: o.name }) })
2019
+ /* @__PURE__ */ jsx(Text, { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: currentOrg?.name || "No organizations" })
1315
2020
  ] })
2021
+ }
2022
+ ),
2023
+ /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(
2024
+ MenuList,
2025
+ {
2026
+ bg: currentOrg?.id ? selectedOrgMenuBg : "gray.100",
2027
+ borderColor: currentOrg?.id ? selectedOrgColor : "gray.300",
2028
+ color: currentOrg?.id ? "white" : "gray.800",
2029
+ mt: "1",
2030
+ minW: "13rem",
2031
+ maxH: "60vh",
2032
+ overflowY: "auto",
2033
+ zIndex: "modal",
2034
+ boxShadow: "2xl",
2035
+ children: [
2036
+ unselectedOrganizations.map((o) => {
2037
+ const orgBrandColor = getOrganizationBrandColor(o);
2038
+ return /* @__PURE__ */ jsx(
2039
+ MenuItem,
2040
+ {
2041
+ onClick: () => switchOrganization?.(o.id),
2042
+ bg: "transparent",
2043
+ color: currentOrg?.id ? "white" : "inherit",
2044
+ _hover: {
2045
+ bg: currentOrg?.id ? lightenHexColor(selectedOrgBrandColor || "#ea6a3a", 0.2) || "#555555" : "gray.100",
2046
+ color: currentOrg?.id ? "white" : "gray.900"
2047
+ },
2048
+ _focus: {
2049
+ bg: currentOrg?.id ? lightenHexColor(selectedOrgBrandColor || "#ea6a3a", 0.2) || "#555555" : "gray.100",
2050
+ color: currentOrg?.id ? "white" : "gray.900"
2051
+ },
2052
+ _active: {
2053
+ bg: currentOrg?.id ? lightenHexColor(selectedOrgBrandColor || "#ea6a3a", 0.24) || "#5d5d5d" : "gray.200",
2054
+ color: currentOrg?.id ? "white" : "gray.900"
2055
+ },
2056
+ children: /* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", width: "100%", overflow: "hidden", children: [
2057
+ /* @__PURE__ */ jsxs(HStack, { minW: 0, flex: "1", spacing: "2.5", children: [
2058
+ /* @__PURE__ */ jsx(
2059
+ OrgBadge,
2060
+ {
2061
+ name: o.name,
2062
+ logoUrl: o.logoUrl,
2063
+ brandColor: orgBrandColor,
2064
+ size: "24px"
2065
+ }
2066
+ ),
2067
+ /* @__PURE__ */ jsx(
2068
+ Text,
2069
+ {
2070
+ overflow: "hidden",
2071
+ textOverflow: "ellipsis",
2072
+ whiteSpace: "nowrap",
2073
+ flex: "1",
2074
+ children: o.name
2075
+ }
2076
+ )
2077
+ ] }),
2078
+ /* @__PURE__ */ jsx(
2079
+ Text,
2080
+ {
2081
+ fontSize: "xs",
2082
+ color: currentOrg?.id ? "whiteAlpha.700" : "gray.500",
2083
+ ml: 2,
2084
+ flexShrink: 0,
2085
+ children: o.id.slice(-8)
2086
+ }
2087
+ )
2088
+ ] })
2089
+ },
2090
+ o.id
2091
+ );
2092
+ }),
2093
+ /* @__PURE__ */ jsx(MenuDivider, { my: 0, borderColor: currentOrg?.id ? "whiteAlpha.300" : void 0 }),
2094
+ /* @__PURE__ */ jsx(
2095
+ MenuItem,
2096
+ {
2097
+ onClick: () => window.dispatchEvent(new CustomEvent("open-create-org")),
2098
+ fontWeight: "bold",
2099
+ bg: "transparent",
2100
+ color: currentOrg?.id ? "white" : "inherit",
2101
+ my: 0,
2102
+ _hover: {
2103
+ bg: currentOrg?.id ? lightenHexColor(selectedOrgBrandColor || "#ea6a3a", 0.2) || "#555555" : "gray.100",
2104
+ color: currentOrg?.id ? "white" : "gray.900"
2105
+ },
2106
+ _focus: {
2107
+ bg: currentOrg?.id ? lightenHexColor(selectedOrgBrandColor || "#ea6a3a", 0.2) || "#555555" : "gray.100",
2108
+ color: currentOrg?.id ? "white" : "gray.900"
2109
+ },
2110
+ _active: {
2111
+ bg: currentOrg?.id ? lightenHexColor(selectedOrgBrandColor || "#ea6a3a", 0.24) || "#5d5d5d" : "gray.200",
2112
+ color: currentOrg?.id ? "white" : "gray.900"
2113
+ },
2114
+ fontSize: "sm",
2115
+ children: "+ New Organization"
2116
+ }
2117
+ )
2118
+ ]
2119
+ }
2120
+ ) })
2121
+ ] })
2122
+ ] });
2123
+ }
2124
+ var OrgPill_default = OrgPill;
2125
+ function ModePill() {
2126
+ const mode = useShellMode();
2127
+ if (!mode) return null;
2128
+ const { currentMode, modes, switchMode, getModeConfig } = mode;
2129
+ const modeConfig = getModeConfig();
2130
+ if (!modeConfig) return null;
2131
+ const hasMultipleModes = modes.length > 1;
2132
+ const isDisabled = !hasMultipleModes;
2133
+ return /* @__PURE__ */ jsx(Box, { mx: "4", mb: "4", children: /* @__PURE__ */ jsxs(Menu, { gutter: 0, children: [
2134
+ /* @__PURE__ */ jsx(
2135
+ MenuButton,
2136
+ {
2137
+ as: Button,
2138
+ leftIcon: modeConfig.icon,
2139
+ rightIcon: /* @__PURE__ */ jsx(FiChevronDown, {}),
2140
+ size: "sm",
2141
+ width: "full",
2142
+ bg: modeConfig.primaryColor,
2143
+ borderColor: modeConfig.primaryColor,
2144
+ color: modeConfig.white,
2145
+ _disabled: {
2146
+ bg: modeConfig.primaryColor,
2147
+ borderColor: modeConfig.primaryColor,
2148
+ color: modeConfig.white,
2149
+ cursor: "not-allowed",
2150
+ opacity: 1
1316
2151
  },
1317
- o.id
1318
- )),
1319
- createOrgHref && /* @__PURE__ */ jsxs(Fragment, { children: [
1320
- /* @__PURE__ */ jsx(MenuDivider, {}),
1321
- /* @__PURE__ */ jsx(MenuItem, { as: "a", href: createOrgHref, children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "brand.500", fontWeight: "600", children: "Create organization" }) })
2152
+ isDisabled,
2153
+ _hover: {
2154
+ bg: modeConfig.primaryColor,
2155
+ color: modeConfig.white
2156
+ },
2157
+ _active: {
2158
+ bg: modeConfig.primaryColor,
2159
+ color: modeConfig.white
2160
+ },
2161
+ children: modeConfig.name
2162
+ }
2163
+ ),
2164
+ hasMultipleModes && /* @__PURE__ */ jsx(MenuList, { children: modes.map((m) => /* @__PURE__ */ jsx(
2165
+ MenuItem,
2166
+ {
2167
+ onClick: () => switchMode(m.id),
2168
+ bg: "transparent",
2169
+ color: currentMode === m.id ? m.primaryColor : "inherit",
2170
+ _hover: { bg: m.bgColor, color: m.primaryColor },
2171
+ icon: m.icon,
2172
+ children: m.name
2173
+ },
2174
+ m.id
2175
+ )) })
2176
+ ] }) });
2177
+ }
2178
+ var ModePill_default = ModePill;
2179
+ function ActivityIcon() {
2180
+ return /* @__PURE__ */ jsx(
2181
+ "svg",
2182
+ {
2183
+ width: "12",
2184
+ height: "12",
2185
+ viewBox: "0 0 24 24",
2186
+ fill: "none",
2187
+ stroke: "currentColor",
2188
+ strokeWidth: "2",
2189
+ strokeLinecap: "round",
2190
+ strokeLinejoin: "round",
2191
+ "aria-hidden": "true",
2192
+ children: /* @__PURE__ */ jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" })
2193
+ }
2194
+ );
2195
+ }
2196
+ function defaultEnvChip(label) {
2197
+ if (!label) return null;
2198
+ const lower = label.toLowerCase();
2199
+ if (lower.includes("prod")) {
2200
+ return { color: "#ef4444", bg: "#fee2e2", border: "#fecaca", name: label };
2201
+ }
2202
+ if (lower.includes("stag")) {
2203
+ return { color: "#f97316", bg: "#ffedd5", border: "#fed7aa", name: label };
2204
+ }
2205
+ if (lower.includes("local") || lower.includes("dev")) {
2206
+ return { color: "#22c55e", bg: "#dcfce7", border: "#bbf7d0", name: label };
2207
+ }
2208
+ return { color: "#6b7280", bg: "#f3f4f6", border: "#e5e7eb", name: label };
2209
+ }
2210
+ var PLACEHOLDER_SHA_VALUES = /* @__PURE__ */ new Set(["unknown", "n/a", "null", "undefined", ""]);
2211
+ function isMeaningfulCommit(value) {
2212
+ if (typeof value !== "string") return false;
2213
+ const trimmed = value.trim();
2214
+ if (!trimmed) return false;
2215
+ return !PLACEHOLDER_SHA_VALUES.has(trimmed.toLowerCase());
2216
+ }
2217
+ function isMeaningfulAuthor(value) {
2218
+ if (typeof value !== "string") return false;
2219
+ const trimmed = value.trim();
2220
+ if (!trimmed) return false;
2221
+ return !PLACEHOLDER_SHA_VALUES.has(trimmed.toLowerCase());
2222
+ }
2223
+ function getFirstName(value) {
2224
+ if (!value) return "";
2225
+ const stripped = value.replace(/<[^>]+>/g, "").trim();
2226
+ if (!stripped) return "";
2227
+ const [firstToken] = stripped.split(/\s+/);
2228
+ return firstToken || stripped;
2229
+ }
2230
+ function CommitCopyButton({
2231
+ envNode,
2232
+ environmentLabel,
2233
+ commit,
2234
+ version = "",
2235
+ commitDate = "",
2236
+ commitAuthor = "",
2237
+ commitLog = "",
2238
+ onDiagnosticsClick
2239
+ }) {
2240
+ const safeCommit = isMeaningfulCommit(commit) ? commit.trim() : "";
2241
+ const { hasCopied, onCopy } = useClipboard(safeCommit);
2242
+ const shortCommit = safeCommit ? safeCommit.slice(-4) : "";
2243
+ const copiedAnimation = keyframes`
2244
+ 0% { opacity: 0; transform: translateY(6px); }
2245
+ 20% { opacity: 1; transform: translateY(0); }
2246
+ 100% { opacity: 0; transform: translateY(-8px); }
2247
+ `;
2248
+ const formattedDate = useMemo(() => {
2249
+ if (!commitDate) return "";
2250
+ const parsed = new Date(commitDate);
2251
+ if (Number.isNaN(parsed.getTime())) return commitDate;
2252
+ return parsed.toLocaleDateString(void 0, { month: "short", day: "numeric" });
2253
+ }, [commitDate]);
2254
+ const metaLine = useMemo(() => {
2255
+ const firstName = isMeaningfulAuthor(commitAuthor) ? getFirstName(commitAuthor) : "";
2256
+ return [formattedDate, firstName].filter(Boolean).join(" | ");
2257
+ }, [formattedDate, commitAuthor]);
2258
+ const normalizedCommitLog = useMemo(() => {
2259
+ if (commitLog?.trim()) {
2260
+ return commitLog.replace(/\r\n/g, "\n").replace(/\\n/g, "\n");
2261
+ }
2262
+ return "";
2263
+ }, [commitLog]);
2264
+ const tooltipContent = normalizedCommitLog.length > 0 ? /* @__PURE__ */ jsx(
2265
+ Box,
2266
+ {
2267
+ maxW: "600px",
2268
+ width: "min(600px, 90vw)",
2269
+ maxH: "80vh",
2270
+ whiteSpace: "pre-wrap",
2271
+ overflowY: "auto",
2272
+ overflowX: "auto",
2273
+ fontSize: "xs",
2274
+ fontFamily: "mono",
2275
+ wordBreak: "break-word",
2276
+ children: normalizedCommitLog
2277
+ }
2278
+ ) : void 0;
2279
+ const envChip = useMemo(() => {
2280
+ if (envNode) return envNode;
2281
+ const chip = defaultEnvChip(environmentLabel);
2282
+ if (!chip) return null;
2283
+ return /* @__PURE__ */ jsx(
2284
+ Box,
2285
+ {
2286
+ as: "span",
2287
+ display: "inline-flex",
2288
+ alignItems: "center",
2289
+ px: "1.5",
2290
+ py: "0.5",
2291
+ borderWidth: "1px",
2292
+ borderColor: chip.border,
2293
+ bg: chip.bg,
2294
+ color: chip.color,
2295
+ borderRadius: "md",
2296
+ fontSize: "2xs",
2297
+ fontWeight: "600",
2298
+ lineHeight: "1",
2299
+ textTransform: "capitalize",
2300
+ "aria-label": `Environment: ${chip.name}`,
2301
+ children: chip.name
2302
+ }
2303
+ );
2304
+ }, [envNode, environmentLabel]);
2305
+ const showEnv = Boolean(envChip);
2306
+ return /* @__PURE__ */ jsxs(
2307
+ VStack,
2308
+ {
2309
+ justify: "center",
2310
+ align: "center",
2311
+ borderRadius: "md",
2312
+ py: 0.5,
2313
+ px: 1,
2314
+ position: "relative",
2315
+ spacing: 0,
2316
+ textAlign: "center",
2317
+ w: "full",
2318
+ children: [
2319
+ /* @__PURE__ */ jsxs(Flex, { justify: "center", align: "center", gap: 2, whiteSpace: "nowrap", children: [
2320
+ showEnv ? /* @__PURE__ */ jsxs(Fragment, { children: [
2321
+ /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx(
2322
+ Tooltip,
2323
+ {
2324
+ label: tooltipContent,
2325
+ isDisabled: !tooltipContent,
2326
+ hasArrow: true,
2327
+ placement: "top-start",
2328
+ openDelay: 150,
2329
+ closeOnClick: false,
2330
+ shouldWrapChildren: true,
2331
+ modifiers: [
2332
+ { name: "preventOverflow", options: { padding: 16 } },
2333
+ { name: "offset", options: { offset: [0, 12] } }
2334
+ ],
2335
+ children: /* @__PURE__ */ jsx(
2336
+ Button,
2337
+ {
2338
+ variant: "unstyled",
2339
+ minW: "auto",
2340
+ h: "auto",
2341
+ p: 0,
2342
+ lineHeight: "1",
2343
+ color: "inherit",
2344
+ onClick: onCopy,
2345
+ "aria-label": "Copy commit ID",
2346
+ isDisabled: !safeCommit,
2347
+ children: envChip
2348
+ }
2349
+ )
2350
+ }
2351
+ ) }),
2352
+ /* @__PURE__ */ jsx(Text, { fontSize: "2xs", color: "gray.300", flexShrink: 0, children: "|" })
2353
+ ] }) : null,
2354
+ shortCommit ? /* @__PURE__ */ jsx(Text, { fontSize: "2xs", color: hasCopied ? "green.500" : "gray.400", flexShrink: 0, children: shortCommit }) : null,
2355
+ version ? /* @__PURE__ */ jsxs(Fragment, { children: [
2356
+ shortCommit ? /* @__PURE__ */ jsx(Text, { fontSize: "2xs", color: "gray.300", flexShrink: 0, children: "|" }) : null,
2357
+ /* @__PURE__ */ jsxs(Text, { fontSize: "2xs", color: "gray.400", flexShrink: 0, children: [
2358
+ "v",
2359
+ version
2360
+ ] })
2361
+ ] }) : null,
2362
+ onDiagnosticsClick ? /* @__PURE__ */ jsxs(Fragment, { children: [
2363
+ /* @__PURE__ */ jsx(Text, { fontSize: "2xs", color: "gray.300", flexShrink: 0, children: "|" }),
2364
+ /* @__PURE__ */ jsx(Tooltip, { label: "Diagnostics", hasArrow: true, placement: "top", children: /* @__PURE__ */ jsx(
2365
+ Button,
2366
+ {
2367
+ variant: "unstyled",
2368
+ minW: "auto",
2369
+ h: "auto",
2370
+ p: 0,
2371
+ lineHeight: "1",
2372
+ color: "gray.400",
2373
+ _hover: { color: "gray.600" },
2374
+ onClick: (e) => {
2375
+ e.stopPropagation();
2376
+ onDiagnosticsClick();
2377
+ },
2378
+ "aria-label": "Diagnostics",
2379
+ children: /* @__PURE__ */ jsx(Icon, { as: ActivityIcon, boxSize: 3 })
2380
+ }
2381
+ ) })
2382
+ ] }) : null
2383
+ ] }),
2384
+ metaLine ? /* @__PURE__ */ jsxs(Text, { fontSize: "2xs", color: "gray.500", textAlign: "center", whiteSpace: "nowrap", children: [
2385
+ "| ",
2386
+ metaLine,
2387
+ " |"
2388
+ ] }) : null,
2389
+ hasCopied && /* @__PURE__ */ jsx(
2390
+ Box,
2391
+ {
2392
+ position: "absolute",
2393
+ bottom: "100%",
2394
+ mb: "6px",
2395
+ px: "2",
2396
+ py: "1",
2397
+ fontSize: "xs",
2398
+ bg: "gray.700",
2399
+ color: "white",
2400
+ borderRadius: "md",
2401
+ animation: `${copiedAnimation} 0.9s ease-out`,
2402
+ pointerEvents: "none",
2403
+ children: "Copied"
2404
+ }
2405
+ )
2406
+ ]
2407
+ }
2408
+ );
2409
+ }
2410
+ function Sidebar({ onClose, id }) {
2411
+ const { brandLogoSrc } = useShellChrome();
2412
+ const navItems = useShellNavItems()?.items ?? [];
2413
+ const footer = useShellFooter();
2414
+ const org = useShellOrganization();
2415
+ const currentOrg = org?.currentOrg ?? null;
2416
+ const location = useLocation();
2417
+ const sidebarBg = useColorModeValue("white", "gray.900");
2418
+ const sidebarBorderColor = useColorModeValue("gray.200", "gray.700");
2419
+ const [expandedMenu, setExpandedMenu] = useState(null);
2420
+ useEffect(() => {
2421
+ const activeMenu = navItems.find(
2422
+ (item) => item.children && item.children.some((child) => {
2423
+ if (!child.href) return false;
2424
+ const childPath = child.href.split("?")[0];
2425
+ if (!childPath) return false;
2426
+ return location.pathname === childPath || location.pathname.startsWith(`${childPath}/`);
2427
+ })
2428
+ )?.name || null;
2429
+ setExpandedMenu(activeMenu);
2430
+ }, [location.pathname, navItems]);
2431
+ const handleMenuToggle = (menuKey) => {
2432
+ if (!menuKey) {
2433
+ setExpandedMenu(null);
2434
+ return;
2435
+ }
2436
+ setExpandedMenu((prev) => prev === menuKey ? null : menuKey);
2437
+ };
2438
+ return /* @__PURE__ */ jsxs(
2439
+ Flex,
2440
+ {
2441
+ id: id || "sidebar-container",
2442
+ transition: "3s ease",
2443
+ bg: sidebarBg,
2444
+ borderRight: "1px",
2445
+ borderRightColor: sidebarBorderColor,
2446
+ w: { base: "full", md: 60 },
2447
+ pos: "fixed",
2448
+ h: "100vh",
2449
+ direction: "column",
2450
+ overflow: "hidden",
2451
+ children: [
2452
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "center", mx: "8", mt: "3", mb: "2", justifyContent: "space-between", flexShrink: 0, children: [
2453
+ /* @__PURE__ */ jsx(Box, { h: "40px", children: brandLogoSrc ? /* @__PURE__ */ jsx(BrandLogo_default, { src: brandLogoSrc, className: "h-full w-auto object-contain" }) : null }),
2454
+ onClose ? /* @__PURE__ */ jsx(
2455
+ IconButton,
2456
+ {
2457
+ display: { base: "flex", md: "none" },
2458
+ onClick: onClose,
2459
+ variant: "outline",
2460
+ "aria-label": "close menu",
2461
+ icon: /* @__PURE__ */ jsx(FiX, {})
2462
+ }
2463
+ ) : null
2464
+ ] }),
2465
+ /* @__PURE__ */ jsxs(
2466
+ Box,
2467
+ {
2468
+ flex: "1",
2469
+ overflowY: "auto",
2470
+ css: {
2471
+ "&::-webkit-scrollbar": { width: "8px" },
2472
+ "&::-webkit-scrollbar-track": { background: "transparent" },
2473
+ "&::-webkit-scrollbar-thumb": { background: "#CBD5E0", borderRadius: "4px" },
2474
+ "&::-webkit-scrollbar-thumb:hover": { background: "#A0AEC0" }
2475
+ },
2476
+ children: [
2477
+ currentOrg?.logoUrl && /* @__PURE__ */ jsx(Box, { mx: "4", mb: "3", children: /* @__PURE__ */ jsx(
2478
+ Image,
2479
+ {
2480
+ src: currentOrg.logoUrl,
2481
+ alt: `${currentOrg.name} logo`,
2482
+ maxH: "70px",
2483
+ w: "full",
2484
+ objectFit: "contain",
2485
+ borderRadius: "md"
2486
+ }
2487
+ ) }),
2488
+ /* @__PURE__ */ jsx(OrgPill_default, {}),
2489
+ /* @__PURE__ */ jsx(ModePill_default, {}),
2490
+ navItems.map((link) => /* @__PURE__ */ jsx(
2491
+ NavItem_default,
2492
+ {
2493
+ icon: link.icon,
2494
+ brandIcon: link.brandIcon,
2495
+ href: link.href,
2496
+ subItems: link.children,
2497
+ badge: link.badge,
2498
+ subLabel: link.subLabel,
2499
+ action: link.action,
2500
+ menuKey: String(link.name),
2501
+ isExpanded: expandedMenu === link.name,
2502
+ onToggle: handleMenuToggle,
2503
+ isDisabled: Boolean(link.disabled),
2504
+ children: link.name
2505
+ },
2506
+ link.name
2507
+ ))
2508
+ ]
2509
+ }
2510
+ ),
2511
+ footer && /* @__PURE__ */ jsx(
2512
+ Box,
2513
+ {
2514
+ px: "1",
2515
+ py: "1",
2516
+ borderTop: "1px",
2517
+ borderColor: "gray.200",
2518
+ flexShrink: 0,
2519
+ bg: sidebarBg,
2520
+ zIndex: 1,
2521
+ children: /* @__PURE__ */ jsxs(VStack, { align: "stretch", spacing: 2, px: "3", py: "2", children: [
2522
+ /* @__PURE__ */ jsx(HStack, { spacing: 1, fontSize: "xs", color: "gray.600", wrap: "wrap", children: footer.links.map((l, idx) => /* @__PURE__ */ jsxs(Box, { display: "inline-flex", alignItems: "center", children: [
2523
+ idx > 0 ? /* @__PURE__ */ jsx(Text, { color: "gray.400", mx: 1, children: "|" }) : null,
2524
+ l.external ? /* @__PURE__ */ jsx(
2525
+ Link$1,
2526
+ {
2527
+ href: l.href,
2528
+ isExternal: true,
2529
+ _hover: { textDecoration: "underline", opacity: 1 },
2530
+ children: l.label
2531
+ }
2532
+ ) : /* @__PURE__ */ jsx(
2533
+ Link$1,
2534
+ {
2535
+ as: NavLink,
2536
+ to: l.href,
2537
+ _hover: { textDecoration: "underline", opacity: 1 },
2538
+ children: l.label
2539
+ }
2540
+ )
2541
+ ] }, `${l.label}-${idx}`)) }),
2542
+ (footer.version || footer.commit || footer.commitFull || footer.environmentLabel || footer.envNode) && /* @__PURE__ */ jsx(
2543
+ CommitCopyButton,
2544
+ {
2545
+ envNode: footer.envNode,
2546
+ environmentLabel: footer.environmentLabel,
2547
+ commit: footer.commitFull ?? footer.commit,
2548
+ version: footer.version,
2549
+ commitDate: footer.commitDate,
2550
+ commitAuthor: footer.commitAuthor,
2551
+ commitLog: footer.commitLog,
2552
+ onDiagnosticsClick: footer.onDiagnosticsClick
2553
+ }
2554
+ )
2555
+ ] })
2556
+ }
2557
+ )
2558
+ ]
2559
+ }
2560
+ );
2561
+ }
2562
+ function Footer({ buildInfo }) {
2563
+ return /* @__PURE__ */ jsx(
2564
+ Box,
2565
+ {
2566
+ as: "footer",
2567
+ px: 4,
2568
+ py: 2,
2569
+ borderTop: "1px solid",
2570
+ borderColor: "gray.200",
2571
+ fontSize: "xs",
2572
+ color: "gray.500",
2573
+ children: /* @__PURE__ */ jsxs(Text, { children: [
2574
+ "politogy ",
2575
+ buildInfo ? `\xB7 ${buildInfo}` : ""
1322
2576
  ] })
1323
- ] }) })
2577
+ }
2578
+ );
2579
+ }
2580
+ function AppLayout({
2581
+ children,
2582
+ buildInfo,
2583
+ modalsSlot,
2584
+ notificationsSlot,
2585
+ overlaySlot
2586
+ }) {
2587
+ const [mobileOpen, setMobileOpen] = useState(false);
2588
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", minH: "100vh", children: [
2589
+ /* @__PURE__ */ jsx(
2590
+ Sidebar,
2591
+ {
2592
+ onClose: mobileOpen ? () => setMobileOpen(false) : void 0
2593
+ }
2594
+ ),
2595
+ /* @__PURE__ */ jsx(Header, { onOpen: () => setMobileOpen(true) }),
2596
+ /* @__PURE__ */ jsxs(Box, { as: "main", flex: "1", ml: { base: 0, md: 60 }, position: "relative", overflow: "auto", children: [
2597
+ children,
2598
+ notificationsSlot
2599
+ ] }),
2600
+ buildInfo ? /* @__PURE__ */ jsx(Footer, { buildInfo }) : null,
2601
+ modalsSlot,
2602
+ overlaySlot
1324
2603
  ] });
1325
2604
  }
1326
2605
  var SUPPORT_REQUEST_SUCCESS_MESSAGE = "Your support request was sent to Politogy leadership.";
@@ -1607,191 +2886,58 @@ function LifebuoyIcon() {
1607
2886
  }
1608
2887
  );
1609
2888
  }
1610
- function avatarUrlFromUser(user) {
1611
- if (!user) return void 0;
1612
- const meta = user.raw.user_metadata;
1613
- return meta?.avatar_url ?? void 0;
1614
- }
1615
- function UserMenu() {
1616
- const { user, status, signOut } = useShellAuth();
1617
- const { myProfileHref } = useShellChrome();
1618
- const linked = useShellLinkedAccounts();
1619
- const { colorMode, toggleColorMode } = useColorMode();
1620
- if (status !== "authenticated" || !user) return null;
1621
- const displayName = user.displayName ?? user.email ?? "User";
1622
- const avatarUrl = avatarUrlFromUser(user);
1623
- return /* @__PURE__ */ jsxs(Menu, { placement: "bottom-end", children: [
1624
- /* @__PURE__ */ jsx(
1625
- MenuButton,
1626
- {
1627
- "aria-label": "Open account menu",
1628
- as: Box,
1629
- cursor: "pointer",
1630
- borderRadius: "full",
1631
- _hover: { opacity: 0.85 },
1632
- children: /* @__PURE__ */ jsx(Avatar, { size: "sm", name: displayName, src: avatarUrl })
1633
- }
1634
- ),
1635
- /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(MenuList, { zIndex: 1500, children: [
1636
- /* @__PURE__ */ jsxs(Box, { px: 3, py: 2, children: [
1637
- /* @__PURE__ */ jsx(Text, { fontSize: "sm", fontWeight: "600", noOfLines: 1, children: displayName }),
1638
- user.email && /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "gray.500", noOfLines: 1, children: user.email })
1639
- ] }),
1640
- /* @__PURE__ */ jsx(MenuDivider, {}),
1641
- /* @__PURE__ */ jsx(MenuItem, { as: "a", href: myProfileHref, children: "My Profile" }),
1642
- /* @__PURE__ */ jsx(MenuItem, { onClick: toggleColorMode, children: colorMode === "light" ? "Switch to dark mode" : "Switch to light mode" }),
1643
- /* @__PURE__ */ jsx(MenuItem, { onClick: () => void signOut(), children: "Sign out" }),
1644
- linked && /* @__PURE__ */ jsxs(Fragment, { children: [
1645
- /* @__PURE__ */ jsx(MenuDivider, {}),
1646
- /* @__PURE__ */ jsx(Box, { px: 3, pt: 1, pb: 1, children: /* @__PURE__ */ jsx(Text, { fontSize: "xs", textTransform: "uppercase", letterSpacing: "0.08em", color: "gray.500", children: "Switch account" }) }),
1647
- linked.linkedAccounts.length === 0 && /* @__PURE__ */ jsx(MenuItem, { isDisabled: true, children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "gray.500", children: "No linked accounts" }) }),
1648
- linked.linkedAccounts.map((acct) => /* @__PURE__ */ jsx(
1649
- MenuItem,
1650
- {
1651
- onClick: () => {
1652
- void linked.switchToAccount(acct.id);
1653
- },
1654
- children: /* @__PURE__ */ jsxs(HStack, { spacing: 2, align: "center", children: [
1655
- /* @__PURE__ */ jsx(
1656
- Avatar,
1657
- {
1658
- size: "xs",
1659
- name: acct.displayName ?? acct.email,
1660
- src: acct.avatarUrl ?? void 0
1661
- }
1662
- ),
1663
- /* @__PURE__ */ jsxs(Flex, { direction: "column", minW: 0, children: [
1664
- /* @__PURE__ */ jsx(Text, { fontSize: "sm", fontWeight: "600", noOfLines: 1, children: acct.displayName ?? acct.email }),
1665
- acct.displayName && /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "gray.500", noOfLines: 1, children: acct.email })
1666
- ] })
1667
- ] })
1668
- },
1669
- acct.id
1670
- )),
1671
- /* @__PURE__ */ jsx(MenuItem, { onClick: linked.onAddAccount, children: "Add account" })
1672
- ] })
1673
- ] }) })
1674
- ] });
1675
- }
1676
- function Header({ extrasSlot }) {
1677
- const { devNotesMenu } = useShellChrome();
1678
- return /* @__PURE__ */ jsxs(
1679
- Flex,
1680
- {
1681
- as: "header",
1682
- align: "center",
1683
- px: 4,
1684
- h: "56px",
1685
- bg: "white",
1686
- borderBottom: "1px solid",
1687
- borderColor: "gray.200",
1688
- gap: 3,
1689
- children: [
1690
- /* @__PURE__ */ jsx(Heading, { size: "md", color: "brand.500", children: "politogy" }),
1691
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(OrgSelector, {}) }),
1692
- /* @__PURE__ */ jsx(Spacer, {}),
1693
- extrasSlot && /* @__PURE__ */ jsx(Box, { children: extrasSlot }),
1694
- /* @__PURE__ */ jsx(SupportRequestButton, {}),
1695
- devNotesMenu,
1696
- /* @__PURE__ */ jsx(UserMenu, {})
1697
- ]
1698
- }
1699
- );
1700
- }
1701
- function Sidebar({ entries, currentApp, footerSlot }) {
1702
- return /* @__PURE__ */ jsxs(
1703
- Box,
1704
- {
1705
- as: "nav",
1706
- w: "200px",
1707
- minH: "100%",
1708
- bg: "gray.50",
1709
- borderRight: "1px solid",
1710
- borderColor: "gray.200",
1711
- p: 3,
1712
- display: "flex",
1713
- flexDirection: "column",
1714
- children: [
1715
- /* @__PURE__ */ jsx(VStack, { align: "stretch", spacing: 1, children: entries.filter((e) => e.visible !== false).map((entry) => /* @__PURE__ */ jsx(
1716
- Link$1,
1717
- {
1718
- href: entry.path,
1719
- "data-current": entry.id === currentApp,
1720
- px: 3,
1721
- py: 2,
1722
- borderRadius: "md",
1723
- fontWeight: entry.id === currentApp ? "bold" : "normal",
1724
- bg: entry.id === currentApp ? "brand.50" : "transparent",
1725
- _hover: { bg: "gray.100" },
1726
- children: /* @__PURE__ */ jsx(Text, { children: entry.label })
1727
- },
1728
- entry.id
1729
- )) }),
1730
- /* @__PURE__ */ jsx(Box, { flex: "1" }),
1731
- footerSlot && /* @__PURE__ */ jsx(Box, { pt: 3, children: footerSlot })
1732
- ]
1733
- }
1734
- );
1735
- }
1736
- function Footer({ buildInfo }) {
1737
- return /* @__PURE__ */ jsx(
1738
- Box,
1739
- {
1740
- as: "footer",
1741
- px: 4,
1742
- py: 2,
1743
- borderTop: "1px solid",
1744
- borderColor: "gray.200",
1745
- fontSize: "xs",
1746
- color: "gray.500",
1747
- children: /* @__PURE__ */ jsxs(Text, { children: [
1748
- "politogy ",
1749
- buildInfo ? `\xB7 ${buildInfo}` : ""
1750
- ] })
1751
- }
1752
- );
1753
- }
1754
- function AppLayout({
1755
- children,
1756
- buildInfo,
1757
- headerExtrasSlot,
1758
- modalsSlot,
1759
- notificationsSlot,
1760
- sidebarFooterSlot,
1761
- overlaySlot
1762
- }) {
1763
- const currentApp = useCurrentApp();
1764
- const entries = useAppRegistry();
1765
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", minH: "100vh", children: [
1766
- /* @__PURE__ */ jsx(Header, { extrasSlot: headerExtrasSlot }),
1767
- /* @__PURE__ */ jsxs(Flex, { flex: "1", minH: 0, children: [
1768
- /* @__PURE__ */ jsx(Sidebar, { entries, currentApp, footerSlot: sidebarFooterSlot }),
1769
- /* @__PURE__ */ jsxs(Box, { as: "main", flex: "1", overflow: "auto", position: "relative", children: [
1770
- children,
1771
- notificationsSlot
1772
- ] })
1773
- ] }),
1774
- /* @__PURE__ */ jsx(Footer, { buildInfo }),
1775
- modalsSlot,
1776
- overlaySlot
1777
- ] });
1778
- }
1779
2889
  function ShellChrome({
1780
2890
  children,
1781
2891
  organization,
1782
2892
  linkedAccounts,
1783
2893
  support,
1784
2894
  devNotesMenu,
1785
- myProfileHref
2895
+ myProfileHref,
2896
+ mode,
2897
+ navItems,
2898
+ navigationPreference,
2899
+ role,
2900
+ footer,
2901
+ brandLogoSrc,
2902
+ headerIconsSlot,
2903
+ organizationSettingsHref,
2904
+ showContinueOnboarding,
2905
+ onContinueOnboarding,
2906
+ pageTitle,
2907
+ breadcrumbs,
2908
+ headerActions
1786
2909
  }) {
1787
2910
  const chrome = useMemo(
1788
2911
  () => ({
1789
2912
  devNotesMenu: devNotesMenu ?? null,
1790
- myProfileHref: myProfileHref ?? "/my-profile"
2913
+ myProfileHref: myProfileHref ?? "/my-profile",
2914
+ brandLogoSrc,
2915
+ headerIconsSlot,
2916
+ organizationSettingsHref,
2917
+ showContinueOnboarding,
2918
+ onContinueOnboarding,
2919
+ pageTitle,
2920
+ breadcrumbs,
2921
+ headerActions
1791
2922
  }),
1792
- [devNotesMenu, myProfileHref]
2923
+ [
2924
+ devNotesMenu,
2925
+ myProfileHref,
2926
+ brandLogoSrc,
2927
+ headerIconsSlot,
2928
+ organizationSettingsHref,
2929
+ showContinueOnboarding,
2930
+ onContinueOnboarding,
2931
+ pageTitle,
2932
+ breadcrumbs,
2933
+ headerActions
2934
+ ]
2935
+ );
2936
+ const navItemsValue = useMemo(
2937
+ () => navItems ? { items: navItems } : null,
2938
+ [navItems]
1793
2939
  );
1794
- return /* @__PURE__ */ jsx(ShellOrgContext.Provider, { value: organization ?? null, children: /* @__PURE__ */ jsx(ShellLinkedAccountsContext.Provider, { value: linkedAccounts ?? null, children: /* @__PURE__ */ jsx(ShellSupportProviderContext.Provider, { value: support ?? null, children: /* @__PURE__ */ jsx(ShellChromeContext.Provider, { value: chrome, children }) }) }) });
2940
+ return /* @__PURE__ */ jsx(ShellOrgContext.Provider, { value: organization ?? null, children: /* @__PURE__ */ jsx(ShellLinkedAccountsContext.Provider, { value: linkedAccounts ?? null, children: /* @__PURE__ */ jsx(ShellSupportProviderContext.Provider, { value: support ?? null, children: /* @__PURE__ */ jsx(ShellModeContext.Provider, { value: mode ?? null, children: /* @__PURE__ */ jsx(ShellNavItemsContext.Provider, { value: navItemsValue, children: /* @__PURE__ */ jsx(ShellNavigationPreferenceContext.Provider, { value: navigationPreference ?? null, children: /* @__PURE__ */ jsx(ShellRoleContext.Provider, { value: role ?? null, children: /* @__PURE__ */ jsx(ShellFooterContext.Provider, { value: footer ?? null, children: /* @__PURE__ */ jsx(ShellChromeContext.Provider, { value: chrome, children }) }) }) }) }) }) }) }) });
1795
2941
  }
1796
2942
 
1797
2943
  // src/sw/registerShellPrecache.ts
@@ -1824,6 +2970,6 @@ function buildSpeculationRules(currentApp, registry) {
1824
2970
  return JSON.stringify(payload);
1825
2971
  }
1826
2972
 
1827
- export { AppLayout, BreadcrumbBar, Card, Footer, GlobalUiIdProvider, Header, HeaderTabBar, OrgSelector, PageContainer, PageHeader, ShellChrome, ShellChromeContext, ShellLinkedAccountsContext, ShellOrgContext, ShellProvider, ShellSupportProviderContext, Sidebar, SupportRequestButton, UserMenu, buildSpeculationRules, politogyTheme, registerShellPrecache, useAppRegistry, useCurrentApp, useShellAuth, useShellChrome, useShellLinkedAccounts, useShellOrganization, useShellSupport };
2973
+ export { AppLayout, BrandIcon, BrandLogo, BreadcrumbBar, Card, CommitCopyButton, Footer, GlobalUiIdProvider, Header, HeaderTabBar, ModePill, NavItem, OrgPill, OrgPill as OrgSelector, PageContainer, PageHeader, ShellChrome, ShellChromeContext, ShellFooterContext, ShellLinkedAccountsContext, ShellModeContext, ShellNavItemsContext, ShellNavigationPreferenceContext, ShellOrgContext, ShellProvider, ShellRoleContext, ShellSupportProviderContext, Sidebar, SupportRequestButton, UserMenu, buildSpeculationRules, getOrganizationBadgeLetters, getOrganizationBrandColor, getUserDisplayName, hexToRgba, lightenHexColor, normalizeHexColor, politogyTheme, registerShellPrecache, useAppRegistry, useCurrentApp, useShellAuth, useShellChrome, useShellFooter, useShellLinkedAccounts, useShellMode, useShellNavItems, useShellNavigationPreference, useShellOrganization, useShellRole, useShellSupport };
1828
2974
  //# sourceMappingURL=index.js.map
1829
2975
  //# sourceMappingURL=index.js.map