xertica-ui 2.9.8 → 2.9.9
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 +8 -0
- package/README.md +1 -1
- package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +19 -5
- package/components/pages/login-page/LoginPage.tsx +19 -5
- package/components/pages/reset-password-page/ResetPasswordPage.tsx +15 -2
- package/components/pages/verify-email-page/VerifyEmailPage.tsx +17 -3
- package/dist/AssistenteContext-Bm8_RJTB.js +200 -0
- package/dist/AssistenteContext-CLK8tgdt.cjs +205 -0
- package/dist/BrandColorsContext-D3Y0CYZF.js +643 -0
- package/dist/BrandColorsContext-SeOZd2Cq.cjs +646 -0
- package/dist/VerifyEmailPage-BInYcjRd.cjs +3305 -0
- package/dist/VerifyEmailPage-Z-cA_IIo.js +3295 -0
- package/dist/XerticaProvider-Ck996htf.cjs +47 -0
- package/dist/XerticaProvider-kE5l0MQn.js +45 -0
- package/dist/brand.cjs.js +1 -1
- package/dist/brand.es.js +1 -1
- package/dist/components/pages/forgot-password-page/ForgotPasswordPage.d.ts +4 -3
- package/dist/components/pages/login-page/LoginPage.d.ts +4 -3
- package/dist/components/pages/verify-email-page/VerifyEmailPage.d.ts +2 -1
- package/dist/hooks.cjs.js +6 -5
- package/dist/hooks.es.js +2 -1
- package/dist/index.cjs.js +2 -2
- package/dist/index.es.js +2 -2
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/package.json +1 -1
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +15 -2
- package/templates/src/features/auth/ui/LoginContent.tsx +15 -2
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +15 -2
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +15 -2
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,14 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [2.9.9] — 2026-07-14
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Isotipo da marca e a inversão de cores apareciam nas telas de auth em qualquer tema, não só no "Xertica"** — os commits que introduziram o isotipo (`31e3bb51`, `7f047240`) trocaram o `leftPanel` do `AuthPageShell` para `"isotype"` de forma incondicional nas 4 páginas de autenticação (`LoginPage`, `ForgotPasswordPage`, `ResetPasswordPage`, `VerifyEmailPage`), então o painel esquerdo sempre exibia a marca e a inversão de cores (`bg-card`/`bg-muted`) associada a ela, mesmo com qualquer outro tema de cor ativo — inclusive o `xertica-original`, o tema clássico padrão. Corrigido lendo `currentTheme` via `useBrandColors()` em cada página e só usando `leftPanel="isotype"` quando `currentTheme === 'xertica'`; para os demais temas, o painel volta a exibir a imagem de capa e o gradiente originais (recuperados do histórico do git, anteriores ao rebranding). Aplicado tanto nos componentes reais (`components/pages/*-page`) quanto nos templates espelho usados pelo CLI (`templates/src/features/auth/ui/*Content.tsx`).
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
14
22
|
## [2.9.8] — 2026-07-14
|
|
15
23
|
|
|
16
24
|
### Fixed
|
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
|
---
|
|
@@ -7,22 +7,25 @@ import { AuthPageShell, SocialLoginButtons } from '../../blocks/auth';
|
|
|
7
7
|
import { ArrowLeft } from 'lucide-react';
|
|
8
8
|
import { useNavigate } from 'react-router-dom';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
|
+
import { useBrandColors } from '../../../contexts/BrandColorsContext';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Forgot Password Page component.
|
|
13
14
|
*
|
|
14
15
|
* @description
|
|
15
16
|
* A screen that allows users to request a password reset link via email.
|
|
16
|
-
* Includes
|
|
17
|
-
*
|
|
17
|
+
* Includes a side-panel (desktop) and alternative social login options
|
|
18
|
+
* similar to the main Login page.
|
|
18
19
|
*
|
|
19
20
|
* @ai-rules
|
|
20
21
|
* 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
|
+
* 2. Visuals: Keeps consistency with `LoginPage` in terms of layout and branding, including
|
|
23
|
+
* the theme-conditional isotype/hero-image left panel.
|
|
22
24
|
*/
|
|
23
25
|
export function ForgotPasswordPage() {
|
|
24
26
|
const navigate = useNavigate();
|
|
25
27
|
const { t } = useTranslation();
|
|
28
|
+
const { currentTheme } = useBrandColors();
|
|
26
29
|
const [email, setEmail] = useState('');
|
|
27
30
|
const [isLoading, setIsLoading] = useState(false);
|
|
28
31
|
|
|
@@ -43,8 +46,8 @@ export function ForgotPasswordPage() {
|
|
|
43
46
|
// Wire your SSO/social provider integration here
|
|
44
47
|
};
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const content = (
|
|
50
|
+
<>
|
|
48
51
|
<div className="text-center">
|
|
49
52
|
<div className="flex items-center justify-center mb-4">
|
|
50
53
|
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
@@ -86,6 +89,17 @@ export function ForgotPasswordPage() {
|
|
|
86
89
|
</form>
|
|
87
90
|
|
|
88
91
|
<SocialLoginButtons onSocialLogin={handleSocialLogin} />
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return currentTheme === 'xertica' ? (
|
|
96
|
+
<AuthPageShell leftPanel="isotype">{content}</AuthPageShell>
|
|
97
|
+
) : (
|
|
98
|
+
<AuthPageShell
|
|
99
|
+
imageSrc="https://images.unsplash.com/photo-1557804506-669a67965ba0?w=1200&h=800&fit=crop&auto=format"
|
|
100
|
+
imageAlt={t('forgotPassword.heroImageAlt')}
|
|
101
|
+
>
|
|
102
|
+
{content}
|
|
89
103
|
</AuthPageShell>
|
|
90
104
|
);
|
|
91
105
|
}
|
|
@@ -6,6 +6,7 @@ import { XerticaLogo } from '../../brand/xertica-logo';
|
|
|
6
6
|
import { AuthPageShell, SocialLoginButtons } from '../../blocks/auth';
|
|
7
7
|
import { useNavigate } from 'react-router-dom';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { useBrandColors } from '../../../contexts/BrandColorsContext';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Props for the LoginPage component.
|
|
@@ -22,18 +23,20 @@ interface LoginPageProps {
|
|
|
22
23
|
* Standard Login Page component.
|
|
23
24
|
*
|
|
24
25
|
* @description
|
|
25
|
-
* A complete, responsive login screen featuring a
|
|
26
|
-
*
|
|
26
|
+
* A complete, responsive login screen featuring a side-panel (on desktop) and a
|
|
27
|
+
* focused authentication form. Supports email/password login and placeholders
|
|
27
28
|
* for Social/SSO providers (Google, MT Login, gov.br).
|
|
28
29
|
*
|
|
29
30
|
* @ai-rules
|
|
30
31
|
* 1. State Management: You must provide the `onLogin` callback to handle the actual authentication logic.
|
|
31
|
-
* 2. Visuals: Left panel renders
|
|
32
|
+
* 2. Visuals: Left panel renders the brand isotype only when the `xertica` theme is active
|
|
33
|
+
* (`AuthPageShell`'s `leftPanel="isotype"`); every other theme shows the original hero image.
|
|
32
34
|
* 3. Routing: Uses `react-router` for navigation to the forgot-password flow.
|
|
33
35
|
*/
|
|
34
36
|
export function LoginPage({ onLogin }: LoginPageProps) {
|
|
35
37
|
const navigate = useNavigate();
|
|
36
38
|
const { t } = useTranslation();
|
|
39
|
+
const { currentTheme } = useBrandColors();
|
|
37
40
|
const [email, setEmail] = useState('');
|
|
38
41
|
const [password, setPassword] = useState('');
|
|
39
42
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -61,8 +64,8 @@ export function LoginPage({ onLogin }: LoginPageProps) {
|
|
|
61
64
|
onLogin('social@user.com', 'social-auth');
|
|
62
65
|
};
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
const content = (
|
|
68
|
+
<>
|
|
66
69
|
<div className="text-center">
|
|
67
70
|
<div className="flex items-center justify-center mb-4">
|
|
68
71
|
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
@@ -117,6 +120,17 @@ export function LoginPage({ onLogin }: LoginPageProps) {
|
|
|
117
120
|
</form>
|
|
118
121
|
|
|
119
122
|
<SocialLoginButtons onSocialLogin={handleSocialLogin} />
|
|
123
|
+
</>
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return currentTheme === 'xertica' ? (
|
|
127
|
+
<AuthPageShell leftPanel="isotype">{content}</AuthPageShell>
|
|
128
|
+
) : (
|
|
129
|
+
<AuthPageShell
|
|
130
|
+
imageSrc="https://images.unsplash.com/photo-1551434678-e076c223a692?w=1200&h=800&fit=crop&auto=format"
|
|
131
|
+
imageAlt={t('login.heroImageAlt')}
|
|
132
|
+
>
|
|
133
|
+
{content}
|
|
120
134
|
</AuthPageShell>
|
|
121
135
|
);
|
|
122
136
|
}
|
|
@@ -7,6 +7,7 @@ import { AuthPageShell, usePasswordStrength, PasswordStrengthMeter } from '../..
|
|
|
7
7
|
import { ArrowLeft, CheckCircle2, AlertCircle } from 'lucide-react';
|
|
8
8
|
import { useNavigate } from 'react-router-dom';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
|
+
import { useBrandColors } from '../../../contexts/BrandColorsContext';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Reset Password Page component.
|
|
@@ -24,6 +25,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
24
25
|
export function ResetPasswordPage() {
|
|
25
26
|
const navigate = useNavigate();
|
|
26
27
|
const { t } = useTranslation();
|
|
28
|
+
const { currentTheme } = useBrandColors();
|
|
27
29
|
const [password, setPassword] = useState('');
|
|
28
30
|
const [confirmPassword, setConfirmPassword] = useState('');
|
|
29
31
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -61,8 +63,8 @@ export function ResetPasswordPage() {
|
|
|
61
63
|
setIsLoading(false);
|
|
62
64
|
};
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
const content = (
|
|
67
|
+
<>
|
|
66
68
|
<button
|
|
67
69
|
onClick={() => navigate('/login')}
|
|
68
70
|
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
@@ -145,6 +147,17 @@ export function ResetPasswordPage() {
|
|
|
145
147
|
{isLoading ? t('resetPassword.submitting') : t('resetPassword.submit')}
|
|
146
148
|
</Button>
|
|
147
149
|
</form>
|
|
150
|
+
</>
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return currentTheme === 'xertica' ? (
|
|
154
|
+
<AuthPageShell leftPanel="isotype">{content}</AuthPageShell>
|
|
155
|
+
) : (
|
|
156
|
+
<AuthPageShell
|
|
157
|
+
imageSrc="https://images.unsplash.com/photo-1555949963-aa79dcee981c?w=1200&h=800&fit=crop&auto=format"
|
|
158
|
+
imageAlt={t('resetPassword.heroImageAlt')}
|
|
159
|
+
>
|
|
160
|
+
{content}
|
|
148
161
|
</AuthPageShell>
|
|
149
162
|
);
|
|
150
163
|
}
|
|
@@ -5,6 +5,7 @@ import { AuthPageShell, SocialLoginButtons } from '../../blocks/auth';
|
|
|
5
5
|
import { ArrowLeft, Mail, CheckCircle2 } from 'lucide-react';
|
|
6
6
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { useBrandColors } from '../../../contexts/BrandColorsContext';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Email Verification Page component.
|
|
@@ -12,7 +13,8 @@ import { useTranslation } from 'react-i18next';
|
|
|
12
13
|
* @description
|
|
13
14
|
* An informative screen shown after a password reset request. It instructs
|
|
14
15
|
* the user to check their email and provides a "Resend email" feature.
|
|
15
|
-
* Includes the same consistent layout
|
|
16
|
+
* Includes the same consistent layout as the other auth pages, including the
|
|
17
|
+
* theme-conditional isotype/hero-image left panel.
|
|
16
18
|
*
|
|
17
19
|
* @ai-rules
|
|
18
20
|
* 1. Data Source: It retrieves the email from the router's `location.state`.
|
|
@@ -23,6 +25,7 @@ export function VerifyEmailPage() {
|
|
|
23
25
|
const navigate = useNavigate();
|
|
24
26
|
const location = useLocation();
|
|
25
27
|
const { t } = useTranslation();
|
|
28
|
+
const { currentTheme } = useBrandColors();
|
|
26
29
|
const email = location.state?.email || 'your@email.com';
|
|
27
30
|
const [isResending, setIsResending] = useState(false);
|
|
28
31
|
const [resendSuccess, setResendSuccess] = useState(false);
|
|
@@ -45,8 +48,8 @@ export function VerifyEmailPage() {
|
|
|
45
48
|
// Wire your SSO/social provider integration here
|
|
46
49
|
};
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
const content = (
|
|
52
|
+
<>
|
|
50
53
|
<div className="text-center">
|
|
51
54
|
<div className="flex items-center justify-center mb-4">
|
|
52
55
|
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
@@ -96,6 +99,17 @@ export function VerifyEmailPage() {
|
|
|
96
99
|
</div>
|
|
97
100
|
|
|
98
101
|
<SocialLoginButtons onSocialLogin={handleSocialLogin} />
|
|
102
|
+
</>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return currentTheme === 'xertica' ? (
|
|
106
|
+
<AuthPageShell leftPanel="isotype">{content}</AuthPageShell>
|
|
107
|
+
) : (
|
|
108
|
+
<AuthPageShell
|
|
109
|
+
imageSrc="https://images.unsplash.com/photo-1563986768609-322da13575f3?w=1200&h=800&fit=crop&auto=format"
|
|
110
|
+
imageAlt={t('verifyEmail.heroImageAlt')}
|
|
111
|
+
>
|
|
112
|
+
{content}
|
|
99
113
|
</AuthPageShell>
|
|
100
114
|
);
|
|
101
115
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { createContext, useState, useEffect, useContext } from 'react';
|
|
3
|
+
|
|
4
|
+
const ApiKeyContext = createContext(void 0);
|
|
5
|
+
const readStorage = (key) => typeof window !== "undefined" ? window.localStorage?.getItem(key) : null;
|
|
6
|
+
const writeStorage = (key, value) => {
|
|
7
|
+
if (typeof window === "undefined") return;
|
|
8
|
+
window.localStorage?.setItem(key, value);
|
|
9
|
+
};
|
|
10
|
+
const removeStorage = (key) => {
|
|
11
|
+
if (typeof window === "undefined") return;
|
|
12
|
+
window.localStorage?.removeItem(key);
|
|
13
|
+
};
|
|
14
|
+
function ApiKeyProvider({
|
|
15
|
+
children,
|
|
16
|
+
initialApiKey,
|
|
17
|
+
initialGeminiApiKey,
|
|
18
|
+
initialGoogleMapsApiKey
|
|
19
|
+
}) {
|
|
20
|
+
const LEAKED_KEYS = [
|
|
21
|
+
"AIzaSyCMsAMytBeOK0Qd7RKFyA5IW9eWt2WTJg",
|
|
22
|
+
"AIzaSyAiYWEIEmx212Up9zfM8kqyMXB4jLs8gq0"
|
|
23
|
+
];
|
|
24
|
+
const [apiKey, setApiKeyState] = useState(() => {
|
|
25
|
+
if (initialApiKey) return initialApiKey;
|
|
26
|
+
const saved = readStorage("xertica-api-key");
|
|
27
|
+
return saved || "";
|
|
28
|
+
});
|
|
29
|
+
const [geminiApiKey, setGeminiApiKeyState] = useState(() => {
|
|
30
|
+
if (initialGeminiApiKey) return initialGeminiApiKey;
|
|
31
|
+
const saved = readStorage("xertica-gemini-api-key");
|
|
32
|
+
if (saved && LEAKED_KEYS.includes(saved)) {
|
|
33
|
+
removeStorage("xertica-gemini-api-key");
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
return saved || "";
|
|
37
|
+
});
|
|
38
|
+
const [googleMapsApiKey, setGoogleMapsApiKeyState] = useState(() => {
|
|
39
|
+
if (initialGoogleMapsApiKey) return initialGoogleMapsApiKey;
|
|
40
|
+
const saved = readStorage("xertica-googlemaps-api-key");
|
|
41
|
+
return saved || "";
|
|
42
|
+
});
|
|
43
|
+
const [isApiKeyValid, setIsApiKeyValid] = useState(false);
|
|
44
|
+
const [isGoogleMapsKeyValid, setIsGoogleMapsKeyValid] = useState(false);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (initialApiKey !== void 0) {
|
|
47
|
+
setApiKeyState(initialApiKey.trim());
|
|
48
|
+
}
|
|
49
|
+
}, [initialApiKey]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (initialGeminiApiKey !== void 0) {
|
|
52
|
+
setGeminiApiKeyState(initialGeminiApiKey.trim());
|
|
53
|
+
}
|
|
54
|
+
}, [initialGeminiApiKey]);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (initialGoogleMapsApiKey !== void 0) {
|
|
57
|
+
setGoogleMapsApiKeyState(initialGoogleMapsApiKey.trim());
|
|
58
|
+
}
|
|
59
|
+
}, [initialGoogleMapsApiKey]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (apiKey) {
|
|
62
|
+
writeStorage("xertica-api-key", apiKey);
|
|
63
|
+
setIsApiKeyValid(apiKey.length > 0);
|
|
64
|
+
} else {
|
|
65
|
+
removeStorage("xertica-api-key");
|
|
66
|
+
setIsApiKeyValid(false);
|
|
67
|
+
}
|
|
68
|
+
}, [apiKey]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (geminiApiKey) {
|
|
71
|
+
writeStorage("xertica-gemini-api-key", geminiApiKey);
|
|
72
|
+
setIsApiKeyValid(geminiApiKey.startsWith("AIzaSy") && geminiApiKey.length > 20);
|
|
73
|
+
} else {
|
|
74
|
+
removeStorage("xertica-gemini-api-key");
|
|
75
|
+
setIsApiKeyValid(false);
|
|
76
|
+
}
|
|
77
|
+
}, [geminiApiKey]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (googleMapsApiKey) {
|
|
80
|
+
writeStorage("xertica-googlemaps-api-key", googleMapsApiKey);
|
|
81
|
+
setIsGoogleMapsKeyValid(
|
|
82
|
+
googleMapsApiKey.startsWith("AIzaSy") && googleMapsApiKey.length > 20
|
|
83
|
+
);
|
|
84
|
+
const reloadMaps = async () => {
|
|
85
|
+
if (googleMapsApiKey.length < 10) return;
|
|
86
|
+
try {
|
|
87
|
+
const { reloadGoogleMaps } = await import('./index-Bei5DzAL.js');
|
|
88
|
+
await reloadGoogleMaps(googleMapsApiKey);
|
|
89
|
+
console.log("[ApiKeyContext] Google Maps recarregado com sucesso");
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("[ApiKeyContext] Erro ao recarregar Google Maps:", error);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
reloadMaps();
|
|
95
|
+
} else {
|
|
96
|
+
removeStorage("xertica-googlemaps-api-key");
|
|
97
|
+
setIsGoogleMapsKeyValid(false);
|
|
98
|
+
}
|
|
99
|
+
}, [googleMapsApiKey]);
|
|
100
|
+
const setApiKey = (key) => {
|
|
101
|
+
setApiKeyState(key.trim());
|
|
102
|
+
};
|
|
103
|
+
const setGeminiApiKey = (key) => {
|
|
104
|
+
setGeminiApiKeyState(key.trim());
|
|
105
|
+
};
|
|
106
|
+
const setGoogleMapsApiKey = (key) => {
|
|
107
|
+
setGoogleMapsApiKeyState(key.trim());
|
|
108
|
+
};
|
|
109
|
+
const reloadMapsApi = async () => {
|
|
110
|
+
if (!googleMapsApiKey || googleMapsApiKey.length < 10) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const { reloadGoogleMaps } = await import('./index-Bei5DzAL.js');
|
|
115
|
+
await reloadGoogleMaps(googleMapsApiKey);
|
|
116
|
+
console.log("[ApiKeyContext] Google Maps recarregado manualmente");
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error("[ApiKeyContext] Erro ao recarregar Google Maps:", error);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
return /* @__PURE__ */ jsx(
|
|
122
|
+
ApiKeyContext.Provider,
|
|
123
|
+
{
|
|
124
|
+
value: {
|
|
125
|
+
apiKey,
|
|
126
|
+
setApiKey,
|
|
127
|
+
geminiApiKey,
|
|
128
|
+
setGeminiApiKey,
|
|
129
|
+
isApiKeyValid,
|
|
130
|
+
googleMapsApiKey,
|
|
131
|
+
setGoogleMapsApiKey,
|
|
132
|
+
isGoogleMapsKeyValid,
|
|
133
|
+
reloadMapsApi
|
|
134
|
+
},
|
|
135
|
+
children
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function useApiKey() {
|
|
140
|
+
const context = useContext(ApiKeyContext);
|
|
141
|
+
if (!context) {
|
|
142
|
+
throw new Error("useApiKey must be used within ApiKeyProvider");
|
|
143
|
+
}
|
|
144
|
+
return context;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const AssistenteContext = createContext(void 0);
|
|
148
|
+
function AssistenteProvider({ children }) {
|
|
149
|
+
const [conversaAtual, setConversaAtual] = useState("1");
|
|
150
|
+
const [conversas, setConversas] = useState([
|
|
151
|
+
{
|
|
152
|
+
id: "1",
|
|
153
|
+
titulo: "Nova Conversa",
|
|
154
|
+
mensagens: [],
|
|
155
|
+
ultimaMensagem: "",
|
|
156
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
157
|
+
favorita: false
|
|
158
|
+
}
|
|
159
|
+
]);
|
|
160
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
161
|
+
const [abaSelecionada, setAbaSelecionada] = useState("chat");
|
|
162
|
+
const [editingDocument, setEditingDocument] = useState(
|
|
163
|
+
null
|
|
164
|
+
);
|
|
165
|
+
const [searchFilter, setSearchFilter] = useState("all");
|
|
166
|
+
const [savedSearches, setSavedSearches] = useState([]);
|
|
167
|
+
const historico = conversas.find((c) => c.id === conversaAtual)?.mensagens || [];
|
|
168
|
+
return /* @__PURE__ */ jsx(
|
|
169
|
+
AssistenteContext.Provider,
|
|
170
|
+
{
|
|
171
|
+
value: {
|
|
172
|
+
historico,
|
|
173
|
+
conversaAtual,
|
|
174
|
+
setConversaAtual,
|
|
175
|
+
conversas,
|
|
176
|
+
setConversas,
|
|
177
|
+
isTyping,
|
|
178
|
+
setIsTyping,
|
|
179
|
+
abaSelecionada,
|
|
180
|
+
setAbaSelecionada,
|
|
181
|
+
editingDocument,
|
|
182
|
+
setEditingDocument,
|
|
183
|
+
searchFilter,
|
|
184
|
+
setSearchFilter,
|
|
185
|
+
savedSearches,
|
|
186
|
+
setSavedSearches
|
|
187
|
+
},
|
|
188
|
+
children
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
function useAssistente() {
|
|
193
|
+
const context = useContext(AssistenteContext);
|
|
194
|
+
if (!context) {
|
|
195
|
+
throw new Error("useAssistente must be used within AssistenteProvider");
|
|
196
|
+
}
|
|
197
|
+
return context;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export { ApiKeyProvider as A, AssistenteProvider as a, useAssistente as b, useApiKey as u };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
const React = require('react');
|
|
5
|
+
|
|
6
|
+
const ApiKeyContext = React.createContext(void 0);
|
|
7
|
+
const readStorage = (key) => typeof window !== "undefined" ? window.localStorage?.getItem(key) : null;
|
|
8
|
+
const writeStorage = (key, value) => {
|
|
9
|
+
if (typeof window === "undefined") return;
|
|
10
|
+
window.localStorage?.setItem(key, value);
|
|
11
|
+
};
|
|
12
|
+
const removeStorage = (key) => {
|
|
13
|
+
if (typeof window === "undefined") return;
|
|
14
|
+
window.localStorage?.removeItem(key);
|
|
15
|
+
};
|
|
16
|
+
function ApiKeyProvider({
|
|
17
|
+
children,
|
|
18
|
+
initialApiKey,
|
|
19
|
+
initialGeminiApiKey,
|
|
20
|
+
initialGoogleMapsApiKey
|
|
21
|
+
}) {
|
|
22
|
+
const LEAKED_KEYS = [
|
|
23
|
+
"AIzaSyCMsAMytBeOK0Qd7RKFyA5IW9eWt2WTJg",
|
|
24
|
+
"AIzaSyAiYWEIEmx212Up9zfM8kqyMXB4jLs8gq0"
|
|
25
|
+
];
|
|
26
|
+
const [apiKey, setApiKeyState] = React.useState(() => {
|
|
27
|
+
if (initialApiKey) return initialApiKey;
|
|
28
|
+
const saved = readStorage("xertica-api-key");
|
|
29
|
+
return saved || "";
|
|
30
|
+
});
|
|
31
|
+
const [geminiApiKey, setGeminiApiKeyState] = React.useState(() => {
|
|
32
|
+
if (initialGeminiApiKey) return initialGeminiApiKey;
|
|
33
|
+
const saved = readStorage("xertica-gemini-api-key");
|
|
34
|
+
if (saved && LEAKED_KEYS.includes(saved)) {
|
|
35
|
+
removeStorage("xertica-gemini-api-key");
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
return saved || "";
|
|
39
|
+
});
|
|
40
|
+
const [googleMapsApiKey, setGoogleMapsApiKeyState] = React.useState(() => {
|
|
41
|
+
if (initialGoogleMapsApiKey) return initialGoogleMapsApiKey;
|
|
42
|
+
const saved = readStorage("xertica-googlemaps-api-key");
|
|
43
|
+
return saved || "";
|
|
44
|
+
});
|
|
45
|
+
const [isApiKeyValid, setIsApiKeyValid] = React.useState(false);
|
|
46
|
+
const [isGoogleMapsKeyValid, setIsGoogleMapsKeyValid] = React.useState(false);
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
if (initialApiKey !== void 0) {
|
|
49
|
+
setApiKeyState(initialApiKey.trim());
|
|
50
|
+
}
|
|
51
|
+
}, [initialApiKey]);
|
|
52
|
+
React.useEffect(() => {
|
|
53
|
+
if (initialGeminiApiKey !== void 0) {
|
|
54
|
+
setGeminiApiKeyState(initialGeminiApiKey.trim());
|
|
55
|
+
}
|
|
56
|
+
}, [initialGeminiApiKey]);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
if (initialGoogleMapsApiKey !== void 0) {
|
|
59
|
+
setGoogleMapsApiKeyState(initialGoogleMapsApiKey.trim());
|
|
60
|
+
}
|
|
61
|
+
}, [initialGoogleMapsApiKey]);
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
if (apiKey) {
|
|
64
|
+
writeStorage("xertica-api-key", apiKey);
|
|
65
|
+
setIsApiKeyValid(apiKey.length > 0);
|
|
66
|
+
} else {
|
|
67
|
+
removeStorage("xertica-api-key");
|
|
68
|
+
setIsApiKeyValid(false);
|
|
69
|
+
}
|
|
70
|
+
}, [apiKey]);
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
if (geminiApiKey) {
|
|
73
|
+
writeStorage("xertica-gemini-api-key", geminiApiKey);
|
|
74
|
+
setIsApiKeyValid(geminiApiKey.startsWith("AIzaSy") && geminiApiKey.length > 20);
|
|
75
|
+
} else {
|
|
76
|
+
removeStorage("xertica-gemini-api-key");
|
|
77
|
+
setIsApiKeyValid(false);
|
|
78
|
+
}
|
|
79
|
+
}, [geminiApiKey]);
|
|
80
|
+
React.useEffect(() => {
|
|
81
|
+
if (googleMapsApiKey) {
|
|
82
|
+
writeStorage("xertica-googlemaps-api-key", googleMapsApiKey);
|
|
83
|
+
setIsGoogleMapsKeyValid(
|
|
84
|
+
googleMapsApiKey.startsWith("AIzaSy") && googleMapsApiKey.length > 20
|
|
85
|
+
);
|
|
86
|
+
const reloadMaps = async () => {
|
|
87
|
+
if (googleMapsApiKey.length < 10) return;
|
|
88
|
+
try {
|
|
89
|
+
const { reloadGoogleMaps } = await Promise.resolve().then(() => require('./index-BQjx1Mdf.cjs'));
|
|
90
|
+
await reloadGoogleMaps(googleMapsApiKey);
|
|
91
|
+
console.log("[ApiKeyContext] Google Maps recarregado com sucesso");
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error("[ApiKeyContext] Erro ao recarregar Google Maps:", error);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
reloadMaps();
|
|
97
|
+
} else {
|
|
98
|
+
removeStorage("xertica-googlemaps-api-key");
|
|
99
|
+
setIsGoogleMapsKeyValid(false);
|
|
100
|
+
}
|
|
101
|
+
}, [googleMapsApiKey]);
|
|
102
|
+
const setApiKey = (key) => {
|
|
103
|
+
setApiKeyState(key.trim());
|
|
104
|
+
};
|
|
105
|
+
const setGeminiApiKey = (key) => {
|
|
106
|
+
setGeminiApiKeyState(key.trim());
|
|
107
|
+
};
|
|
108
|
+
const setGoogleMapsApiKey = (key) => {
|
|
109
|
+
setGoogleMapsApiKeyState(key.trim());
|
|
110
|
+
};
|
|
111
|
+
const reloadMapsApi = async () => {
|
|
112
|
+
if (!googleMapsApiKey || googleMapsApiKey.length < 10) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const { reloadGoogleMaps } = await Promise.resolve().then(() => require('./index-BQjx1Mdf.cjs'));
|
|
117
|
+
await reloadGoogleMaps(googleMapsApiKey);
|
|
118
|
+
console.log("[ApiKeyContext] Google Maps recarregado manualmente");
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error("[ApiKeyContext] Erro ao recarregar Google Maps:", error);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
124
|
+
ApiKeyContext.Provider,
|
|
125
|
+
{
|
|
126
|
+
value: {
|
|
127
|
+
apiKey,
|
|
128
|
+
setApiKey,
|
|
129
|
+
geminiApiKey,
|
|
130
|
+
setGeminiApiKey,
|
|
131
|
+
isApiKeyValid,
|
|
132
|
+
googleMapsApiKey,
|
|
133
|
+
setGoogleMapsApiKey,
|
|
134
|
+
isGoogleMapsKeyValid,
|
|
135
|
+
reloadMapsApi
|
|
136
|
+
},
|
|
137
|
+
children
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function useApiKey() {
|
|
142
|
+
const context = React.useContext(ApiKeyContext);
|
|
143
|
+
if (!context) {
|
|
144
|
+
throw new Error("useApiKey must be used within ApiKeyProvider");
|
|
145
|
+
}
|
|
146
|
+
return context;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const AssistenteContext = React.createContext(void 0);
|
|
150
|
+
function AssistenteProvider({ children }) {
|
|
151
|
+
const [conversaAtual, setConversaAtual] = React.useState("1");
|
|
152
|
+
const [conversas, setConversas] = React.useState([
|
|
153
|
+
{
|
|
154
|
+
id: "1",
|
|
155
|
+
titulo: "Nova Conversa",
|
|
156
|
+
mensagens: [],
|
|
157
|
+
ultimaMensagem: "",
|
|
158
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
159
|
+
favorita: false
|
|
160
|
+
}
|
|
161
|
+
]);
|
|
162
|
+
const [isTyping, setIsTyping] = React.useState(false);
|
|
163
|
+
const [abaSelecionada, setAbaSelecionada] = React.useState("chat");
|
|
164
|
+
const [editingDocument, setEditingDocument] = React.useState(
|
|
165
|
+
null
|
|
166
|
+
);
|
|
167
|
+
const [searchFilter, setSearchFilter] = React.useState("all");
|
|
168
|
+
const [savedSearches, setSavedSearches] = React.useState([]);
|
|
169
|
+
const historico = conversas.find((c) => c.id === conversaAtual)?.mensagens || [];
|
|
170
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
AssistenteContext.Provider,
|
|
172
|
+
{
|
|
173
|
+
value: {
|
|
174
|
+
historico,
|
|
175
|
+
conversaAtual,
|
|
176
|
+
setConversaAtual,
|
|
177
|
+
conversas,
|
|
178
|
+
setConversas,
|
|
179
|
+
isTyping,
|
|
180
|
+
setIsTyping,
|
|
181
|
+
abaSelecionada,
|
|
182
|
+
setAbaSelecionada,
|
|
183
|
+
editingDocument,
|
|
184
|
+
setEditingDocument,
|
|
185
|
+
searchFilter,
|
|
186
|
+
setSearchFilter,
|
|
187
|
+
savedSearches,
|
|
188
|
+
setSavedSearches
|
|
189
|
+
},
|
|
190
|
+
children
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
function useAssistente() {
|
|
195
|
+
const context = React.useContext(AssistenteContext);
|
|
196
|
+
if (!context) {
|
|
197
|
+
throw new Error("useAssistente must be used within AssistenteProvider");
|
|
198
|
+
}
|
|
199
|
+
return context;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
exports.ApiKeyProvider = ApiKeyProvider;
|
|
203
|
+
exports.AssistenteProvider = AssistenteProvider;
|
|
204
|
+
exports.useApiKey = useApiKey;
|
|
205
|
+
exports.useAssistente = useAssistente;
|