@codebit-programando-solucoes/codebit-web-antd 1.1.52 → 1.1.53
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/components/ErrorRetry.d.ts +3 -0
- package/dist/contexts/CodebitConfigContext.d.ts +5 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useErrorMessage.d.ts +3 -0
- package/dist/hooks/useErrorModal.d.ts +5 -1
- package/dist/hooks/useSessionExpiredModal.d.ts +18 -0
- package/dist/index.cjs +112 -42
- package/package.json +1 -1
|
@@ -27,6 +27,9 @@ export interface ErrorRetryProps {
|
|
|
27
27
|
* Automatically captures exceptions to Sentry and logs errors to console.
|
|
28
28
|
* For 401 (Unauthorized) and 403 (Forbidden) errors, the retry button will reload the page instead of calling the provided onRetry callback.
|
|
29
29
|
*
|
|
30
|
+
* A 401 (expired session) additionally opens the session-expired modal when rendered inside `CodebitConfigProvider` — a single modal, however many components report it,
|
|
31
|
+
* whose only way out is reloading the page. A 401 is logged but not sent to Sentry.
|
|
32
|
+
*
|
|
30
33
|
* @param props - Component props
|
|
31
34
|
* @returns JSX.Element - Error display with retry button
|
|
32
35
|
*/
|
|
@@ -78,6 +78,11 @@ export interface CodebitConfigContextType {
|
|
|
78
78
|
* host app chooses to wire it up that way itself.
|
|
79
79
|
*/
|
|
80
80
|
onMenuClick?: (key: string) => boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Opens the session-expired modal, whose only way out is reloading the page. Idempotent: every call after the first is a no-op. Usually reached through
|
|
83
|
+
* `useSessionExpiredModal()` rather than read from the context directly.
|
|
84
|
+
*/
|
|
85
|
+
showSessionExpiredModal: () => void;
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
/** Properties for the CodebitConfigProvider component. */
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -20,12 +20,15 @@ export type ErrorStatus = 403 | 404 | 500 | 'error';
|
|
|
20
20
|
* @property {ErrorStatus} status - Normalized status for UI handling.
|
|
21
21
|
* @property {string} retryText - Text displayed on the retry button.
|
|
22
22
|
* @property {(() => void) | null} onRetryAction - Callback invoked when the user clicks the retry button, or `null` when no action is required.
|
|
23
|
+
* @property {boolean} sessionExpired - `true` exactly when the error maps to `Error.sessionExpiredError` (no `errorCode`, HTTP 401). `ErrorRetry` and `useErrorModal` open the
|
|
24
|
+
* session-expired modal on it.
|
|
23
25
|
*/
|
|
24
26
|
export interface ErrorMessage {
|
|
25
27
|
errorMessage: string;
|
|
26
28
|
status: ErrorStatus;
|
|
27
29
|
retryText: string;
|
|
28
30
|
onRetryAction: (() => void) | null;
|
|
31
|
+
sessionExpired: boolean;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/**
|
|
@@ -13,10 +13,14 @@ export interface UseErrorModalReturn {
|
|
|
13
13
|
/**
|
|
14
14
|
* Shows an error modal.
|
|
15
15
|
*
|
|
16
|
+
* An expired session (HTTP 401) opens the session-expired modal instead when called inside `CodebitConfigProvider` — one modal however many calls report it, with reloading
|
|
17
|
+
* the page as its only way out. Without a provider, the error modal is shown as usual but its close always reloads. A 401 is logged but not sent to Sentry.
|
|
18
|
+
*
|
|
16
19
|
* @param error - The error to be displayed. It can be any object that extends the native `Error` type.
|
|
17
20
|
* @param title - Optional custom title for the modal. If omitted, a default translated title is used.
|
|
18
21
|
* @param closeButtonText - Optional custom text for the modal's close button.
|
|
19
|
-
* @param onClose - Optional callback executed when the modal is closed. If not provided, the retry action derived from the error message will be used.
|
|
22
|
+
* @param onClose - Optional callback executed when the modal is closed. If not provided, the retry action derived from the error message will be used. Ignored for an
|
|
23
|
+
* expired session, which always reloads the page.
|
|
20
24
|
*/
|
|
21
25
|
showErrorModal(
|
|
22
26
|
error: AcceptedError,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook that gives access to the session-expired modal hosted by `CodebitConfigProvider` — a modal whose only way out is reloading the page.
|
|
3
|
+
*
|
|
4
|
+
* Call `showSessionExpiredModal` from an effect or an event handler, never during render: it updates the provider's state.
|
|
5
|
+
*
|
|
6
|
+
* @returns An object containing the `showSessionExpiredModal` function.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useSessionExpiredModal(): UseSessionExpiredModalReturn;
|
|
9
|
+
|
|
10
|
+
/** Shape of the object returned by {@link useSessionExpiredModal}. */
|
|
11
|
+
export interface UseSessionExpiredModalReturn {
|
|
12
|
+
/**
|
|
13
|
+
* Opens the session-expired modal. Idempotent: however many callers report the same expired session, one modal shows.
|
|
14
|
+
*
|
|
15
|
+
* @returns `true` when a `CodebitConfigProvider` is above the caller and handled it; `false` when there is none, so the caller keeps its own behavior for the 401.
|
|
16
|
+
*/
|
|
17
|
+
showSessionExpiredModal(): boolean;
|
|
18
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -308,6 +308,7 @@ var __webpack_exports__ = {};
|
|
|
308
308
|
ForgotPassword: ()=>ForgotPassword,
|
|
309
309
|
CodebitConfigContext: ()=>CodebitConfigContext,
|
|
310
310
|
LoginContainer: ()=>LoginContainer,
|
|
311
|
+
useSessionExpiredModal: ()=>useSessionExpiredModal,
|
|
311
312
|
useErrorMessage: ()=>useErrorMessage,
|
|
312
313
|
LocalLoginRecoveryPasswordResult: ()=>LocalLoginRecoveryPasswordResult,
|
|
313
314
|
FilterContainer: ()=>FilterContainer,
|
|
@@ -352,7 +353,8 @@ var __webpack_exports__ = {};
|
|
|
352
353
|
forbiddenError: 'Recurso negado pelo servidor',
|
|
353
354
|
notFoundError: 'Recurso não encontrado pelo servidor',
|
|
354
355
|
internalServerError: 'Erro interno no servidor',
|
|
355
|
-
sessionExpiredError: 'Sessão expirada'
|
|
356
|
+
sessionExpiredError: 'Sessão expirada',
|
|
357
|
+
sessionExpiredDescription: 'Sua sessão expirou. Recarregue a página para entrar novamente.'
|
|
356
358
|
},
|
|
357
359
|
Login: {
|
|
358
360
|
loginTitle: 'Entrar',
|
|
@@ -496,7 +498,8 @@ var __webpack_exports__ = {};
|
|
|
496
498
|
forbiddenError: 'Resource denied by server',
|
|
497
499
|
notFoundError: 'Resource not found by server',
|
|
498
500
|
internalServerError: 'Internal server error',
|
|
499
|
-
sessionExpiredError: 'Session expired'
|
|
501
|
+
sessionExpiredError: 'Session expired',
|
|
502
|
+
sessionExpiredDescription: 'Your session has expired. Reload the page to sign in again.'
|
|
500
503
|
},
|
|
501
504
|
Login: {
|
|
502
505
|
loginTitle: 'Login',
|
|
@@ -618,11 +621,64 @@ var __webpack_exports__ = {};
|
|
|
618
621
|
const external_react_router_namespaceObject = require("react-router");
|
|
619
622
|
const react_namespaceObject = require("@sentry/react");
|
|
620
623
|
const codebit_web_namespaceObject = require("@codebit-programando-solucoes/codebit-web");
|
|
624
|
+
const external_react_i18next_namespaceObject = require("react-i18next");
|
|
625
|
+
const external_i18next_namespaceObject = require("i18next");
|
|
626
|
+
const i18nLib = external_i18next_namespaceObject.createInstance();
|
|
627
|
+
i18nLib.use(external_react_i18next_namespaceObject.initReactI18next).init({
|
|
628
|
+
resources: {
|
|
629
|
+
pt: locales_pt,
|
|
630
|
+
en: locales_en
|
|
631
|
+
},
|
|
632
|
+
lng: 'pt',
|
|
633
|
+
fallbackLng: 'pt',
|
|
634
|
+
defaultNS: 'codebitWebAntd',
|
|
635
|
+
ns: [
|
|
636
|
+
'codebitWebAntd'
|
|
637
|
+
],
|
|
638
|
+
interpolation: {
|
|
639
|
+
escapeValue: false
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
const i18n = i18nLib;
|
|
643
|
+
const useCodebitWebAntdTranslation = ()=>(0, external_react_i18next_namespaceObject.useTranslation)('codebitWebAntd', {
|
|
644
|
+
i18n: i18n
|
|
645
|
+
});
|
|
646
|
+
const SESSION_EXPIRED_MODAL_Z_INDEX_OFFSET = 1100;
|
|
647
|
+
const reload = ()=>{
|
|
648
|
+
globalThis.location.reload();
|
|
649
|
+
};
|
|
650
|
+
function SessionExpiredModal({ open }) {
|
|
651
|
+
const { t } = useCodebitWebAntdTranslation();
|
|
652
|
+
const { token } = external_antd_namespaceObject.theme.useToken();
|
|
653
|
+
return /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Modal, {
|
|
654
|
+
open: open,
|
|
655
|
+
title: t('Error.sessionExpiredError'),
|
|
656
|
+
onOk: reload,
|
|
657
|
+
onCancel: reload,
|
|
658
|
+
okText: t('Error.reloadPageButton'),
|
|
659
|
+
okButtonProps: {
|
|
660
|
+
icon: /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.ReloadOutlined, {})
|
|
661
|
+
},
|
|
662
|
+
footer: (_, { OkBtn })=>/*#__PURE__*/ (0, jsx_runtime.jsx)(OkBtn, {}),
|
|
663
|
+
maskClosable: false,
|
|
664
|
+
centered: true,
|
|
665
|
+
width: 416,
|
|
666
|
+
zIndex: token['zIndexPopupBase'] + SESSION_EXPIRED_MODAL_Z_INDEX_OFFSET,
|
|
667
|
+
children: t("Error.sessionExpiredDescription")
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
SessionExpiredModal.propTypes = {
|
|
671
|
+
open: external_prop_types_default().bool.isRequired
|
|
672
|
+
};
|
|
621
673
|
const logger = new codebit_web_namespaceObject.Logger('CodebitConfigContext');
|
|
622
674
|
const CodebitConfigContext = /*#__PURE__*/ (0, external_react_.createContext)(null);
|
|
623
675
|
function CodebitConfigProvider({ children, menuItems, version = '1.0', showVersion = ()=>true, secondarySidebar, secondarySidebarWidth = 280, bottomMenuItems, headerExtra, doLogout, checkLogin, onMenuClick }) {
|
|
624
676
|
const [isLogged, setIsLogged] = (0, external_react_.useState)(null);
|
|
625
677
|
const [user, setUser] = (0, external_react_.useState)(null);
|
|
678
|
+
const [sessionExpired, setSessionExpired] = (0, external_react_.useState)(false);
|
|
679
|
+
const showSessionExpiredModal = (0, external_react_.useCallback)(()=>{
|
|
680
|
+
setSessionExpired(true);
|
|
681
|
+
}, []);
|
|
626
682
|
const logout = async ()=>{
|
|
627
683
|
await doLogout();
|
|
628
684
|
setIsLogged(false);
|
|
@@ -666,7 +722,8 @@ var __webpack_exports__ = {};
|
|
|
666
722
|
secondarySidebarWidth,
|
|
667
723
|
bottomMenuItems,
|
|
668
724
|
headerExtra,
|
|
669
|
-
onMenuClick
|
|
725
|
+
onMenuClick,
|
|
726
|
+
showSessionExpiredModal
|
|
670
727
|
}), [
|
|
671
728
|
menuItems,
|
|
672
729
|
isLogged,
|
|
@@ -677,11 +734,17 @@ var __webpack_exports__ = {};
|
|
|
677
734
|
secondarySidebarWidth,
|
|
678
735
|
bottomMenuItems,
|
|
679
736
|
headerExtra,
|
|
680
|
-
onMenuClick
|
|
737
|
+
onMenuClick,
|
|
738
|
+
showSessionExpiredModal
|
|
681
739
|
]);
|
|
682
|
-
return /*#__PURE__*/ (0, jsx_runtime.
|
|
740
|
+
return /*#__PURE__*/ (0, jsx_runtime.jsxs)(CodebitConfigContext.Provider, {
|
|
683
741
|
value: contextValue,
|
|
684
|
-
children:
|
|
742
|
+
children: [
|
|
743
|
+
children,
|
|
744
|
+
/*#__PURE__*/ (0, jsx_runtime.jsx)(SessionExpiredModal, {
|
|
745
|
+
open: sessionExpired
|
|
746
|
+
})
|
|
747
|
+
]
|
|
685
748
|
});
|
|
686
749
|
}
|
|
687
750
|
CodebitConfigProvider.propTypes = {
|
|
@@ -900,28 +963,6 @@ var __webpack_exports__ = {};
|
|
|
900
963
|
headerSpace: "headerSpace-Fd_iha",
|
|
901
964
|
content: "content-OZHp2h"
|
|
902
965
|
};
|
|
903
|
-
const external_react_i18next_namespaceObject = require("react-i18next");
|
|
904
|
-
const external_i18next_namespaceObject = require("i18next");
|
|
905
|
-
const i18nLib = external_i18next_namespaceObject.createInstance();
|
|
906
|
-
i18nLib.use(external_react_i18next_namespaceObject.initReactI18next).init({
|
|
907
|
-
resources: {
|
|
908
|
-
pt: locales_pt,
|
|
909
|
-
en: locales_en
|
|
910
|
-
},
|
|
911
|
-
lng: 'pt',
|
|
912
|
-
fallbackLng: 'pt',
|
|
913
|
-
defaultNS: 'codebitWebAntd',
|
|
914
|
-
ns: [
|
|
915
|
-
'codebitWebAntd'
|
|
916
|
-
],
|
|
917
|
-
interpolation: {
|
|
918
|
-
escapeValue: false
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
const i18n = i18nLib;
|
|
922
|
-
const useCodebitWebAntdTranslation = ()=>(0, external_react_i18next_namespaceObject.useTranslation)('codebitWebAntd', {
|
|
923
|
-
i18n: i18n
|
|
924
|
-
});
|
|
925
966
|
const { Header, Sider, Content } = external_antd_namespaceObject.Layout;
|
|
926
967
|
const MENU_KEY_APP_LOGOUT = 'app-logout';
|
|
927
968
|
function filterMenuItemsByRole(items, user) {
|
|
@@ -1385,6 +1426,7 @@ var __webpack_exports__ = {};
|
|
|
1385
1426
|
let errorMessage = t('Error.defaultError');
|
|
1386
1427
|
let retryText = t('Error.retryButton');
|
|
1387
1428
|
let onRetryAction = null;
|
|
1429
|
+
let sessionExpired = false;
|
|
1388
1430
|
if (error?.errorCode) switch(error.errorCode){
|
|
1389
1431
|
case codebit_web_namespaceObject.ErrorCode.ERR_NETWORK:
|
|
1390
1432
|
errorMessage = t('Error.networkError');
|
|
@@ -1419,27 +1461,50 @@ var __webpack_exports__ = {};
|
|
|
1419
1461
|
onRetryAction = ()=>{
|
|
1420
1462
|
globalThis.location.reload();
|
|
1421
1463
|
};
|
|
1464
|
+
sessionExpired = true;
|
|
1422
1465
|
break;
|
|
1423
1466
|
}
|
|
1424
1467
|
return {
|
|
1425
1468
|
errorMessage,
|
|
1426
1469
|
status,
|
|
1427
1470
|
retryText,
|
|
1428
|
-
onRetryAction
|
|
1471
|
+
onRetryAction,
|
|
1472
|
+
sessionExpired
|
|
1429
1473
|
};
|
|
1430
1474
|
};
|
|
1431
1475
|
return {
|
|
1432
1476
|
getErrorMessage
|
|
1433
1477
|
};
|
|
1434
1478
|
}
|
|
1479
|
+
function useSessionExpiredModal() {
|
|
1480
|
+
const show = (0, external_react_.useContext)(CodebitConfigContext)?.showSessionExpiredModal;
|
|
1481
|
+
const showSessionExpiredModal = (0, external_react_.useCallback)(()=>{
|
|
1482
|
+
if (!show) return false;
|
|
1483
|
+
show();
|
|
1484
|
+
return true;
|
|
1485
|
+
}, [
|
|
1486
|
+
show
|
|
1487
|
+
]);
|
|
1488
|
+
return {
|
|
1489
|
+
showSessionExpiredModal
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1435
1492
|
const ErrorRetry_logger = new codebit_web_namespaceObject.Logger('ErrorRetry');
|
|
1436
1493
|
function ErrorRetry({ error, onRetry }) {
|
|
1437
|
-
|
|
1494
|
+
const { getErrorMessage } = useErrorMessage();
|
|
1495
|
+
const { status, errorMessage, retryText, onRetryAction, sessionExpired } = getErrorMessage(error);
|
|
1496
|
+
const { showSessionExpiredModal } = useSessionExpiredModal();
|
|
1497
|
+
if (sessionExpired) ErrorRetry_logger.warn('Session expired.', error);
|
|
1498
|
+
else if (error && error instanceof Error) {
|
|
1438
1499
|
ErrorRetry_logger.error('Fail to load.', error);
|
|
1439
1500
|
react_namespaceObject.captureException(error);
|
|
1440
1501
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1502
|
+
(0, external_react_.useEffect)(()=>{
|
|
1503
|
+
if (sessionExpired) showSessionExpiredModal();
|
|
1504
|
+
}, [
|
|
1505
|
+
sessionExpired,
|
|
1506
|
+
showSessionExpiredModal
|
|
1507
|
+
]);
|
|
1443
1508
|
return /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Card, {
|
|
1444
1509
|
children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Result, {
|
|
1445
1510
|
status: status,
|
|
@@ -2881,17 +2946,20 @@ var __webpack_exports__ = {};
|
|
|
2881
2946
|
const { modal } = external_antd_namespaceObject.App.useApp();
|
|
2882
2947
|
const { getErrorMessage } = useErrorMessage();
|
|
2883
2948
|
const { t } = useCodebitWebAntdTranslation();
|
|
2949
|
+
const { showSessionExpiredModal } = useSessionExpiredModal();
|
|
2884
2950
|
const showErrorModal = (error, title, closeButtonText, onClose)=>{
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2951
|
+
const { errorMessage, retryText, onRetryAction, sessionExpired } = getErrorMessage(error);
|
|
2952
|
+
if (sessionExpired) {
|
|
2953
|
+
useErrorModal_logger.warn('Session expired.', error);
|
|
2954
|
+
if (showSessionExpiredModal()) return;
|
|
2955
|
+
onClose = onRetryAction;
|
|
2956
|
+
} else {
|
|
2957
|
+
if (error && error instanceof Error) {
|
|
2958
|
+
useErrorModal_logger.error('Fail to load.', error);
|
|
2959
|
+
react_namespaceObject.captureException(error);
|
|
2960
|
+
}
|
|
2961
|
+
onClose = onClose ?? onRetryAction;
|
|
2893
2962
|
}
|
|
2894
|
-
onClose = onClose ?? onRetryAction;
|
|
2895
2963
|
modal.error({
|
|
2896
2964
|
title: title || t('Error.errorTitle'),
|
|
2897
2965
|
content: errorMessage + '.',
|
|
@@ -2992,6 +3060,7 @@ exports.ThemeToggle = __webpack_exports__.ThemeToggle;
|
|
|
2992
3060
|
exports.UserMenu = __webpack_exports__.UserMenu;
|
|
2993
3061
|
exports.useErrorMessage = __webpack_exports__.useErrorMessage;
|
|
2994
3062
|
exports.useErrorModal = __webpack_exports__.useErrorModal;
|
|
3063
|
+
exports.useSessionExpiredModal = __webpack_exports__.useSessionExpiredModal;
|
|
2995
3064
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
2996
3065
|
"AdminUserOutlinedIcon",
|
|
2997
3066
|
"AudioPlayer",
|
|
@@ -3019,7 +3088,8 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
3019
3088
|
"ThemeToggle",
|
|
3020
3089
|
"UserMenu",
|
|
3021
3090
|
"useErrorMessage",
|
|
3022
|
-
"useErrorModal"
|
|
3091
|
+
"useErrorModal",
|
|
3092
|
+
"useSessionExpiredModal"
|
|
3023
3093
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
3024
3094
|
Object.defineProperty(exports, '__esModule', {
|
|
3025
3095
|
value: true
|