@umituz/web-dashboard 3.2.2 → 4.0.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/README.md +202 -14
- package/package.json +21 -29
- package/src/domains/analytics/components/AnalyticsCard.tsx +23 -21
- package/src/domains/analytics/components/AnalyticsChart.tsx +345 -149
- package/src/domains/analytics/components/AnalyticsLayout.tsx +93 -112
- package/src/domains/analytics/components/MetricCard.tsx +42 -64
- package/src/domains/analytics/hooks/index.ts +7 -1
- package/src/domains/analytics/hooks/useAnalytics.ts +144 -139
- package/src/domains/analytics/index.ts +11 -0
- package/src/domains/analytics/services/AnalyticsEngineService.ts +103 -55
- package/src/domains/analytics/services/PerformanceService.ts +117 -69
- package/src/domains/analytics/types/analytics.ts +20 -6
- package/src/domains/analytics/types/index.ts +3 -0
- package/src/domains/analytics/utils/analytics.test.ts +183 -0
- package/src/domains/analytics/utils/analytics.ts +26 -9
- package/src/domains/analytics/utils/i18nKeys.ts +40 -0
- package/src/domains/analytics/utils/index.ts +2 -0
- package/src/domains/auth/components/AuthLayout.tsx +63 -54
- package/src/domains/auth/components/ForgotPasswordForm.tsx +96 -98
- package/src/domains/auth/components/LoginForm.tsx +167 -226
- package/src/domains/auth/components/RegisterForm.tsx +202 -272
- package/src/domains/auth/components/ResetPasswordForm.tsx +105 -151
- package/src/domains/auth/components/shared/AuthErrorBanner.tsx +35 -0
- package/src/domains/auth/components/shared/EmailInput.tsx +91 -0
- package/src/domains/auth/components/shared/PasswordInput.tsx +119 -0
- package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +75 -0
- package/src/domains/auth/components/shared/SocialLoginButtons.tsx +58 -0
- package/src/domains/auth/components/shared/SocialProviderIcons.tsx +48 -0
- package/src/domains/auth/components/shared/index.ts +20 -0
- package/src/domains/auth/hooks/index.ts +1 -0
- package/src/domains/auth/hooks/useAuth.ts +151 -232
- package/src/domains/auth/index.ts +8 -4
- package/src/domains/auth/types/auth.ts +26 -5
- package/src/domains/auth/types/index.ts +1 -0
- package/src/domains/auth/utils/auth.test.ts +182 -0
- package/src/domains/auth/utils/auth.ts +71 -130
- package/src/domains/auth/utils/i18nKeys.ts +127 -0
- package/src/domains/auth/utils/index.ts +7 -1
- package/src/domains/auth/utils/secureToken.ts +33 -0
- package/src/domains/auth/utils/validationKeys.ts +23 -0
- package/src/domains/calendar/hooks/useCalendar.ts +145 -151
- package/src/domains/calendar/index.ts +0 -1
- package/src/domains/calendar/services/CalendarService.ts +122 -179
- package/src/domains/calendar/utils/contentItemMapper.test.ts +108 -0
- package/src/domains/calendar/utils/contentItemMapper.ts +99 -0
- package/src/domains/calendar/utils/index.ts +33 -30
- package/src/domains/layouts/components/BrandLogo.tsx +12 -9
- package/src/domains/layouts/components/DashboardHeader.tsx +78 -80
- package/src/domains/layouts/components/DashboardLayout.tsx +45 -21
- package/src/domains/layouts/components/DashboardSidebar.tsx +48 -51
- package/src/domains/layouts/types/layout.ts +7 -1
- package/src/domains/layouts/types/user.ts +3 -1
- package/src/domains/modules/build.test.tsx +97 -0
- package/src/domains/modules/build.tsx +71 -0
- package/src/domains/modules/index.ts +2 -0
- package/src/domains/modules/types.ts +29 -0
- package/src/domains/onboarding/components/AppFocusStep.tsx +99 -68
- package/src/domains/onboarding/components/OnboardingWizard.tsx +109 -183
- package/src/domains/onboarding/components/PlanStep.tsx +156 -123
- package/src/domains/onboarding/components/PlatformsStep.tsx +74 -54
- package/src/domains/onboarding/components/StepNavigation.tsx +108 -0
- package/src/domains/onboarding/components/StepProgress.tsx +62 -0
- package/src/domains/onboarding/components/UserTypeStep.tsx +59 -80
- package/src/domains/onboarding/components/index.ts +4 -0
- package/src/domains/onboarding/hooks/useOnboarding.ts +76 -66
- package/src/domains/onboarding/index.ts +1 -0
- package/src/domains/onboarding/types/onboarding.ts +57 -17
- package/src/domains/onboarding/utils/i18nKeys.ts +72 -0
- package/src/domains/onboarding/utils/index.ts +2 -0
- package/src/domains/permissions/PermissionsContext.tsx +103 -0
- package/src/domains/permissions/hasPermission.test.ts +50 -0
- package/src/domains/permissions/hasPermission.ts +38 -0
- package/src/domains/permissions/index.ts +19 -0
- package/src/domains/permissions/types.ts +35 -0
- package/src/domains/permissions/utils/i18nKeys.ts +11 -0
- package/src/domains/session/DemoLogin.tsx +50 -0
- package/src/domains/session/OrgSwitcher.tsx +110 -0
- package/src/domains/session/SessionContext.tsx +150 -0
- package/src/domains/session/inMemoryAuthAdapter.ts +53 -0
- package/src/domains/session/index.ts +18 -0
- package/src/domains/session/types.ts +47 -0
- package/src/domains/session/utils/i18nKeys.ts +12 -0
- package/src/domains/settings/components/AuditLogList.tsx +59 -0
- package/src/domains/settings/components/PermissionMatrixScreen.tsx +80 -0
- package/src/domains/settings/components/SettingsLayout.tsx +75 -60
- package/src/domains/settings/components/SettingsSection.tsx +35 -22
- package/src/domains/settings/components/UserManagementTable.tsx +87 -0
- package/src/domains/settings/components/index.ts +10 -0
- package/src/domains/settings/hooks/useSettings.ts +77 -52
- package/src/domains/settings/types/settings.ts +2 -0
- package/src/domains/settings/utils/i18nKeys.ts +18 -0
- package/src/domains/settings/utils/settings.ts +11 -4
- package/src/i18n/I18nContext.tsx +33 -0
- package/src/i18n/defaultT.test.ts +43 -0
- package/src/i18n/defaultT.ts +34 -0
- package/src/i18n/index.ts +7 -0
- package/src/i18n/types.ts +13 -0
- package/src/index.ts +1 -7
- package/src/primitives/Button.tsx +30 -0
- package/src/primitives/Checkbox.tsx +40 -0
- package/src/primitives/Input.tsx +19 -0
- package/src/primitives/Skeleton.tsx +26 -0
- package/src/primitives/cn.ts +15 -0
- package/src/primitives/index.ts +23 -0
- package/src/styles/dashboard.css +3045 -0
- package/src/styles/tokens.contract.test.ts +66 -0
- package/src/styles/tokens.css +124 -0
- package/src/domains/billing/components/BillingLayout.tsx +0 -63
- package/src/domains/billing/components/BillingPage.tsx +0 -49
- package/src/domains/billing/components/BillingPortal.tsx +0 -198
- package/src/domains/billing/components/InvoiceCard.tsx +0 -143
- package/src/domains/billing/components/PaymentMethodsList.tsx +0 -116
- package/src/domains/billing/components/PlanComparison.tsx +0 -199
- package/src/domains/billing/components/UsageCard.tsx +0 -103
- package/src/domains/billing/components/index.ts +0 -13
- package/src/domains/billing/constants/billing.ts +0 -69
- package/src/domains/billing/hooks/index.ts +0 -7
- package/src/domains/billing/hooks/useBilling.ts +0 -385
- package/src/domains/billing/index.ts +0 -73
- package/src/domains/billing/types/billing.ts +0 -342
- package/src/domains/billing/types/index.ts +0 -30
- package/src/domains/billing/utils/billing.ts +0 -360
- package/src/domains/billing/utils/index.ts +0 -31
- package/src/domains/billing/utils/polar.ts +0 -63
- package/src/domains/calendar/components/index.ts +0 -7
|
@@ -1,47 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth Layout Component
|
|
3
3
|
*
|
|
4
|
-
* Configurable layout wrapper for authentication pages
|
|
4
|
+
* Configurable layout wrapper for authentication pages.
|
|
5
|
+
* Strings stay localizable via the optional `translate` prop; when it
|
|
6
|
+
* is not provided the embedded English fallbacks are rendered.
|
|
7
|
+
* Styling is entirely `udb-auth*` classes from the package stylesheet.
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
import { BrandLogo } from "../../layouts/components";
|
|
8
|
-
import { cn } from "
|
|
9
|
-
import
|
|
11
|
+
import { cn } from "../../../primitives";
|
|
12
|
+
import { AUTH_KEYS } from "../utils/i18nKeys";
|
|
13
|
+
import type { AuthLayoutProps, Translate } from "../types/auth";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Embedded fallbacks used when no `translate` prop is supplied.
|
|
17
|
+
* Keys mirror the AUTH_KEYS catalog so consumers can override
|
|
18
|
+
* every string via their own i18n setup.
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_STRINGS: Record<string, string> = {
|
|
21
|
+
[AUTH_KEYS.layout.backToHome]: "Back to home",
|
|
22
|
+
[AUTH_KEYS.layout.orContinueWith]: "Or continue with",
|
|
23
|
+
[AUTH_KEYS.layout.allRightsReserved]: "All rights reserved.",
|
|
24
|
+
};
|
|
10
25
|
|
|
11
26
|
export const AuthLayout = ({
|
|
12
27
|
config,
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
translate,
|
|
29
|
+
onSocialLogin,
|
|
15
30
|
children,
|
|
16
31
|
}: AuthLayoutProps) => {
|
|
32
|
+
const t: Translate = translate ?? ((key) => DEFAULT_STRINGS[key] ?? key);
|
|
33
|
+
// Prefer an explicit home route; fall back to afterLoginRoute for backwards compatibility.
|
|
34
|
+
const homeRoute = config.homeRoute ?? config.afterLoginRoute;
|
|
35
|
+
const footerLinks = config.footerLinks ?? [];
|
|
36
|
+
|
|
17
37
|
return (
|
|
18
|
-
<div className="
|
|
38
|
+
<div className="udb-root udb-auth">
|
|
19
39
|
{/* Header */}
|
|
20
|
-
<header className="
|
|
21
|
-
<div className="
|
|
40
|
+
<header className="udb-auth__header">
|
|
41
|
+
<div className="udb-auth__brand">
|
|
22
42
|
<BrandLogo size={32} />
|
|
23
|
-
<span className="
|
|
43
|
+
<span className="udb-auth__brand-name">{config.brandName}</span>
|
|
24
44
|
</div>
|
|
25
45
|
|
|
26
46
|
{/* Additional Header Content */}
|
|
27
|
-
{
|
|
28
|
-
<a
|
|
29
|
-
|
|
30
|
-
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
31
|
-
>
|
|
32
|
-
Back to home
|
|
47
|
+
{homeRoute && (
|
|
48
|
+
<a href={homeRoute} className="udb-auth__home-link">
|
|
49
|
+
{t(AUTH_KEYS.layout.backToHome)}
|
|
33
50
|
</a>
|
|
34
51
|
)}
|
|
35
52
|
</header>
|
|
36
53
|
|
|
37
54
|
{/* Main Content */}
|
|
38
|
-
<main className="
|
|
39
|
-
<div className="
|
|
55
|
+
<main className="udb-auth__main">
|
|
56
|
+
<div className="udb-auth__column">
|
|
40
57
|
{/* Card Container */}
|
|
41
|
-
<div className="
|
|
58
|
+
<div className="udb-auth__card">
|
|
42
59
|
{/* Tagline */}
|
|
43
60
|
{config.brandTagline && (
|
|
44
|
-
<p className="
|
|
61
|
+
<p className="udb-auth__tagline">
|
|
45
62
|
{config.brandTagline}
|
|
46
63
|
</p>
|
|
47
64
|
)}
|
|
@@ -52,36 +69,24 @@ export const AuthLayout = ({
|
|
|
52
69
|
|
|
53
70
|
{/* Social Login */}
|
|
54
71
|
{config.showSocialLogin && config.socialProviders && config.socialProviders.length > 0 && (
|
|
55
|
-
<div className="
|
|
72
|
+
<div className="udb-auth__social">
|
|
56
73
|
{/* Divider */}
|
|
57
|
-
<div className="
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
</
|
|
61
|
-
<div className="relative flex justify-center text-sm">
|
|
62
|
-
<span className="px-2 bg-secondary/30 text-muted-foreground">
|
|
63
|
-
Or continue with
|
|
64
|
-
</span>
|
|
65
|
-
</div>
|
|
74
|
+
<div className="udb-divider-row">
|
|
75
|
+
<span className="udb-divider-row__label">
|
|
76
|
+
{t(AUTH_KEYS.layout.orContinueWith)}
|
|
77
|
+
</span>
|
|
66
78
|
</div>
|
|
67
79
|
|
|
68
80
|
{/* Social Buttons */}
|
|
69
|
-
<div className="
|
|
81
|
+
<div className="udb-social-grid">
|
|
70
82
|
{config.socialProviders.map((provider) => (
|
|
71
83
|
<button
|
|
72
84
|
key={provider.id}
|
|
73
85
|
type="button"
|
|
74
|
-
className={cn(
|
|
75
|
-
|
|
76
|
-
"bg-background hover:bg-muted transition-colors",
|
|
77
|
-
"text-sm font-medium text-foreground"
|
|
78
|
-
)}
|
|
79
|
-
onClick={() => {
|
|
80
|
-
// Handle social login
|
|
81
|
-
console.log(`Social login with ${provider.id}`);
|
|
82
|
-
}}
|
|
86
|
+
className={cn("udb-social-btn")}
|
|
87
|
+
onClick={() => onSocialLogin?.(provider.id)}
|
|
83
88
|
>
|
|
84
|
-
<span className="
|
|
89
|
+
<span className="udb-social-btn__icon">{provider.icon}</span>
|
|
85
90
|
<span>{provider.name}</span>
|
|
86
91
|
</button>
|
|
87
92
|
))}
|
|
@@ -92,20 +97,24 @@ export const AuthLayout = ({
|
|
|
92
97
|
</main>
|
|
93
98
|
|
|
94
99
|
{/* Footer */}
|
|
95
|
-
<footer className="
|
|
96
|
-
<div className="
|
|
97
|
-
<p
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
<footer className="udb-auth__footer">
|
|
101
|
+
<div className="udb-auth__footer-inner">
|
|
102
|
+
<p>
|
|
103
|
+
© {new Date().getFullYear()} {config.brandName}. {t(AUTH_KEYS.layout.allRightsReserved)}
|
|
104
|
+
</p>
|
|
105
|
+
{footerLinks.length > 0 && (
|
|
106
|
+
<div className="udb-auth__footer-links">
|
|
107
|
+
{footerLinks.map((link) => (
|
|
108
|
+
<a
|
|
109
|
+
key={link.href}
|
|
110
|
+
href={link.href}
|
|
111
|
+
className="udb-auth__footer-link"
|
|
112
|
+
>
|
|
113
|
+
{link.label}
|
|
114
|
+
</a>
|
|
115
|
+
))}
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
109
118
|
</div>
|
|
110
119
|
</footer>
|
|
111
120
|
</div>
|
|
@@ -1,16 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ForgotPasswordForm
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Composed of shared auth primitives (EmailInput, AuthErrorBanner).
|
|
5
|
+
* All copy is i18n-keyed; the success state uses lucide icons
|
|
6
|
+
* instead of the previous `✓` Unicode glyphs.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
import { useState } from "react";
|
|
8
10
|
import { useNavigate } from "react-router-dom";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { Button } from "
|
|
12
|
-
import type { ForgotPasswordFormProps } from "../types/auth";
|
|
11
|
+
import { useT } from "../../../i18n";
|
|
12
|
+
import { ArrowLeft, Loader2, CheckCircle2, Check } from "lucide-react";
|
|
13
|
+
import { Button } from "../../../primitives";
|
|
14
|
+
import type { ForgotPasswordFormProps, Translate } from "../types/auth";
|
|
13
15
|
import { validateForgotPassword } from "../utils/auth";
|
|
16
|
+
import { AUTH_KEYS } from "../utils/i18nKeys";
|
|
17
|
+
import { AUTH_VALIDATION_KEYS } from "../utils/validationKeys";
|
|
18
|
+
import { AuthErrorBanner, EmailInput } from "./shared";
|
|
19
|
+
|
|
20
|
+
const KEYS = {
|
|
21
|
+
title: 'auth.forgotPassword.title',
|
|
22
|
+
description: 'auth.forgotPassword.description',
|
|
23
|
+
emailLabel: AUTH_KEYS.fields.email,
|
|
24
|
+
emailPlaceholder: AUTH_KEYS.placeholders.emailExample,
|
|
25
|
+
submitting: 'auth.forgotPassword.submitting',
|
|
26
|
+
submit: 'auth.forgotPassword.submit',
|
|
27
|
+
backToSignIn: 'auth.forgotPassword.backToSignIn',
|
|
28
|
+
successTitle: 'auth.forgotPassword.successTitle',
|
|
29
|
+
successMessage: 'auth.forgotPassword.successMessage',
|
|
30
|
+
sentTo: 'auth.forgotPassword.sentTo',
|
|
31
|
+
step1: 'auth.forgotPassword.steps.step1',
|
|
32
|
+
step2: 'auth.forgotPassword.steps.step2',
|
|
33
|
+
step3: 'auth.forgotPassword.steps.step3',
|
|
34
|
+
invalidEmail: AUTH_VALIDATION_KEYS.emailInvalid,
|
|
35
|
+
sendResetFailed: 'auth.errors.sendResetFailed',
|
|
36
|
+
} as const;
|
|
14
37
|
|
|
15
38
|
export const ForgotPasswordForm = ({
|
|
16
39
|
config,
|
|
@@ -19,6 +42,9 @@ export const ForgotPasswordForm = ({
|
|
|
19
42
|
showBackLink = true,
|
|
20
43
|
}: ForgotPasswordFormProps) => {
|
|
21
44
|
const navigate = useNavigate();
|
|
45
|
+
const t = useT();
|
|
46
|
+
const translate: Translate = t;
|
|
47
|
+
|
|
22
48
|
const [email, setEmail] = useState("");
|
|
23
49
|
const [isLoading, setIsLoading] = useState(false);
|
|
24
50
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -28,29 +54,26 @@ export const ForgotPasswordForm = ({
|
|
|
28
54
|
e.preventDefault();
|
|
29
55
|
setError(null);
|
|
30
56
|
|
|
31
|
-
// Validate
|
|
32
57
|
const validation = validateForgotPassword({ email });
|
|
33
58
|
if (!validation.valid) {
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
const message = validation.error ?? KEYS.invalidEmail;
|
|
60
|
+
setError(message);
|
|
61
|
+
onError?.(message);
|
|
36
62
|
return;
|
|
37
63
|
}
|
|
38
64
|
|
|
39
65
|
setIsLoading(true);
|
|
40
|
-
|
|
41
66
|
try {
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
await
|
|
47
|
-
|
|
67
|
+
// Hand off to the consumer's auth provider. The form has
|
|
68
|
+
// no network knowledge of its own — it's pure orchestration.
|
|
69
|
+
// The submitted email is passed through so the consumer can
|
|
70
|
+
// actually trigger the reset email.
|
|
71
|
+
await onSuccess?.({ email });
|
|
48
72
|
setSuccess(true);
|
|
49
|
-
await onSuccess?.();
|
|
50
73
|
} catch (err) {
|
|
51
|
-
const
|
|
52
|
-
setError(
|
|
53
|
-
onError?.(
|
|
74
|
+
const message = err instanceof Error ? err.message : KEYS.sendResetFailed;
|
|
75
|
+
setError(message);
|
|
76
|
+
onError?.(message);
|
|
54
77
|
} finally {
|
|
55
78
|
setIsLoading(false);
|
|
56
79
|
}
|
|
@@ -58,48 +81,44 @@ export const ForgotPasswordForm = ({
|
|
|
58
81
|
|
|
59
82
|
if (success) {
|
|
60
83
|
return (
|
|
61
|
-
<div className="
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<div className="w-16 h-16 bg-green-100 dark:bg-green-900/20 rounded-full flex items-center justify-center">
|
|
66
|
-
<CheckCircle className="h-8 w-8 text-green-600 dark:text-green-500" />
|
|
67
|
-
</div>
|
|
84
|
+
<div className="udb-form" role="status" aria-live="polite">
|
|
85
|
+
<div className="udb-form__header">
|
|
86
|
+
<div className="udb-success-badge">
|
|
87
|
+
<CheckCircle2 className="udb-success-badge__icon" aria-hidden="true" />
|
|
68
88
|
</div>
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
<span className="font-medium text-foreground">{email}</span>
|
|
76
|
-
</p>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
{/* Instructions */}
|
|
81
|
-
<div className="bg-muted/50 border border-border rounded-lg p-4 space-y-2">
|
|
82
|
-
<p className="text-sm text-muted-foreground">
|
|
83
|
-
✓ Click the link in your email
|
|
84
|
-
</p>
|
|
85
|
-
<p className="text-sm text-muted-foreground">
|
|
86
|
-
✓ Create a new password
|
|
87
|
-
</p>
|
|
88
|
-
<p className="text-sm text-muted-foreground">
|
|
89
|
-
✓ Sign in with your new password
|
|
89
|
+
<h1 className="udb-form__title">
|
|
90
|
+
{translate(KEYS.successTitle)}
|
|
91
|
+
</h1>
|
|
92
|
+
<p className="udb-form__subtitle">
|
|
93
|
+
{translate(KEYS.sentTo)}{' '}
|
|
94
|
+
<span className="udb-form__subtitle-strong">{email}</span>
|
|
90
95
|
</p>
|
|
91
96
|
</div>
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
<ol className="udb-steps-list">
|
|
99
|
+
<li className="udb-steps-list__item">
|
|
100
|
+
<Check className="udb-steps-list__icon" aria-hidden="true" />
|
|
101
|
+
{translate(KEYS.step1)}
|
|
102
|
+
</li>
|
|
103
|
+
<li className="udb-steps-list__item">
|
|
104
|
+
<Check className="udb-steps-list__icon" aria-hidden="true" />
|
|
105
|
+
{translate(KEYS.step2)}
|
|
106
|
+
</li>
|
|
107
|
+
<li className="udb-steps-list__item">
|
|
108
|
+
<Check className="udb-steps-list__icon" aria-hidden="true" />
|
|
109
|
+
{translate(KEYS.step3)}
|
|
110
|
+
</li>
|
|
111
|
+
</ol>
|
|
112
|
+
|
|
94
113
|
{showBackLink && config.loginRoute && (
|
|
95
114
|
<Button
|
|
96
115
|
type="button"
|
|
97
116
|
variant="ghost"
|
|
98
|
-
onClick={() => navigate(config.loginRoute
|
|
99
|
-
className="
|
|
117
|
+
onClick={() => navigate(config.loginRoute)}
|
|
118
|
+
className="udb-submit-btn"
|
|
100
119
|
>
|
|
101
|
-
<ArrowLeft className="
|
|
102
|
-
|
|
120
|
+
<ArrowLeft className="udb-btn__spinner" aria-hidden="true" />
|
|
121
|
+
{translate(KEYS.backToSignIn)}
|
|
103
122
|
</Button>
|
|
104
123
|
)}
|
|
105
124
|
</div>
|
|
@@ -107,71 +126,50 @@ export const ForgotPasswordForm = ({
|
|
|
107
126
|
}
|
|
108
127
|
|
|
109
128
|
return (
|
|
110
|
-
<form onSubmit={handleSubmit} className="
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
Forgot password?
|
|
129
|
+
<form onSubmit={handleSubmit} className="udb-form" noValidate>
|
|
130
|
+
<div className="udb-form__header">
|
|
131
|
+
<h1 className="udb-form__title udb-form__title--lg">
|
|
132
|
+
{translate(KEYS.title)}
|
|
115
133
|
</h1>
|
|
116
|
-
<p className="
|
|
117
|
-
No worries, we'll send you reset instructions
|
|
118
|
-
</p>
|
|
134
|
+
<p className="udb-form__subtitle">{translate(KEYS.description)}</p>
|
|
119
135
|
</div>
|
|
120
136
|
|
|
121
|
-
{
|
|
122
|
-
{error && (
|
|
123
|
-
<div className="bg-destructive/10 border border-destructive/20 text-destructive px-4 py-3 rounded-lg text-sm">
|
|
124
|
-
{error}
|
|
125
|
-
</div>
|
|
126
|
-
)}
|
|
137
|
+
<AuthErrorBanner message={error} translate={translate} />
|
|
127
138
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
placeholder="you@example.com"
|
|
139
|
-
className={cn(
|
|
140
|
-
"w-full px-4 py-3 rounded-lg border bg-background",
|
|
141
|
-
"focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent",
|
|
142
|
-
"placeholder:text-muted-foreground",
|
|
143
|
-
error && "border-destructive"
|
|
144
|
-
)}
|
|
145
|
-
disabled={isLoading}
|
|
146
|
-
autoComplete="email"
|
|
147
|
-
/>
|
|
148
|
-
</div>
|
|
139
|
+
<EmailInput
|
|
140
|
+
id="forgot-email"
|
|
141
|
+
labelKey={KEYS.emailLabel}
|
|
142
|
+
placeholderKey={KEYS.emailPlaceholder}
|
|
143
|
+
value={email}
|
|
144
|
+
onChange={setEmail}
|
|
145
|
+
translate={translate}
|
|
146
|
+
disabled={isLoading}
|
|
147
|
+
required
|
|
148
|
+
/>
|
|
149
149
|
|
|
150
|
-
{/* Submit Button */}
|
|
151
150
|
<Button
|
|
152
151
|
type="submit"
|
|
153
|
-
className="
|
|
152
|
+
className="udb-submit-btn udb-submit-btn--pill"
|
|
154
153
|
disabled={isLoading}
|
|
155
154
|
>
|
|
156
155
|
{isLoading ? (
|
|
157
156
|
<>
|
|
158
|
-
<Loader2 className="
|
|
159
|
-
|
|
157
|
+
<Loader2 className="udb-btn__spinner" aria-hidden="true" />
|
|
158
|
+
{translate(KEYS.submitting)}
|
|
160
159
|
</>
|
|
161
160
|
) : (
|
|
162
|
-
|
|
161
|
+
translate(KEYS.submit)
|
|
163
162
|
)}
|
|
164
163
|
</Button>
|
|
165
164
|
|
|
166
|
-
{/* Back to Login */}
|
|
167
165
|
{showBackLink && config.loginRoute && (
|
|
168
166
|
<button
|
|
169
167
|
type="button"
|
|
170
|
-
onClick={() => navigate(config.loginRoute
|
|
171
|
-
className="
|
|
168
|
+
onClick={() => navigate(config.loginRoute)}
|
|
169
|
+
className="udb-back-link"
|
|
172
170
|
>
|
|
173
|
-
<ArrowLeft className="
|
|
174
|
-
|
|
171
|
+
<ArrowLeft className="udb-back-link__icon" aria-hidden="true" />
|
|
172
|
+
{translate(KEYS.backToSignIn)}
|
|
175
173
|
</button>
|
|
176
174
|
)}
|
|
177
175
|
</form>
|