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.
Files changed (304) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +20 -12
  3. package/bin/generate-tokens.ts +25 -9
  4. package/components/assistant/xertica-assistant/use-assistant.ts +5 -6
  5. package/components/assistant/xertica-assistant/xertica-assistant.tsx +1 -1
  6. package/{templates/src/features/auth/ui → components/blocks/auth}/AuthPageShell.tsx +8 -3
  7. package/components/blocks/auth/PasswordStrengthMeter.tsx +41 -0
  8. package/components/blocks/auth/SocialLoginButtons.tsx +95 -0
  9. package/components/blocks/auth/index.ts +11 -0
  10. package/components/blocks/auth/usePasswordStrength.ts +13 -0
  11. package/components/blocks/index.ts +1 -0
  12. package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +45 -139
  13. package/components/pages/login-page/LoginPage.tsx +52 -145
  14. package/components/pages/reset-password-page/ResetPasswordPage.tsx +79 -169
  15. package/components/pages/verify-email-page/VerifyEmailPage.tsx +48 -150
  16. package/components/ui/button/button.test.tsx +2 -2
  17. package/components/ui/button/button.tsx +6 -6
  18. package/components/ui/card/card.tsx +14 -2
  19. package/components/ui/input/input.tsx +1 -1
  20. package/components/ui/input-otp/input-otp.tsx +1 -1
  21. package/components/ui/search/search.tsx +1 -1
  22. package/components/ui/select/select.tsx +2 -2
  23. package/components/ui/switch/switch.tsx +1 -1
  24. package/components/ui/textarea/textarea.tsx +1 -1
  25. package/contexts/BrandColorsContext.tsx +50 -2
  26. package/contexts/theme-data.ts +100 -3
  27. package/dist/AssistantChart-BFBJXVcm.js +3383 -0
  28. package/dist/AssistantChart-BTnE94FR.cjs +3551 -0
  29. package/dist/AssistantChart-BvccsQMx.cjs +3551 -0
  30. package/dist/AssistantChart-CImCNLfD.cjs +3551 -0
  31. package/dist/AssistantChart-CkskcBVD.js +3383 -0
  32. package/dist/AssistantChart-CrPkO9ip.js +3383 -0
  33. package/dist/AssistantChart-DKz2O1R8.cjs +3551 -0
  34. package/dist/AssistantChart-DYL6UEvo.cjs +3551 -0
  35. package/dist/AssistantChart-DfdIfmQr.js +3383 -0
  36. package/dist/AssistantChart-keo-ogKi.js +3383 -0
  37. package/dist/AudioPlayer-AoU5MTiM.cjs +1282 -0
  38. package/dist/AudioPlayer-Bx1vrblm.js +1278 -0
  39. package/dist/AudioPlayer-D0-I6J3J.cjs +1282 -0
  40. package/dist/AudioPlayer-DIWX_Yg7.cjs +1282 -0
  41. package/dist/AudioPlayer-DrWJnS7c.js +1278 -0
  42. package/dist/AudioPlayer-_P-Z7pAK.js +1278 -0
  43. package/dist/BrandColorsContext-B25Bpr3b.cjs +819 -0
  44. package/dist/BrandColorsContext-BCieATdW.js +812 -0
  45. package/dist/BrandColorsContext-DP0DXAXS.js +824 -0
  46. package/dist/BrandColorsContext-DjpC9wR_.cjs +798 -0
  47. package/dist/BrandColorsContext-MpXFhte7.cjs +831 -0
  48. package/dist/BrandColorsContext-V3tOc-Zw.js +791 -0
  49. package/dist/CodeBlock-CK4rO6lM.js +221 -0
  50. package/dist/CodeBlock-CKXamfVd.cjs +223 -0
  51. package/dist/CodeBlock-CVmE7isS.cjs +223 -0
  52. package/dist/CodeBlock-D7i5-cMv.js +221 -0
  53. package/dist/CodeBlock-Dg56j2ut.cjs +223 -0
  54. package/dist/CodeBlock-Dt6nUQoo.js +221 -0
  55. package/dist/CustomTooltipContent-Bxynuq7T.js +33 -0
  56. package/dist/CustomTooltipContent-BzjOSA0P.cjs +54 -0
  57. package/dist/CustomTooltipContent-C-ZeCNsf.js +33 -0
  58. package/dist/CustomTooltipContent-CON_QrXm.cjs +54 -0
  59. package/dist/CustomTooltipContent-UR_n2QfW.js +33 -0
  60. package/dist/CustomTooltipContent-v1SXjoFn.cjs +54 -0
  61. package/dist/FeatureCard-4n_8a63v.cjs +497 -0
  62. package/dist/FeatureCard-BLiAWT3w.js +485 -0
  63. package/dist/FeatureCard-BXRW97Ef.cjs +497 -0
  64. package/dist/FeatureCard-BhpfmPPx.js +485 -0
  65. package/dist/FeatureCard-CDgLlWE2.js +485 -0
  66. package/dist/FeatureCard-CgQ8lq-I.cjs +497 -0
  67. package/dist/FeatureCard-Cp8cVC6T.cjs +497 -0
  68. package/dist/FeatureCard-D-5yS_OD.cjs +497 -0
  69. package/dist/FeatureCard-D0MwrTRP.js +485 -0
  70. package/dist/FeatureCard-uE5i0in_.js +485 -0
  71. package/dist/ImageWithFallback-BOh1FM3I.js +98 -0
  72. package/dist/ImageWithFallback-BQO7952C.js +98 -0
  73. package/dist/ImageWithFallback-BkMXHCvV.cjs +122 -0
  74. package/dist/ImageWithFallback-C72nwszA.cjs +122 -0
  75. package/dist/ImageWithFallback-CCUMDuEC.cjs +122 -0
  76. package/dist/ImageWithFallback-CfkAkctC.cjs +122 -0
  77. package/dist/ImageWithFallback-DQD5TTlY.js +98 -0
  78. package/dist/ImageWithFallback-bw-CxxDc.js +98 -0
  79. package/dist/LanguageContext-CX_zdxre.js +1912 -0
  80. package/dist/LanguageContext-CjhSSqJT.cjs +1917 -0
  81. package/dist/LanguageSelector-1XhM7R1i.cjs +48 -0
  82. package/dist/LanguageSelector-B3ZVNc55.js +46 -0
  83. package/dist/LanguageSelector-BR3Ss0k_.js +46 -0
  84. package/dist/LanguageSelector-CHOLPmHr.cjs +48 -0
  85. package/dist/LanguageSelector-Cj6zl0CM.js +46 -0
  86. package/dist/LanguageSelector-DK1zD4VH.cjs +48 -0
  87. package/dist/LanguageSelector-DseCAa0p.cjs +48 -0
  88. package/dist/LanguageSelector-Du09anfM.js +46 -0
  89. package/dist/LanguageSelector-PZHlFfEs.cjs +48 -0
  90. package/dist/LanguageSelector-SB-NJSmx.js +46 -0
  91. package/dist/PasswordStrengthMeter-B3S6i_k4.cjs +179 -0
  92. package/dist/PasswordStrengthMeter-B8ky1nJI.js +173 -0
  93. package/dist/PasswordStrengthMeter-C5VEopH-.cjs +179 -0
  94. package/dist/PasswordStrengthMeter-C8irs0G1.js +173 -0
  95. package/dist/PasswordStrengthMeter-CIm7_jNT.js +173 -0
  96. package/dist/PasswordStrengthMeter-DKSAbQzu.js +173 -0
  97. package/dist/PasswordStrengthMeter-DxLu54p-.cjs +179 -0
  98. package/dist/PasswordStrengthMeter-MLzhax-d.cjs +179 -0
  99. package/dist/PasswordStrengthMeter-k-e9Pole.cjs +179 -0
  100. package/dist/PasswordStrengthMeter-pAi2viq5.js +173 -0
  101. package/dist/ThemeContext-CEY9pCn-.cjs +86 -0
  102. package/dist/ThemeContext-Dk3tH7Ym.js +83 -0
  103. package/dist/ThemeToggle-4pQbVkpK.js +215 -0
  104. package/dist/ThemeToggle-B6rIVLvC.cjs +219 -0
  105. package/dist/ThemeToggle-BUOTps5l.cjs +219 -0
  106. package/dist/ThemeToggle-BpM_iHh8.js +215 -0
  107. package/dist/ThemeToggle-CO5mPl6e.js +215 -0
  108. package/dist/ThemeToggle-CZRlv0d4.cjs +219 -0
  109. package/dist/ThemeToggle-DbZQ0JSV.cjs +219 -0
  110. package/dist/ThemeToggle-vLXEq2TS.js +215 -0
  111. package/dist/VerifyEmailPage-BKiaw2YK.js +2954 -0
  112. package/dist/VerifyEmailPage-BTjI2c9n.js +2954 -0
  113. package/dist/VerifyEmailPage-Beaicc6V.cjs +2963 -0
  114. package/dist/VerifyEmailPage-BehPBp0F.cjs +2963 -0
  115. package/dist/VerifyEmailPage-CJGfDSOx.js +2954 -0
  116. package/dist/VerifyEmailPage-DOlZUNc0.cjs +2963 -0
  117. package/dist/VerifyEmailPage-DQPRl_RJ.js +2954 -0
  118. package/dist/VerifyEmailPage-Djb6hthM.js +2954 -0
  119. package/dist/VerifyEmailPage-DrdqMbLC.cjs +2963 -0
  120. package/dist/VerifyEmailPage-hyDfeCRa.cjs +2963 -0
  121. package/dist/VerifyEmailPage-kKSXl8vu.cjs +2963 -0
  122. package/dist/VerifyEmailPage-w4N-bDhU.js +2954 -0
  123. package/dist/XerticaOrbe-B9tegV1n.js +1925 -0
  124. package/dist/XerticaOrbe-BQ_NjwPS.cjs +1927 -0
  125. package/dist/XerticaOrbe-BdUX171Q.cjs +1927 -0
  126. package/dist/XerticaOrbe-BdlKXRHU.cjs +1927 -0
  127. package/dist/XerticaOrbe-CByPAFE5.js +1925 -0
  128. package/dist/XerticaOrbe-Ccu9eFMu.js +1925 -0
  129. package/dist/XerticaProvider-BVid1oqn.cjs +46 -0
  130. package/dist/XerticaProvider-BijpC_py.cjs +46 -0
  131. package/dist/XerticaProvider-Bz_YfNY4.js +44 -0
  132. package/dist/XerticaProvider-C2wDRQhT.js +44 -0
  133. package/dist/XerticaProvider-CtvYfRbZ.cjs +46 -0
  134. package/dist/XerticaProvider-DMiyExQS.js +44 -0
  135. package/dist/XerticaProvider-DP2lzKip.cjs +46 -0
  136. package/dist/XerticaProvider-De79hhgp.js +44 -0
  137. package/dist/XerticaProvider-Dkk1Vbsy.js +44 -0
  138. package/dist/XerticaProvider-EdzH9Nrw.cjs +46 -0
  139. package/dist/alert-dialog-9XYAn5z5.js +819 -0
  140. package/dist/alert-dialog-B281hud0.cjs +872 -0
  141. package/dist/alert-dialog-BVWbrcrw.cjs +872 -0
  142. package/dist/alert-dialog-B_Ko1xpu.js +819 -0
  143. package/dist/alert-dialog-BzIcTKn_.cjs +872 -0
  144. package/dist/alert-dialog-CIxE8T-J.cjs +872 -0
  145. package/dist/alert-dialog-DW-_iPuf.js +819 -0
  146. package/dist/alert-dialog-kty5pxQp.js +819 -0
  147. package/dist/assistant.cjs.js +2 -2
  148. package/dist/assistant.es.js +2 -2
  149. package/dist/avatar-BJwIMbZ_.js +54 -0
  150. package/dist/avatar-C4Zh5fAJ.cjs +77 -0
  151. package/dist/avatar-CTEUKZUT.js +54 -0
  152. package/dist/avatar-D1bZhlhL.cjs +77 -0
  153. package/dist/avatar-DKIxHeKG.cjs +77 -0
  154. package/dist/avatar-v16eZC8p.js +54 -0
  155. package/dist/blocks.cjs.js +7 -3
  156. package/dist/blocks.es.js +2 -2
  157. package/dist/brand.cjs.js +8 -7
  158. package/dist/brand.es.js +4 -3
  159. package/dist/breadcrumb-B1mUiaWB.cjs +98 -0
  160. package/dist/breadcrumb-BIRgmem-.js +90 -0
  161. package/dist/breadcrumb-BSng-oUf.cjs +98 -0
  162. package/dist/breadcrumb-CES7furU.js +90 -0
  163. package/dist/breadcrumb-CP275kbF.cjs +98 -0
  164. package/dist/breadcrumb-D7hr1EvJ.js +90 -0
  165. package/dist/button-BYtQygX5.cjs +85 -0
  166. package/dist/button-BnAW-a_w.js +62 -0
  167. package/dist/button-CUPDQews.js +62 -0
  168. package/dist/button-DFa-rtPy.js +62 -0
  169. package/dist/button-DV1G6XrI.cjs +85 -0
  170. package/dist/button-Kd9Fj53S.cjs +85 -0
  171. package/dist/cli.js +94 -10
  172. package/dist/components/blocks/auth/AuthPageShell.d.ts +12 -0
  173. package/dist/components/blocks/auth/PasswordStrengthMeter.d.ts +7 -0
  174. package/dist/components/blocks/auth/SocialLoginButtons.d.ts +8 -0
  175. package/dist/components/blocks/auth/index.d.ts +8 -0
  176. package/dist/components/blocks/auth/usePasswordStrength.d.ts +3 -0
  177. package/dist/components/blocks/index.d.ts +1 -0
  178. package/dist/components/pages/reset-password-page/ResetPasswordPage.d.ts +1 -1
  179. package/dist/components/ui/card/card.d.ts +11 -1
  180. package/dist/contexts/theme-data.d.ts +15 -1
  181. package/dist/dropdown-menu-B9wTZ0uX.cjs +225 -0
  182. package/dist/dropdown-menu-BeBtZOrs.cjs +225 -0
  183. package/dist/dropdown-menu-D7mRDGis.js +190 -0
  184. package/dist/dropdown-menu-D8ZmqHB7.js +190 -0
  185. package/dist/dropdown-menu-_AWBeOts.cjs +225 -0
  186. package/dist/dropdown-menu-qGzLO_HO.js +190 -0
  187. package/dist/hooks.cjs.js +6 -5
  188. package/dist/hooks.es.js +3 -2
  189. package/dist/index.cjs.js +42 -37
  190. package/dist/index.es.js +25 -24
  191. package/dist/input-B53E6K8G.js +65 -0
  192. package/dist/input-B7Q7vcoG.cjs +91 -0
  193. package/dist/input-BysqLMK1.js +65 -0
  194. package/dist/input-CStdFsaH.cjs +91 -0
  195. package/dist/input-CXYth6nW.cjs +91 -0
  196. package/dist/input-CX_fWE2D.cjs +91 -0
  197. package/dist/input-CitUVnEn.js +65 -0
  198. package/dist/input-wkBYTp-B.js +65 -0
  199. package/dist/layout.cjs.js +1 -1
  200. package/dist/layout.es.js +1 -1
  201. package/dist/media.cjs.js +1 -1
  202. package/dist/media.es.js +1 -1
  203. package/dist/pages.cjs.js +1 -1
  204. package/dist/pages.es.js +1 -1
  205. package/dist/rich-text-editor-BHiRUzWZ.js +2894 -0
  206. package/dist/rich-text-editor-BWMRUMJ2.js +2894 -0
  207. package/dist/rich-text-editor-BoE6Ks5x.cjs +2968 -0
  208. package/dist/rich-text-editor-BzuEwR_X.js +2894 -0
  209. package/dist/rich-text-editor-CcopGbQ-.js +2894 -0
  210. package/dist/rich-text-editor-CmxSRJIi.cjs +2968 -0
  211. package/dist/rich-text-editor-DyYAu28v.cjs +2968 -0
  212. package/dist/rich-text-editor-JS_eMQCC.cjs +2968 -0
  213. package/dist/select-BNfFJwYp.js +130 -0
  214. package/dist/select-CN8IDjGB.js +130 -0
  215. package/dist/select-CwRzQHA7.cjs +161 -0
  216. package/dist/select-CwgD3nMD.js +130 -0
  217. package/dist/select-DEHUm_AC.cjs +161 -0
  218. package/dist/select-DJDgpVm3.cjs +161 -0
  219. package/dist/select-DSLWQLmo.cjs +161 -0
  220. package/dist/select-YFx0wCYT.js +130 -0
  221. package/dist/sidebar-B7YzwAp3.js +1151 -0
  222. package/dist/sidebar-BF7JEL7U.js +1151 -0
  223. package/dist/sidebar-Bjr2doX5.cjs +1154 -0
  224. package/dist/sidebar-CMrNCmhb.cjs +1154 -0
  225. package/dist/sidebar-CO9x8AVn.cjs +1154 -0
  226. package/dist/sidebar-Cb0U7NsV.cjs +1154 -0
  227. package/dist/sidebar-Cn_KB4c6.js +1151 -0
  228. package/dist/sidebar-Db_C30ph.cjs +1154 -0
  229. package/dist/sidebar-miPHsRz4.js +1151 -0
  230. package/dist/sidebar-pZF8mLLu.js +1151 -0
  231. package/dist/skeleton-BX2XTjMO.cjs +87 -0
  232. package/dist/skeleton-BoLK0iV7.js +78 -0
  233. package/dist/skeleton-Bysq8yeB.js +79 -0
  234. package/dist/skeleton-C7nspYnC.cjs +87 -0
  235. package/dist/skeleton-DL4V5Vub.cjs +87 -0
  236. package/dist/skeleton-DSMhdqp1.js +78 -0
  237. package/dist/skeleton-DhJswvTW.cjs +88 -0
  238. package/dist/skeleton-DicUfozA.js +78 -0
  239. package/dist/slider-77T_Eqw4.js +56 -0
  240. package/dist/slider-B6puMLXS.cjs +78 -0
  241. package/dist/slider-B785N7lZ.js +56 -0
  242. package/dist/slider-C6yssKyZ.cjs +78 -0
  243. package/dist/slider-CKZO9gVh.cjs +78 -0
  244. package/dist/slider-Cn-ImSze.js +56 -0
  245. package/dist/tooltip-BiJVGob3.cjs +72 -0
  246. package/dist/tooltip-D2OPn-1v.cjs +72 -0
  247. package/dist/tooltip-DlCHQuh1.js +48 -0
  248. package/dist/tooltip-fQ2Va0G8.cjs +72 -0
  249. package/dist/tooltip-oAw00FX2.js +48 -0
  250. package/dist/tooltip-u147L0wD.js +48 -0
  251. package/dist/ui.cjs.js +17 -17
  252. package/dist/ui.es.js +13 -13
  253. package/dist/xertica-assistant-8fh8KA2K.js +2173 -0
  254. package/dist/xertica-assistant-BjfG3zyL.cjs +2180 -0
  255. package/dist/xertica-assistant-C2A0R94j.js +2173 -0
  256. package/dist/xertica-assistant-CSUprvTR.js +2173 -0
  257. package/dist/xertica-assistant-Caubm0HR.js +2173 -0
  258. package/dist/xertica-assistant-DEexPM2u.cjs +2180 -0
  259. package/dist/xertica-assistant-Dh1dmEx3.cjs +2180 -0
  260. package/dist/xertica-assistant-GGcyHEZG.cjs +2180 -0
  261. package/dist/xertica-assistant-USefPe2L.js +2173 -0
  262. package/dist/xertica-assistant-cwSHFwfv.cjs +2180 -0
  263. package/dist/xertica-ui.css +1 -1
  264. package/docs/i18n.md +2 -0
  265. package/package.json +3 -2
  266. package/styles/xertica/base.css +38 -0
  267. package/styles/xertica/tokens.css +9 -0
  268. package/templates/package.json +2 -2
  269. package/templates/src/app/App.tsx +5 -1
  270. package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -2
  271. package/templates/src/features/auth/ui/LoginContent.tsx +1 -2
  272. package/templates/src/features/auth/ui/ResetPasswordContent.tsx +3 -57
  273. package/templates/src/features/auth/ui/VerifyEmailContent.tsx +1 -2
  274. package/templates/src/features/home/index.ts +0 -10
  275. package/templates/src/features/home/ui/HomeContent.tsx +19 -74
  276. package/templates/src/features/template/index.ts +0 -4
  277. package/templates/src/features/template/ui/TemplateContent.tsx +397 -1107
  278. package/templates/src/locales/en/pages/templates.json +70 -270
  279. package/templates/src/locales/es/pages/templates.json +77 -277
  280. package/templates/src/locales/pt-BR/pages/templates.json +77 -277
  281. package/templates/src/styles/xertica/tokens.css +9 -0
  282. package/templates/vite.config.ts +25 -0
  283. package/templates/src/features/auth/ui/SocialLoginButtons.tsx +0 -78
  284. package/templates/src/features/home/data/mock.ts +0 -41
  285. package/templates/src/features/home/hooks/useFeatureCards.ts +0 -20
  286. package/templates/src/features/home/store/dashboardStore.ts +0 -25
  287. package/templates/src/features/template/ui/CrudTemplate.tsx +0 -112
  288. package/templates/src/features/template/ui/DashboardTemplate.tsx +0 -110
  289. package/templates/src/features/template/ui/FormTemplate.tsx +0 -117
  290. package/templates/src/features/template/ui/LoginTemplate.tsx +0 -59
  291. package/templates/src/locales/en/pages/crudTemplate.json +0 -25
  292. package/templates/src/locales/en/pages/dashboardTemplate.json +0 -20
  293. package/templates/src/locales/en/pages/formTemplate.json +0 -16
  294. package/templates/src/locales/en/pages/loginTemplate.json +0 -9
  295. package/templates/src/locales/es/pages/crudTemplate.json +0 -25
  296. package/templates/src/locales/es/pages/dashboardTemplate.json +0 -20
  297. package/templates/src/locales/es/pages/formTemplate.json +0 -16
  298. package/templates/src/locales/es/pages/loginTemplate.json +0 -9
  299. package/templates/src/locales/pt-BR/pages/crudTemplate.json +0 -25
  300. package/templates/src/locales/pt-BR/pages/dashboardTemplate.json +0 -20
  301. package/templates/src/locales/pt-BR/pages/formTemplate.json +0 -16
  302. package/templates/src/locales/pt-BR/pages/loginTemplate.json +0 -9
  303. package/templates/vite.config.d.ts +0 -2
  304. 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
- [![npm version](https://img.shields.io/badge/npm-2.6.0-blue)](https://www.npmjs.com/package/xertica-ui)
5
+ [![npm version](https://img.shields.io/badge/npm-2.7.0-blue)](https://www.npmjs.com/package/xertica-ui)
6
6
  [![license](https://img.shields.io/badge/license-proprietary-red)](./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
- Nine 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`.
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
- | ![#2C275B](https://img.shields.io/badge/-%232C275B-2C275B?style=flat-square) Xertica | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
360
+ | ![#2C275B](https://img.shields.io/badge/-%232C275B-2C275B?style=flat-square) Xertica Classic | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
361
+ | ![#1E1E1E](https://img.shields.io/badge/-%231E1E1E-1E1E1E?style=flat-square) Xertica | `xertica` | `#1E1E1E` | `#1E1E1E` | `#141311` |
361
362
  | ![#18181B](https://img.shields.io/badge/-%2318181B-18181B?style=flat-square) Zinc | `zinc` | `#18181B` | `#18181B` | `#05050d` |
362
363
  | ![#0F172A](https://img.shields.io/badge/-%230F172A-0F172A?style=flat-square) Slate | `slate` | `#0F172A` | `#0F172A` | `#05050d` |
363
364
  | ![#2563EB](https://img.shields.io/badge/-%232563EB-2563EB?style=flat-square) 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 9 themes
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 (unchanged) |
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 | Description |
471
- | -------------------- | -------------------------------------- |
472
- | `npm run dev` | Start development server |
473
- | `npm run build` | Production bundle |
474
- | `npm run storybook` | Launch component library documentation |
475
- | `npm run test` | Run unit tests via Vitest |
476
- | `npm run type-check` | TypeScript validation |
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
 
@@ -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.parentElement;
261
- if (!container) {
262
- messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
263
- return;
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-background relative`,
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 'xertica-ui/ui';
3
- import { LanguageSelector } from 'xertica-ui/brand';
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
+ }
@@ -1 +1,2 @@
1
1
  export * from './card-patterns';
2
+ export * from './auth';
@@ -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 { ImageWithFallback } from '../../figma/ImageWithFallback';
7
- import { LanguageSelector } from '../../brand/language-selector';
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
- <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" />
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
- {/* 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} />
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
- {/* 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>
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
- </div>
186
- </div>
89
+ </form>
90
+
91
+ <SocialLoginButtons onSocialLogin={handleSocialLogin} />
92
+ </AuthPageShell>
187
93
  );
188
94
  }