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
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,24 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [2.7.0] — 2026-07-07
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`xertica-ui/blocks` ganha um novo namespace `auth`** — `AuthPageShell` (layout de imagem hero + seletor de idioma + painel de formulário centralizado), `SocialLoginButtons` (com prop `providers` configurável, default `['google', 'mtLogin', 'govBr']`, para projetos que não usam todos os provedores de login social), `usePasswordStrength` e `PasswordStrengthMeter`. Extraídos da duplicação entre `components/pages/{login,forgot-password,reset-password,verify-email}-page/*` e `templates/src/features/auth/ui/*`, que agora consomem a mesma fonte via `xertica-ui/blocks`.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Assistente de IA — clique numa sugestão inicial rolava a página e deslocava o header** — o efeito de auto-scroll de `use-assistant.ts` media `messagesEndRef.current.parentElement` (uma `<div>` sem overflow próprio) em vez do viewport real do `ScrollArea` do Radix, fazendo a checagem "está perto do fim da rolagem" ser sempre verdadeira — inclusive na primeira mensagem, na transição da tela de boas-vindas para a conversa. Sem `block: 'nearest'`, o `scrollIntoView` podia rolar a janela inteira. Corrigido para medir o `[data-radix-scroll-area-viewport]` correto e conter o scroll dentro dele.
|
|
23
|
+
- **`components/pages/forgot-password-page/ForgotPasswordPage.tsx` tinha texto hardcoded em português**, sem passar por `useTranslation()` como as demais páginas de auth. Convertido para `t('forgotPassword.xxx')`, com as chaves adicionadas em `locales/*/pages/forgotPassword.json` (en/es/pt-BR) na raiz.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **`templates/src/features/home` e `templates/src/features/template` reduzidos a placeholders mínimos** — esse conteúdo é vitrine de exemplo, feito para ser substituído pelo consumidor do `npx xertica-ui init`, não para ficar sincronizado com a lib. Removido código morto (`CrudTemplate`, `DashboardTemplate`, `FormTemplate`, `LoginTemplate`, `dashboardStore`, `mock.ts`, `useFeatureCards`) e os locales órfãos correspondentes nos 3 idiomas.
|
|
28
|
+
- Removidos `templates/vite.config.js`/`vite.config.d.ts` — artefatos de build obsoletos e versionados por engano desde a v2.1.0, que faziam o Vite carregar uma versão desatualizada da config (sem os aliases de desenvolvimento do monorepo) em vez do `vite.config.ts` atual.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
14
32
|
## [2.6.0] — 2026-07-06
|
|
15
33
|
|
|
16
34
|
### Security
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Enterprise-grade React design system** built on Tailwind CSS v4, Radix UI, and Lucide Icons — with a robust AI-first documentation layer for precise LLM-driven composition and autonomous agent interaction.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/xertica-ui)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -257,15 +257,14 @@ export function useAssistant({
|
|
|
257
257
|
// Auto-scroll on new messages (condicional ≤ 120px)
|
|
258
258
|
useEffect(() => {
|
|
259
259
|
if (messagesEndRef.current && abaSelecionada === 'chat') {
|
|
260
|
-
const container = messagesEndRef.current.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
260
|
+
const container = messagesEndRef.current.closest<HTMLElement>(
|
|
261
|
+
'[data-radix-scroll-area-viewport]'
|
|
262
|
+
);
|
|
263
|
+
if (!container) return;
|
|
265
264
|
const distanceFromBottom =
|
|
266
265
|
container.scrollHeight - container.scrollTop - container.clientHeight;
|
|
267
266
|
if (distanceFromBottom < 120) {
|
|
268
|
-
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
267
|
+
messagesEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
269
268
|
}
|
|
270
269
|
}
|
|
271
270
|
}, [mensagens, abaSelecionada]);
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ImageWithFallback } from '
|
|
3
|
-
import { LanguageSelector } from '
|
|
2
|
+
import { ImageWithFallback } from '../../figma/ImageWithFallback';
|
|
3
|
+
import { LanguageSelector } from '../../brand/language-selector';
|
|
4
4
|
|
|
5
|
-
interface AuthPageShellProps {
|
|
5
|
+
export interface AuthPageShellProps {
|
|
6
6
|
imageSrc: string;
|
|
7
7
|
imageAlt: string;
|
|
8
8
|
children: React.ReactNode;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Shared two-column shell used by every auth page (Login, ForgotPassword,
|
|
13
|
+
* ResetPassword, VerifyEmail): hero image on the left, centered form panel
|
|
14
|
+
* with a language selector on the right.
|
|
15
|
+
*/
|
|
11
16
|
export function AuthPageShell({ imageSrc, imageAlt, children }: AuthPageShellProps) {
|
|
12
17
|
return (
|
|
13
18
|
<div className="h-screen w-full flex overflow-y-auto">
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import type { PasswordStrength } from './usePasswordStrength';
|
|
4
|
+
|
|
5
|
+
export interface PasswordStrengthMeterProps {
|
|
6
|
+
strength: PasswordStrength;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const BAR_WIDTH: Record<Exclude<PasswordStrength, null>, string> = {
|
|
10
|
+
weak: 'w-1/3 bg-destructive',
|
|
11
|
+
medium: 'w-2/3 bg-[var(--chart-3)]',
|
|
12
|
+
strong: 'w-full bg-[var(--chart-2)]',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const TEXT_COLOR: Record<Exclude<PasswordStrength, null>, string> = {
|
|
16
|
+
weak: 'text-destructive',
|
|
17
|
+
medium: 'text-[var(--chart-3)]',
|
|
18
|
+
strong: 'text-[var(--chart-2)]',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const LABEL_KEY: Record<Exclude<PasswordStrength, null>, string> = {
|
|
22
|
+
weak: 'resetPassword.strengthWeak',
|
|
23
|
+
medium: 'resetPassword.strengthMedium',
|
|
24
|
+
strong: 'resetPassword.strengthStrong',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Progress bar + label shown under the new-password field on reset/create-password screens. */
|
|
28
|
+
export function PasswordStrengthMeter({ strength }: PasswordStrengthMeterProps) {
|
|
29
|
+
const { t } = useTranslation();
|
|
30
|
+
|
|
31
|
+
if (!strength) return null;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="flex items-center gap-2">
|
|
35
|
+
<div className="flex-1 h-1 bg-muted rounded-full overflow-hidden">
|
|
36
|
+
<div className={`h-full transition-all duration-300 ${BAR_WIDTH[strength]}`} />
|
|
37
|
+
</div>
|
|
38
|
+
<span className={`text-small ${TEXT_COLOR[strength]}`}>{t(LABEL_KEY[strength])}</span>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Lock } from 'lucide-react';
|
|
4
|
+
import { Button } from '../../ui/button';
|
|
5
|
+
|
|
6
|
+
export type SocialProvider = 'google' | 'mtLogin' | 'govBr';
|
|
7
|
+
|
|
8
|
+
export interface SocialLoginButtonsProps {
|
|
9
|
+
onSocialLogin: (provider: string) => void;
|
|
10
|
+
/** Which SSO providers to render — projects without gov.br/MT Login can pass a smaller list. */
|
|
11
|
+
providers?: SocialProvider[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DEFAULT_PROVIDERS: SocialProvider[] = ['google', 'mtLogin', 'govBr'];
|
|
15
|
+
|
|
16
|
+
export function SocialLoginButtons({
|
|
17
|
+
onSocialLogin,
|
|
18
|
+
providers = DEFAULT_PROVIDERS,
|
|
19
|
+
}: SocialLoginButtonsProps) {
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
|
|
22
|
+
if (providers.length === 0) return null;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<div className="relative">
|
|
27
|
+
<div className="absolute inset-0 flex items-center">
|
|
28
|
+
<div className="w-full border-t border-border" />
|
|
29
|
+
</div>
|
|
30
|
+
<div className="relative flex justify-center text-sm">
|
|
31
|
+
<span className="bg-muted px-2 text-muted-foreground">{t('login.orContinueWith')}</span>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div className="space-y-3">
|
|
36
|
+
{providers.includes('google') && (
|
|
37
|
+
<Button
|
|
38
|
+
type="button"
|
|
39
|
+
variant="outline"
|
|
40
|
+
className="w-full justify-center"
|
|
41
|
+
onClick={() => onSocialLogin('Google')}
|
|
42
|
+
>
|
|
43
|
+
<svg className="w-5 h-5 mr-2" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
|
44
|
+
<path
|
|
45
|
+
fill="#EA4335"
|
|
46
|
+
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"
|
|
47
|
+
/>
|
|
48
|
+
<path
|
|
49
|
+
fill="#4285F4"
|
|
50
|
+
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"
|
|
51
|
+
/>
|
|
52
|
+
<path
|
|
53
|
+
fill="#FBBC05"
|
|
54
|
+
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"
|
|
55
|
+
/>
|
|
56
|
+
<path
|
|
57
|
+
fill="#34A853"
|
|
58
|
+
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"
|
|
59
|
+
/>
|
|
60
|
+
<path fill="none" d="M0 0h48v48H0z" />
|
|
61
|
+
</svg>
|
|
62
|
+
<span>{t('login.signInWithGoogle')}</span>
|
|
63
|
+
</Button>
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
{providers.includes('mtLogin') && (
|
|
67
|
+
<Button
|
|
68
|
+
type="button"
|
|
69
|
+
variant="outline"
|
|
70
|
+
className="w-full justify-center"
|
|
71
|
+
onClick={() => onSocialLogin('MT Login')}
|
|
72
|
+
>
|
|
73
|
+
<Lock className="w-5 h-5 mr-2 text-[var(--chart-4)]" />
|
|
74
|
+
<span>{t('login.signInWithMTLogin')}</span>
|
|
75
|
+
</Button>
|
|
76
|
+
)}
|
|
77
|
+
|
|
78
|
+
{providers.includes('govBr') && (
|
|
79
|
+
<Button
|
|
80
|
+
type="button"
|
|
81
|
+
variant="outline"
|
|
82
|
+
className="w-full justify-center font-normal"
|
|
83
|
+
onClick={() => onSocialLogin('gov.br')}
|
|
84
|
+
>
|
|
85
|
+
<span>
|
|
86
|
+
{t('login.signInWithGovBr').split('gov.br')[0]}
|
|
87
|
+
<strong className="font-semibold">gov.br</strong>
|
|
88
|
+
{t('login.signInWithGovBr').split('gov.br')[1] || ''}
|
|
89
|
+
</span>
|
|
90
|
+
</Button>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
</>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AuthPageShell } from './AuthPageShell';
|
|
2
|
+
export type { AuthPageShellProps } from './AuthPageShell';
|
|
3
|
+
|
|
4
|
+
export { SocialLoginButtons } from './SocialLoginButtons';
|
|
5
|
+
export type { SocialLoginButtonsProps, SocialProvider } from './SocialLoginButtons';
|
|
6
|
+
|
|
7
|
+
export { usePasswordStrength } from './usePasswordStrength';
|
|
8
|
+
export type { PasswordStrength } from './usePasswordStrength';
|
|
9
|
+
|
|
10
|
+
export { PasswordStrengthMeter } from './PasswordStrengthMeter';
|
|
11
|
+
export type { PasswordStrengthMeterProps } from './PasswordStrengthMeter';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
export type PasswordStrength = 'weak' | 'medium' | 'strong' | null;
|
|
4
|
+
|
|
5
|
+
/** Length-based password strength heuristic shared by every reset/create-password screen. */
|
|
6
|
+
export function usePasswordStrength(password: string): PasswordStrength {
|
|
7
|
+
return useMemo(() => {
|
|
8
|
+
if (password.length === 0) return null;
|
|
9
|
+
if (password.length < 6) return 'weak';
|
|
10
|
+
if (password.length < 10) return 'medium';
|
|
11
|
+
return 'strong';
|
|
12
|
+
}, [password]);
|
|
13
|
+
}
|
|
@@ -3,10 +3,10 @@ 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 {
|
|
8
|
-
import { ArrowLeft, Lock } from 'lucide-react';
|
|
6
|
+
import { AuthPageShell, SocialLoginButtons } from '../../blocks/auth';
|
|
7
|
+
import { ArrowLeft } from 'lucide-react';
|
|
9
8
|
import { useNavigate } from 'react-router-dom';
|
|
9
|
+
import { useTranslation } from 'react-i18next';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Forgot Password Page component.
|
|
@@ -22,6 +22,7 @@ import { useNavigate } from 'react-router-dom';
|
|
|
22
22
|
*/
|
|
23
23
|
export function ForgotPasswordPage() {
|
|
24
24
|
const navigate = useNavigate();
|
|
25
|
+
const { t } = useTranslation();
|
|
25
26
|
const [email, setEmail] = useState('');
|
|
26
27
|
const [isLoading, setIsLoading] = useState(false);
|
|
27
28
|
|
|
@@ -43,146 +44,51 @@ export function ForgotPasswordPage() {
|
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
return (
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{/* Gradient overlay */}
|
|
57
|
-
<div className="absolute inset-0 bg-[image:var(--gradient-diagonal)] opacity-80" />
|
|
47
|
+
<AuthPageShell
|
|
48
|
+
imageSrc="https://images.unsplash.com/photo-1557804506-669a67965ba0?w=1200&h=800&fit=crop&auto=format"
|
|
49
|
+
imageAlt={t('forgotPassword.heroImageAlt')}
|
|
50
|
+
>
|
|
51
|
+
<div className="text-center">
|
|
52
|
+
<div className="flex items-center justify-center mb-4">
|
|
53
|
+
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
54
|
+
</div>
|
|
55
|
+
<h2 className="text-sm text-muted-foreground">{t('forgotPassword.heading')}</h2>
|
|
56
|
+
<p className="mt-2 text-muted-foreground">{t('forgotPassword.subheading')}</p>
|
|
58
57
|
</div>
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
60
|
+
<div className="form-group space-y-2">
|
|
61
|
+
<Label htmlFor="email">{t('forgotPassword.emailLabel')}</Label>
|
|
62
|
+
<Input
|
|
63
|
+
id="email"
|
|
64
|
+
name="email"
|
|
65
|
+
type="email"
|
|
66
|
+
required
|
|
67
|
+
className="w-full"
|
|
68
|
+
placeholder={t('forgotPassword.emailPlaceholder')}
|
|
69
|
+
value={email}
|
|
70
|
+
onChange={e => setEmail(e.target.value)}
|
|
71
|
+
/>
|
|
65
72
|
</div>
|
|
66
73
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Digite seu e-mail e enviaremos as instruções para redefinir sua senha
|
|
82
|
-
</p>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
{/* Form */}
|
|
86
|
-
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
87
|
-
<div className="form-group space-y-2">
|
|
88
|
-
<Label htmlFor="email" className="form-label">
|
|
89
|
-
E-mail
|
|
90
|
-
</Label>
|
|
91
|
-
<Input
|
|
92
|
-
id="email"
|
|
93
|
-
name="email"
|
|
94
|
-
type="email"
|
|
95
|
-
required
|
|
96
|
-
className="w-full"
|
|
97
|
-
placeholder="seu@email.com"
|
|
98
|
-
value={email}
|
|
99
|
-
onChange={e => setEmail(e.target.value)}
|
|
100
|
-
/>
|
|
101
|
-
</div>
|
|
102
|
-
|
|
103
|
-
<div className="space-y-3">
|
|
104
|
-
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
105
|
-
{isLoading ? 'Enviando...' : 'Enviar instruções'}
|
|
106
|
-
</Button>
|
|
107
|
-
|
|
108
|
-
<Button
|
|
109
|
-
type="button"
|
|
110
|
-
onClick={() => navigate('/login')}
|
|
111
|
-
variant="outline"
|
|
112
|
-
className="w-full text-muted-foreground hover:text-foreground"
|
|
113
|
-
>
|
|
114
|
-
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
115
|
-
Voltar para o login
|
|
116
|
-
</Button>
|
|
117
|
-
</div>
|
|
118
|
-
</form>
|
|
119
|
-
|
|
120
|
-
{/* Divider */}
|
|
121
|
-
<div className="relative">
|
|
122
|
-
<div className="absolute inset-0 flex items-center">
|
|
123
|
-
<div className="w-full border-t border-border"></div>
|
|
124
|
-
</div>
|
|
125
|
-
<div className="relative flex justify-center text-sm">
|
|
126
|
-
<span className="bg-muted px-2 text-muted-foreground">ou continue com</span>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
|
|
130
|
-
{/* Social/SSO login options */}
|
|
131
|
-
<div className="space-y-3">
|
|
132
|
-
{/* Google */}
|
|
133
|
-
<Button
|
|
134
|
-
type="button"
|
|
135
|
-
variant="outline"
|
|
136
|
-
className="w-full justify-center"
|
|
137
|
-
onClick={() => handleSocialLogin('Google')}
|
|
138
|
-
>
|
|
139
|
-
<svg className="w-5 h-5 mr-2" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
|
140
|
-
<path
|
|
141
|
-
fill="#EA4335"
|
|
142
|
-
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"
|
|
143
|
-
></path>
|
|
144
|
-
<path
|
|
145
|
-
fill="#4285F4"
|
|
146
|
-
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"
|
|
147
|
-
></path>
|
|
148
|
-
<path
|
|
149
|
-
fill="#FBBC05"
|
|
150
|
-
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"
|
|
151
|
-
></path>
|
|
152
|
-
<path
|
|
153
|
-
fill="#34A853"
|
|
154
|
-
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"
|
|
155
|
-
></path>
|
|
156
|
-
<path fill="none" d="M0 0h48v48H0z"></path>
|
|
157
|
-
</svg>
|
|
158
|
-
<span>Entrar com Google</span>
|
|
159
|
-
</Button>
|
|
160
|
-
|
|
161
|
-
{/* MT Login */}
|
|
162
|
-
<Button
|
|
163
|
-
type="button"
|
|
164
|
-
variant="outline"
|
|
165
|
-
className="w-full justify-center"
|
|
166
|
-
onClick={() => handleSocialLogin('MT Login')}
|
|
167
|
-
>
|
|
168
|
-
<Lock className="w-5 h-5 mr-2 text-[var(--chart-4)]" />
|
|
169
|
-
<span>Entrar com MT Login</span>
|
|
170
|
-
</Button>
|
|
171
|
-
|
|
172
|
-
{/* gov.br */}
|
|
173
|
-
<Button
|
|
174
|
-
type="button"
|
|
175
|
-
variant="outline"
|
|
176
|
-
className="w-full justify-center font-normal"
|
|
177
|
-
onClick={() => handleSocialLogin('gov.br')}
|
|
178
|
-
>
|
|
179
|
-
<span>
|
|
180
|
-
Entrar com <strong className="font-semibold">gov.br</strong>
|
|
181
|
-
</span>
|
|
182
|
-
</Button>
|
|
183
|
-
</div>
|
|
74
|
+
<div className="space-y-3">
|
|
75
|
+
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
76
|
+
{isLoading ? t('forgotPassword.submitting') : t('forgotPassword.submit')}
|
|
77
|
+
</Button>
|
|
78
|
+
|
|
79
|
+
<Button
|
|
80
|
+
type="button"
|
|
81
|
+
onClick={() => navigate('/login')}
|
|
82
|
+
variant="outline"
|
|
83
|
+
className="w-full text-muted-foreground hover:text-foreground"
|
|
84
|
+
>
|
|
85
|
+
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
86
|
+
{t('forgotPassword.backToLogin')}
|
|
87
|
+
</Button>
|
|
184
88
|
</div>
|
|
185
|
-
</
|
|
186
|
-
|
|
89
|
+
</form>
|
|
90
|
+
|
|
91
|
+
<SocialLoginButtons onSocialLogin={handleSocialLogin} />
|
|
92
|
+
</AuthPageShell>
|
|
187
93
|
);
|
|
188
94
|
}
|