xertica-ui 2.1.2 → 2.1.3

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 (402) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -1
  3. package/assets/xertica-logo.svg +37 -37
  4. package/assets/xertica-x-logo.svg +20 -20
  5. package/bin/cli.ts +1 -1
  6. package/bin/generate-tokens.ts +13 -7
  7. package/components/assistant/index.ts +6 -6
  8. package/components/assistant/xertica-assistant/index.ts +2 -0
  9. package/components/assistant/xertica-assistant/use-assistant.ts +564 -0
  10. package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
  11. package/components/assistant/xertica-assistant/xertica-assistant.tsx +183 -474
  12. package/components/blocks/card-patterns/ActivityCard.tsx +72 -72
  13. package/components/blocks/card-patterns/FeatureCard.tsx +100 -100
  14. package/components/blocks/card-patterns/NotificationCard.tsx +127 -127
  15. package/components/blocks/card-patterns/ProfileCard.tsx +84 -84
  16. package/components/blocks/card-patterns/ProjectCard.tsx +89 -89
  17. package/components/blocks/card-patterns/QuickActionCard.tsx +62 -62
  18. package/components/blocks/card-patterns/card-patterns.stories.tsx +445 -445
  19. package/components/blocks/card-patterns/index.ts +17 -17
  20. package/components/blocks/index.ts +1 -1
  21. package/components/brand/index.ts +6 -6
  22. package/components/brand/xertica-provider/XerticaProvider.tsx +48 -48
  23. package/components/brand/xertica-provider/xertica-provider.mdx +61 -61
  24. package/components/brand/xertica-provider/xertica-provider.test.tsx +52 -52
  25. package/components/examples/MapExamples.tsx +282 -282
  26. package/components/examples/SimpleFilterableMap.tsx +191 -191
  27. package/components/examples/index.ts +51 -51
  28. package/components/figma/ImageWithFallback.tsx +27 -27
  29. package/components/hooks/index.ts +7 -7
  30. package/components/index.ts +5 -5
  31. package/components/layout/header/header.test.tsx +8 -8
  32. package/components/layout/header/header.tsx +4 -4
  33. package/components/layout/index.ts +2 -2
  34. package/components/layout/sidebar/index.ts +2 -0
  35. package/components/layout/sidebar/sidebar.mdx +1 -1
  36. package/components/layout/sidebar/sidebar.stories.tsx +160 -8
  37. package/components/layout/sidebar/sidebar.test.tsx +11 -11
  38. package/components/layout/sidebar/sidebar.tsx +610 -471
  39. package/components/layout/sidebar/use-sidebar.ts +113 -0
  40. package/components/media/FloatingMediaWrapper.tsx +11 -11
  41. package/components/media/audio-player/AudioPlayer.tsx +22 -22
  42. package/components/media/index.ts +3 -3
  43. package/components/pages/home-content/HomeContent.tsx +7 -7
  44. package/components/pages/home-content/home-content.mdx +62 -62
  45. package/components/pages/home-page/HomePage.stories.tsx +39 -39
  46. package/components/pages/home-page/HomePage.tsx +1 -1
  47. package/components/pages/home-page/home-page.mdx +53 -53
  48. package/components/pages/index.ts +8 -8
  49. package/components/pages/template-content/TemplateContent.tsx +5 -5
  50. package/components/pages/template-content/template-content.mdx +61 -61
  51. package/components/pages/template-page/TemplatePage.stories.tsx +39 -39
  52. package/components/pages/template-page/TemplatePage.tsx +5 -5
  53. package/components/pages/template-page/template-page.mdx +53 -53
  54. package/components/public-api-smoke.test.tsx +52 -52
  55. package/components/shared/assistant-utils.ts +43 -43
  56. package/components/shared/layout-constants.ts +1 -1
  57. package/components/ui/accordion/accordion.mdx +8 -8
  58. package/components/ui/accordion/accordion.stories.tsx +53 -53
  59. package/components/ui/alert/alert.mdx +8 -8
  60. package/components/ui/alert-dialog/alert-dialog.mdx +8 -8
  61. package/components/ui/aspect-ratio/aspect-ratio.mdx +8 -8
  62. package/components/ui/assistant-chart/assistant-chart.mdx +8 -8
  63. package/components/ui/avatar/avatar.mdx +8 -8
  64. package/components/ui/badge/badge.mdx +8 -8
  65. package/components/ui/badge/badge.tsx +9 -9
  66. package/components/ui/breadcrumb/breadcrumb.mdx +8 -8
  67. package/components/ui/button/button.mdx +8 -8
  68. package/components/ui/calendar/calendar.mdx +8 -8
  69. package/components/ui/calendar/calendar.tsx +258 -258
  70. package/components/ui/card/card.mdx +8 -8
  71. package/components/ui/card/card.stories.tsx +245 -245
  72. package/components/ui/carousel/carousel.mdx +8 -8
  73. package/components/ui/chart/chart.mdx +8 -8
  74. package/components/ui/chart/chart.stories.tsx +1303 -344
  75. package/components/ui/chart/chart.test.tsx +154 -154
  76. package/components/ui/chart/chart.tsx +2367 -1494
  77. package/components/ui/checkbox/checkbox.mdx +8 -8
  78. package/components/ui/checkbox/checkbox.stories.tsx +20 -20
  79. package/components/ui/collapsible/collapsible.mdx +8 -8
  80. package/components/ui/command/command.mdx +8 -8
  81. package/components/ui/context-menu/context-menu.mdx +8 -8
  82. package/components/ui/dialog/dialog.mdx +8 -8
  83. package/components/ui/drawer/drawer.mdx +8 -8
  84. package/components/ui/dropdown-menu/dropdown-menu.mdx +8 -8
  85. package/components/ui/empty/empty.mdx +8 -8
  86. package/components/ui/file-upload/file-upload.mdx +8 -8
  87. package/components/ui/file-upload/file-upload.stories.tsx +100 -0
  88. package/components/ui/file-upload/file-upload.tsx +14 -74
  89. package/components/ui/file-upload/index.ts +1 -0
  90. package/components/ui/file-upload/use-file-upload.ts +181 -0
  91. package/components/ui/google-maps-loader/google-maps-loader.tsx +2 -2
  92. package/components/ui/hover-card/hover-card.mdx +8 -8
  93. package/components/ui/input/input.mdx +8 -8
  94. package/components/ui/input-otp/input-otp.mdx +8 -8
  95. package/components/ui/input-otp/input-otp.stories.tsx +6 -6
  96. package/components/ui/label/label.mdx +8 -8
  97. package/components/ui/map/map.mdx +8 -8
  98. package/components/ui/map/map.stories.tsx +51 -51
  99. package/components/ui/map/map.tsx +2 -2
  100. package/components/ui/menubar/menubar.mdx +8 -8
  101. package/components/ui/navigation-menu/navigation-menu.mdx +8 -8
  102. package/components/ui/notification-badge/notification-badge.mdx +8 -8
  103. package/components/ui/pagination/index.ts +2 -0
  104. package/components/ui/pagination/pagination.mdx +8 -8
  105. package/components/ui/pagination/pagination.stories.tsx +94 -0
  106. package/components/ui/pagination/use-pagination.ts +194 -0
  107. package/components/ui/popover/popover.mdx +8 -8
  108. package/components/ui/progress/progress.mdx +8 -8
  109. package/components/ui/progress/progress.tsx +68 -68
  110. package/components/ui/radio-group/radio-group.mdx +8 -8
  111. package/components/ui/rating/rating.mdx +8 -8
  112. package/components/ui/resizable/resizable.mdx +8 -8
  113. package/components/ui/rich-text-editor/index.ts +2 -0
  114. package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
  115. package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
  116. package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
  117. package/components/ui/route-map/route-map.mdx +8 -8
  118. package/components/ui/route-map/route-map.stories.tsx +54 -54
  119. package/components/ui/route-map/route-map.tsx +2 -2
  120. package/components/ui/scroll-area/scroll-area.mdx +8 -8
  121. package/components/ui/search/search.mdx +8 -8
  122. package/components/ui/select/select.mdx +8 -8
  123. package/components/ui/select/select.stories.tsx +9 -9
  124. package/components/ui/separator/separator.mdx +8 -8
  125. package/components/ui/sheet/sheet.mdx +8 -8
  126. package/components/ui/sheet/sheet.stories.tsx +95 -95
  127. package/components/ui/simple-map/simple-map.mdx +8 -8
  128. package/components/ui/simple-map/simple-map.stories.tsx +48 -48
  129. package/components/ui/skeleton/skeleton.mdx +8 -8
  130. package/components/ui/slider/slider.mdx +8 -8
  131. package/components/ui/sonner/sonner.mdx +8 -8
  132. package/components/ui/stats-card/stats-card.mdx +8 -8
  133. package/components/ui/stepper/index.ts +3 -1
  134. package/components/ui/stepper/stepper.mdx +8 -8
  135. package/components/ui/stepper/stepper.stories.tsx +116 -0
  136. package/components/ui/stepper/stepper.tsx +4 -4
  137. package/components/ui/stepper/use-stepper.ts +137 -0
  138. package/components/ui/switch/switch.mdx +8 -8
  139. package/components/ui/switch/switch.stories.tsx +20 -20
  140. package/components/ui/table/table.mdx +8 -8
  141. package/components/ui/tabs/tabs.mdx +8 -8
  142. package/components/ui/tabs/tabs.stories.tsx +26 -26
  143. package/components/ui/textarea/textarea.mdx +8 -8
  144. package/components/ui/timeline/timeline.mdx +8 -8
  145. package/components/ui/toggle/toggle.mdx +8 -8
  146. package/components/ui/toggle-group/toggle-group.mdx +8 -8
  147. package/components/ui/tooltip/tooltip.mdx +8 -8
  148. package/components/ui/tree-view/index.ts +4 -1
  149. package/components/ui/tree-view/tree-view.mdx +8 -8
  150. package/components/ui/tree-view/tree-view.stories.tsx +110 -4
  151. package/components/ui/tree-view/tree-view.tsx +17 -125
  152. package/components/ui/tree-view/use-tree-view.ts +229 -0
  153. package/components.json +1512 -1512
  154. package/contexts/ApiKeyContext.tsx +72 -72
  155. package/contexts/BrandColorsContext.tsx +26 -26
  156. package/contexts/LanguageContext.tsx +13 -13
  157. package/contexts/LayoutContext.test.tsx +11 -11
  158. package/contexts/LayoutContext.tsx +29 -29
  159. package/contexts/ThemeContext.tsx +26 -26
  160. package/contexts/theme-data.ts +4 -4
  161. package/dist/{AssistantChart-CVzmmhx4.js → AssistantChart-C_hwFRRr.js} +4 -4
  162. package/dist/{AssistantChart-BAx9VQvb.cjs → AssistantChart-CldVCVDe.cjs} +311 -94
  163. package/dist/{AssistantChart-CVko2A1W.js → AssistantChart-Cu3m7RBo.js} +314 -97
  164. package/dist/{AssistantChart-9w31gdAb.cjs → AssistantChart-DoZCyS5r.cjs} +4 -4
  165. package/dist/AudioPlayer-B1lt5cPl.cjs +989 -0
  166. package/dist/AudioPlayer-BZ7bibzU.cjs +982 -0
  167. package/dist/AudioPlayer-C12BjQBV.cjs +997 -0
  168. package/dist/AudioPlayer-DMcG_c7L.js +990 -0
  169. package/dist/AudioPlayer-DcFKRJE_.js +998 -0
  170. package/dist/AudioPlayer-e8LfNoqO.js +983 -0
  171. package/dist/{xertica-assistant-Qp3ydksa.cjs → CodeBlock-7TTgmdGG.cjs} +263 -51
  172. package/dist/{xertica-assistant-gnCJdcZY.js → CodeBlock-BeSt1h5P.js} +219 -7
  173. package/dist/CodeBlock-BgfYL_rD.cjs +2094 -0
  174. package/dist/CodeBlock-BlcqlA9M.cjs +2094 -0
  175. package/dist/CodeBlock-Bnmeu5ez.cjs +2094 -0
  176. package/dist/CodeBlock-BtfPlbAI.js +2078 -0
  177. package/dist/CodeBlock-CIySIuYr.js +2078 -0
  178. package/dist/CodeBlock-CuPtUM-7.cjs +2094 -0
  179. package/dist/CodeBlock-D6ffWXgc.js +2078 -0
  180. package/dist/CodeBlock-D8dcwbit.cjs +2094 -0
  181. package/dist/CodeBlock-DMZrFnlw.cjs +2094 -0
  182. package/dist/CodeBlock-DlBehYN8.js +2078 -0
  183. package/dist/CodeBlock-DnYNI8rQ.js +2078 -0
  184. package/dist/CodeBlock-DvKWbSnE.cjs +2094 -0
  185. package/dist/CodeBlock-DwMCfkFY.js +2078 -0
  186. package/dist/CodeBlock-Dy6CNYyj.js +2078 -0
  187. package/dist/CodeBlock-U1pPOQI7.cjs +2094 -0
  188. package/dist/CodeBlock-f_GpNhEB.js +2078 -0
  189. package/dist/CodeBlock-oB6u8nI1.js +2078 -0
  190. package/dist/CodeBlock-tZC31B73.cjs +2094 -0
  191. package/dist/ImageWithFallback-CGtidP6B.cjs +4542 -0
  192. package/dist/ImageWithFallback-lsg3pdFg.js +4508 -0
  193. package/dist/LanguageSelector-B5YfbHra.js +231 -0
  194. package/dist/LanguageSelector-D6uacAIM.cjs +230 -0
  195. package/dist/LayoutContext-BAql6ZRY.js +97 -0
  196. package/dist/LayoutContext-BvK-ggDa.cjs +96 -0
  197. package/dist/ThemeContext-BoH4NLfN.js +734 -0
  198. package/dist/{ThemeContext-BbBNoFTG.js → ThemeContext-C2EwAPDt.js} +2 -2
  199. package/dist/ThemeContext-r69W20Xg.cjs +733 -0
  200. package/dist/{ThemeContext-Cmr8Ex8H.cjs → ThemeContext-vTjumZeM.cjs} +2 -2
  201. package/dist/{VerifyEmailPage-BE-L9mB7.js → VerifyEmailPage-C0c2e5n0.js} +7 -7
  202. package/dist/{VerifyEmailPage-DMBh4NM9.cjs → VerifyEmailPage-CYXtbKi3.cjs} +1 -1
  203. package/dist/{VerifyEmailPage-CR7kb5df.cjs → VerifyEmailPage-Cwi3kbol.cjs} +7 -7
  204. package/dist/{VerifyEmailPage-CdYPSJoO.js → VerifyEmailPage-DvMLZgFt.js} +1 -1
  205. package/dist/XerticaOrbe-KL1RBHzw.cjs +1354 -0
  206. package/dist/XerticaOrbe-zwS1p2a8.js +1355 -0
  207. package/dist/XerticaProvider-6btlAlzc.js +17 -0
  208. package/dist/XerticaProvider-BNoNOxQ5.cjs +16 -0
  209. package/dist/XerticaProvider-BlY2limY.cjs +38 -0
  210. package/dist/{XerticaProvider-BITjgC5p.js → XerticaProvider-CEoWMTxu.js} +2 -2
  211. package/dist/{XerticaProvider-By8q3Roe.cjs → XerticaProvider-CllrbMEJ.cjs} +2 -2
  212. package/dist/XerticaProvider-DDuiIcKo.js +39 -0
  213. package/dist/XerticaProvider-cI9hSs27.cjs +38 -0
  214. package/dist/XerticaProvider-hSwhNQex.js +39 -0
  215. package/dist/{alert-dialog-yckpaOpy.cjs → alert-dialog-DSKByiKZ.cjs} +3 -3
  216. package/dist/{alert-dialog-iDe5VE5o.js → alert-dialog-s-vmNkJ_.js} +3 -3
  217. package/dist/assistant.cjs.js +2 -1
  218. package/dist/assistant.es.js +3 -2
  219. package/dist/brand.cjs.js +1 -1
  220. package/dist/brand.es.js +1 -1
  221. package/dist/breadcrumb-CqJ7bHY5.js +161 -0
  222. package/dist/breadcrumb-m9Hb2_XN.cjs +177 -0
  223. package/dist/cli.js +14 -8
  224. package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
  225. package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +126 -0
  226. package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +7 -2
  227. package/dist/components/blocks/audio-player/AudioPlayer.d.ts +35 -0
  228. package/dist/components/blocks/audio-player/index.d.ts +1 -0
  229. package/dist/components/blocks/document-editor/DocumentEditor.d.ts +26 -0
  230. package/dist/components/blocks/document-editor/index.d.ts +1 -0
  231. package/dist/components/blocks/podcast-player/PodcastPlayer.d.ts +41 -0
  232. package/dist/components/blocks/podcast-player/index.d.ts +1 -0
  233. package/dist/components/layout/sidebar/index.d.ts +2 -0
  234. package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
  235. package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
  236. package/dist/components/shared/layout-constants.d.ts +1 -1
  237. package/dist/components/ui/button/button.d.ts +1 -1
  238. package/dist/components/ui/chart/chart.d.ts +162 -5
  239. package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
  240. package/dist/components/ui/file-upload/index.d.ts +1 -0
  241. package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
  242. package/dist/components/ui/pagination/index.d.ts +2 -0
  243. package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
  244. package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
  245. package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
  246. package/dist/components/ui/stepper/index.d.ts +3 -1
  247. package/dist/components/ui/stepper/stepper.d.ts +2 -2
  248. package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
  249. package/dist/components/ui/tree-view/index.d.ts +4 -1
  250. package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
  251. package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
  252. package/dist/dropdown-menu-BDB5CmQs.cjs +247 -0
  253. package/dist/dropdown-menu-DQidbKBD.js +231 -0
  254. package/dist/google-maps-loader-BFWp6VPd.js +287 -0
  255. package/dist/{google-maps-loader-t2IlYBzw.js → google-maps-loader-CTYySAun.js} +4 -0
  256. package/dist/{google-maps-loader-BqsYL48U.cjs → google-maps-loader-Y-QkD-Li.cjs} +5 -0
  257. package/dist/google-maps-loader-eS3uQ5TA.cjs +287 -0
  258. package/dist/header-Cgy6vYPk.cjs +731 -0
  259. package/dist/header-DRlT4jgI.js +715 -0
  260. package/dist/header-Dux00SI4.cjs +731 -0
  261. package/dist/header-EkGKXPsD.js +715 -0
  262. package/dist/header-WfEywpyc.cjs +731 -0
  263. package/dist/header-tifNQn2U.js +715 -0
  264. package/dist/hooks.cjs.js +1 -1
  265. package/dist/hooks.es.js +1 -1
  266. package/dist/index-BhapVLVj.js +8 -0
  267. package/dist/{index-D3RLKRAs.cjs → index-COtD8bRW.cjs} +1 -1
  268. package/dist/index-D6fxYEY8.cjs +7 -0
  269. package/dist/index-DW5tYe26.js +8 -0
  270. package/dist/index.cjs.js +19 -8
  271. package/dist/index.es.js +39 -28
  272. package/dist/index.umd.js +1043 -470
  273. package/dist/input-2R4loU86.js +127 -0
  274. package/dist/input-DWANSKGb.cjs +145 -0
  275. package/dist/layout.cjs.js +89 -1
  276. package/dist/layout.es.js +90 -2
  277. package/dist/pages.cjs.js +1 -1
  278. package/dist/pages.es.js +1 -1
  279. package/dist/rich-text-editor-0mraWT5y.cjs +2376 -0
  280. package/dist/rich-text-editor-B6jMRLzk.cjs +1939 -0
  281. package/dist/rich-text-editor-B8_oYcIR.js +1730 -0
  282. package/dist/rich-text-editor-B9UbSXNb.js +1203 -0
  283. package/dist/rich-text-editor-BYuRBNBU.js +2373 -0
  284. package/dist/rich-text-editor-Bb9pySTs.cjs +2374 -0
  285. package/dist/rich-text-editor-BcL6L3cm.cjs +2374 -0
  286. package/dist/rich-text-editor-BoVZYtTs.cjs +2391 -0
  287. package/dist/rich-text-editor-CPV1lEPH.cjs +1748 -0
  288. package/dist/rich-text-editor-CoKqbCtu.cjs +1799 -0
  289. package/dist/rich-text-editor-Cw56T_mB.js +2356 -0
  290. package/dist/rich-text-editor-Cyt8qs2b.js +1921 -0
  291. package/dist/rich-text-editor-D6H84OcX.cjs +1220 -0
  292. package/dist/rich-text-editor-D76gD-QI.js +2328 -0
  293. package/dist/rich-text-editor-DKkokOnA.js +1781 -0
  294. package/dist/rich-text-editor-DNsdpN64.cjs +2359 -0
  295. package/dist/rich-text-editor-DfG8bCyY.js +2358 -0
  296. package/dist/rich-text-editor-DgF8s7xW.js +2949 -0
  297. package/dist/rich-text-editor-Dxjw31Z4.js +2341 -0
  298. package/dist/rich-text-editor-DzP0Epmb.js +2356 -0
  299. package/dist/rich-text-editor-mWoaSCE4.cjs +2966 -0
  300. package/dist/rich-text-editor-skplNlBM.cjs +2345 -0
  301. package/dist/select-Bkbr0f-Z.cjs +162 -0
  302. package/dist/select-CvIVdX2n.js +145 -0
  303. package/dist/sidebar-CK_0ZQHj.cjs +803 -0
  304. package/dist/sidebar-CUuOvYhK.js +787 -0
  305. package/dist/slider-Bc5Hd0y1.js +56 -0
  306. package/dist/slider-N7hFFj6X.cjs +73 -0
  307. package/dist/tooltip-Ded96neP.cjs +137 -0
  308. package/dist/tooltip-HDOoD2-0.js +120 -0
  309. package/dist/ui.cjs.js +14 -4
  310. package/dist/ui.es.js +26 -16
  311. package/dist/use-mobile-B0hNy_Y6.cjs +4303 -0
  312. package/dist/use-mobile-BXuYROXM.js +4202 -0
  313. package/dist/use-mobile-Bbd51ASU.cjs +4392 -0
  314. package/dist/use-mobile-Bk6CX-TC.js +4359 -0
  315. package/dist/use-mobile-BvYdisLP.js +4202 -0
  316. package/dist/use-mobile-BzuxjzNX.cjs +4392 -0
  317. package/dist/use-mobile-CG2-SdXV.cjs +4235 -0
  318. package/dist/use-mobile-CKb5pqTs.js +4269 -0
  319. package/dist/use-mobile-CYuAuGDl.js +4202 -0
  320. package/dist/use-mobile-CaENcqm-.js +4508 -0
  321. package/dist/use-mobile-CbrYgJGJ.js +4203 -0
  322. package/dist/use-mobile-DMOvImGQ.cjs +4542 -0
  323. package/dist/use-mobile-DRB3BQgD.cjs +4235 -0
  324. package/dist/use-mobile-DZvv7QMR.js +4359 -0
  325. package/dist/use-mobile-DdI_TXam.cjs +4235 -0
  326. package/dist/use-mobile-DlceKf8a.js +4359 -0
  327. package/dist/use-mobile-DsOnow1o.cjs +4236 -0
  328. package/dist/use-mobile-Kcj6jSnK.cjs +4392 -0
  329. package/dist/use-mobile-bnKcua_i.js +4202 -0
  330. package/dist/use-mobile-ncXBeE2z.cjs +4235 -0
  331. package/dist/{xertica-assistant-B1IaHXnB.cjs → xertica-assistant-dyP7KHM5.cjs} +533 -392
  332. package/dist/{xertica-assistant-DPsESB6t.js → xertica-assistant-yX1CFBBo.js} +535 -394
  333. package/dist/xertica-ui.css +2 -2
  334. package/docs/ai-usage.md +28 -10
  335. package/docs/architecture.md +76 -0
  336. package/docs/components/assistant.md +159 -0
  337. package/docs/components/calendar.md +154 -154
  338. package/docs/components/card-patterns.md +337 -337
  339. package/docs/components/card.md +235 -235
  340. package/docs/components/chart.md +354 -39
  341. package/docs/components/file-upload.md +119 -2
  342. package/docs/components/map.md +84 -84
  343. package/docs/components/pagination.md +187 -0
  344. package/docs/components/rich-text-editor.md +164 -0
  345. package/docs/components/sidebar.md +153 -4
  346. package/docs/components/stepper.md +157 -12
  347. package/docs/components/tree-view.md +164 -6
  348. package/docs/components/xertica-provider.md +24 -24
  349. package/docs/decision-tree.md +287 -287
  350. package/docs/getting-started.md +1 -1
  351. package/docs/guidelines.md +14 -8
  352. package/docs/layout.md +2 -2
  353. package/docs/llms.md +4 -4
  354. package/guidelines/Guidelines.md +252 -250
  355. package/hooks/useTheme.ts +3 -3
  356. package/imports/Podcast.tsx +388 -388
  357. package/imports/XerticaAi.tsx +45 -45
  358. package/imports/XerticaX.tsx +19 -19
  359. package/imports/svg-aueiaqngck.ts +11 -11
  360. package/imports/svg-v9krss1ozd.ts +16 -16
  361. package/imports/svg-vhrdofe3qe.ts +5 -5
  362. package/llms-compact.txt +327 -327
  363. package/llms.txt +160 -160
  364. package/package.json +203 -203
  365. package/styles/xertica/app-overrides/chat.css +61 -61
  366. package/styles/xertica/app-overrides/scrollbar.css +33 -33
  367. package/styles/xertica/integrations/google-maps.css +76 -76
  368. package/styles/xertica/integrations/sonner.css +73 -73
  369. package/styles/xertica/tokens.css +41 -12
  370. package/templates/CLAUDE.md +182 -172
  371. package/templates/guidelines/Guidelines.md +325 -313
  372. package/templates/package.json +3 -3
  373. package/templates/src/features/auth/index.ts +4 -4
  374. package/templates/src/features/auth/ui/AuthPageShell.tsx +34 -34
  375. package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +70 -70
  376. package/templates/src/features/auth/ui/LoginContent.tsx +90 -90
  377. package/templates/src/features/auth/ui/ResetPasswordContent.tsx +151 -151
  378. package/templates/src/features/auth/ui/SocialLoginButtons.tsx +59 -59
  379. package/templates/src/features/auth/ui/VerifyEmailContent.tsx +82 -82
  380. package/templates/src/features/home/index.ts +1 -1
  381. package/templates/src/features/home/ui/HomeContent.tsx +100 -100
  382. package/templates/src/features/template/index.ts +5 -5
  383. package/templates/src/features/template/ui/CrudTemplate.tsx +3 -3
  384. package/templates/src/features/template/ui/DashboardTemplate.tsx +3 -3
  385. package/templates/src/features/template/ui/FormTemplate.tsx +120 -120
  386. package/templates/src/features/template/ui/LoginTemplate.tsx +3 -3
  387. package/templates/src/pages/AssistantPage.tsx +328 -328
  388. package/templates/src/pages/ForgotPasswordPage.tsx +6 -6
  389. package/templates/src/pages/HomePage.tsx +57 -57
  390. package/templates/src/pages/LoginPage.tsx +10 -10
  391. package/templates/src/pages/ResetPasswordPage.tsx +6 -6
  392. package/templates/src/pages/TemplatePage.tsx +30 -30
  393. package/templates/src/pages/VerifyEmailPage.tsx +6 -6
  394. package/templates/src/shared/config/navigation.ts +20 -20
  395. package/templates/src/shared/lib/auth.ts +20 -20
  396. package/templates/src/shared/types/auth.ts +3 -3
  397. package/templates/src/styles/xertica/tokens.css +39 -10
  398. package/templates/tsconfig.json +5 -5
  399. package/utils/gemini.ts +140 -140
  400. package/dist/VerifyEmailPage-Bae2cBXT.cjs +0 -2827
  401. package/dist/VerifyEmailPage-CbgjOF0v.js +0 -2828
  402. package/dist/index-CkTUgOwX.js +0 -8
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useEffect } from "react";
1
+ import React, { useState, useRef, useEffect, createContext, useContext } from "react";
2
2
  import { motion, AnimatePresence } from "framer-motion";
3
3
  import {
4
4
  Menu,
@@ -31,10 +31,12 @@ import { XerticaLogo } from '../../brand/xertica-logo';
31
31
  import { XerticaXLogo } from '../../brand/xertica-xlogo';
32
32
  import { Button } from '../../ui/button';
33
33
  import { useOptionalLayout } from "../../../contexts/LayoutContext";
34
+ import { useSidebar } from "./use-sidebar";
35
+
36
+ // ─────────────────────────────────────────────────────────────────────────────
37
+ // Internal tooltip styled for the sidebar
38
+ // ─────────────────────────────────────────────────────────────────────────────
34
39
 
35
- /**
36
- * Tooltip content styled for the sidebar.
37
- */
38
40
  function SidebarTooltipContent({
39
41
  className,
40
42
  sideOffset = 0,
@@ -61,7 +63,10 @@ function SidebarTooltipContent({
61
63
  );
62
64
  }
63
65
 
64
- // Interface for Route Config (copied/imported type)
66
+ // ─────────────────────────────────────────────────────────────────────────────
67
+ // Public interfaces
68
+ // ─────────────────────────────────────────────────────────────────────────────
69
+
65
70
  /**
66
71
  * Contextual action menu item for routes and groups.
67
72
  */
@@ -98,7 +103,7 @@ export interface RouteConfig {
98
103
  children?: RouteConfig[];
99
104
  }
100
105
 
101
- interface NavigationItem {
106
+ export interface NavigationItem {
102
107
  path: string;
103
108
  label: string;
104
109
  icon?: any;
@@ -159,9 +164,9 @@ export interface SidebarProps {
159
164
  /** Callback to toggle expansion state (defaults to LayoutContext toggle if available) */
160
165
  onToggle?: () => void;
161
166
  /** Authenticated user info */
162
- user?: {
163
- name?: string;
164
- email?: string;
167
+ user?: {
168
+ name?: string;
169
+ email?: string;
165
170
  avatar?: string;
166
171
  } | null;
167
172
  /** Logout callback */
@@ -194,73 +199,173 @@ export interface SidebarProps {
194
199
  width?: number;
195
200
  }
196
201
 
202
+ // ─────────────────────────────────────────────────────────────────────────────
203
+ // Compound Component Context
204
+ // ─────────────────────────────────────────────────────────────────────────────
205
+
206
+ interface SidebarContextValue {
207
+ expanded: boolean;
208
+ isMobileViewport: boolean;
209
+ onToggle: () => void;
210
+ navigate: (path: string) => void;
211
+ location: { pathname: string };
212
+ width: number;
213
+ }
214
+
215
+ const SidebarContext = createContext<SidebarContextValue | null>(null);
216
+
217
+ function useSidebarContext() {
218
+ const ctx = useContext(SidebarContext);
219
+ if (!ctx) {
220
+ throw new Error("Sidebar compound components must be used within <Sidebar.Root>");
221
+ }
222
+ return ctx;
223
+ }
224
+
225
+ // ─────────────────────────────────────────────────────────────────────────────
226
+ // Compound Sub-components
227
+ // ─────────────────────────────────────────────────────────────────────────────
228
+
197
229
  /**
198
- * Primary navigation sidebar component.
199
- *
200
- * @description
201
- * Manages desktop/mobile responsive navigation rendering with two variants:
202
- * - `"default"` — simple flat or grouped route list.
203
- * - `"assistant"` — advanced variant with fixed areas, search, filters, and grouped navigation.
230
+ * Root container for the Sidebar. Provides context to all sub-components.
231
+ * Use this when building a fully custom sidebar layout.
204
232
  *
205
- * This component is autonomous: it works out-of-the-box using local state or
206
- * integrates automatically with `LayoutContext` if wrapped in `LayoutProvider`.
207
- *
208
- * @ai-rules
209
- * 1. NEVER recreate the sidebar with raw Tailwind classes — always use this component.
210
- * 2. Use `variant="assistant"` for AI/tool sidebars; use `variant="default"` for standard navigation.
211
- * 3. Supports `Ctrl+B` keyboard shortcut automatically via `LayoutProvider`.
233
+ * @example
234
+ * <Sidebar.Root expanded={expanded} onToggle={toggle} width={280}>
235
+ * <Sidebar.Header logo={<MyLogo />} />
236
+ * <Sidebar.Nav navigationGroups={groups} />
237
+ * <Sidebar.Footer user={user} onLogout={logout} />
238
+ * </Sidebar.Root>
212
239
  */
213
- export function Sidebar({
240
+ function SidebarRoot({
214
241
  expanded: expandedProp,
215
242
  onToggle: onToggleProp,
216
- user,
217
- onLogout = () => {},
218
- onSettingsClick,
219
- location: locationProp,
220
243
  navigate: navigateProp,
221
- routes,
222
- logo,
223
- logoCollapsed,
224
- variant = "default",
225
- fixedArea,
226
- search,
227
- navigationGroups = [],
228
- footer,
229
- showFooter,
244
+ location: locationProp,
230
245
  width: widthProp,
231
- }: SidebarProps) {
246
+ children,
247
+ className,
248
+ }: {
249
+ expanded?: boolean;
250
+ onToggle?: () => void;
251
+ navigate?: (path: string) => void;
252
+ location?: { pathname: string };
253
+ width?: number;
254
+ children: React.ReactNode;
255
+ className?: string;
256
+ }) {
232
257
  const layoutContext = useOptionalLayout();
233
-
234
258
  const [localExpanded, setLocalExpanded] = useState(false);
235
259
  const [isMobileViewport, setIsMobileViewport] = useState(false);
236
-
260
+
237
261
  const expanded = expandedProp !== undefined ? expandedProp : (layoutContext?.sidebarExpanded ?? localExpanded);
238
262
  const onToggle = onToggleProp || (layoutContext?.toggleSidebar || (() => setLocalExpanded(prev => !prev)));
239
- const width = widthProp !== undefined ? widthProp : (layoutContext?.sidebarWidth ?? 256);
240
-
241
- // Safe navigation fallback
263
+ const width = widthProp !== undefined ? widthProp : (layoutContext?.sidebarWidth ?? 280);
242
264
  const navigate = navigateProp || ((path: string) => {
243
265
  if (typeof window !== 'undefined') window.location.href = path;
244
266
  });
245
-
246
- // Safe location fallback
247
267
  const location = locationProp || (typeof window !== 'undefined' ? window.location : { pathname: '/' });
248
- const {
249
- showUser = true,
250
- showSettings = true,
251
- showLogout = true,
252
- } = footer || {};
253
268
 
254
- const displayFooter = showFooter !== undefined ? showFooter : variant === "default";
269
+ useEffect(() => {
270
+ const checkViewport = () => setIsMobileViewport(window.innerWidth < 768);
271
+ checkViewport();
272
+ window.addEventListener("resize", checkViewport);
273
+ return () => window.removeEventListener("resize", checkViewport);
274
+ }, []);
275
+
276
+ return (
277
+ <SidebarContext.Provider value={{ expanded, isMobileViewport, onToggle, navigate, location, width }}>
278
+ <TooltipProvider delayDuration={300}>
279
+ <style>{`
280
+ @media (max-width: 767px) {
281
+ [style*="padding-left"].flex-1,
282
+ [style*="paddingLeft"].flex-1 {
283
+ padding-left: 0 !important;
284
+ }
285
+ }
286
+ `}</style>
287
+ <div
288
+ className={cn(
289
+ "bg-sidebar text-sidebar-foreground transition-all duration-300 ease-in-out flex flex-col z-50",
290
+ expanded
291
+ ? "fixed inset-0 md:fixed md:inset-y-0 md:left-0"
292
+ : "fixed inset-y-0 left-0 w-20 -translate-x-full md:translate-x-0",
293
+ className
294
+ )}
295
+ style={expanded && !isMobileViewport ? { width: `${width}px` } : undefined}
296
+ >
297
+ {children}
298
+ </div>
299
+ </TooltipProvider>
300
+ </SidebarContext.Provider>
301
+ );
302
+ }
303
+
304
+ /**
305
+ * Toggle button + logo header area for the Sidebar.
306
+ */
307
+ function SidebarHeader({
308
+ logo,
309
+ logoCollapsed,
310
+ }: {
311
+ logo?: React.ReactNode;
312
+ logoCollapsed?: React.ReactNode;
313
+ }) {
314
+ const { expanded, onToggle } = useSidebarContext();
315
+
316
+ return (
317
+ <>
318
+ {/* Menu Toggle Button */}
319
+ <div className="flex-shrink-0 p-[14px] pt-[13px] pr-[14px] pb-[12px] pl-[14px]">
320
+ <button
321
+ onClick={onToggle}
322
+ className="w-full h-10 flex items-center gap-3 px-3 justify-center rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
323
+ aria-label={expanded ? "Recolher menu" : "Expandir menu"}
324
+ >
325
+ {expanded ? (
326
+ <ArrowLeft className="w-5 h-5" />
327
+ ) : (
328
+ <Menu className="w-5 h-5" />
329
+ )}
330
+ </button>
331
+ </div>
332
+
333
+ {/* Logo */}
334
+ <div className="flex-shrink-0 px-4 py-4">
335
+ <div className="flex items-center h-10 justify-center">
336
+ <div className="flex items-center justify-center flex-shrink-0">
337
+ {expanded ? (
338
+ logo || <XerticaLogo className="h-5 w-auto" variant="white" />
339
+ ) : (
340
+ logoCollapsed || <XerticaXLogo className="h-5 w-auto" variant="white" />
341
+ )}
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </>
346
+ );
347
+ }
348
+
349
+ /**
350
+ * Navigation area for the Sidebar (default variant).
351
+ * Renders grouped or flat navigation items with overflow handling.
352
+ */
353
+ function SidebarNav({
354
+ navigationGroups = [],
355
+ routes = [],
356
+ variant = "default",
357
+ }: {
358
+ navigationGroups?: RouteGroup[];
359
+ routes?: RouteConfig[];
360
+ variant?: "default" | "assistant";
361
+ }) {
362
+ const { expanded, navigate, location, onToggle } = useSidebarContext();
255
363
  const navRef = useRef<HTMLDivElement>(null);
256
364
  const [localActiveItem, setLocalActiveItem] = useState<string | null>(null);
257
365
  const [hasOverflow, setHasOverflow] = useState(false);
258
366
  const [visibleItems, setVisibleItems] = useState<NavigationItem[]>([]);
259
367
  const [overflowItems, setOverflowItems] = useState<NavigationItem[]>([]);
260
- const [openSubmenu, setOpenSubmenu] = useState<string | null>(null);
261
- const [isFilterOpen, setIsFilterOpen] = useState(false);
262
368
 
263
- // Label translations map
264
369
  const labelTranslations: Record<string, string> = {
265
370
  home: "Início",
266
371
  dashboard: "Painel",
@@ -274,21 +379,14 @@ export function Sidebar({
274
379
  children: route.children,
275
380
  }));
276
381
 
277
- const isSettingsActive = location.pathname === "/settings";
278
-
279
- // Detect vertical overflow to handle extra items
280
382
  useEffect(() => {
281
383
  if (typeof window === "undefined") return;
282
384
  const checkOverflow = () => {
283
385
  if (!navRef.current) return;
284
386
  if (variant === "assistant") return;
285
-
286
- setIsMobileViewport(window.innerWidth < 768);
287
-
288
387
  const navHeight = navRef.current.clientHeight;
289
- const itemHeight = 44; // h-10 + gap
388
+ const itemHeight = 44;
290
389
  const maxVisibleItems = Math.floor(navHeight / itemHeight);
291
-
292
390
  if (navigationItems.length > maxVisibleItems) {
293
391
  setHasOverflow(true);
294
392
  setVisibleItems(navigationItems.slice(0, maxVisibleItems - 1));
@@ -299,17 +397,14 @@ export function Sidebar({
299
397
  setOverflowItems([]);
300
398
  }
301
399
  };
302
-
303
400
  checkOverflow();
304
401
  window.addEventListener("resize", checkOverflow);
305
402
  return () => window.removeEventListener("resize", checkOverflow);
306
- }, [navigationItems.length]);
403
+ }, [navigationItems.length, variant]);
307
404
 
308
405
  const handleNavigate = (path: string) => {
309
406
  setLocalActiveItem(path);
310
407
  navigate(path);
311
- setOpenSubmenu(null);
312
- // Close menu on mobile after navigation
313
408
  if (typeof window !== "undefined" && window.innerWidth < 768) {
314
409
  onToggle();
315
410
  }
@@ -324,10 +419,70 @@ export function Sidebar({
324
419
  actions: route.actions,
325
420
  });
326
421
 
422
+ const renderActionItems = (actions: ActionMenuItem[]): React.ReactNode => {
423
+ return actions.map((action, idx) => {
424
+ const Icon = action.icon;
425
+ if (action.children && action.children.length > 0) {
426
+ return (
427
+ <DropdownMenuSub key={idx}>
428
+ <DropdownMenuSubTrigger>
429
+ {Icon && <Icon className="mr-2 h-4 w-4" />}
430
+ <span>{action.label}</span>
431
+ </DropdownMenuSubTrigger>
432
+ <DropdownMenuPortal>
433
+ <DropdownMenuSubContent className="w-48 bg-popover border-border">
434
+ {renderActionItems(action.children)}
435
+ </DropdownMenuSubContent>
436
+ </DropdownMenuPortal>
437
+ </DropdownMenuSub>
438
+ );
439
+ }
440
+ return (
441
+ <DropdownMenuItem
442
+ key={idx}
443
+ className={cn(
444
+ "flex items-center gap-2",
445
+ action.variant === "destructive" ? "text-destructive focus:text-destructive" : ""
446
+ )}
447
+ onClick={(e) => {
448
+ e.stopPropagation();
449
+ action.onClick?.(null);
450
+ }}
451
+ >
452
+ {Icon && <Icon className="h-4 w-4 flex-shrink-0" />}
453
+ <span>{action.label}</span>
454
+ </DropdownMenuItem>
455
+ );
456
+ });
457
+ };
458
+
459
+ const renderAssistantActionMenu = (actions?: ActionMenuItem[], isHeader: boolean = false) => {
460
+ if (!actions || actions.length === 0) return null;
461
+ return (
462
+ <DropdownMenu>
463
+ <DropdownMenuTrigger asChild>
464
+ <Button
465
+ variant="ghost"
466
+ size="icon"
467
+ className={cn(
468
+ "h-8 w-8 text-sidebar-foreground/80 hover:bg-sidebar-foreground/20 hover:text-sidebar-foreground rounded-full transition-all",
469
+ !isHeader && "opacity-0 group-hover/item:opacity-100"
470
+ )}
471
+ aria-label="Mais opções"
472
+ >
473
+ <MoreVertical className="h-4 w-4" />
474
+ </Button>
475
+ </DropdownMenuTrigger>
476
+ <DropdownMenuContent align="end" className="w-48 bg-popover border-border p-1">
477
+ {renderActionItems(actions)}
478
+ </DropdownMenuContent>
479
+ </DropdownMenu>
480
+ );
481
+ };
482
+
327
483
  const renderDefaultItem = (item: NavigationItem) => {
328
484
  const Icon = item.icon;
329
485
  const hasChildren = item.children && item.children.length > 0;
330
-
331
486
  const activeClass = item.active
332
487
  ? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
333
488
  : "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground";
@@ -408,7 +563,6 @@ export function Sidebar({
408
563
 
409
564
  const renderDefaultGroup = (group: RouteGroup) => {
410
565
  const GroupIcon = group.icon;
411
-
412
566
  if (!expanded) {
413
567
  return (
414
568
  <div key={group.id} className="space-y-1">
@@ -416,7 +570,6 @@ export function Sidebar({
416
570
  </div>
417
571
  );
418
572
  }
419
-
420
573
  return (
421
574
  <div key={group.id}>
422
575
  {(group.label || GroupIcon) && (
@@ -440,70 +593,6 @@ export function Sidebar({
440
593
  );
441
594
  };
442
595
 
443
- const renderActionItems = (actions: ActionMenuItem[]) => {
444
- return actions.map((action, idx) => {
445
- const Icon = action.icon;
446
-
447
- if (action.children && action.children.length > 0) {
448
- return (
449
- <DropdownMenuSub key={idx}>
450
- <DropdownMenuSubTrigger>
451
- {Icon && <Icon className="mr-2 h-4 w-4" />}
452
- <span>{action.label}</span>
453
- </DropdownMenuSubTrigger>
454
- <DropdownMenuPortal>
455
- <DropdownMenuSubContent className="w-48 bg-popover border-border">
456
- {renderActionItems(action.children)}
457
- </DropdownMenuSubContent>
458
- </DropdownMenuPortal>
459
- </DropdownMenuSub>
460
- );
461
- }
462
-
463
- return (
464
- <DropdownMenuItem
465
- key={idx}
466
- className={cn(
467
- "flex items-center gap-2",
468
- action.variant === "destructive" ? "text-destructive focus:text-destructive" : ""
469
- )}
470
- onClick={(e) => {
471
- e.stopPropagation();
472
- action.onClick?.(null);
473
- }}
474
- >
475
- {Icon && <Icon className="h-4 w-4 flex-shrink-0" />}
476
- <span>{action.label}</span>
477
- </DropdownMenuItem>
478
- );
479
- });
480
- };
481
-
482
- const renderAssistantActionMenu = (actions?: ActionMenuItem[], isHeader: boolean = false) => {
483
- if (!actions || actions.length === 0) return null;
484
-
485
- return (
486
- <DropdownMenu>
487
- <DropdownMenuTrigger asChild>
488
- <Button
489
- variant="ghost"
490
- size="icon"
491
- className={cn(
492
- "h-8 w-8 text-sidebar-foreground/80 hover:bg-sidebar-foreground/20 hover:text-sidebar-foreground rounded-full transition-all",
493
- !isHeader && "opacity-0 group-hover/item:opacity-100"
494
- )}
495
- aria-label="Mais opções"
496
- >
497
- <MoreVertical className="h-4 w-4" />
498
- </Button>
499
- </DropdownMenuTrigger>
500
- <DropdownMenuContent align="end" className="w-48 bg-popover border-border p-1">
501
- {renderActionItems(actions)}
502
- </DropdownMenuContent>
503
- </DropdownMenu>
504
- );
505
- };
506
-
507
596
  const renderAssistantGroup = (group: RouteGroup) => {
508
597
  const isAnyItemActive = group.items.some(item =>
509
598
  location.pathname === item.path || location.pathname.startsWith(item.path + "/")
@@ -512,18 +601,12 @@ export function Sidebar({
512
601
 
513
602
  if (!expanded) {
514
603
  if (!GroupIcon) return null;
515
-
516
604
  return (
517
605
  <div key={group.id} className="py-2 flex justify-center">
518
606
  <Tooltip>
519
607
  <TooltipTrigger asChild>
520
608
  <button
521
- onClick={() => {
522
- // If we click a category when closed, we could either expand
523
- // or navigate to the first item.
524
- // For now, let's just trigger the onToggle to show items.
525
- onToggle();
526
- }}
609
+ onClick={() => onToggle()}
527
610
  aria-label={group.label}
528
611
  className={cn(
529
612
  "h-10 w-10 flex items-center justify-center rounded-[var(--radius-button)] transition-all duration-200",
@@ -563,7 +646,6 @@ export function Sidebar({
563
646
  const isRouteActive = location.pathname === item.path || location.pathname.startsWith(item.path + "/");
564
647
  const isActive = isRouteActive || localActiveItem === item.path;
565
648
  const Icon = item.icon;
566
-
567
649
  return (
568
650
  <div
569
651
  key={item.path}
@@ -596,354 +678,411 @@ export function Sidebar({
596
678
  );
597
679
  };
598
680
 
599
- return (
600
- <TooltipProvider delayDuration={300}>
601
- {/* Mobile Responsiveness Override embedded in Sidebar */}
602
- <style>{`
603
- @media (max-width: 767px) {
604
- [style*="padding-left"].flex-1,
605
- [style*="paddingLeft"].flex-1 {
606
- padding-left: 0 !important;
607
- }
608
- }
609
- `}</style>
610
- {/* Sidebar */}
611
- <div
612
- className={`bg-sidebar text-sidebar-foreground transition-all duration-300 ease-in-out flex flex-col z-50 ${expanded
613
- ? "fixed inset-0 md:fixed md:inset-y-0 md:left-0"
614
- : "fixed inset-y-0 left-0 w-20 -translate-x-full md:translate-x-0"
615
- }`}
616
- style={expanded && !isMobileViewport ? { width: `${width}px` } : undefined}
617
- >
618
- {/* Menu Toggle Button */}
619
- <div className="flex-shrink-0 p-[14px] pt-[13px] pr-[14px] pb-[12px] pl-[14px]">
620
- <button
621
- onClick={onToggle}
622
- className="w-full h-10 flex items-center gap-3 px-3 justify-center rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
623
- aria-label={expanded ? "Recolher menu" : "Expandir menu"}
624
- >
625
- {expanded ? (
626
- <ArrowLeft className="w-5 h-5" />
627
- ) : (
628
- <Menu className="w-5 h-5" />
629
- )}
630
- </button>
631
- </div>
681
+ if (variant === "assistant") {
682
+ return (
683
+ <div className="flex-1 min-h-0 overflow-hidden">
684
+ <ScrollArea className="h-full px-4">
685
+ {navigationGroups.map(group => renderAssistantGroup(group))}
686
+ </ScrollArea>
687
+ </div>
688
+ );
689
+ }
632
690
 
633
- {/* Logo */}
634
- <div className="flex-shrink-0 px-4 py-4">
635
- <div
636
- className={`flex items-center h-10 ${expanded ? "justify-center" : "justify-center"}`}
637
- >
638
- <div className="flex items-center justify-center flex-shrink-0">
639
- {expanded ? (
640
- logo || (
641
- <XerticaLogo
642
- className="h-5 w-auto"
643
- variant="white"
644
- />
645
- )
646
- ) : (
647
- logoCollapsed || (
648
- <XerticaXLogo
649
- className="h-5 w-auto"
650
- variant="white"
651
- />
652
- )
653
- )}
654
- </div>
691
+ return (
692
+ <div className="flex-1 min-h-0 overflow-hidden">
693
+ <nav className="h-full px-4 py-4 overflow-hidden" ref={navRef}>
694
+ {navigationGroups && navigationGroups.length > 0 ? (
695
+ <div className="space-y-3">
696
+ {navigationGroups.map((group) => (
697
+ <React.Fragment key={group.id}>
698
+ {renderDefaultGroup(group)}
699
+ </React.Fragment>
700
+ ))}
655
701
  </div>
656
- </div>
657
-
658
- {/* Assistant-specific Header (Search/Fixed Area) - Always fixed when present */}
659
- {variant === "assistant" && ((fixedArea && fixedArea.show) || (search && search.show)) && (
660
- <div className="flex-shrink-0 px-4 pb-4 space-y-4 border-b border-sidebar-border/30 mb-2">
661
- {fixedArea?.show && fixedArea.content && expanded && (
662
- <div className="animate-in fade-in slide-in-from-top-1 duration-300">
663
- {fixedArea.content}
664
- </div>
702
+ ) : (
703
+ <div className="space-y-1">
704
+ {(hasOverflow ? visibleItems : navigationItems).map((item) =>
705
+ renderDefaultItem(item)
665
706
  )}
666
- {search?.show && expanded && (
667
- <>
668
- <div className="flex items-center gap-2">
669
- <div className="relative flex-1">
670
- <SearchIcon className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-sidebar-foreground/50" />
671
- <Input
672
- type="text"
673
- placeholder={search.placeholder || "Buscar..."}
674
- aria-label={search.placeholder || "Buscar..."}
675
- value={search.value}
676
- onChange={(e) => search.onChange?.(e.target.value)}
677
- className="w-full h-9 bg-sidebar-foreground/10 border-sidebar-border text-sidebar-foreground placeholder:text-sidebar-foreground/50 pl-9 focus-visible:ring-1 focus-visible:ring-sidebar-foreground/30 focus-visible:ring-offset-0"
678
- />
707
+ {hasOverflow && (
708
+ <Popover>
709
+ <PopoverTrigger asChild>
710
+ <button
711
+ className={
712
+ expanded
713
+ ? "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
714
+ : "w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
715
+ }
716
+ aria-label="Mais opções"
717
+ >
718
+ <MoreVertical className="w-5 h-5 flex-shrink-0" />
719
+ {expanded && (
720
+ <span className="truncate text-sidebar-foreground">Mais opções</span>
721
+ )}
722
+ </button>
723
+ </PopoverTrigger>
724
+ <PopoverContent
725
+ side="right"
726
+ align="start"
727
+ className="w-56 p-2 bg-popover border border-border rounded-[var(--radius-card)] shadow-lg"
728
+ sideOffset={8}
729
+ >
730
+ <div className="space-y-1">
731
+ {overflowItems.map((item) => {
732
+ const Icon = item.icon;
733
+ return (
734
+ <button
735
+ key={item.path}
736
+ onClick={() => handleNavigate(item.path)}
737
+ className="w-full h-9 flex items-center gap-2 px-3 rounded-[var(--radius-button)] transition-all duration-200 text-popover-foreground/80 hover:bg-accent hover:text-accent-foreground text-left"
738
+ >
739
+ {Icon && <Icon className="w-4 h-4 flex-shrink-0" />}
740
+ <span className="truncate">{item.label}</span>
741
+ </button>
742
+ );
743
+ })}
679
744
  </div>
680
- {search.filter?.show && search.filter.content && (
681
- <Button
682
- variant="ghost"
683
- size="icon"
684
- onClick={() => setIsFilterOpen(!isFilterOpen)}
685
- className={cn(
686
- "h-9 w-9 text-sidebar-foreground transition-all duration-200",
687
- isFilterOpen ? "bg-sidebar-foreground/20" : "hover:bg-sidebar-foreground/15"
688
- )}
689
- aria-label={isFilterOpen ? "Fechar filtros" : "Abrir filtros"}
690
- >
691
- {search.filter.icon || <Filter className="h-4 w-4" />}
692
- </Button>
693
- )}
694
- </div>
695
-
696
- <AnimatePresence>
697
- {isFilterOpen && search.filter?.show && search.filter.content && (
698
- <motion.div
699
- initial={{ height: 0, opacity: 0 }}
700
- animate={{ height: "auto", opacity: 1 }}
701
- exit={{ height: 0, opacity: 0 }}
702
- transition={{ duration: 0.2 }}
703
- className="overflow-hidden"
704
- >
705
- <div className="pt-2 border-t border-sidebar-border/20">
706
- {search.filter.content}
707
- </div>
708
- </motion.div>
709
- )}
710
- </AnimatePresence>
711
- </>
712
- )}
713
- {(!expanded && (fixedArea?.show || search?.show)) && (
714
- <div className="flex flex-col items-center gap-4 py-2">
715
- {fixedArea?.show && fixedArea.icon && (
716
- <Tooltip>
717
- <TooltipTrigger asChild>
718
- <button
719
- onClick={() => fixedArea.onClick?.()}
720
- className="h-10 w-10 flex items-center justify-center rounded-[var(--radius-button)] bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 transition-all"
721
- aria-label="Nova Conversa"
722
- >
723
- {React.isValidElement(fixedArea.icon) ? fixedArea.icon : <fixedArea.icon className="h-5 w-5" />}
724
- </button>
725
- </TooltipTrigger>
726
- <SidebarTooltipContent side="right">Nova Conversa</SidebarTooltipContent>
727
- </Tooltip>
728
- )}
729
- {search?.show && (
730
- <Tooltip>
731
- <TooltipTrigger asChild>
732
- <button
733
- className="h-10 w-10 flex items-center justify-center rounded-[var(--radius-button)] text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
734
- aria-label="Buscar"
735
- >
736
- <SearchIcon className="h-5 w-5" />
737
- </button>
738
- </TooltipTrigger>
739
- <SidebarTooltipContent side="right">Buscar</SidebarTooltipContent>
740
- </Tooltip>
741
- )}
742
- </div>
745
+ </PopoverContent>
746
+ </Popover>
743
747
  )}
744
748
  </div>
745
749
  )}
750
+ </nav>
751
+ </div>
752
+ );
753
+ }
746
754
 
747
- {/* Main Content Area - Scrollable */}
748
- <div className="flex-1 min-h-0 overflow-hidden">
749
- {variant === "default" ? (
750
- <nav className="h-full px-4 py-4 overflow-hidden" ref={navRef}>
751
- {navigationGroups && navigationGroups.length > 0 ? (
752
- <div className="space-y-3">
753
- {navigationGroups.map((group, idx) => (
754
- <React.Fragment key={group.id}>
755
- {renderDefaultGroup(group)}
756
- </React.Fragment>
757
- ))}
758
- </div>
759
- ) : (
760
- <div className="space-y-1">
761
- {(hasOverflow ? visibleItems : navigationItems).map((item) =>
762
- renderDefaultItem(item)
763
- )}
755
+ /**
756
+ * Assistant-specific search + fixed area header for the Sidebar.
757
+ */
758
+ function SidebarSearch({
759
+ fixedArea,
760
+ search,
761
+ }: {
762
+ fixedArea?: SidebarFixedAreaConfig;
763
+ search?: SidebarSearchConfig;
764
+ }) {
765
+ const { expanded } = useSidebarContext();
766
+ const [isFilterOpen, setIsFilterOpen] = useState(false);
764
767
 
765
- {hasOverflow && (
766
- <Popover>
767
- <PopoverTrigger asChild>
768
- <button
769
- className={
770
- expanded
771
- ? "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
772
- : "w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
773
- }
774
- aria-label="Mais opções"
775
- >
776
- <MoreVertical className="w-5 h-5 flex-shrink-0" />
777
- {expanded && (
778
- <span className="truncate text-sidebar-foreground">
779
- Mais opções
780
- </span>
781
- )}
782
- </button>
783
- </PopoverTrigger>
784
- <PopoverContent
785
- side="right"
786
- align="start"
787
- className="w-56 p-2 bg-popover border border-border rounded-[var(--radius-card)] shadow-lg"
788
- sideOffset={8}
789
- >
790
- <div className="space-y-1">
791
- {overflowItems.map((item) => {
792
- const Icon = item.icon;
793
- return (
794
- <button
795
- key={item.path}
796
- onClick={() => handleNavigate(item.path)}
797
- className="w-full h-9 flex items-center gap-2 px-3 rounded-[var(--radius-button)] transition-all duration-200 text-popover-foreground/80 hover:bg-accent hover:text-accent-foreground text-left"
798
- >
799
- {Icon && <Icon className="w-4 h-4 flex-shrink-0" />}
800
- <span className="truncate">{item.label}</span>
801
- </button>
802
- );
803
- })}
804
- </div>
805
- </PopoverContent>
806
- </Popover>
807
- )}
808
- </div>
809
- )}
810
- </nav>
811
- ) : (
812
- <ScrollArea className="h-full px-4">
813
- {navigationGroups.map(group => renderAssistantGroup(group))}
814
- </ScrollArea>
815
- )}
816
- </div>
768
+ if (!((fixedArea && fixedArea.show) || (search && search.show))) return null;
817
769
 
818
- {/* Sidebar Footer - User settings */}
819
- {displayFooter && (showUser || showSettings || showLogout) && (
820
- <div className="flex-shrink-0 p-4 space-y-2">
821
- {/* User avatar */}
822
- {showUser && (
823
- !expanded ? (
824
- <Tooltip>
825
- <TooltipTrigger asChild>
826
- <button
827
- className="w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
828
- aria-label="Perfil do usuário"
829
- >
830
- <Avatar className="w-7 h-7 flex-shrink-0">
831
- <AvatarImage src={user?.avatar} alt={user?.name || "User"} />
832
- <AvatarFallback className="bg-sidebar-foreground/15 text-sidebar-foreground text-xs">
833
- {user?.name ? user.name.charAt(0).toUpperCase() : "U"}
834
- </AvatarFallback>
835
- </Avatar>
836
- </button>
837
- </TooltipTrigger>
838
- <SidebarTooltipContent side="right" sideOffset={0}>
839
- <p>{user?.name || "Perfil"}</p>
840
- </SidebarTooltipContent>
841
- </Tooltip>
842
- ) : (
843
- <button
844
- className="w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
845
- >
846
- <Avatar className="w-7 h-7 flex-shrink-0">
847
- <AvatarImage src={user?.avatar} alt={user?.name || "User"} />
848
- <AvatarFallback className="bg-sidebar-foreground/15 text-sidebar-foreground text-xs">
849
- {user?.name ? user.name.charAt(0).toUpperCase() : "U"}
850
- </AvatarFallback>
851
- </Avatar>
852
- <span className="text-sidebar-foreground truncate">
853
- {user?.name || "Perfil"}
854
- </span>
855
- </button>
856
- )
770
+ return (
771
+ <div className="flex-shrink-0 px-4 pb-4 space-y-4 border-b border-sidebar-border/30 mb-2">
772
+ {fixedArea?.show && fixedArea.content && expanded && (
773
+ <div className="animate-in fade-in slide-in-from-top-1 duration-300">
774
+ {fixedArea.content}
775
+ </div>
776
+ )}
777
+ {search?.show && expanded && (
778
+ <>
779
+ <div className="flex items-center gap-2">
780
+ <div className="relative flex-1">
781
+ <SearchIcon className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-sidebar-foreground/50" />
782
+ <Input
783
+ type="text"
784
+ placeholder={search.placeholder || "Buscar..."}
785
+ aria-label={search.placeholder || "Buscar..."}
786
+ value={search.value}
787
+ onChange={(e) => search.onChange?.(e.target.value)}
788
+ className="w-full h-9 bg-sidebar-foreground/10 border-sidebar-border text-sidebar-foreground placeholder:text-sidebar-foreground/50 pl-9 focus-visible:ring-1 focus-visible:ring-sidebar-foreground/30 focus-visible:ring-offset-0"
789
+ />
790
+ </div>
791
+ {search.filter?.show && search.filter.content && (
792
+ <Button
793
+ variant="ghost"
794
+ size="icon"
795
+ onClick={() => setIsFilterOpen(!isFilterOpen)}
796
+ className={cn(
797
+ "h-9 w-9 text-sidebar-foreground transition-all duration-200",
798
+ isFilterOpen ? "bg-sidebar-foreground/20" : "hover:bg-sidebar-foreground/15"
799
+ )}
800
+ aria-label={isFilterOpen ? "Fechar filtros" : "Abrir filtros"}
801
+ >
802
+ {search.filter.icon || <Filter className="h-4 w-4" />}
803
+ </Button>
857
804
  )}
858
-
859
- {/* Settings Button */}
860
- {showSettings && (
861
- !expanded ? (
862
- <Tooltip>
863
- <TooltipTrigger asChild>
864
- <button
865
- onClick={() => {
866
- if (onSettingsClick) {
867
- onSettingsClick();
868
- } else {
869
- navigate("/settings");
870
- }
871
- if (typeof window !== "undefined" && window.innerWidth < 768) {
872
- onToggle();
873
- }
874
- }}
875
- className={"w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 " +
876
- (isSettingsActive
877
- ? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
878
- : "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground")
879
- }
880
- aria-label="Configurações"
881
- >
882
- <Settings className="w-5 h-5 flex-shrink-0" />
883
- </button>
884
- </TooltipTrigger>
885
- <SidebarTooltipContent side="right" sideOffset={0}>
886
- <p>Configurações</p>
887
- </SidebarTooltipContent>
888
- </Tooltip>
889
- ) : (
805
+ </div>
806
+ <AnimatePresence>
807
+ {isFilterOpen && search.filter?.show && search.filter.content && (
808
+ <motion.div
809
+ initial={{ height: 0, opacity: 0 }}
810
+ animate={{ height: "auto", opacity: 1 }}
811
+ exit={{ height: 0, opacity: 0 }}
812
+ transition={{ duration: 0.2 }}
813
+ className="overflow-hidden"
814
+ >
815
+ <div className="pt-2 border-t border-sidebar-border/20">
816
+ {search.filter.content}
817
+ </div>
818
+ </motion.div>
819
+ )}
820
+ </AnimatePresence>
821
+ </>
822
+ )}
823
+ {(!expanded && (fixedArea?.show || search?.show)) && (
824
+ <div className="flex flex-col items-center gap-4 py-2">
825
+ {fixedArea?.show && fixedArea.icon && (
826
+ <Tooltip>
827
+ <TooltipTrigger asChild>
890
828
  <button
891
- onClick={() => {
892
- if (onSettingsClick) {
893
- onSettingsClick();
894
- } else {
895
- navigate("/settings");
896
- }
897
- if (typeof window !== "undefined" && window.innerWidth < 768) {
898
- onToggle();
899
- }
900
- }}
901
- className={
902
- isSettingsActive
903
- ? "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
904
- : "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
905
- }
829
+ onClick={() => fixedArea.onClick?.()}
830
+ className="h-10 w-10 flex items-center justify-center rounded-[var(--radius-button)] bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 transition-all"
831
+ aria-label="Nova Conversa"
906
832
  >
907
- <Settings className="w-5 h-5 flex-shrink-0" />
908
- <span className="truncate text-sidebar-foreground">
909
- Configurações
910
- </span>
833
+ {React.isValidElement(fixedArea.icon) ? fixedArea.icon : <fixedArea.icon className="h-5 w-5" />}
911
834
  </button>
912
- )
913
- )}
914
-
915
- {/* Logout Button */}
916
- {showLogout && (
917
- !expanded ? (
918
- <Tooltip>
919
- <TooltipTrigger asChild>
920
- <button
921
- onClick={onLogout}
922
- className="w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
923
- aria-label="Sair"
924
- >
925
- <LogOut className="w-5 h-5 flex-shrink-0" />
926
- </button>
927
- </TooltipTrigger>
928
- <SidebarTooltipContent side="right" sideOffset={0}>
929
- <p>Sair</p>
930
- </SidebarTooltipContent>
931
- </Tooltip>
932
- ) : (
835
+ </TooltipTrigger>
836
+ <SidebarTooltipContent side="right">Nova Conversa</SidebarTooltipContent>
837
+ </Tooltip>
838
+ )}
839
+ {search?.show && (
840
+ <Tooltip>
841
+ <TooltipTrigger asChild>
933
842
  <button
934
- onClick={onLogout}
935
- className="w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
843
+ className="h-10 w-10 flex items-center justify-center rounded-[var(--radius-button)] text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
844
+ aria-label="Buscar"
936
845
  >
937
- <LogOut className="w-5 h-5 flex-shrink-0" />
938
- <span className="truncate text-sidebar-foreground">
939
- Sair
940
- </span>
846
+ <SearchIcon className="h-5 w-5" />
941
847
  </button>
942
- )
848
+ </TooltipTrigger>
849
+ <SidebarTooltipContent side="right">Buscar</SidebarTooltipContent>
850
+ </Tooltip>
851
+ )}
852
+ </div>
853
+ )}
854
+ </div>
855
+ );
856
+ }
857
+
858
+ /**
859
+ * Footer area for the Sidebar with user info, settings, and logout.
860
+ */
861
+ function SidebarFooter({
862
+ user,
863
+ onLogout = () => { },
864
+ onSettingsClick,
865
+ showUser = true,
866
+ showSettings = true,
867
+ showLogout = true,
868
+ }: {
869
+ user?: { name?: string; email?: string; avatar?: string } | null;
870
+ onLogout?: () => void;
871
+ onSettingsClick?: () => void;
872
+ showUser?: boolean;
873
+ showSettings?: boolean;
874
+ showLogout?: boolean;
875
+ }) {
876
+ const { expanded, navigate, location, onToggle } = useSidebarContext();
877
+ const isSettingsActive = location.pathname === "/settings";
878
+
879
+ const handleSettingsClick = () => {
880
+ if (onSettingsClick) {
881
+ onSettingsClick();
882
+ } else {
883
+ navigate("/settings");
884
+ }
885
+ if (typeof window !== "undefined" && window.innerWidth < 768) {
886
+ onToggle();
887
+ }
888
+ };
889
+
890
+ return (
891
+ <div className="flex-shrink-0 p-4 space-y-2">
892
+ {showUser && (
893
+ !expanded ? (
894
+ <Tooltip>
895
+ <TooltipTrigger asChild>
896
+ <button
897
+ className="w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
898
+ aria-label="Perfil do usuário"
899
+ >
900
+ <Avatar className="w-7 h-7 flex-shrink-0">
901
+ <AvatarImage src={user?.avatar} alt={user?.name || "User"} />
902
+ <AvatarFallback className="bg-sidebar-foreground/15 text-sidebar-foreground text-xs">
903
+ {user?.name ? user.name.charAt(0).toUpperCase() : "U"}
904
+ </AvatarFallback>
905
+ </Avatar>
906
+ </button>
907
+ </TooltipTrigger>
908
+ <SidebarTooltipContent side="right" sideOffset={0}>
909
+ <p>{user?.name || "Perfil"}</p>
910
+ </SidebarTooltipContent>
911
+ </Tooltip>
912
+ ) : (
913
+ <button className="w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground">
914
+ <Avatar className="w-7 h-7 flex-shrink-0">
915
+ <AvatarImage src={user?.avatar} alt={user?.name || "User"} />
916
+ <AvatarFallback className="bg-sidebar-foreground/15 text-sidebar-foreground text-xs">
917
+ {user?.name ? user.name.charAt(0).toUpperCase() : "U"}
918
+ </AvatarFallback>
919
+ </Avatar>
920
+ <span className="text-sidebar-foreground truncate">{user?.name || "Perfil"}</span>
921
+ </button>
922
+ )
923
+ )}
924
+
925
+ {showSettings && (
926
+ !expanded ? (
927
+ <Tooltip>
928
+ <TooltipTrigger asChild>
929
+ <button
930
+ onClick={handleSettingsClick}
931
+ className={cn(
932
+ "w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200",
933
+ isSettingsActive
934
+ ? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
935
+ : "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
936
+ )}
937
+ aria-label="Configurações"
938
+ >
939
+ <Settings className="w-5 h-5 flex-shrink-0" />
940
+ </button>
941
+ </TooltipTrigger>
942
+ <SidebarTooltipContent side="right" sideOffset={0}>
943
+ <p>Configurações</p>
944
+ </SidebarTooltipContent>
945
+ </Tooltip>
946
+ ) : (
947
+ <button
948
+ onClick={handleSettingsClick}
949
+ className={cn(
950
+ "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200",
951
+ isSettingsActive
952
+ ? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
953
+ : "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
943
954
  )}
944
- </div>
945
- )}
946
- </div>
947
- </TooltipProvider>
955
+ >
956
+ <Settings className="w-5 h-5 flex-shrink-0" />
957
+ <span className="truncate text-sidebar-foreground">Configurações</span>
958
+ </button>
959
+ )
960
+ )}
961
+
962
+ {showLogout && (
963
+ !expanded ? (
964
+ <Tooltip>
965
+ <TooltipTrigger asChild>
966
+ <button
967
+ onClick={onLogout}
968
+ className="w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
969
+ aria-label="Sair"
970
+ >
971
+ <LogOut className="w-5 h-5 flex-shrink-0" />
972
+ </button>
973
+ </TooltipTrigger>
974
+ <SidebarTooltipContent side="right" sideOffset={0}>
975
+ <p>Sair</p>
976
+ </SidebarTooltipContent>
977
+ </Tooltip>
978
+ ) : (
979
+ <button
980
+ onClick={onLogout}
981
+ className="w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
982
+ >
983
+ <LogOut className="w-5 h-5 flex-shrink-0" />
984
+ <span className="truncate text-sidebar-foreground">Sair</span>
985
+ </button>
986
+ )
987
+ )}
988
+ </div>
948
989
  );
949
990
  }
991
+
992
+ // ─────────────────────────────────────────────────────────────────────────────
993
+ // Main Sidebar component (backward-compatible monolithic API)
994
+ // ─────────────────────────────────────────────────────────────────────────────
995
+
996
+ /**
997
+ * Primary navigation sidebar component.
998
+ *
999
+ * @description
1000
+ * Manages desktop/mobile responsive navigation rendering with two variants:
1001
+ * - `"default"` — simple flat or grouped route list.
1002
+ * - `"assistant"` — advanced variant with fixed areas, search, filters, and grouped navigation.
1003
+ *
1004
+ * This component is autonomous: it works out-of-the-box using local state or
1005
+ * integrates automatically with `LayoutContext` if wrapped in `LayoutProvider`.
1006
+ *
1007
+ * For advanced customization, use the Compound Component API:
1008
+ * `<Sidebar.Root>`, `<Sidebar.Header>`, `<Sidebar.Search>`, `<Sidebar.Nav>`, `<Sidebar.Footer>`
1009
+ *
1010
+ * @ai-rules
1011
+ * 1. NEVER recreate the sidebar with raw Tailwind classes — always use this component.
1012
+ * 2. Use `variant="assistant"` for AI/tool sidebars; use `variant="default"` for standard navigation.
1013
+ * 3. Supports `Ctrl+B` keyboard shortcut automatically via `LayoutProvider`.
1014
+ */
1015
+ export function Sidebar({
1016
+ expanded: expandedProp,
1017
+ onToggle: onToggleProp,
1018
+ user,
1019
+ onLogout = () => { },
1020
+ onSettingsClick,
1021
+ location: locationProp,
1022
+ navigate: navigateProp,
1023
+ routes,
1024
+ logo,
1025
+ logoCollapsed,
1026
+ variant = "default",
1027
+ fixedArea,
1028
+ search,
1029
+ navigationGroups = [],
1030
+ footer,
1031
+ showFooter,
1032
+ width: widthProp,
1033
+ }: SidebarProps) {
1034
+ const {
1035
+ showUser = true,
1036
+ showSettings = true,
1037
+ showLogout = true,
1038
+ } = footer || {};
1039
+
1040
+ const displayFooter = showFooter !== undefined ? showFooter : variant === "default";
1041
+
1042
+ return (
1043
+ <SidebarRoot
1044
+ expanded={expandedProp}
1045
+ onToggle={onToggleProp}
1046
+ navigate={navigateProp}
1047
+ location={locationProp}
1048
+ width={widthProp}
1049
+ >
1050
+ <SidebarHeader logo={logo} logoCollapsed={logoCollapsed} />
1051
+
1052
+ {variant === "assistant" && (
1053
+ <SidebarSearch fixedArea={fixedArea} search={search} />
1054
+ )}
1055
+
1056
+ <SidebarNav
1057
+ navigationGroups={navigationGroups}
1058
+ routes={routes}
1059
+ variant={variant}
1060
+ />
1061
+
1062
+ {displayFooter && (showUser || showSettings || showLogout) && (
1063
+ <SidebarFooter
1064
+ user={user}
1065
+ onLogout={onLogout}
1066
+ onSettingsClick={onSettingsClick}
1067
+ showUser={showUser}
1068
+ showSettings={showSettings}
1069
+ showLogout={showLogout}
1070
+ />
1071
+ )}
1072
+ </SidebarRoot>
1073
+ );
1074
+ }
1075
+
1076
+ // ─────────────────────────────────────────────────────────────────────────────
1077
+ // Attach Compound Components to Sidebar namespace
1078
+ // ─────────────────────────────────────────────────────────────────────────────
1079
+
1080
+ Sidebar.Root = SidebarRoot;
1081
+ Sidebar.Header = SidebarHeader;
1082
+ Sidebar.Search = SidebarSearch;
1083
+ Sidebar.Nav = SidebarNav;
1084
+ Sidebar.Footer = SidebarFooter;
1085
+
1086
+ // Re-export hook for headless usage
1087
+ export { useSidebar } from "./use-sidebar";
1088
+ export type { UseSidebarProps } from "./use-sidebar";