@eintrek/erp-shell 0.1.56 → 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 +1 -1
- package/dist/index.esm.js +192 -92
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +192 -91
- package/dist/index.js.map +1 -1
- package/dist/navigation/navigation-data-helpers.d.ts +45 -3
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
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,43 +703,66 @@ 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
|
-
*
|
|
708
|
-
*
|
|
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
|
|
711
|
-
|
|
712
|
-
|
|
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
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
-
|
|
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
|
-
|
|
731
|
-
|
|
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.
|
|
@@ -761,6 +784,102 @@ function buildUrlWithCode(code, url) {
|
|
|
761
784
|
const base = cleanPath ? `/${code}/${cleanPath}` : `/${code}`;
|
|
762
785
|
return `${base}${query}${hash}`;
|
|
763
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;
|
|
882
|
+
}
|
|
764
883
|
|
|
765
884
|
function NavTools({ tools, closeOnNavigation = false }) {
|
|
766
885
|
const pathname = usePathname();
|
|
@@ -1246,7 +1365,7 @@ function platformApiBase(app) {
|
|
|
1246
1365
|
* This package's version, attached to help messages so the back office can
|
|
1247
1366
|
* tell which shell a report came from. Keep in step with package.json.
|
|
1248
1367
|
*/
|
|
1249
|
-
const SHELL_VERSION = "0.1.
|
|
1368
|
+
const SHELL_VERSION = "0.1.57";
|
|
1250
1369
|
|
|
1251
1370
|
const HELP_APP_LABEL = {
|
|
1252
1371
|
platform: "แพลตฟอร์ม",
|
|
@@ -1794,31 +1913,6 @@ function buildSystemLinks({ orgCode, activeApp, features, }) {
|
|
|
1794
1913
|
}));
|
|
1795
1914
|
}
|
|
1796
1915
|
|
|
1797
|
-
// Helper function to check if pathname matches a URL pattern
|
|
1798
|
-
// This prevents false matches like /reports/sales matching /sales
|
|
1799
|
-
// Handles paths with or without [code] prefix
|
|
1800
|
-
function isPathActive(pathname, url) {
|
|
1801
|
-
// Exact match
|
|
1802
|
-
if (pathname === url) {
|
|
1803
|
-
return true;
|
|
1804
|
-
}
|
|
1805
|
-
// Check if pathname starts with url + "/" to ensure exact segment matching
|
|
1806
|
-
// This prevents /reports/sales from matching /sales
|
|
1807
|
-
// Example: /org123/reports/sales should NOT match /org123/sales
|
|
1808
|
-
// But /org123/sales/overview SHOULD match /org123/sales
|
|
1809
|
-
if (pathname.startsWith(url + "/")) {
|
|
1810
|
-
return true;
|
|
1811
|
-
}
|
|
1812
|
-
return false;
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
/** "/O1/" and "/O1" are the same page; a query string never is. */
|
|
1816
|
-
function samePage(pathname, url) {
|
|
1817
|
-
if (url.includes("?"))
|
|
1818
|
-
return false;
|
|
1819
|
-
const strip = (p) => (p.length > 1 ? p.replace(/\/+$/, "") : p);
|
|
1820
|
-
return strip(pathname) === strip(url);
|
|
1821
|
-
}
|
|
1822
1916
|
function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, chrome = "brand", highlightActiveModule = false, }) {
|
|
1823
1917
|
const workspace = chrome === "workspace";
|
|
1824
1918
|
const params = useParams();
|
|
@@ -1829,36 +1923,24 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
|
|
|
1829
1923
|
setOpen(false);
|
|
1830
1924
|
};
|
|
1831
1925
|
const code = orgCode ?? params?.code;
|
|
1832
|
-
//
|
|
1833
|
-
|
|
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) => {
|
|
1834
1935
|
const itemUrl = buildUrlWithCode(code, item.url);
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
return best;
|
|
1844
|
-
const candidateUrl = subItemUrl;
|
|
1845
|
-
if (!best)
|
|
1846
|
-
return candidateUrl;
|
|
1847
|
-
return candidateUrl.length > best.length ? candidateUrl : best;
|
|
1848
|
-
}, null) ?? null;
|
|
1849
|
-
const hasActiveSubItem = !!activeSubItemUrl ||
|
|
1850
|
-
!!item.subModules?.some(subItem => subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
|
|
1851
|
-
isPathActive(pathname, subPage.url)));
|
|
1852
|
-
// Parent only active when path matches its URL exactly AND no child
|
|
1853
|
-
// is active (so parent+child don't both light up). When the app opts
|
|
1854
|
-
// into highlightActiveModule, leaf items trust `item.isActive` alone —
|
|
1855
|
-
// needed for query-only hub actions (`/?start=1`) that share a path
|
|
1856
|
-
// with another leaf (e.g. "แอปของฉัน" at `/`).
|
|
1857
|
-
const isItemActive = highlightActiveModule && !item.subModules?.length
|
|
1858
|
-
? !!item.isActive
|
|
1859
|
-
: (workspace ? samePage(pathname, itemUrl) : pathname === itemUrl) &&
|
|
1860
|
-
!hasActiveSubItem;
|
|
1861
|
-
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
|
+
};
|
|
1862
1944
|
});
|
|
1863
1945
|
// A group opens when its page becomes current and otherwise stays as the
|
|
1864
1946
|
// user left it. defaultOpen alone decided once, on the first render — and
|
|
@@ -1896,7 +1978,7 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
|
|
|
1896
1978
|
item.group &&
|
|
1897
1979
|
item.group !== items[index - 1]?.group ? (jsx("li", { className: "list-none px-3 pb-2.5 text-xs text-muted-foreground" +
|
|
1898
1980
|
(index > 0 ? " pt-5" : ""), children: item.group }, `group-${item.id}`)) : null;
|
|
1899
|
-
const { itemUrl,
|
|
1981
|
+
const { itemUrl, activeSub, activeSubPage, isItemActive } = states[index];
|
|
1900
1982
|
const IconComp = item.icon;
|
|
1901
1983
|
const workspaceButton = workspace
|
|
1902
1984
|
? "h-auto gap-3 rounded-[10px] px-3 py-2.5 text-xs " +
|
|
@@ -1910,9 +1992,9 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
|
|
|
1910
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 " +
|
|
1911
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
|
|
1912
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]"
|
|
1913
|
-
: undefined, children: item.subModules.map(subItem => {
|
|
1995
|
+
: undefined, children: item.subModules.map((subItem, subIndex) => {
|
|
1914
1996
|
const subItemUrl = buildUrlWithCode(code, subItem.url);
|
|
1915
|
-
const isSubItemActive =
|
|
1997
|
+
const isSubItemActive = activeSub === subIndex;
|
|
1916
1998
|
const SubIcon = subItem.icon;
|
|
1917
1999
|
const approvalCount = approvalCounts && subItem.approvalCountKey
|
|
1918
2000
|
? (approvalCounts[subItem.approvalCountKey] ?? 0)
|
|
@@ -1920,10 +2002,10 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
|
|
|
1920
2002
|
if (subItem.isCollapsible &&
|
|
1921
2003
|
subItem.subPages &&
|
|
1922
2004
|
subItem.subPages.length > 0) {
|
|
1923
|
-
|
|
1924
|
-
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) => {
|
|
1925
2006
|
const subPageUrl = buildUrlWithCode(code, subPage.url);
|
|
1926
|
-
const isSubPageActive =
|
|
2007
|
+
const isSubPageActive = isSubItemActive &&
|
|
2008
|
+
activeSubPage === subPageIndex;
|
|
1927
2009
|
const SubPageIcon = subPage.icon;
|
|
1928
2010
|
return (jsx(SidebarMenuSubItem, { children: jsx(SidebarMenuSubButton, { asChild: true, className: `font-thai w-full text-sm ${isSubPageActive
|
|
1929
2011
|
? "text-sidebar-accent-foreground font-medium"
|
|
@@ -3376,6 +3458,24 @@ function PermissionProvider({ children, usePermission, fallbackRulesMap, }) {
|
|
|
3376
3458
|
return jsx(Fragment$1, { children: children });
|
|
3377
3459
|
}
|
|
3378
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
|
+
|
|
3379
3479
|
/** Cookie name for the cross-subdomain theme choice. */
|
|
3380
3480
|
const THEME_COOKIE = "erp-theme";
|
|
3381
3481
|
/**
|
|
@@ -3621,5 +3721,5 @@ const getAxiosErrorMessage = (error) => {
|
|
|
3621
3721
|
return String(error);
|
|
3622
3722
|
};
|
|
3623
3723
|
|
|
3624
|
-
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 };
|
|
3625
3725
|
//# sourceMappingURL=index.esm.js.map
|