anima-ds-nucleus 1.0.17 → 1.0.18
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/anima-ds-nucleus.css +1 -1
- package/dist/anima-ds.cjs.js +111 -95
- package/dist/anima-ds.esm.js +3521 -2714
- package/package.json +1 -1
- package/src/components/DataDisplay/Card/CardTituloCorto.jsx +23 -2
- package/src/components/DataDisplay/Card/CardTituloCortoMasEstado.jsx +23 -2
- package/src/components/DataDisplay/Card/CardTituloLargo.jsx +23 -2
- package/src/components/DataDisplay/Card/CardTituloLargoMasEstado.jsx +23 -2
- package/src/components/Inputs/PasswordInput/PasswordInput.jsx +85 -0
- package/src/components/Layout/Header/HeaderConBuscador.stories.jsx +1 -0
- package/src/components/Layout/Header/HeaderCore.jsx +12 -12
- package/src/components/Layout/Header/HeaderGeneral.jsx +28 -2
- package/src/components/Layout/Header/HeaderPoint.jsx +3 -3
- package/src/components/Layout/NavPoint/NavPoint.jsx +36 -21
- package/src/components/Layout/SaludoConFechaDashboard/SaludoConFechaDashboard.jsx +0 -1
- package/src/components/Layout/Sidebar/SidebarCore.jsx +82 -30
- package/src/components/Views/ForgotPassword/ForgotPassword.jsx +341 -0
- package/src/components/Views/ForgotPassword/ForgotPassword.stories.jsx +186 -0
- package/src/components/Views/LoginForm/LoginForm.jsx +332 -43
- package/src/components/Views/LoginForm/LoginForm.stories.jsx +182 -4
- package/src/i18n/config.js +12 -0
- package/src/index.js +1 -0
|
@@ -308,7 +308,7 @@ const SidebarCoreMobile = ({
|
|
|
308
308
|
</div>
|
|
309
309
|
|
|
310
310
|
{/* Items de la sección */}
|
|
311
|
-
<div
|
|
311
|
+
<div>
|
|
312
312
|
{section.items.map((item) => (
|
|
313
313
|
<div key={item.id} className="px-4">
|
|
314
314
|
<button
|
|
@@ -524,6 +524,7 @@ export const SidebarCore = ({
|
|
|
524
524
|
footerCollapsedContent, // Contenido a mostrar en el footer cuando está colapsado (puede ser string, ReactNode, o objeto con icon y text)
|
|
525
525
|
coreContainerStyle, // Estilos personalizados para el contenedor de LogoHexa y "Core" (solo mobile)
|
|
526
526
|
coreTextStyle, // Estilos personalizados para el texto "Core" (solo mobile)
|
|
527
|
+
fluidWidth = false, // Si es true, el root respeta el ancho del contenedor (grid/flex) en lugar de usar anchos fijos
|
|
527
528
|
className = '',
|
|
528
529
|
...props
|
|
529
530
|
}) => {
|
|
@@ -609,15 +610,31 @@ export const SidebarCore = ({
|
|
|
609
610
|
);
|
|
610
611
|
}
|
|
611
612
|
|
|
613
|
+
// Determinar estilos del root según fluidWidth
|
|
614
|
+
const rootWidthStyles = fluidWidth
|
|
615
|
+
? {
|
|
616
|
+
width: '100%',
|
|
617
|
+
maxWidth: '100%',
|
|
618
|
+
minWidth: 0,
|
|
619
|
+
boxSizing: 'border-box',
|
|
620
|
+
}
|
|
621
|
+
: isCollapsed
|
|
622
|
+
? { width: desktopCollapsedWidth }
|
|
623
|
+
: {};
|
|
624
|
+
|
|
625
|
+
const rootClassName = fluidWidth
|
|
626
|
+
? `bg-white transition-all duration-300 ease-in-out h-full ${className}`
|
|
627
|
+
: `bg-white transition-all duration-300 ease-in-out h-full ${
|
|
628
|
+
isCollapsed ? '' : 'w-64'
|
|
629
|
+
} ${className}`;
|
|
630
|
+
|
|
612
631
|
return (
|
|
613
632
|
<aside
|
|
614
|
-
className={
|
|
615
|
-
isCollapsed ? '' : 'w-64'
|
|
616
|
-
} ${className}`}
|
|
633
|
+
className={rootClassName}
|
|
617
634
|
style={{
|
|
618
635
|
border: '1px solid #C4C4C4',
|
|
619
636
|
borderRadius: '16px',
|
|
620
|
-
...
|
|
637
|
+
...rootWidthStyles,
|
|
621
638
|
}}
|
|
622
639
|
{...props}
|
|
623
640
|
>
|
|
@@ -631,21 +648,21 @@ export const SidebarCore = ({
|
|
|
631
648
|
flex items-center justify-between hover:bg-gray-50 transition-colors ${desktopCompanyButtonClassName}`}
|
|
632
649
|
style={desktopCompanyButtonStyle}
|
|
633
650
|
>
|
|
634
|
-
<div className="flex items-center space-x-3">
|
|
651
|
+
<div className="flex items-center space-x-3 min-w-0 flex-1">
|
|
635
652
|
{/* Logo hexagonal */}
|
|
636
653
|
{companyLogo ? (
|
|
637
654
|
<img
|
|
638
655
|
src={companyLogo}
|
|
639
656
|
alt={companyName}
|
|
640
|
-
className="w-8 h-8 rounded"
|
|
657
|
+
className="w-8 h-8 rounded flex-shrink-0"
|
|
641
658
|
/>
|
|
642
659
|
) : (
|
|
643
|
-
<LogoHexa width={36} height={40} />
|
|
660
|
+
<LogoHexa width={36} height={40} className="flex-shrink-0" />
|
|
644
661
|
)}
|
|
645
662
|
{/* Nombre de la empresa */}
|
|
646
663
|
<Typography
|
|
647
664
|
variant="body-md"
|
|
648
|
-
className={desktopCompanyNameClassName}
|
|
665
|
+
className={`${desktopCompanyNameClassName} min-w-0`}
|
|
649
666
|
style={{
|
|
650
667
|
color: '#223B40',
|
|
651
668
|
fontFamily: 'IBM Plex Sans',
|
|
@@ -656,6 +673,9 @@ export const SidebarCore = ({
|
|
|
656
673
|
letterSpacing: '0px',
|
|
657
674
|
opacity: 1,
|
|
658
675
|
transform: 'rotate(0deg)',
|
|
676
|
+
overflow: 'hidden',
|
|
677
|
+
textOverflow: 'ellipsis',
|
|
678
|
+
whiteSpace: 'nowrap',
|
|
659
679
|
...(desktopCompanyNameStyle || {}),
|
|
660
680
|
}}
|
|
661
681
|
>
|
|
@@ -784,9 +804,9 @@ export const SidebarCore = ({
|
|
|
784
804
|
>
|
|
785
805
|
<button
|
|
786
806
|
onClick={() => onItemClick && onItemClick('inicio')}
|
|
787
|
-
className={`
|
|
788
|
-
isCollapsed ? 'justify-center' : '
|
|
789
|
-
} ${isCollapsed ? '' : '
|
|
807
|
+
className={`flex items-center ${
|
|
808
|
+
isCollapsed ? 'justify-center' : 'justify-between'
|
|
809
|
+
} ${isCollapsed ? '' : 'rounded-lg'} transition-all duration-200 ${
|
|
790
810
|
activeItem === 'inicio'
|
|
791
811
|
? ''
|
|
792
812
|
: 'color-gray-700 hover:bg-gray-100'
|
|
@@ -799,13 +819,22 @@ export const SidebarCore = ({
|
|
|
799
819
|
backgroundColor: 'transparent',
|
|
800
820
|
borderRadius: '8px',
|
|
801
821
|
}
|
|
802
|
-
:
|
|
803
|
-
|
|
804
|
-
|
|
822
|
+
: {
|
|
823
|
+
width: '228px',
|
|
824
|
+
height: '40px',
|
|
825
|
+
paddingTop: '8px',
|
|
826
|
+
paddingRight: '12px',
|
|
827
|
+
paddingBottom: '8px',
|
|
828
|
+
paddingLeft: '12px',
|
|
829
|
+
gap: '12px',
|
|
830
|
+
borderRadius: '8px',
|
|
831
|
+
opacity: 1,
|
|
832
|
+
backgroundColor: activeItem === 'inicio' ? '#2D5C63' : 'transparent',
|
|
833
|
+
}
|
|
805
834
|
}
|
|
806
835
|
>
|
|
807
836
|
<div
|
|
808
|
-
className={`flex items-center ${isCollapsed ? '' : ''}`}
|
|
837
|
+
className={`flex items-center ${isCollapsed ? '' : 'min-w-0 flex-1'}`}
|
|
809
838
|
style={
|
|
810
839
|
isCollapsed
|
|
811
840
|
? {
|
|
@@ -813,7 +842,9 @@ export const SidebarCore = ({
|
|
|
813
842
|
columnGap: `${desktopCollapsedOverlapColumnGapPx}px`,
|
|
814
843
|
width: '100%',
|
|
815
844
|
}
|
|
816
|
-
: {
|
|
845
|
+
: {
|
|
846
|
+
minWidth: 0,
|
|
847
|
+
}
|
|
817
848
|
}
|
|
818
849
|
>
|
|
819
850
|
{isCollapsed ? (
|
|
@@ -883,8 +914,13 @@ export const SidebarCore = ({
|
|
|
883
914
|
<Typography
|
|
884
915
|
variant="body-lg"
|
|
885
916
|
weight={activeItem === 'inicio' ? desktopItemLabelWeightActive : desktopItemLabelWeightInactive}
|
|
886
|
-
className={activeItem === 'inicio' ? 'color-white' : 'color-gray-700'}
|
|
887
|
-
style={{
|
|
917
|
+
className={`${activeItem === 'inicio' ? 'color-white' : 'color-gray-700'} min-w-0`}
|
|
918
|
+
style={{
|
|
919
|
+
fontSize: desktopItemLabelFontSize,
|
|
920
|
+
overflow: 'hidden',
|
|
921
|
+
textOverflow: 'ellipsis',
|
|
922
|
+
whiteSpace: 'nowrap',
|
|
923
|
+
}}
|
|
888
924
|
>
|
|
889
925
|
Inicio
|
|
890
926
|
</Typography>
|
|
@@ -992,7 +1028,7 @@ export const SidebarCore = ({
|
|
|
992
1028
|
)}
|
|
993
1029
|
|
|
994
1030
|
{/* Items de la sección */}
|
|
995
|
-
<div
|
|
1031
|
+
<div>
|
|
996
1032
|
{section.items.map((item) => (
|
|
997
1033
|
<div key={item.id} className="px-4">
|
|
998
1034
|
<div
|
|
@@ -1011,9 +1047,9 @@ export const SidebarCore = ({
|
|
|
1011
1047
|
>
|
|
1012
1048
|
<button
|
|
1013
1049
|
onClick={() => onItemClick && onItemClick(item.id)}
|
|
1014
|
-
className={`
|
|
1015
|
-
isCollapsed ? 'justify-center' : '
|
|
1016
|
-
} ${isCollapsed ? '' : '
|
|
1050
|
+
className={`flex items-center ${
|
|
1051
|
+
isCollapsed ? 'justify-center' : 'justify-between'
|
|
1052
|
+
} ${isCollapsed ? '' : 'rounded-lg'} transition-all duration-200 ${
|
|
1017
1053
|
activeItem === item.id
|
|
1018
1054
|
? ''
|
|
1019
1055
|
: 'color-gray-700 hover:bg-gray-100'
|
|
@@ -1026,14 +1062,23 @@ export const SidebarCore = ({
|
|
|
1026
1062
|
backgroundColor: 'transparent',
|
|
1027
1063
|
borderRadius: '8px',
|
|
1028
1064
|
}
|
|
1029
|
-
:
|
|
1030
|
-
|
|
1031
|
-
|
|
1065
|
+
: {
|
|
1066
|
+
width: '228px',
|
|
1067
|
+
height: '40px',
|
|
1068
|
+
paddingTop: '8px',
|
|
1069
|
+
paddingRight: '12px',
|
|
1070
|
+
paddingBottom: '8px',
|
|
1071
|
+
paddingLeft: '12px',
|
|
1072
|
+
gap: '12px',
|
|
1073
|
+
borderRadius: '8px',
|
|
1074
|
+
opacity: 1,
|
|
1075
|
+
backgroundColor: activeItem === item.id ? '#2D5C63' : 'transparent',
|
|
1076
|
+
}
|
|
1032
1077
|
}
|
|
1033
1078
|
title={isCollapsed ? item.label : ''}
|
|
1034
1079
|
>
|
|
1035
1080
|
<div
|
|
1036
|
-
className=
|
|
1081
|
+
className={`flex items-center ${isCollapsed ? '' : 'min-w-0 flex-1'}`}
|
|
1037
1082
|
style={
|
|
1038
1083
|
isCollapsed
|
|
1039
1084
|
? {
|
|
@@ -1041,7 +1086,9 @@ export const SidebarCore = ({
|
|
|
1041
1086
|
columnGap: `${desktopCollapsedOverlapColumnGapPx}px`,
|
|
1042
1087
|
width: '100%',
|
|
1043
1088
|
}
|
|
1044
|
-
: {
|
|
1089
|
+
: {
|
|
1090
|
+
minWidth: 0,
|
|
1091
|
+
}
|
|
1045
1092
|
}
|
|
1046
1093
|
>
|
|
1047
1094
|
{isCollapsed ? (
|
|
@@ -1111,8 +1158,13 @@ export const SidebarCore = ({
|
|
|
1111
1158
|
<Typography
|
|
1112
1159
|
variant="body-lg"
|
|
1113
1160
|
weight={activeItem === item.id ? desktopItemLabelWeightActive : desktopItemLabelWeightInactive}
|
|
1114
|
-
className={activeItem === item.id ? 'color-white' : 'color-gray-700'}
|
|
1115
|
-
style={{
|
|
1161
|
+
className={`${activeItem === item.id ? 'color-white' : 'color-gray-700'} min-w-0`}
|
|
1162
|
+
style={{
|
|
1163
|
+
fontSize: desktopItemLabelFontSize,
|
|
1164
|
+
overflow: 'hidden',
|
|
1165
|
+
textOverflow: 'ellipsis',
|
|
1166
|
+
whiteSpace: 'nowrap',
|
|
1167
|
+
}}
|
|
1116
1168
|
>
|
|
1117
1169
|
{item.label}
|
|
1118
1170
|
</Typography>
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button } from '../../Atoms/Button/Button';
|
|
4
|
+
import { Typography } from '../../Atoms/Typography/Typography';
|
|
5
|
+
import { Icon } from '../../Atoms/Icon/Icon';
|
|
6
|
+
|
|
7
|
+
export const ForgotPassword = ({
|
|
8
|
+
onBackToLogin,
|
|
9
|
+
onSubmit,
|
|
10
|
+
loading: externalLoading,
|
|
11
|
+
error: externalError,
|
|
12
|
+
className = '',
|
|
13
|
+
variant = 'default',
|
|
14
|
+
...props
|
|
15
|
+
}) => {
|
|
16
|
+
const { t } = useTranslation();
|
|
17
|
+
const isHexaLogin = variant === 'hexa-login';
|
|
18
|
+
|
|
19
|
+
// Estados internos (si no se pasan como props)
|
|
20
|
+
const [email, setEmail] = useState('');
|
|
21
|
+
const [errors, setErrors] = useState({});
|
|
22
|
+
const [internalLoading, setInternalLoading] = useState(false);
|
|
23
|
+
const [resetLink, setResetLink] = useState(null);
|
|
24
|
+
const [internalError, setInternalError] = useState(null);
|
|
25
|
+
|
|
26
|
+
// Usar loading externo o interno
|
|
27
|
+
const loading = externalLoading !== undefined ? externalLoading : internalLoading;
|
|
28
|
+
// Usar error externo o interno
|
|
29
|
+
const error = externalError !== undefined ? externalError : internalError;
|
|
30
|
+
|
|
31
|
+
// Funciones de validación
|
|
32
|
+
const hasAtSymbol = (email) => {
|
|
33
|
+
return email && email.includes('@');
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const isValidEmail = (email) => {
|
|
37
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
38
|
+
return emailRegex.test(email);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const validateForm = () => {
|
|
42
|
+
const newErrors = {};
|
|
43
|
+
|
|
44
|
+
if (!email) {
|
|
45
|
+
newErrors.email = t('errors.required') ?? 'Este campo es obligatorio';
|
|
46
|
+
} else if (!isValidEmail(email)) {
|
|
47
|
+
newErrors.email = t('errors.invalidEmail') ?? 'El mail ingresado no es válido';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setErrors(newErrors);
|
|
51
|
+
return Object.keys(newErrors).length === 0;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleEmailChange = (e) => {
|
|
55
|
+
const value = e.target.value;
|
|
56
|
+
setEmail(value);
|
|
57
|
+
|
|
58
|
+
// Limpiar errores al cambiar
|
|
59
|
+
if (errors.email) {
|
|
60
|
+
setErrors((prev) => ({ ...prev, email: null }));
|
|
61
|
+
}
|
|
62
|
+
// Si hay error interno, limpiarlo
|
|
63
|
+
if (internalError) {
|
|
64
|
+
setInternalError(null);
|
|
65
|
+
}
|
|
66
|
+
// Si hay error externo, no lo manejamos aquí (el componente padre debe manejarlo)
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleSubmit = async (e) => {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
|
|
72
|
+
if (!validateForm()) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!onSubmit) {
|
|
77
|
+
console.warn('ForgotPassword: onSubmit prop is required');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Usar loading interno si no se pasa como prop
|
|
82
|
+
if (externalLoading === undefined) {
|
|
83
|
+
setInternalLoading(true);
|
|
84
|
+
}
|
|
85
|
+
setErrors({});
|
|
86
|
+
setInternalError(null);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const result = await onSubmit({ email, countryCode: null });
|
|
90
|
+
|
|
91
|
+
if (result?.resetLink) {
|
|
92
|
+
setResetLink(result.resetLink);
|
|
93
|
+
} else {
|
|
94
|
+
// Si no hay resetLink pero no hay error, asumir éxito
|
|
95
|
+
setResetLink('success');
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
let errorMessage = t('forgotPassword.errors.sendFailed') ?? 'Error al enviar el link. Intenta nuevamente.';
|
|
99
|
+
|
|
100
|
+
if (err?.response?.status === 404) {
|
|
101
|
+
errorMessage = err?.response?.data?.message ||
|
|
102
|
+
err?.message ||
|
|
103
|
+
'No se encontró una cuenta asociada a este correo electrónico. Por favor, verifica que el correo esté correcto.';
|
|
104
|
+
} else if (err?.response?.status === 400) {
|
|
105
|
+
errorMessage = err?.response?.data?.message ||
|
|
106
|
+
err?.message ||
|
|
107
|
+
'El correo electrónico ingresado no es válido.';
|
|
108
|
+
} else if (err?.response?.status >= 500) {
|
|
109
|
+
errorMessage = 'Error del servidor. Por favor, intenta más tarde.';
|
|
110
|
+
} else if (err?.message) {
|
|
111
|
+
errorMessage = err.message;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (externalError === undefined) {
|
|
115
|
+
setInternalError(errorMessage);
|
|
116
|
+
}
|
|
117
|
+
// Si el error viene como prop, el componente padre lo maneja
|
|
118
|
+
} finally {
|
|
119
|
+
if (externalLoading === undefined) {
|
|
120
|
+
setInternalLoading(false);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div
|
|
127
|
+
className={`min-h-screen flex flex-col ${className}`}
|
|
128
|
+
style={{ backgroundColor: '#f5f5f5' }}
|
|
129
|
+
{...props}
|
|
130
|
+
>
|
|
131
|
+
<div className="flex-1 flex items-center justify-center p-4">
|
|
132
|
+
<div className="w-full max-w-md">
|
|
133
|
+
{/* Título y subtítulo */}
|
|
134
|
+
<div className="mb-8 text-center">
|
|
135
|
+
<Typography
|
|
136
|
+
variant="h3"
|
|
137
|
+
className="mb-2 font-semibold"
|
|
138
|
+
style={{
|
|
139
|
+
color: '#2D5C63',
|
|
140
|
+
fontFamily: 'inherit',
|
|
141
|
+
fontWeight: 400,
|
|
142
|
+
fontStyle: 'normal',
|
|
143
|
+
fontSize: '32px',
|
|
144
|
+
lineHeight: '48px',
|
|
145
|
+
letterSpacing: '0px',
|
|
146
|
+
textAlign: 'center',
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{t('forgotPassword.title') ?? 'Recuperar contraseña'}
|
|
150
|
+
</Typography>
|
|
151
|
+
<Typography
|
|
152
|
+
variant="body2"
|
|
153
|
+
className="text-gray-600"
|
|
154
|
+
style={{
|
|
155
|
+
fontFamily: 'inherit',
|
|
156
|
+
fontWeight: 400,
|
|
157
|
+
fontStyle: 'normal',
|
|
158
|
+
fontSize: '16px',
|
|
159
|
+
lineHeight: '24px',
|
|
160
|
+
letterSpacing: '0px',
|
|
161
|
+
textAlign: 'center',
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
{t('forgotPassword.subtitle') ?? 'Ingresa tu correo electrónico y te enviaremos un link para restablecer tu contraseña.'}
|
|
165
|
+
</Typography>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{/* Card blanca */}
|
|
169
|
+
<div className="bg-white border border-gray-200 rounded-lg p-6 shadow-md">
|
|
170
|
+
{/* Estado de éxito */}
|
|
171
|
+
{resetLink && !loading && (
|
|
172
|
+
<div className="text-center space-y-4">
|
|
173
|
+
<div
|
|
174
|
+
className="rounded-lg p-4 border"
|
|
175
|
+
style={{
|
|
176
|
+
backgroundColor: '#d1fae5',
|
|
177
|
+
borderColor: '#10b981',
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
<Typography
|
|
181
|
+
variant="body2"
|
|
182
|
+
className="font-medium"
|
|
183
|
+
style={{ color: '#065f46' }}
|
|
184
|
+
>
|
|
185
|
+
{t('forgotPassword.success.title') ?? 'Link generado'}
|
|
186
|
+
</Typography>
|
|
187
|
+
<Typography
|
|
188
|
+
variant="body2"
|
|
189
|
+
className="mt-2"
|
|
190
|
+
style={{ color: '#047857', fontSize: '14px' }}
|
|
191
|
+
>
|
|
192
|
+
{t('forgotPassword.success.message') ?? 'Se ha generado un link de recuperación. Haz click en el enlace a continuación para restablecer tu contraseña.'}
|
|
193
|
+
</Typography>
|
|
194
|
+
</div>
|
|
195
|
+
{resetLink !== 'success' && (
|
|
196
|
+
<a
|
|
197
|
+
href={resetLink}
|
|
198
|
+
className="inline-block px-4 py-2 rounded-lg font-medium text-white hover:opacity-90 transition-opacity"
|
|
199
|
+
style={{ backgroundColor: '#2D5C63' }}
|
|
200
|
+
>
|
|
201
|
+
{t('forgotPassword.success.linkLabel') ?? 'Ir a restablecer contraseña'}
|
|
202
|
+
</a>
|
|
203
|
+
)}
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
|
|
207
|
+
{/* Estado de carga */}
|
|
208
|
+
{loading && (
|
|
209
|
+
<div className="text-center space-y-4">
|
|
210
|
+
<div
|
|
211
|
+
className="rounded-lg p-4 border"
|
|
212
|
+
style={{
|
|
213
|
+
backgroundColor: '#dbeafe',
|
|
214
|
+
borderColor: '#3b82f6',
|
|
215
|
+
}}
|
|
216
|
+
>
|
|
217
|
+
<Typography
|
|
218
|
+
variant="body2"
|
|
219
|
+
className="font-medium"
|
|
220
|
+
style={{ color: '#1e40af' }}
|
|
221
|
+
>
|
|
222
|
+
{t('forgotPassword.sending') ?? 'Enviando...'}
|
|
223
|
+
</Typography>
|
|
224
|
+
<Typography
|
|
225
|
+
variant="body2"
|
|
226
|
+
className="mt-2"
|
|
227
|
+
style={{ color: '#1e3a8a', fontSize: '14px' }}
|
|
228
|
+
>
|
|
229
|
+
{t('forgotPassword.waitingMessage') ?? 'Espera, estamos procesando tu solicitud...'}
|
|
230
|
+
</Typography>
|
|
231
|
+
<Typography
|
|
232
|
+
variant="body2"
|
|
233
|
+
className="mt-2"
|
|
234
|
+
style={{ color: '#1e3a8a', fontSize: '13px' }}
|
|
235
|
+
>
|
|
236
|
+
{t('forgotPassword.waitingSubmessage') ?? 'Hemos enviado un link de recuperación a tu correo. Esto puede tardar unos momentos.'}
|
|
237
|
+
</Typography>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
|
|
242
|
+
{/* Estado de formulario */}
|
|
243
|
+
{!resetLink && !loading && (
|
|
244
|
+
<>
|
|
245
|
+
{/* Botón volver */}
|
|
246
|
+
{onBackToLogin && (
|
|
247
|
+
<button
|
|
248
|
+
type="button"
|
|
249
|
+
onClick={onBackToLogin}
|
|
250
|
+
className="flex items-center gap-2 mb-6 text-body-md hover:underline font-medium cursor-pointer"
|
|
251
|
+
style={{ color: '#2D5C63' }}
|
|
252
|
+
>
|
|
253
|
+
<Icon name="ArrowLeftIcon" variant="24-outline" size={18} />
|
|
254
|
+
<span>{t('forgotPassword.backToLogin') ?? 'Volver al inicio de sesión'}</span>
|
|
255
|
+
</button>
|
|
256
|
+
)}
|
|
257
|
+
|
|
258
|
+
{/* Formulario */}
|
|
259
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
260
|
+
{/* Input de email */}
|
|
261
|
+
<div>
|
|
262
|
+
<label
|
|
263
|
+
className="block mb-2 font-medium text-sm"
|
|
264
|
+
style={{ color: '#2D5C63' }}
|
|
265
|
+
>
|
|
266
|
+
{t('login.email') ?? t('form.email') ?? 'Correo electrónico'}
|
|
267
|
+
</label>
|
|
268
|
+
<input
|
|
269
|
+
type="email"
|
|
270
|
+
placeholder={t('login.emailPlaceholder') ?? t('placeholder.email') ?? 'Ingresa tu correo electrónico'}
|
|
271
|
+
value={email}
|
|
272
|
+
onChange={handleEmailChange}
|
|
273
|
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm text-gray-900"
|
|
274
|
+
style={{
|
|
275
|
+
borderColor: errors.email ? '#ef4444' : '#2D5C63',
|
|
276
|
+
borderWidth: '1px',
|
|
277
|
+
borderStyle: 'solid',
|
|
278
|
+
}}
|
|
279
|
+
/>
|
|
280
|
+
{errors.email && (
|
|
281
|
+
<p className="mt-1 text-sm text-red-600">{errors.email}</p>
|
|
282
|
+
)}
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
{/* Error general */}
|
|
286
|
+
{error && (
|
|
287
|
+
<div
|
|
288
|
+
className="rounded-lg p-4 border"
|
|
289
|
+
style={{
|
|
290
|
+
backgroundColor: '#fecaca',
|
|
291
|
+
borderColor: '#ef4444',
|
|
292
|
+
}}
|
|
293
|
+
>
|
|
294
|
+
<Typography
|
|
295
|
+
variant="body2"
|
|
296
|
+
className="font-bold"
|
|
297
|
+
style={{ color: '#b91c1c' }}
|
|
298
|
+
>
|
|
299
|
+
{error}
|
|
300
|
+
</Typography>
|
|
301
|
+
</div>
|
|
302
|
+
)}
|
|
303
|
+
|
|
304
|
+
{/* Botón de envío */}
|
|
305
|
+
<div className="pt-4">
|
|
306
|
+
<Button
|
|
307
|
+
tipo="Primary"
|
|
308
|
+
color="Teal"
|
|
309
|
+
tamaño="Default"
|
|
310
|
+
type="submit"
|
|
311
|
+
className="w-full"
|
|
312
|
+
disabled={loading || !hasAtSymbol(email)}
|
|
313
|
+
>
|
|
314
|
+
<span
|
|
315
|
+
style={{
|
|
316
|
+
fontFamily: "'IBM Plex Sans', sans-serif",
|
|
317
|
+
fontWeight: 400,
|
|
318
|
+
fontStyle: 'normal',
|
|
319
|
+
fontSize: '16px',
|
|
320
|
+
lineHeight: '24px',
|
|
321
|
+
letterSpacing: '0%',
|
|
322
|
+
}}
|
|
323
|
+
>
|
|
324
|
+
{loading
|
|
325
|
+
? t('forgotPassword.sending') ?? 'Enviando...'
|
|
326
|
+
: t('forgotPassword.button') ?? 'Enviar link'}
|
|
327
|
+
</span>
|
|
328
|
+
</Button>
|
|
329
|
+
</div>
|
|
330
|
+
</form>
|
|
331
|
+
</>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
export default ForgotPassword;
|
|
341
|
+
|