@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
package/dist/index.js
CHANGED
|
@@ -1133,6 +1133,23 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
|
|
|
1133
1133
|
lastOrgKeyRef.current = currentOrgKey;
|
|
1134
1134
|
}
|
|
1135
1135
|
const contentOrgKey = lastOrgKeyRef.current;
|
|
1136
|
+
// Hold the page back until the active organisation is the one the URL names.
|
|
1137
|
+
//
|
|
1138
|
+
// The sync effect above writes the cookie through a server action, which is
|
|
1139
|
+
// a round trip. Until it lands, getOrgFromCookie() still answers with the
|
|
1140
|
+
// previous organisation and every request the page fires on mount carries
|
|
1141
|
+
// it — so /O26000021/work-record asked about O26000033 and was correctly
|
|
1142
|
+
// told there was nothing there. An empty table is the mild version: the
|
|
1143
|
+
// create form posts to the same header, so a record could be filed against
|
|
1144
|
+
// a company the user was not looking at.
|
|
1145
|
+
//
|
|
1146
|
+
// Only while the two genuinely disagree. A code naming no organisation of
|
|
1147
|
+
// the caller's is left to the app to refuse, and a page with no code in its
|
|
1148
|
+
// path is unaffected.
|
|
1149
|
+
const orgContextIsStale = !!orgFromCode &&
|
|
1150
|
+
organizationId !== null &&
|
|
1151
|
+
organizationId !== undefined &&
|
|
1152
|
+
String(organizationId) !== String(orgFromCode.id);
|
|
1136
1153
|
// ≤1024px: sidebar renders as a modal overlay (backdrop dims content). >1024px:
|
|
1137
1154
|
// sidebar is docked and the content gets a left margin to clear it.
|
|
1138
1155
|
const [isSidebarModalLayout, setIsSidebarModalLayout] = React__namespace.useState(false);
|
|
@@ -1179,7 +1196,7 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
|
|
|
1179
1196
|
const contentMargin = open && !isSidebarModalLayout ? "ml-[21.5rem]" : "";
|
|
1180
1197
|
const needsLeftGutter = !(open && !isSidebarModalLayout);
|
|
1181
1198
|
const wrap = permissionProvider ?? ((c) => c);
|
|
1182
|
-
return (jsxRuntime.jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsxRuntime.jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsxRuntime.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) })), jsxRuntime.jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxRuntime.jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxRuntime.jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsxRuntime.jsx(HeaderBackground, {}), jsxRuntime.jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsxRuntime.jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsxRuntime.jsx("div", { id: "header-title" })] })] }), jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: jsxRuntime.jsxs("div", { className: "space-y-4 text-center", children: [jsxRuntime.jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsxRuntime.jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
|
|
1199
|
+
return (jsxRuntime.jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsxRuntime.jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsxRuntime.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) })), jsxRuntime.jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxRuntime.jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxRuntime.jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsxRuntime.jsx(HeaderBackground, {}), jsxRuntime.jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsxRuntime.jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsxRuntime.jsx("div", { id: "header-title" })] })] }), jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: jsxRuntime.jsxs("div", { className: "space-y-4 text-center", children: [jsxRuntime.jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsxRuntime.jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: orgContextIsStale ? loadingComponent : wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
|
|
1183
1200
|
}
|
|
1184
1201
|
/** Local tiny classnames helper — avoids pulling clsx/tailwind-merge into
|
|
1185
1202
|
* the shell just for the layout file. Falsy values are skipped. */
|
|
@@ -1216,6 +1233,7 @@ function AccessDenied({ title = "ขออภัย, คุณไม่มีส
|
|
|
1216
1233
|
|
|
1217
1234
|
const EMPLOYEE_RECORD_REQUIRED = "EMPLOYEE_RECORD_REQUIRED";
|
|
1218
1235
|
const EMPLOYEE_RECORD_REQUIRED_EVENT = "erp:employee-record-required";
|
|
1236
|
+
const EMPLOYEE_RECORD_OK_EVENT = "erp:employee-record-ok";
|
|
1219
1237
|
/**
|
|
1220
1238
|
* Shown when the API refuses every request because the signed-in account has
|
|
1221
1239
|
* no employee record in this organisation.
|
|
@@ -1231,14 +1249,19 @@ function EmployeeRequiredGate({ platformBaseUrl, }) {
|
|
|
1231
1249
|
const code = params?.code ?? "";
|
|
1232
1250
|
React.useEffect(() => {
|
|
1233
1251
|
const onBlocked = () => setBlocked(true);
|
|
1252
|
+
const onOk = () => setBlocked(false);
|
|
1234
1253
|
window.addEventListener(EMPLOYEE_RECORD_REQUIRED_EVENT, onBlocked);
|
|
1235
|
-
|
|
1254
|
+
window.addEventListener(EMPLOYEE_RECORD_OK_EVENT, onOk);
|
|
1255
|
+
return () => {
|
|
1256
|
+
window.removeEventListener(EMPLOYEE_RECORD_REQUIRED_EVENT, onBlocked);
|
|
1257
|
+
window.removeEventListener(EMPLOYEE_RECORD_OK_EVENT, onOk);
|
|
1258
|
+
};
|
|
1236
1259
|
}, []);
|
|
1237
1260
|
if (!blocked)
|
|
1238
1261
|
return null;
|
|
1239
1262
|
const base = platformBaseUrl?.replace(/\/$/, "") ?? "";
|
|
1240
1263
|
const createHref = code ? `${base}/${code}/employee/create` : `${base}/`;
|
|
1241
|
-
return (jsxRuntime.jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-background/95 p-6 backdrop-blur-sm", children: jsxRuntime.jsxs("div", { className: "w-full max-w-md space-y-5 text-center", children: [jsxRuntime.jsx(lucideReact.ContactRound, { className: "mx-auto h-12 w-12 text-muted-foreground" }), jsxRuntime.jsxs("div", { className: "space-y-2", children: [jsxRuntime.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" }), jsxRuntime.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" })] }), jsxRuntime.jsxs("div", { className: "flex flex-wrap justify-center gap-2", children: [jsxRuntime.jsx(erpTheme.Button, { asChild: true, children: jsxRuntime.jsxs("a", { href: createHref, children: [jsxRuntime.jsx(lucideReact.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"] }) })
|
|
1264
|
+
return (jsxRuntime.jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-background/95 p-6 backdrop-blur-sm", children: jsxRuntime.jsxs("div", { className: "w-full max-w-md space-y-5 text-center", children: [jsxRuntime.jsx(lucideReact.ContactRound, { className: "mx-auto h-12 w-12 text-muted-foreground" }), jsxRuntime.jsxs("div", { className: "space-y-2", children: [jsxRuntime.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" }), jsxRuntime.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" })] }), jsxRuntime.jsxs("div", { className: "flex flex-wrap justify-center gap-2", children: [jsxRuntime.jsx(erpTheme.Button, { onClick: () => window.location.reload(), children: "\u0E25\u0E2D\u0E07\u0E43\u0E2B\u0E21\u0E48" }), jsxRuntime.jsx(erpTheme.Button, { variant: "outline", asChild: true, children: jsxRuntime.jsxs("a", { href: createHref, children: [jsxRuntime.jsx(lucideReact.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"] }) })] }), jsxRuntime.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" })] }) }));
|
|
1242
1265
|
}
|
|
1243
1266
|
|
|
1244
1267
|
const shellClassName = "flex min-w-0 flex-1 flex-col gap-6 overflow-x-hidden";
|
|
@@ -1889,7 +1912,16 @@ const refreshTokenSingleFlight = async () => {
|
|
|
1889
1912
|
}
|
|
1890
1913
|
return refreshPromise;
|
|
1891
1914
|
};
|
|
1892
|
-
instance.interceptors.response.use(response =>
|
|
1915
|
+
instance.interceptors.response.use(response => {
|
|
1916
|
+
// Any successful call means the session can act in this organisation
|
|
1917
|
+
// after all. Without this the gate, once raised, stayed up through a
|
|
1918
|
+
// whole working session — including the seconds between an administrator
|
|
1919
|
+
// adding somebody and the employee record existing.
|
|
1920
|
+
if (typeof window !== "undefined") {
|
|
1921
|
+
window.dispatchEvent(new CustomEvent("erp:employee-record-ok"));
|
|
1922
|
+
}
|
|
1923
|
+
return response;
|
|
1924
|
+
}, async (error) => {
|
|
1893
1925
|
const originalRequest = error.config;
|
|
1894
1926
|
const requestUrl = originalRequest?.url || error.config?.url;
|
|
1895
1927
|
if (error.response?.status === 401 &&
|
|
@@ -1966,6 +1998,7 @@ exports.ApiError = ApiError;
|
|
|
1966
1998
|
exports.AppBreadcrumb = AppBreadcrumb;
|
|
1967
1999
|
exports.AppLayoutShell = AppLayoutShell;
|
|
1968
2000
|
exports.AppSidebar = AppSidebar;
|
|
2001
|
+
exports.EMPLOYEE_RECORD_OK_EVENT = EMPLOYEE_RECORD_OK_EVENT;
|
|
1969
2002
|
exports.EMPLOYEE_RECORD_REQUIRED = EMPLOYEE_RECORD_REQUIRED;
|
|
1970
2003
|
exports.EMPLOYEE_RECORD_REQUIRED_EVENT = EMPLOYEE_RECORD_REQUIRED_EVENT;
|
|
1971
2004
|
exports.EmployeeRequiredGate = EmployeeRequiredGate;
|