xertica-ui 2.3.0 → 2.4.1

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 (105) hide show
  1. package/CHANGELOG.md +22 -2
  2. package/README.md +33 -22
  3. package/bin/cli.ts +136 -47
  4. package/bin/language-config.ts +5 -8
  5. package/components/assistant/modern-chat-input/ModernChatInput.tsx +17 -7
  6. package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +1 -3
  7. package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +13 -3
  8. package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +10 -6
  9. package/components/assistant/xertica-assistant/xertica-assistant.tsx +1 -3
  10. package/components/blocks/card-patterns/FeatureCardSkeleton.tsx +1 -6
  11. package/components/blocks/card-patterns/ProfileCard.tsx +1 -3
  12. package/components/blocks/card-patterns/ProjectCardSkeleton.tsx +1 -6
  13. package/components/brand/language-selector/language-selector.stories.tsx +1 -4
  14. package/components/brand/theme-toggle/ThemeToggle.tsx +5 -1
  15. package/components/brand/xertica-provider/XerticaProvider.tsx +1 -4
  16. package/components/index.ts +1 -5
  17. package/components/layout/sidebar/sidebar.tsx +9 -3
  18. package/components/media/audio-player/AudioPlayer.tsx +4 -2
  19. package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +188 -188
  20. package/components/pages/home-content/HomeContent.tsx +55 -55
  21. package/components/pages/home-page/HomePage.tsx +5 -1
  22. package/components/pages/login-page/LoginPage.tsx +4 -2
  23. package/components/pages/reset-password-page/ResetPasswordPage.tsx +7 -3
  24. package/components/pages/template-content/TemplateContent.tsx +268 -149
  25. package/components/pages/verify-email-page/VerifyEmailPage.tsx +9 -9
  26. package/components/shared/error-boundary.stories.tsx +114 -132
  27. package/components/shared/error-boundary.tsx +150 -154
  28. package/components/shared/error-fallbacks.tsx +222 -226
  29. package/components/ui/stats-card/stats-card-skeleton.tsx +1 -3
  30. package/components/ui/stats-card/stats-card.stories.tsx +18 -0
  31. package/components/ui/stats-card/stats-card.tsx +18 -2
  32. package/components.json +512 -892
  33. package/contexts/AuthContext.tsx +121 -118
  34. package/contexts/LanguageContext.tsx +1 -2
  35. package/dist/AssistantChart-BKVtGUKF.js +3383 -0
  36. package/dist/AssistantChart-WeycT5Pd.cjs +3551 -0
  37. package/dist/VerifyEmailPage-Bp1XXl3H.cjs +3305 -0
  38. package/dist/VerifyEmailPage-DGhuIqkb.js +3296 -0
  39. package/dist/XerticaProvider-BErr83Bg.js +42 -0
  40. package/dist/XerticaProvider-CwOkHxiT.cjs +44 -0
  41. package/dist/XerticaXLogo-BX3ueACh.js +255 -0
  42. package/dist/XerticaXLogo-qBPhwK3g.cjs +260 -0
  43. package/dist/assistant.cjs.js +1 -1
  44. package/dist/assistant.es.js +1 -1
  45. package/dist/brand.cjs.js +2 -2
  46. package/dist/brand.es.js +2 -2
  47. package/dist/cli.js +90 -37
  48. package/dist/components/brand/theme-toggle/ThemeToggle.d.ts +1 -1
  49. package/dist/components/index.d.ts +1 -1
  50. package/dist/components/ui/stats-card/stats-card.d.ts +10 -0
  51. package/dist/index.cjs.js +6 -6
  52. package/dist/index.es.js +6 -6
  53. package/dist/layout.cjs.js +1 -1
  54. package/dist/layout.es.js +1 -1
  55. package/dist/pages.cjs.js +1 -1
  56. package/dist/pages.es.js +1 -1
  57. package/dist/sidebar-B4ZWaMrE.js +792 -0
  58. package/dist/sidebar-BS1p2V7t.cjs +795 -0
  59. package/dist/ui.cjs.js +1 -1
  60. package/dist/ui.es.js +1 -1
  61. package/dist/xertica-assistant-B1NaSFFj.js +2173 -0
  62. package/dist/xertica-assistant-CIaUlbIt.cjs +2180 -0
  63. package/dist/xertica-ui.css +1 -1
  64. package/docs/architecture-improvements.md +5 -5
  65. package/docs/architecture.md +16 -10
  66. package/docs/components/card-patterns.md +19 -17
  67. package/docs/components/error-boundary.md +201 -191
  68. package/docs/components/hooks.md +15 -13
  69. package/docs/components/language-selector.md +20 -16
  70. package/docs/components/pages.md +323 -309
  71. package/docs/components/stats-card.md +20 -2
  72. package/docs/doc-audit.md +12 -11
  73. package/docs/getting-started.md +41 -28
  74. package/docs/guidelines.md +14 -12
  75. package/docs/i18n.md +61 -57
  76. package/docs/installation.md +268 -267
  77. package/docs/llms.md +17 -17
  78. package/docs/state-management.md +17 -17
  79. package/guidelines/Guidelines.md +17 -14
  80. package/llms-compact.txt +1 -1
  81. package/llms-full.txt +11553 -7133
  82. package/llms.txt +1 -1
  83. package/package.json +1 -1
  84. package/styles/xertica/base.css +90 -84
  85. package/templates/CLAUDE.md +16 -1
  86. package/templates/guidelines/Guidelines.md +42 -18
  87. package/templates/package.json +3 -3
  88. package/templates/src/app/components/AuthGuard.tsx +131 -82
  89. package/templates/src/features/auth/ui/AuthPageShell.tsx +32 -32
  90. package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -3
  91. package/templates/src/features/auth/ui/ResetPasswordContent.tsx +6 -2
  92. package/templates/src/features/auth/ui/VerifyEmailContent.tsx +2 -6
  93. package/templates/src/features/home/data/mock.ts +41 -35
  94. package/templates/src/features/home/ui/HomeContent.tsx +62 -64
  95. package/templates/src/features/template/ui/CrudTemplate.tsx +1 -4
  96. package/templates/src/features/template/ui/LoginTemplate.tsx +1 -1
  97. package/templates/src/features/template/ui/TemplateContent.tsx +28 -20
  98. package/templates/src/locales/en/pages/templates.json +17 -17
  99. package/templates/src/locales/es/pages/templates.json +17 -17
  100. package/templates/src/locales/pt-BR/pages/templates.json +17 -17
  101. package/templates/src/pages/AssistantPage.tsx +26 -20
  102. package/templates/src/pages/HomePage.tsx +5 -1
  103. package/templates/src/shared/error-boundary.tsx +150 -154
  104. package/templates/src/shared/error-fallbacks.tsx +222 -226
  105. package/templates/vite.config.ts +12 -9
@@ -394,9 +394,7 @@ export function AssistantMessageBubble({
394
394
  {/* Search Commands */}
395
395
  {msg.searchCommands && msg.searchResults && (
396
396
  <div>
397
- <h4 className="mb-2 text-foreground">
398
- {t('assistant.searchActions')}
399
- </h4>
397
+ <h4 className="mb-2 text-foreground">{t('assistant.searchActions')}</h4>
400
398
  <div className="grid grid-cols-1 gap-2">
401
399
  {msg.searchCommands.map(command => (
402
400
  <button
@@ -518,17 +516,23 @@ export function AssistantMessageBubble({
518
516
  ) : (
519
517
  <>
520
518
  <DropdownMenuItem
521
- onClick={() => onOpenFeedbackDialog(msg.id, t('assistant.feedback.notWhatIWanted'))}
519
+ onClick={() =>
520
+ onOpenFeedbackDialog(msg.id, t('assistant.feedback.notWhatIWanted'))
521
+ }
522
522
  >
523
523
  {t('assistant.feedback.notWhatIWanted')}
524
524
  </DropdownMenuItem>
525
525
  <DropdownMenuItem
526
- onClick={() => onOpenFeedbackDialog(msg.id, t('assistant.feedback.incorrectInfo'))}
526
+ onClick={() =>
527
+ onOpenFeedbackDialog(msg.id, t('assistant.feedback.incorrectInfo'))
528
+ }
527
529
  >
528
530
  {t('assistant.feedback.incorrectInfo')}
529
531
  </DropdownMenuItem>
530
532
  <DropdownMenuItem
531
- onClick={() => onOpenFeedbackDialog(msg.id, t('assistant.feedback.incompleteAnswer'))}
533
+ onClick={() =>
534
+ onOpenFeedbackDialog(msg.id, t('assistant.feedback.incompleteAnswer'))
535
+ }
532
536
  >
533
537
  {t('assistant.feedback.incompleteAnswer')}
534
538
  </DropdownMenuItem>
@@ -389,9 +389,7 @@ export function XerticaAssistant({
389
389
  // Render
390
390
  // ============================================================================
391
391
 
392
- const containerWidth =
393
- width ??
394
- (isFullPage ? '100%' : isExpanded ? '420px' : '80px'); // Compacto quando fechado - apenas ícones
392
+ const containerWidth = width ?? (isFullPage ? '100%' : isExpanded ? '420px' : '80px'); // Compacto quando fechado - apenas ícones
395
393
  const containerHeight = height ?? 'h-full';
396
394
 
397
395
  // Mobile floating button — shown when collapsed on mobile
@@ -1,12 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import * as React from 'react';
4
- import {
5
- Card,
6
- CardHeader,
7
- CardContent,
8
- CardFooter,
9
- } from '../../ui/card';
4
+ import { Card, CardHeader, CardContent, CardFooter } from '../../ui/card';
10
5
  import { Skeleton } from '../../ui/skeleton';
11
6
  import { cn } from '../../shared/utils';
12
7
 
@@ -51,9 +51,7 @@ export function ProfileCard({
51
51
  }),
52
52
  [t]
53
53
  );
54
- const s = status
55
- ? { label: statusLabelMap[status], variant: statusVariant[status] }
56
- : null;
54
+ const s = status ? { label: statusLabelMap[status], variant: statusVariant[status] } : null;
57
55
 
58
56
  return (
59
57
  <Card className={cn('w-full', className)} {...props}>
@@ -1,12 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import * as React from 'react';
4
- import {
5
- Card,
6
- CardHeader,
7
- CardContent,
8
- CardFooter,
9
- } from '../../ui/card';
4
+ import { Card, CardHeader, CardContent, CardFooter } from '../../ui/card';
10
5
  import { Skeleton } from '../../ui/skeleton';
11
6
  import { cn } from '../../shared/utils';
12
7
 
@@ -75,10 +75,7 @@ export const Minimal: Story = {
75
75
  export const ExtendedWithFrench: Story = {
76
76
  name: 'Extended (with custom language)',
77
77
  decorators: [
78
- withLanguages([
79
- ...DEFAULT_LANGUAGES,
80
- { code: 'fr', label: 'Français', shortLabel: 'FR' },
81
- ]),
78
+ withLanguages([...DEFAULT_LANGUAGES, { code: 'fr', label: 'Français', shortLabel: 'FR' }]),
82
79
  ],
83
80
  parameters: {
84
81
  docs: {
@@ -17,9 +17,13 @@ export function ThemeToggle({
17
17
  className = '',
18
18
  showLabel = false,
19
19
  }: ThemeToggleProps) {
20
- const { isDark, toggleTheme } = useTheme();
20
+ const { isDark, toggleTheme, disableDarkMode } = useTheme();
21
21
  const { t } = useTranslation();
22
22
 
23
+ if (disableDarkMode) {
24
+ return null;
25
+ }
26
+
23
27
  const getSizeClasses = () => {
24
28
  switch (size) {
25
29
  case 'sm':
@@ -85,10 +85,7 @@ export function XerticaProvider({
85
85
  primaryColor={primaryColor}
86
86
  useCustomTokens={useCustomTokens}
87
87
  >
88
- <LanguageProvider
89
- availableLanguages={availableLanguages}
90
- defaultLanguage={defaultLanguage}
91
- >
88
+ <LanguageProvider availableLanguages={availableLanguages} defaultLanguage={defaultLanguage}>
92
89
  <ApiKeyProvider
93
90
  initialApiKey={apiKey}
94
91
  initialGeminiApiKey={apiKey}
@@ -64,11 +64,7 @@ export { LanguageSelector } from './brand/language-selector';
64
64
  export * from '../contexts/LayoutContext';
65
65
 
66
66
  // Language / i18n
67
- export {
68
- useLanguage,
69
- LanguageProvider,
70
- DEFAULT_LANGUAGES,
71
- } from '../contexts/LanguageContext';
67
+ export { useLanguage, LanguageProvider, DEFAULT_LANGUAGES } from '../contexts/LanguageContext';
72
68
  export type {
73
69
  Language,
74
70
  LanguageDefinition,
@@ -709,7 +709,9 @@ function SidebarNav({
709
709
  >
710
710
  <MoreVertical className="w-5 h-5 flex-shrink-0" />
711
711
  {expanded && (
712
- <span className="truncate text-sidebar-foreground">{t('sidebar.moreOptions')}</span>
712
+ <span className="truncate text-sidebar-foreground">
713
+ {t('sidebar.moreOptions')}
714
+ </span>
713
715
  )}
714
716
  </button>
715
717
  </PopoverTrigger>
@@ -830,7 +832,9 @@ function SidebarSearch({
830
832
  )}
831
833
  </button>
832
834
  </TooltipTrigger>
833
- <SidebarTooltipContent side="right">{t('assistant.newConversation')}</SidebarTooltipContent>
835
+ <SidebarTooltipContent side="right">
836
+ {t('assistant.newConversation')}
837
+ </SidebarTooltipContent>
834
838
  </Tooltip>
835
839
  )}
836
840
  {search?.show && (
@@ -915,7 +919,9 @@ function SidebarFooter({
915
919
  {user?.name ? user.name.charAt(0).toUpperCase() : 'U'}
916
920
  </AvatarFallback>
917
921
  </Avatar>
918
- <span className="text-sidebar-foreground truncate">{user?.name || t('sidebar.profile')}</span>
922
+ <span className="text-sidebar-foreground truncate">
923
+ {user?.name || t('sidebar.profile')}
924
+ </span>
919
925
  </button>
920
926
  ))}
921
927
 
@@ -395,7 +395,8 @@ export function AudioPlayer({
395
395
 
396
396
  <div className="md:hidden">
397
397
  <DropdownMenuItem onClick={resetAudio} className="cursor-pointer py-2.5">
398
- <RotateCcw className="w-4 h-4 mr-3 text-muted-foreground" /> {t('media.restart')}
398
+ <RotateCcw className="w-4 h-4 mr-3 text-muted-foreground" />{' '}
399
+ {t('media.restart')}
399
400
  </DropdownMenuItem>
400
401
  <DropdownMenuItem className="cursor-pointer py-2.5">
401
402
  <Info className="w-4 h-4 mr-3 text-muted-foreground" /> {t('media.info')}
@@ -428,7 +429,8 @@ export function AudioPlayer({
428
429
  </div>
429
430
  <DropdownMenuSeparator className="my-1" />
430
431
  <DropdownMenuItem className="cursor-pointer py-2.5">
431
- <Download className="w-4 h-4 mr-3 text-muted-foreground" /> {t('media.downloadAudio')}
432
+ <Download className="w-4 h-4 mr-3 text-muted-foreground" />{' '}
433
+ {t('media.downloadAudio')}
432
434
  </DropdownMenuItem>
433
435
  </div>
434
436
  </DropdownMenuContent>
@@ -1,188 +1,188 @@
1
- import React, { useState } from 'react';
2
- import { Button } from '../../ui/button';
3
- import { Input } from '../../ui/input';
4
- import { Label } from '../../ui/label';
5
- import { XerticaLogo } from '../../brand/xertica-logo';
6
- import { ImageWithFallback } from '../../figma/ImageWithFallback';
7
- import { LanguageSelector } from '../../brand/language-selector';
8
- import { ArrowLeft, Lock } from 'lucide-react';
9
- import { useNavigate } from 'react-router-dom';
10
-
11
- /**
12
- * Forgot Password Page component.
13
- *
14
- * @description
15
- * A screen that allows users to request a password reset link via email.
16
- * Includes a hero image side-panel (desktop) and alternative social login
17
- * options similar to the main Login page.
18
- *
19
- * @ai-rules
20
- * 1. Navigation: Successfully submitting the email triggers a navigation to `/verify-email`.
21
- * 2. Visuals: Keeps consistency with `LoginPage` in terms of layout and branding.
22
- */
23
- export function ForgotPasswordPage() {
24
- const navigate = useNavigate();
25
- const [email, setEmail] = useState('');
26
- const [isLoading, setIsLoading] = useState(false);
27
-
28
- const handleSubmit = async (e: React.FormEvent) => {
29
- e.preventDefault();
30
- setIsLoading(true);
31
-
32
- // Simulate email sending
33
- await new Promise(resolve => setTimeout(resolve, 1500));
34
-
35
- // Navigate to verification screen
36
- navigate('/verify-email', { state: { email } });
37
-
38
- setIsLoading(false);
39
- };
40
-
41
- const handleSocialLogin = (_provider: string) => {
42
- // Wire your SSO/social provider integration here
43
- };
44
-
45
- return (
46
- <div className="min-h-screen flex">
47
- {/* Left side - Full background image */}
48
- <div className="hidden lg:flex lg:flex-1 relative overflow-hidden">
49
- {/* Background image filling all space */}
50
- <ImageWithFallback
51
- src="https://images.unsplash.com/photo-1557804506-669a67965ba0?w=1200&h=800&fit=crop&auto=format"
52
- alt="Segurança e tecnologia"
53
- className="absolute inset-0 w-full h-full object-cover"
54
- />
55
-
56
- {/* Gradient overlay */}
57
- <div className="absolute inset-0 bg-[image:var(--gradient-diagonal)] opacity-80" />
58
- </div>
59
-
60
- {/* Right side - Form */}
61
- <div className="flex-1 flex items-center justify-center px-4 sm:px-6 lg:px-8 lg:flex-none lg:w-1/2 relative bg-muted">
62
- {/* Language selector in the top right corner */}
63
- <div className="absolute top-4 right-4 z-20">
64
- <LanguageSelector variant="minimal" showIcon={false} />
65
- </div>
66
-
67
- {/* Mobile background gradient */}
68
- <div className="absolute inset-0 lg:hidden bg-[image:var(--gradient-diagonal)] opacity-10 dark:opacity-5" />
69
-
70
- <div className="w-full max-w-sm space-y-6 relative z-10">
71
- {/* Form header */}
72
- <div className="text-center">
73
- <div className="flex items-center justify-center mb-4">
74
- <XerticaLogo
75
- className="h-12 w-auto text-primary dark:text-foreground"
76
- variant="theme"
77
- />
78
- </div>
79
- <h2 className="text-sm text-muted-foreground">Recuperar senha</h2>
80
- <p className="mt-2 text-muted-foreground">
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>
184
- </div>
185
- </div>
186
- </div>
187
- );
188
- }
1
+ import React, { useState } from 'react';
2
+ import { Button } from '../../ui/button';
3
+ import { Input } from '../../ui/input';
4
+ import { Label } from '../../ui/label';
5
+ import { XerticaLogo } from '../../brand/xertica-logo';
6
+ import { ImageWithFallback } from '../../figma/ImageWithFallback';
7
+ import { LanguageSelector } from '../../brand/language-selector';
8
+ import { ArrowLeft, Lock } from 'lucide-react';
9
+ import { useNavigate } from 'react-router-dom';
10
+
11
+ /**
12
+ * Forgot Password Page component.
13
+ *
14
+ * @description
15
+ * A screen that allows users to request a password reset link via email.
16
+ * Includes a hero image side-panel (desktop) and alternative social login
17
+ * options similar to the main Login page.
18
+ *
19
+ * @ai-rules
20
+ * 1. Navigation: Successfully submitting the email triggers a navigation to `/verify-email`.
21
+ * 2. Visuals: Keeps consistency with `LoginPage` in terms of layout and branding.
22
+ */
23
+ export function ForgotPasswordPage() {
24
+ const navigate = useNavigate();
25
+ const [email, setEmail] = useState('');
26
+ const [isLoading, setIsLoading] = useState(false);
27
+
28
+ const handleSubmit = async (e: React.FormEvent) => {
29
+ e.preventDefault();
30
+ setIsLoading(true);
31
+
32
+ // Simulate email sending
33
+ await new Promise(resolve => setTimeout(resolve, 1500));
34
+
35
+ // Navigate to verification screen
36
+ navigate('/verify-email', { state: { email } });
37
+
38
+ setIsLoading(false);
39
+ };
40
+
41
+ const handleSocialLogin = (_provider: string) => {
42
+ // Wire your SSO/social provider integration here
43
+ };
44
+
45
+ return (
46
+ <div className="h-screen w-full flex overflow-y-auto">
47
+ {/* Left side - Full background image */}
48
+ <div className="hidden lg:flex lg:flex-1 relative overflow-hidden">
49
+ {/* Background image filling all space */}
50
+ <ImageWithFallback
51
+ src="https://images.unsplash.com/photo-1557804506-669a67965ba0?w=1200&h=800&fit=crop&auto=format"
52
+ alt="Segurança e tecnologia"
53
+ className="absolute inset-0 w-full h-full object-cover"
54
+ />
55
+
56
+ {/* Gradient overlay */}
57
+ <div className="absolute inset-0 bg-[image:var(--gradient-diagonal)] opacity-80" />
58
+ </div>
59
+
60
+ {/* Right side - Form */}
61
+ <div className="flex-1 flex items-center justify-center px-4 sm:px-6 lg:px-8 lg:flex-none lg:w-1/2 relative bg-muted">
62
+ {/* Language selector in the top right corner */}
63
+ <div className="absolute top-4 right-4 z-20">
64
+ <LanguageSelector variant="minimal" showIcon={false} />
65
+ </div>
66
+
67
+ {/* Mobile background gradient */}
68
+ <div className="absolute inset-0 lg:hidden bg-[image:var(--gradient-diagonal)] opacity-10 dark:opacity-5" />
69
+
70
+ <div className="w-full max-w-sm space-y-6 relative z-10">
71
+ {/* Form header */}
72
+ <div className="text-center">
73
+ <div className="flex items-center justify-center mb-4">
74
+ <XerticaLogo
75
+ className="h-12 w-auto text-primary dark:text-foreground"
76
+ variant="theme"
77
+ />
78
+ </div>
79
+ <h2 className="text-sm text-muted-foreground">Recuperar senha</h2>
80
+ <p className="mt-2 text-muted-foreground">
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>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ );
188
+ }