anima-ds-nucleus 1.0.16 → 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.
Files changed (25) hide show
  1. package/dist/anima-ds-nucleus.css +1 -1
  2. package/dist/anima-ds.cjs.js +150 -124
  3. package/dist/anima-ds.esm.js +6799 -5651
  4. package/package.json +1 -1
  5. package/src/components/DataDisplay/Card/CardTituloCorto.jsx +23 -2
  6. package/src/components/DataDisplay/Card/CardTituloCortoMasEstado.jsx +23 -2
  7. package/src/components/DataDisplay/Card/CardTituloLargo.jsx +23 -2
  8. package/src/components/DataDisplay/Card/CardTituloLargoMasEstado.jsx +23 -2
  9. package/src/components/Inputs/PasswordInput/PasswordInput.jsx +85 -0
  10. package/src/components/Layout/Header/HeaderCompartido.jsx +192 -0
  11. package/src/components/Layout/Header/HeaderCompartido.stories.jsx +66 -0
  12. package/src/components/Layout/Header/HeaderConBuscador.stories.jsx +1 -0
  13. package/src/components/Layout/Header/HeaderCore.jsx +12 -12
  14. package/src/components/Layout/Header/HeaderGeneral.jsx +248 -53
  15. package/src/components/Layout/Header/HeaderGeneral.stories.jsx +34 -0
  16. package/src/components/Layout/Header/HeaderPoint.jsx +3 -3
  17. package/src/components/Layout/NavPoint/NavPoint.jsx +76 -55
  18. package/src/components/Layout/SaludoConFechaDashboard/SaludoConFechaDashboard.jsx +0 -1
  19. package/src/components/Layout/Sidebar/SidebarCore.jsx +82 -30
  20. package/src/components/Views/ForgotPassword/ForgotPassword.jsx +341 -0
  21. package/src/components/Views/ForgotPassword/ForgotPassword.stories.jsx +186 -0
  22. package/src/components/Views/LoginForm/LoginForm.jsx +332 -43
  23. package/src/components/Views/LoginForm/LoginForm.stories.jsx +182 -4
  24. package/src/i18n/config.js +12 -0
  25. package/src/index.js +2 -0
@@ -0,0 +1,186 @@
1
+ import { ForgotPassword } from './ForgotPassword';
2
+ import { I18nProvider } from '../../../providers/I18nProvider';
3
+
4
+ export default {
5
+ title: 'Views/ForgotPassword',
6
+ component: ForgotPassword,
7
+ tags: ['autodocs'],
8
+ decorators: [
9
+ (Story) => (
10
+ <I18nProvider language="es-AR">
11
+ <Story />
12
+ </I18nProvider>
13
+ ),
14
+ ],
15
+ argTypes: {
16
+ variant: {
17
+ control: 'select',
18
+ options: ['default', 'hexa-login'],
19
+ description: 'Variante del componente',
20
+ },
21
+ loading: {
22
+ control: 'boolean',
23
+ description: 'Estado de carga',
24
+ },
25
+ error: {
26
+ control: 'text',
27
+ description: 'Error general del servidor',
28
+ },
29
+ },
30
+ };
31
+
32
+ // Estado por defecto
33
+ export const Default = {
34
+ args: {
35
+ onBackToLogin: () => {
36
+ console.log('Back to login clicked');
37
+ alert('Volver al login');
38
+ },
39
+ onSubmit: async ({ email, countryCode }) => {
40
+ console.log('Forgot password data:', { email, countryCode });
41
+ await new Promise((resolve) => setTimeout(resolve, 2000));
42
+ return { resetLink: 'https://example.com/reset-password' };
43
+ },
44
+ },
45
+ };
46
+
47
+ // Estado de carga
48
+ export const Loading = {
49
+ args: {
50
+ loading: true,
51
+ onBackToLogin: () => {
52
+ alert('Volver al login');
53
+ },
54
+ onSubmit: async () => {
55
+ await new Promise((resolve) => setTimeout(resolve, 5000));
56
+ },
57
+ },
58
+ };
59
+
60
+ // Estado de éxito
61
+ export const Success = {
62
+ args: {
63
+ onBackToLogin: () => {
64
+ alert('Volver al login');
65
+ },
66
+ onSubmit: async ({ email }) => {
67
+ console.log('Email:', email);
68
+ await new Promise((resolve) => setTimeout(resolve, 1000));
69
+ return { resetLink: 'https://example.com/reset-password?token=abc123' };
70
+ },
71
+ },
72
+ };
73
+
74
+ // Con error del servidor
75
+ export const WithError = {
76
+ args: {
77
+ error: 'No se encontró una cuenta asociada a este correo electrónico.',
78
+ onBackToLogin: () => {
79
+ alert('Volver al login');
80
+ },
81
+ onSubmit: async () => {
82
+ throw new Error('Usuario no encontrado');
83
+ },
84
+ },
85
+ };
86
+
87
+ // Error 404
88
+ export const Error404 = {
89
+ args: {
90
+ onBackToLogin: () => {
91
+ alert('Volver al login');
92
+ },
93
+ onSubmit: async () => {
94
+ const error = new Error('Not found');
95
+ error.response = { status: 404, data: { message: 'Usuario no encontrado' } };
96
+ throw error;
97
+ },
98
+ },
99
+ };
100
+
101
+ // Error 400
102
+ export const Error400 = {
103
+ args: {
104
+ onBackToLogin: () => {
105
+ alert('Volver al login');
106
+ },
107
+ onSubmit: async () => {
108
+ const error = new Error('Bad request');
109
+ error.response = { status: 400, data: { message: 'Email inválido' } };
110
+ throw error;
111
+ },
112
+ },
113
+ };
114
+
115
+ // Error 500
116
+ export const Error500 = {
117
+ args: {
118
+ onBackToLogin: () => {
119
+ alert('Volver al login');
120
+ },
121
+ onSubmit: async () => {
122
+ const error = new Error('Server error');
123
+ error.response = { status: 500 };
124
+ throw error;
125
+ },
126
+ },
127
+ };
128
+
129
+ // Variante hexa-login
130
+ export const HexaLogin = {
131
+ args: {
132
+ variant: 'hexa-login',
133
+ onBackToLogin: () => {
134
+ alert('Volver al login');
135
+ },
136
+ onSubmit: async ({ email }) => {
137
+ console.log('Email:', email);
138
+ await new Promise((resolve) => setTimeout(resolve, 2000));
139
+ return { resetLink: 'https://example.com/reset-password' };
140
+ },
141
+ },
142
+ };
143
+
144
+ // Versión en español
145
+ export const Spanish = {
146
+ decorators: [
147
+ (Story) => (
148
+ <I18nProvider language="es-AR">
149
+ <Story />
150
+ </I18nProvider>
151
+ ),
152
+ ],
153
+ args: {
154
+ onBackToLogin: () => {
155
+ alert('Volver al login');
156
+ },
157
+ onSubmit: async ({ email }) => {
158
+ console.log('Email:', email);
159
+ await new Promise((resolve) => setTimeout(resolve, 2000));
160
+ return { resetLink: 'https://example.com/reset-password' };
161
+ },
162
+ },
163
+ };
164
+
165
+ // Versión en portugués
166
+ export const Portuguese = {
167
+ decorators: [
168
+ (Story) => (
169
+ <I18nProvider language="pt-BR">
170
+ <Story />
171
+ </I18nProvider>
172
+ ),
173
+ ],
174
+ args: {
175
+ onBackToLogin: () => {
176
+ alert('Volver ao login');
177
+ },
178
+ onSubmit: async ({ email }) => {
179
+ console.log('Email:', email);
180
+ await new Promise((resolve) => setTimeout(resolve, 2000));
181
+ return { resetLink: 'https://example.com/reset-password' };
182
+ },
183
+ },
184
+ };
185
+
186
+
@@ -1,15 +1,30 @@
1
1
  import { useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
+ import { Button } from '../../Atoms/Button/Button';
4
+ import { Typography } from '../../Atoms/Typography/Typography';
3
5
  import { Input } from '../../Inputs/Input/Input';
4
6
  import { Checkbox } from '../../Inputs/Checkbox/Checkbox';
5
- import { Button } from '../../Atoms/Button/Button';
7
+ import { PasswordInput } from '../../Inputs/PasswordInput/PasswordInput';
6
8
 
7
9
  export const LoginForm = ({
10
+ variant,
8
11
  onSubmit,
12
+ loading = false,
13
+ onForgotPassword,
14
+ onGoogleLogin,
15
+ onMicrosoftLogin,
16
+ showSocialLogin = true,
17
+ showDivider = true,
18
+ showPasswordRequirementsOnError = true,
19
+ labels,
20
+ generalError,
21
+ generalErrorTitle,
22
+ generalErrorSubtitle,
9
23
  className = '',
10
24
  ...props
11
25
  }) => {
12
26
  const { t } = useTranslation();
27
+ const isHexaLogin = variant === 'hexa-login';
13
28
  const [formData, setFormData] = useState({
14
29
  email: '',
15
30
  password: '',
@@ -45,52 +60,326 @@ export const LoginForm = ({
45
60
  }
46
61
  };
47
62
 
63
+ const resolvedLabels = {
64
+ emailLabel: labels?.emailLabel ?? t('form.email') ?? 'Email',
65
+ passwordLabel: labels?.passwordLabel ?? t('form.password') ?? 'Contraseña',
66
+ rememberMeLabel: labels?.rememberMeLabel ?? t('form.rememberMe') ?? 'Recordarme',
67
+ forgotPasswordLabel: labels?.forgotPasswordLabel ?? t('form.forgotPassword') ?? 'Olvidé mi contraseña',
68
+ continueWithLabel: labels?.continueWithLabel ?? 'Continuar con',
69
+ googleLabel: labels?.googleLabel ?? 'Google',
70
+ microsoftLabel: labels?.microsoftLabel ?? 'Microsoft',
71
+ };
72
+
73
+ const loginButtonText = loading ? (t('form.loggingIn') ?? 'Ingresando...') : (t('form.login') ?? 'Ingresar');
74
+
48
75
  return (
49
- <form
50
- onSubmit={handleSubmit}
51
- className={`max-w-md mx-auto bg-white p-6 rounded-lg shadow-md ${className}`}
52
- {...props}
53
- >
54
- <h2 className="text-2xl font-bold mb-6 text-center">{t('form.login')}</h2>
55
-
56
- <div className="space-y-4">
57
- <Input
58
- label={t('form.email')}
59
- type="email"
60
- placeholder={t('placeholder.email')}
61
- value={formData.email}
62
- onChange={(e) => handleChange('email', e.target.value)}
63
- error={errors.email}
64
- required
65
- />
66
-
67
- <Input
68
- label={t('form.password')}
69
- type="password"
70
- placeholder={t('placeholder.password')}
71
- value={formData.password}
72
- onChange={(e) => handleChange('password', e.target.value)}
73
- error={errors.password}
74
- required
75
- />
76
-
77
- <Checkbox
78
- label={t('form.rememberMe')}
79
- checked={formData.rememberMe}
80
- onChange={(e) => handleChange('rememberMe', e.target.checked)}
81
- />
82
-
83
- <Button type="submit" variant="primary" className="w-full">
84
- {t('form.login')}
85
- </Button>
86
-
87
- <div className="text-center">
88
- <a href="#" className="text-sm text-blue-600 hover:underline">
89
- {t('form.forgotPassword')}
76
+ isHexaLogin ? (
77
+ <div
78
+ style={{
79
+ width: '427px',
80
+ minHeight: '409px',
81
+ borderRadius: '14px',
82
+ border: '1px solid #0000001A',
83
+ padding: '24px',
84
+ background: '#FFFFFF',
85
+ boxShadow: '0px 4px 6px -4px #0000001A, 0px 10px 15px -3px #0000001A',
86
+ }}
87
+ >
88
+ <form onSubmit={handleSubmit} className={className} {...props}>
89
+ {/* 1) Email */}
90
+ <div className="w-full">
91
+ <label
92
+ className="block mb-2 font-medium text-sm"
93
+ style={{ color: '#2D5C63' }}
94
+ >
95
+ {resolvedLabels.emailLabel}
96
+ </label>
97
+ <input
98
+ type="email"
99
+ value={formData.email}
100
+ onChange={(e) => handleChange('email', e.target.value)}
101
+ placeholder="Ingresa tu correo electronico"
102
+ 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"
103
+ style={{ borderColor: errors.email ? '#ef4444' : '#2D5C63' }}
104
+ />
105
+ </div>
106
+
107
+ {/* 2) Password */}
108
+ <div className="w-full mt-4">
109
+ <PasswordInput
110
+ variant="hexa-login"
111
+ label={resolvedLabels.passwordLabel}
112
+ labelClassName="block mb-2 font-medium text-sm"
113
+ labelStyle={{ color: '#2D5C63' }}
114
+ placeholder={t('placeholder.password')}
115
+ value={formData.password}
116
+ onChange={(e) => handleChange('password', e.target.value)}
117
+ inputClassName="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"
118
+ style={{
119
+ paddingRight: '2.75rem',
120
+ borderColor: errors.password ? '#ef4444' : '#2D5C63',
121
+ }}
122
+ />
123
+ </div>
124
+
125
+ {/* 3) Password requirements box (solo si hay error de password) */}
126
+ {showPasswordRequirementsOnError && !!errors.password && (
127
+ <div
128
+ className="rounded-lg p-4 border mt-4"
129
+ style={{
130
+ background: '#f5f3ff',
131
+ borderColor: '#a78bfa',
132
+ color: '#0c4a6e',
133
+ fontFamily: 'IBM Plex Mono',
134
+ }}
135
+ >
136
+ <ul className="list-disc list-inside text-sm space-y-1" style={{ color: '#0c4a6e' }}>
137
+ <li>Al menos 8 caracteres</li>
138
+ <li>Al menos una letra mayúscula</li>
139
+ <li>Al menos un número</li>
140
+ <li>Al menos un caracter especial</li>
141
+ </ul>
142
+ </div>
143
+ )}
144
+
145
+ {/* 4) Row remember/forgot */}
146
+ <div className="flex items-center justify-between mt-4">
147
+ <label className="flex items-center">
148
+ <input
149
+ type="checkbox"
150
+ checked={formData.rememberMe}
151
+ onChange={(e) => handleChange('rememberMe', e.target.checked)}
152
+ className="w-4 h-4 border-gray-300 rounded focus:outline-none focus:ring-0"
153
+ style={{
154
+ accentColor: '#2D5C63',
155
+ borderColor: formData.rememberMe ? '#2D5C63' : '#d1d5db',
156
+ }}
157
+ />
158
+ <span className="ml-2 text-sm font-medium" style={{ color: '#2D5C63' }}>
159
+ {resolvedLabels.rememberMeLabel}
160
+ </span>
161
+ </label>
162
+
163
+ <a
164
+ href="#"
165
+ className="text-body-md hover:underline font-medium"
166
+ style={{ color: '#2D5C63' }}
167
+ onClick={(e) => {
168
+ e.preventDefault();
169
+ if (onForgotPassword) onForgotPassword();
170
+ }}
171
+ >
172
+ {resolvedLabels.forgotPasswordLabel}
90
173
  </a>
91
174
  </div>
175
+
176
+ {/* 5) Error general box rojo (si existe) */}
177
+ {(generalError || errors.general) && (
178
+ <div
179
+ className="mt-4"
180
+ style={{
181
+ width: '377px',
182
+ height: '68px',
183
+ paddingTop: '12px',
184
+ paddingRight: '16px',
185
+ paddingBottom: '12px',
186
+ paddingLeft: '16px',
187
+ gap: '4px',
188
+ borderRadius: '8px',
189
+ borderWidth: '1px',
190
+ border: '1px solid #B30808',
191
+ backgroundColor: '#F8DBDB',
192
+ display: 'flex',
193
+ flexDirection: 'column',
194
+ }}
195
+ >
196
+ <Typography
197
+ variant="body2"
198
+ style={{
199
+ fontFamily: "'IBM Plex Mono', monospace",
200
+ fontWeight: 400,
201
+ fontStyle: 'normal',
202
+ fontSize: '14px',
203
+ lineHeight: '21px',
204
+ letterSpacing: '0.02em',
205
+ color: '#B30808',
206
+ margin: 0,
207
+ }}
208
+ >
209
+ {generalErrorTitle ??
210
+ (typeof generalError === 'string' ? generalError : null) ??
211
+ (errors.general && typeof errors.general === 'string' ? errors.general : null) ??
212
+ (t('errors.invalidCredentials') ?? 'Correo o contraseña incorrectos.')}
213
+ </Typography>
214
+ <Typography
215
+ variant="body2"
216
+ style={{
217
+ fontFamily: "'IBM Plex Mono', monospace",
218
+ fontWeight: 400,
219
+ fontStyle: 'normal',
220
+ fontSize: '14px',
221
+ lineHeight: '21px',
222
+ letterSpacing: '0.02em',
223
+ color: '#B30808',
224
+ margin: 0,
225
+ }}
226
+ >
227
+ {generalErrorSubtitle ??
228
+ (t('errors.tryAgain') ?? 'Intentá nuevamente.')}
229
+ </Typography>
230
+ </div>
231
+ )}
232
+
233
+ {/* 6) Botón login */}
234
+ <div className="mt-4">
235
+ <Button
236
+ type="submit"
237
+ tipo="Primary"
238
+ color="Teal"
239
+ tamaño="Default"
240
+ className="w-full"
241
+ disabled={loading}
242
+ >
243
+ <span
244
+ style={{
245
+ fontFamily: "'IBM Plex Sans', sans-serif",
246
+ fontWeight: 400,
247
+ fontStyle: 'normal',
248
+ fontSize: '16px',
249
+ lineHeight: '24px',
250
+ letterSpacing: '0%',
251
+ }}
252
+ >
253
+ {loginButtonText}
254
+ </span>
255
+ </Button>
256
+ </div>
257
+
258
+ {/* 7) Divider */}
259
+ {showDivider && (
260
+ <div className="flex items-center gap-3 my-6">
261
+ <div className="flex-1" style={{ borderTop: '1px solid #2D5C63' }} />
262
+ <Typography
263
+ variant="body2"
264
+ className="text-body-md font-medium"
265
+ style={{ color: '#2D5C63', whiteSpace: 'nowrap' }}
266
+ >
267
+ {resolvedLabels.continueWithLabel}
268
+ </Typography>
269
+ <div className="flex-1" style={{ borderTop: '1px solid #2D5C63' }} />
270
+ </div>
271
+ )}
272
+
273
+ {/* 8) Social buttons */}
274
+ {showSocialLogin && (
275
+ <div
276
+ style={{
277
+ width: '377px',
278
+ height: '36px',
279
+ gap: '20px',
280
+ marginLeft: 'auto',
281
+ marginRight: 'auto',
282
+ display: 'flex',
283
+ }}
284
+ >
285
+ <button
286
+ type="button"
287
+ onClick={onGoogleLogin}
288
+ className="flex-1 flex items-center justify-center bg-white hover:bg-gray-50 transition-colors"
289
+ style={{
290
+ width: '178.5px',
291
+ height: '36px',
292
+ padding: '8px 43px',
293
+ gap: '16px',
294
+ borderRadius: '8px',
295
+ border: '1px solid #38656D',
296
+ }}
297
+ >
298
+ <svg width="18" height="18" viewBox="0 0 48 48" aria-hidden="true">
299
+ <path fill="#FFC107" d="M43.611 20.083H42V20H24v8h11.303C33.651 32.659 29.227 36 24 36c-6.627 0-12-5.373-12-12s5.373-12 12-12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.047 6.053 29.272 4 24 4 12.955 4 4 12.955 4 24s8.955 20 20 20 20-8.955 20-20c0-1.341-.138-2.65-.389-3.917z" />
300
+ <path fill="#FF3D00" d="M6.306 14.691l6.571 4.819C14.655 16.108 19.002 12 24 12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.047 6.053 29.272 4 24 4 16.318 4 9.656 8.337 6.306 14.691z" />
301
+ <path fill="#4CAF50" d="M24 44c5.166 0 9.86-1.977 13.409-5.197l-6.19-5.238C29.211 35.091 26.715 36 24 36c-5.205 0-9.616-3.318-11.281-7.946l-6.522 5.025C9.505 39.556 16.227 44 24 44z" />
302
+ <path fill="#1976D2" d="M43.611 20.083H42V20H24v8h11.303c-.793 2.116-2.318 3.91-4.284 5.238l.003-.002 6.19 5.238C36.971 39.205 44 34 44 24c0-1.341-.138-2.65-.389-3.917z" />
303
+ </svg>
304
+ <Typography variant="body2" className="text-gray-900 font-medium">
305
+ {resolvedLabels.googleLabel}
306
+ </Typography>
307
+ </button>
308
+
309
+ <button
310
+ type="button"
311
+ onClick={onMicrosoftLogin}
312
+ className="flex-1 flex items-center justify-center bg-white hover:bg-gray-50 transition-colors"
313
+ style={{
314
+ width: '178.5px',
315
+ height: '36px',
316
+ padding: '8px 43px',
317
+ gap: '16px',
318
+ borderRadius: '8px',
319
+ border: '1px solid #38656D',
320
+ }}
321
+ >
322
+ <svg width="18" height="18" viewBox="0 0 48 48" aria-hidden="true">
323
+ <path fill="#F25022" d="M6 6h17v17H6z" />
324
+ <path fill="#7FBA00" d="M25 6h17v17H25z" />
325
+ <path fill="#00A4EF" d="M6 25h17v17H6z" />
326
+ <path fill="#FFB900" d="M25 25h17v17H25z" />
327
+ </svg>
328
+ <Typography variant="body2" className="text-gray-900 font-medium">
329
+ {resolvedLabels.microsoftLabel}
330
+ </Typography>
331
+ </button>
332
+ </div>
333
+ )}
334
+ </form>
92
335
  </div>
93
- </form>
336
+ ) : (
337
+ <form
338
+ onSubmit={handleSubmit}
339
+ className={`max-w-md mx-auto bg-white p-6 rounded-lg shadow-md ${className}`}
340
+ {...props}
341
+ >
342
+ <h2 className="text-2xl font-bold mb-6 text-center">{t('form.login')}</h2>
343
+
344
+ <div className="space-y-4">
345
+ <Input
346
+ label={t('form.email')}
347
+ type="email"
348
+ placeholder={t('placeholder.email')}
349
+ value={formData.email}
350
+ onChange={(e) => handleChange('email', e.target.value)}
351
+ error={errors.email}
352
+ required
353
+ />
354
+
355
+ <Input
356
+ label={t('form.password')}
357
+ type="password"
358
+ placeholder={t('placeholder.password')}
359
+ value={formData.password}
360
+ onChange={(e) => handleChange('password', e.target.value)}
361
+ error={errors.password}
362
+ required
363
+ />
364
+
365
+ <Checkbox
366
+ label={t('form.rememberMe')}
367
+ checked={formData.rememberMe}
368
+ onChange={(e) => handleChange('rememberMe', e.target.checked)}
369
+ />
370
+
371
+ <Button type="submit" variant="primary" className="w-full">
372
+ {t('form.login')}
373
+ </Button>
374
+
375
+ <div className="text-center">
376
+ <a href="#" className="text-sm text-blue-600 hover:underline">
377
+ {t('form.forgotPassword')}
378
+ </a>
379
+ </div>
380
+ </div>
381
+ </form>
382
+ )
94
383
  );
95
384
  };
96
385