xertica-ui 2.6.0 → 2.7.0
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/CHANGELOG.md +18 -0
- package/README.md +1 -1
- package/components/assistant/xertica-assistant/use-assistant.ts +5 -6
- package/{templates/src/features/auth/ui → components/blocks/auth}/AuthPageShell.tsx +8 -3
- package/components/blocks/auth/PasswordStrengthMeter.tsx +41 -0
- package/components/blocks/auth/SocialLoginButtons.tsx +95 -0
- package/components/blocks/auth/index.ts +11 -0
- package/components/blocks/auth/usePasswordStrength.ts +13 -0
- package/components/blocks/index.ts +1 -0
- package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +45 -139
- package/components/pages/login-page/LoginPage.tsx +52 -145
- package/components/pages/reset-password-page/ResetPasswordPage.tsx +79 -169
- package/components/pages/verify-email-page/VerifyEmailPage.tsx +48 -150
- package/dist/AssistantChart-CImCNLfD.cjs +3551 -0
- package/dist/AssistantChart-DfdIfmQr.js +3383 -0
- package/dist/FeatureCard-BhpfmPPx.js +485 -0
- package/dist/FeatureCard-D-5yS_OD.cjs +497 -0
- package/dist/ImageWithFallback-CfkAkctC.cjs +122 -0
- package/dist/ImageWithFallback-bw-CxxDc.js +98 -0
- package/dist/LanguageContext-CX_zdxre.js +1912 -0
- package/dist/LanguageContext-CjhSSqJT.cjs +1917 -0
- package/dist/LanguageSelector-BR3Ss0k_.js +46 -0
- package/dist/LanguageSelector-CHOLPmHr.cjs +48 -0
- package/dist/PasswordStrengthMeter-C8irs0G1.js +173 -0
- package/dist/PasswordStrengthMeter-MLzhax-d.cjs +179 -0
- package/dist/ThemeContext-CEY9pCn-.cjs +86 -0
- package/dist/ThemeContext-Dk3tH7Ym.js +83 -0
- package/dist/ThemeToggle-BpM_iHh8.js +215 -0
- package/dist/ThemeToggle-CZRlv0d4.cjs +219 -0
- package/dist/VerifyEmailPage-BKiaw2YK.js +2954 -0
- package/dist/VerifyEmailPage-Beaicc6V.cjs +2963 -0
- package/dist/VerifyEmailPage-BehPBp0F.cjs +2963 -0
- package/dist/VerifyEmailPage-Djb6hthM.js +2954 -0
- package/dist/XerticaProvider-BijpC_py.cjs +46 -0
- package/dist/XerticaProvider-C2wDRQhT.js +44 -0
- package/dist/alert-dialog-BzIcTKn_.cjs +872 -0
- package/dist/alert-dialog-kty5pxQp.js +819 -0
- package/dist/assistant.cjs.js +1 -1
- package/dist/assistant.es.js +1 -1
- package/dist/blocks.cjs.js +7 -3
- package/dist/blocks.es.js +2 -2
- package/dist/brand.cjs.js +7 -6
- package/dist/brand.es.js +3 -2
- package/dist/components/blocks/auth/AuthPageShell.d.ts +12 -0
- package/dist/components/blocks/auth/PasswordStrengthMeter.d.ts +7 -0
- package/dist/components/blocks/auth/SocialLoginButtons.d.ts +8 -0
- package/dist/components/blocks/auth/index.d.ts +8 -0
- package/dist/components/blocks/auth/usePasswordStrength.d.ts +3 -0
- package/dist/components/blocks/index.d.ts +1 -0
- package/dist/components/pages/reset-password-page/ResetPasswordPage.d.ts +1 -1
- package/dist/hooks.cjs.js +5 -4
- package/dist/hooks.es.js +2 -1
- package/dist/index.cjs.js +29 -24
- package/dist/index.es.js +12 -11
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/sidebar-B7YzwAp3.js +1151 -0
- package/dist/sidebar-Cb0U7NsV.cjs +1154 -0
- package/dist/ui.cjs.js +7 -7
- package/dist/ui.es.js +3 -3
- package/dist/xertica-assistant-8fh8KA2K.js +2173 -0
- package/dist/xertica-assistant-BjfG3zyL.cjs +2180 -0
- package/docs/i18n.md +2 -0
- package/package.json +1 -1
- package/templates/package.json +2 -2
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -2
- package/templates/src/features/auth/ui/LoginContent.tsx +1 -2
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +3 -57
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +1 -2
- package/templates/src/features/home/index.ts +0 -10
- package/templates/src/features/home/ui/HomeContent.tsx +21 -75
- package/templates/src/features/template/index.ts +0 -4
- package/templates/src/features/template/ui/TemplateContent.tsx +26 -1279
- package/templates/src/locales/en/pages/templates.json +5 -313
- package/templates/src/locales/es/pages/templates.json +5 -313
- package/templates/src/locales/pt-BR/pages/templates.json +5 -313
- package/templates/src/features/auth/ui/SocialLoginButtons.tsx +0 -78
- package/templates/src/features/home/data/mock.ts +0 -41
- package/templates/src/features/home/hooks/useFeatureCards.ts +0 -20
- package/templates/src/features/home/store/dashboardStore.ts +0 -25
- package/templates/src/features/template/ui/CrudTemplate.tsx +0 -112
- package/templates/src/features/template/ui/DashboardTemplate.tsx +0 -110
- package/templates/src/features/template/ui/FormTemplate.tsx +0 -117
- package/templates/src/features/template/ui/LoginTemplate.tsx +0 -59
- package/templates/src/locales/en/pages/crudTemplate.json +0 -25
- package/templates/src/locales/en/pages/dashboardTemplate.json +0 -20
- package/templates/src/locales/en/pages/formTemplate.json +0 -16
- package/templates/src/locales/en/pages/loginTemplate.json +0 -9
- package/templates/src/locales/es/pages/crudTemplate.json +0 -25
- package/templates/src/locales/es/pages/dashboardTemplate.json +0 -20
- package/templates/src/locales/es/pages/formTemplate.json +0 -16
- package/templates/src/locales/es/pages/loginTemplate.json +0 -9
- package/templates/src/locales/pt-BR/pages/crudTemplate.json +0 -25
- package/templates/src/locales/pt-BR/pages/dashboardTemplate.json +0 -20
- package/templates/src/locales/pt-BR/pages/formTemplate.json +0 -16
- package/templates/src/locales/pt-BR/pages/loginTemplate.json +0 -9
- package/templates/vite.config.d.ts +0 -2
- package/templates/vite.config.js +0 -20
|
@@ -3,9 +3,7 @@ import { Button } from '../../ui/button';
|
|
|
3
3
|
import { Input } from '../../ui/input';
|
|
4
4
|
import { Label } from '../../ui/label';
|
|
5
5
|
import { XerticaLogo } from '../../brand/xertica-logo';
|
|
6
|
-
import {
|
|
7
|
-
import { LanguageSelector } from '../../brand/language-selector';
|
|
8
|
-
import { Lock } from 'lucide-react';
|
|
6
|
+
import { AuthPageShell, SocialLoginButtons } from '../../blocks/auth';
|
|
9
7
|
import { useNavigate } from 'react-router-dom';
|
|
10
8
|
import { useTranslation } from 'react-i18next';
|
|
11
9
|
|
|
@@ -64,155 +62,64 @@ export function LoginPage({ onLogin }: LoginPageProps) {
|
|
|
64
62
|
};
|
|
65
63
|
|
|
66
64
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
{/* Gradient overlay */}
|
|
78
|
-
<div className="absolute inset-0 bg-[image:var(--gradient-diagonal)] opacity-80" />
|
|
65
|
+
<AuthPageShell
|
|
66
|
+
imageSrc="https://images.unsplash.com/photo-1551434678-e076c223a692?w=1200&h=800&fit=crop&auto=format"
|
|
67
|
+
imageAlt={t('login.heroImageAlt')}
|
|
68
|
+
>
|
|
69
|
+
<div className="text-center">
|
|
70
|
+
<div className="flex items-center justify-center mb-4">
|
|
71
|
+
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
72
|
+
</div>
|
|
73
|
+
<h2 className="text-sm text-muted-foreground">{t('login.heading')}</h2>
|
|
79
74
|
</div>
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
77
|
+
<div className="space-y-2">
|
|
78
|
+
<Label htmlFor="email">{t('login.emailLabel')}</Label>
|
|
79
|
+
<Input
|
|
80
|
+
id="email"
|
|
81
|
+
name="email"
|
|
82
|
+
type="email"
|
|
83
|
+
required
|
|
84
|
+
className="w-full"
|
|
85
|
+
placeholder={t('login.emailPlaceholder')}
|
|
86
|
+
value={email}
|
|
87
|
+
onChange={e => setEmail(e.target.value)}
|
|
88
|
+
/>
|
|
86
89
|
</div>
|
|
87
90
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{/* Form */}
|
|
103
|
-
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
104
|
-
<div className="space-y-2">
|
|
105
|
-
<Label htmlFor="email">{t('login.emailLabel')}</Label>
|
|
106
|
-
<Input
|
|
107
|
-
id="email"
|
|
108
|
-
name="email"
|
|
109
|
-
type="email"
|
|
110
|
-
required
|
|
111
|
-
className="w-full"
|
|
112
|
-
placeholder={t('login.emailPlaceholder')}
|
|
113
|
-
value={email}
|
|
114
|
-
onChange={e => setEmail(e.target.value)}
|
|
115
|
-
/>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
<div className="space-y-2">
|
|
119
|
-
<Label htmlFor="password">{t('login.passwordLabel')}</Label>
|
|
120
|
-
<Input
|
|
121
|
-
id="password"
|
|
122
|
-
name="password"
|
|
123
|
-
type="password"
|
|
124
|
-
required
|
|
125
|
-
className="w-full"
|
|
126
|
-
placeholder="••••••••"
|
|
127
|
-
value={password}
|
|
128
|
-
onChange={e => setPassword(e.target.value)}
|
|
129
|
-
/>
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
{error && <div className="text-destructive text-sm text-center">{error}</div>}
|
|
133
|
-
|
|
134
|
-
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
135
|
-
{isLoading ? t('login.submitting') : t('login.submit')}
|
|
136
|
-
</Button>
|
|
137
|
-
|
|
138
|
-
{/* Password recovery link */}
|
|
139
|
-
<div className="text-center">
|
|
140
|
-
<button
|
|
141
|
-
type="button"
|
|
142
|
-
onClick={() => navigate('/forgot-password')}
|
|
143
|
-
className="text-sm text-primary hover:opacity-80 transition-colors"
|
|
144
|
-
>
|
|
145
|
-
{t('login.forgotPassword')}
|
|
146
|
-
</button>
|
|
147
|
-
</div>
|
|
148
|
-
</form>
|
|
149
|
-
|
|
150
|
-
{/* Divider */}
|
|
151
|
-
<div className="relative">
|
|
152
|
-
<div className="absolute inset-0 flex items-center">
|
|
153
|
-
<div className="w-full border-t border-border"></div>
|
|
154
|
-
</div>
|
|
155
|
-
<div className="relative flex justify-center text-sm">
|
|
156
|
-
<span className="bg-muted px-2 text-muted-foreground">
|
|
157
|
-
{t('login.orContinueWith')}
|
|
158
|
-
</span>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
91
|
+
<div className="space-y-2">
|
|
92
|
+
<Label htmlFor="password">{t('login.passwordLabel')}</Label>
|
|
93
|
+
<Input
|
|
94
|
+
id="password"
|
|
95
|
+
name="password"
|
|
96
|
+
type="password"
|
|
97
|
+
required
|
|
98
|
+
className="w-full"
|
|
99
|
+
placeholder="••••••••"
|
|
100
|
+
value={password}
|
|
101
|
+
onChange={e => setPassword(e.target.value)}
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
161
104
|
|
|
162
|
-
|
|
163
|
-
<div className="space-y-3">
|
|
164
|
-
{/* Google */}
|
|
165
|
-
<Button
|
|
166
|
-
type="button"
|
|
167
|
-
variant="outline"
|
|
168
|
-
className="w-full justify-center"
|
|
169
|
-
onClick={() => handleSocialLogin('Google')}
|
|
170
|
-
>
|
|
171
|
-
<svg className="w-5 h-5 mr-2" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
|
172
|
-
<path
|
|
173
|
-
fill="#EA4335"
|
|
174
|
-
d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
|
|
175
|
-
></path>
|
|
176
|
-
<path
|
|
177
|
-
fill="#4285F4"
|
|
178
|
-
d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
|
|
179
|
-
></path>
|
|
180
|
-
<path
|
|
181
|
-
fill="#FBBC05"
|
|
182
|
-
d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"
|
|
183
|
-
></path>
|
|
184
|
-
<path
|
|
185
|
-
fill="#34A853"
|
|
186
|
-
d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
|
|
187
|
-
></path>
|
|
188
|
-
<path fill="none" d="M0 0h48v48H0z"></path>
|
|
189
|
-
</svg>
|
|
190
|
-
<span>{t('login.signInWithGoogle')}</span>
|
|
191
|
-
</Button>
|
|
105
|
+
{error && <div className="text-destructive text-sm text-center">{error}</div>}
|
|
192
106
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
variant="outline"
|
|
197
|
-
className="w-full justify-center"
|
|
198
|
-
onClick={() => handleSocialLogin('MT Login')}
|
|
199
|
-
>
|
|
200
|
-
<Lock className="w-5 h-5 mr-2 text-[var(--chart-4)]" />
|
|
201
|
-
<span>{t('login.signInWithMTLogin')}</span>
|
|
202
|
-
</Button>
|
|
107
|
+
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
108
|
+
{isLoading ? t('login.submitting') : t('login.submit')}
|
|
109
|
+
</Button>
|
|
203
110
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
</Button>
|
|
213
|
-
</div>
|
|
111
|
+
<div className="text-center">
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
onClick={() => navigate('/forgot-password')}
|
|
115
|
+
className="text-sm text-primary hover:opacity-80 transition-colors"
|
|
116
|
+
>
|
|
117
|
+
{t('login.forgotPassword')}
|
|
118
|
+
</button>
|
|
214
119
|
</div>
|
|
215
|
-
</
|
|
216
|
-
|
|
120
|
+
</form>
|
|
121
|
+
|
|
122
|
+
<SocialLoginButtons onSocialLogin={handleSocialLogin} />
|
|
123
|
+
</AuthPageShell>
|
|
217
124
|
);
|
|
218
125
|
}
|
|
@@ -3,8 +3,7 @@ import { Button } from '../../ui/button';
|
|
|
3
3
|
import { Input } from '../../ui/input';
|
|
4
4
|
import { Label } from '../../ui/label';
|
|
5
5
|
import { XerticaLogo } from '../../brand/xertica-logo';
|
|
6
|
-
import {
|
|
7
|
-
import { LanguageSelector } from '../../brand/language-selector';
|
|
6
|
+
import { AuthPageShell, usePasswordStrength, PasswordStrengthMeter } from '../../blocks/auth';
|
|
8
7
|
import { ArrowLeft, CheckCircle2, AlertCircle } from 'lucide-react';
|
|
9
8
|
import { useNavigate } from 'react-router-dom';
|
|
10
9
|
import { useTranslation } from 'react-i18next';
|
|
@@ -18,7 +17,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
18
17
|
* password requirements.
|
|
19
18
|
*
|
|
20
19
|
* @ai-rules
|
|
21
|
-
* 1. Validation:
|
|
20
|
+
* 1. Validation: Uses the shared `usePasswordStrength` hook to categorize passwords as 'weak', 'medium', or 'strong'.
|
|
22
21
|
* 2. Feedback: Uses `CheckCircle2` icons to show real-time progress on password requirements (length, matching).
|
|
23
22
|
* 3. Navigation: Redirects to `/login` with a `resetSuccess` flag after a successful password update.
|
|
24
23
|
*/
|
|
@@ -29,29 +28,10 @@ export function ResetPasswordPage() {
|
|
|
29
28
|
const [confirmPassword, setConfirmPassword] = useState('');
|
|
30
29
|
const [isLoading, setIsLoading] = useState(false);
|
|
31
30
|
const [error, setError] = useState('');
|
|
32
|
-
const
|
|
33
|
-
null
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
// Password strength validation
|
|
37
|
-
const checkPasswordStrength = (pwd: string) => {
|
|
38
|
-
if (pwd.length === 0) {
|
|
39
|
-
setPasswordStrength(null);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (pwd.length < 6) {
|
|
44
|
-
setPasswordStrength('weak');
|
|
45
|
-
} else if (pwd.length < 10) {
|
|
46
|
-
setPasswordStrength('medium');
|
|
47
|
-
} else {
|
|
48
|
-
setPasswordStrength('strong');
|
|
49
|
-
}
|
|
50
|
-
};
|
|
31
|
+
const passwordStrength = usePasswordStrength(password);
|
|
51
32
|
|
|
52
33
|
const handlePasswordChange = (value: string) => {
|
|
53
34
|
setPassword(value);
|
|
54
|
-
checkPasswordStrength(value);
|
|
55
35
|
setError('');
|
|
56
36
|
};
|
|
57
37
|
|
|
@@ -81,163 +61,93 @@ export function ResetPasswordPage() {
|
|
|
81
61
|
setIsLoading(false);
|
|
82
62
|
};
|
|
83
63
|
|
|
84
|
-
const getStrengthColor = () => {
|
|
85
|
-
switch (passwordStrength) {
|
|
86
|
-
case 'weak':
|
|
87
|
-
return 'text-destructive';
|
|
88
|
-
case 'medium':
|
|
89
|
-
return 'text-[var(--chart-3)]';
|
|
90
|
-
case 'strong':
|
|
91
|
-
return 'text-[var(--chart-2)]';
|
|
92
|
-
default:
|
|
93
|
-
return 'text-muted-foreground';
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const getStrengthText = () => {
|
|
98
|
-
switch (passwordStrength) {
|
|
99
|
-
case 'weak':
|
|
100
|
-
return t('resetPassword.strengthWeak');
|
|
101
|
-
case 'medium':
|
|
102
|
-
return t('resetPassword.strengthMedium');
|
|
103
|
-
case 'strong':
|
|
104
|
-
return t('resetPassword.strengthStrong');
|
|
105
|
-
default:
|
|
106
|
-
return '';
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
|
|
110
64
|
return (
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
/>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
65
|
+
<AuthPageShell
|
|
66
|
+
imageSrc="https://images.unsplash.com/photo-1555949963-aa79dcee981c?w=1200&h=800&fit=crop&auto=format"
|
|
67
|
+
imageAlt={t('resetPassword.heroImageAlt')}
|
|
68
|
+
>
|
|
69
|
+
<button
|
|
70
|
+
onClick={() => navigate('/login')}
|
|
71
|
+
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
72
|
+
>
|
|
73
|
+
<ArrowLeft className="w-4 h-4" />
|
|
74
|
+
<span className="text-small">{t('resetPassword.backToLogin')}</span>
|
|
75
|
+
</button>
|
|
76
|
+
|
|
77
|
+
<div className="text-center">
|
|
78
|
+
<div className="flex items-center justify-center mb-4">
|
|
79
|
+
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
80
|
+
</div>
|
|
81
|
+
<h2 className="text-sm text-muted-foreground">{t('resetPassword.heading')}</h2>
|
|
82
|
+
<p className="mt-2 text-muted-foreground">{t('resetPassword.subheading')}</p>
|
|
123
83
|
</div>
|
|
124
84
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
85
|
+
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
86
|
+
<div className="space-y-2">
|
|
87
|
+
<Label htmlFor="password">{t('resetPassword.newPasswordLabel')}</Label>
|
|
88
|
+
<Input
|
|
89
|
+
id="password"
|
|
90
|
+
name="password"
|
|
91
|
+
type="password"
|
|
92
|
+
required
|
|
93
|
+
className="w-full"
|
|
94
|
+
placeholder="••••••••"
|
|
95
|
+
value={password}
|
|
96
|
+
onChange={e => handlePasswordChange(e.target.value)}
|
|
97
|
+
/>
|
|
98
|
+
<PasswordStrengthMeter strength={passwordStrength} />
|
|
130
99
|
</div>
|
|
131
100
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
className="
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<XerticaLogo
|
|
149
|
-
className="h-12 w-auto text-primary dark:text-foreground"
|
|
150
|
-
variant="theme"
|
|
151
|
-
/>
|
|
152
|
-
</div>
|
|
153
|
-
<h2 className="text-sm text-muted-foreground">{t('resetPassword.heading')}</h2>
|
|
154
|
-
<p className="mt-2 text-muted-foreground">{t('resetPassword.subheading')}</p>
|
|
155
|
-
</div>
|
|
101
|
+
<div className="space-y-2">
|
|
102
|
+
<Label htmlFor="confirmPassword">{t('resetPassword.confirmPasswordLabel')}</Label>
|
|
103
|
+
<Input
|
|
104
|
+
id="confirmPassword"
|
|
105
|
+
name="confirmPassword"
|
|
106
|
+
type="password"
|
|
107
|
+
required
|
|
108
|
+
className="w-full"
|
|
109
|
+
placeholder="••••••••"
|
|
110
|
+
value={confirmPassword}
|
|
111
|
+
onChange={e => {
|
|
112
|
+
setConfirmPassword(e.target.value);
|
|
113
|
+
setError('');
|
|
114
|
+
}}
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
156
117
|
|
|
157
|
-
|
|
158
|
-
<
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
name="password"
|
|
164
|
-
type="password"
|
|
165
|
-
required
|
|
166
|
-
className="w-full"
|
|
167
|
-
placeholder="••••••••"
|
|
168
|
-
value={password}
|
|
169
|
-
onChange={e => handlePasswordChange(e.target.value)}
|
|
118
|
+
<div className="bg-accent rounded-[var(--radius)] p-4 space-y-2">
|
|
119
|
+
<p className="text-sm">{t('resetPassword.requirements')}</p>
|
|
120
|
+
<div className="space-y-1">
|
|
121
|
+
<div className="flex items-center gap-2">
|
|
122
|
+
<CheckCircle2
|
|
123
|
+
className={`w-4 h-4 ${password.length >= 6 ? 'text-[var(--chart-2)]' : 'text-muted-foreground'}`}
|
|
170
124
|
/>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
<div
|
|
175
|
-
className={`h-full transition-all duration-300 ${
|
|
176
|
-
passwordStrength === 'weak'
|
|
177
|
-
? 'w-1/3 bg-destructive'
|
|
178
|
-
: passwordStrength === 'medium'
|
|
179
|
-
? 'w-2/3 bg-[var(--chart-3)]'
|
|
180
|
-
: 'w-full bg-[var(--chart-2)]'
|
|
181
|
-
}`}
|
|
182
|
-
/>
|
|
183
|
-
</div>
|
|
184
|
-
<span className={`text-small ${getStrengthColor()}`}>{getStrengthText()}</span>
|
|
185
|
-
</div>
|
|
186
|
-
)}
|
|
125
|
+
<span className="text-sm text-muted-foreground">
|
|
126
|
+
{t('resetPassword.requirementMinChars')}
|
|
127
|
+
</span>
|
|
187
128
|
</div>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
<Input
|
|
192
|
-
id="confirmPassword"
|
|
193
|
-
name="confirmPassword"
|
|
194
|
-
type="password"
|
|
195
|
-
required
|
|
196
|
-
className="w-full"
|
|
197
|
-
placeholder="••••••••"
|
|
198
|
-
value={confirmPassword}
|
|
199
|
-
onChange={e => {
|
|
200
|
-
setConfirmPassword(e.target.value);
|
|
201
|
-
setError('');
|
|
202
|
-
}}
|
|
129
|
+
<div className="flex items-center gap-2">
|
|
130
|
+
<CheckCircle2
|
|
131
|
+
className={`w-4 h-4 ${password === confirmPassword && password.length > 0 ? 'text-[var(--chart-2)]' : 'text-muted-foreground'}`}
|
|
203
132
|
/>
|
|
133
|
+
<span className="text-sm text-muted-foreground">
|
|
134
|
+
{t('resetPassword.requirementMatch')}
|
|
135
|
+
</span>
|
|
204
136
|
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
205
139
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
/>
|
|
213
|
-
<span className="text-sm text-muted-foreground">
|
|
214
|
-
{t('resetPassword.requirementMinChars')}
|
|
215
|
-
</span>
|
|
216
|
-
</div>
|
|
217
|
-
<div className="flex items-center gap-2">
|
|
218
|
-
<CheckCircle2
|
|
219
|
-
className={`w-4 h-4 ${password === confirmPassword && password.length > 0 ? 'text-[var(--chart-2)]' : 'text-muted-foreground'}`}
|
|
220
|
-
/>
|
|
221
|
-
<span className="text-sm text-muted-foreground">
|
|
222
|
-
{t('resetPassword.requirementMatch')}
|
|
223
|
-
</span>
|
|
224
|
-
</div>
|
|
225
|
-
</div>
|
|
226
|
-
</div>
|
|
140
|
+
{error && (
|
|
141
|
+
<div className="flex items-center gap-2 text-destructive bg-destructive/10 rounded-[var(--radius)] p-3">
|
|
142
|
+
<AlertCircle className="w-4 h-4 flex-shrink-0" />
|
|
143
|
+
<span className="text-sm">{error}</span>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
227
146
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
)}
|
|
234
|
-
|
|
235
|
-
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
236
|
-
{isLoading ? t('resetPassword.submitting') : t('resetPassword.submit')}
|
|
237
|
-
</Button>
|
|
238
|
-
</form>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
147
|
+
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
148
|
+
{isLoading ? t('resetPassword.submitting') : t('resetPassword.submit')}
|
|
149
|
+
</Button>
|
|
150
|
+
</form>
|
|
151
|
+
</AuthPageShell>
|
|
242
152
|
);
|
|
243
153
|
}
|