@codebit-programando-solucoes/codebit-web-antd 1.1.51 → 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.
@@ -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. */
@@ -1,2 +1,3 @@
1
1
  export * from './useErrorMessage';
2
2
  export * from './useErrorModal';
3
+ export * from './useSessionExpiredModal';
@@ -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.jsx)(CodebitConfigContext.Provider, {
740
+ return /*#__PURE__*/ (0, jsx_runtime.jsxs)(CodebitConfigContext.Provider, {
683
741
  value: contextValue,
684
- children: 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
- if (error && error instanceof Error) {
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
- const { getErrorMessage } = useErrorMessage();
1442
- const { status, errorMessage, retryText, onRetryAction } = getErrorMessage(error);
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
- if (error && error instanceof Error) {
2886
- useErrorModal_logger.error('Fail to load.', error);
2887
- react_namespaceObject.captureException(error);
2888
- }
2889
- const { errorMessage, retryText, onRetryAction } = getErrorMessage(error);
2890
- if (error && error instanceof Error) {
2891
- useErrorModal_logger.error(error);
2892
- react_namespaceObject.captureException(error);
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
package/dist/index.css CHANGED
@@ -117,7 +117,7 @@ html.dark-theme {
117
117
  }
118
118
 
119
119
  .header-vgc67L .ant-space .ant-space-item {
120
- vertical-align: middle;
120
+ align-items: center;
121
121
  height: 100%;
122
122
  display: inline-flex;
123
123
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebit-programando-solucoes/codebit-web-antd",
3
- "version": "1.1.51",
3
+ "version": "1.1.53",
4
4
  "main": "./dist/index.cjs",
5
5
  "type": "module",
6
6
  "types": "./src/index.d.ts",