@eintrek/erp-shell 0.1.54 → 0.1.56
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
|
@@ -762,6 +762,7 @@ function getActiveSubModule(pathname, modules) {
|
|
|
762
762
|
}
|
|
763
763
|
/** Prepend `code` (org code) to a relative URL so `/sales` → `/O25.../sales`.
|
|
764
764
|
* Absolute `http(s)://` URLs are left intact; optional `{code}` placeholder is replaced.
|
|
765
|
+
* Query/hash are preserved: `/?start=1` → `/O25...?start=1` (not `/O25.../?start=1`).
|
|
765
766
|
*/
|
|
766
767
|
function buildUrlWithCode(code, url) {
|
|
767
768
|
if (/^https?:\/\//i.test(url)) {
|
|
@@ -771,8 +772,13 @@ function buildUrlWithCode(code, url) {
|
|
|
771
772
|
}
|
|
772
773
|
if (!code)
|
|
773
774
|
return url;
|
|
774
|
-
const
|
|
775
|
-
|
|
775
|
+
const match = url.match(/^([^?#]*)(\?[^#]*)?(#.*)?$/);
|
|
776
|
+
const path = match?.[1] ?? url;
|
|
777
|
+
const query = match?.[2] ?? "";
|
|
778
|
+
const hash = match?.[3] ?? "";
|
|
779
|
+
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
780
|
+
const base = cleanPath ? `/${code}/${cleanPath}` : `/${code}`;
|
|
781
|
+
return `${base}${query}${hash}`;
|
|
776
782
|
}
|
|
777
783
|
|
|
778
784
|
function NavTools({ tools, closeOnNavigation = false }) {
|
|
@@ -955,6 +961,13 @@ function cardRoleLine(roles, limit = CARD_ROLE_LIMIT) {
|
|
|
955
961
|
const names = roles.map(r => r.name_th);
|
|
956
962
|
return { shown: names.slice(0, limit), more: Math.max(0, names.length - limit) };
|
|
957
963
|
}
|
|
964
|
+
/**
|
|
965
|
+
* Workspace profile card: owner chip + "อีก N บทบาท", without listing role
|
|
966
|
+
* names on the trigger (full names stay in the account menu).
|
|
967
|
+
*/
|
|
968
|
+
function workspaceRoleExtra(my) {
|
|
969
|
+
return my.roles.length;
|
|
970
|
+
}
|
|
958
971
|
/** Everything, for the tooltip: "เจ้าของ · ผู้ดูแลสูงสุด · พนักงานทั่วไป". */
|
|
959
972
|
function roleSummary(my) {
|
|
960
973
|
return [...(my.isOwner ? [OWNER_CHIP] : []), ...my.roles.map(r => r.name_th)].join(" · ");
|
|
@@ -1494,11 +1507,16 @@ function OwnerChip({ size = "card" }) {
|
|
|
1494
1507
|
: "inline-flex shrink-0 items-center rounded-full bg-primary/10 px-1.5 text-xs font-semibold leading-[17px] text-primary", children: OWNER_CHIP }));
|
|
1495
1508
|
}
|
|
1496
1509
|
/**
|
|
1497
|
-
*
|
|
1498
|
-
*
|
|
1499
|
-
* that is wider than the card on its own; the tooltip has everything.
|
|
1510
|
+
* Brand card: owner chip, then two role names and a count of the rest.
|
|
1511
|
+
* Workspace card: owner chip + "อีก N บทบาท" (names live in the menu).
|
|
1500
1512
|
*/
|
|
1501
|
-
function CardRoles({ my, className }) {
|
|
1513
|
+
function CardRoles({ my, className, variant = "brand", }) {
|
|
1514
|
+
if (variant === "workspace") {
|
|
1515
|
+
const extra = workspaceRoleExtra(my);
|
|
1516
|
+
return (jsxRuntime.jsxs("span", { className: `flex min-w-0 flex-wrap items-center gap-1.5 ${className}`, title: roleSummary(my), children: [my.isOwner ? jsxRuntime.jsx(OwnerChip, {}) : null, !my.isOwner && my.roles[0] ? (jsxRuntime.jsx("span", { className: "truncate", children: my.roles[0].name_th })) : null, extra > 0 && (my.isOwner || my.roles.length > 1) ? (jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: my.isOwner
|
|
1517
|
+
? `อีก ${extra} บทบาท`
|
|
1518
|
+
: `อีก ${extra - 1} บทบาท` })) : null] }));
|
|
1519
|
+
}
|
|
1502
1520
|
const { shown, more } = cardRoleLine(my.roles);
|
|
1503
1521
|
return (jsxRuntime.jsxs("span", { className: `flex min-w-0 flex-wrap items-center gap-x-1 gap-y-0.5 ${className}`, title: roleSummary(my), children: [my.isOwner ? jsxRuntime.jsx(OwnerChip, {}) : null, shown.map((name, i) => {
|
|
1504
1522
|
const last = i === shown.length - 1;
|
|
@@ -1656,8 +1674,8 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
|
|
|
1656
1674
|
openHelp();
|
|
1657
1675
|
}, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [jsxRuntime.jsx(lucideReact.CircleHelp, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E0A\u0E48\u0E27\u0E22\u0E40\u0E2B\u0E25\u0E37\u0E2D" })] })), jsxRuntime.jsx("button", { onClick: handleSignOut, disabled: isSigningOut, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed mt-1", children: isSigningOut ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A..." })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.LogOut, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })] })) })] })] })) : null;
|
|
1658
1676
|
if (variant === "workspace") {
|
|
1659
|
-
return (jsxRuntime.jsx("div", { className: "mt-
|
|
1660
|
-
? "absolute right-
|
|
1677
|
+
return (jsxRuntime.jsx("div", { className: "mt-2.5 border-t border-sidebar-border pt-3.5", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), "aria-expanded": isOpen, className: `relative flex w-full gap-2.5 rounded-[14px] border border-transparent bg-sidebar-accent/50 px-2.5 py-2.5 text-left transition-colors hover:border-sidebar-border hover:bg-sidebar-accent/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring ${cardTall ? "items-start" : "items-center"}`, children: [jsxRuntime.jsxs(erpTheme.Avatar, { className: "size-[38px] shrink-0 rounded-full border border-sidebar-border", children: [user.avatar ? (jsxRuntime.jsx(erpTheme.AvatarImage, { src: user.avatar, alt: user.name })) : null, jsxRuntime.jsx(erpTheme.AvatarFallback, { className: "rounded-full bg-primary/10 text-[17px] font-medium text-primary", children: getInitials(user.name) })] }), jsxRuntime.jsxs("span", { className: "grid min-w-0 flex-1 gap-0.5 leading-tight", children: [jsxRuntime.jsx("strong", { className: `truncate text-[13px] font-semibold ${cardTall ? "pr-5" : ""}`, title: user.name, children: user.name }), jsxRuntime.jsx("small", { className: "truncate text-[11px] text-muted-foreground", title: user.email, children: user.email }), myRoles ? (jsxRuntime.jsx(CardRoles, { my: myRoles, variant: "workspace", className: "mt-0.5 text-[11px] font-medium leading-[17px] text-sidebar-foreground/80" })) : rolesLoading ? (jsxRuntime.jsx("span", { className: "my-[3px] block h-[11px] w-20 animate-pulse rounded bg-sidebar-accent" })) : null] }), jsxRuntime.jsx(lucideReact.ChevronUp, { className: cardTall
|
|
1678
|
+
? "absolute right-2.5 top-3 size-3.5 text-muted-foreground"
|
|
1661
1679
|
: "size-3.5 shrink-0 text-muted-foreground" })] }), typeof window !== "undefined" &&
|
|
1662
1680
|
reactDom.createPortal(dropdownContent, document.body)] }) }));
|
|
1663
1681
|
}
|
|
@@ -1851,10 +1869,14 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
|
|
|
1851
1869
|
!!item.subModules?.some(subItem => subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
|
|
1852
1870
|
isPathActive(pathname, subPage.url)));
|
|
1853
1871
|
// Parent only active when path matches its URL exactly AND no child
|
|
1854
|
-
// is active (so parent+child don't both light up).
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1872
|
+
// is active (so parent+child don't both light up). When the app opts
|
|
1873
|
+
// into highlightActiveModule, leaf items trust `item.isActive` alone —
|
|
1874
|
+
// needed for query-only hub actions (`/?start=1`) that share a path
|
|
1875
|
+
// with another leaf (e.g. "แอปของฉัน" at `/`).
|
|
1876
|
+
const isItemActive = highlightActiveModule && !item.subModules?.length
|
|
1877
|
+
? !!item.isActive
|
|
1878
|
+
: (workspace ? samePage(pathname, itemUrl) : pathname === itemUrl) &&
|
|
1879
|
+
!hasActiveSubItem;
|
|
1858
1880
|
return { itemUrl, activeSubItemUrl, hasActiveSubItem, isItemActive };
|
|
1859
1881
|
});
|
|
1860
1882
|
// A group opens when its page becomes current and otherwise stays as the
|
|
@@ -2022,7 +2044,7 @@ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, clo
|
|
|
2022
2044
|
if (!open)
|
|
2023
2045
|
return null;
|
|
2024
2046
|
if (chrome === "workspace") {
|
|
2025
|
-
return (jsxRuntime.jsxs("div", { className: erpTheme.cn("flex h-screen w-[
|
|
2047
|
+
return (jsxRuntime.jsxs("div", { className: erpTheme.cn("flex h-screen w-[264px] shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground",
|
|
2026
2048
|
// As a drawer (≤1024px) it floats over the page and needs the lift.
|
|
2027
2049
|
"max-[1024px]:shadow-2xl", className), children: [header ? jsxRuntime.jsx("div", { className: "shrink-0 px-[18px] pt-8 pb-6", children: header }) : null, jsxRuntime.jsxs(SidebarContent, { className: "flex-1 overflow-y-auto px-[18px]", children: [jsxRuntime.jsx(NavMain, { items: modulesWithActiveState, orgCode: orgCode, approvalCounts: approvalCounts, closeOnNavigation: closeOnNavigation, chrome: "workspace", highlightActiveModule: highlightActiveModule }), jsxRuntime.jsx(NavTools, { tools: tools, closeOnNavigation: closeOnNavigation })] }), bottom ? jsxRuntime.jsx("div", { className: "shrink-0 px-[18px] pt-6", children: bottom }) : null, jsxRuntime.jsx("div", { className: "shrink-0 px-[18px] pb-3", children: footer })] }));
|
|
2028
2050
|
}
|
|
@@ -3072,7 +3094,7 @@ function cx(...parts) {
|
|
|
3072
3094
|
* name, and a spaced caption beneath it.
|
|
3073
3095
|
*/
|
|
3074
3096
|
function WorkspaceBrand({ name, caption, mark, href, compact = false, className, }) {
|
|
3075
|
-
const body = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: erpTheme.cn("grid shrink-0 place-items-center overflow-hidden rounded-[
|
|
3097
|
+
const body = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: erpTheme.cn("grid shrink-0 place-items-center overflow-hidden rounded-[12px] bg-transparent [&>img]:size-full [&>img]:object-contain", compact ? "size-9 rounded-[11px]" : "size-11"), children: mark }), compact ? (jsxRuntime.jsx("span", { className: "sr-only", children: name })) : (jsxRuntime.jsxs("span", { className: "min-w-0", children: [jsxRuntime.jsx("span", { className: "block truncate text-2xl leading-tight font-medium tracking-[-0.05em]", children: name }), caption ? (jsxRuntime.jsx("small", { className: "mt-0.5 block truncate text-[9px] font-semibold tracking-[0.3em] text-muted-foreground", children: caption })) : null] }))] }));
|
|
3076
3098
|
const cls = erpTheme.cn("flex items-center gap-[11px] rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", !compact && "px-4", className);
|
|
3077
3099
|
return href ? (jsxRuntime.jsx(Link, { href: href, className: cls, "aria-label": compact ? name : undefined, children: body })) : (jsxRuntime.jsx("div", { className: cls, children: body }));
|
|
3078
3100
|
}
|