xertica-ui 2.5.2 → 2.6.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 (577) hide show
  1. package/CHANGELOG.md +687 -656
  2. package/README.md +482 -482
  3. package/assets/xertica-logo.svg +37 -37
  4. package/assets/xertica-x-logo.svg +20 -20
  5. package/bin/cli.ts +1293 -1256
  6. package/bin/generate-tokens.ts +256 -262
  7. package/bin/language-config.test.ts +32 -0
  8. package/bin/language-config.ts +36 -11
  9. package/components/Introduction.mdx +121 -121
  10. package/components/assistant/code-block/CodeBlock.tsx +7 -6
  11. package/components/assistant/code-block/code-block.mdx +38 -38
  12. package/components/assistant/code-block/code-block.test.tsx +2 -1
  13. package/components/assistant/formatted-document/FormattedDocument.tsx +3 -0
  14. package/components/assistant/formatted-document/formatted-document.mdx +31 -31
  15. package/components/assistant/formatted-document/formatted-document.test.tsx +5 -4
  16. package/components/assistant/markdown-message/MarkdownMessage.tsx +1 -1
  17. package/components/assistant/markdown-message/markdown-message.mdx +44 -44
  18. package/components/assistant/modern-chat-input/modern-chat-input.mdx +71 -71
  19. package/components/assistant/modern-chat-input/modern-chat-input.test.tsx +3 -2
  20. package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +1 -1
  21. package/components/assistant/xertica-assistant/types.ts +3 -2
  22. package/components/assistant/xertica-assistant/use-assistant.ts +5 -4
  23. package/components/assistant/xertica-assistant/xertica-assistant.mdx +66 -66
  24. package/components/assistant/xertica-assistant/xertica-assistant.test.tsx +2 -1
  25. package/components/blocks/card-patterns/card-patterns.mdx +123 -123
  26. package/components/brand/branding/branding.mdx +27 -27
  27. package/components/brand/language-selector/language-selector.mdx +126 -126
  28. package/components/brand/theme-toggle/theme-toggle.mdx +47 -47
  29. package/components/brand/theme-toggle/theme-toggle.test.tsx +6 -2
  30. package/components/brand/xertica-logo/xertica-logo.mdx +56 -56
  31. package/components/brand/xertica-orbe/xertica-orbe.mdx +38 -38
  32. package/components/brand/xertica-provider/XerticaProvider.tsx +112 -112
  33. package/components/brand/xertica-provider/xertica-provider.mdx +61 -61
  34. package/components/brand/xertica-provider/xertica-provider.test.tsx +25 -13
  35. package/components/brand/xertica-xlogo/xertica-xlogo.mdx +55 -55
  36. package/components/examples/DrawingMapExample.tsx +1 -4
  37. package/components/layout/header/header.mdx +117 -117
  38. package/components/layout/header/header.test.tsx +2 -1
  39. package/components/layout/sidebar/sidebar.mdx +99 -99
  40. package/components/layout/sidebar/sidebar.stories.tsx +1033 -1033
  41. package/components/layout/sidebar/sidebar.test.tsx +2 -1
  42. package/components/layout/sidebar/sidebar.tsx +1509 -1501
  43. package/components/media/audio-player/AudioPlayer.mdx +67 -67
  44. package/components/media/audio-player/AudioPlayer.test.tsx +2 -1
  45. package/components/media/floating-media-wrapper.mdx +74 -74
  46. package/components/media/video-player/VideoPlayer.mdx +102 -102
  47. package/components/media/video-player/VideoPlayer.test.tsx +3 -2
  48. package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +188 -188
  49. package/components/pages/forgot-password-page/forgot-password-page.mdx +60 -60
  50. package/components/pages/forgot-password-page/forgot-password-page.test.tsx +2 -1
  51. package/components/pages/home-content/HomeContent.tsx +2 -3
  52. package/components/pages/home-content/home-content.mdx +62 -62
  53. package/components/pages/home-page/HomePage.stories.tsx +8 -15
  54. package/components/pages/home-page/home-page.mdx +53 -53
  55. package/components/pages/home-page/home-page.test.tsx +2 -1
  56. package/components/pages/login-page/LoginPage.mdx +31 -31
  57. package/components/pages/login-page/LoginPage.tsx +218 -218
  58. package/components/pages/login-page/login-page.test.tsx +2 -1
  59. package/components/pages/reset-password-page/ResetPasswordPage.tsx +243 -243
  60. package/components/pages/reset-password-page/reset-password-page.mdx +63 -63
  61. package/components/pages/template-content/TemplateContent.tsx +2 -3
  62. package/components/pages/template-content/template-content.mdx +61 -61
  63. package/components/pages/template-page/TemplatePage.stories.tsx +32 -32
  64. package/components/pages/template-page/template-page.mdx +53 -53
  65. package/components/pages/template-page/template-page.test.tsx +2 -1
  66. package/components/pages/verify-email-page/VerifyEmailPage.tsx +206 -206
  67. package/components/pages/verify-email-page/verify-email-page.mdx +55 -55
  68. package/components/public-api-smoke.test.tsx +10 -6
  69. package/components/shared/assistant-utils.ts +3 -2
  70. package/components/shared/navigation.ts +2 -2
  71. package/components/ui/accordion/accordion.mdx +29 -29
  72. package/components/ui/alert/alert.mdx +29 -29
  73. package/components/ui/alert-dialog/alert-dialog.mdx +26 -26
  74. package/components/ui/aspect-ratio/aspect-ratio.mdx +25 -25
  75. package/components/ui/assistant-chart/AssistantChart.tsx +8 -2
  76. package/components/ui/assistant-chart/assistant-chart.mdx +25 -25
  77. package/components/ui/avatar/avatar.mdx +26 -26
  78. package/components/ui/badge/badge.mdx +28 -28
  79. package/components/ui/breadcrumb/breadcrumb.mdx +26 -26
  80. package/components/ui/button/button.mdx +29 -29
  81. package/components/ui/calendar/calendar.mdx +26 -26
  82. package/components/ui/card/card.mdx +26 -26
  83. package/components/ui/carousel/carousel.mdx +26 -26
  84. package/components/ui/chart/chart.mdx +26 -26
  85. package/components/ui/chart/chart.test.tsx +178 -178
  86. package/components/ui/chart/chart.tsx +2252 -2245
  87. package/components/ui/checkbox/checkbox.mdx +26 -26
  88. package/components/ui/collapsible/collapsible.mdx +25 -25
  89. package/components/ui/command/command.mdx +26 -26
  90. package/components/ui/context-menu/context-menu.mdx +26 -26
  91. package/components/ui/dialog/dialog.mdx +26 -26
  92. package/components/ui/drawer/drawer.mdx +26 -26
  93. package/components/ui/dropdown-menu/dropdown-menu.mdx +26 -26
  94. package/components/ui/empty/empty.mdx +26 -26
  95. package/components/ui/file-upload/file-upload.mdx +26 -26
  96. package/components/ui/form/form.mdx +20 -20
  97. package/components/ui/google-maps-loader/google-maps-loader.mdx +68 -68
  98. package/components/ui/hover-card/hover-card.mdx +26 -26
  99. package/components/ui/input/input.mdx +29 -29
  100. package/components/ui/input-otp/input-otp.mdx +26 -26
  101. package/components/ui/label/label.mdx +25 -25
  102. package/components/ui/map/map.mdx +26 -26
  103. package/components/ui/map/map.tsx +6 -1
  104. package/components/ui/menubar/menubar.mdx +26 -26
  105. package/components/ui/navigation-menu/navigation-menu.mdx +26 -26
  106. package/components/ui/notification-badge/notification-badge.mdx +26 -26
  107. package/components/ui/page-header/page-header.mdx +44 -44
  108. package/components/ui/pagination/pagination.mdx +26 -26
  109. package/components/ui/popover/popover.mdx +26 -26
  110. package/components/ui/progress/progress.mdx +25 -25
  111. package/components/ui/radio-group/radio-group.mdx +26 -26
  112. package/components/ui/rating/rating.mdx +26 -26
  113. package/components/ui/resizable/resizable.mdx +26 -26
  114. package/components/ui/resizable/resizable.tsx +13 -8
  115. package/components/ui/rich-text-editor/rich-text-editor.mdx +47 -47
  116. package/components/ui/route-map/route-map.mdx +26 -26
  117. package/components/ui/scroll-area/scroll-area.mdx +25 -25
  118. package/components/ui/search/search.mdx +27 -27
  119. package/components/ui/select/select.mdx +26 -26
  120. package/components/ui/separator/separator.mdx +25 -25
  121. package/components/ui/sheet/sheet.mdx +26 -26
  122. package/components/ui/simple-map/simple-map.mdx +25 -25
  123. package/components/ui/skeleton/skeleton.mdx +25 -25
  124. package/components/ui/slider/slider.mdx +26 -26
  125. package/components/ui/sonner/sonner.mdx +26 -26
  126. package/components/ui/stats-card/stats-card.mdx +26 -26
  127. package/components/ui/stats-card/stats-card.stories.tsx +117 -117
  128. package/components/ui/stats-card/stats-card.tsx +109 -109
  129. package/components/ui/stepper/stepper.mdx +26 -26
  130. package/components/ui/switch/switch.mdx +25 -25
  131. package/components/ui/table/table.mdx +27 -27
  132. package/components/ui/tabs/tabs.mdx +26 -26
  133. package/components/ui/textarea/textarea.mdx +25 -25
  134. package/components/ui/timeline/timeline.mdx +26 -26
  135. package/components/ui/toggle/toggle.mdx +26 -26
  136. package/components/ui/toggle-group/toggle-group.mdx +26 -26
  137. package/components/ui/tooltip/tooltip.mdx +26 -26
  138. package/components/ui/tree-view/tree-view.mdx +26 -26
  139. package/components.json +511 -511
  140. package/contexts/BrandColorsContext.tsx +282 -282
  141. package/contexts/i18n-singleton.test.tsx +57 -0
  142. package/contexts/theme-data.ts +391 -391
  143. package/dist/{AssistantChart-DMJJ_Amf.js → AssistantChart-BMNSA_Pk.js} +5 -5
  144. package/dist/{AssistantChart-D0A1FPYt.cjs → AssistantChart-BzM7plyc.cjs} +150 -114
  145. package/dist/{AssistantChart-BXU1ACnh.js → AssistantChart-C842evTi.js} +134 -240
  146. package/dist/{AssistantChart-BZTPJ5dP.cjs → AssistantChart-CVF2uh37.cjs} +5 -5
  147. package/dist/{AssistantChart-COGiOV-g.cjs → AssistantChart-JooMdVQU.cjs} +17 -7
  148. package/dist/{AssistantChart-CWX1OWNM.js → AssistantChart-KHS-tp-L.js} +17 -7
  149. package/dist/{BrandColorsContext-BwY-b6M4.cjs → BrandColorsContext-B4_81eas.cjs} +2 -2
  150. package/dist/{BrandColorsContext-BMRJ04Wf.js → BrandColorsContext-DbK5LnQ_.js} +2 -2
  151. package/dist/{CodeBlock-CjeFavqV.js → CodeBlock-DiKO8ocT.js} +11 -11
  152. package/dist/{CodeBlock-CV6dtsTM.cjs → CodeBlock-ZYThr7Kn.cjs} +12 -9
  153. package/dist/{XerticaXLogo-mqjoBiLI.js → LanguageSelector-CmTj3lWq.js} +85 -82
  154. package/dist/{XerticaXLogo-BX3ueACh.js → LanguageSelector-DxW6o7bt.js} +81 -81
  155. package/dist/{XerticaXLogo-uQgwns_E.cjs → LanguageSelector-Sw73qvWV.cjs} +84 -81
  156. package/dist/{ThemeContext-BgclCB35.js → ThemeContext-BGeh_Kdr.js} +290 -212
  157. package/dist/{ThemeContext-BblcjQup.cjs → ThemeContext-D50irx5T.cjs} +291 -205
  158. package/dist/{VerifyEmailPage-C_Zk6Gen.cjs → VerifyEmailPage-B6bOrybJ.cjs} +17 -17
  159. package/dist/{VerifyEmailPage-B31mCrMc.js → VerifyEmailPage-BRE5Fz59.js} +8 -8
  160. package/dist/{VerifyEmailPage-BJjAMUTW.js → VerifyEmailPage-BcCluouh.js} +151 -78
  161. package/dist/VerifyEmailPage-Bx1sr0uQ.cjs +3298 -0
  162. package/dist/{VerifyEmailPage-X14vhdyl.js → VerifyEmailPage-DUYyXCX9.js} +12 -12
  163. package/dist/VerifyEmailPage-DXXMHbzq.js +3289 -0
  164. package/dist/{VerifyEmailPage-u_Dn7t1U.cjs → VerifyEmailPage-DpYtFk35.cjs} +21 -21
  165. package/dist/{VerifyEmailPage-Cyl55sJb.js → VerifyEmailPage-NXKWW3Dk.js} +134 -64
  166. package/dist/{VerifyEmailPage-Bv8Ah_TK.cjs → VerifyEmailPage-QrCz4Wqr.cjs} +143 -73
  167. package/dist/{VerifyEmailPage-CkBYfsNy.cjs → VerifyEmailPage-ZmQra0Wi.cjs} +160 -87
  168. package/dist/{XerticaProvider-BErr83Bg.js → XerticaProvider-BF3q_myt.js} +5 -4
  169. package/dist/{XerticaProvider-CeS5G_n5.cjs → XerticaProvider-C_JSOz1U.cjs} +2 -2
  170. package/dist/{XerticaProvider-CwOkHxiT.cjs → XerticaProvider-D5zjrqZq.cjs} +5 -4
  171. package/dist/{XerticaProvider-ra2NciRq.js → XerticaProvider-KTGl4Cn1.js} +2 -2
  172. package/dist/{alert-dialog-DqlRW_An.js → alert-dialog-BrpwXKwS.js} +3 -3
  173. package/dist/{alert-dialog-DhwPioBa.cjs → alert-dialog-CZU1Zase.cjs} +3 -3
  174. package/dist/{alert-dialog-KpoabzJb.js → alert-dialog-ClRoLH7l.js} +40 -56
  175. package/dist/{alert-dialog-B4M3vQKS.cjs → alert-dialog-DBBqvU-V.cjs} +53 -38
  176. package/dist/assistant.cjs.js +2 -2
  177. package/dist/assistant.es.js +2 -2
  178. package/dist/brand.cjs.js +6 -6
  179. package/dist/brand.es.js +2 -2
  180. package/dist/cli.js +63 -21
  181. package/dist/components/assistant/code-block/CodeBlock.d.ts +2 -1
  182. package/dist/components/assistant/formatted-document/FormattedDocument.d.ts +2 -1
  183. package/dist/components/assistant/markdown-message/MarkdownMessage.d.ts +2 -1
  184. package/dist/components/assistant/modern-chat-input/ModernChatInput.d.ts +2 -1
  185. package/dist/components/assistant/xertica-assistant/parts/AssistantCollapsedView.d.ts +2 -1
  186. package/dist/components/assistant/xertica-assistant/parts/AssistantConversationList.d.ts +2 -1
  187. package/dist/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.d.ts +2 -1
  188. package/dist/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.d.ts +2 -1
  189. package/dist/components/assistant/xertica-assistant/parts/AssistantHeader.d.ts +2 -1
  190. package/dist/components/assistant/xertica-assistant/parts/AssistantMessageBubble.d.ts +2 -1
  191. package/dist/components/assistant/xertica-assistant/parts/AssistantTabBar.d.ts +2 -1
  192. package/dist/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.d.ts +2 -1
  193. package/dist/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.d.ts +2 -1
  194. package/dist/components/assistant/xertica-assistant/types.d.ts +3 -2
  195. package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +1 -1
  196. package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +2 -1
  197. package/dist/components/blocks/card-patterns/ActivityCard.d.ts +1 -1
  198. package/dist/components/blocks/card-patterns/ActivityCardSkeleton.d.ts +1 -1
  199. package/dist/components/blocks/card-patterns/FeatureCard.d.ts +1 -1
  200. package/dist/components/blocks/card-patterns/FeatureCardSkeleton.d.ts +1 -1
  201. package/dist/components/blocks/card-patterns/NotificationCard.d.ts +1 -1
  202. package/dist/components/blocks/card-patterns/NotificationCardSkeleton.d.ts +1 -1
  203. package/dist/components/blocks/card-patterns/ProfileCard.d.ts +1 -1
  204. package/dist/components/blocks/card-patterns/ProfileCardSkeleton.d.ts +1 -1
  205. package/dist/components/blocks/card-patterns/ProjectCard.d.ts +1 -1
  206. package/dist/components/blocks/card-patterns/ProjectCardSkeleton.d.ts +1 -1
  207. package/dist/components/blocks/card-patterns/QuickActionCard.d.ts +1 -1
  208. package/dist/components/blocks/card-patterns/QuickActionCardSkeleton.d.ts +1 -1
  209. package/dist/components/brand/language-selector/LanguageSelector.d.ts +2 -1
  210. package/dist/components/brand/theme-toggle/ThemeToggle.d.ts +2 -1
  211. package/dist/components/brand/xertica-logo/XerticaLogo.d.ts +2 -1
  212. package/dist/components/brand/xertica-orbe/XerticaOrbe.d.ts +2 -1
  213. package/dist/components/brand/xertica-provider/XerticaProvider.d.ts +1 -1
  214. package/dist/components/brand/xertica-xlogo/XerticaXLogo.d.ts +2 -1
  215. package/dist/components/examples/DrawingMapExample.d.ts +2 -1
  216. package/dist/components/examples/FilterableMapExample.d.ts +1 -1
  217. package/dist/components/examples/MapShowcase.d.ts +2 -1
  218. package/dist/components/examples/SimpleFilterableMap.d.ts +2 -1
  219. package/dist/components/figma/ImageWithFallback.d.ts +1 -1
  220. package/dist/components/layout/header/header.d.ts +1 -1
  221. package/dist/components/layout/sidebar/sidebar.d.ts +25 -13
  222. package/dist/components/media/FloatingMediaWrapper.d.ts +1 -1
  223. package/dist/components/media/audio-player/AudioPlayer.d.ts +2 -1
  224. package/dist/components/media/video-player/VideoPlayer.d.ts +2 -1
  225. package/dist/components/pages/forgot-password-page/ForgotPasswordPage.d.ts +2 -1
  226. package/dist/components/pages/home-content/HomeContent.d.ts +2 -1
  227. package/dist/components/pages/home-page/HomePage.d.ts +2 -1
  228. package/dist/components/pages/login-page/LoginPage.d.ts +2 -1
  229. package/dist/components/pages/reset-password-page/ResetPasswordPage.d.ts +2 -1
  230. package/dist/components/pages/template-content/TemplateContent.d.ts +2 -1
  231. package/dist/components/pages/template-page/TemplatePage.d.ts +2 -1
  232. package/dist/components/pages/verify-email-page/VerifyEmailPage.d.ts +2 -1
  233. package/dist/components/shared/CustomTooltipContent.d.ts +1 -1
  234. package/dist/components/shared/assistant-utils.d.ts +3 -2
  235. package/dist/components/shared/error-boundary.d.ts +4 -4
  236. package/dist/components/shared/error-fallbacks.d.ts +4 -3
  237. package/dist/components/shared/navigation.d.ts +6 -2
  238. package/dist/components/ui/accordion/accordion.d.ts +4 -4
  239. package/dist/components/ui/alert/alert.d.ts +3 -3
  240. package/dist/components/ui/alert-dialog/alert-dialog.d.ts +10 -10
  241. package/dist/components/ui/aspect-ratio/aspect-ratio.d.ts +1 -1
  242. package/dist/components/ui/assistant-chart/AssistantChart.d.ts +4 -3
  243. package/dist/components/ui/avatar/avatar.d.ts +3 -3
  244. package/dist/components/ui/badge/badge.d.ts +1 -1
  245. package/dist/components/ui/breadcrumb/breadcrumb.d.ts +7 -7
  246. package/dist/components/ui/calendar/calendar.d.ts +2 -2
  247. package/dist/components/ui/card/card.d.ts +7 -7
  248. package/dist/components/ui/carousel/carousel.d.ts +5 -5
  249. package/dist/components/ui/chart/chart.d.ts +16 -16
  250. package/dist/components/ui/collapsible/collapsible.d.ts +3 -3
  251. package/dist/components/ui/command/command.d.ts +9 -9
  252. package/dist/components/ui/context-menu/context-menu.d.ts +15 -15
  253. package/dist/components/ui/dialog/dialog.d.ts +9 -9
  254. package/dist/components/ui/drawer/drawer.d.ts +10 -10
  255. package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts +15 -15
  256. package/dist/components/ui/form/form.d.ts +7 -7
  257. package/dist/components/ui/google-maps-loader/google-maps-loader.d.ts +3 -3
  258. package/dist/components/ui/hover-card/hover-card.d.ts +3 -3
  259. package/dist/components/ui/input-otp/input-otp.d.ts +4 -4
  260. package/dist/components/ui/label/label.d.ts +1 -1
  261. package/dist/components/ui/menubar/menubar.d.ts +16 -16
  262. package/dist/components/ui/navigation-menu/navigation-menu.d.ts +8 -8
  263. package/dist/components/ui/page-header/page-header.d.ts +3 -3
  264. package/dist/components/ui/pagination/pagination.d.ts +7 -7
  265. package/dist/components/ui/popover/popover.d.ts +4 -4
  266. package/dist/components/ui/progress/progress.d.ts +1 -1
  267. package/dist/components/ui/resizable/resizable.d.ts +11 -7
  268. package/dist/components/ui/rich-text-editor/rich-text-editor.d.ts +1 -1
  269. package/dist/components/ui/scroll-area/scroll-area.d.ts +2 -2
  270. package/dist/components/ui/separator/separator.d.ts +1 -1
  271. package/dist/components/ui/sheet/sheet.d.ts +10 -10
  272. package/dist/components/ui/skeleton/skeleton.d.ts +1 -1
  273. package/dist/components/ui/sonner/sonner.d.ts +2 -1
  274. package/dist/components/ui/stats-card/stats-card-skeleton.d.ts +1 -1
  275. package/dist/components/ui/table/table.d.ts +8 -8
  276. package/dist/components/ui/tabs/tabs.d.ts +4 -4
  277. package/dist/components/ui/tooltip/tooltip.d.ts +4 -4
  278. package/dist/contexts/ApiKeyContext.d.ts +2 -2
  279. package/dist/contexts/AssistenteContext.d.ts +1 -1
  280. package/dist/contexts/AuthContext.d.ts +1 -1
  281. package/dist/contexts/LanguageContext.d.ts +2 -2
  282. package/dist/contexts/LayoutContext.d.ts +2 -2
  283. package/dist/contexts/ThemeContext.d.ts +1 -1
  284. package/dist/features/assistant/hooks/useAssistantConfig.d.ts +1 -1
  285. package/dist/features/home/hooks/useDashboardStats.d.ts +1 -1
  286. package/dist/features/home/hooks/useFeatureCards.d.ts +1 -1
  287. package/dist/features/home/hooks/useTeamMembers.d.ts +1 -1
  288. package/dist/{google-maps-loader-BCe58h9D.js → google-maps-loader-BOug999f.js} +0 -4
  289. package/dist/{google-maps-loader-casMyxlo.cjs → google-maps-loader-DfeBRI8Q.cjs} +0 -5
  290. package/dist/hooks.cjs.js +2 -2
  291. package/dist/hooks.es.js +2 -2
  292. package/dist/{index-9GWd0qxq.cjs → index-BQjx1Mdf.cjs} +1 -1
  293. package/dist/index-Bei5DzAL.js +6 -0
  294. package/dist/index.cjs.js +18 -18
  295. package/dist/index.es.js +12 -12
  296. package/dist/{input-cc-PTD4R.js → input-B0_vbA3g.js} +3 -61
  297. package/dist/{input-C_UiS2Py.cjs → input-ByYuOv8A.cjs} +1 -62
  298. package/dist/layout.cjs.js +1 -1
  299. package/dist/layout.es.js +1 -1
  300. package/dist/pages.cjs.js +1 -1
  301. package/dist/pages.es.js +1 -1
  302. package/dist/{rich-text-editor-DxO1Hz3a.cjs → rich-text-editor-BuqSu718.cjs} +70 -5
  303. package/dist/{rich-text-editor-DqLICivI.js → rich-text-editor-CV8FtA_l.js} +68 -6
  304. package/dist/{rich-text-editor-DloeW0wc.js → rich-text-editor-DV1SixzE.js} +61 -3
  305. package/dist/{rich-text-editor-B2CKz7nx.cjs → rich-text-editor-DnSDglzW.cjs} +63 -2
  306. package/dist/{sidebar-BS1p2V7t.cjs → sidebar-BGJ0j567.cjs} +367 -9
  307. package/dist/{sidebar-3XyzjVBw.js → sidebar-BI6mZlC8.js} +364 -6
  308. package/dist/{sidebar-DyYvgyBj.cjs → sidebar-BtmotOg-.cjs} +367 -9
  309. package/dist/{sidebar-Duermn32.js → sidebar-D3EgGVxF.js} +77 -63
  310. package/dist/{sidebar-CLmIjgNd.cjs → sidebar-DDTqMGnR.cjs} +80 -66
  311. package/dist/{sidebar-B4ZWaMrE.js → sidebar-I7EXyf5D.js} +364 -6
  312. package/dist/ui.cjs.js +7 -7
  313. package/dist/ui.es.js +5 -5
  314. package/dist/{xertica-assistant-QFUnv5I2.cjs → xertica-assistant-BrF9jScR.cjs} +4 -4
  315. package/dist/{xertica-assistant-BbgzsgsG.js → xertica-assistant-CJVnvMqU.js} +55 -45
  316. package/dist/{xertica-assistant-d2qs2Vd9.cjs → xertica-assistant-CKPtc1z5.cjs} +58 -40
  317. package/dist/{xertica-assistant-CyikE3N_.js → xertica-assistant-CsBsxTsI.js} +2 -2
  318. package/dist/{xertica-assistant-CIaUlbIt.cjs → xertica-assistant-DER0brPQ.cjs} +6 -5
  319. package/dist/{xertica-assistant-B1NaSFFj.js → xertica-assistant-DQCjpk5M.js} +4 -3
  320. package/dist/{xertica-assistant-B687qEPU.js → xertica-assistant-Dk-9AJzw.js} +16 -7
  321. package/dist/{xertica-assistant-sOHwTgIP.cjs → xertica-assistant-DxhIKvuY.cjs} +18 -9
  322. package/dist/xertica-ui.css +2 -2
  323. package/docs/components/audio-player.md +1 -1
  324. package/docs/components/sidebar.md +331 -331
  325. package/docs/components/stats-card.md +138 -138
  326. package/docs/components/toggle.md +12 -0
  327. package/docs/doc-audit.md +35 -229
  328. package/docs/i18n.md +31 -10
  329. package/docs/llms.md +6 -4
  330. package/llms-compact.txt +444 -444
  331. package/llms-full.txt +12144 -12144
  332. package/llms.txt +187 -187
  333. package/package.json +223 -219
  334. package/scripts/a11y-audit.mjs +162 -162
  335. package/scripts/fix-stories.cjs +19 -19
  336. package/styles/globals.css +0 -1
  337. package/styles/xertica/base.css +8 -0
  338. package/styles/xertica/tokens.css +240 -240
  339. package/templates/.env.example +2 -2
  340. package/templates/.prettierignore +4 -4
  341. package/templates/.prettierrc +10 -10
  342. package/templates/guidelines/Guidelines.md +865 -865
  343. package/templates/index.html +13 -13
  344. package/templates/package.json +69 -69
  345. package/templates/src/app/components/AuthGuard.tsx +11 -0
  346. package/templates/src/features/auth/ui/AuthPageShell.tsx +32 -32
  347. package/templates/src/features/home/ui/HomeContent.tsx +2 -3
  348. package/templates/src/features/settings/index.ts +2 -0
  349. package/templates/src/features/settings/ui/SettingsContent.tsx +134 -0
  350. package/templates/src/features/template/ui/TemplateContent.tsx +2 -3
  351. package/templates/src/i18n.ts +36 -13
  352. package/templates/src/locales/en/pages/settings.json +15 -0
  353. package/templates/src/locales/es/pages/settings.json +15 -0
  354. package/templates/src/locales/pt-BR/pages/settings.json +15 -0
  355. package/templates/src/pages/SettingsPage.tsx +28 -0
  356. package/templates/src/shared/config/navigation.ts +2 -2
  357. package/templates/src/styles/xertica/tokens.css +240 -240
  358. package/templates/vite.config.d.ts +2 -2
  359. package/templates/vite.config.js +20 -20
  360. package/utils/gemini.ts +2 -2
  361. package/dist/AssistantChart-9w31gdAb.cjs +0 -3363
  362. package/dist/AssistantChart-BAudAfne.cjs +0 -3591
  363. package/dist/AssistantChart-BAx9VQvb.cjs +0 -3374
  364. package/dist/AssistantChart-BP8upjMk.js +0 -3565
  365. package/dist/AssistantChart-CVko2A1W.js +0 -3348
  366. package/dist/AssistantChart-CVzmmhx4.js +0 -3337
  367. package/dist/AssistantChart-CeU2dIb6.js +0 -3469
  368. package/dist/AssistantChart-DO5UuX4J.cjs +0 -3495
  369. package/dist/AudioPlayer-1ypwE2Wh.cjs +0 -936
  370. package/dist/AudioPlayer-BJ2IuRQW.cjs +0 -1275
  371. package/dist/AudioPlayer-BWDW6TN9.cjs +0 -1279
  372. package/dist/AudioPlayer-CM962GEh.js +0 -1280
  373. package/dist/AudioPlayer-CbZId7cj.cjs +0 -998
  374. package/dist/AudioPlayer-DU8zt8-z.js +0 -999
  375. package/dist/AudioPlayer-DuKXrCfy.js +0 -937
  376. package/dist/AudioPlayer-Xxma6_-H.js +0 -1276
  377. package/dist/BrandColorsContext-DLVJgtmH.js +0 -666
  378. package/dist/BrandColorsContext-DZT7JjeD.js +0 -659
  379. package/dist/BrandColorsContext-GUyFmkX0.cjs +0 -665
  380. package/dist/BrandColorsContext-awnBCmC4.cjs +0 -666
  381. package/dist/CodeBlock-C2ZUXt8V.js +0 -219
  382. package/dist/CodeBlock-C5oYnLQp.cjs +0 -218
  383. package/dist/CodeBlock-DuxdtN-l.js +0 -219
  384. package/dist/CodeBlock-POVYONvC.cjs +0 -218
  385. package/dist/CustomTooltipContent-BT6brkVJ.cjs +0 -49
  386. package/dist/CustomTooltipContent-CfOfikhq.js +0 -33
  387. package/dist/CustomTooltipContent-DHjkY0ww.js +0 -40
  388. package/dist/CustomTooltipContent-c_K-DWRr.cjs +0 -56
  389. package/dist/FeatureCard-9GhnlgL3.js +0 -336
  390. package/dist/FeatureCard-Bcb3Zi54.js +0 -302
  391. package/dist/FeatureCard-Cfs6Ishq.js +0 -485
  392. package/dist/FeatureCard-CkPd2psQ.cjs +0 -301
  393. package/dist/FeatureCard-Dkp-xmzM.cjs +0 -335
  394. package/dist/FeatureCard-ie3L8Fpr.cjs +0 -484
  395. package/dist/FeatureCardSkeleton-CAgu8stE.js +0 -27
  396. package/dist/FeatureCardSkeleton-mO3Bdm_V.cjs +0 -26
  397. package/dist/LanguageContext-BwhwC3G2.js +0 -657
  398. package/dist/LanguageContext-DvUt5jBg.cjs +0 -656
  399. package/dist/LayoutContext-BDmcZfMH.cjs +0 -84
  400. package/dist/LayoutContext-C4kKN9RV.cjs +0 -93
  401. package/dist/LayoutContext-CGEe1oPq.js +0 -94
  402. package/dist/LayoutContext-CwT5KLiW.cjs +0 -104
  403. package/dist/LayoutContext-DVLCsoQn.js +0 -105
  404. package/dist/LayoutContext-dbQvdC4O.js +0 -85
  405. package/dist/ThemeContext-BbBNoFTG.js +0 -735
  406. package/dist/ThemeContext-Bo-W2WZH.js +0 -1856
  407. package/dist/ThemeContext-CP3a0jxy.cjs +0 -1855
  408. package/dist/ThemeContext-Cmr8Ex8H.cjs +0 -734
  409. package/dist/ThemeContext-CpqYShLq.cjs +0 -324
  410. package/dist/ThemeContext-D3LzacmG.js +0 -188
  411. package/dist/ThemeContext-DQUOeziy.cjs +0 -1855
  412. package/dist/ThemeContext-Du2nE1PL.js +0 -325
  413. package/dist/ThemeContext-GeEBTJ3q.cjs +0 -1621
  414. package/dist/ThemeContext-JyLK9B1o.js +0 -1622
  415. package/dist/ThemeContext-RTy1m2Uq.js +0 -82
  416. package/dist/ThemeContext-U4dEYc6C.cjs +0 -187
  417. package/dist/ThemeContext-bSzuOit2.cjs +0 -81
  418. package/dist/ThemeContext-ept8jhXI.js +0 -1856
  419. package/dist/VerifyEmailPage-BE-L9mB7.js +0 -2828
  420. package/dist/VerifyEmailPage-BIBOKV7Z.js +0 -3214
  421. package/dist/VerifyEmailPage-BRSP-Pwt.cjs +0 -3196
  422. package/dist/VerifyEmailPage-Bae2cBXT.cjs +0 -2827
  423. package/dist/VerifyEmailPage-BiRm7Nh4.cjs +0 -3213
  424. package/dist/VerifyEmailPage-Bvfv8HVQ.js +0 -3214
  425. package/dist/VerifyEmailPage-CR7kb5df.cjs +0 -2827
  426. package/dist/VerifyEmailPage-C_ihbcth.js +0 -2828
  427. package/dist/VerifyEmailPage-CbgjOF0v.js +0 -2828
  428. package/dist/VerifyEmailPage-CdYPSJoO.js +0 -2828
  429. package/dist/VerifyEmailPage-D-FRj5TU.cjs +0 -3213
  430. package/dist/VerifyEmailPage-DF2ilhum.cjs +0 -3210
  431. package/dist/VerifyEmailPage-DMBh4NM9.cjs +0 -2827
  432. package/dist/VerifyEmailPage-DTtFfC-J.js +0 -3197
  433. package/dist/VerifyEmailPage-Dt7zgA4w.cjs +0 -2827
  434. package/dist/VerifyEmailPage-EhudUdqF.js +0 -3211
  435. package/dist/VerifyEmailPage-RrUApqBN.js +0 -3214
  436. package/dist/VerifyEmailPage-VoMI7MYH.cjs +0 -3213
  437. package/dist/VerifyEmailPage-hdB8JQGv.cjs +0 -3213
  438. package/dist/VerifyEmailPage-vYHbYK3q.js +0 -3214
  439. package/dist/XerticaOrbe-BCUVSPPB.cjs +0 -1924
  440. package/dist/XerticaOrbe-BGIB1CTK.js +0 -1355
  441. package/dist/XerticaOrbe-By8eEvR-.js +0 -1925
  442. package/dist/XerticaOrbe-FWGjBj0U.cjs +0 -1354
  443. package/dist/XerticaProvider-AChwphCO.cjs +0 -48
  444. package/dist/XerticaProvider-AbWlr7Af.cjs +0 -48
  445. package/dist/XerticaProvider-B8CaV7xu.cjs +0 -39
  446. package/dist/XerticaProvider-BITjgC5p.js +0 -39
  447. package/dist/XerticaProvider-BSyFrmC0.js +0 -49
  448. package/dist/XerticaProvider-By8q3Roe.cjs +0 -38
  449. package/dist/XerticaProvider-CUYJZc32.js +0 -49
  450. package/dist/XerticaProvider-CW9hpCdF.cjs +0 -39
  451. package/dist/XerticaProvider-CWgby5mY.js +0 -49
  452. package/dist/XerticaProvider-CWs6EwNa.js +0 -49
  453. package/dist/XerticaProvider-CiNKjMx1.cjs +0 -48
  454. package/dist/XerticaProvider-CjQAQPcn.cjs +0 -48
  455. package/dist/XerticaProvider-D5lLumH-.js +0 -49
  456. package/dist/XerticaProvider-DQtvJU7m.js +0 -40
  457. package/dist/XerticaProvider-DUOJg9iX.js +0 -49
  458. package/dist/XerticaProvider-Dl_b72_l.cjs +0 -51
  459. package/dist/XerticaProvider-qQUDop71.cjs +0 -48
  460. package/dist/XerticaProvider-siSt9uG2.js +0 -40
  461. package/dist/XerticaXLogo-8TTzBjHw.cjs +0 -251
  462. package/dist/XerticaXLogo-B2svDGZh.cjs +0 -251
  463. package/dist/XerticaXLogo-BWaag64t.js +0 -252
  464. package/dist/XerticaXLogo-CFuIlYFH.js +0 -252
  465. package/dist/XerticaXLogo-CU-U-GP4.cjs +0 -251
  466. package/dist/XerticaXLogo-ChryA6xj.js +0 -252
  467. package/dist/XerticaXLogo-CowGv7BC.js +0 -252
  468. package/dist/XerticaXLogo-CziKMQil.cjs +0 -242
  469. package/dist/XerticaXLogo-D8jf0SNv.cjs +0 -214
  470. package/dist/XerticaXLogo-DHz5SugF.js +0 -252
  471. package/dist/XerticaXLogo-DTee_y8X.cjs +0 -251
  472. package/dist/XerticaXLogo-DfUvz-lD.js +0 -243
  473. package/dist/XerticaXLogo-DzXKZBtG.js +0 -231
  474. package/dist/XerticaXLogo-fAJMy3H4.js +0 -215
  475. package/dist/XerticaXLogo-kslQ8Tk_.cjs +0 -251
  476. package/dist/XerticaXLogo-xWbElCZj.cjs +0 -230
  477. package/dist/alert-dialog-iDe5VE5o.js +0 -842
  478. package/dist/alert-dialog-yckpaOpy.cjs +0 -865
  479. package/dist/avatar-Cq5_E8r9.cjs +0 -78
  480. package/dist/avatar-CuYu2MKt.cjs +0 -72
  481. package/dist/avatar-Cxyofu1H.js +0 -56
  482. package/dist/avatar-DSBfJMRJ.js +0 -62
  483. package/dist/breadcrumb-CVzzzqfo.js +0 -103
  484. package/dist/breadcrumb-DIJ0X3Ot.js +0 -96
  485. package/dist/breadcrumb-DjSxkjlQ.cjs +0 -95
  486. package/dist/breadcrumb-DnDlQFg2.cjs +0 -102
  487. package/dist/button-0CXwCRTm.cjs +0 -85
  488. package/dist/button-Bnv9SvYK.cjs +0 -79
  489. package/dist/button-C6uvh0rV.js +0 -63
  490. package/dist/button-qkz7Icqf.js +0 -69
  491. package/dist/card-B8-Gl5DL.js +0 -73
  492. package/dist/card-Bv_c57KU.cjs +0 -72
  493. package/dist/card-DJTsYhLL.cjs +0 -89
  494. package/dist/card-fi8zAN-I.js +0 -90
  495. package/dist/dropdown-menu-B_uEXNc4.cjs +0 -220
  496. package/dist/dropdown-menu-CFuCssWA.js +0 -204
  497. package/dist/dropdown-menu-Ze1YZFyc.cjs +0 -247
  498. package/dist/dropdown-menu-nc_Lvg-u.js +0 -231
  499. package/dist/google-maps-loader-BVoVhUwA.js +0 -316
  500. package/dist/google-maps-loader-BqsYL48U.cjs +0 -285
  501. package/dist/google-maps-loader-DjJaYhV6.cjs +0 -316
  502. package/dist/google-maps-loader-t2IlYBzw.js +0 -286
  503. package/dist/index-B7A523O_.js +0 -8
  504. package/dist/index-BabBx2pa.js +0 -6
  505. package/dist/index-CkTUgOwX.js +0 -8
  506. package/dist/index-D3RLKRAs.cjs +0 -7
  507. package/dist/index-DmHhnb7a.cjs +0 -7
  508. package/dist/index.umd.js +0 -13712
  509. package/dist/input-CNFHVKvo.cjs +0 -145
  510. package/dist/input-CPl84zAx.cjs +0 -151
  511. package/dist/input-D-6IsAca.js +0 -133
  512. package/dist/input-wPYg0d0P.js +0 -127
  513. package/dist/progress-BLwqortq.cjs +0 -105
  514. package/dist/progress-Cs9i4DPx.js +0 -81
  515. package/dist/progress-D9mtsf6j.js +0 -89
  516. package/dist/progress-DDeuWPZw.cjs +0 -97
  517. package/dist/rich-text-editor-1UW3Y2JK.js +0 -2891
  518. package/dist/rich-text-editor-BM-LPpTh.js +0 -2902
  519. package/dist/rich-text-editor-BmsjY03B.js +0 -2949
  520. package/dist/rich-text-editor-BrdC5evx.js +0 -2244
  521. package/dist/rich-text-editor-CyggTOLQ.cjs +0 -2908
  522. package/dist/rich-text-editor-DbV-S7Fi.cjs +0 -2261
  523. package/dist/rich-text-editor-DoCkKhzk.cjs +0 -2919
  524. package/dist/rich-text-editor-GS2kpTAK.cjs +0 -2966
  525. package/dist/routes.d.ts +0 -10
  526. package/dist/select-Cmb8zF7w.cjs +0 -156
  527. package/dist/select-CprSNX0z.js +0 -139
  528. package/dist/select-DT7Zjn42.cjs +0 -162
  529. package/dist/select-En8DkoaQ.js +0 -145
  530. package/dist/sidebar-B3EYhli0.cjs +0 -800
  531. package/dist/sidebar-B9NR0lCe.cjs +0 -800
  532. package/dist/sidebar-BcJ6LxvX.cjs +0 -733
  533. package/dist/sidebar-BvF5I2Ue.cjs +0 -800
  534. package/dist/sidebar-C5B_LHek.cjs +0 -795
  535. package/dist/sidebar-CA6_ek3f.js +0 -801
  536. package/dist/sidebar-CRMiBtAi.js +0 -801
  537. package/dist/sidebar-CVUGHOS_.cjs +0 -756
  538. package/dist/sidebar-CZ2mWaMM.cjs +0 -800
  539. package/dist/sidebar-CmvwjnVb.js +0 -757
  540. package/dist/sidebar-CplprZpM.js +0 -801
  541. package/dist/sidebar-Dz7bd3zP.js +0 -796
  542. package/dist/sidebar-KIS0C2JH.js +0 -801
  543. package/dist/sidebar-OTO_up7Z.js +0 -801
  544. package/dist/sidebar-WoKvFWCN.js +0 -717
  545. package/dist/sidebar-zowjejT2.cjs +0 -800
  546. package/dist/skeleton-4zoHiFJ_.cjs +0 -83
  547. package/dist/skeleton-C4bHqUBf.js +0 -84
  548. package/dist/slider-C0JKA9k3.js +0 -56
  549. package/dist/slider-CPmwzm_3.cjs +0 -73
  550. package/dist/slider-DcJharR9.cjs +0 -73
  551. package/dist/slider-Pay3fka2.js +0 -56
  552. package/dist/sonner-DeUIj0Zg.cjs +0 -63
  553. package/dist/sonner-g9RIfi35.js +0 -47
  554. package/dist/tooltip-CNtsL5cB.cjs +0 -71
  555. package/dist/tooltip-CTyZCD8h.js +0 -55
  556. package/dist/tooltip-CwgSdy3h.cjs +0 -67
  557. package/dist/tooltip-D-OrYnKO.js +0 -51
  558. package/dist/use-audio-player-BJFV24XW.js +0 -188
  559. package/dist/use-audio-player-Bkh23vQ3.js +0 -177
  560. package/dist/use-audio-player-Dn1NR9xN.cjs +0 -176
  561. package/dist/use-audio-player-DyVYlOnR.cjs +0 -187
  562. package/dist/use-mobile-BB4M_4Ru.cjs +0 -47
  563. package/dist/use-mobile-CLu9nqGL.js +0 -31
  564. package/dist/use-mobile-CNwiBNxu.js +0 -33
  565. package/dist/use-mobile-Dlf74ufz.cjs +0 -49
  566. package/dist/xertica-assistant-B1IaHXnB.cjs +0 -1894
  567. package/dist/xertica-assistant-BMqdyRVi.js +0 -2082
  568. package/dist/xertica-assistant-Bj3vBCq_.cjs +0 -2081
  569. package/dist/xertica-assistant-CfBCo21g.js +0 -2148
  570. package/dist/xertica-assistant-DIM_Do1k.js +0 -1866
  571. package/dist/xertica-assistant-DPsESB6t.js +0 -1878
  572. package/dist/xertica-assistant-DrsRwla1.cjs +0 -2147
  573. package/dist/xertica-assistant-Qp3ydksa.cjs +0 -1882
  574. package/dist/xertica-assistant-gnCJdcZY.js +0 -1866
  575. package/dist/xertica-assistant-yyrR0JKl.cjs +0 -1882
  576. package/styles/xertica/app-overrides/scrollbar.css +0 -33
  577. package/dist/{XerticaXLogo-qBPhwK3g.cjs → LanguageSelector-1cINLJ23.cjs} +80 -80
@@ -1,865 +1,865 @@
1
- # Project Guidelines — Xertica UI App
2
-
3
- > **Scope**: These guidelines apply to any React application scaffolded with `npx xertica-ui@latest init`. They define architecture, import conventions, component rules, state management, and AI agent behavior.
4
- >
5
- > **AI agents — reading order**:
6
- > 1. This file (architecture + rules)
7
- > 2. `node_modules/xertica-ui/llms-compact.txt` (component catalog + quick reference)
8
- > 3. `node_modules/xertica-ui/docs/decision-tree.md` (when choosing between similar components)
9
- > 4. `node_modules/xertica-ui/docs/components/<name>.md` (when you need a specific component's full props)
10
- > 5. `node_modules/xertica-ui/docs/ai-usage.md` (mandatory constraint list)
11
-
12
- ---
13
-
14
- ## 1. Architecture — Feature-Sliced Design + Feature-Driven Architecture
15
-
16
- This project follows **FSD (Feature-Sliced Design)** layered architecture combined with **FDA (Feature-Driven Architecture)** vertical slicing. **Layers can only import from layers below them.**
17
-
18
- ```
19
- src/
20
- ├── app/ # Layer 1 — Application shell (imports from all layers)
21
- │ ├── App.tsx # XerticaProvider + BrowserRouter + AuthGuard (CLI-generated)
22
- │ ├── context/
23
- │ │ └── AuthContext.tsx # AuthProvider + useAuth() hook
24
- │ └── components/
25
- │ ├── AppLayout.tsx # Sidebar + children + optional XerticaAssistant
26
- │ └── AuthGuard.tsx # All route definitions + route guard components
27
-
28
- ├── shared/ # Layer 2 — Shared utilities (no business logic)
29
- │ ├── config/
30
- │ │ └── navigation.ts # RouteConfig type, routes[], getRoutesByRole()
31
- │ ├── mock/ # Domain mock data (TypeScript static arrays + helpers)
32
- │ ├── lib/
33
- │ │ └── auth.ts # getStoredUser, storeUser, clearStoredUser
34
- │ └── types/
35
- │ └── auth.ts # User interface + UserRole type
36
-
37
- ├── features/ # Layer 3 — Vertical slices by business capability
38
- │ ├── auth/ # Login, ForgotPassword, VerifyEmail, ResetPassword
39
- │ ├── home/ # Post-login hub (FeatureCards grid)
40
- │ │ ├── data/mock.ts # Types + async fetch + factory functions (getMockXxx)
41
- │ │ ├── hooks/useFeatureCards.ts # React Query, queryKey includes `language`
42
- │ │ ├── store/dashboardStore.ts # Zustand UI state (no server data)
43
- │ │ ├── ui/HomeContent.tsx
44
- │ │ └── index.ts
45
- │ ├── assistant/ # XerticaAssistant — AppLayout depends on it
46
- │ │ ├── data/mock.ts
47
- │ │ ├── hooks/useAssistantConfig.ts
48
- │ │ └── index.ts
49
- │ └── <domain>/ # One slice per business domain
50
- │ ├── data/mock.ts # Domain types + mock fetch functions
51
- │ ├── hooks/use<Domain>.ts # React Query wrapper
52
- │ ├── store/<domain>Store.ts # Zustand UI state (optional)
53
- │ ├── ui/<Domain>Content.tsx # Page content component
54
- │ └── index.ts # Barrel — ONLY public interface for this feature
55
-
56
- ├── pages/ # Layer 4 — Thin route wrappers only
57
- │ └── <Name>Page.tsx # AppLayout + <NameContent> + optional XerticaAssistant
58
-
59
- ├── i18n.ts # i18next setup (CLI-generated — do not hand-edit)
60
- ├── locales/
61
- │ ├── .languages.json # CLI-managed language selection
62
- │ └── pt-BR/ # One folder per language
63
- │ ├── common.json # Shared action labels
64
- │ ├── nav.json # Navigation labels
65
- │ ├── errors.json # Error boundary UI
66
- │ ├── pages/ # Per-page namespaces
67
- │ └── components/ # Per-component namespaces
68
- └── styles/
69
- ├── index.css # Imports: xertica-ui/style.css, tokens.css, @source
70
- └── xertica/tokens.css # Brand CSS variables — customize here only
71
- ```
72
-
73
- ### Layer Import Rules
74
-
75
- - `app/` → can import from all layers
76
- - `shared/` → cannot import from `features/` or `pages/`
77
- - `features/<a>/` → cannot import from `features/<b>/` — extract shared code to `shared/`
78
- - `pages/` → imports only from `features/` barrels and `app/`
79
-
80
- ### Feature Barrel Rule
81
-
82
- The `index.ts` is the **only** public interface of a feature. Always import from the barrel, never from internal paths:
83
-
84
- ```tsx
85
- // ✅ Correct
86
- import { LoginContent } from '../features/auth';
87
-
88
- // ❌ Wrong — never import from internal paths
89
- import { LoginContent } from '../features/auth/ui/LoginContent';
90
- ```
91
-
92
- ---
93
-
94
- ## 2. Import Rules
95
-
96
- Always use the correct `xertica-ui` subpath for the domain you need:
97
-
98
- ```tsx
99
- // UI primitives — buttons, inputs, dialogs, tables, badges, etc.
100
- import {
101
- Button, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
102
- Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter,
103
- Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
104
- AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader,
105
- AlertDialogTitle, AlertDialogDescription, AlertDialogFooter,
106
- AlertDialogCancel, AlertDialogAction,
107
- Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
108
- Badge, Progress, ScrollArea, Separator, Skeleton, Label, Switch,
109
- Tabs, TabsContent, TabsList, TabsTrigger,
110
- Collapsible, CollapsibleContent, CollapsibleTrigger,
111
- Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,
112
- Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,
113
- Popover, PopoverContent, PopoverTrigger,
114
- Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription,
115
- Alert, AlertDescription, Drawer,
116
- } from 'xertica-ui/ui';
117
-
118
- // Composed high-level block components
119
- import {
120
- FeatureCard, FeatureCardSkeleton,
121
- QuickActionCard, QuickActionCardSkeleton,
122
- ProjectCard, ProjectCardSkeleton,
123
- ActivityCard, ActivityCardSkeleton,
124
- NotificationCard, NotificationCardSkeleton,
125
- ProfileCard, ProfileCardSkeleton,
126
- StatsCard, StatsCardSkeleton,
127
- } from 'xertica-ui/blocks';
128
-
129
- // Application shell
130
- import { Sidebar, Header } from 'xertica-ui/layout';
131
-
132
- // Brand / providers
133
- import { XerticaProvider, XerticaLogo, ThemeToggle, LanguageSelector } from 'xertica-ui/brand';
134
-
135
- // AI assistant
136
- import { XerticaAssistant, generateDemoResponse } from 'xertica-ui/assistant';
137
-
138
- // Media players
139
- import { VideoPlayer, AudioPlayer } from 'xertica-ui/media';
140
-
141
- // Hooks
142
- import { useLayout, useOptionalLayout, useTheme, useLanguage } from 'xertica-ui/hooks';
143
-
144
- // Icons — ALWAYS from lucide-react, NEVER from xertica-ui or inline SVG
145
- import { Home, Settings, Plus, Trash2, ChevronRight, Search } from 'lucide-react';
146
-
147
- // Charts — use recharts (pre-installed)
148
- import {
149
- BarChart, Bar, LineChart, Line, PieChart, Pie, Cell,
150
- XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,
151
- } from 'recharts';
152
-
153
- // Toast
154
- import { toast } from 'sonner';
155
-
156
- // Form validation
157
- import { useForm } from 'react-hook-form';
158
- import { zodResolver } from '@hookform/resolvers/zod';
159
- import * as z from 'zod';
160
-
161
- // i18n
162
- import { useTranslation } from 'react-i18next';
163
- ```
164
-
165
- > **Never import from `xertica-ui` root barrel in FSD/FDA projects.** Always use the granular subpaths above — they produce smaller bundles and prevent accidental cross-domain coupling.
166
-
167
- ---
168
-
169
- ## 3. Non-Negotiable Rules
170
-
171
- ### 3.1 No Raw HTML for UI Surfaces
172
-
173
- | ❌ Never | ✅ Always |
174
- |---|---|
175
- | `<button>` | `<Button>` from `xertica-ui/ui` |
176
- | `<input>` | `<Input>` from `xertica-ui/ui` |
177
- | `<select>` | `<Select>` from `xertica-ui/ui` |
178
- | `<textarea>` | `<Textarea>` from `xertica-ui/ui` |
179
- | `<div class="card ...">` | `<Card>` from `xertica-ui/ui` |
180
- | Custom scrollable `<div>` | `<ScrollArea>` from `xertica-ui/ui` |
181
- | Raw `<progress>` | `<Progress>` from `xertica-ui/ui` |
182
- | Inline SVG `<svg><path .../></svg>` | `import { IconName } from 'lucide-react'` |
183
-
184
- ### 3.2 Color Rules
185
-
186
- **Always forbidden:**
187
- ```tsx
188
- // ❌ Never — raw values are non-themeable
189
- style={{ backgroundColor: '#3B82F6' }}
190
- style={{ color: 'rgb(59,130,246)' }}
191
- className="text-[#ffffff]"
192
- ```
193
-
194
- **Required for semantic/status contexts** (errors, warnings, success, danger actions, status badges):
195
- ```tsx
196
- // ❌ Wrong — Tailwind color for semantic state
197
- <div className="bg-red-500">Error</div>
198
-
199
- // ✅ Required — token adapts to theme and dark mode
200
- <div className="bg-destructive text-destructive-foreground">Error</div>
201
- ```
202
-
203
- **Acceptable for non-semantic layout** (spacing, decorative elements, charts):
204
- ```tsx
205
- // ✅ OK — non-semantic context, no theme dependency
206
- <div className="bg-blue-500/15 border-gray-200">Decorative</div>
207
- ```
208
-
209
- Semantic token reference:
210
- | Intent | Token class |
211
- |---|---|
212
- | Error / Danger | `bg-destructive` / `text-destructive` |
213
- | Warning | `bg-warning` / `text-warning` |
214
- | Success | `bg-success` / `text-success` |
215
- | Informational | `bg-info` / `text-info` |
216
- | Neutral/secondary | `bg-secondary` / `text-secondary-foreground` |
217
- | Primary action | `bg-primary` / `text-primary-foreground` |
218
- | Muted background | `bg-muted` / `text-muted-foreground` |
219
-
220
- ### 3.3 Border Radius — Never Hardcode
221
-
222
- ```tsx
223
- // ❌ Wrong
224
- className="rounded-lg"
225
- className="rounded-xl"
226
-
227
- // ✅ Correct — inherits the project's configured radius token
228
- className="rounded-[var(--radius)]"
229
- ```
230
-
231
- ### 3.4 Layout State — Never Hardcode
232
-
233
- ```tsx
234
- // ❌ Wrong — breaks when sidebar width changes
235
- <div style={{ paddingLeft: '280px' }}>
236
-
237
- // ✅ Correct — always from context
238
- const { sidebarExpanded, sidebarWidth } = useLayout();
239
- <div style={{ paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px' }}>
240
- ```
241
-
242
- `80px` is the fixed collapsed sidebar width. The expanded value (`sidebarWidth`) defaults to `280px` but is configurable. Never hardcode either value.
243
-
244
- ### 3.5 Destructive Actions — Always Confirm with AlertDialog
245
-
246
- ```tsx
247
- <AlertDialog>
248
- <AlertDialogTrigger asChild>
249
- <Button variant="destructive">Delete Record</Button>
250
- </AlertDialogTrigger>
251
- <AlertDialogContent>
252
- <AlertDialogHeader>
253
- <AlertDialogTitle>Are you sure?</AlertDialogTitle>
254
- <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
255
- </AlertDialogHeader>
256
- <AlertDialogFooter>
257
- <AlertDialogCancel>Cancel</AlertDialogCancel>
258
- <AlertDialogAction onClick={handleDelete}>Delete</AlertDialogAction>
259
- </AlertDialogFooter>
260
- </AlertDialogContent>
261
- </AlertDialog>
262
- ```
263
-
264
- ### 3.6 Toast Notifications
265
-
266
- Use `toast` from `sonner` for ephemeral action feedback. Never render `<Toaster>` manually — it is auto-injected by `<XerticaProvider>`.
267
-
268
- ```tsx
269
- import { toast } from 'sonner';
270
-
271
- toast.success(t('users.createSuccess'));
272
- toast.error(t('errors.somethingWentWrong'));
273
- toast.info(t('feature.inDevelopment'));
274
- toast.warning(t('billing.syncConflict'));
275
- ```
276
-
277
- ### 3.7 Form Validation — react-hook-form + zod
278
-
279
- Never implement manual validation with `useState` + conditional checks:
280
-
281
- ```tsx
282
- // ❌ Wrong — manual validation
283
- const [error, setError] = useState('');
284
- if (!email.includes('@')) setError('Invalid email');
285
-
286
- // ✅ Correct — declarative schema, errors flow automatically
287
- const schema = z.object({
288
- email: z.string().email('Invalid email address'),
289
- name: z.string().min(2, 'Name must be at least 2 characters'),
290
- });
291
-
292
- const form = useForm({ resolver: zodResolver(schema) });
293
- // Error messages render automatically via <FormMessage />
294
- ```
295
-
296
- ### 3.8 Mock Data Belongs in `data/mock.ts`
297
-
298
- Never hardcode data arrays inside component files:
299
-
300
- ```tsx
301
- // ❌ Wrong — data inside the component
302
- const users = [{ id: 1, name: 'Alice' }, ...];
303
-
304
- // ✅ Correct — data in features/<domain>/data/mock.ts
305
- export async function fetchUsers(): Promise<User[]> {
306
- await new Promise(r => setTimeout(r, 300)); // simulate network
307
- return MOCK_USERS;
308
- }
309
- ```
310
-
311
- ---
312
-
313
- ## 4. Page Structure — Standard Shell
314
-
315
- Every content component follows this exact shell. No exceptions:
316
-
317
- ```tsx
318
- import { Link } from 'react-router-dom';
319
- import { Header } from 'xertica-ui/layout';
320
- import { ScrollArea } from 'xertica-ui/ui';
321
- import { useLayout } from 'xertica-ui/hooks';
322
- import { useTranslation } from 'react-i18next';
323
-
324
- export function ExampleContent() {
325
- const { t } = useTranslation();
326
- const { sidebarExpanded, sidebarWidth } = useLayout();
327
-
328
- return (
329
- <div
330
- style={{ paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px' }}
331
- className="flex-1 flex flex-col overflow-hidden transition-all duration-300"
332
- >
333
- {/* Header with breadcrumbs + theme/language controls */}
334
- <Header
335
- showThemeToggle
336
- showLanguageSelector
337
- breadcrumbs={[
338
- { label: t('nav.home', 'App'), href: '/home' },
339
- { label: t('nav.thisPage', 'This Page') },
340
- ]}
341
- renderLink={(href, props) => <Link to={href} {...props} />}
342
- />
343
-
344
- {/* Scrollable main content */}
345
- <main className="flex-1 overflow-hidden bg-muted">
346
- <ScrollArea className="h-full">
347
- <div className="p-4 md:p-6">
348
- <div className="max-w-7xl mx-auto space-y-6">
349
- {/* 1. KPI StatsCards row (if applicable) */}
350
- {/* 2. Main content cards */}
351
- {/* 3. Tables with filters */}
352
- </div>
353
- </div>
354
- </ScrollArea>
355
- </main>
356
-
357
- {/* Dialogs / Sheets rendered OUTSIDE <main> */}
358
- </div>
359
- );
360
- }
361
- ```
362
-
363
- ### Pages Are Thin Wrappers
364
-
365
- ```tsx
366
- // pages/ExamplePage.tsx — thin wrapper only, no logic
367
- export function ExamplePage() {
368
- return (
369
- <AppLayout>
370
- <ExampleContent />
371
- </AppLayout>
372
- );
373
- }
374
- ```
375
-
376
- ---
377
-
378
- ## 5. Adding a New Route (Step-by-Step)
379
-
380
- 1. **Create feature content**: `src/features/<name>/ui/<NameContent>.tsx`
381
- 2. **Export from the barrel**: `src/features/<name>/index.ts`
382
- 3. **Create the page**: `src/pages/<NamePage>.tsx` (thin AppLayout wrapper)
383
- 4. **Register the route** in `src/shared/config/navigation.ts`:
384
- ```ts
385
- import { IconName } from 'lucide-react';
386
-
387
- { path: '/route', label: 'Label', labelKey: 'nav.keyName',
388
- icon: IconName, allowedRoles: ['admin', 'parceira'], group: 'groupName' }
389
- ```
390
- 5. **Register the `<Route>`** in `src/app/components/AuthGuard.tsx`:
391
- ```tsx
392
- <Route path="/route" element={
393
- <RoleRoute allowedRoles={['admin', 'parceira']}>
394
- <NamePage />
395
- </RoleRoute>
396
- } />
397
- ```
398
- 6. **Add the i18n key** to `src/locales/<lang>/nav.json` for every configured language
399
- 7. **Add a navigation card** to `HomeContent.tsx` if this is a primary module
400
-
401
- ---
402
-
403
- ## 6. Authentication Pattern
404
-
405
- Auth state lives in `src/app/context/AuthContext.tsx` via `useAuth()`:
406
-
407
- ```tsx
408
- const { user, isLoading, login, logout } = useAuth();
409
-
410
- // isLoading: true while localStorage is being read — ALWAYS check before redirecting
411
- // user: null when not authenticated; User object when authenticated
412
- // login(email, password): returns boolean — true on success
413
- // logout(): clears storage, navigates to /login
414
- ```
415
-
416
- ### Route Guards
417
-
418
- Three guard components are defined in `AuthGuard.tsx`:
419
-
420
- | Guard | Behavior |
421
- |---|---|
422
- | `<GuestRoute>` | Auth pages only. Redirects authenticated users to `getRoleHome(role)` |
423
- | `<ProtectedRoute>` | Any authenticated user. Redirects unauthenticated to `/login` |
424
- | `<RoleRoute allowedRoles={[]}>` | Role-specific. Unauthorized roles redirect to `getRoleHome(role)` — never to `/login` |
425
-
426
- ```tsx
427
- // getRoleHome(role) — determines post-login landing per role
428
- // Default pattern: most roles → '/home', restricted roles → their designated page
429
- ```
430
-
431
- ### MOCK_USERS in AuthContext
432
-
433
- For prototypes, credentials are stored in `MOCK_USERS` in `AuthContext.tsx`. Each entry maps email → `{ password, name, role, avatar?, clienteId? }`. The `clienteId` field is used for data isolation (e.g., a client user who can only see their own records).
434
-
435
- ---
436
-
437
- ## 7. Navigation and Sidebar Configuration
438
-
439
- ### `RouteConfig` Shape (`src/shared/config/navigation.ts`)
440
-
441
- ```ts
442
- export interface RouteConfig {
443
- path: string;
444
- label: string; // Fallback display label
445
- labelKey: string; // i18n key: 'nav.myRoute'
446
- icon: ComponentType<any>; // lucide-react icon
447
- allowedRoles: UserRole[];
448
- group?: string; // Groups routes visually in the sidebar
449
- }
450
- ```
451
-
452
- ### Filtering Routes by Role
453
-
454
- `AppLayout.tsx` uses `getRoutesByRole(user.role)` to pass only accessible routes to `<Sidebar>`. Each route's `allowedRoles` controls visibility — it mirrors `<RoleRoute>` in `AuthGuard.tsx`.
455
-
456
- ### Dynamic Label Enrichment
457
-
458
- The sidebar `label` string can be enriched at runtime (e.g., adding a pending count badge). Do this in `AppLayout.tsx` via a `routes.map()` before passing to `<Sidebar>`:
459
-
460
- ```tsx
461
- const allowedRoutes = baseRoutes.map(route => {
462
- if (route.path === '/target' && count > 0) {
463
- return { ...route, label: `${route.label} (${count})` };
464
- }
465
- return route;
466
- });
467
- ```
468
-
469
- ---
470
-
471
- ## 8. State Management
472
-
473
- | Layer | Tool | Use for |
474
- |---|---|---|
475
- | Server state | React Query (`useQuery`) | Data from APIs / mock fetch functions |
476
- | Client UI state | Zustand | Filters, tabs, toggles shared across components |
477
- | Auth state | `AuthContext` / `useAuth()` | Current user, login, logout |
478
- | Layout state | `LayoutContext` / `useLayout()` | Sidebar width, assistant panel |
479
- | Local component state | `useState` | Dialogs open/closed, inline edits, ephemeral form state |
480
-
481
- ### React Query — Required Pattern
482
-
483
- ```ts
484
- // features/<domain>/hooks/use<Domain>.ts
485
- import { useQuery } from '@tanstack/react-query';
486
- import { useLanguage } from 'xertica-ui/hooks';
487
- import { fetchItems, type Item } from '../data/mock';
488
-
489
- export function useItems() {
490
- const { language } = useLanguage(); // include language when response has translated strings
491
- return useQuery<Item[]>({
492
- queryKey: ['items', language],
493
- queryFn: fetchItems,
494
- staleTime: 5 * 60 * 1000, // set appropriate staleTime — never leave at default 0
495
- });
496
- }
497
- ```
498
-
499
- ### Zustand — Required Pattern
500
-
501
- ```ts
502
- // features/<domain>/store/<domain>Store.ts
503
- import { create } from 'zustand';
504
-
505
- interface MyStore {
506
- activeTab: string;
507
- setActiveTab: (tab: string) => void;
508
- }
509
-
510
- export const useMyStore = create<MyStore>(set => ({
511
- activeTab: 'overview',
512
- setActiveTab: tab => set({ activeTab: tab }),
513
- }));
514
-
515
- // In component — always subscribe with a selector to avoid unnecessary re-renders:
516
- const activeTab = useMyStore(s => s.activeTab); // ✅ selector
517
- const store = useMyStore(); // ❌ subscribes to everything
518
- ```
519
-
520
- ### Swap Pattern — Replacing Mock with Real API
521
-
522
- Replace only the fetch function body in `data/mock.ts`. The hook, component, and type contract are unchanged:
523
-
524
- ```ts
525
- // Before (mock)
526
- export async function fetchItems(): Promise<Item[]> {
527
- await new Promise(r => setTimeout(r, 300));
528
- return MOCK_ITEMS;
529
- }
530
-
531
- // After (real API)
532
- export async function fetchItems(): Promise<Item[]> {
533
- const res = await fetch('/api/items', {
534
- headers: { 'Accept-Language': i18n.language },
535
- });
536
- if (!res.ok) throw new Error('Failed to fetch');
537
- return res.json();
538
- }
539
- ```
540
-
541
- ---
542
-
543
- ## 9. Loading States — Skeletons
544
-
545
- Always render a skeleton — never a spinner alone — for data-bearing surfaces. Spinners are acceptable only for inline actions (button submitting, "saving…"):
546
-
547
- ```tsx
548
- // Cards grid
549
- {isLoading
550
- ? Array.from({ length: 6 }).map((_, i) => <FeatureCardSkeleton key={i} showAction />)
551
- : items.map(item => <FeatureCard key={item.id} {...item} />)
552
- }
553
-
554
- // Tables
555
- {isLoading
556
- ? Array.from({ length: 5 }).map((_, i) => (
557
- <TableRow key={i}>
558
- <TableCell><Skeleton className="h-3.5 w-28" /></TableCell>
559
- <TableCell><Skeleton className="h-5 w-20 rounded-full" /></TableCell>
560
- <TableCell><Skeleton className="h-3.5 w-16" /></TableCell>
561
- </TableRow>
562
- ))
563
- : rows.map(row => <TableRow key={row.id}>{/* cells */}</TableRow>)
564
- }
565
-
566
- // StatsCards row
567
- {isLoading
568
- ? Array.from({ length: 4 }).map((_, i) => <StatsCardSkeleton key={i} />)
569
- : stats.map(s => <StatsCard key={s.id} {...s} />)
570
- }
571
- ```
572
-
573
- Skeleton companions available: `FeatureCardSkeleton`, `QuickActionCardSkeleton`, `ProjectCardSkeleton`, `ActivityCardSkeleton`, `NotificationCardSkeleton`, `ProfileCardSkeleton`, `StatsCardSkeleton`.
574
-
575
- ---
576
-
577
- ## 10. Internationalization
578
-
579
- ### Setup (CLI-generated — do not hand-edit)
580
-
581
- `src/i18n.ts` uses `import.meta.glob` to auto-discover all JSON files under `src/locales/<lang>/`. Adding a new JSON file requires no changes to `i18n.ts`.
582
-
583
- ### Using Translations in Components
584
-
585
- ```tsx
586
- import { useTranslation } from 'react-i18next';
587
-
588
- function MyComponent() {
589
- const { t } = useTranslation();
590
- return (
591
- <div>
592
- <h1>{t('home.welcome')}</h1>
593
- <Button>{t('common.save')}</Button>
594
- </div>
595
- );
596
- }
597
- ```
598
-
599
- Applies to **all** user-facing strings: labels, placeholders, `aria-label`, toast messages, error text, dropdown items, tooltips.
600
-
601
- ### Using Translations Outside Components
602
-
603
- In `data/mock.ts` and utility files, use the `i18n` instance directly (not the hook):
604
-
605
- ```ts
606
- import i18n from '../../../i18n';
607
-
608
- export async function fetchFeatureCards() {
609
- return [{ title: i18n.t('home.cardsTitle') }]; // evaluated at query time
610
- }
611
- ```
612
-
613
- ### Frozen Constants vs Factory Functions
614
-
615
- ```ts
616
- // ❌ Wrong — frozen at module load time in the initial language
617
- export const OPTIONS = [i18n.t('option.first'), i18n.t('option.second')];
618
-
619
- // ✅ Correct — re-evaluated on every call, always returns current language
620
- export function getOptions() {
621
- return [i18n.t('option.first'), i18n.t('option.second')];
622
- }
623
- ```
624
-
625
- ### Language-Aware queryKey (Mandatory)
626
-
627
- Every React Query hook whose response contains translated strings must include `language` in its `queryKey`:
628
-
629
- ```ts
630
- const { language } = useLanguage();
631
- return useQuery({ queryKey: ['items', language], queryFn: fetchItems });
632
- ```
633
-
634
- ### Monolingual Projects
635
-
636
- When only one language is configured, `<LanguageSelector>` auto-hides. To force it visible: `<LanguageSelector showWhenMonolingual />`.
637
-
638
- ### Adding / Removing Languages
639
-
640
- ```bash
641
- npx xertica-ui update
642
- # → select "Languages"
643
- ```
644
-
645
- ---
646
-
647
- ## 11. Component Selection Guide
648
-
649
- For a full decision tree see `node_modules/xertica-ui/docs/decision-tree.md`. Key shortcuts:
650
-
651
- | Scenario | Component |
652
- |---|---|
653
- | "Are you sure you want to delete?" (blocking) | `AlertDialog` |
654
- | Compact form or detail view in overlay | `Dialog` |
655
- | Wide edit form or detail panel | `Sheet` |
656
- | Action feedback (auto-dismisses) | `toast.success/error/info/warning()` |
657
- | Persistent inline warning / status banner | `Alert variant="warning"` |
658
- | KPI number cards | `StatsCard` |
659
- | Feature/module navigation cards | `FeatureCard` |
660
- | Project status with progress bar | `ProjectCard` |
661
- | Chronological event list | `ActivityCard` |
662
- | Icon button label (hover) | `Tooltip` |
663
- | User profile preview (hover) | `HoverCard` |
664
- | Date picker | `Calendar` inside `Popover` |
665
- | Filter panel (click-triggered) | `Popover` |
666
- | Page sections (horizontal switch) | `Tabs` |
667
- | Expandable single section | `Collapsible` |
668
- | Settings with multiple expandable groups | `Accordion` |
669
- | Rich text editing | `RichTextEditor` |
670
- | OTP / 2FA code input | `InputOTP` |
671
- | Step-by-step flow | `Stepper` |
672
- | Known % completion progress | `Progress` |
673
- | Unknown completion (page/card loading) | `Skeleton` |
674
- | Tabular records | `Table` + `Pagination` |
675
-
676
- ---
677
-
678
- ## 12. Page Patterns (from `docs/patterns/`)
679
-
680
- ### Dashboard Pattern
681
-
682
- ```tsx
683
- <div className="flex flex-col gap-6 p-6 max-w-[1400px] mx-auto">
684
- {/* 1 — Page title + CTA */}
685
- <div className="flex items-center justify-between">
686
- <h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
687
- <Button>Download Report</Button>
688
- </div>
689
-
690
- {/* 2 — KPI Stats Row */}
691
- <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4">
692
- <StatsCard title="Revenue" value="$45,231" icon={<DollarSign className="size-4" />} />
693
- {/* ... */}
694
- </div>
695
-
696
- {/* 3 — Chart + Feed in 7-column grid */}
697
- <div className="grid gap-4 grid-cols-1 lg:grid-cols-7">
698
- <Card className="col-span-1 lg:col-span-4"> {/* Chart */} </Card>
699
- <Card className="col-span-1 lg:col-span-3"> {/* Activity feed */} </Card>
700
- </div>
701
- </div>
702
- ```
703
-
704
- ### Form Pattern (react-hook-form + zod)
705
-
706
- ```tsx
707
- const schema = z.object({
708
- name: z.string().min(2),
709
- email: z.string().email(),
710
- role: z.enum(['admin', 'user']),
711
- });
712
-
713
- function MyForm() {
714
- const form = useForm({ resolver: zodResolver(schema) });
715
-
716
- return (
717
- <Form {...form}>
718
- <form onSubmit={form.handleSubmit(onSubmit)}>
719
- <FormField control={form.control} name="name" render={({ field }) => (
720
- <FormItem>
721
- <FormLabel>Name</FormLabel>
722
- <FormControl><Input {...field} /></FormControl>
723
- <FormMessage />
724
- </FormItem>
725
- )} />
726
- <Button type="submit">Save</Button>
727
- </form>
728
- </Form>
729
- );
730
- }
731
- ```
732
-
733
- ### CRUD Table Pattern
734
-
735
- ```tsx
736
- <Card>
737
- <CardHeader className="flex flex-row items-center justify-between">
738
- <CardTitle>Records</CardTitle>
739
- <Button size="sm"><Plus className="size-4 mr-2" />New Record</Button>
740
- </CardHeader>
741
- <CardContent>
742
- {/* Search + Filters */}
743
- <div className="flex gap-2 mb-4">
744
- <Input placeholder="Search..." className="max-w-sm" />
745
- <Select><SelectTrigger><SelectValue placeholder="Status" /></SelectTrigger>...</Select>
746
- </div>
747
- {/* Table */}
748
- <Table>
749
- <TableHeader>
750
- <TableRow>
751
- <TableHead>Name</TableHead>
752
- <TableHead>Status</TableHead>
753
- <TableHead className="text-right">Actions</TableHead>
754
- </TableRow>
755
- </TableHeader>
756
- <TableBody>
757
- {isLoading
758
- ? Array.from({ length: 5 }).map((_, i) => (
759
- <TableRow key={i}>
760
- <TableCell><Skeleton className="h-4 w-32" /></TableCell>
761
- <TableCell><Skeleton className="h-5 w-16 rounded-full" /></TableCell>
762
- <TableCell className="text-right"><Skeleton className="h-8 w-8 ml-auto" /></TableCell>
763
- </TableRow>
764
- ))
765
- : rows.map(row => <TableRow key={row.id}>{/* ... */}</TableRow>)
766
- }
767
- </TableBody>
768
- </Table>
769
- </CardContent>
770
- </Card>
771
- ```
772
-
773
- ---
774
-
775
- ## 13. Error Boundaries
776
-
777
- Three pre-configured wrappers for granular error isolation:
778
-
779
- ```tsx
780
- import { AppErrorBoundary, PageErrorBoundary, SectionErrorBoundary }
781
- from '../shared/error-boundary';
782
-
783
- // App.tsx — wraps the entire application
784
- <AppErrorBoundary>
785
- <QueryClientProvider>
786
- <XerticaProvider>
787
- <Router>
788
- <AuthProvider>
789
- <PageErrorBoundary> {/* wraps the route tree */}
790
- <Suspense fallback={null}>
791
- <AuthGuard />
792
- </Suspense>
793
- </PageErrorBoundary>
794
- </AuthProvider>
795
- </Router>
796
- </XerticaProvider>
797
- </QueryClientProvider>
798
- </AppErrorBoundary>
799
-
800
- // Inside pages — wraps data-dependent sections
801
- <SectionErrorBoundary>
802
- <MyDataDrivenComponent />
803
- </SectionErrorBoundary>
804
- ```
805
-
806
- ---
807
-
808
- ## 14. Theme and Token Customization
809
-
810
- Edit only `src/styles/xertica/tokens.css` — never modify anything in `node_modules/xertica-ui/styles/`.
811
-
812
- To switch themes via CLI:
813
- ```bash
814
- npx xertica-ui update
815
- # → select "Theme only"
816
- ```
817
-
818
- The token file uses HSL values and supports both light and dark modes via the `.dark` class on `<html>`. CSS variable names follow the shadcn convention: `--background`, `--foreground`, `--primary`, `--destructive`, etc.
819
-
820
- ---
821
-
822
- ## 15. AI Agent Reference
823
-
824
- | What you need | Where to read |
825
- |---|---|
826
- | Complete component catalog | `node_modules/xertica-ui/llms-compact.txt` |
827
- | Full component API with all props | `node_modules/xertica-ui/llms-full.txt` |
828
- | "Dialog or Sheet? Tooltip or Popover?" | `node_modules/xertica-ui/docs/decision-tree.md` |
829
- | Specific component props | `node_modules/xertica-ui/docs/components/<name>.md` |
830
- | Mandatory constraint list for AI | `node_modules/xertica-ui/docs/ai-usage.md` |
831
- | i18n setup and language switching | `node_modules/xertica-ui/docs/i18n.md` |
832
- | State management patterns | `node_modules/xertica-ui/docs/state-management.md` |
833
- | Layout system (sidebar width, assistant) | `node_modules/xertica-ui/docs/layout.md` |
834
- | Page-level composition patterns | `node_modules/xertica-ui/docs/patterns/*.md` |
835
- | This project's architecture rules | `guidelines/Guidelines.md` (you are here) |
836
-
837
- ---
838
-
839
- ## 16. Development Checklist
840
-
841
- Before completing any feature:
842
-
843
- - [ ] Feature content is in `src/features/<name>/ui/`
844
- - [ ] Feature barrel at `src/features/<name>/index.ts` exports everything
845
- - [ ] Page is a thin wrapper in `src/pages/` — no logic, no state
846
- - [ ] Route registered in `src/shared/config/navigation.ts` with `label`, `labelKey`, `icon`, `allowedRoles`, `group`
847
- - [ ] Route registered in `src/app/components/AuthGuard.tsx` with correct guard (`ProtectedRoute` or `RoleRoute`)
848
- - [ ] i18n key added to `src/locales/<lang>/nav.json` for **all** configured languages
849
- - [ ] Navigation card added to `HomeContent.tsx` if this is a primary module
850
- - [ ] All data arrays live in `features/<name>/data/mock.ts`, not in components
851
- - [ ] Server state fetched via React Query hook — not `useState` + `useEffect`
852
- - [ ] React Query `queryKey` includes `language` when response contains translated strings
853
- - [ ] `staleTime` is set on every `useQuery` call — never left at default `0`
854
- - [ ] Client UI state (filters, tabs) uses Zustand with selectors — not prop drilling
855
- - [ ] Only `xertica-ui` components used — no raw HTML elements
856
- - [ ] No raw hex/rgb/hsl values in `className` or `style` — semantic tokens for status contexts
857
- - [ ] No hardcoded border radii — `rounded-[var(--radius)]`
858
- - [ ] Icons from `lucide-react` only — no inline SVG
859
- - [ ] Layout state from `useLayout()` — no hardcoded `padding-left` or sidebar widths
860
- - [ ] Destructive actions wrapped in `<AlertDialog>` with explicit confirm/cancel
861
- - [ ] Forms use `react-hook-form` + `zod` — no manual `useState` + if-validation
862
- - [ ] Loading states render a `*Skeleton` component, not a blank space or spinner alone
863
- - [ ] All user-facing strings go through `useTranslation()` — no hardcoded UI text
864
- - [ ] `toast` used for action feedback — message goes through `t()`
865
- - [ ] Responsive: tested on mobile (< 640px), tablet (640–1024px), desktop (> 1024px)
1
+ # Project Guidelines — Xertica UI App
2
+
3
+ > **Scope**: These guidelines apply to any React application scaffolded with `npx xertica-ui@latest init`. They define architecture, import conventions, component rules, state management, and AI agent behavior.
4
+ >
5
+ > **AI agents — reading order**:
6
+ > 1. This file (architecture + rules)
7
+ > 2. `node_modules/xertica-ui/llms-compact.txt` (component catalog + quick reference)
8
+ > 3. `node_modules/xertica-ui/docs/decision-tree.md` (when choosing between similar components)
9
+ > 4. `node_modules/xertica-ui/docs/components/<name>.md` (when you need a specific component's full props)
10
+ > 5. `node_modules/xertica-ui/docs/ai-usage.md` (mandatory constraint list)
11
+
12
+ ---
13
+
14
+ ## 1. Architecture — Feature-Sliced Design + Feature-Driven Architecture
15
+
16
+ This project follows **FSD (Feature-Sliced Design)** layered architecture combined with **FDA (Feature-Driven Architecture)** vertical slicing. **Layers can only import from layers below them.**
17
+
18
+ ```
19
+ src/
20
+ ├── app/ # Layer 1 — Application shell (imports from all layers)
21
+ │ ├── App.tsx # XerticaProvider + BrowserRouter + AuthGuard (CLI-generated)
22
+ │ ├── context/
23
+ │ │ └── AuthContext.tsx # AuthProvider + useAuth() hook
24
+ │ └── components/
25
+ │ ├── AppLayout.tsx # Sidebar + children + optional XerticaAssistant
26
+ │ └── AuthGuard.tsx # All route definitions + route guard components
27
+
28
+ ├── shared/ # Layer 2 — Shared utilities (no business logic)
29
+ │ ├── config/
30
+ │ │ └── navigation.ts # RouteConfig type, routes[], getRoutesByRole()
31
+ │ ├── mock/ # Domain mock data (TypeScript static arrays + helpers)
32
+ │ ├── lib/
33
+ │ │ └── auth.ts # getStoredUser, storeUser, clearStoredUser
34
+ │ └── types/
35
+ │ └── auth.ts # User interface + UserRole type
36
+
37
+ ├── features/ # Layer 3 — Vertical slices by business capability
38
+ │ ├── auth/ # Login, ForgotPassword, VerifyEmail, ResetPassword
39
+ │ ├── home/ # Post-login hub (FeatureCards grid)
40
+ │ │ ├── data/mock.ts # Types + async fetch + factory functions (getMockXxx)
41
+ │ │ ├── hooks/useFeatureCards.ts # React Query, queryKey includes `language`
42
+ │ │ ├── store/dashboardStore.ts # Zustand UI state (no server data)
43
+ │ │ ├── ui/HomeContent.tsx
44
+ │ │ └── index.ts
45
+ │ ├── assistant/ # XerticaAssistant — AppLayout depends on it
46
+ │ │ ├── data/mock.ts
47
+ │ │ ├── hooks/useAssistantConfig.ts
48
+ │ │ └── index.ts
49
+ │ └── <domain>/ # One slice per business domain
50
+ │ ├── data/mock.ts # Domain types + mock fetch functions
51
+ │ ├── hooks/use<Domain>.ts # React Query wrapper
52
+ │ ├── store/<domain>Store.ts # Zustand UI state (optional)
53
+ │ ├── ui/<Domain>Content.tsx # Page content component
54
+ │ └── index.ts # Barrel — ONLY public interface for this feature
55
+
56
+ ├── pages/ # Layer 4 — Thin route wrappers only
57
+ │ └── <Name>Page.tsx # AppLayout + <NameContent> + optional XerticaAssistant
58
+
59
+ ├── i18n.ts # i18next setup (CLI-generated — do not hand-edit)
60
+ ├── locales/
61
+ │ ├── .languages.json # CLI-managed language selection
62
+ │ └── pt-BR/ # One folder per language
63
+ │ ├── common.json # Shared action labels
64
+ │ ├── nav.json # Navigation labels
65
+ │ ├── errors.json # Error boundary UI
66
+ │ ├── pages/ # Per-page namespaces
67
+ │ └── components/ # Per-component namespaces
68
+ └── styles/
69
+ ├── index.css # Imports: xertica-ui/style.css, tokens.css, @source
70
+ └── xertica/tokens.css # Brand CSS variables — customize here only
71
+ ```
72
+
73
+ ### Layer Import Rules
74
+
75
+ - `app/` → can import from all layers
76
+ - `shared/` → cannot import from `features/` or `pages/`
77
+ - `features/<a>/` → cannot import from `features/<b>/` — extract shared code to `shared/`
78
+ - `pages/` → imports only from `features/` barrels and `app/`
79
+
80
+ ### Feature Barrel Rule
81
+
82
+ The `index.ts` is the **only** public interface of a feature. Always import from the barrel, never from internal paths:
83
+
84
+ ```tsx
85
+ // ✅ Correct
86
+ import { LoginContent } from '../features/auth';
87
+
88
+ // ❌ Wrong — never import from internal paths
89
+ import { LoginContent } from '../features/auth/ui/LoginContent';
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 2. Import Rules
95
+
96
+ Always use the correct `xertica-ui` subpath for the domain you need:
97
+
98
+ ```tsx
99
+ // UI primitives — buttons, inputs, dialogs, tables, badges, etc.
100
+ import {
101
+ Button, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
102
+ Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter,
103
+ Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
104
+ AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader,
105
+ AlertDialogTitle, AlertDialogDescription, AlertDialogFooter,
106
+ AlertDialogCancel, AlertDialogAction,
107
+ Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
108
+ Badge, Progress, ScrollArea, Separator, Skeleton, Label, Switch,
109
+ Tabs, TabsContent, TabsList, TabsTrigger,
110
+ Collapsible, CollapsibleContent, CollapsibleTrigger,
111
+ Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,
112
+ Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,
113
+ Popover, PopoverContent, PopoverTrigger,
114
+ Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription,
115
+ Alert, AlertDescription, Drawer,
116
+ } from 'xertica-ui/ui';
117
+
118
+ // Composed high-level block components
119
+ import {
120
+ FeatureCard, FeatureCardSkeleton,
121
+ QuickActionCard, QuickActionCardSkeleton,
122
+ ProjectCard, ProjectCardSkeleton,
123
+ ActivityCard, ActivityCardSkeleton,
124
+ NotificationCard, NotificationCardSkeleton,
125
+ ProfileCard, ProfileCardSkeleton,
126
+ StatsCard, StatsCardSkeleton,
127
+ } from 'xertica-ui/blocks';
128
+
129
+ // Application shell
130
+ import { Sidebar, Header } from 'xertica-ui/layout';
131
+
132
+ // Brand / providers
133
+ import { XerticaProvider, XerticaLogo, ThemeToggle, LanguageSelector } from 'xertica-ui/brand';
134
+
135
+ // AI assistant
136
+ import { XerticaAssistant, generateDemoResponse } from 'xertica-ui/assistant';
137
+
138
+ // Media players
139
+ import { VideoPlayer, AudioPlayer } from 'xertica-ui/media';
140
+
141
+ // Hooks
142
+ import { useLayout, useOptionalLayout, useTheme, useLanguage } from 'xertica-ui/hooks';
143
+
144
+ // Icons — ALWAYS from lucide-react, NEVER from xertica-ui or inline SVG
145
+ import { Home, Settings, Plus, Trash2, ChevronRight, Search } from 'lucide-react';
146
+
147
+ // Charts — use recharts (pre-installed)
148
+ import {
149
+ BarChart, Bar, LineChart, Line, PieChart, Pie, Cell,
150
+ XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,
151
+ } from 'recharts';
152
+
153
+ // Toast
154
+ import { toast } from 'sonner';
155
+
156
+ // Form validation
157
+ import { useForm } from 'react-hook-form';
158
+ import { zodResolver } from '@hookform/resolvers/zod';
159
+ import * as z from 'zod';
160
+
161
+ // i18n
162
+ import { useTranslation } from 'react-i18next';
163
+ ```
164
+
165
+ > **Never import from `xertica-ui` root barrel in FSD/FDA projects.** Always use the granular subpaths above — they produce smaller bundles and prevent accidental cross-domain coupling.
166
+
167
+ ---
168
+
169
+ ## 3. Non-Negotiable Rules
170
+
171
+ ### 3.1 No Raw HTML for UI Surfaces
172
+
173
+ | ❌ Never | ✅ Always |
174
+ |---|---|
175
+ | `<button>` | `<Button>` from `xertica-ui/ui` |
176
+ | `<input>` | `<Input>` from `xertica-ui/ui` |
177
+ | `<select>` | `<Select>` from `xertica-ui/ui` |
178
+ | `<textarea>` | `<Textarea>` from `xertica-ui/ui` |
179
+ | `<div class="card ...">` | `<Card>` from `xertica-ui/ui` |
180
+ | Custom scrollable `<div>` | `<ScrollArea>` from `xertica-ui/ui` |
181
+ | Raw `<progress>` | `<Progress>` from `xertica-ui/ui` |
182
+ | Inline SVG `<svg><path .../></svg>` | `import { IconName } from 'lucide-react'` |
183
+
184
+ ### 3.2 Color Rules
185
+
186
+ **Always forbidden:**
187
+ ```tsx
188
+ // ❌ Never — raw values are non-themeable
189
+ style={{ backgroundColor: '#3B82F6' }}
190
+ style={{ color: 'rgb(59,130,246)' }}
191
+ className="text-[#ffffff]"
192
+ ```
193
+
194
+ **Required for semantic/status contexts** (errors, warnings, success, danger actions, status badges):
195
+ ```tsx
196
+ // ❌ Wrong — Tailwind color for semantic state
197
+ <div className="bg-red-500">Error</div>
198
+
199
+ // ✅ Required — token adapts to theme and dark mode
200
+ <div className="bg-destructive text-destructive-foreground">Error</div>
201
+ ```
202
+
203
+ **Acceptable for non-semantic layout** (spacing, decorative elements, charts):
204
+ ```tsx
205
+ // ✅ OK — non-semantic context, no theme dependency
206
+ <div className="bg-blue-500/15 border-gray-200">Decorative</div>
207
+ ```
208
+
209
+ Semantic token reference:
210
+ | Intent | Token class |
211
+ |---|---|
212
+ | Error / Danger | `bg-destructive` / `text-destructive` |
213
+ | Warning | `bg-warning` / `text-warning` |
214
+ | Success | `bg-success` / `text-success` |
215
+ | Informational | `bg-info` / `text-info` |
216
+ | Neutral/secondary | `bg-secondary` / `text-secondary-foreground` |
217
+ | Primary action | `bg-primary` / `text-primary-foreground` |
218
+ | Muted background | `bg-muted` / `text-muted-foreground` |
219
+
220
+ ### 3.3 Border Radius — Never Hardcode
221
+
222
+ ```tsx
223
+ // ❌ Wrong
224
+ className="rounded-lg"
225
+ className="rounded-xl"
226
+
227
+ // ✅ Correct — inherits the project's configured radius token
228
+ className="rounded-[var(--radius)]"
229
+ ```
230
+
231
+ ### 3.4 Layout State — Never Hardcode
232
+
233
+ ```tsx
234
+ // ❌ Wrong — breaks when sidebar width changes
235
+ <div style={{ paddingLeft: '280px' }}>
236
+
237
+ // ✅ Correct — always from context
238
+ const { sidebarExpanded, sidebarWidth } = useLayout();
239
+ <div style={{ paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px' }}>
240
+ ```
241
+
242
+ `80px` is the fixed collapsed sidebar width. The expanded value (`sidebarWidth`) defaults to `280px` but is configurable. Never hardcode either value.
243
+
244
+ ### 3.5 Destructive Actions — Always Confirm with AlertDialog
245
+
246
+ ```tsx
247
+ <AlertDialog>
248
+ <AlertDialogTrigger asChild>
249
+ <Button variant="destructive">Delete Record</Button>
250
+ </AlertDialogTrigger>
251
+ <AlertDialogContent>
252
+ <AlertDialogHeader>
253
+ <AlertDialogTitle>Are you sure?</AlertDialogTitle>
254
+ <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
255
+ </AlertDialogHeader>
256
+ <AlertDialogFooter>
257
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
258
+ <AlertDialogAction onClick={handleDelete}>Delete</AlertDialogAction>
259
+ </AlertDialogFooter>
260
+ </AlertDialogContent>
261
+ </AlertDialog>
262
+ ```
263
+
264
+ ### 3.6 Toast Notifications
265
+
266
+ Use `toast` from `sonner` for ephemeral action feedback. Never render `<Toaster>` manually — it is auto-injected by `<XerticaProvider>`.
267
+
268
+ ```tsx
269
+ import { toast } from 'sonner';
270
+
271
+ toast.success(t('users.createSuccess'));
272
+ toast.error(t('errors.somethingWentWrong'));
273
+ toast.info(t('feature.inDevelopment'));
274
+ toast.warning(t('billing.syncConflict'));
275
+ ```
276
+
277
+ ### 3.7 Form Validation — react-hook-form + zod
278
+
279
+ Never implement manual validation with `useState` + conditional checks:
280
+
281
+ ```tsx
282
+ // ❌ Wrong — manual validation
283
+ const [error, setError] = useState('');
284
+ if (!email.includes('@')) setError('Invalid email');
285
+
286
+ // ✅ Correct — declarative schema, errors flow automatically
287
+ const schema = z.object({
288
+ email: z.string().email('Invalid email address'),
289
+ name: z.string().min(2, 'Name must be at least 2 characters'),
290
+ });
291
+
292
+ const form = useForm({ resolver: zodResolver(schema) });
293
+ // Error messages render automatically via <FormMessage />
294
+ ```
295
+
296
+ ### 3.8 Mock Data Belongs in `data/mock.ts`
297
+
298
+ Never hardcode data arrays inside component files:
299
+
300
+ ```tsx
301
+ // ❌ Wrong — data inside the component
302
+ const users = [{ id: 1, name: 'Alice' }, ...];
303
+
304
+ // ✅ Correct — data in features/<domain>/data/mock.ts
305
+ export async function fetchUsers(): Promise<User[]> {
306
+ await new Promise(r => setTimeout(r, 300)); // simulate network
307
+ return MOCK_USERS;
308
+ }
309
+ ```
310
+
311
+ ---
312
+
313
+ ## 4. Page Structure — Standard Shell
314
+
315
+ Every content component follows this exact shell. No exceptions:
316
+
317
+ ```tsx
318
+ import { Link } from 'react-router-dom';
319
+ import { Header } from 'xertica-ui/layout';
320
+ import { ScrollArea } from 'xertica-ui/ui';
321
+ import { useLayout } from 'xertica-ui/hooks';
322
+ import { useTranslation } from 'react-i18next';
323
+
324
+ export function ExampleContent() {
325
+ const { t } = useTranslation();
326
+ const { sidebarExpanded, sidebarWidth } = useLayout();
327
+
328
+ return (
329
+ <div
330
+ style={{ paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px' }}
331
+ className="flex-1 flex flex-col overflow-hidden transition-all duration-300"
332
+ >
333
+ {/* Header with breadcrumbs + theme/language controls */}
334
+ <Header
335
+ showThemeToggle
336
+ showLanguageSelector
337
+ breadcrumbs={[
338
+ { label: t('nav.home', 'App'), href: '/home' },
339
+ { label: t('nav.thisPage', 'This Page') },
340
+ ]}
341
+ renderLink={(href, props) => <Link to={href} {...props} />}
342
+ />
343
+
344
+ {/* Scrollable main content */}
345
+ <main className="flex-1 overflow-hidden bg-muted">
346
+ <ScrollArea className="h-full">
347
+ <div className="p-4 md:p-6">
348
+ <div className="max-w-7xl mx-auto space-y-6">
349
+ {/* 1. KPI StatsCards row (if applicable) */}
350
+ {/* 2. Main content cards */}
351
+ {/* 3. Tables with filters */}
352
+ </div>
353
+ </div>
354
+ </ScrollArea>
355
+ </main>
356
+
357
+ {/* Dialogs / Sheets rendered OUTSIDE <main> */}
358
+ </div>
359
+ );
360
+ }
361
+ ```
362
+
363
+ ### Pages Are Thin Wrappers
364
+
365
+ ```tsx
366
+ // pages/ExamplePage.tsx — thin wrapper only, no logic
367
+ export function ExamplePage() {
368
+ return (
369
+ <AppLayout>
370
+ <ExampleContent />
371
+ </AppLayout>
372
+ );
373
+ }
374
+ ```
375
+
376
+ ---
377
+
378
+ ## 5. Adding a New Route (Step-by-Step)
379
+
380
+ 1. **Create feature content**: `src/features/<name>/ui/<NameContent>.tsx`
381
+ 2. **Export from the barrel**: `src/features/<name>/index.ts`
382
+ 3. **Create the page**: `src/pages/<NamePage>.tsx` (thin AppLayout wrapper)
383
+ 4. **Register the route** in `src/shared/config/navigation.ts`:
384
+ ```ts
385
+ import { IconName } from 'lucide-react';
386
+
387
+ { path: '/route', label: 'Label', labelKey: 'nav.keyName',
388
+ icon: IconName, allowedRoles: ['admin', 'parceira'], group: 'groupName' }
389
+ ```
390
+ 5. **Register the `<Route>`** in `src/app/components/AuthGuard.tsx`:
391
+ ```tsx
392
+ <Route path="/route" element={
393
+ <RoleRoute allowedRoles={['admin', 'parceira']}>
394
+ <NamePage />
395
+ </RoleRoute>
396
+ } />
397
+ ```
398
+ 6. **Add the i18n key** to `src/locales/<lang>/nav.json` for every configured language
399
+ 7. **Add a navigation card** to `HomeContent.tsx` if this is a primary module
400
+
401
+ ---
402
+
403
+ ## 6. Authentication Pattern
404
+
405
+ Auth state lives in `src/app/context/AuthContext.tsx` via `useAuth()`:
406
+
407
+ ```tsx
408
+ const { user, isLoading, login, logout } = useAuth();
409
+
410
+ // isLoading: true while localStorage is being read — ALWAYS check before redirecting
411
+ // user: null when not authenticated; User object when authenticated
412
+ // login(email, password): returns boolean — true on success
413
+ // logout(): clears storage, navigates to /login
414
+ ```
415
+
416
+ ### Route Guards
417
+
418
+ Three guard components are defined in `AuthGuard.tsx`:
419
+
420
+ | Guard | Behavior |
421
+ |---|---|
422
+ | `<GuestRoute>` | Auth pages only. Redirects authenticated users to `getRoleHome(role)` |
423
+ | `<ProtectedRoute>` | Any authenticated user. Redirects unauthenticated to `/login` |
424
+ | `<RoleRoute allowedRoles={[]}>` | Role-specific. Unauthorized roles redirect to `getRoleHome(role)` — never to `/login` |
425
+
426
+ ```tsx
427
+ // getRoleHome(role) — determines post-login landing per role
428
+ // Default pattern: most roles → '/home', restricted roles → their designated page
429
+ ```
430
+
431
+ ### MOCK_USERS in AuthContext
432
+
433
+ For prototypes, credentials are stored in `MOCK_USERS` in `AuthContext.tsx`. Each entry maps email → `{ password, name, role, avatar?, clienteId? }`. The `clienteId` field is used for data isolation (e.g., a client user who can only see their own records).
434
+
435
+ ---
436
+
437
+ ## 7. Navigation and Sidebar Configuration
438
+
439
+ ### `RouteConfig` Shape (`src/shared/config/navigation.ts`)
440
+
441
+ ```ts
442
+ export interface RouteConfig {
443
+ path: string;
444
+ label: string; // Fallback display label
445
+ labelKey: string; // i18n key: 'nav.myRoute'
446
+ icon: ComponentType<any>; // lucide-react icon
447
+ allowedRoles: UserRole[];
448
+ group?: string; // Groups routes visually in the sidebar
449
+ }
450
+ ```
451
+
452
+ ### Filtering Routes by Role
453
+
454
+ `AppLayout.tsx` uses `getRoutesByRole(user.role)` to pass only accessible routes to `<Sidebar>`. Each route's `allowedRoles` controls visibility — it mirrors `<RoleRoute>` in `AuthGuard.tsx`.
455
+
456
+ ### Dynamic Label Enrichment
457
+
458
+ The sidebar `label` string can be enriched at runtime (e.g., adding a pending count badge). Do this in `AppLayout.tsx` via a `routes.map()` before passing to `<Sidebar>`:
459
+
460
+ ```tsx
461
+ const allowedRoutes = baseRoutes.map(route => {
462
+ if (route.path === '/target' && count > 0) {
463
+ return { ...route, label: `${route.label} (${count})` };
464
+ }
465
+ return route;
466
+ });
467
+ ```
468
+
469
+ ---
470
+
471
+ ## 8. State Management
472
+
473
+ | Layer | Tool | Use for |
474
+ |---|---|---|
475
+ | Server state | React Query (`useQuery`) | Data from APIs / mock fetch functions |
476
+ | Client UI state | Zustand | Filters, tabs, toggles shared across components |
477
+ | Auth state | `AuthContext` / `useAuth()` | Current user, login, logout |
478
+ | Layout state | `LayoutContext` / `useLayout()` | Sidebar width, assistant panel |
479
+ | Local component state | `useState` | Dialogs open/closed, inline edits, ephemeral form state |
480
+
481
+ ### React Query — Required Pattern
482
+
483
+ ```ts
484
+ // features/<domain>/hooks/use<Domain>.ts
485
+ import { useQuery } from '@tanstack/react-query';
486
+ import { useLanguage } from 'xertica-ui/hooks';
487
+ import { fetchItems, type Item } from '../data/mock';
488
+
489
+ export function useItems() {
490
+ const { language } = useLanguage(); // include language when response has translated strings
491
+ return useQuery<Item[]>({
492
+ queryKey: ['items', language],
493
+ queryFn: fetchItems,
494
+ staleTime: 5 * 60 * 1000, // set appropriate staleTime — never leave at default 0
495
+ });
496
+ }
497
+ ```
498
+
499
+ ### Zustand — Required Pattern
500
+
501
+ ```ts
502
+ // features/<domain>/store/<domain>Store.ts
503
+ import { create } from 'zustand';
504
+
505
+ interface MyStore {
506
+ activeTab: string;
507
+ setActiveTab: (tab: string) => void;
508
+ }
509
+
510
+ export const useMyStore = create<MyStore>(set => ({
511
+ activeTab: 'overview',
512
+ setActiveTab: tab => set({ activeTab: tab }),
513
+ }));
514
+
515
+ // In component — always subscribe with a selector to avoid unnecessary re-renders:
516
+ const activeTab = useMyStore(s => s.activeTab); // ✅ selector
517
+ const store = useMyStore(); // ❌ subscribes to everything
518
+ ```
519
+
520
+ ### Swap Pattern — Replacing Mock with Real API
521
+
522
+ Replace only the fetch function body in `data/mock.ts`. The hook, component, and type contract are unchanged:
523
+
524
+ ```ts
525
+ // Before (mock)
526
+ export async function fetchItems(): Promise<Item[]> {
527
+ await new Promise(r => setTimeout(r, 300));
528
+ return MOCK_ITEMS;
529
+ }
530
+
531
+ // After (real API)
532
+ export async function fetchItems(): Promise<Item[]> {
533
+ const res = await fetch('/api/items', {
534
+ headers: { 'Accept-Language': i18n.language },
535
+ });
536
+ if (!res.ok) throw new Error('Failed to fetch');
537
+ return res.json();
538
+ }
539
+ ```
540
+
541
+ ---
542
+
543
+ ## 9. Loading States — Skeletons
544
+
545
+ Always render a skeleton — never a spinner alone — for data-bearing surfaces. Spinners are acceptable only for inline actions (button submitting, "saving…"):
546
+
547
+ ```tsx
548
+ // Cards grid
549
+ {isLoading
550
+ ? Array.from({ length: 6 }).map((_, i) => <FeatureCardSkeleton key={i} showAction />)
551
+ : items.map(item => <FeatureCard key={item.id} {...item} />)
552
+ }
553
+
554
+ // Tables
555
+ {isLoading
556
+ ? Array.from({ length: 5 }).map((_, i) => (
557
+ <TableRow key={i}>
558
+ <TableCell><Skeleton className="h-3.5 w-28" /></TableCell>
559
+ <TableCell><Skeleton className="h-5 w-20 rounded-full" /></TableCell>
560
+ <TableCell><Skeleton className="h-3.5 w-16" /></TableCell>
561
+ </TableRow>
562
+ ))
563
+ : rows.map(row => <TableRow key={row.id}>{/* cells */}</TableRow>)
564
+ }
565
+
566
+ // StatsCards row
567
+ {isLoading
568
+ ? Array.from({ length: 4 }).map((_, i) => <StatsCardSkeleton key={i} />)
569
+ : stats.map(s => <StatsCard key={s.id} {...s} />)
570
+ }
571
+ ```
572
+
573
+ Skeleton companions available: `FeatureCardSkeleton`, `QuickActionCardSkeleton`, `ProjectCardSkeleton`, `ActivityCardSkeleton`, `NotificationCardSkeleton`, `ProfileCardSkeleton`, `StatsCardSkeleton`.
574
+
575
+ ---
576
+
577
+ ## 10. Internationalization
578
+
579
+ ### Setup (CLI-generated — do not hand-edit)
580
+
581
+ `src/i18n.ts` uses `import.meta.glob` to auto-discover all JSON files under `src/locales/<lang>/`. Adding a new JSON file requires no changes to `i18n.ts`.
582
+
583
+ ### Using Translations in Components
584
+
585
+ ```tsx
586
+ import { useTranslation } from 'react-i18next';
587
+
588
+ function MyComponent() {
589
+ const { t } = useTranslation();
590
+ return (
591
+ <div>
592
+ <h1>{t('home.welcome')}</h1>
593
+ <Button>{t('common.save')}</Button>
594
+ </div>
595
+ );
596
+ }
597
+ ```
598
+
599
+ Applies to **all** user-facing strings: labels, placeholders, `aria-label`, toast messages, error text, dropdown items, tooltips.
600
+
601
+ ### Using Translations Outside Components
602
+
603
+ In `data/mock.ts` and utility files, use the `i18n` instance directly (not the hook):
604
+
605
+ ```ts
606
+ import i18n from '../../../i18n';
607
+
608
+ export async function fetchFeatureCards() {
609
+ return [{ title: i18n.t('home.cardsTitle') }]; // evaluated at query time
610
+ }
611
+ ```
612
+
613
+ ### Frozen Constants vs Factory Functions
614
+
615
+ ```ts
616
+ // ❌ Wrong — frozen at module load time in the initial language
617
+ export const OPTIONS = [i18n.t('option.first'), i18n.t('option.second')];
618
+
619
+ // ✅ Correct — re-evaluated on every call, always returns current language
620
+ export function getOptions() {
621
+ return [i18n.t('option.first'), i18n.t('option.second')];
622
+ }
623
+ ```
624
+
625
+ ### Language-Aware queryKey (Mandatory)
626
+
627
+ Every React Query hook whose response contains translated strings must include `language` in its `queryKey`:
628
+
629
+ ```ts
630
+ const { language } = useLanguage();
631
+ return useQuery({ queryKey: ['items', language], queryFn: fetchItems });
632
+ ```
633
+
634
+ ### Monolingual Projects
635
+
636
+ When only one language is configured, `<LanguageSelector>` auto-hides. To force it visible: `<LanguageSelector showWhenMonolingual />`.
637
+
638
+ ### Adding / Removing Languages
639
+
640
+ ```bash
641
+ npx xertica-ui update
642
+ # → select "Languages"
643
+ ```
644
+
645
+ ---
646
+
647
+ ## 11. Component Selection Guide
648
+
649
+ For a full decision tree see `node_modules/xertica-ui/docs/decision-tree.md`. Key shortcuts:
650
+
651
+ | Scenario | Component |
652
+ |---|---|
653
+ | "Are you sure you want to delete?" (blocking) | `AlertDialog` |
654
+ | Compact form or detail view in overlay | `Dialog` |
655
+ | Wide edit form or detail panel | `Sheet` |
656
+ | Action feedback (auto-dismisses) | `toast.success/error/info/warning()` |
657
+ | Persistent inline warning / status banner | `Alert variant="warning"` |
658
+ | KPI number cards | `StatsCard` |
659
+ | Feature/module navigation cards | `FeatureCard` |
660
+ | Project status with progress bar | `ProjectCard` |
661
+ | Chronological event list | `ActivityCard` |
662
+ | Icon button label (hover) | `Tooltip` |
663
+ | User profile preview (hover) | `HoverCard` |
664
+ | Date picker | `Calendar` inside `Popover` |
665
+ | Filter panel (click-triggered) | `Popover` |
666
+ | Page sections (horizontal switch) | `Tabs` |
667
+ | Expandable single section | `Collapsible` |
668
+ | Settings with multiple expandable groups | `Accordion` |
669
+ | Rich text editing | `RichTextEditor` |
670
+ | OTP / 2FA code input | `InputOTP` |
671
+ | Step-by-step flow | `Stepper` |
672
+ | Known % completion progress | `Progress` |
673
+ | Unknown completion (page/card loading) | `Skeleton` |
674
+ | Tabular records | `Table` + `Pagination` |
675
+
676
+ ---
677
+
678
+ ## 12. Page Patterns (from `docs/patterns/`)
679
+
680
+ ### Dashboard Pattern
681
+
682
+ ```tsx
683
+ <div className="flex flex-col gap-6 p-6 max-w-[1400px] mx-auto">
684
+ {/* 1 — Page title + CTA */}
685
+ <div className="flex items-center justify-between">
686
+ <h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
687
+ <Button>Download Report</Button>
688
+ </div>
689
+
690
+ {/* 2 — KPI Stats Row */}
691
+ <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4">
692
+ <StatsCard title="Revenue" value="$45,231" icon={<DollarSign className="size-4" />} />
693
+ {/* ... */}
694
+ </div>
695
+
696
+ {/* 3 — Chart + Feed in 7-column grid */}
697
+ <div className="grid gap-4 grid-cols-1 lg:grid-cols-7">
698
+ <Card className="col-span-1 lg:col-span-4"> {/* Chart */} </Card>
699
+ <Card className="col-span-1 lg:col-span-3"> {/* Activity feed */} </Card>
700
+ </div>
701
+ </div>
702
+ ```
703
+
704
+ ### Form Pattern (react-hook-form + zod)
705
+
706
+ ```tsx
707
+ const schema = z.object({
708
+ name: z.string().min(2),
709
+ email: z.string().email(),
710
+ role: z.enum(['admin', 'user']),
711
+ });
712
+
713
+ function MyForm() {
714
+ const form = useForm({ resolver: zodResolver(schema) });
715
+
716
+ return (
717
+ <Form {...form}>
718
+ <form onSubmit={form.handleSubmit(onSubmit)}>
719
+ <FormField control={form.control} name="name" render={({ field }) => (
720
+ <FormItem>
721
+ <FormLabel>Name</FormLabel>
722
+ <FormControl><Input {...field} /></FormControl>
723
+ <FormMessage />
724
+ </FormItem>
725
+ )} />
726
+ <Button type="submit">Save</Button>
727
+ </form>
728
+ </Form>
729
+ );
730
+ }
731
+ ```
732
+
733
+ ### CRUD Table Pattern
734
+
735
+ ```tsx
736
+ <Card>
737
+ <CardHeader className="flex flex-row items-center justify-between">
738
+ <CardTitle>Records</CardTitle>
739
+ <Button size="sm"><Plus className="size-4 mr-2" />New Record</Button>
740
+ </CardHeader>
741
+ <CardContent>
742
+ {/* Search + Filters */}
743
+ <div className="flex gap-2 mb-4">
744
+ <Input placeholder="Search..." className="max-w-sm" />
745
+ <Select><SelectTrigger><SelectValue placeholder="Status" /></SelectTrigger>...</Select>
746
+ </div>
747
+ {/* Table */}
748
+ <Table>
749
+ <TableHeader>
750
+ <TableRow>
751
+ <TableHead>Name</TableHead>
752
+ <TableHead>Status</TableHead>
753
+ <TableHead className="text-right">Actions</TableHead>
754
+ </TableRow>
755
+ </TableHeader>
756
+ <TableBody>
757
+ {isLoading
758
+ ? Array.from({ length: 5 }).map((_, i) => (
759
+ <TableRow key={i}>
760
+ <TableCell><Skeleton className="h-4 w-32" /></TableCell>
761
+ <TableCell><Skeleton className="h-5 w-16 rounded-full" /></TableCell>
762
+ <TableCell className="text-right"><Skeleton className="h-8 w-8 ml-auto" /></TableCell>
763
+ </TableRow>
764
+ ))
765
+ : rows.map(row => <TableRow key={row.id}>{/* ... */}</TableRow>)
766
+ }
767
+ </TableBody>
768
+ </Table>
769
+ </CardContent>
770
+ </Card>
771
+ ```
772
+
773
+ ---
774
+
775
+ ## 13. Error Boundaries
776
+
777
+ Three pre-configured wrappers for granular error isolation:
778
+
779
+ ```tsx
780
+ import { AppErrorBoundary, PageErrorBoundary, SectionErrorBoundary }
781
+ from '../shared/error-boundary';
782
+
783
+ // App.tsx — wraps the entire application
784
+ <AppErrorBoundary>
785
+ <QueryClientProvider>
786
+ <XerticaProvider>
787
+ <Router>
788
+ <AuthProvider>
789
+ <PageErrorBoundary> {/* wraps the route tree */}
790
+ <Suspense fallback={null}>
791
+ <AuthGuard />
792
+ </Suspense>
793
+ </PageErrorBoundary>
794
+ </AuthProvider>
795
+ </Router>
796
+ </XerticaProvider>
797
+ </QueryClientProvider>
798
+ </AppErrorBoundary>
799
+
800
+ // Inside pages — wraps data-dependent sections
801
+ <SectionErrorBoundary>
802
+ <MyDataDrivenComponent />
803
+ </SectionErrorBoundary>
804
+ ```
805
+
806
+ ---
807
+
808
+ ## 14. Theme and Token Customization
809
+
810
+ Edit only `src/styles/xertica/tokens.css` — never modify anything in `node_modules/xertica-ui/styles/`.
811
+
812
+ To switch themes via CLI:
813
+ ```bash
814
+ npx xertica-ui update
815
+ # → select "Theme only"
816
+ ```
817
+
818
+ The token file uses HSL values and supports both light and dark modes via the `.dark` class on `<html>`. CSS variable names follow the shadcn convention: `--background`, `--foreground`, `--primary`, `--destructive`, etc.
819
+
820
+ ---
821
+
822
+ ## 15. AI Agent Reference
823
+
824
+ | What you need | Where to read |
825
+ |---|---|
826
+ | Complete component catalog | `node_modules/xertica-ui/llms-compact.txt` |
827
+ | Full component API with all props | `node_modules/xertica-ui/llms-full.txt` |
828
+ | "Dialog or Sheet? Tooltip or Popover?" | `node_modules/xertica-ui/docs/decision-tree.md` |
829
+ | Specific component props | `node_modules/xertica-ui/docs/components/<name>.md` |
830
+ | Mandatory constraint list for AI | `node_modules/xertica-ui/docs/ai-usage.md` |
831
+ | i18n setup and language switching | `node_modules/xertica-ui/docs/i18n.md` |
832
+ | State management patterns | `node_modules/xertica-ui/docs/state-management.md` |
833
+ | Layout system (sidebar width, assistant) | `node_modules/xertica-ui/docs/layout.md` |
834
+ | Page-level composition patterns | `node_modules/xertica-ui/docs/patterns/*.md` |
835
+ | This project's architecture rules | `guidelines/Guidelines.md` (you are here) |
836
+
837
+ ---
838
+
839
+ ## 16. Development Checklist
840
+
841
+ Before completing any feature:
842
+
843
+ - [ ] Feature content is in `src/features/<name>/ui/`
844
+ - [ ] Feature barrel at `src/features/<name>/index.ts` exports everything
845
+ - [ ] Page is a thin wrapper in `src/pages/` — no logic, no state
846
+ - [ ] Route registered in `src/shared/config/navigation.ts` with `label`, `labelKey`, `icon`, `allowedRoles`, `group`
847
+ - [ ] Route registered in `src/app/components/AuthGuard.tsx` with correct guard (`ProtectedRoute` or `RoleRoute`)
848
+ - [ ] i18n key added to `src/locales/<lang>/nav.json` for **all** configured languages
849
+ - [ ] Navigation card added to `HomeContent.tsx` if this is a primary module
850
+ - [ ] All data arrays live in `features/<name>/data/mock.ts`, not in components
851
+ - [ ] Server state fetched via React Query hook — not `useState` + `useEffect`
852
+ - [ ] React Query `queryKey` includes `language` when response contains translated strings
853
+ - [ ] `staleTime` is set on every `useQuery` call — never left at default `0`
854
+ - [ ] Client UI state (filters, tabs) uses Zustand with selectors — not prop drilling
855
+ - [ ] Only `xertica-ui` components used — no raw HTML elements
856
+ - [ ] No raw hex/rgb/hsl values in `className` or `style` — semantic tokens for status contexts
857
+ - [ ] No hardcoded border radii — `rounded-[var(--radius)]`
858
+ - [ ] Icons from `lucide-react` only — no inline SVG
859
+ - [ ] Layout state from `useLayout()` — no hardcoded `padding-left` or sidebar widths
860
+ - [ ] Destructive actions wrapped in `<AlertDialog>` with explicit confirm/cancel
861
+ - [ ] Forms use `react-hook-form` + `zod` — no manual `useState` + if-validation
862
+ - [ ] Loading states render a `*Skeleton` component, not a blank space or spinner alone
863
+ - [ ] All user-facing strings go through `useTranslation()` — no hardcoded UI text
864
+ - [ ] `toast` used for action feedback — message goes through `t()`
865
+ - [ ] Responsive: tested on mobile (< 640px), tablet (640–1024px), desktop (> 1024px)