@eintrek/erp-shell 0.1.34 → 0.1.36
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/auth/employee-required-gate.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +37 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +37 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const EMPLOYEE_RECORD_REQUIRED = "EMPLOYEE_RECORD_REQUIRED";
|
|
2
2
|
export declare const EMPLOYEE_RECORD_REQUIRED_EVENT = "erp:employee-record-required";
|
|
3
|
+
export declare const EMPLOYEE_RECORD_OK_EVENT = "erp:employee-record-ok";
|
|
3
4
|
/**
|
|
4
5
|
* Shown when the API refuses every request because the signed-in account has
|
|
5
6
|
* no employee record in this organisation.
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { SidebarHeader } from "./navigation/sidebar-header";
|
|
|
20
20
|
export { AppSidebar, type AppSidebarProps } from "./navigation/app-sidebar";
|
|
21
21
|
export { AppLayoutShell, type AppLayoutShellProps, type AppLayoutShellOrganization, } from "./navigation/app-layout-shell";
|
|
22
22
|
export { AccessDenied, type AccessDeniedProps } from "./auth/access-denied";
|
|
23
|
-
export { EmployeeRequiredGate, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, } from "./auth/employee-required-gate";
|
|
23
|
+
export { EmployeeRequiredGate, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EMPLOYEE_RECORD_OK_EVENT, } from "./auth/employee-required-gate";
|
|
24
24
|
export { PermissionGateShell, type PermissionGateShellProps, type PermissionGateSkeletonVariant, } from "./auth/permission-gate-shell";
|
|
25
25
|
export { PermissionProvider, type PermissionProviderProps, type PermissionProbe, type UsePermissionHook, } from "./auth/permission-provider";
|
|
26
26
|
export { getRequiredChildGroups, normalizePathnameForPermissionRules, resolveUiRoutePermission, type UiRoutePermissionLookup, } from "./auth/permission-utils";
|
package/dist/index.esm.js
CHANGED
|
@@ -1114,6 +1114,23 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
|
|
|
1114
1114
|
lastOrgKeyRef.current = currentOrgKey;
|
|
1115
1115
|
}
|
|
1116
1116
|
const contentOrgKey = lastOrgKeyRef.current;
|
|
1117
|
+
// Hold the page back until the active organisation is the one the URL names.
|
|
1118
|
+
//
|
|
1119
|
+
// The sync effect above writes the cookie through a server action, which is
|
|
1120
|
+
// a round trip. Until it lands, getOrgFromCookie() still answers with the
|
|
1121
|
+
// previous organisation and every request the page fires on mount carries
|
|
1122
|
+
// it — so /O26000021/work-record asked about O26000033 and was correctly
|
|
1123
|
+
// told there was nothing there. An empty table is the mild version: the
|
|
1124
|
+
// create form posts to the same header, so a record could be filed against
|
|
1125
|
+
// a company the user was not looking at.
|
|
1126
|
+
//
|
|
1127
|
+
// Only while the two genuinely disagree. A code naming no organisation of
|
|
1128
|
+
// the caller's is left to the app to refuse, and a page with no code in its
|
|
1129
|
+
// path is unaffected.
|
|
1130
|
+
const orgContextIsStale = !!orgFromCode &&
|
|
1131
|
+
organizationId !== null &&
|
|
1132
|
+
organizationId !== undefined &&
|
|
1133
|
+
String(organizationId) !== String(orgFromCode.id);
|
|
1117
1134
|
// ≤1024px: sidebar renders as a modal overlay (backdrop dims content). >1024px:
|
|
1118
1135
|
// sidebar is docked and the content gets a left margin to clear it.
|
|
1119
1136
|
const [isSidebarModalLayout, setIsSidebarModalLayout] = React.useState(false);
|
|
@@ -1160,7 +1177,7 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
|
|
|
1160
1177
|
const contentMargin = open && !isSidebarModalLayout ? "ml-[21.5rem]" : "";
|
|
1161
1178
|
const needsLeftGutter = !(open && !isSidebarModalLayout);
|
|
1162
1179
|
const wrap = permissionProvider ?? ((c) => c);
|
|
1163
|
-
return (jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsx("button", { type: "button", "aria-label": "\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39", className: "fixed inset-0 z-40 bg-black/50", onClick: () => setOpen(false) })), jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsx(HeaderBackground, {}), jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsx("div", { id: "header-title" })] })] }), jsx(Suspense, { fallback: jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsx("div", { className: "flex h-full items-center justify-center", children: jsxs("div", { className: "space-y-4 text-center", children: [jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
|
|
1180
|
+
return (jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsx("button", { type: "button", "aria-label": "\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39", className: "fixed inset-0 z-40 bg-black/50", onClick: () => setOpen(false) })), jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsx(HeaderBackground, {}), jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsx("div", { id: "header-title" })] })] }), jsx(Suspense, { fallback: jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsx("div", { className: "flex h-full items-center justify-center", children: jsxs("div", { className: "space-y-4 text-center", children: [jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: orgContextIsStale ? loadingComponent : wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
|
|
1164
1181
|
}
|
|
1165
1182
|
/** Local tiny classnames helper — avoids pulling clsx/tailwind-merge into
|
|
1166
1183
|
* the shell just for the layout file. Falsy values are skipped. */
|
|
@@ -1197,6 +1214,7 @@ function AccessDenied({ title = "ขออภัย, คุณไม่มีส
|
|
|
1197
1214
|
|
|
1198
1215
|
const EMPLOYEE_RECORD_REQUIRED = "EMPLOYEE_RECORD_REQUIRED";
|
|
1199
1216
|
const EMPLOYEE_RECORD_REQUIRED_EVENT = "erp:employee-record-required";
|
|
1217
|
+
const EMPLOYEE_RECORD_OK_EVENT = "erp:employee-record-ok";
|
|
1200
1218
|
/**
|
|
1201
1219
|
* Shown when the API refuses every request because the signed-in account has
|
|
1202
1220
|
* no employee record in this organisation.
|
|
@@ -1212,14 +1230,19 @@ function EmployeeRequiredGate({ platformBaseUrl, }) {
|
|
|
1212
1230
|
const code = params?.code ?? "";
|
|
1213
1231
|
useEffect(() => {
|
|
1214
1232
|
const onBlocked = () => setBlocked(true);
|
|
1233
|
+
const onOk = () => setBlocked(false);
|
|
1215
1234
|
window.addEventListener(EMPLOYEE_RECORD_REQUIRED_EVENT, onBlocked);
|
|
1216
|
-
|
|
1235
|
+
window.addEventListener(EMPLOYEE_RECORD_OK_EVENT, onOk);
|
|
1236
|
+
return () => {
|
|
1237
|
+
window.removeEventListener(EMPLOYEE_RECORD_REQUIRED_EVENT, onBlocked);
|
|
1238
|
+
window.removeEventListener(EMPLOYEE_RECORD_OK_EVENT, onOk);
|
|
1239
|
+
};
|
|
1217
1240
|
}, []);
|
|
1218
1241
|
if (!blocked)
|
|
1219
1242
|
return null;
|
|
1220
1243
|
const base = platformBaseUrl?.replace(/\/$/, "") ?? "";
|
|
1221
1244
|
const createHref = code ? `${base}/${code}/employee/create` : `${base}/`;
|
|
1222
|
-
return (jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-background/95 p-6 backdrop-blur-sm", children: jsxs("div", { className: "w-full max-w-md space-y-5 text-center", children: [jsx(ContactRound, { className: "mx-auto h-12 w-12 text-muted-foreground" }), jsxs("div", { className: "space-y-2", children: [jsx("h1", { className: "text-lg font-semibold tracking-tight", children: "\u0E1A\u0E31\u0E0D\u0E0A\u0E35\u0E19\u0E35\u0E49\u0E22\u0E31\u0E07\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49\u0E1C\u0E39\u0E01\u0E01\u0E31\u0E1A\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E1E\u0E19\u0E31\u0E01\u0E07\u0E32\u0E19" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E25\u0E32 \u0E25\u0E07\u0E40\u0E27\u0E25\u0E32 \u0E40\u0E1A\u0E34\u0E01\u0E40\u0E07\u0E34\u0E19 \u0E41\u0E25\u0E30\u0E01\u0E32\u0E23\u0E2D\u0E19\u0E38\u0E21\u0E31\u0E15\u0E34 \u0E23\u0E30\u0E1A\u0E38\u0E15\u0E31\u0E27\u0E04\u0E19\u0E08\u0E32\u0E01\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E1E\u0E19\u0E31\u0E01\u0E07\u0E32\u0E19 \u0E08\u0E36\u0E07\u0E22\u0E31\u0E07\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19\u0E43\u0E19\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49" })] }), jsxs("div", { className: "flex flex-wrap justify-center gap-2", children: [jsx(Button, { asChild: true, children: jsxs("a", { href: createHref, children: [jsx(UserPlus, { className: "mr-1 h-4 w-4" }), "\u0E2A\u0E23\u0E49\u0E32\u0E07\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E1E\u0E19\u0E31\u0E01\u0E07\u0E32\u0E19"] }) })
|
|
1245
|
+
return (jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-background/95 p-6 backdrop-blur-sm", children: jsxs("div", { className: "w-full max-w-md space-y-5 text-center", children: [jsx(ContactRound, { className: "mx-auto h-12 w-12 text-muted-foreground" }), jsxs("div", { className: "space-y-2", children: [jsx("h1", { className: "text-lg font-semibold tracking-tight", children: "\u0E1A\u0E31\u0E0D\u0E0A\u0E35\u0E19\u0E35\u0E49\u0E22\u0E31\u0E07\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49\u0E1C\u0E39\u0E01\u0E01\u0E31\u0E1A\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E1E\u0E19\u0E31\u0E01\u0E07\u0E32\u0E19" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E25\u0E32 \u0E25\u0E07\u0E40\u0E27\u0E25\u0E32 \u0E40\u0E1A\u0E34\u0E01\u0E40\u0E07\u0E34\u0E19 \u0E41\u0E25\u0E30\u0E01\u0E32\u0E23\u0E2D\u0E19\u0E38\u0E21\u0E31\u0E15\u0E34 \u0E23\u0E30\u0E1A\u0E38\u0E15\u0E31\u0E27\u0E04\u0E19\u0E08\u0E32\u0E01\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E1E\u0E19\u0E31\u0E01\u0E07\u0E32\u0E19 \u0E08\u0E36\u0E07\u0E22\u0E31\u0E07\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19\u0E43\u0E19\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49" })] }), jsxs("div", { className: "flex flex-wrap justify-center gap-2", children: [jsx(Button, { onClick: () => window.location.reload(), children: "\u0E25\u0E2D\u0E07\u0E43\u0E2B\u0E21\u0E48" }), jsx(Button, { variant: "outline", asChild: true, children: jsxs("a", { href: createHref, children: [jsx(UserPlus, { className: "mr-1 h-4 w-4" }), "\u0E2A\u0E23\u0E49\u0E32\u0E07\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E1E\u0E19\u0E31\u0E01\u0E07\u0E32\u0E19"] }) })] }), jsx("p", { className: "text-xs text-muted-foreground", children: "\u0E16\u0E49\u0E32\u0E04\u0E38\u0E13\u0E44\u0E21\u0E48\u0E21\u0E35\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E41\u0E01\u0E49\u0E44\u0E02 \u0E43\u0E2B\u0E49\u0E41\u0E08\u0E49\u0E07\u0E1C\u0E39\u0E49\u0E14\u0E39\u0E41\u0E25\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] }) }));
|
|
1223
1246
|
}
|
|
1224
1247
|
|
|
1225
1248
|
const shellClassName = "flex min-w-0 flex-1 flex-col gap-6 overflow-x-hidden";
|
|
@@ -1870,7 +1893,16 @@ const refreshTokenSingleFlight = async () => {
|
|
|
1870
1893
|
}
|
|
1871
1894
|
return refreshPromise;
|
|
1872
1895
|
};
|
|
1873
|
-
instance.interceptors.response.use(response =>
|
|
1896
|
+
instance.interceptors.response.use(response => {
|
|
1897
|
+
// Any successful call means the session can act in this organisation
|
|
1898
|
+
// after all. Without this the gate, once raised, stayed up through a
|
|
1899
|
+
// whole working session — including the seconds between an administrator
|
|
1900
|
+
// adding somebody and the employee record existing.
|
|
1901
|
+
if (typeof window !== "undefined") {
|
|
1902
|
+
window.dispatchEvent(new CustomEvent("erp:employee-record-ok"));
|
|
1903
|
+
}
|
|
1904
|
+
return response;
|
|
1905
|
+
}, async (error) => {
|
|
1874
1906
|
const originalRequest = error.config;
|
|
1875
1907
|
const requestUrl = originalRequest?.url || error.config?.url;
|
|
1876
1908
|
if (error.response?.status === 401 &&
|
|
@@ -1938,5 +1970,5 @@ const getAxiosErrorMessage = (error) => {
|
|
|
1938
1970
|
return String(error);
|
|
1939
1971
|
};
|
|
1940
1972
|
|
|
1941
|
-
export { AccessDenied, ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EmployeeRequiredGate, FeatureFlagsProvider, HeaderBackground, NavMain, NavTools, NavUser, OrganizationSwitcher, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, instance as axiosInstance, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, decodeJWT, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, isLikelyTransientNetworkError, isPathActive, normalizePathnameForPermissionRules, resolveUiRoutePermission, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
|
|
1973
|
+
export { AccessDenied, ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, EMPLOYEE_RECORD_OK_EVENT, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EmployeeRequiredGate, FeatureFlagsProvider, HeaderBackground, NavMain, NavTools, NavUser, OrganizationSwitcher, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, instance as axiosInstance, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, decodeJWT, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, isLikelyTransientNetworkError, isPathActive, normalizePathnameForPermissionRules, resolveUiRoutePermission, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
|
|
1942
1974
|
//# sourceMappingURL=index.esm.js.map
|