@eintrek/erp-shell 0.1.55 → 0.1.57

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.d.ts CHANGED
@@ -38,7 +38,7 @@ export { PermissionProvider, type PermissionProviderProps, type PermissionProbe,
38
38
  export { getRequiredChildGroups, normalizePathnameForPermissionRules, resolveUiRoutePermission, type UiRoutePermissionLookup, } from "./auth/permission-utils";
39
39
  export * from "./navigation/navigation-helpers";
40
40
  export type { Module, SubPage, SubModule, Tool, Company, NavigationData, } from "./navigation/navigation-types";
41
- export { canAccessNavPermissionRoute, filterNavigationToolsByPermission, filterNavigationModulesByPermission, getActiveModule, getActiveSubModule, buildUrlWithCode, } from "./navigation/navigation-data-helpers";
41
+ export { canAccessNavPermissionRoute, filterNavigationToolsByPermission, filterNavigationModulesByPermission, getActiveModule, getActiveSubModule, buildUrlWithCode, resolveActiveNav, type ActiveNavEntry, type ResolveActiveNavOptions, } from "./navigation/navigation-data-helpers";
42
42
  export { ThemeProvider } from "./providers/theme-provider";
43
43
  export { FeatureFlagsProvider, useFeatureFlags, useFeatureFlag, type FeatureFlags, } from "./providers/feature-flags-provider";
44
44
  export { ReactQueryProvider } from "./providers/react-query-provider";
package/dist/index.esm.js CHANGED
@@ -703,46 +703,70 @@ function filterNavigationModulesByPermission(modules, rulesMap, userChildSegment
703
703
  })
704
704
  .filter((m) => m !== null);
705
705
  }
706
+ /** The path below the org code: "/O1/project/budgets" → "/project/budgets". */
707
+ function pathBelowCode(pathname) {
708
+ const segments = pathname.split("?")[0].split("/").filter(Boolean);
709
+ return segments.length > 1 ? "/" + segments.slice(1).join("/") : "/";
710
+ }
706
711
  /**
707
- * Find the active top-level module id from `pathname`. Strips the org-code
708
- * prefix from the path (first path segment) before matching.
712
+ * Whether `url` (relative, no org code) covers `path`: the same page, or a
713
+ * page below it on a segment boundary. "/" covers only the home itself and a
714
+ * query-only URL covers nothing.
709
715
  */
710
- function getActiveModule(pathname, modules) {
711
- const segments = pathname.split("/").filter(Boolean);
712
- const pathWithoutCode = segments.length > 1 ? "/" + segments.slice(1).join("/") : "/";
716
+ function coversPath(url, path) {
717
+ if (!url || url.includes("?") || /^https?:\/\//i.test(url))
718
+ return false;
719
+ if (url === "/")
720
+ return path === "/";
721
+ const clean = url.replace(/\/+$/, "");
722
+ return path === clean || path.startsWith(clean + "/");
723
+ }
724
+ /**
725
+ * The module and page whose URL is the longest one covering the path, over
726
+ * every module — a module's own URL, its pages and their sub-pages. Equal
727
+ * lengths go to the first in menu order.
728
+ */
729
+ function longestMatch(path, modules) {
730
+ let best = null;
731
+ const consider = (module, sub, url) => {
732
+ if (!coversPath(url, path))
733
+ return;
734
+ if (!best || url.length > best.length) {
735
+ best = { module, sub, length: url.length };
736
+ }
737
+ };
713
738
  for (const navModule of modules) {
714
- if (pathWithoutCode === navModule.url || pathname.endsWith(navModule.url)) {
715
- return navModule.id;
716
- }
717
- if (navModule.subModules) {
718
- for (const subModule of navModule.subModules) {
719
- if (pathWithoutCode.startsWith(subModule.url) ||
720
- pathname.includes(subModule.url)) {
721
- return navModule.id;
722
- }
723
- }
739
+ for (const sub of navModule.subModules ?? []) {
740
+ consider(navModule, sub, sub.url);
741
+ for (const page of sub.subPages ?? [])
742
+ consider(navModule, sub, page.url);
724
743
  }
744
+ // After its pages: on a tie the page is the more specific answer.
745
+ consider(navModule, null, navModule.url);
725
746
  }
726
- return null;
747
+ const found = best;
748
+ return found ? { module: found.module, sub: found.sub } : null;
749
+ }
750
+ /**
751
+ * Find the active top-level module id from `pathname`: the module whose URL
752
+ * (or one of whose pages' URLs) is the longest prefix of the path below the
753
+ * org code.
754
+ *
755
+ * Before 0.1.57 this took the first module with a page URL anywhere in the
756
+ * path (`includes`), so "/project" claimed /project/schedule and the sidebar,
757
+ * the trail and the tabs could disagree about where the user was.
758
+ */
759
+ function getActiveModule(pathname, modules) {
760
+ return longestMatch(pathBelowCode(pathname), modules)?.module.id ?? null;
727
761
  }
728
- /** Find the active sub-module id from `pathname`. */
762
+ /** Find the active sub-module id from `pathname`, by the same rule. */
729
763
  function getActiveSubModule(pathname, modules) {
730
- const segments = pathname.split("/").filter(Boolean);
731
- const pathWithoutCode = segments.length > 1 ? "/" + segments.slice(1).join("/") : "/";
732
- for (const navModule of modules) {
733
- if (navModule.subModules) {
734
- for (const subModule of navModule.subModules) {
735
- if (pathWithoutCode.startsWith(subModule.url) ||
736
- pathname.includes(subModule.url)) {
737
- return subModule.id;
738
- }
739
- }
740
- }
741
- }
742
- return null;
764
+ // Null when the module's own URL is the best match: no page of it is.
765
+ return longestMatch(pathBelowCode(pathname), modules)?.sub?.id ?? null;
743
766
  }
744
767
  /** Prepend `code` (org code) to a relative URL so `/sales` → `/O25.../sales`.
745
768
  * Absolute `http(s)://` URLs are left intact; optional `{code}` placeholder is replaced.
769
+ * Query/hash are preserved: `/?start=1` → `/O25...?start=1` (not `/O25.../?start=1`).
746
770
  */
747
771
  function buildUrlWithCode(code, url) {
748
772
  if (/^https?:\/\//i.test(url)) {
@@ -752,8 +776,109 @@ function buildUrlWithCode(code, url) {
752
776
  }
753
777
  if (!code)
754
778
  return url;
755
- const cleanUrl = url.startsWith("/") ? url.slice(1) : url;
756
- return `/${code}/${cleanUrl}`;
779
+ const match = url.match(/^([^?#]*)(\?[^#]*)?(#.*)?$/);
780
+ const path = match?.[1] ?? url;
781
+ const query = match?.[2] ?? "";
782
+ const hash = match?.[3] ?? "";
783
+ const cleanPath = path.startsWith("/") ? path.slice(1) : path;
784
+ const base = cleanPath ? `/${code}/${cleanPath}` : `/${code}`;
785
+ return `${base}${query}${hash}`;
786
+ }
787
+ const strip = (p) => (p.length > 1 ? p.replace(/\/+$/, "") : p);
788
+ /** Same check as `isPathActive`: the page itself or one below it. */
789
+ const pathIsAtOrBelow = (pathname, url) => pathname === url || pathname.startsWith(url + "/");
790
+ /** "/O1/" and "/O1" are the same page; a query string never is. */
791
+ function samePage(pathname, url) {
792
+ if (url.includes("?"))
793
+ return false;
794
+ return strip(pathname) === strip(url);
795
+ }
796
+ /**
797
+ * How long the URL that makes `url` cover `pathname` is — or -1 when it does
798
+ * not. The organisation's home ("/" → "/O1") covers only itself: every page
799
+ * sits below it, and a dashboard is not the parent of every page.
800
+ */
801
+ function coverLength(pathname, url, code) {
802
+ const built = buildUrlWithCode(code, url);
803
+ const isRoot = url === "/" || (!!code && strip(built) === `/${code}`);
804
+ if (isRoot) {
805
+ return samePage(pathname, built) || samePage(pathname, url)
806
+ ? strip(built).length
807
+ : -1;
808
+ }
809
+ if (pathIsAtOrBelow(pathname, built))
810
+ return built.length;
811
+ if (pathIsAtOrBelow(pathname, url))
812
+ return url.length;
813
+ return -1;
814
+ }
815
+ /**
816
+ * Pick the single sidebar entry for `pathname`: the longest URL that covers
817
+ * the path, across every group. Before 0.1.57 the longest match was taken in
818
+ * each group separately, so /project/schedule lit both "โครงการ" (/project,
819
+ * under โครงการ) and "ภาพรวมแผน" (/project/schedule, under แผนงาน).
820
+ *
821
+ * Equal lengths go to the group the app marks active (`item.isActive`, from
822
+ * its `activeModule`), then to a page over a group row, then to the first in
823
+ * menu order.
824
+ */
825
+ function resolveActiveNav(items, pathname, { code, workspace = false, highlightActiveModule = false } = {}) {
826
+ const path = pathname ?? "";
827
+ // The app named a one-line module as the current one: that is the answer.
828
+ if (highlightActiveModule) {
829
+ const leaf = items.findIndex(item => !item.subModules?.length && !!item.isActive);
830
+ if (leaf >= 0)
831
+ return { item: leaf, sub: null, subPage: null };
832
+ }
833
+ let best = null;
834
+ const consider = (c) => {
835
+ if (!best) {
836
+ best = c;
837
+ return;
838
+ }
839
+ if (c.length !== best.length) {
840
+ if (c.length > best.length)
841
+ best = c;
842
+ return;
843
+ }
844
+ const cActive = !!items[c.item].isActive;
845
+ const bActive = !!items[best.item].isActive;
846
+ if (cActive !== bActive) {
847
+ if (cActive)
848
+ best = c;
849
+ return;
850
+ }
851
+ if (c.sub !== null && best.sub === null)
852
+ best = c;
853
+ };
854
+ items.forEach((item, i) => {
855
+ const itemUrl = buildUrlWithCode(code, item.url);
856
+ const subs = item.subModules ?? [];
857
+ // The row itself: only on its own URL. A one-line item under
858
+ // highlightActiveModule is lit by isActive alone (handled above).
859
+ if (!(highlightActiveModule && subs.length === 0)) {
860
+ const own = workspace ? samePage(path, itemUrl) : path === itemUrl;
861
+ if (own)
862
+ consider({ item: i, sub: null, subPage: null, length: strip(itemUrl).length });
863
+ }
864
+ subs.forEach((sub, j) => {
865
+ // The sub-item counts by its own URL or by the longest of its pages.
866
+ let subPage = null;
867
+ let pageLength = -1;
868
+ (sub.subPages ?? []).forEach((page, k) => {
869
+ const l = coverLength(path, page.url, code);
870
+ if (l > pageLength) {
871
+ pageLength = l;
872
+ subPage = k;
873
+ }
874
+ });
875
+ const length = Math.max(coverLength(path, sub.url, code), pageLength);
876
+ if (length >= 0)
877
+ consider({ item: i, sub: j, subPage, length });
878
+ });
879
+ });
880
+ const found = best;
881
+ return found ? { item: found.item, sub: found.sub, subPage: found.subPage } : null;
757
882
  }
758
883
 
759
884
  function NavTools({ tools, closeOnNavigation = false }) {
@@ -1240,7 +1365,7 @@ function platformApiBase(app) {
1240
1365
  * This package's version, attached to help messages so the back office can
1241
1366
  * tell which shell a report came from. Keep in step with package.json.
1242
1367
  */
1243
- const SHELL_VERSION = "0.1.53";
1368
+ const SHELL_VERSION = "0.1.57";
1244
1369
 
1245
1370
  const HELP_APP_LABEL = {
1246
1371
  platform: "แพลตฟอร์ม",
@@ -1788,31 +1913,6 @@ function buildSystemLinks({ orgCode, activeApp, features, }) {
1788
1913
  }));
1789
1914
  }
1790
1915
 
1791
- // Helper function to check if pathname matches a URL pattern
1792
- // This prevents false matches like /reports/sales matching /sales
1793
- // Handles paths with or without [code] prefix
1794
- function isPathActive(pathname, url) {
1795
- // Exact match
1796
- if (pathname === url) {
1797
- return true;
1798
- }
1799
- // Check if pathname starts with url + "/" to ensure exact segment matching
1800
- // This prevents /reports/sales from matching /sales
1801
- // Example: /org123/reports/sales should NOT match /org123/sales
1802
- // But /org123/sales/overview SHOULD match /org123/sales
1803
- if (pathname.startsWith(url + "/")) {
1804
- return true;
1805
- }
1806
- return false;
1807
- }
1808
-
1809
- /** "/O1/" and "/O1" are the same page; a query string never is. */
1810
- function samePage(pathname, url) {
1811
- if (url.includes("?"))
1812
- return false;
1813
- const strip = (p) => (p.length > 1 ? p.replace(/\/+$/, "") : p);
1814
- return strip(pathname) === strip(url);
1815
- }
1816
1916
  function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, chrome = "brand", highlightActiveModule = false, }) {
1817
1917
  const workspace = chrome === "workspace";
1818
1918
  const params = useParams();
@@ -1823,32 +1923,24 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
1823
1923
  setOpen(false);
1824
1924
  };
1825
1925
  const code = orgCode ?? params?.code;
1826
- // Which module (and sub-item) the current page belongs to.
1827
- const states = items.map(item => {
1926
+ // The one entry that stands for this page — the longest matching URL
1927
+ // across every group, not per group (which lit /project and
1928
+ // /project/schedule together).
1929
+ const active = resolveActiveNav(items, pathname, {
1930
+ code,
1931
+ workspace,
1932
+ highlightActiveModule,
1933
+ });
1934
+ const states = items.map((item, i) => {
1828
1935
  const itemUrl = buildUrlWithCode(code, item.url);
1829
- // Most specific (longest) matching sub-item URL so only one link is active.
1830
- const activeSubItemUrl = item.subModules?.reduce((best, subItem) => {
1831
- const subItemUrl = buildUrlWithCode(code, subItem.url);
1832
- const matches = isPathActive(pathname, subItemUrl) ||
1833
- isPathActive(pathname, subItem.url);
1834
- const subPageMatches = subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
1835
- isPathActive(pathname, subPage.url));
1836
- if (!matches && !subPageMatches)
1837
- return best;
1838
- const candidateUrl = subItemUrl;
1839
- if (!best)
1840
- return candidateUrl;
1841
- return candidateUrl.length > best.length ? candidateUrl : best;
1842
- }, null) ?? null;
1843
- const hasActiveSubItem = !!activeSubItemUrl ||
1844
- !!item.subModules?.some(subItem => subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
1845
- isPathActive(pathname, subPage.url)));
1846
- // Parent only active when path matches its URL exactly AND no child
1847
- // is active (so parent+child don't both light up).
1848
- const isItemActive = (highlightActiveModule && !item.subModules?.length && !!item.isActive) ||
1849
- ((workspace ? samePage(pathname, itemUrl) : pathname === itemUrl) &&
1850
- !hasActiveSubItem);
1851
- return { itemUrl, activeSubItemUrl, hasActiveSubItem, isItemActive };
1936
+ const mine = active?.item === i ? active : null;
1937
+ return {
1938
+ itemUrl,
1939
+ activeSub: mine?.sub ?? null,
1940
+ activeSubPage: mine?.subPage ?? null,
1941
+ hasActiveSubItem: mine?.sub != null,
1942
+ isItemActive: !!mine && mine.sub === null,
1943
+ };
1852
1944
  });
1853
1945
  // A group opens when its page becomes current and otherwise stays as the
1854
1946
  // user left it. defaultOpen alone decided once, on the first render — and
@@ -1886,7 +1978,7 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
1886
1978
  item.group &&
1887
1979
  item.group !== items[index - 1]?.group ? (jsx("li", { className: "list-none px-3 pb-2.5 text-xs text-muted-foreground" +
1888
1980
  (index > 0 ? " pt-5" : ""), children: item.group }, `group-${item.id}`)) : null;
1889
- const { itemUrl, activeSubItemUrl, isItemActive } = states[index];
1981
+ const { itemUrl, activeSub, activeSubPage, isItemActive } = states[index];
1890
1982
  const IconComp = item.icon;
1891
1983
  const workspaceButton = workspace
1892
1984
  ? "h-auto gap-3 rounded-[10px] px-3 py-2.5 text-xs " +
@@ -1900,9 +1992,9 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
1900
1992
  workspaceButton, children: item.subModules && item.subModules.length > 0 ? (jsxs(Fragment$1, { children: [icon, jsx("span", { id: item.id, children: item.titleTh }), jsx(ChevronRight, { className: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" })] })) : (jsxs(Link, { href: itemUrl, prefetch: false, className: "flex items-center w-full min-w-0 flex-1 " +
1901
1993
  (workspace ? "gap-3" : "gap-2"), onClick: handleNavLinkClick, "aria-current": isItemActive ? "page" : undefined, children: [icon, jsx("span", { id: item.id, className: "flex-1 truncate", title: item.titleTh, children: item.titleTh }), badge] })) }) }), item.subModules && item.subModules.length > 0 && (jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { className: workspace
1902
1994
  ? "mx-0 mt-1 mb-3 ml-[21px] gap-0.5 border-l border-sidebar-border pl-2.5 [&_a]:h-auto [&_a]:gap-[11px] [&_a]:px-3 [&_a]:py-2 [&_a]:text-xs [&_svg]:!size-[17px]"
1903
- : undefined, children: item.subModules.map(subItem => {
1995
+ : undefined, children: item.subModules.map((subItem, subIndex) => {
1904
1996
  const subItemUrl = buildUrlWithCode(code, subItem.url);
1905
- const isSubItemActive = subItemUrl === activeSubItemUrl;
1997
+ const isSubItemActive = activeSub === subIndex;
1906
1998
  const SubIcon = subItem.icon;
1907
1999
  const approvalCount = approvalCounts && subItem.approvalCountKey
1908
2000
  ? (approvalCounts[subItem.approvalCountKey] ?? 0)
@@ -1910,10 +2002,10 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
1910
2002
  if (subItem.isCollapsible &&
1911
2003
  subItem.subPages &&
1912
2004
  subItem.subPages.length > 0) {
1913
- const hasActiveSubPage = subItem.subPages.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)));
1914
- return (jsx(Collapsible, { asChild: true, defaultOpen: isSubItemActive || hasActiveSubPage, className: "group/sub-collapsible", children: jsxs(SidebarMenuSubItem, { children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(SidebarMenuSubButton, { className: "w-full", isActive: isSubItemActive || hasActiveSubPage, children: [SubIcon && (jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), jsx(ChevronDown, { className: "ml-auto size-4 transition-transform duration-200 group-data-[state=open]/sub-collapsible:rotate-180" })] }) }), jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { className: "ml-4", children: subItem.subPages.map(subPage => {
2005
+ return (jsx(Collapsible, { asChild: true, defaultOpen: isSubItemActive, className: "group/sub-collapsible", children: jsxs(SidebarMenuSubItem, { children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(SidebarMenuSubButton, { className: "w-full", isActive: isSubItemActive, children: [SubIcon && (jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), jsx(ChevronDown, { className: "ml-auto size-4 transition-transform duration-200 group-data-[state=open]/sub-collapsible:rotate-180" })] }) }), jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { className: "ml-4", children: subItem.subPages.map((subPage, subPageIndex) => {
1915
2006
  const subPageUrl = buildUrlWithCode(code, subPage.url);
1916
- const isSubPageActive = isPathActive(pathname, subPageUrl);
2007
+ const isSubPageActive = isSubItemActive &&
2008
+ activeSubPage === subPageIndex;
1917
2009
  const SubPageIcon = subPage.icon;
1918
2010
  return (jsx(SidebarMenuSubItem, { children: jsx(SidebarMenuSubButton, { asChild: true, className: `font-thai w-full text-sm ${isSubPageActive
1919
2011
  ? "text-sidebar-accent-foreground font-medium"
@@ -3366,6 +3458,24 @@ function PermissionProvider({ children, usePermission, fallbackRulesMap, }) {
3366
3458
  return jsx(Fragment$1, { children: children });
3367
3459
  }
3368
3460
 
3461
+ // Helper function to check if pathname matches a URL pattern
3462
+ // This prevents false matches like /reports/sales matching /sales
3463
+ // Handles paths with or without [code] prefix
3464
+ function isPathActive(pathname, url) {
3465
+ // Exact match
3466
+ if (pathname === url) {
3467
+ return true;
3468
+ }
3469
+ // Check if pathname starts with url + "/" to ensure exact segment matching
3470
+ // This prevents /reports/sales from matching /sales
3471
+ // Example: /org123/reports/sales should NOT match /org123/sales
3472
+ // But /org123/sales/overview SHOULD match /org123/sales
3473
+ if (pathname.startsWith(url + "/")) {
3474
+ return true;
3475
+ }
3476
+ return false;
3477
+ }
3478
+
3369
3479
  /** Cookie name for the cross-subdomain theme choice. */
3370
3480
  const THEME_COOKIE = "erp-theme";
3371
3481
  /**
@@ -3611,5 +3721,5 @@ const getAxiosErrorMessage = (error) => {
3611
3721
  return String(error);
3612
3722
  };
3613
3723
 
3614
- export { AccessDenied, ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, EMPLOYEE_RECORD_OK_EVENT, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EmployeeRequiredGate, FeatureFlagsProvider, HeaderBackground, HelpButton, HelpDialogHost, NavMain, NavTools, NavUser, OWNER_CHIP, OrganizationSwitcher, PLAN_FEATURES, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SHELL_VERSION, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarHelpCard, SidebarHelpLink, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, WORKSPACE_HEADER_VARS, WORKSPACE_LIGHT_HEADER_VARS, WorkspaceBrand, instance as axiosInstance, buildSystemLinks, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, closeHelp, decodeJWT, describeAttached, describeBrowser, detectHelpApp, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, hasPlanFeature, isLikelyTransientNetworkError, isPathActive, myRolesQueryKey, normalizePathnameForPermissionRules, openHelp, parseMyRoles, resolveUiRoutePermission, roleSummary, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useMyRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
3724
+ export { AccessDenied, ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, EMPLOYEE_RECORD_OK_EVENT, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EmployeeRequiredGate, FeatureFlagsProvider, HeaderBackground, HelpButton, HelpDialogHost, NavMain, NavTools, NavUser, OWNER_CHIP, OrganizationSwitcher, PLAN_FEATURES, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SHELL_VERSION, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarHelpCard, SidebarHelpLink, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, WORKSPACE_HEADER_VARS, WORKSPACE_LIGHT_HEADER_VARS, WorkspaceBrand, instance as axiosInstance, buildSystemLinks, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, closeHelp, decodeJWT, describeAttached, describeBrowser, detectHelpApp, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, hasPlanFeature, isLikelyTransientNetworkError, isPathActive, myRolesQueryKey, normalizePathnameForPermissionRules, openHelp, parseMyRoles, resolveActiveNav, resolveUiRoutePermission, roleSummary, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useMyRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
3615
3725
  //# sourceMappingURL=index.esm.js.map