xertica-ui 2.6.0 → 2.8.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 +34 -0
- package/README.md +20 -12
- package/bin/generate-tokens.ts +25 -9
- package/components/assistant/xertica-assistant/use-assistant.ts +5 -6
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +1 -1
- 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/components/ui/button/button.test.tsx +2 -2
- package/components/ui/button/button.tsx +6 -6
- package/components/ui/card/card.tsx +14 -2
- package/components/ui/input/input.tsx +1 -1
- package/components/ui/input-otp/input-otp.tsx +1 -1
- package/components/ui/search/search.tsx +1 -1
- package/components/ui/select/select.tsx +2 -2
- package/components/ui/switch/switch.tsx +1 -1
- package/components/ui/textarea/textarea.tsx +1 -1
- package/contexts/BrandColorsContext.tsx +50 -2
- package/contexts/theme-data.ts +100 -3
- package/dist/AssistantChart-BFBJXVcm.js +3383 -0
- package/dist/AssistantChart-BTnE94FR.cjs +3551 -0
- package/dist/AssistantChart-BvccsQMx.cjs +3551 -0
- package/dist/AssistantChart-CImCNLfD.cjs +3551 -0
- package/dist/AssistantChart-CkskcBVD.js +3383 -0
- package/dist/AssistantChart-CrPkO9ip.js +3383 -0
- package/dist/AssistantChart-DKz2O1R8.cjs +3551 -0
- package/dist/AssistantChart-DYL6UEvo.cjs +3551 -0
- package/dist/AssistantChart-DfdIfmQr.js +3383 -0
- package/dist/AssistantChart-keo-ogKi.js +3383 -0
- package/dist/AudioPlayer-AoU5MTiM.cjs +1282 -0
- package/dist/AudioPlayer-Bx1vrblm.js +1278 -0
- package/dist/AudioPlayer-D0-I6J3J.cjs +1282 -0
- package/dist/AudioPlayer-DIWX_Yg7.cjs +1282 -0
- package/dist/AudioPlayer-DrWJnS7c.js +1278 -0
- package/dist/AudioPlayer-_P-Z7pAK.js +1278 -0
- package/dist/BrandColorsContext-B25Bpr3b.cjs +819 -0
- package/dist/BrandColorsContext-BCieATdW.js +812 -0
- package/dist/BrandColorsContext-DP0DXAXS.js +824 -0
- package/dist/BrandColorsContext-DjpC9wR_.cjs +798 -0
- package/dist/BrandColorsContext-MpXFhte7.cjs +831 -0
- package/dist/BrandColorsContext-V3tOc-Zw.js +791 -0
- package/dist/CodeBlock-CK4rO6lM.js +221 -0
- package/dist/CodeBlock-CKXamfVd.cjs +223 -0
- package/dist/CodeBlock-CVmE7isS.cjs +223 -0
- package/dist/CodeBlock-D7i5-cMv.js +221 -0
- package/dist/CodeBlock-Dg56j2ut.cjs +223 -0
- package/dist/CodeBlock-Dt6nUQoo.js +221 -0
- package/dist/CustomTooltipContent-Bxynuq7T.js +33 -0
- package/dist/CustomTooltipContent-BzjOSA0P.cjs +54 -0
- package/dist/CustomTooltipContent-C-ZeCNsf.js +33 -0
- package/dist/CustomTooltipContent-CON_QrXm.cjs +54 -0
- package/dist/CustomTooltipContent-UR_n2QfW.js +33 -0
- package/dist/CustomTooltipContent-v1SXjoFn.cjs +54 -0
- package/dist/FeatureCard-4n_8a63v.cjs +497 -0
- package/dist/FeatureCard-BLiAWT3w.js +485 -0
- package/dist/FeatureCard-BXRW97Ef.cjs +497 -0
- package/dist/FeatureCard-BhpfmPPx.js +485 -0
- package/dist/FeatureCard-CDgLlWE2.js +485 -0
- package/dist/FeatureCard-CgQ8lq-I.cjs +497 -0
- package/dist/FeatureCard-Cp8cVC6T.cjs +497 -0
- package/dist/FeatureCard-D-5yS_OD.cjs +497 -0
- package/dist/FeatureCard-D0MwrTRP.js +485 -0
- package/dist/FeatureCard-uE5i0in_.js +485 -0
- package/dist/ImageWithFallback-BOh1FM3I.js +98 -0
- package/dist/ImageWithFallback-BQO7952C.js +98 -0
- package/dist/ImageWithFallback-BkMXHCvV.cjs +122 -0
- package/dist/ImageWithFallback-C72nwszA.cjs +122 -0
- package/dist/ImageWithFallback-CCUMDuEC.cjs +122 -0
- package/dist/ImageWithFallback-CfkAkctC.cjs +122 -0
- package/dist/ImageWithFallback-DQD5TTlY.js +98 -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-1XhM7R1i.cjs +48 -0
- package/dist/LanguageSelector-B3ZVNc55.js +46 -0
- package/dist/LanguageSelector-BR3Ss0k_.js +46 -0
- package/dist/LanguageSelector-CHOLPmHr.cjs +48 -0
- package/dist/LanguageSelector-Cj6zl0CM.js +46 -0
- package/dist/LanguageSelector-DK1zD4VH.cjs +48 -0
- package/dist/LanguageSelector-DseCAa0p.cjs +48 -0
- package/dist/LanguageSelector-Du09anfM.js +46 -0
- package/dist/LanguageSelector-PZHlFfEs.cjs +48 -0
- package/dist/LanguageSelector-SB-NJSmx.js +46 -0
- package/dist/PasswordStrengthMeter-B3S6i_k4.cjs +179 -0
- package/dist/PasswordStrengthMeter-B8ky1nJI.js +173 -0
- package/dist/PasswordStrengthMeter-C5VEopH-.cjs +179 -0
- package/dist/PasswordStrengthMeter-C8irs0G1.js +173 -0
- package/dist/PasswordStrengthMeter-CIm7_jNT.js +173 -0
- package/dist/PasswordStrengthMeter-DKSAbQzu.js +173 -0
- package/dist/PasswordStrengthMeter-DxLu54p-.cjs +179 -0
- package/dist/PasswordStrengthMeter-MLzhax-d.cjs +179 -0
- package/dist/PasswordStrengthMeter-k-e9Pole.cjs +179 -0
- package/dist/PasswordStrengthMeter-pAi2viq5.js +173 -0
- package/dist/ThemeContext-CEY9pCn-.cjs +86 -0
- package/dist/ThemeContext-Dk3tH7Ym.js +83 -0
- package/dist/ThemeToggle-4pQbVkpK.js +215 -0
- package/dist/ThemeToggle-B6rIVLvC.cjs +219 -0
- package/dist/ThemeToggle-BUOTps5l.cjs +219 -0
- package/dist/ThemeToggle-BpM_iHh8.js +215 -0
- package/dist/ThemeToggle-CO5mPl6e.js +215 -0
- package/dist/ThemeToggle-CZRlv0d4.cjs +219 -0
- package/dist/ThemeToggle-DbZQ0JSV.cjs +219 -0
- package/dist/ThemeToggle-vLXEq2TS.js +215 -0
- package/dist/VerifyEmailPage-BKiaw2YK.js +2954 -0
- package/dist/VerifyEmailPage-BTjI2c9n.js +2954 -0
- package/dist/VerifyEmailPage-Beaicc6V.cjs +2963 -0
- package/dist/VerifyEmailPage-BehPBp0F.cjs +2963 -0
- package/dist/VerifyEmailPage-CJGfDSOx.js +2954 -0
- package/dist/VerifyEmailPage-DOlZUNc0.cjs +2963 -0
- package/dist/VerifyEmailPage-DQPRl_RJ.js +2954 -0
- package/dist/VerifyEmailPage-Djb6hthM.js +2954 -0
- package/dist/VerifyEmailPage-DrdqMbLC.cjs +2963 -0
- package/dist/VerifyEmailPage-hyDfeCRa.cjs +2963 -0
- package/dist/VerifyEmailPage-kKSXl8vu.cjs +2963 -0
- package/dist/VerifyEmailPage-w4N-bDhU.js +2954 -0
- package/dist/XerticaOrbe-B9tegV1n.js +1925 -0
- package/dist/XerticaOrbe-BQ_NjwPS.cjs +1927 -0
- package/dist/XerticaOrbe-BdUX171Q.cjs +1927 -0
- package/dist/XerticaOrbe-BdlKXRHU.cjs +1927 -0
- package/dist/XerticaOrbe-CByPAFE5.js +1925 -0
- package/dist/XerticaOrbe-Ccu9eFMu.js +1925 -0
- package/dist/XerticaProvider-BVid1oqn.cjs +46 -0
- package/dist/XerticaProvider-BijpC_py.cjs +46 -0
- package/dist/XerticaProvider-Bz_YfNY4.js +44 -0
- package/dist/XerticaProvider-C2wDRQhT.js +44 -0
- package/dist/XerticaProvider-CtvYfRbZ.cjs +46 -0
- package/dist/XerticaProvider-DMiyExQS.js +44 -0
- package/dist/XerticaProvider-DP2lzKip.cjs +46 -0
- package/dist/XerticaProvider-De79hhgp.js +44 -0
- package/dist/XerticaProvider-Dkk1Vbsy.js +44 -0
- package/dist/XerticaProvider-EdzH9Nrw.cjs +46 -0
- package/dist/alert-dialog-9XYAn5z5.js +819 -0
- package/dist/alert-dialog-B281hud0.cjs +872 -0
- package/dist/alert-dialog-BVWbrcrw.cjs +872 -0
- package/dist/alert-dialog-B_Ko1xpu.js +819 -0
- package/dist/alert-dialog-BzIcTKn_.cjs +872 -0
- package/dist/alert-dialog-CIxE8T-J.cjs +872 -0
- package/dist/alert-dialog-DW-_iPuf.js +819 -0
- package/dist/alert-dialog-kty5pxQp.js +819 -0
- package/dist/assistant.cjs.js +2 -2
- package/dist/assistant.es.js +2 -2
- package/dist/avatar-BJwIMbZ_.js +54 -0
- package/dist/avatar-C4Zh5fAJ.cjs +77 -0
- package/dist/avatar-CTEUKZUT.js +54 -0
- package/dist/avatar-D1bZhlhL.cjs +77 -0
- package/dist/avatar-DKIxHeKG.cjs +77 -0
- package/dist/avatar-v16eZC8p.js +54 -0
- package/dist/blocks.cjs.js +7 -3
- package/dist/blocks.es.js +2 -2
- package/dist/brand.cjs.js +8 -7
- package/dist/brand.es.js +4 -3
- package/dist/breadcrumb-B1mUiaWB.cjs +98 -0
- package/dist/breadcrumb-BIRgmem-.js +90 -0
- package/dist/breadcrumb-BSng-oUf.cjs +98 -0
- package/dist/breadcrumb-CES7furU.js +90 -0
- package/dist/breadcrumb-CP275kbF.cjs +98 -0
- package/dist/breadcrumb-D7hr1EvJ.js +90 -0
- package/dist/button-BYtQygX5.cjs +85 -0
- package/dist/button-BnAW-a_w.js +62 -0
- package/dist/button-CUPDQews.js +62 -0
- package/dist/button-DFa-rtPy.js +62 -0
- package/dist/button-DV1G6XrI.cjs +85 -0
- package/dist/button-Kd9Fj53S.cjs +85 -0
- package/dist/cli.js +94 -10
- 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/components/ui/card/card.d.ts +11 -1
- package/dist/contexts/theme-data.d.ts +15 -1
- package/dist/dropdown-menu-B9wTZ0uX.cjs +225 -0
- package/dist/dropdown-menu-BeBtZOrs.cjs +225 -0
- package/dist/dropdown-menu-D7mRDGis.js +190 -0
- package/dist/dropdown-menu-D8ZmqHB7.js +190 -0
- package/dist/dropdown-menu-_AWBeOts.cjs +225 -0
- package/dist/dropdown-menu-qGzLO_HO.js +190 -0
- package/dist/hooks.cjs.js +6 -5
- package/dist/hooks.es.js +3 -2
- package/dist/index.cjs.js +42 -37
- package/dist/index.es.js +25 -24
- package/dist/input-B53E6K8G.js +65 -0
- package/dist/input-B7Q7vcoG.cjs +91 -0
- package/dist/input-BysqLMK1.js +65 -0
- package/dist/input-CStdFsaH.cjs +91 -0
- package/dist/input-CXYth6nW.cjs +91 -0
- package/dist/input-CX_fWE2D.cjs +91 -0
- package/dist/input-CitUVnEn.js +65 -0
- package/dist/input-wkBYTp-B.js +65 -0
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/media.cjs.js +1 -1
- package/dist/media.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-BHiRUzWZ.js +2894 -0
- package/dist/rich-text-editor-BWMRUMJ2.js +2894 -0
- package/dist/rich-text-editor-BoE6Ks5x.cjs +2968 -0
- package/dist/rich-text-editor-BzuEwR_X.js +2894 -0
- package/dist/rich-text-editor-CcopGbQ-.js +2894 -0
- package/dist/rich-text-editor-CmxSRJIi.cjs +2968 -0
- package/dist/rich-text-editor-DyYAu28v.cjs +2968 -0
- package/dist/rich-text-editor-JS_eMQCC.cjs +2968 -0
- package/dist/select-BNfFJwYp.js +130 -0
- package/dist/select-CN8IDjGB.js +130 -0
- package/dist/select-CwRzQHA7.cjs +161 -0
- package/dist/select-CwgD3nMD.js +130 -0
- package/dist/select-DEHUm_AC.cjs +161 -0
- package/dist/select-DJDgpVm3.cjs +161 -0
- package/dist/select-DSLWQLmo.cjs +161 -0
- package/dist/select-YFx0wCYT.js +130 -0
- package/dist/sidebar-B7YzwAp3.js +1151 -0
- package/dist/sidebar-BF7JEL7U.js +1151 -0
- package/dist/sidebar-Bjr2doX5.cjs +1154 -0
- package/dist/sidebar-CMrNCmhb.cjs +1154 -0
- package/dist/sidebar-CO9x8AVn.cjs +1154 -0
- package/dist/sidebar-Cb0U7NsV.cjs +1154 -0
- package/dist/sidebar-Cn_KB4c6.js +1151 -0
- package/dist/sidebar-Db_C30ph.cjs +1154 -0
- package/dist/sidebar-miPHsRz4.js +1151 -0
- package/dist/sidebar-pZF8mLLu.js +1151 -0
- package/dist/skeleton-BX2XTjMO.cjs +87 -0
- package/dist/skeleton-BoLK0iV7.js +78 -0
- package/dist/skeleton-Bysq8yeB.js +79 -0
- package/dist/skeleton-C7nspYnC.cjs +87 -0
- package/dist/skeleton-DL4V5Vub.cjs +87 -0
- package/dist/skeleton-DSMhdqp1.js +78 -0
- package/dist/skeleton-DhJswvTW.cjs +88 -0
- package/dist/skeleton-DicUfozA.js +78 -0
- package/dist/slider-77T_Eqw4.js +56 -0
- package/dist/slider-B6puMLXS.cjs +78 -0
- package/dist/slider-B785N7lZ.js +56 -0
- package/dist/slider-C6yssKyZ.cjs +78 -0
- package/dist/slider-CKZO9gVh.cjs +78 -0
- package/dist/slider-Cn-ImSze.js +56 -0
- package/dist/tooltip-BiJVGob3.cjs +72 -0
- package/dist/tooltip-D2OPn-1v.cjs +72 -0
- package/dist/tooltip-DlCHQuh1.js +48 -0
- package/dist/tooltip-fQ2Va0G8.cjs +72 -0
- package/dist/tooltip-oAw00FX2.js +48 -0
- package/dist/tooltip-u147L0wD.js +48 -0
- package/dist/ui.cjs.js +17 -17
- package/dist/ui.es.js +13 -13
- package/dist/xertica-assistant-8fh8KA2K.js +2173 -0
- package/dist/xertica-assistant-BjfG3zyL.cjs +2180 -0
- package/dist/xertica-assistant-C2A0R94j.js +2173 -0
- package/dist/xertica-assistant-CSUprvTR.js +2173 -0
- package/dist/xertica-assistant-Caubm0HR.js +2173 -0
- package/dist/xertica-assistant-DEexPM2u.cjs +2180 -0
- package/dist/xertica-assistant-Dh1dmEx3.cjs +2180 -0
- package/dist/xertica-assistant-GGcyHEZG.cjs +2180 -0
- package/dist/xertica-assistant-USefPe2L.js +2173 -0
- package/dist/xertica-assistant-cwSHFwfv.cjs +2180 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/i18n.md +2 -0
- package/package.json +3 -2
- package/styles/xertica/base.css +38 -0
- package/styles/xertica/tokens.css +9 -0
- package/templates/package.json +2 -2
- package/templates/src/app/App.tsx +5 -1
- 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 +19 -74
- package/templates/src/features/template/index.ts +0 -4
- package/templates/src/features/template/ui/TemplateContent.tsx +397 -1107
- package/templates/src/locales/en/pages/templates.json +70 -270
- package/templates/src/locales/es/pages/templates.json +77 -277
- package/templates/src/locales/pt-BR/pages/templates.json +77 -277
- package/templates/src/styles/xertica/tokens.css +9 -0
- package/templates/vite.config.ts +25 -0
- 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,40 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [2.8.0] — 2026-07-09
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Novo tema `xertica`** — a nova identidade visual da Xertica (papel quente `#FFFEF8`, tintas vivas e o preto da marca como acento geral), somando-se aos temas já existentes em `contexts/theme-data.ts` → `colorThemes`. Botões neste tema ganham radius pill (`buttonRadius: '9999px'`), stroke em todas as variantes preenchidas (`buttonStrokeWidth`/`buttonStrokeColor(Dark)`), botão primário amarelo (`#FAF338`) com contorno e texto pretos, e botão secundário na cor preta da marca (`buttonSecondaryBg(Dark)`/`buttonSecondaryForeground(Dark)`). O amarelo é **intencionalmente restrito** ao botão `default` e ao estado ligado do `Switch` — o acento geral do tema (`--primary`, usado por links, radio buttons, foco, logo, etc.) é Negro Xertica `#1E1E1E` (Crema `#F2EDD8` no dark mode), evitado de virar amarelo sem contraste em elementos que não são botão/switch. O tema `xertica-original` (agora exibido como **"Xertica Classic"**) permanece o padrão — o `id` não foi alterado, então projetos scaffolded e chamadas `setTheme('xertica-original')` continuam funcionando sem mudanças.
|
|
19
|
+
- **`BrandColors` ganha campos opcionais de forma/estrutura de botão** — `backgroundLight`, `buttonRadius`, `buttonStrokeWidth`, `buttonStrokeColor`/`buttonStrokeColorDark`, `buttonSecondaryBg`/`buttonSecondaryBgDark`, `buttonSecondaryForeground`/`buttonSecondaryForegroundDark`, e `buttonPrimaryBg`/`buttonPrimaryBgDark`, `buttonPrimaryForeground`/`buttonPrimaryForegroundDark` (acento do botão `default` e do `Switch`, com fallback para `var(--primary)`/`var(--primary-foreground)`). Todos são opcionais e têm fallback idêntico ao comportamento anterior, então os 9 temas pré-existentes ficam pixel-idênticos. Removido o campo morto `radius?: string` (nunca lido por `applyColors()`).
|
|
20
|
+
- **`components/ui/button/button.tsx`** — as variantes preenchidas (`default`, `destructive`, `secondary`, `success`, `info`, `warning`) agora aceitam um stroke via `--button-stroke-width`/`--button-stroke-color`; a variante `default` passou a usar `--button-primary-bg`/`--button-primary-foreground` (em vez de `bg-primary`/`text-primary-foreground` direto) para permitir um acento de botão distinto do `--primary` semântico geral; a variante `secondary` passou a usar os tokens `--button-secondary-bg`/`--button-secondary-foreground` (com fallback para `--secondary`/`--secondary-foreground`) em vez do modificador de opacidade `/80`, que não funcionava sobre cores vindas de `var()`.
|
|
21
|
+
- **`components/ui/switch/switch.tsx`** — o estado marcado (`checked`) passou a usar `--button-primary-bg` em vez de `bg-primary`, para acompanhar o mesmo acento do botão `default`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `bin/generate-tokens.ts` e os `tokens.css` estáticos (`styles/xertica/`, `templates/src/styles/xertica/`) passam a emitir `--button-stroke-width`, `--button-stroke-color`, `--button-secondary-bg`, `--button-secondary-foreground`, `--button-primary-bg`, `--button-primary-foreground`, e `--radius-button`/`--background` (light) respeitam os novos campos do tema quando definidos.
|
|
26
|
+
- `.storybook/preview.tsx` — toolbar "Brand Color" renomeia `xertica-original` para "Xertica Classic" e adiciona a entrada "Xertica" (`xertica`).
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## [2.7.0] — 2026-07-07
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **`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`.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **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.
|
|
39
|
+
- **`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.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- **`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.
|
|
44
|
+
- 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.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
14
48
|
## [2.6.0] — 2026-07-06
|
|
15
49
|
|
|
16
50
|
### 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
|
---
|
|
@@ -353,11 +353,12 @@ Xertica UI uses semantic CSS tokens. **Never use raw colors or generic Tailwind
|
|
|
353
353
|
|
|
354
354
|
### Color Themes
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
Ten built-in themes — each sets the primary color, sidebar, chart palette, and dark-mode surface hue. Select at `init` time or change later with `npx xertica-ui update → Theme only`.
|
|
357
357
|
|
|
358
358
|
| Theme | ID | Primary | Sidebar | Dark bg |
|
|
359
359
|
|---|---|---|---|---|
|
|
360
|
-
|  Xertica | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
|
|
360
|
+
|  Xertica Classic | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
|
|
361
|
+
|  Xertica | `xertica` | `#1E1E1E` | `#1E1E1E` | `#141311` |
|
|
361
362
|
|  Zinc | `zinc` | `#18181B` | `#18181B` | `#05050d` |
|
|
362
363
|
|  Slate | `slate` | `#0F172A` | `#0F172A` | `#05050d` |
|
|
363
364
|
|  Blue | `blue` | `#2563EB` | `#1E3A8A` | `#03050f` |
|
|
@@ -369,6 +370,8 @@ Nine built-in themes — each sets the primary color, sidebar, chart palette, an
|
|
|
369
370
|
|
|
370
371
|
> Each badge renders the **exact primary color** of that theme as a filled swatch. Generated by the CLI and stored in `src/styles/xertica/tokens.css`.
|
|
371
372
|
|
|
373
|
+
**"Xertica" theme — serigrafia identity:** pill-shaped buttons (`--radius-button: 9999px`) and a visible stroke on every filled button variant. The general brand accent (`--primary`, used by links, focus rings, radio buttons, the logo, etc.) is Negro Xertica (`#1E1E1E`, `#F2EDD8` in dark mode) for reliable contrast everywhere. Yellow (`#FAF338`) is intentionally scoped to just two controls — the `Button` `default` variant and `Switch`'s checked state — via the `--button-primary-bg`/`--button-primary-foreground` tokens, both with black text/outline. The `secondary` button is black (`#1E1E1E`). The page background uses a warm off-white paper tone (`#FFFEF8`) while cards stay pure white. The full accent palette (and the "serigrafia" color-intersection combinations meant for print/marketing pieces — e.g. Celeste `#1899AF` + Magenta `#C45BAA` → Violeta `#5A4A96`) is documented in `contexts/theme-data.ts`.
|
|
374
|
+
|
|
372
375
|
**CLI — Select at project creation:**
|
|
373
376
|
|
|
374
377
|
```bash
|
|
@@ -381,7 +384,7 @@ npx xertica-ui@latest init my-app
|
|
|
381
384
|
```bash
|
|
382
385
|
npx xertica-ui update
|
|
383
386
|
# → choose "Theme only"
|
|
384
|
-
# → select any of the
|
|
387
|
+
# → select any of the 10 themes
|
|
385
388
|
# → src/styles/xertica/tokens.css is fully regenerated
|
|
386
389
|
# → .xertica.json is updated with the new themeId for future updates
|
|
387
390
|
```
|
|
@@ -396,6 +399,7 @@ npx xertica-ui update
|
|
|
396
399
|
<XerticaProvider defaultColorTheme="violet">
|
|
397
400
|
<XerticaProvider defaultColorTheme="amber">
|
|
398
401
|
<XerticaProvider defaultColorTheme="orange">
|
|
402
|
+
<XerticaProvider defaultColorTheme="xertica"> {/* new serigrafia identity */}
|
|
399
403
|
<XerticaProvider defaultColorTheme="xertica-original"> {/* default */}
|
|
400
404
|
|
|
401
405
|
// Custom hex — sets primary only; dark surfaces use the neutral default
|
|
@@ -414,7 +418,8 @@ npx xertica-ui update
|
|
|
414
418
|
| `--card`, `--popover`| ✅ Dark mode only (hue-tinted) |
|
|
415
419
|
| `--muted`, `--accent`| ✅ Dark mode only (hue-tinted) |
|
|
416
420
|
| `--border`, `--input`| ✅ Dark mode only (hue-tinted) |
|
|
417
|
-
| Light mode surfaces | ❌ Always white/zinc (
|
|
421
|
+
| Light mode surfaces | ❌ Always white/zinc, unless the theme sets `backgroundLight` (only `xertica` does) |
|
|
422
|
+
| `--radius-button`, button stroke, `secondary` button colors | ✅ Only when the theme defines `buttonRadius`/`buttonStroke*`/`buttonSecondary*` (only `xertica` does) |
|
|
418
423
|
|
|
419
424
|
### Mobile Content Padding
|
|
420
425
|
|
|
@@ -467,13 +472,16 @@ See [`docs/i18n.md`](./docs/i18n.md) for setup, runtime configuration, and addin
|
|
|
467
472
|
|
|
468
473
|
## 📜 Scripts
|
|
469
474
|
|
|
470
|
-
| Command
|
|
471
|
-
|
|
|
472
|
-
| `npm run dev`
|
|
473
|
-
| `npm run
|
|
474
|
-
| `npm run
|
|
475
|
-
| `npm run
|
|
476
|
-
| `npm run
|
|
475
|
+
| Command | Description |
|
|
476
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
477
|
+
| `npm run dev` | Runs the `templates/` scaffold with live Design System source (via monorepo alias) — the same app a `npx xertica-ui init` consumer gets. Use this to test Design System changes against real pages. |
|
|
478
|
+
| `npm run dev:pages` | Runs the root harness (`App.tsx`) for the standalone `xertica-ui/pages` showcase subpath. |
|
|
479
|
+
| `npm run build` | Production bundle |
|
|
480
|
+
| `npm run storybook` | Launch component library documentation |
|
|
481
|
+
| `npm run test` | Run unit tests via Vitest |
|
|
482
|
+
| `npm run type-check` | TypeScript validation |
|
|
483
|
+
|
|
484
|
+
> **Note**: `templates/` has its own dependencies — run `npm install` inside `templates/` once before the first `npm run dev`.
|
|
477
485
|
|
|
478
486
|
---
|
|
479
487
|
|
package/bin/generate-tokens.ts
CHANGED
|
@@ -42,13 +42,13 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
42
42
|
--xertica-dark: ${rgba(colors.sidebarDark)};
|
|
43
43
|
|
|
44
44
|
/* Semantic Colors */
|
|
45
|
-
--background: rgba(255, 255, 255, 1);
|
|
45
|
+
--background: ${colors.backgroundLight ? rgba(colors.backgroundLight) : 'rgba(255, 255, 255, 1)'};
|
|
46
46
|
--foreground: rgba(9, 9, 11, 1);
|
|
47
|
-
|
|
48
|
-
--card: rgba(255, 255, 255, 1);
|
|
47
|
+
|
|
48
|
+
--card: ${colors.cardLight ? rgba(colors.cardLight) : 'rgba(255, 255, 255, 1)'};
|
|
49
49
|
--card-foreground: rgba(9, 9, 11, 1);
|
|
50
|
-
|
|
51
|
-
--popover: rgba(255, 255, 255, 1);
|
|
50
|
+
|
|
51
|
+
--popover: ${colors.cardLight ? rgba(colors.cardLight) : 'rgba(255, 255, 255, 1)'};
|
|
52
52
|
--popover-foreground: rgba(9, 9, 11, 1);
|
|
53
53
|
|
|
54
54
|
--primary: var(--xertica-primary);
|
|
@@ -59,7 +59,7 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
59
59
|
--secondary: rgba(244, 244, 245, 1);
|
|
60
60
|
--secondary-foreground: rgba(24, 24, 27, 1);
|
|
61
61
|
|
|
62
|
-
--muted: rgba(244, 244, 245, 1);
|
|
62
|
+
--muted: ${colors.backgroundLight ? rgba(colors.backgroundLight) : 'rgba(244, 244, 245, 1)'};
|
|
63
63
|
--muted-foreground: rgba(113, 113, 122, 1);
|
|
64
64
|
|
|
65
65
|
--accent: rgba(244, 244, 245, 1);
|
|
@@ -77,6 +77,7 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
77
77
|
--warning-foreground: rgba(28, 25, 23, 1);
|
|
78
78
|
|
|
79
79
|
--border: rgba(228, 228, 231, 1);
|
|
80
|
+
--input-border: ${colors.inputBorderColor || 'var(--border)'};
|
|
80
81
|
--input: rgba(244, 244, 245, 0.5);
|
|
81
82
|
--input-background: rgba(244, 244, 245, 0.5);
|
|
82
83
|
--ring: ${rgba(colors.primary, 0.5)};
|
|
@@ -106,9 +107,17 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
106
107
|
|
|
107
108
|
/* Spacing & Radius */
|
|
108
109
|
--radius: 6px;
|
|
109
|
-
--radius-button: 12px;
|
|
110
|
+
--radius-button: ${colors.buttonRadius || '12px'};
|
|
110
111
|
--radius-card: 12px;
|
|
111
|
-
|
|
112
|
+
|
|
113
|
+
/* Button stroke & secondary (theme-overridable; defaults preserve the flat look) */
|
|
114
|
+
--button-stroke-width: ${colors.buttonStrokeWidth || '0px'};
|
|
115
|
+
--button-stroke-color: ${colors.buttonStrokeColor || 'transparent'};
|
|
116
|
+
--button-secondary-bg: ${colors.buttonSecondaryBg || 'var(--secondary)'};
|
|
117
|
+
--button-secondary-foreground: ${colors.buttonSecondaryForeground || 'var(--secondary-foreground)'};
|
|
118
|
+
--button-primary-bg: ${colors.buttonPrimaryBg || 'var(--primary)'};
|
|
119
|
+
--button-primary-foreground: ${colors.buttonPrimaryForeground || 'var(--primary-foreground)'};
|
|
120
|
+
|
|
112
121
|
--elevation-sm: 0px 0px 48px 0px rgba(0, 0, 0, 0.1);
|
|
113
122
|
|
|
114
123
|
/* Typography */
|
|
@@ -209,7 +218,14 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
209
218
|
--input: ${rgba(colors.darkMuted, 0.5)};
|
|
210
219
|
--input-background: ${rgba(colors.darkMuted, 0.5)};
|
|
211
220
|
--ring: ${rgba(colors.primaryDarkMode, 0.5)};
|
|
212
|
-
|
|
221
|
+
|
|
222
|
+
/* Button stroke & secondary (theme-overridable; defaults preserve the flat look) */
|
|
223
|
+
--button-stroke-color: ${colors.buttonStrokeColorDark || colors.buttonStrokeColor || 'transparent'};
|
|
224
|
+
--button-secondary-bg: ${colors.buttonSecondaryBgDark || colors.buttonSecondaryBg || 'var(--secondary)'};
|
|
225
|
+
--button-secondary-foreground: ${colors.buttonSecondaryForegroundDark || colors.buttonSecondaryForeground || 'var(--secondary-foreground)'};
|
|
226
|
+
--button-primary-bg: ${colors.buttonPrimaryBgDark || colors.buttonPrimaryBg || 'var(--primary)'};
|
|
227
|
+
--button-primary-foreground: ${colors.buttonPrimaryForegroundDark || colors.buttonPrimaryForeground || 'var(--primary-foreground)'};
|
|
228
|
+
|
|
213
229
|
--elevation-sm: 0px 0px 48px 0px rgba(0, 0, 0, 0.3);
|
|
214
230
|
|
|
215
231
|
/* Charts — dark mode: slightly lighter/more saturated for contrast on dark bg */
|
|
@@ -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]);
|
|
@@ -441,7 +441,7 @@ export function XerticaAssistant({
|
|
|
441
441
|
{/* Main Assistant Container */}
|
|
442
442
|
<div
|
|
443
443
|
className={cn(
|
|
444
|
-
`${containerHeight} flex flex-col bg-
|
|
444
|
+
`${containerHeight} flex flex-col bg-card relative`,
|
|
445
445
|
!isFullPage && 'border-l border-border shadow-sm',
|
|
446
446
|
isMobile && isExpanded && 'fixed inset-0 z-[100] h-[100dvh] w-full border-l-0 shadow-2xl',
|
|
447
447
|
className
|
|
@@ -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
|
}
|