@eintrek/erp-shell 0.1.32 → 0.1.34
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 +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +38 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1214,6 +1214,33 @@ function AccessDenied({ title = "ขออภัย, คุณไม่มีส
|
|
|
1214
1214
|
return (jsxRuntime.jsx("div", { className: wrapperClass, children: jsxRuntime.jsx("div", { className: "relative w-full max-w-3xl overflow-hidden rounded-2xl border border-border/60 bg-card/60 p-8 shadow-sm backdrop-blur sm:p-10", children: jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-8 text-center sm:flex-row sm:text-left", children: [jsxRuntime.jsxs("div", { className: "relative shrink-0", children: [jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full bg-destructive/20 blur-2xl" }), jsxRuntime.jsx("div", { className: "relative flex h-28 w-28 items-center justify-center rounded-3xl border border-border/60 bg-gradient-to-br from-muted/40 to-background shadow-inner", children: jsxRuntime.jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-full border border-destructive/40 bg-destructive/10", children: jsxRuntime.jsx(lucideReact.Ban, { className: "h-8 w-8 text-destructive", strokeWidth: 2.5 }) }) })] }), jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [jsxRuntime.jsx("h1", { className: "text-xl font-bold text-foreground sm:text-2xl", children: title }), message ? (jsxRuntime.jsx("p", { className: "mt-3 whitespace-pre-line text-sm leading-relaxed text-muted-foreground sm:text-base", children: message })) : null, jsxRuntime.jsxs("div", { className: "mt-6 flex flex-col gap-3 sm:flex-row", children: [jsxRuntime.jsxs(erpTheme.Button, { type: "button", onClick: () => go(homeTarget), className: "w-full sm:w-auto", children: [jsxRuntime.jsx(lucideReact.Home, { className: "mr-2 h-4 w-4" }), homeLabel] }), supportHref ? (jsxRuntime.jsxs(erpTheme.Button, { type: "button", variant: "outline", onClick: () => go(supportHref), className: "w-full sm:w-auto", children: [jsxRuntime.jsx(lucideReact.Headset, { className: "mr-2 h-4 w-4" }), supportLabel] })) : null] })] })] }) }) }));
|
|
1215
1215
|
}
|
|
1216
1216
|
|
|
1217
|
+
const EMPLOYEE_RECORD_REQUIRED = "EMPLOYEE_RECORD_REQUIRED";
|
|
1218
|
+
const EMPLOYEE_RECORD_REQUIRED_EVENT = "erp:employee-record-required";
|
|
1219
|
+
/**
|
|
1220
|
+
* Shown when the API refuses every request because the signed-in account has
|
|
1221
|
+
* no employee record in this organisation.
|
|
1222
|
+
*
|
|
1223
|
+
* A gate rather than a banner. Leave, attendance, approvals and payslips all
|
|
1224
|
+
* identify a person by that record, so once the server started enforcing it
|
|
1225
|
+
* every call fails — and without something that says so, the app becomes a
|
|
1226
|
+
* screen of error toasts with no explanation and no way forward.
|
|
1227
|
+
*/
|
|
1228
|
+
function EmployeeRequiredGate({ platformBaseUrl, }) {
|
|
1229
|
+
const [blocked, setBlocked] = React.useState(false);
|
|
1230
|
+
const params = navigation.useParams();
|
|
1231
|
+
const code = params?.code ?? "";
|
|
1232
|
+
React.useEffect(() => {
|
|
1233
|
+
const onBlocked = () => setBlocked(true);
|
|
1234
|
+
window.addEventListener(EMPLOYEE_RECORD_REQUIRED_EVENT, onBlocked);
|
|
1235
|
+
return () => window.removeEventListener(EMPLOYEE_RECORD_REQUIRED_EVENT, onBlocked);
|
|
1236
|
+
}, []);
|
|
1237
|
+
if (!blocked)
|
|
1238
|
+
return null;
|
|
1239
|
+
const base = platformBaseUrl?.replace(/\/$/, "") ?? "";
|
|
1240
|
+
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"] }) }), jsxRuntime.jsx(erpTheme.Button, { variant: "outline", onClick: () => window.location.reload(), children: "\u0E25\u0E2D\u0E07\u0E43\u0E2B\u0E21\u0E48" })] }), 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
|
+
}
|
|
1243
|
+
|
|
1217
1244
|
const shellClassName = "flex min-w-0 flex-1 flex-col gap-6 overflow-x-hidden";
|
|
1218
1245
|
/**
|
|
1219
1246
|
* Shared layout for “กำลังตรวจสอบสิทธิ์” (skeleton) and “ไม่มีสิทธิ์” (message).
|
|
@@ -1879,6 +1906,15 @@ instance.interceptors.response.use(response => response, async (error) => {
|
|
|
1879
1906
|
await redirectToLogout();
|
|
1880
1907
|
return Promise.reject(toApiError(error));
|
|
1881
1908
|
}
|
|
1909
|
+
// The server refuses every organisation-scoped call when the account has
|
|
1910
|
+
// no employee record. Announce it once so a gate can explain it, instead
|
|
1911
|
+
// of letting each failed query raise its own toast.
|
|
1912
|
+
if (error.response?.status === 403) {
|
|
1913
|
+
const body = error.response.data;
|
|
1914
|
+
if (body?.code === "EMPLOYEE_RECORD_REQUIRED" && typeof window !== "undefined") {
|
|
1915
|
+
window.dispatchEvent(new CustomEvent("erp:employee-record-required"));
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1882
1918
|
return Promise.reject(toApiError(error));
|
|
1883
1919
|
});
|
|
1884
1920
|
|
|
@@ -1930,6 +1966,9 @@ exports.ApiError = ApiError;
|
|
|
1930
1966
|
exports.AppBreadcrumb = AppBreadcrumb;
|
|
1931
1967
|
exports.AppLayoutShell = AppLayoutShell;
|
|
1932
1968
|
exports.AppSidebar = AppSidebar;
|
|
1969
|
+
exports.EMPLOYEE_RECORD_REQUIRED = EMPLOYEE_RECORD_REQUIRED;
|
|
1970
|
+
exports.EMPLOYEE_RECORD_REQUIRED_EVENT = EMPLOYEE_RECORD_REQUIRED_EVENT;
|
|
1971
|
+
exports.EmployeeRequiredGate = EmployeeRequiredGate;
|
|
1933
1972
|
exports.FeatureFlagsProvider = FeatureFlagsProvider;
|
|
1934
1973
|
exports.HeaderBackground = HeaderBackground;
|
|
1935
1974
|
exports.NavMain = NavMain;
|