@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.esm.js
CHANGED
|
@@ -5,7 +5,7 @@ import { useSession, signOut } from 'next-auth/react';
|
|
|
5
5
|
import { useQueryClient, useQuery, QueryClientContext, QueryClient, MutationCache, QueryCache, QueryClientProvider } from '@tanstack/react-query';
|
|
6
6
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
7
7
|
import { cn, Button, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage, Avatar, AvatarImage, AvatarFallback, Collapsible, CollapsibleTrigger, CollapsibleContent, Badge, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator, Skeleton, Dialog, DialogContent, DialogHeader, DialogTitle, Label, Textarea, DialogFooter, Card, CardHeader, CardContent } from '@eintrek/erp-theme';
|
|
8
|
-
import { PanelLeftClose, Menu, ChevronRight, Building2, Palette, ChevronUp, Sun, Moon, Monitor, Globe, User, LayoutGrid, ExternalLink, CircleHelp, Loader2, LogOut,
|
|
8
|
+
import { PanelLeftClose, Menu, ChevronRight, Building2, Palette, ChevronUp, Sun, Moon, Monitor, Globe, User, LayoutGrid, ExternalLink, CircleHelp, Loader2, LogOut, Calculator, Warehouse, FolderKanban, Users, ChevronDown, Plus, ChevronsUpDown, ImagePlus, X, CircleCheck, ArrowLeft, ArrowRight, Compass, ArrowUpRight, Ban, Home, Headset, ContactRound, UserPlus } from 'lucide-react';
|
|
9
9
|
import { create } from 'zustand';
|
|
10
10
|
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
11
11
|
import { useRouter, usePathname, useParams } from 'next/navigation';
|
|
@@ -743,6 +743,7 @@ function getActiveSubModule(pathname, modules) {
|
|
|
743
743
|
}
|
|
744
744
|
/** Prepend `code` (org code) to a relative URL so `/sales` → `/O25.../sales`.
|
|
745
745
|
* Absolute `http(s)://` URLs are left intact; optional `{code}` placeholder is replaced.
|
|
746
|
+
* Query/hash are preserved: `/?start=1` → `/O25...?start=1` (not `/O25.../?start=1`).
|
|
746
747
|
*/
|
|
747
748
|
function buildUrlWithCode(code, url) {
|
|
748
749
|
if (/^https?:\/\//i.test(url)) {
|
|
@@ -752,8 +753,13 @@ function buildUrlWithCode(code, url) {
|
|
|
752
753
|
}
|
|
753
754
|
if (!code)
|
|
754
755
|
return url;
|
|
755
|
-
const
|
|
756
|
-
|
|
756
|
+
const match = url.match(/^([^?#]*)(\?[^#]*)?(#.*)?$/);
|
|
757
|
+
const path = match?.[1] ?? url;
|
|
758
|
+
const query = match?.[2] ?? "";
|
|
759
|
+
const hash = match?.[3] ?? "";
|
|
760
|
+
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
761
|
+
const base = cleanPath ? `/${code}/${cleanPath}` : `/${code}`;
|
|
762
|
+
return `${base}${query}${hash}`;
|
|
757
763
|
}
|
|
758
764
|
|
|
759
765
|
function NavTools({ tools, closeOnNavigation = false }) {
|
|
@@ -936,6 +942,13 @@ function cardRoleLine(roles, limit = CARD_ROLE_LIMIT) {
|
|
|
936
942
|
const names = roles.map(r => r.name_th);
|
|
937
943
|
return { shown: names.slice(0, limit), more: Math.max(0, names.length - limit) };
|
|
938
944
|
}
|
|
945
|
+
/**
|
|
946
|
+
* Workspace profile card: owner chip + "อีก N บทบาท", without listing role
|
|
947
|
+
* names on the trigger (full names stay in the account menu).
|
|
948
|
+
*/
|
|
949
|
+
function workspaceRoleExtra(my) {
|
|
950
|
+
return my.roles.length;
|
|
951
|
+
}
|
|
939
952
|
/** Everything, for the tooltip: "เจ้าของ · ผู้ดูแลสูงสุด · พนักงานทั่วไป". */
|
|
940
953
|
function roleSummary(my) {
|
|
941
954
|
return [...(my.isOwner ? [OWNER_CHIP] : []), ...my.roles.map(r => r.name_th)].join(" · ");
|
|
@@ -1475,11 +1488,16 @@ function OwnerChip({ size = "card" }) {
|
|
|
1475
1488
|
: "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 }));
|
|
1476
1489
|
}
|
|
1477
1490
|
/**
|
|
1478
|
-
*
|
|
1479
|
-
*
|
|
1480
|
-
* that is wider than the card on its own; the tooltip has everything.
|
|
1491
|
+
* Brand card: owner chip, then two role names and a count of the rest.
|
|
1492
|
+
* Workspace card: owner chip + "อีก N บทบาท" (names live in the menu).
|
|
1481
1493
|
*/
|
|
1482
|
-
function CardRoles({ my, className }) {
|
|
1494
|
+
function CardRoles({ my, className, variant = "brand", }) {
|
|
1495
|
+
if (variant === "workspace") {
|
|
1496
|
+
const extra = workspaceRoleExtra(my);
|
|
1497
|
+
return (jsxs("span", { className: `flex min-w-0 flex-wrap items-center gap-1.5 ${className}`, title: roleSummary(my), children: [my.isOwner ? jsx(OwnerChip, {}) : null, !my.isOwner && my.roles[0] ? (jsx("span", { className: "truncate", children: my.roles[0].name_th })) : null, extra > 0 && (my.isOwner || my.roles.length > 1) ? (jsx("span", { className: "shrink-0 text-muted-foreground", children: my.isOwner
|
|
1498
|
+
? `อีก ${extra} บทบาท`
|
|
1499
|
+
: `อีก ${extra - 1} บทบาท` })) : null] }));
|
|
1500
|
+
}
|
|
1483
1501
|
const { shown, more } = cardRoleLine(my.roles);
|
|
1484
1502
|
return (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 ? jsx(OwnerChip, {}) : null, shown.map((name, i) => {
|
|
1485
1503
|
const last = i === shown.length - 1;
|
|
@@ -1637,8 +1655,8 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
|
|
|
1637
1655
|
openHelp();
|
|
1638
1656
|
}, 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: [jsx(CircleHelp, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E0A\u0E48\u0E27\u0E22\u0E40\u0E2B\u0E25\u0E37\u0E2D" })] })), 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 ? (jsxs(Fragment$1, { children: [jsx(Loader2, { className: "h-4 w-4 animate-spin" }), jsx("span", { className: "flex-1 text-left", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A..." })] })) : (jsxs(Fragment$1, { children: [jsx(LogOut, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })] })) })] })] })) : null;
|
|
1639
1657
|
if (variant === "workspace") {
|
|
1640
|
-
return (jsx("div", { className: "mt-
|
|
1641
|
-
? "absolute right-
|
|
1658
|
+
return (jsx("div", { className: "mt-2.5 border-t border-sidebar-border pt-3.5", children: jsxs("div", { className: "relative", children: [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: [jsxs(Avatar, { className: "size-[38px] shrink-0 rounded-full border border-sidebar-border", children: [user.avatar ? (jsx(AvatarImage, { src: user.avatar, alt: user.name })) : null, jsx(AvatarFallback, { className: "rounded-full bg-primary/10 text-[17px] font-medium text-primary", children: getInitials(user.name) })] }), jsxs("span", { className: "grid min-w-0 flex-1 gap-0.5 leading-tight", children: [jsx("strong", { className: `truncate text-[13px] font-semibold ${cardTall ? "pr-5" : ""}`, title: user.name, children: user.name }), jsx("small", { className: "truncate text-[11px] text-muted-foreground", title: user.email, children: user.email }), myRoles ? (jsx(CardRoles, { my: myRoles, variant: "workspace", className: "mt-0.5 text-[11px] font-medium leading-[17px] text-sidebar-foreground/80" })) : rolesLoading ? (jsx("span", { className: "my-[3px] block h-[11px] w-20 animate-pulse rounded bg-sidebar-accent" })) : null] }), jsx(ChevronUp, { className: cardTall
|
|
1659
|
+
? "absolute right-2.5 top-3 size-3.5 text-muted-foreground"
|
|
1642
1660
|
: "size-3.5 shrink-0 text-muted-foreground" })] }), typeof window !== "undefined" &&
|
|
1643
1661
|
createPortal(dropdownContent, document.body)] }) }));
|
|
1644
1662
|
}
|
|
@@ -1832,10 +1850,14 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
|
|
|
1832
1850
|
!!item.subModules?.some(subItem => subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
|
|
1833
1851
|
isPathActive(pathname, subPage.url)));
|
|
1834
1852
|
// Parent only active when path matches its URL exactly AND no child
|
|
1835
|
-
// is active (so parent+child don't both light up).
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
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;
|
|
1839
1861
|
return { itemUrl, activeSubItemUrl, hasActiveSubItem, isItemActive };
|
|
1840
1862
|
});
|
|
1841
1863
|
// A group opens when its page becomes current and otherwise stays as the
|
|
@@ -2003,7 +2025,7 @@ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, clo
|
|
|
2003
2025
|
if (!open)
|
|
2004
2026
|
return null;
|
|
2005
2027
|
if (chrome === "workspace") {
|
|
2006
|
-
return (jsxs("div", { className: cn("flex h-screen w-[
|
|
2028
|
+
return (jsxs("div", { className: cn("flex h-screen w-[264px] shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground",
|
|
2007
2029
|
// As a drawer (≤1024px) it floats over the page and needs the lift.
|
|
2008
2030
|
"max-[1024px]:shadow-2xl", className), children: [header ? jsx("div", { className: "shrink-0 px-[18px] pt-8 pb-6", children: header }) : null, jsxs(SidebarContent, { className: "flex-1 overflow-y-auto px-[18px]", children: [jsx(NavMain, { items: modulesWithActiveState, orgCode: orgCode, approvalCounts: approvalCounts, closeOnNavigation: closeOnNavigation, chrome: "workspace", highlightActiveModule: highlightActiveModule }), jsx(NavTools, { tools: tools, closeOnNavigation: closeOnNavigation })] }), bottom ? jsx("div", { className: "shrink-0 px-[18px] pt-6", children: bottom }) : null, jsx("div", { className: "shrink-0 px-[18px] pb-3", children: footer })] }));
|
|
2009
2031
|
}
|
|
@@ -3053,7 +3075,7 @@ function cx(...parts) {
|
|
|
3053
3075
|
* name, and a spaced caption beneath it.
|
|
3054
3076
|
*/
|
|
3055
3077
|
function WorkspaceBrand({ name, caption, mark, href, compact = false, className, }) {
|
|
3056
|
-
const body = (jsxs(Fragment$1, { children: [jsx("span", { className: cn("grid shrink-0 place-items-center overflow-hidden rounded-[
|
|
3078
|
+
const body = (jsxs(Fragment$1, { children: [jsx("span", { className: 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 ? (jsx("span", { className: "sr-only", children: name })) : (jsxs("span", { className: "min-w-0", children: [jsx("span", { className: "block truncate text-2xl leading-tight font-medium tracking-[-0.05em]", children: name }), caption ? (jsx("small", { className: "mt-0.5 block truncate text-[9px] font-semibold tracking-[0.3em] text-muted-foreground", children: caption })) : null] }))] }));
|
|
3057
3079
|
const cls = cn("flex items-center gap-[11px] rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", !compact && "px-4", className);
|
|
3058
3080
|
return href ? (jsx(Link, { href: href, className: cls, "aria-label": compact ? name : undefined, children: body })) : (jsx("div", { className: cls, children: body }));
|
|
3059
3081
|
}
|