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
@@ -3,9 +3,7 @@ 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 { Lock } from 'lucide-react';
6
+ import { AuthPageShell, SocialLoginButtons } from '../../blocks/auth';
9
7
  import { useNavigate } from 'react-router-dom';
10
8
  import { useTranslation } from 'react-i18next';
11
9
 
@@ -64,155 +62,64 @@ export function LoginPage({ onLogin }: LoginPageProps) {
64
62
  };
65
63
 
66
64
  return (
67
- <div className="h-screen w-full flex overflow-y-auto">
68
- {/* Left side - Full background image */}
69
- <div className="hidden lg:flex lg:flex-1 relative overflow-hidden">
70
- {/* Background image filling all space */}
71
- <ImageWithFallback
72
- src="https://images.unsplash.com/photo-1551434678-e076c223a692?w=1200&h=800&fit=crop&auto=format"
73
- alt={t('login.heroImageAlt')}
74
- className="absolute inset-0 w-full h-full object-cover"
75
- />
76
-
77
- {/* Gradient overlay */}
78
- <div className="absolute inset-0 bg-[image:var(--gradient-diagonal)] opacity-80" />
65
+ <AuthPageShell
66
+ imageSrc="https://images.unsplash.com/photo-1551434678-e076c223a692?w=1200&h=800&fit=crop&auto=format"
67
+ imageAlt={t('login.heroImageAlt')}
68
+ >
69
+ <div className="text-center">
70
+ <div className="flex items-center justify-center mb-4">
71
+ <XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
72
+ </div>
73
+ <h2 className="text-sm text-muted-foreground">{t('login.heading')}</h2>
79
74
  </div>
80
75
 
81
- {/* Right side - Form */}
82
- <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">
83
- {/* Language selector in the top right corner */}
84
- <div className="absolute top-4 right-4 z-20">
85
- <LanguageSelector variant="minimal" showIcon={false} />
76
+ <form className="space-y-6" onSubmit={handleSubmit}>
77
+ <div className="space-y-2">
78
+ <Label htmlFor="email">{t('login.emailLabel')}</Label>
79
+ <Input
80
+ id="email"
81
+ name="email"
82
+ type="email"
83
+ required
84
+ className="w-full"
85
+ placeholder={t('login.emailPlaceholder')}
86
+ value={email}
87
+ onChange={e => setEmail(e.target.value)}
88
+ />
86
89
  </div>
87
90
 
88
- {/* Mobile background gradient */}
89
- <div className="absolute inset-0 lg:hidden bg-[image:var(--gradient-diagonal)] opacity-10 dark:opacity-5" />
90
- <div className="w-full max-w-sm space-y-6 relative z-10">
91
- {/* Form header */}
92
- <div className="text-center">
93
- <div className="flex items-center justify-center mb-4">
94
- <XerticaLogo
95
- className="h-12 w-auto text-primary dark:text-foreground"
96
- variant="theme"
97
- />
98
- </div>
99
- <h2 className="text-sm text-muted-foreground">{t('login.heading')}</h2>
100
- </div>
101
-
102
- {/* Form */}
103
- <form className="space-y-6" onSubmit={handleSubmit}>
104
- <div className="space-y-2">
105
- <Label htmlFor="email">{t('login.emailLabel')}</Label>
106
- <Input
107
- id="email"
108
- name="email"
109
- type="email"
110
- required
111
- className="w-full"
112
- placeholder={t('login.emailPlaceholder')}
113
- value={email}
114
- onChange={e => setEmail(e.target.value)}
115
- />
116
- </div>
117
-
118
- <div className="space-y-2">
119
- <Label htmlFor="password">{t('login.passwordLabel')}</Label>
120
- <Input
121
- id="password"
122
- name="password"
123
- type="password"
124
- required
125
- className="w-full"
126
- placeholder="••••••••"
127
- value={password}
128
- onChange={e => setPassword(e.target.value)}
129
- />
130
- </div>
131
-
132
- {error && <div className="text-destructive text-sm text-center">{error}</div>}
133
-
134
- <Button type="submit" className="w-full" disabled={isLoading}>
135
- {isLoading ? t('login.submitting') : t('login.submit')}
136
- </Button>
137
-
138
- {/* Password recovery link */}
139
- <div className="text-center">
140
- <button
141
- type="button"
142
- onClick={() => navigate('/forgot-password')}
143
- className="text-sm text-primary hover:opacity-80 transition-colors"
144
- >
145
- {t('login.forgotPassword')}
146
- </button>
147
- </div>
148
- </form>
149
-
150
- {/* Divider */}
151
- <div className="relative">
152
- <div className="absolute inset-0 flex items-center">
153
- <div className="w-full border-t border-border"></div>
154
- </div>
155
- <div className="relative flex justify-center text-sm">
156
- <span className="bg-muted px-2 text-muted-foreground">
157
- {t('login.orContinueWith')}
158
- </span>
159
- </div>
160
- </div>
91
+ <div className="space-y-2">
92
+ <Label htmlFor="password">{t('login.passwordLabel')}</Label>
93
+ <Input
94
+ id="password"
95
+ name="password"
96
+ type="password"
97
+ required
98
+ className="w-full"
99
+ placeholder="••••••••"
100
+ value={password}
101
+ onChange={e => setPassword(e.target.value)}
102
+ />
103
+ </div>
161
104
 
162
- {/* Social/SSO login options */}
163
- <div className="space-y-3">
164
- {/* Google */}
165
- <Button
166
- type="button"
167
- variant="outline"
168
- className="w-full justify-center"
169
- onClick={() => handleSocialLogin('Google')}
170
- >
171
- <svg className="w-5 h-5 mr-2" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
172
- <path
173
- fill="#EA4335"
174
- 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"
175
- ></path>
176
- <path
177
- fill="#4285F4"
178
- 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"
179
- ></path>
180
- <path
181
- fill="#FBBC05"
182
- 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"
183
- ></path>
184
- <path
185
- fill="#34A853"
186
- 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"
187
- ></path>
188
- <path fill="none" d="M0 0h48v48H0z"></path>
189
- </svg>
190
- <span>{t('login.signInWithGoogle')}</span>
191
- </Button>
105
+ {error && <div className="text-destructive text-sm text-center">{error}</div>}
192
106
 
193
- {/* MT Login */}
194
- <Button
195
- type="button"
196
- variant="outline"
197
- className="w-full justify-center"
198
- onClick={() => handleSocialLogin('MT Login')}
199
- >
200
- <Lock className="w-5 h-5 mr-2 text-[var(--chart-4)]" />
201
- <span>{t('login.signInWithMTLogin')}</span>
202
- </Button>
107
+ <Button type="submit" className="w-full" disabled={isLoading}>
108
+ {isLoading ? t('login.submitting') : t('login.submit')}
109
+ </Button>
203
110
 
204
- {/* gov.br */}
205
- <Button
206
- type="button"
207
- variant="outline"
208
- className="w-full justify-center font-normal"
209
- onClick={() => handleSocialLogin('gov.br')}
210
- >
211
- <span>{t('login.signInWithGovBr')}</span>
212
- </Button>
213
- </div>
111
+ <div className="text-center">
112
+ <button
113
+ type="button"
114
+ onClick={() => navigate('/forgot-password')}
115
+ className="text-sm text-primary hover:opacity-80 transition-colors"
116
+ >
117
+ {t('login.forgotPassword')}
118
+ </button>
214
119
  </div>
215
- </div>
216
- </div>
120
+ </form>
121
+
122
+ <SocialLoginButtons onSocialLogin={handleSocialLogin} />
123
+ </AuthPageShell>
217
124
  );
218
125
  }
@@ -3,8 +3,7 @@ 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';
6
+ import { AuthPageShell, usePasswordStrength, PasswordStrengthMeter } from '../../blocks/auth';
8
7
  import { ArrowLeft, CheckCircle2, AlertCircle } from 'lucide-react';
9
8
  import { useNavigate } from 'react-router-dom';
10
9
  import { useTranslation } from 'react-i18next';
@@ -18,7 +17,7 @@ import { useTranslation } from 'react-i18next';
18
17
  * password requirements.
19
18
  *
20
19
  * @ai-rules
21
- * 1. Validation: Includes a `checkPasswordStrength` helper that categorizes passwords as 'weak', 'medium', or 'strong'.
20
+ * 1. Validation: Uses the shared `usePasswordStrength` hook to categorize passwords as 'weak', 'medium', or 'strong'.
22
21
  * 2. Feedback: Uses `CheckCircle2` icons to show real-time progress on password requirements (length, matching).
23
22
  * 3. Navigation: Redirects to `/login` with a `resetSuccess` flag after a successful password update.
24
23
  */
@@ -29,29 +28,10 @@ export function ResetPasswordPage() {
29
28
  const [confirmPassword, setConfirmPassword] = useState('');
30
29
  const [isLoading, setIsLoading] = useState(false);
31
30
  const [error, setError] = useState('');
32
- const [passwordStrength, setPasswordStrength] = useState<'weak' | 'medium' | 'strong' | null>(
33
- null
34
- );
35
-
36
- // Password strength validation
37
- const checkPasswordStrength = (pwd: string) => {
38
- if (pwd.length === 0) {
39
- setPasswordStrength(null);
40
- return;
41
- }
42
-
43
- if (pwd.length < 6) {
44
- setPasswordStrength('weak');
45
- } else if (pwd.length < 10) {
46
- setPasswordStrength('medium');
47
- } else {
48
- setPasswordStrength('strong');
49
- }
50
- };
31
+ const passwordStrength = usePasswordStrength(password);
51
32
 
52
33
  const handlePasswordChange = (value: string) => {
53
34
  setPassword(value);
54
- checkPasswordStrength(value);
55
35
  setError('');
56
36
  };
57
37
 
@@ -81,163 +61,93 @@ export function ResetPasswordPage() {
81
61
  setIsLoading(false);
82
62
  };
83
63
 
84
- const getStrengthColor = () => {
85
- switch (passwordStrength) {
86
- case 'weak':
87
- return 'text-destructive';
88
- case 'medium':
89
- return 'text-[var(--chart-3)]';
90
- case 'strong':
91
- return 'text-[var(--chart-2)]';
92
- default:
93
- return 'text-muted-foreground';
94
- }
95
- };
96
-
97
- const getStrengthText = () => {
98
- switch (passwordStrength) {
99
- case 'weak':
100
- return t('resetPassword.strengthWeak');
101
- case 'medium':
102
- return t('resetPassword.strengthMedium');
103
- case 'strong':
104
- return t('resetPassword.strengthStrong');
105
- default:
106
- return '';
107
- }
108
- };
109
-
110
64
  return (
111
- <div className="h-screen w-full flex overflow-y-auto">
112
- {/* Left side - Full background image */}
113
- <div className="hidden lg:flex lg:flex-1 relative overflow-hidden">
114
- {/* Background image filling all space */}
115
- <ImageWithFallback
116
- src="https://images.unsplash.com/photo-1555949963-aa79dcee981c?w=1200&h=800&fit=crop&auto=format"
117
- alt={t('resetPassword.heroImageAlt')}
118
- className="absolute inset-0 w-full h-full object-cover"
119
- />
120
-
121
- {/* Gradient overlay */}
122
- <div className="absolute inset-0 bg-[image:var(--gradient-diagonal)] opacity-80" />
65
+ <AuthPageShell
66
+ imageSrc="https://images.unsplash.com/photo-1555949963-aa79dcee981c?w=1200&h=800&fit=crop&auto=format"
67
+ imageAlt={t('resetPassword.heroImageAlt')}
68
+ >
69
+ <button
70
+ onClick={() => navigate('/login')}
71
+ className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
72
+ >
73
+ <ArrowLeft className="w-4 h-4" />
74
+ <span className="text-small">{t('resetPassword.backToLogin')}</span>
75
+ </button>
76
+
77
+ <div className="text-center">
78
+ <div className="flex items-center justify-center mb-4">
79
+ <XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
80
+ </div>
81
+ <h2 className="text-sm text-muted-foreground">{t('resetPassword.heading')}</h2>
82
+ <p className="mt-2 text-muted-foreground">{t('resetPassword.subheading')}</p>
123
83
  </div>
124
84
 
125
- {/* Right side - Form */}
126
- <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">
127
- {/* Language selector in the top right corner */}
128
- <div className="absolute top-4 right-4 z-20">
129
- <LanguageSelector variant="minimal" showIcon={false} />
85
+ <form className="space-y-6" onSubmit={handleSubmit}>
86
+ <div className="space-y-2">
87
+ <Label htmlFor="password">{t('resetPassword.newPasswordLabel')}</Label>
88
+ <Input
89
+ id="password"
90
+ name="password"
91
+ type="password"
92
+ required
93
+ className="w-full"
94
+ placeholder="••••••••"
95
+ value={password}
96
+ onChange={e => handlePasswordChange(e.target.value)}
97
+ />
98
+ <PasswordStrengthMeter strength={passwordStrength} />
130
99
  </div>
131
100
 
132
- {/* Mobile background gradient */}
133
- <div className="absolute inset-0 lg:hidden bg-[image:var(--gradient-diagonal)] opacity-10 dark:opacity-5" />
134
-
135
- <div className="w-full max-w-sm space-y-6 relative z-10">
136
- {/* Botão voltar */}
137
- <button
138
- onClick={() => navigate('/login')}
139
- className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
140
- >
141
- <ArrowLeft className="w-4 h-4" />
142
- <span className="text-small">{t('resetPassword.backToLogin')}</span>
143
- </button>
144
-
145
- {/* Header do formulário */}
146
- <div className="text-center">
147
- <div className="flex items-center justify-center mb-4">
148
- <XerticaLogo
149
- className="h-12 w-auto text-primary dark:text-foreground"
150
- variant="theme"
151
- />
152
- </div>
153
- <h2 className="text-sm text-muted-foreground">{t('resetPassword.heading')}</h2>
154
- <p className="mt-2 text-muted-foreground">{t('resetPassword.subheading')}</p>
155
- </div>
101
+ <div className="space-y-2">
102
+ <Label htmlFor="confirmPassword">{t('resetPassword.confirmPasswordLabel')}</Label>
103
+ <Input
104
+ id="confirmPassword"
105
+ name="confirmPassword"
106
+ type="password"
107
+ required
108
+ className="w-full"
109
+ placeholder="••••••••"
110
+ value={confirmPassword}
111
+ onChange={e => {
112
+ setConfirmPassword(e.target.value);
113
+ setError('');
114
+ }}
115
+ />
116
+ </div>
156
117
 
157
- {/* Form */}
158
- <form className="space-y-6" onSubmit={handleSubmit}>
159
- <div className="space-y-2">
160
- <Label htmlFor="password">{t('resetPassword.newPasswordLabel')}</Label>
161
- <Input
162
- id="password"
163
- name="password"
164
- type="password"
165
- required
166
- className="w-full"
167
- placeholder="••••••••"
168
- value={password}
169
- onChange={e => handlePasswordChange(e.target.value)}
118
+ <div className="bg-accent rounded-[var(--radius)] p-4 space-y-2">
119
+ <p className="text-sm">{t('resetPassword.requirements')}</p>
120
+ <div className="space-y-1">
121
+ <div className="flex items-center gap-2">
122
+ <CheckCircle2
123
+ className={`w-4 h-4 ${password.length >= 6 ? 'text-[var(--chart-2)]' : 'text-muted-foreground'}`}
170
124
  />
171
- {passwordStrength && (
172
- <div className="flex items-center gap-2">
173
- <div className="flex-1 h-1 bg-muted rounded-full overflow-hidden">
174
- <div
175
- className={`h-full transition-all duration-300 ${
176
- passwordStrength === 'weak'
177
- ? 'w-1/3 bg-destructive'
178
- : passwordStrength === 'medium'
179
- ? 'w-2/3 bg-[var(--chart-3)]'
180
- : 'w-full bg-[var(--chart-2)]'
181
- }`}
182
- />
183
- </div>
184
- <span className={`text-small ${getStrengthColor()}`}>{getStrengthText()}</span>
185
- </div>
186
- )}
125
+ <span className="text-sm text-muted-foreground">
126
+ {t('resetPassword.requirementMinChars')}
127
+ </span>
187
128
  </div>
188
-
189
- <div className="space-y-2">
190
- <Label htmlFor="confirmPassword">{t('resetPassword.confirmPasswordLabel')}</Label>
191
- <Input
192
- id="confirmPassword"
193
- name="confirmPassword"
194
- type="password"
195
- required
196
- className="w-full"
197
- placeholder="••••••••"
198
- value={confirmPassword}
199
- onChange={e => {
200
- setConfirmPassword(e.target.value);
201
- setError('');
202
- }}
129
+ <div className="flex items-center gap-2">
130
+ <CheckCircle2
131
+ className={`w-4 h-4 ${password === confirmPassword && password.length > 0 ? 'text-[var(--chart-2)]' : 'text-muted-foreground'}`}
203
132
  />
133
+ <span className="text-sm text-muted-foreground">
134
+ {t('resetPassword.requirementMatch')}
135
+ </span>
204
136
  </div>
137
+ </div>
138
+ </div>
205
139
 
206
- <div className="bg-accent rounded-[var(--radius)] p-4 space-y-2">
207
- <p className="text-sm">{t('resetPassword.requirements')}</p>
208
- <div className="space-y-1">
209
- <div className="flex items-center gap-2">
210
- <CheckCircle2
211
- className={`w-4 h-4 ${password.length >= 6 ? 'text-[var(--chart-2)]' : 'text-muted-foreground'}`}
212
- />
213
- <span className="text-sm text-muted-foreground">
214
- {t('resetPassword.requirementMinChars')}
215
- </span>
216
- </div>
217
- <div className="flex items-center gap-2">
218
- <CheckCircle2
219
- className={`w-4 h-4 ${password === confirmPassword && password.length > 0 ? 'text-[var(--chart-2)]' : 'text-muted-foreground'}`}
220
- />
221
- <span className="text-sm text-muted-foreground">
222
- {t('resetPassword.requirementMatch')}
223
- </span>
224
- </div>
225
- </div>
226
- </div>
140
+ {error && (
141
+ <div className="flex items-center gap-2 text-destructive bg-destructive/10 rounded-[var(--radius)] p-3">
142
+ <AlertCircle className="w-4 h-4 flex-shrink-0" />
143
+ <span className="text-sm">{error}</span>
144
+ </div>
145
+ )}
227
146
 
228
- {error && (
229
- <div className="flex items-center gap-2 text-destructive bg-destructive/10 rounded-[var(--radius)] p-3">
230
- <AlertCircle className="w-4 h-4 flex-shrink-0" />
231
- <span className="text-sm">{error}</span>
232
- </div>
233
- )}
234
-
235
- <Button type="submit" className="w-full" disabled={isLoading}>
236
- {isLoading ? t('resetPassword.submitting') : t('resetPassword.submit')}
237
- </Button>
238
- </form>
239
- </div>
240
- </div>
241
- </div>
147
+ <Button type="submit" className="w-full" disabled={isLoading}>
148
+ {isLoading ? t('resetPassword.submitting') : t('resetPassword.submit')}
149
+ </Button>
150
+ </form>
151
+ </AuthPageShell>
242
152
  );
243
153
  }