@codebit-programando-solucoes/codebit-web-antd 1.1.9 → 1.1.16

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.
@@ -13,15 +13,15 @@ export interface MenuItem {
13
13
  }
14
14
 
15
15
  /** User object type */
16
- export interface UserType {
16
+ export interface LoggedUser {
17
17
  /** User's full name */
18
- name: string;
18
+ name?: string;
19
19
  /** User's email address */
20
- email: string;
20
+ email?: string;
21
21
  /** Unique user identifier */
22
- id: string;
22
+ id?: string;
23
23
  /** User roles/permissions */
24
- roles: string[];
24
+ roles?: string[];
25
25
  }
26
26
 
27
27
  /** Context type for Codebit configuration */
@@ -29,7 +29,7 @@ export interface CodebitConfigContextType {
29
29
  /** Array of menu items */
30
30
  menuItems: MenuItem[];
31
31
  /** Current user object */
32
- user: UserType;
32
+ user: LoggedUser;
33
33
  /** Logout handler */
34
34
  onLogout: () => void | Promise<void>;
35
35
  /** Function to toggle the theme */
@@ -40,17 +40,14 @@ export interface CodebitConfigContextType {
40
40
  version?: string;
41
41
  /** Show version in menu */
42
42
  showVersion?: boolean;
43
- /** OAuth callback URL for authentication */
44
- oauthCallbackUrl: string;
43
+ /** Internationalization instance */
44
+ i18n?: import('i18next').i18n;
45
45
  /** Optional secondary sidebar content */
46
46
  secondarySidebar?: React.ReactNode;
47
47
  /** Width of the secondary sidebar */
48
48
  secondarySidebarWidth?: number;
49
49
  }
50
50
 
51
- /** Context for Codebit configuration */
52
- export const CodebitConfigContext: React.Context<CodebitConfigContextType | null>;
53
-
54
51
  /** Props for the CodebitConfigProvider component */
55
52
  export interface CodebitConfigProviderProps {
56
53
  /** Child components */
@@ -58,15 +55,9 @@ export interface CodebitConfigProviderProps {
58
55
  /** Menu items configuration */
59
56
  menuItems: MenuItem[];
60
57
  /** Current user object */
61
- user: UserType;
58
+ user: LoggedUser;
62
59
  /** Logout handler */
63
60
  onLogout: () => void | Promise<void>;
64
- /** Function to toggle the theme */
65
- toggleTheme: () => void;
66
- /** Current dark mode state */
67
- isDarkMode: boolean;
68
- /** OAuth callback URL for authentication */
69
- oauthCallbackUrl: string;
70
61
  /** Application version. Default is '1.0' */
71
62
  version?: string;
72
63
  /** Show version in menu. Default is true */
@@ -75,6 +66,8 @@ export interface CodebitConfigProviderProps {
75
66
  secondarySidebar?: React.ReactNode;
76
67
  /** Width of the secondary sidebar */
77
68
  secondarySidebarWidth?: number;
69
+ /** Controls theme persistence in localStorage. Default is `true` */
70
+ storeThemeInLocalStorage?: boolean;
78
71
  }
79
72
 
80
73
  /**
@@ -88,3 +81,6 @@ export interface CodebitConfigProviderProps {
88
81
  export function CodebitConfigProvider(
89
82
  props: CodebitConfigProviderProps,
90
83
  ): React.JSX.Element;
84
+
85
+ /** Context for Codebit configuration */
86
+ export const CodebitConfigContext: React.Context<CodebitConfigContextType | null>;
package/dist/index.cjs CHANGED
@@ -296,14 +296,18 @@ var __webpack_exports__ = {};
296
296
  CssTokenBridge: ()=>CssTokenBridge,
297
297
  LoggedMainContainer: ()=>LoggedMainContainer,
298
298
  Login: ()=>Login,
299
- FilterContainer: ()=>FilterContainer,
300
- HandleError: ()=>HandleError,
301
299
  ErrorRetry: ()=>ErrorRetry,
302
- CodebitConfigProvider: ()=>CodebitConfigProvider,
303
300
  CodebitConfigContext: ()=>CodebitConfigContext,
304
301
  ListCard: ()=>ListCard,
305
- ThemeToggle: ()=>ThemeToggle,
306
- LoginContainer: ()=>LoginContainer
302
+ ForgotPassword: ()=>ForgotPassword,
303
+ LoginContainer: ()=>LoginContainer,
304
+ CodebitTheme: ()=>CodebitTheme,
305
+ LocalLoginRecoveryPasswordResult: ()=>LocalLoginRecoveryPasswordResult,
306
+ FilterContainer: ()=>FilterContainer,
307
+ HandleError: ()=>HandleError,
308
+ LocalLoginAuthenticationResult: ()=>LocalLoginAuthenticationResult,
309
+ CodebitConfigProvider: ()=>CodebitConfigProvider,
310
+ ThemeToggle: ()=>ThemeToggle
307
311
  });
308
312
  var jsx_runtime = __webpack_require__("../../node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-runtime.js");
309
313
  const icons_namespaceObject = require("@ant-design/icons");
@@ -346,7 +350,53 @@ var __webpack_exports__ = {};
346
350
  },
347
351
  Login: {
348
352
  loginTitle: 'Entrar',
349
- googleLogin: 'Entrar com o Google'
353
+ googleLogin: 'Entrar com o Google',
354
+ emailLabel: 'E-mail',
355
+ passwordLabel: 'Senha',
356
+ emailPlaceholder: 'Digite seu e-mail',
357
+ passwordPlaceholder: 'Digite sua senha',
358
+ forgotPassword: 'Esqueceu sua senha?',
359
+ loginButton: 'Entrar',
360
+ emailRequired: 'Por favor, insira seu e-mail!',
361
+ emailInvalid: 'Por favor, insira um e-mail válido!',
362
+ passwordRequired: 'Por favor, insira sua senha!',
363
+ separator: 'ou',
364
+ errorTitle: 'Erro ao autenticar',
365
+ genericError: 'Ocorreu um erro. Por favor, tente novamente.',
366
+ invalidPassword: 'A senha está incorreta.',
367
+ invalidEmailOrPassword: 'O e-mail ou senha estão incorretos.',
368
+ userDisabled: 'Sua conta foi desativada. Por favor, entre em contato com o suporte.',
369
+ authenticationFailed: 'Autenticação falhou'
370
+ },
371
+ ForgotPassword: {
372
+ title: 'Redefinição de senha',
373
+ instructions: 'Informe seu e-mail para receber as instruções de recuperação de senha.',
374
+ emailRequired: 'Por favor, insira seu e-mail!',
375
+ emailInvalid: 'Por favor, insira um e-mail válido!',
376
+ emailPlaceholder: 'E-mail',
377
+ submitButton: 'Recuperar senha',
378
+ backToLogin: 'Voltar para o login',
379
+ resetSuccessMessage: 'Se o endereço informado estiver correto, enviaremos um e-mail com instruções para redefinição de senha.'
380
+ },
381
+ ChangePassword: {
382
+ title: 'Alterar senha',
383
+ passwordInstructions: 'Defina uma nova senha para realizar seu login.',
384
+ newPassword: 'Nova senha',
385
+ confirmPassword: 'Confirmar senha',
386
+ submit: 'Enviar',
387
+ backToLogin: 'Voltar para o login',
388
+ passwordRequired: 'Por favor, insira sua nova senha!',
389
+ passwordLength: '8 caracteres',
390
+ passwordUpperCase: 'uma letra maiúscula',
391
+ passwordLowerCase: 'uma letra minúscula',
392
+ passwordNumbers: 'um número',
393
+ passwordSpecialChars: 'um caractere especial (!@#$%^&*)',
394
+ passwordWeak: 'Senha fraca: Deve ter pelo menos {requirements}',
395
+ passwordsNotMatch: 'As senhas digitadas não coincidem!',
396
+ tokenInvalidTitle: 'Token inválido',
397
+ tokenInvalidOrExpired: 'O token informado é inválido ou está expirado. Inicie o processo de recuperação de senha e tente novamente.',
398
+ passwordChangedSuccess: 'A senha foi alterada com sucesso. Você será redirecionado ao login após fechar essa janela.',
399
+ tokenInvalidRecovery: 'O token informado é inválido ou está expirado. Inicie o processo de recuperação de senha novamente.'
350
400
  },
351
401
  LoginContainer: {
352
402
  theme: 'Tema',
@@ -360,6 +410,12 @@ var __webpack_exports__ = {};
360
410
  },
361
411
  ListCard: {
362
412
  clearFilters: 'Limpar filtros'
413
+ },
414
+ Common: {
415
+ ok: 'Ok',
416
+ close: 'Fechar',
417
+ invalidEmail: 'O e-mail é inválido ou não está registrado.',
418
+ recaptchaFailed: 'Validação reCAPTCHA falhou. Por favor, tente novamente.'
363
419
  }
364
420
  }
365
421
  };
@@ -395,7 +451,52 @@ var __webpack_exports__ = {};
395
451
  },
396
452
  Login: {
397
453
  loginTitle: 'Login',
398
- googleLogin: 'Login with Google'
454
+ googleLogin: 'Login with Google',
455
+ emailLabel: 'Email',
456
+ passwordLabel: 'Password',
457
+ emailPlaceholder: 'Enter your email',
458
+ passwordPlaceholder: 'Enter your password',
459
+ forgotPassword: 'Forgot password?',
460
+ loginButton: 'Login',
461
+ emailRequired: 'Please enter your email!',
462
+ emailInvalid: 'Please enter a valid email!',
463
+ passwordRequired: 'Please enter your password!',
464
+ separator: 'or',
465
+ errorTitle: 'Login Error',
466
+ invalidPassword: 'The password is incorrect.',
467
+ invalidEmailOrPassword: 'The email or password is incorrect.',
468
+ authenticationFailed: 'Authentication failed',
469
+ userDisabled: 'Your account has been disabled. Please contact support.'
470
+ },
471
+ ForgotPassword: {
472
+ title: 'Password reset',
473
+ instructions: 'Enter your email to receive password reset instructions.',
474
+ emailRequired: 'Please enter your email!',
475
+ emailInvalid: 'Please enter a valid email!',
476
+ emailPlaceholder: 'Email',
477
+ submitButton: 'Reset password',
478
+ backToLogin: 'Back to login',
479
+ resetSuccessMessage: 'If the provided email is correct, we will send you an email with instructions to reset your password.'
480
+ },
481
+ ChangePassword: {
482
+ title: 'Change password',
483
+ passwordInstructions: 'Set a new password to log in.',
484
+ newPassword: 'New password',
485
+ confirmPassword: 'Confirm password',
486
+ submit: 'Send',
487
+ backToLogin: 'Back to login',
488
+ passwordRequired: 'Please enter your new password!',
489
+ passwordLength: '8 characters',
490
+ passwordUpperCase: 'an uppercase letter',
491
+ passwordLowerCase: 'a lowercase letter',
492
+ passwordNumbers: 'a number',
493
+ passwordSpecialChars: 'a special character (!@#$%^&*)',
494
+ passwordWeak: 'Weak password: Must have at least {requirements}',
495
+ passwordsNotMatch: 'The passwords do not match!',
496
+ tokenInvalidTitle: 'Invalid Token',
497
+ tokenInvalidOrExpired: 'The provided token is invalid or expired. Start the password recovery process and try again.',
498
+ passwordChangedSuccess: 'Your password has been changed successfully. You will be redirected to the login after closing this window.',
499
+ tokenInvalidRecovery: 'The provided token is invalid or expired. Start the password recovery process again.'
399
500
  },
400
501
  LoginContainer: {
401
502
  theme: 'Theme',
@@ -409,6 +510,13 @@ var __webpack_exports__ = {};
409
510
  },
410
511
  ListCard: {
411
512
  clearFilters: 'Clear filters'
513
+ },
514
+ Common: {
515
+ ok: 'Ok',
516
+ close: 'Close',
517
+ genericError: 'An error occurred. Please try again.',
518
+ invalidEmail: 'The email address is invalid or not registered.',
519
+ recaptchaFailed: 'reCAPTCHA validation failed. Please try again.'
412
520
  }
413
521
  }
414
522
  };
@@ -430,30 +538,61 @@ var __webpack_exports__ = {};
430
538
  });
431
539
  const i18n = i18nLib;
432
540
  const CodebitConfigContext = /*#__PURE__*/ (0, external_react_.createContext)(null);
433
- function CodebitConfigProvider({ children, menuItems, user, onLogout, toggleTheme, isDarkMode, oauthCallbackUrl, version = '1.0', showVersion = true, secondarySidebar, secondarySidebarWidth = 280 }) {
541
+ function CodebitConfigProvider({ children, menuItems, user, onLogout, version = '1.0', showVersion = true, secondarySidebar, secondarySidebarWidth = 280, storeThemeInLocalStorage = true }) {
542
+ const getSystemTheme = ()=>globalThis.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false;
543
+ const [isDarkMode, setIsDarkMode] = (0, external_react_.useState)(()=>{
544
+ if (storeThemeInLocalStorage) {
545
+ const savedTheme = localStorage.getItem('theme');
546
+ if (null !== savedTheme) return 'dark' === savedTheme;
547
+ }
548
+ return getSystemTheme();
549
+ });
550
+ (0, external_react_.useEffect)(()=>{
551
+ const mediaQuery = globalThis.matchMedia('(prefers-color-scheme: dark)');
552
+ const handleThemeChange = (e)=>{
553
+ if (!storeThemeInLocalStorage) return void setIsDarkMode(e.matches);
554
+ const savedTheme = localStorage.getItem('theme');
555
+ if (null === savedTheme) setIsDarkMode(e.matches);
556
+ };
557
+ mediaQuery.addEventListener('change', handleThemeChange);
558
+ return ()=>{
559
+ mediaQuery.removeEventListener('change', handleThemeChange);
560
+ };
561
+ }, [
562
+ storeThemeInLocalStorage
563
+ ]);
564
+ const toggleTheme = ()=>{
565
+ const newTheme = !isDarkMode;
566
+ setIsDarkMode(newTheme);
567
+ if (storeThemeInLocalStorage) localStorage.setItem('theme', newTheme ? 'dark' : 'light');
568
+ };
569
+ const resetToSystemTheme = ()=>{
570
+ if (storeThemeInLocalStorage) localStorage.removeItem('theme');
571
+ setIsDarkMode(getSystemTheme());
572
+ };
434
573
  const contextValue = (0, external_react_.useMemo)(()=>({
435
574
  menuItems,
436
575
  user,
437
576
  onLogout,
438
- toggleTheme,
439
- isDarkMode,
440
- oauthCallbackUrl,
441
577
  version,
442
578
  showVersion,
443
579
  i18n: i18n,
444
580
  secondarySidebar,
445
- secondarySidebarWidth
581
+ secondarySidebarWidth,
582
+ isDarkMode,
583
+ setIsDarkMode,
584
+ toggleTheme,
585
+ resetToSystemTheme
446
586
  }), [
447
587
  menuItems,
448
588
  user,
449
589
  onLogout,
450
- toggleTheme,
451
- isDarkMode,
452
590
  version,
453
591
  showVersion,
454
- oauthCallbackUrl,
455
592
  secondarySidebar,
456
- secondarySidebarWidth
593
+ secondarySidebarWidth,
594
+ isDarkMode,
595
+ storeThemeInLocalStorage
457
596
  ]);
458
597
  return /*#__PURE__*/ (0, jsx_runtime.jsxs)(CodebitConfigContext.Provider, {
459
598
  value: contextValue,
@@ -480,11 +619,9 @@ var __webpack_exports__ = {};
480
619
  onLogout: external_prop_types_default().func.isRequired,
481
620
  version: external_prop_types_default().string,
482
621
  showVersion: external_prop_types_default().bool,
483
- toggleTheme: external_prop_types_default().func.isRequired,
484
- isDarkMode: external_prop_types_default().bool.isRequired,
485
- oauthCallbackUrl: external_prop_types_default().string.isRequired,
486
622
  secondarySidebar: external_prop_types_default().node,
487
- secondarySidebarWidth: external_prop_types_default().number
623
+ secondarySidebarWidth: external_prop_types_default().number,
624
+ storeThemeInLocalStorage: external_prop_types_default().bool
488
625
  };
489
626
  const ThemeToggle = ()=>{
490
627
  const config = (0, external_react_.useContext)(CodebitConfigContext);
@@ -1022,21 +1159,16 @@ var __webpack_exports__ = {};
1022
1159
  justify: 'center',
1023
1160
  className: Login_module.titleRow,
1024
1161
  children: [
1025
- 'string' == typeof title ? /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Title, {
1162
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Title, {
1026
1163
  level: 3,
1027
1164
  className: Login_module.appTitle,
1028
1165
  children: title
1029
- }) : title,
1166
+ }),
1030
1167
  /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Divider, {
1031
1168
  className: Login_module.divider
1032
1169
  })
1033
1170
  ]
1034
1171
  }),
1035
- /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Title, {
1036
- level: 4,
1037
- className: `login-header ${Login_module.loginHeader}`,
1038
- children: t('LoginContainer.signIn')
1039
- }),
1040
1172
  children
1041
1173
  ]
1042
1174
  })
@@ -1051,42 +1183,227 @@ var __webpack_exports__ = {};
1051
1183
  ]).isRequired,
1052
1184
  children: external_prop_types_default().node
1053
1185
  };
1054
- function Login({ title }) {
1055
- const [loading, setLoading] = (0, external_react_.useState)(false);
1056
- const config = (0, external_react_.useContext)(CodebitConfigContext);
1057
- if (!config) throw new Error('Login must be used within CodebitConfigProvider');
1058
- const { oauthCallbackUrl } = config;
1186
+ const logger = new codebit_web_namespaceObject.Logger('Login');
1187
+ const LocalLoginAuthenticationResult = Object.freeze({
1188
+ INVALID_EMAIL: 'INVALID_EMAIL',
1189
+ INVALID_PASSWORD: 'INVALID_PASSWORD',
1190
+ INVALID_EMAIL_OR_PASSWORD: 'INVALID_EMAIL_OR_PASSWORD',
1191
+ RECAPTCHA_VALIDATION_FAIL: 'RECAPTCHA_VALIDATION_FAIL',
1192
+ USER_DISABLED: 'USER_DISABLED',
1193
+ SUCCESS: 'SUCCESS'
1194
+ });
1195
+ function Login({ title, oauthCallbackUrl, navigateToForgotPassword, submitAuthenticateLocalUser }) {
1196
+ const [localLoading, setLocalLoading] = (0, external_react_.useState)(false);
1197
+ const [googleLoading, setGoogleLoading] = (0, external_react_.useState)(false);
1198
+ const [form] = external_antd_namespaceObject.Form.useForm();
1059
1199
  const { t } = useCodebitWebAntdTranslation();
1200
+ const { modal } = external_antd_namespaceObject.App.useApp();
1201
+ const navigate = (0, external_react_router_namespaceObject.useNavigate)();
1202
+ const context = {
1203
+ navigate
1204
+ };
1060
1205
  const handleGoogleLogin = ()=>{
1061
- setLoading(true);
1206
+ setGoogleLoading(true);
1062
1207
  setTimeout(()=>{
1063
- console.debug('Redirecting to Google Login...');
1064
1208
  globalThis.location.assign(oauthCallbackUrl);
1209
+ setGoogleLoading(false);
1065
1210
  }, 1000);
1066
1211
  };
1067
- return /*#__PURE__*/ (0, jsx_runtime.jsx)(LoginContainer, {
1212
+ const handleLocalLogin = async (values)=>{
1213
+ setLocalLoading(true);
1214
+ try {
1215
+ const { email, password } = values;
1216
+ const result = await submitAuthenticateLocalUser(email, password, context);
1217
+ switch(result){
1218
+ case LocalLoginAuthenticationResult.SUCCESS:
1219
+ break;
1220
+ case LocalLoginAuthenticationResult.INVALID_EMAIL:
1221
+ form.setFields([
1222
+ {
1223
+ name: 'email',
1224
+ errors: [
1225
+ t('Common.invalidEmail')
1226
+ ]
1227
+ }
1228
+ ]);
1229
+ break;
1230
+ case LocalLoginAuthenticationResult.INVALID_PASSWORD:
1231
+ form.setFields([
1232
+ {
1233
+ name: 'password',
1234
+ errors: [
1235
+ t('Login.invalidPassword')
1236
+ ]
1237
+ }
1238
+ ]);
1239
+ break;
1240
+ case LocalLoginAuthenticationResult.INVALID_EMAIL_OR_PASSWORD:
1241
+ form.setFields([
1242
+ {
1243
+ name: 'email',
1244
+ errors: [
1245
+ t('Login.invalidEmailOrPassword')
1246
+ ]
1247
+ },
1248
+ {
1249
+ name: 'password',
1250
+ errors: [
1251
+ t('Login.invalidEmailOrPassword')
1252
+ ]
1253
+ }
1254
+ ]);
1255
+ break;
1256
+ case LocalLoginAuthenticationResult.RECAPTCHA_VALIDATION_FAIL:
1257
+ modal.error({
1258
+ title: t('Login.authenticationFailed'),
1259
+ content: t('Common.recaptchaFailed'),
1260
+ okText: t('Common.ok')
1261
+ });
1262
+ break;
1263
+ case LocalLoginAuthenticationResult.USER_DISABLED:
1264
+ modal.error({
1265
+ title: t('Login.authenticationFailed'),
1266
+ content: t('Login.userDisabled'),
1267
+ okText: t('Common.ok')
1268
+ });
1269
+ break;
1270
+ default:
1271
+ logger.warn('Unhandled authentication result:', result);
1272
+ modal.error({
1273
+ title: t('Login.authenticationFailed'),
1274
+ content: t('Common.genericError'),
1275
+ okText: t('Common.ok')
1276
+ });
1277
+ }
1278
+ } catch (error) {
1279
+ logger.error(error);
1280
+ react_namespaceObject.captureException(error);
1281
+ modal.error({
1282
+ title: t('Login.errorTitle'),
1283
+ content: t('Common.genericError'),
1284
+ okText: t('Common.ok')
1285
+ });
1286
+ } finally{
1287
+ setLocalLoading(false);
1288
+ }
1289
+ };
1290
+ const handleNavigateToForgotPassword = async ()=>{
1291
+ await navigateToForgotPassword(context);
1292
+ };
1293
+ return /*#__PURE__*/ (0, jsx_runtime.jsxs)(LoginContainer, {
1068
1294
  title: title || t('Login.loginTitle'),
1069
- children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Flex, {
1070
- vertical: true,
1071
- align: 'center',
1072
- gap: 20,
1073
- children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Button, {
1074
- type: 'primary',
1295
+ children: [
1296
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Title, {
1297
+ level: 4,
1298
+ className: `${Login_module.loginHeader}`,
1299
+ children: t('LoginContainer.signIn')
1300
+ }),
1301
+ /*#__PURE__*/ (0, jsx_runtime.jsxs)(external_antd_namespaceObject.Space, {
1302
+ direction: 'vertical',
1075
1303
  size: 'large',
1076
- icon: /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.GoogleOutlined, {}),
1077
- loading: loading,
1078
- onClick: handleGoogleLogin,
1079
- block: true,
1080
- children: t('Login.googleLogin')
1304
+ style: {
1305
+ width: '100%'
1306
+ },
1307
+ children: [
1308
+ submitAuthenticateLocalUser && /*#__PURE__*/ (0, jsx_runtime.jsxs)(external_antd_namespaceObject.Form, {
1309
+ name: 'login-form',
1310
+ layout: 'vertical',
1311
+ onFinish: handleLocalLogin,
1312
+ children: [
1313
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Form.Item, {
1314
+ name: 'email',
1315
+ label: t('Login.emailLabel'),
1316
+ rules: [
1317
+ {
1318
+ required: true,
1319
+ message: t('Login.emailRequired')
1320
+ },
1321
+ {
1322
+ type: 'email',
1323
+ message: t('Login.emailInvalid')
1324
+ }
1325
+ ],
1326
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Input, {
1327
+ prefix: /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.MailOutlined, {}),
1328
+ placeholder: t('Login.emailPlaceholder'),
1329
+ size: 'large'
1330
+ })
1331
+ }),
1332
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Form.Item, {
1333
+ name: 'password',
1334
+ label: t('Login.passwordLabel'),
1335
+ rules: [
1336
+ {
1337
+ required: true,
1338
+ message: t('Login.passwordRequired')
1339
+ }
1340
+ ],
1341
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Input.Password, {
1342
+ prefix: /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.LockOutlined, {}),
1343
+ placeholder: t('Login.passwordPlaceholder'),
1344
+ size: 'large'
1345
+ })
1346
+ }),
1347
+ navigateToForgotPassword && /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Form.Item, {
1348
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Row, {
1349
+ justify: 'end',
1350
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Col, {
1351
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Link, {
1352
+ onClick: handleNavigateToForgotPassword,
1353
+ children: t('Login.forgotPassword')
1354
+ })
1355
+ })
1356
+ })
1357
+ }),
1358
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Form.Item, {
1359
+ style: {
1360
+ marginBottom: 0
1361
+ },
1362
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Button, {
1363
+ type: 'primary',
1364
+ htmlType: 'submit',
1365
+ size: 'large',
1366
+ loading: localLoading,
1367
+ disabled: googleLoading,
1368
+ block: true,
1369
+ children: t('Login.loginButton')
1370
+ })
1371
+ })
1372
+ ]
1373
+ }),
1374
+ submitAuthenticateLocalUser && oauthCallbackUrl && /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Divider, {
1375
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Text, {
1376
+ type: 'secondary',
1377
+ children: t('Login.separator')
1378
+ })
1379
+ }),
1380
+ oauthCallbackUrl && /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Flex, {
1381
+ vertical: true,
1382
+ align: 'center',
1383
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Button, {
1384
+ type: 'primary',
1385
+ size: 'large',
1386
+ icon: /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.GoogleOutlined, {}),
1387
+ loading: googleLoading,
1388
+ onClick: handleGoogleLogin,
1389
+ disabled: localLoading,
1390
+ block: true,
1391
+ children: t('Login.googleLogin')
1392
+ })
1393
+ })
1394
+ ]
1081
1395
  })
1082
- })
1396
+ ]
1083
1397
  });
1084
1398
  }
1085
1399
  Login.propTypes = {
1086
1400
  title: external_prop_types_default().oneOfType([
1087
1401
  external_prop_types_default().string,
1088
1402
  external_prop_types_default().node
1089
- ])
1403
+ ]),
1404
+ oauthCallbackUrl: external_prop_types_default().string,
1405
+ navigateToForgotPassword: external_prop_types_default().func,
1406
+ submitAuthenticateLocalUser: external_prop_types_default().func
1090
1407
  };
1091
1408
  const HandleError_module = {
1092
1409
  container: "container-A_JXAc",
@@ -1152,14 +1469,199 @@ var __webpack_exports__ = {};
1152
1469
  HandleError.propTypes = {
1153
1470
  title: external_prop_types_default().string
1154
1471
  };
1472
+ const ForgotPassword_logger = new codebit_web_namespaceObject.Logger('ForgotPassword');
1473
+ const LocalLoginRecoveryPasswordResult = Object.freeze({
1474
+ INVALID_EMAIL: 'INVALID_EMAIL',
1475
+ RECAPTCHA_VALIDATION_FAIL: 'RECAPTCHA_VALIDATION_FAIL',
1476
+ SUCCESS: 'SUCCESS'
1477
+ });
1478
+ const ForgotPassword = ({ navigateToLogin, submitRecoveryPassword })=>{
1479
+ const [form] = external_antd_namespaceObject.Form.useForm();
1480
+ const { modal } = external_antd_namespaceObject.App.useApp();
1481
+ const [loading, setLoading] = (0, external_react_.useState)(false);
1482
+ const { t } = useCodebitWebAntdTranslation();
1483
+ const navigate = (0, external_react_router_namespaceObject.useNavigate)();
1484
+ const context = {
1485
+ navigate
1486
+ };
1487
+ const handleNavigateToLogin = async ()=>{
1488
+ await navigateToLogin(context);
1489
+ };
1490
+ const handleRecoveryPassword = async (values)=>{
1491
+ setLoading(true);
1492
+ try {
1493
+ setLoading(true);
1494
+ const result = await submitRecoveryPassword(values.email, context);
1495
+ switch(result){
1496
+ case LocalLoginRecoveryPasswordResult.SUCCESS:
1497
+ modal.success({
1498
+ title: t('ForgotPassword.title'),
1499
+ content: t('ForgotPassword.resetSuccessMessage'),
1500
+ onOk: handleNavigateToLogin,
1501
+ okText: t('Common.ok')
1502
+ });
1503
+ break;
1504
+ case LocalLoginRecoveryPasswordResult.INVALID_EMAIL:
1505
+ form.setFields([
1506
+ {
1507
+ name: 'email',
1508
+ errors: [
1509
+ t('Common.invalidEmail')
1510
+ ]
1511
+ }
1512
+ ]);
1513
+ break;
1514
+ case LocalLoginRecoveryPasswordResult.RECAPTCHA_VALIDATION_FAIL:
1515
+ modal.error({
1516
+ title: t('ForgotPassword.title'),
1517
+ content: t('Common.recaptchaFailed'),
1518
+ okText: t('Common.ok')
1519
+ });
1520
+ break;
1521
+ }
1522
+ } catch (error) {
1523
+ ForgotPassword_logger.error(error);
1524
+ react_namespaceObject.captureException(error);
1525
+ modal.error({
1526
+ title: t('ForgotPassword.title'),
1527
+ content: t('Common.genericError'),
1528
+ okText: t('Common.ok')
1529
+ });
1530
+ } finally{
1531
+ setLoading(false);
1532
+ }
1533
+ };
1534
+ return /*#__PURE__*/ (0, jsx_runtime.jsx)(LoginContainer, {
1535
+ title: t('ForgotPassword.title'),
1536
+ children: /*#__PURE__*/ (0, jsx_runtime.jsxs)(external_antd_namespaceObject.Form, {
1537
+ form: form,
1538
+ name: 'forgot-password-form',
1539
+ onFinish: handleRecoveryPassword,
1540
+ initialValues: {
1541
+ email: ''
1542
+ },
1543
+ style: {
1544
+ width: '100%'
1545
+ },
1546
+ children: [
1547
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Paragraph, {
1548
+ style: {
1549
+ textAlign: 'center'
1550
+ },
1551
+ children: t('ForgotPassword.instructions')
1552
+ }),
1553
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Form.Item, {
1554
+ name: 'email',
1555
+ rules: [
1556
+ {
1557
+ required: true,
1558
+ message: t('ForgotPassword.emailRequired')
1559
+ },
1560
+ {
1561
+ type: 'email',
1562
+ message: t('ForgotPassword.emailInvalid')
1563
+ }
1564
+ ],
1565
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Input, {
1566
+ prefix: /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.MailOutlined, {}),
1567
+ placeholder: t('ForgotPassword.emailPlaceholder'),
1568
+ size: 'large'
1569
+ })
1570
+ }),
1571
+ /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Form.Item, {
1572
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Button, {
1573
+ type: 'primary',
1574
+ htmlType: 'submit',
1575
+ size: 'large',
1576
+ loading: loading,
1577
+ style: {
1578
+ width: '100%'
1579
+ },
1580
+ children: t('ForgotPassword.submitButton')
1581
+ })
1582
+ }),
1583
+ navigateToLogin && /*#__PURE__*/ (0, jsx_runtime.jsx)("div", {
1584
+ style: {
1585
+ textAlign: 'center'
1586
+ },
1587
+ children: /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Typography.Link, {
1588
+ onClick: handleNavigateToLogin,
1589
+ children: t('ForgotPassword.backToLogin')
1590
+ })
1591
+ })
1592
+ ]
1593
+ })
1594
+ });
1595
+ };
1596
+ ForgotPassword.propTypes = {
1597
+ navigateToLogin: external_prop_types_default().func,
1598
+ submitRecoveryPassword: external_prop_types_default().func.isRequired
1599
+ };
1600
+ const CodebitTheme = (props)=>{
1601
+ const { token } = external_antd_namespaceObject.theme.useToken();
1602
+ const config = (0, external_react_.useContext)(CodebitConfigContext);
1603
+ if (!config) throw new Error('CodebitTheme must be used within CodebitConfigProvider');
1604
+ const { isDarkMode } = config;
1605
+ (0, external_react_.useEffect)(()=>{
1606
+ const rootElement = document.getElementById('root');
1607
+ if (isDarkMode) {
1608
+ document.body.classList.add('dark-theme');
1609
+ rootElement.classList.add('dark-theme');
1610
+ } else {
1611
+ document.body.classList.remove('dark-theme');
1612
+ rootElement.classList.remove('dark-theme');
1613
+ }
1614
+ document.documentElement.style.setProperty('--color-primary', isDarkMode ? '#589C75' : '#29D266');
1615
+ }, [
1616
+ isDarkMode,
1617
+ token.colorBgContainer
1618
+ ]);
1619
+ return /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.ConfigProvider, {
1620
+ theme: {
1621
+ algorithm: isDarkMode ? external_antd_namespaceObject.theme.darkAlgorithm : external_antd_namespaceObject.theme.defaultAlgorithm,
1622
+ token: {
1623
+ colorPrimary: isDarkMode ? '#589C75' : '#29D266',
1624
+ colorInfo: isDarkMode ? '#589C75' : '#29D266',
1625
+ colorLink: isDarkMode ? '#589C75' : '#29D266'
1626
+ },
1627
+ components: {
1628
+ Menu: {
1629
+ itemSelectedBg: isDarkMode ? '#589C75' : '#29D266',
1630
+ itemSelectedColor: '#FAFAFA',
1631
+ darkItemSelectedBg: isDarkMode ? '#589C75' : '#29D266',
1632
+ darkItemSelectedColor: '#FAFAFA'
1633
+ },
1634
+ Button: {
1635
+ colorPrimary: isDarkMode ? '#589C75' : '#29D266',
1636
+ colorPrimaryHover: isDarkMode ? '#6DB589' : '#3FE67F',
1637
+ colorPrimaryActive: isDarkMode ? '#4A8361' : '#1FBF4D'
1638
+ },
1639
+ Layout: {
1640
+ siderBg: isDarkMode ? '#1a1a1a' : '#001529',
1641
+ triggerBg: isDarkMode ? '#589C75' : '#29D266',
1642
+ triggerColor: '#FAFAFA'
1643
+ },
1644
+ Header: {
1645
+ colorPrimary: isDarkMode ? '#589C75' : '#29D266'
1646
+ }
1647
+ }
1648
+ },
1649
+ ...props
1650
+ });
1651
+ };
1652
+ CodebitTheme.propTypes = {};
1155
1653
  })();
1156
1654
  exports.CodebitConfigContext = __webpack_exports__.CodebitConfigContext;
1157
1655
  exports.CodebitConfigProvider = __webpack_exports__.CodebitConfigProvider;
1656
+ exports.CodebitTheme = __webpack_exports__.CodebitTheme;
1158
1657
  exports.CssTokenBridge = __webpack_exports__.CssTokenBridge;
1159
1658
  exports.ErrorRetry = __webpack_exports__.ErrorRetry;
1160
1659
  exports.FilterContainer = __webpack_exports__.FilterContainer;
1660
+ exports.ForgotPassword = __webpack_exports__.ForgotPassword;
1161
1661
  exports.HandleError = __webpack_exports__.HandleError;
1162
1662
  exports.ListCard = __webpack_exports__.ListCard;
1663
+ exports.LocalLoginAuthenticationResult = __webpack_exports__.LocalLoginAuthenticationResult;
1664
+ exports.LocalLoginRecoveryPasswordResult = __webpack_exports__.LocalLoginRecoveryPasswordResult;
1163
1665
  exports.LoggedMainContainer = __webpack_exports__.LoggedMainContainer;
1164
1666
  exports.Login = __webpack_exports__.Login;
1165
1667
  exports.LoginContainer = __webpack_exports__.LoginContainer;
@@ -1168,11 +1670,15 @@ exports.ThemeToggle = __webpack_exports__.ThemeToggle;
1168
1670
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
1169
1671
  "CodebitConfigContext",
1170
1672
  "CodebitConfigProvider",
1673
+ "CodebitTheme",
1171
1674
  "CssTokenBridge",
1172
1675
  "ErrorRetry",
1173
1676
  "FilterContainer",
1677
+ "ForgotPassword",
1174
1678
  "HandleError",
1175
1679
  "ListCard",
1680
+ "LocalLoginAuthenticationResult",
1681
+ "LocalLoginRecoveryPasswordResult",
1176
1682
  "LoggedMainContainer",
1177
1683
  "Login",
1178
1684
  "LoginContainer",
package/dist/index.css CHANGED
@@ -178,7 +178,7 @@
178
178
  }
179
179
 
180
180
  .themeRow-ZhL0jS {
181
- margin-bottom: 16px;
181
+ margin-bottom: var(--antd-margin);
182
182
  }
183
183
 
184
184
  .loginCard-k07Wjb {
@@ -190,7 +190,7 @@
190
190
  }
191
191
 
192
192
  .titleRow-XMkFzo {
193
- margin-bottom: 20px;
193
+ margin-bottom: var(--antd-margin-sm);
194
194
  }
195
195
 
196
196
  .appTitle-tL41k9 {
@@ -198,12 +198,13 @@
198
198
  }
199
199
 
200
200
  .divider-oJpEKR {
201
- margin: 12px 0;
201
+ margin: var(--antd-margin-xs) 0;
202
202
  }
203
203
 
204
204
  .loginHeader-bBGO5B {
205
205
  text-align: center;
206
- margin-bottom: 24px;
206
+ margin-top: 0;
207
+ margin-bottom: var(--antd-margin);
207
208
  font-weight: normal;
208
209
  }
209
210
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './locales';
2
1
  export * from './components/index';
3
2
  export * from './contexts/index';
4
3
  export * from './public-pages/index';
4
+ export * from './theme';
@@ -0,0 +1,35 @@
1
+ import * as React from 'react';
2
+ import { LocalLoginConfContext } from './Login';
3
+
4
+ /** Props for the ChangePassword component (none required) */
5
+ export interface ChangePasswordProps {
6
+ /** Validates password reset token in the current context */
7
+ checkToken: (context: LocalLoginConfContext) => Promise<Boolean>;
8
+
9
+ /** Submits new password using validated token from context */
10
+ submitChangePassword?: (
11
+ password: string,
12
+ context: LocalLoginConfContext,
13
+ ) => Promise<LocalLoginChangePasswordResult>;
14
+ }
15
+
16
+ export enum LocalLoginChangePasswordResult {
17
+ INVALID_TOKEN = 'INVALID_TOKEN',
18
+ SUCCESS = 'SUCCESS',
19
+ }
20
+
21
+ /**
22
+ * React component for password change functionality.
23
+ *
24
+ * Handles the complete password reset workflow including:
25
+ *
26
+ * - Token validation
27
+ * - Password strength validation
28
+ * - Form submission
29
+ * - Success/error messaging
30
+ *
31
+ * @param props - Component props (none required)
32
+ * @returns JSX element with password change interface
33
+ * @throws Error if used outside CodebitConfigProvider context
34
+ */
35
+ export function ChangePassword(props: ChangePasswordProps): React.JSX.Element;
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+ import { LocalLoginConfContext } from './Login';
3
+
4
+ /** Props for the ForgotPassword component */
5
+ export interface ForgotPasswordProps {
6
+ /** Navigates to the login screen when authentication is required */
7
+ navigateToLogin: (context: LocalLoginConfContext) => Promise<void>;
8
+
9
+ /** Submits password recovery request for the specified email */
10
+ submitRecoveryPassword?: (
11
+ email: string,
12
+ context: LocalLoginConfContext,
13
+ ) => Promise<LocalLoginRecoveryPasswordResult>;
14
+ }
15
+
16
+ export enum LocalLoginRecoveryPasswordResult {
17
+ INVALID_EMAIL = 'INVALID_EMAIL',
18
+ RECAPTCHA_VALIDATION_FAIL = 'RECAPTCHA_VALIDATION_FAIL',
19
+ SUCCESS = 'SUCCESS',
20
+ }
21
+
22
+ /**
23
+ * Password reset component for initiating account recovery.
24
+ *
25
+ * Handles the complete password reset workflow:
26
+ *
27
+ * - Email address input
28
+ * - Reset link request
29
+ * - Success/error messaging
30
+ *
31
+ * Requires usage within `CodebitConfigProvider` context to access authentication settings.
32
+ *
33
+ * @param props - Component props
34
+ * @returns JSX element with password reset interface
35
+ * @throws Error if used outside CodebitConfigProvider context
36
+ */
37
+ export function ForgotPassword(props: ForgotPasswordProps): React.JSX.Element;
@@ -1,19 +1,54 @@
1
1
  import * as React from 'react';
2
+ import { NavigateFunction, Params } from 'react-router';
3
+
4
+ export enum LocalLoginAuthenticationResult {
5
+ /** Email is invalid or not registered */
6
+ INVALID_EMAIL = 'INVALID_EMAIL',
7
+ /** Password is incorrect */
8
+ INVALID_PASSWORD = 'INVALID_PASSWORD',
9
+ /** Email or password combination is invalid */
10
+ INVALID_EMAIL_OR_PASSWORD = 'INVALID_EMAIL_OR_PASSWORD',
11
+ /** ReCAPTCHA validation failed */
12
+ RECAPTCHA_VALIDATION_FAIL = 'RECAPTCHA_VALIDATION_FAIL',
13
+ /** User account is disabled */
14
+ USER_DISABLED = 'USER_DISABLED',
15
+ /** Authentication was successful */
16
+ SUCCESS = 'SUCCESS',
17
+ }
18
+
19
+ /** Context for local login configuration */
20
+ export type LocalLoginConfContext = {
21
+ navigate: NavigateFunction;
22
+ params?: Readonly<Params<string>>;
23
+ };
2
24
 
3
25
  /** Props for the Login component */
4
26
  export interface LoginProps {
5
- /** Title for the login screen */
27
+ /** Optional title for the login screen (overrides localized default) */
6
28
  title?: string | React.ReactNode;
29
+
30
+ /** OAuth callback URL for authentication */
31
+ oauthCallbackUrl?: string;
32
+
33
+ /** Function to authenticate a local user */
34
+ submitAuthenticateLocalUser?: (
35
+ email: string,
36
+ password: string,
37
+ context: LocalLoginConfContext,
38
+ ) => Promise<LocalLoginAuthenticationResult>;
39
+
40
+ /** Optional function to initiate password recovery process. When omitted, password reset functionality will be disabled. */
41
+ navigateToForgotPassword?: (
42
+ context: LocalLoginConfContext,
43
+ ) => Promise<void>;
7
44
  }
8
45
 
9
46
  /**
10
- * Login component that renders a login screen with Google OAuth integration.
47
+ * Login component that renders both Google OAuth and email/password login options.
11
48
  *
12
- * This component displays a login button that redirects to the configured OAuth callback URL. It must be used within a CodebitConfigProvider context to access the OAuth
13
- * configuration.
49
+ * This component displays both Google login button and local authentication form (when configured), requiring usage within CodebitConfigProvider to access authentication settings.
14
50
  *
15
51
  * @param props - Component props
16
- * @returns Login screen with Google login button
17
- * @throws Error if used outside CodebitConfigProvider context
52
+ * @returns JSX element with complete login interface
18
53
  */
19
54
  export function Login(props: LoginProps): React.JSX.Element;
@@ -1,3 +1,4 @@
1
1
  export * from './Login';
2
2
  export * from './LoginContainer';
3
3
  export * from './HandleError';
4
+ export * from './ForgotPassword';
@@ -0,0 +1,5 @@
1
+ import { ConfigProviderProps } from 'antd';
2
+ import { FC } from 'react';
3
+
4
+ /** A theme provider component that configures Ant Design with Codebit's custom theme Handles both light and dark mode variants */
5
+ export declare const CodebitTheme: FC<ConfigProviderProps>;
@@ -0,0 +1 @@
1
+ export * from './CodebitTheme';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebit-programando-solucoes/codebit-web-antd",
3
- "version": "1.1.9",
3
+ "version": "1.1.16",
4
4
  "main": "./dist/index.cjs",
5
5
  "type": "module",
6
6
  "types": "./src/index.d.ts",
@@ -26,18 +26,18 @@
26
26
  "antd": "^5.28.1",
27
27
  "i18next": "^25.6.2",
28
28
  "prop-types": "^15.8.1",
29
- "react": "^19.1.1",
30
- "react-dom": "^19.2.0",
31
- "react-i18next": "^16.3.3",
32
- "react-router": "^7.9.6",
29
+ "react-i18next": "^16.3.5",
30
+ "react-router": "7.12.0",
33
31
  "sass": "^1.94.0"
34
32
  },
35
33
  "devDependencies": {
36
- "@eslint/js": "^9.39.1",
37
- "@rsbuild/plugin-react": "^1.4.1",
38
- "@rsbuild/plugin-sass": "^1.4.0",
34
+ "@eslint/js": "^9.39.2",
35
+ "@rsbuild/plugin-react": "1.4.3",
36
+ "@rsbuild/plugin-sass": "1.4.1",
39
37
  "@rslib/core": "^0.18.0",
40
38
  "@swc/core": "^1.13.5",
39
+ "react": "^19.2.0",
40
+ "react-dom": "^19.2.0",
41
41
  "cross-env": "^10.0.0",
42
42
  "css-loader": "^7.1.2",
43
43
  "del-cli": "^6.0.0",