xertica-ui 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +564 -525
- package/README.md +417 -382
- package/bin/cli.ts +1244 -748
- package/bin/generate-tokens.ts +262 -262
- package/bin/language-config.ts +5 -8
- package/components/assets/xertica-orbe-animation.ts +1162 -1162
- package/components/assistant/code-block/CodeBlock.tsx +268 -268
- package/components/assistant/code-block/code-block.stories.tsx +57 -57
- package/components/assistant/code-block/code-block.test.tsx +44 -44
- package/components/assistant/code-block/index.ts +1 -1
- package/components/assistant/formatted-document/FormattedDocument.tsx +147 -147
- package/components/assistant/formatted-document/formatted-document.stories.tsx +51 -51
- package/components/assistant/formatted-document/formatted-document.test.tsx +42 -42
- package/components/assistant/formatted-document/index.ts +1 -1
- package/components/assistant/index.ts +6 -6
- package/components/assistant/markdown-message/MarkdownMessage.tsx +152 -152
- package/components/assistant/markdown-message/index.ts +1 -1
- package/components/assistant/markdown-message/markdown-message.stories.tsx +50 -50
- package/components/assistant/markdown-message/markdown-message.test.tsx +33 -33
- package/components/assistant/modern-chat-input/ModernChatInput.tsx +17 -7
- package/components/assistant/modern-chat-input/index.ts +1 -1
- package/components/assistant/modern-chat-input/modern-chat-input.stories.tsx +131 -131
- package/components/assistant/modern-chat-input/modern-chat-input.test.tsx +79 -79
- package/components/assistant/xertica-assistant/index.ts +3 -3
- package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +99 -99
- package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -106
- package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -81
- package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +88 -78
- package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +75 -75
- package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +564 -560
- package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +67 -67
- package/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.tsx +41 -41
- package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +103 -103
- package/components/assistant/xertica-assistant/parts/index.ts +16 -16
- package/components/assistant/xertica-assistant/types.ts +134 -134
- package/components/assistant/xertica-assistant/use-assistant.ts +615 -615
- package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +407 -407
- package/components/assistant/xertica-assistant/xertica-assistant.test.tsx +65 -65
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +611 -613
- package/components/blocks/card-patterns/ActivityCard.tsx +100 -100
- package/components/blocks/card-patterns/FeatureCard.tsx +109 -109
- package/components/blocks/card-patterns/FeatureCardSkeleton.tsx +1 -6
- package/components/blocks/card-patterns/NotificationCard.tsx +140 -140
- package/components/blocks/card-patterns/ProfileCard.tsx +112 -114
- package/components/blocks/card-patterns/ProjectCard.tsx +123 -123
- package/components/blocks/card-patterns/ProjectCardSkeleton.tsx +1 -6
- package/components/blocks/card-patterns/QuickActionCard.tsx +68 -68
- package/components/blocks/card-patterns/card-patterns.mdx +123 -123
- package/components/blocks/card-patterns/card-patterns.stories.tsx +594 -594
- package/components/blocks/card-patterns/index.ts +29 -29
- package/components/blocks/index.ts +1 -1
- package/components/brand/branding/branding.stories.tsx +57 -57
- package/components/brand/index.ts +6 -6
- package/components/brand/language-selector/index.ts +1 -1
- package/components/brand/language-selector/language-selector.mdx +126 -126
- package/components/brand/language-selector/language-selector.stories.tsx +1 -4
- package/components/brand/theme-toggle/ThemeToggle.tsx +74 -70
- package/components/brand/theme-toggle/index.ts +1 -1
- package/components/brand/theme-toggle/theme-toggle.stories.tsx +34 -34
- package/components/brand/theme-toggle/theme-toggle.test.tsx +34 -34
- package/components/brand/xertica-logo/XerticaLogo.stories.tsx +82 -82
- package/components/brand/xertica-logo/XerticaLogo.tsx +104 -104
- package/components/brand/xertica-logo/index.ts +1 -1
- package/components/brand/xertica-logo/xertica-logo.test.tsx +26 -26
- package/components/brand/xertica-orbe/XerticaOrbe.tsx +1927 -1927
- package/components/brand/xertica-orbe/index.ts +1 -1
- package/components/brand/xertica-orbe/xertica-orbe.stories.tsx +40 -40
- package/components/brand/xertica-orbe/xertica-orbe.test.tsx +19 -19
- package/components/brand/xertica-provider/XerticaProvider.tsx +1 -4
- package/components/brand/xertica-provider/index.ts +1 -1
- package/components/brand/xertica-provider/xertica-provider.test.tsx +74 -74
- package/components/brand/xertica-xlogo/XerticaXLogo.stories.tsx +79 -79
- package/components/brand/xertica-xlogo/XerticaXLogo.tsx +65 -65
- package/components/brand/xertica-xlogo/index.ts +1 -1
- package/components/brand/xertica-xlogo/xertica-xlogo.test.tsx +16 -16
- package/components/examples/ApiKeyMapExample.tsx +71 -71
- package/components/examples/DrawingMapExample.tsx +565 -565
- package/components/examples/FilterableMapExample.tsx +393 -393
- package/components/examples/LocationPickerExample.tsx +348 -348
- package/components/examples/MapExamples.tsx +268 -268
- package/components/examples/MapGmpExample.tsx +169 -169
- package/components/examples/MapShowcase.tsx +471 -471
- package/components/examples/RouteMapExamples.tsx +329 -329
- package/components/examples/SidebarLogoExample.tsx +65 -65
- package/components/examples/SimpleFilterableMap.tsx +219 -219
- package/components/examples/index.ts +45 -45
- package/components/figma/ImageWithFallback.tsx +27 -27
- package/components/hooks/index.ts +13 -13
- package/components/hooks/use-layout-shortcuts.ts +43 -43
- package/components/index.ts +86 -90
- package/components/layout/header/header.stories.tsx +204 -204
- package/components/layout/header/header.test.tsx +75 -75
- package/components/layout/header/header.tsx +349 -349
- package/components/layout/header/index.ts +1 -1
- package/components/layout/index.ts +2 -2
- package/components/layout/sidebar/index.ts +3 -3
- package/components/layout/sidebar/sidebar.stories.tsx +586 -586
- package/components/layout/sidebar/sidebar.test.tsx +76 -76
- package/components/layout/sidebar/sidebar.tsx +1079 -1073
- package/components/layout/sidebar/use-sidebar.ts +104 -104
- package/components/media/FloatingMediaWrapper.tsx +371 -371
- package/components/media/audio-player/AudioPlayer.stories.tsx +124 -124
- package/components/media/audio-player/AudioPlayer.test.tsx +106 -106
- package/components/media/audio-player/AudioPlayer.tsx +767 -765
- package/components/media/audio-player/index.ts +1 -1
- package/components/media/audio-player/use-audio-player.ts +312 -312
- package/components/media/index.ts +3 -3
- package/components/media/video-player/VideoPlayer.stories.tsx +98 -98
- package/components/media/video-player/VideoPlayer.test.tsx +73 -73
- package/components/media/video-player/VideoPlayer.tsx +310 -310
- package/components/media/video-player/index.ts +1 -1
- package/components/pages/forgot-password-page/ForgotPasswordPage.stories.tsx +24 -24
- package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +188 -188
- package/components/pages/forgot-password-page/forgot-password-page.test.tsx +45 -45
- package/components/pages/forgot-password-page/index.ts +1 -1
- package/components/pages/home-content/HomeContent.stories.tsx +43 -43
- package/components/pages/home-content/HomeContent.tsx +120 -120
- package/components/pages/home-content/index.ts +1 -1
- package/components/pages/home-page/HomePage.stories.tsx +39 -39
- package/components/pages/home-page/HomePage.tsx +78 -74
- package/components/pages/home-page/home-page.test.tsx +53 -53
- package/components/pages/home-page/index.ts +1 -1
- package/components/pages/index.ts +8 -8
- package/components/pages/login-page/LoginPage.stories.tsx +39 -39
- package/components/pages/login-page/LoginPage.tsx +218 -216
- package/components/pages/login-page/index.ts +1 -1
- package/components/pages/login-page/login-page.test.tsx +63 -63
- package/components/pages/reset-password-page/ResetPasswordPage.stories.tsx +24 -24
- package/components/pages/reset-password-page/ResetPasswordPage.tsx +243 -239
- package/components/pages/reset-password-page/index.ts +1 -1
- package/components/pages/template-content/TemplateContent.stories.tsx +43 -43
- package/components/pages/template-content/TemplateContent.tsx +1354 -1235
- package/components/pages/template-content/index.ts +1 -1
- package/components/pages/template-page/TemplatePage.stories.tsx +39 -39
- package/components/pages/template-page/TemplatePage.tsx +62 -62
- package/components/pages/template-page/index.ts +1 -1
- package/components/pages/template-page/template-page.test.tsx +52 -52
- package/components/pages/verify-email-page/VerifyEmailPage.stories.tsx +41 -41
- package/components/pages/verify-email-page/VerifyEmailPage.tsx +206 -206
- package/components/pages/verify-email-page/index.ts +1 -1
- package/components/public-api-smoke.test.tsx +52 -52
- package/components/shared/CustomTooltipContent.tsx +48 -48
- package/components/shared/assistant-utils.test.ts +16 -16
- package/components/shared/assistant-utils.ts +225 -225
- package/components/shared/error-boundary.stories.tsx +114 -132
- package/components/shared/error-boundary.tsx +150 -154
- package/components/shared/error-fallbacks.tsx +222 -226
- package/components/shared/layout-constants.ts +8 -8
- package/components/shared/navigation.ts +35 -35
- package/components/shared/use-mobile.test.ts +16 -16
- package/components/shared/use-mobile.ts +36 -36
- package/components/shared/utils.test.ts +14 -14
- package/components/shared/utils.ts +6 -6
- package/components/ui/accordion/accordion.stories.tsx +105 -105
- package/components/ui/accordion/accordion.test.tsx +59 -59
- package/components/ui/accordion/accordion.tsx +77 -77
- package/components/ui/accordion/index.ts +1 -1
- package/components/ui/alert/alert.stories.tsx +86 -86
- package/components/ui/alert/alert.test.tsx +53 -53
- package/components/ui/alert/alert.tsx +93 -93
- package/components/ui/alert/index.ts +1 -1
- package/components/ui/alert-dialog/alert-dialog.stories.tsx +84 -84
- package/components/ui/alert-dialog/alert-dialog.test.tsx +70 -70
- package/components/ui/alert-dialog/alert-dialog.tsx +149 -149
- package/components/ui/alert-dialog/index.ts +1 -1
- package/components/ui/aspect-ratio/aspect-ratio.stories.tsx +46 -46
- package/components/ui/aspect-ratio/aspect-ratio.test.tsx +28 -28
- package/components/ui/aspect-ratio/aspect-ratio.tsx +20 -20
- package/components/ui/aspect-ratio/index.ts +1 -1
- package/components/ui/assistant-chart/AssistantChart.tsx +64 -64
- package/components/ui/assistant-chart/assistant-chart.stories.tsx +44 -44
- package/components/ui/assistant-chart/assistant-chart.test.tsx +46 -46
- package/components/ui/assistant-chart/index.ts +1 -1
- package/components/ui/avatar/avatar.stories.tsx +86 -86
- package/components/ui/avatar/avatar.test.tsx +55 -55
- package/components/ui/avatar/avatar.tsx +71 -71
- package/components/ui/avatar/index.ts +1 -1
- package/components/ui/badge/badge.stories.tsx +72 -72
- package/components/ui/badge/badge.test.tsx +40 -40
- package/components/ui/badge/badge.tsx +58 -58
- package/components/ui/badge/index.ts +1 -1
- package/components/ui/breadcrumb/breadcrumb.stories.tsx +123 -123
- package/components/ui/breadcrumb/breadcrumb.test.tsx +70 -70
- package/components/ui/breadcrumb/breadcrumb.tsx +114 -114
- package/components/ui/breadcrumb/index.ts +1 -1
- package/components/ui/button/button.stories.tsx +183 -183
- package/components/ui/button/button.test.tsx +64 -64
- package/components/ui/button/button.tsx +98 -98
- package/components/ui/button/index.ts +1 -1
- package/components/ui/calendar/calendar.stories.tsx +108 -108
- package/components/ui/calendar/calendar.test.tsx +53 -53
- package/components/ui/calendar/calendar.tsx +230 -230
- package/components/ui/calendar/index.ts +1 -1
- package/components/ui/card/card.stories.tsx +301 -301
- package/components/ui/card/card.test.tsx +55 -55
- package/components/ui/card/card.tsx +83 -83
- package/components/ui/card/index.ts +1 -1
- package/components/ui/carousel/carousel.stories.tsx +80 -80
- package/components/ui/carousel/carousel.test.tsx +75 -75
- package/components/ui/carousel/carousel.tsx +242 -242
- package/components/ui/carousel/index.ts +1 -1
- package/components/ui/chart/chart.stories.tsx +1328 -1328
- package/components/ui/chart/chart.test.tsx +178 -178
- package/components/ui/chart/chart.tsx +2232 -2232
- package/components/ui/chart/index.ts +1 -1
- package/components/ui/checkbox/checkbox.stories.tsx +109 -109
- package/components/ui/checkbox/checkbox.test.tsx +49 -49
- package/components/ui/checkbox/checkbox.tsx +68 -68
- package/components/ui/checkbox/index.ts +1 -1
- package/components/ui/collapsible/collapsible.stories.tsx +45 -45
- package/components/ui/collapsible/collapsible.test.tsx +51 -51
- package/components/ui/collapsible/collapsible.tsx +32 -32
- package/components/ui/collapsible/index.ts +1 -1
- package/components/ui/command/command.stories.tsx +134 -134
- package/components/ui/command/command.test.tsx +48 -48
- package/components/ui/command/command.tsx +163 -163
- package/components/ui/command/index.ts +1 -1
- package/components/ui/context-menu/context-menu.stories.tsx +76 -76
- package/components/ui/context-menu/context-menu.test.tsx +61 -61
- package/components/ui/context-menu/context-menu.tsx +236 -236
- package/components/ui/context-menu/index.ts +1 -1
- package/components/ui/dialog/dialog.stories.tsx +174 -174
- package/components/ui/dialog/dialog.test.tsx +78 -78
- package/components/ui/dialog/dialog.tsx +189 -189
- package/components/ui/dialog/index.ts +1 -1
- package/components/ui/drawer/drawer.stories.tsx +71 -71
- package/components/ui/drawer/drawer.test.tsx +67 -67
- package/components/ui/drawer/drawer.tsx +146 -146
- package/components/ui/drawer/index.ts +1 -1
- package/components/ui/dropdown-menu/dropdown-menu.stories.tsx +156 -156
- package/components/ui/dropdown-menu/dropdown-menu.test.tsx +62 -62
- package/components/ui/dropdown-menu/dropdown-menu.tsx +240 -240
- package/components/ui/dropdown-menu/index.ts +1 -1
- package/components/ui/empty/empty.stories.tsx +85 -85
- package/components/ui/empty/empty.test.tsx +31 -31
- package/components/ui/empty/empty.tsx +88 -88
- package/components/ui/empty/index.ts +1 -1
- package/components/ui/file-upload/file-upload.stories.tsx +144 -144
- package/components/ui/file-upload/file-upload.test.tsx +65 -65
- package/components/ui/file-upload/file-upload.tsx +142 -142
- package/components/ui/file-upload/index.ts +2 -2
- package/components/ui/file-upload/use-file-upload.ts +177 -177
- package/components/ui/form/form.stories.tsx +85 -85
- package/components/ui/form/form.test.tsx +75 -75
- package/components/ui/form/form.tsx +163 -163
- package/components/ui/form/index.ts +1 -1
- package/components/ui/google-maps-loader/google-maps-loader.test.tsx +35 -35
- package/components/ui/google-maps-loader/google-maps-loader.tsx +465 -465
- package/components/ui/google-maps-loader/index.ts +1 -1
- package/components/ui/hover-card/hover-card.stories.tsx +61 -61
- package/components/ui/hover-card/hover-card.test.tsx +48 -48
- package/components/ui/hover-card/hover-card.tsx +50 -50
- package/components/ui/hover-card/index.ts +1 -1
- package/components/ui/index.ts +400 -400
- package/components/ui/input/index.ts +1 -1
- package/components/ui/input/input.stories.tsx +153 -153
- package/components/ui/input/input.test.tsx +47 -47
- package/components/ui/input/input.tsx +57 -57
- package/components/ui/input-otp/index.ts +1 -1
- package/components/ui/input-otp/input-otp.stories.tsx +120 -120
- package/components/ui/input-otp/input-otp.test.tsx +74 -74
- package/components/ui/input-otp/input-otp.tsx +101 -101
- package/components/ui/label/index.ts +1 -1
- package/components/ui/label/label.stories.tsx +74 -74
- package/components/ui/label/label.test.tsx +45 -45
- package/components/ui/label/label.tsx +53 -53
- package/components/ui/map/index.ts +1 -1
- package/components/ui/map/map.stories.tsx +86 -86
- package/components/ui/map/map.test.tsx +82 -82
- package/components/ui/map/map.tsx +506 -506
- package/components/ui/map/mock.test.tsx +13 -13
- package/components/ui/map-config/index.ts +1 -1
- package/components/ui/map-config/map-config.ts +18 -18
- package/components/ui/map-layers/index.ts +1 -1
- package/components/ui/map-layers/map-layers.test.tsx +48 -48
- package/components/ui/map-layers/map-layers.tsx +126 -126
- package/components/ui/map.exports/index.ts +1 -1
- package/components/ui/map.exports/map.exports.ts +31 -31
- package/components/ui/menubar/index.ts +1 -1
- package/components/ui/menubar/menubar.stories.tsx +130 -130
- package/components/ui/menubar/menubar.test.tsx +53 -53
- package/components/ui/menubar/menubar.tsx +265 -265
- package/components/ui/navigation-menu/index.ts +1 -1
- package/components/ui/navigation-menu/navigation-menu.stories.tsx +126 -126
- package/components/ui/navigation-menu/navigation-menu.test.tsx +47 -47
- package/components/ui/navigation-menu/navigation-menu.tsx +165 -165
- package/components/ui/notification-badge/index.ts +1 -1
- package/components/ui/notification-badge/notification-badge.stories.tsx +66 -66
- package/components/ui/notification-badge/notification-badge.test.tsx +61 -61
- package/components/ui/notification-badge/notification-badge.tsx +91 -91
- package/components/ui/page-header/index.ts +1 -1
- package/components/ui/page-header/page-header.stories.tsx +69 -69
- package/components/ui/page-header/page-header.test.tsx +37 -37
- package/components/ui/page-header/page-header.tsx +124 -124
- package/components/ui/pagination/index.ts +3 -3
- package/components/ui/pagination/pagination.stories.tsx +210 -210
- package/components/ui/pagination/pagination.test.tsx +63 -63
- package/components/ui/pagination/pagination.tsx +140 -140
- package/components/ui/pagination/use-pagination.ts +173 -173
- package/components/ui/popover/index.ts +1 -1
- package/components/ui/popover/popover.stories.tsx +73 -73
- package/components/ui/popover/popover.test.tsx +48 -48
- package/components/ui/popover/popover.tsx +54 -54
- package/components/ui/progress/index.ts +1 -1
- package/components/ui/progress/progress.stories.tsx +55 -55
- package/components/ui/progress/progress.test.tsx +23 -23
- package/components/ui/progress/progress.tsx +68 -68
- package/components/ui/radio-group/index.ts +1 -1
- package/components/ui/radio-group/radio-group.stories.tsx +114 -114
- package/components/ui/radio-group/radio-group.test.tsx +78 -78
- package/components/ui/radio-group/radio-group.tsx +93 -93
- package/components/ui/rating/index.ts +1 -1
- package/components/ui/rating/rating.stories.tsx +50 -50
- package/components/ui/rating/rating.test.tsx +48 -48
- package/components/ui/rating/rating.tsx +145 -145
- package/components/ui/resizable/index.ts +1 -1
- package/components/ui/resizable/resizable.stories.tsx +88 -88
- package/components/ui/resizable/resizable.test.tsx +61 -61
- package/components/ui/resizable/resizable.tsx +452 -452
- package/components/ui/rich-text-editor/index.ts +7 -7
- package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +290 -290
- package/components/ui/rich-text-editor/rich-text-editor.test.tsx +86 -86
- package/components/ui/rich-text-editor/rich-text-editor.tsx +634 -634
- package/components/ui/rich-text-editor/use-rich-text-editor.ts +453 -453
- package/components/ui/route-map/index.ts +1 -1
- package/components/ui/route-map/route-map.stories.tsx +48 -48
- package/components/ui/route-map/route-map.test.tsx +108 -108
- package/components/ui/route-map/route-map.tsx +349 -349
- package/components/ui/scroll-area/index.ts +1 -1
- package/components/ui/scroll-area/scroll-area.stories.tsx +31 -31
- package/components/ui/scroll-area/scroll-area.test.tsx +27 -27
- package/components/ui/scroll-area/scroll-area.tsx +70 -70
- package/components/ui/search/index.ts +1 -1
- package/components/ui/search/search.stories.tsx +107 -107
- package/components/ui/search/search.test.tsx +67 -67
- package/components/ui/search/search.tsx +141 -141
- package/components/ui/select/index.ts +1 -1
- package/components/ui/select/select.stories.tsx +163 -163
- package/components/ui/select/select.test.tsx +99 -99
- package/components/ui/select/select.tsx +195 -195
- package/components/ui/separator/index.ts +1 -1
- package/components/ui/separator/separator.stories.tsx +55 -55
- package/components/ui/separator/separator.test.tsx +23 -23
- package/components/ui/separator/separator.tsx +39 -39
- package/components/ui/sheet/index.ts +1 -1
- package/components/ui/sheet/sheet.stories.tsx +93 -93
- package/components/ui/sheet/sheet.test.tsx +62 -62
- package/components/ui/sheet/sheet.tsx +149 -149
- package/components/ui/simple-map/index.ts +1 -1
- package/components/ui/simple-map/simple-map.stories.tsx +44 -44
- package/components/ui/simple-map/simple-map.test.tsx +36 -36
- package/components/ui/simple-map/simple-map.tsx +92 -92
- package/components/ui/skeleton/index.ts +1 -1
- package/components/ui/skeleton/skeleton.stories.tsx +36 -36
- package/components/ui/skeleton/skeleton.test.tsx +19 -19
- package/components/ui/skeleton/skeleton.tsx +25 -25
- package/components/ui/slider/index.ts +1 -1
- package/components/ui/slider/slider.stories.tsx +44 -44
- package/components/ui/slider/slider.test.tsx +25 -25
- package/components/ui/slider/slider.tsx +66 -66
- package/components/ui/sonner/index.ts +1 -1
- package/components/ui/sonner/sonner.stories.tsx +41 -41
- package/components/ui/sonner/sonner.test.tsx +24 -24
- package/components/ui/sonner/sonner.tsx +74 -74
- package/components/ui/stats-card/index.ts +2 -2
- package/components/ui/stats-card/stats-card-skeleton.tsx +1 -3
- package/components/ui/stats-card/stats-card.stories.tsx +99 -99
- package/components/ui/stats-card/stats-card.test.tsx +34 -34
- package/components/ui/stats-card/stats-card.tsx +93 -93
- package/components/ui/stepper/index.ts +3 -3
- package/components/ui/stepper/stepper.stories.tsx +171 -171
- package/components/ui/stepper/stepper.test.tsx +47 -47
- package/components/ui/stepper/stepper.tsx +190 -190
- package/components/ui/stepper/use-stepper.ts +139 -139
- package/components/ui/switch/index.ts +1 -1
- package/components/ui/switch/switch.stories.tsx +93 -93
- package/components/ui/switch/switch.test.tsx +44 -44
- package/components/ui/switch/switch.tsx +70 -70
- package/components/ui/table/index.ts +1 -1
- package/components/ui/table/table.stories.tsx +114 -114
- package/components/ui/table/table.test.tsx +43 -43
- package/components/ui/table/table.tsx +104 -104
- package/components/ui/tabs/index.ts +1 -1
- package/components/ui/tabs/tabs.stories.tsx +140 -140
- package/components/ui/tabs/tabs.test.tsx +50 -50
- package/components/ui/tabs/tabs.tsx +66 -66
- package/components/ui/textarea/index.ts +1 -1
- package/components/ui/textarea/textarea.stories.tsx +69 -69
- package/components/ui/textarea/textarea.test.tsx +41 -41
- package/components/ui/textarea/textarea.tsx +61 -61
- package/components/ui/timeline/index.ts +1 -1
- package/components/ui/timeline/timeline.stories.tsx +97 -97
- package/components/ui/timeline/timeline.test.tsx +53 -53
- package/components/ui/timeline/timeline.tsx +124 -124
- package/components/ui/toggle/index.ts +1 -1
- package/components/ui/toggle/toggle.stories.tsx +56 -56
- package/components/ui/toggle/toggle.test.tsx +32 -32
- package/components/ui/toggle/toggle.tsx +55 -55
- package/components/ui/toggle-group/index.ts +1 -1
- package/components/ui/toggle-group/toggle-group.stories.tsx +66 -66
- package/components/ui/toggle-group/toggle-group.test.tsx +47 -47
- package/components/ui/toggle-group/toggle-group.tsx +79 -79
- package/components/ui/tooltip/index.ts +1 -1
- package/components/ui/tooltip/tooltip.stories.tsx +83 -83
- package/components/ui/tooltip/tooltip.test.tsx +39 -39
- package/components/ui/tooltip/tooltip.tsx +69 -69
- package/components/ui/tree-view/index.ts +4 -4
- package/components/ui/tree-view/tree-view.stories.tsx +154 -154
- package/components/ui/tree-view/tree-view.test.tsx +58 -58
- package/components/ui/tree-view/tree-view.tsx +171 -171
- package/components/ui/tree-view/use-tree-view.ts +237 -237
- package/components.json +892 -892
- package/contexts/ApiKeyContext.test.tsx +26 -26
- package/contexts/ApiKeyContext.tsx +196 -196
- package/contexts/AssistenteContext.test.tsx +17 -17
- package/contexts/AssistenteContext.tsx +113 -113
- package/contexts/AuthContext.tsx +121 -118
- package/contexts/BrandColorsContext.test.tsx +21 -21
- package/contexts/BrandColorsContext.tsx +251 -251
- package/contexts/LanguageContext.tsx +1 -2
- package/contexts/LayoutContext.test.tsx +29 -29
- package/contexts/LayoutContext.tsx +140 -140
- package/contexts/ThemeContext.test.tsx +38 -38
- package/contexts/ThemeContext.tsx +111 -111
- package/contexts/index.ts +8 -8
- package/contexts/theme-data.ts +340 -340
- package/dist/AssistantChart-COGiOV-g.cjs +3541 -0
- package/dist/AssistantChart-CWX1OWNM.js +3373 -0
- package/dist/AudioPlayer-9psiEucT.cjs +1282 -0
- package/dist/AudioPlayer-Dp2bD1Gk.js +1278 -0
- package/dist/BrandColorsContext-DZT7JjeD.js +659 -0
- package/dist/BrandColorsContext-awnBCmC4.cjs +666 -0
- package/dist/CodeBlock-DYkTfR0f.js +221 -0
- package/dist/CodeBlock-EOvp9cVu.cjs +223 -0
- package/dist/CustomTooltipContent-BhdIeBEg.cjs +54 -0
- package/dist/CustomTooltipContent-CNbVB2NS.js +33 -0
- package/dist/FeatureCard-BZ4CYxFf.cjs +497 -0
- package/dist/FeatureCard-DNycVGwT.js +485 -0
- package/dist/FeatureCardSkeleton-DZqc96mt.js +27 -0
- package/dist/FeatureCardSkeleton-pTa0YNKP.cjs +29 -0
- package/dist/LayoutContext-BEq_-n98.cjs +96 -0
- package/dist/LayoutContext-DNl1xSoX.js +92 -0
- package/dist/ThemeContext-CMD3z2Dz.cjs +1930 -0
- package/dist/ThemeContext-x_F2zsnv.js +1923 -0
- package/dist/VerifyEmailPage-BJjAMUTW.js +3223 -0
- package/dist/VerifyEmailPage-Bv8Ah_TK.cjs +3235 -0
- package/dist/VerifyEmailPage-CkBYfsNy.cjs +3232 -0
- package/dist/VerifyEmailPage-Cyl55sJb.js +3226 -0
- package/dist/VerifyEmailPage-X14vhdyl.js +3296 -0
- package/dist/VerifyEmailPage-u_Dn7t1U.cjs +3305 -0
- package/dist/XerticaOrbe-Uk2JML1-.cjs +1927 -0
- package/dist/XerticaOrbe-jA5T2iOk.js +1925 -0
- package/dist/XerticaProvider-BErr83Bg.js +42 -0
- package/dist/XerticaProvider-CwOkHxiT.cjs +44 -0
- package/dist/XerticaProvider-DUOJg9iX.js +49 -0
- package/dist/XerticaProvider-Dl_b72_l.cjs +51 -0
- package/dist/XerticaXLogo-BX3ueACh.js +255 -0
- package/dist/XerticaXLogo-mqjoBiLI.js +252 -0
- package/dist/XerticaXLogo-qBPhwK3g.cjs +260 -0
- package/dist/XerticaXLogo-uQgwns_E.cjs +257 -0
- package/dist/alert-dialog-DhwPioBa.cjs +885 -0
- package/dist/alert-dialog-DqlRW_An.js +831 -0
- package/dist/assistant.cjs.js +8 -4
- package/dist/assistant.es.js +5 -11
- package/dist/avatar-3kO2Anrp.js +54 -0
- package/dist/avatar-BCM7YQRC.cjs +77 -0
- package/dist/blocks.cjs.js +9 -4
- package/dist/blocks.es.js +2 -16
- package/dist/brand.cjs.js +10 -5
- package/dist/brand.es.js +3 -11
- package/dist/breadcrumb-BKtHF4gk.cjs +98 -0
- package/dist/breadcrumb-ifNsA7Zl.js +90 -0
- package/dist/button-0BlA47It.cjs +85 -0
- package/dist/button-DZHzN1Gd.js +62 -0
- package/dist/cli.js +471 -93
- package/dist/components/brand/theme-toggle/ThemeToggle.d.ts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/dropdown-menu-BMcykFDf.cjs +225 -0
- package/dist/dropdown-menu-Dn_eV2Xb.js +190 -0
- package/dist/google-maps-loader-BCe58h9D.js +308 -0
- package/dist/google-maps-loader-casMyxlo.cjs +316 -0
- package/dist/hooks.cjs.js +12 -8
- package/dist/hooks.es.js +10 -27
- package/dist/index-9GWd0qxq.cjs +12 -0
- package/dist/index-BabBx2pa.js +6 -0
- package/dist/index.cjs.js +37 -32
- package/dist/index.es.js +30 -363
- package/dist/input-C_UiS2Py.cjs +152 -0
- package/dist/input-cc-PTD4R.js +123 -0
- package/dist/layout.cjs.js +10 -6
- package/dist/layout.es.js +7 -9
- package/dist/media.cjs.js +8 -3
- package/dist/media.es.js +1 -6
- package/dist/pages.cjs.js +8 -3
- package/dist/pages.es.js +1 -11
- package/dist/progress-C7Lti5wo.js +80 -0
- package/dist/progress-Cqwxbqs1.cjs +103 -0
- package/dist/rich-text-editor-DqLICivI.js +2832 -0
- package/dist/rich-text-editor-DxO1Hz3a.cjs +2903 -0
- package/dist/select-CH6v_KcQ.cjs +161 -0
- package/dist/select-D-xvCZK2.js +130 -0
- package/dist/sidebar-3XyzjVBw.js +792 -0
- package/dist/sidebar-B4ZWaMrE.js +792 -0
- package/dist/sidebar-BS1p2V7t.cjs +795 -0
- package/dist/sidebar-DyYvgyBj.cjs +795 -0
- package/dist/skeleton-DjiHerJn.cjs +87 -0
- package/dist/skeleton-DtR5tkYe.js +78 -0
- package/dist/slider-B00b9SVK.cjs +78 -0
- package/dist/slider-DQCNUUMj.js +56 -0
- package/dist/sonner-B-jWlik1.cjs +68 -0
- package/dist/sonner-C9tiqj4f.js +47 -0
- package/dist/tooltip-D8n9UYoU.cjs +72 -0
- package/dist/tooltip-RtbSmPYJ.js +48 -0
- package/dist/ui.cjs.js +23 -18
- package/dist/ui.es.js +16 -303
- package/dist/use-audio-player-B78fd2ct.js +188 -0
- package/dist/use-audio-player-DGvhPrgR.cjs +190 -0
- package/dist/use-mobile-BdXTRb0Z.cjs +51 -0
- package/dist/use-mobile-Ce2cBAQe.js +29 -0
- package/dist/xertica-assistant-B1NaSFFj.js +2173 -0
- package/dist/xertica-assistant-B687qEPU.js +2165 -0
- package/dist/xertica-assistant-CIaUlbIt.cjs +2180 -0
- package/dist/xertica-assistant-sOHwTgIP.cjs +2172 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/ai-usage.md +195 -195
- package/docs/architecture-improvements.md +456 -456
- package/docs/architecture.md +312 -306
- package/docs/components/accordion.md +109 -109
- package/docs/components/alert-dialog.md +127 -127
- package/docs/components/alert.md +106 -106
- package/docs/components/aspect-ratio.md +58 -58
- package/docs/components/assistant-chart.md +47 -47
- package/docs/components/assistant.md +428 -426
- package/docs/components/audio-player.md +167 -167
- package/docs/components/avatar.md +101 -101
- package/docs/components/badge.md +84 -84
- package/docs/components/branding.md +252 -252
- package/docs/components/breadcrumb.md +104 -104
- package/docs/components/button.md +156 -156
- package/docs/components/calendar.md +141 -141
- package/docs/components/card-patterns.md +447 -445
- package/docs/components/card.md +245 -245
- package/docs/components/carousel.md +100 -100
- package/docs/components/chart.md +638 -638
- package/docs/components/checkbox.md +88 -88
- package/docs/components/code-block.md +105 -105
- package/docs/components/collapsible.md +86 -86
- package/docs/components/command.md +113 -113
- package/docs/components/context-menu.md +81 -81
- package/docs/components/dialog.md +198 -198
- package/docs/components/drawer.md +105 -105
- package/docs/components/dropdown-menu.md +127 -127
- package/docs/components/empty.md +127 -127
- package/docs/components/error-boundary.md +201 -191
- package/docs/components/file-upload.md +189 -189
- package/docs/components/floating-media-wrapper.md +63 -63
- package/docs/components/form.md +177 -177
- package/docs/components/formatted-document.md +105 -105
- package/docs/components/google-maps-loader.md +44 -44
- package/docs/components/header.md +177 -177
- package/docs/components/hooks.md +432 -430
- package/docs/components/hover-card.md +86 -86
- package/docs/components/image-with-fallback.md +107 -107
- package/docs/components/input-otp.md +95 -95
- package/docs/components/input.md +130 -130
- package/docs/components/label.md +69 -69
- package/docs/components/language-selector.md +20 -16
- package/docs/components/map-layers.md +138 -138
- package/docs/components/map.md +84 -84
- package/docs/components/markdown-message.md +47 -47
- package/docs/components/menubar.md +89 -89
- package/docs/components/modern-chat-input.md +164 -164
- package/docs/components/navigation-menu.md +83 -83
- package/docs/components/notification-badge.md +78 -78
- package/docs/components/page-header.md +93 -93
- package/docs/components/pages.md +323 -309
- package/docs/components/pagination.md +334 -334
- package/docs/components/popover.md +116 -116
- package/docs/components/progress.md +103 -103
- package/docs/components/radio-group.md +133 -133
- package/docs/components/rating.md +77 -77
- package/docs/components/resizable.md +84 -84
- package/docs/components/rich-text-editor.md +255 -255
- package/docs/components/route-map.md +124 -124
- package/docs/components/scroll-area.md +58 -58
- package/docs/components/search.md +87 -87
- package/docs/components/select.md +144 -144
- package/docs/components/separator.md +58 -58
- package/docs/components/sheet.md +122 -122
- package/docs/components/sidebar.md +314 -314
- package/docs/components/simple-map.md +51 -51
- package/docs/components/skeleton.md +99 -99
- package/docs/components/slider.md +84 -84
- package/docs/components/sonner.md +115 -115
- package/docs/components/stats-card.md +120 -120
- package/docs/components/stepper.md +268 -268
- package/docs/components/switch.md +106 -106
- package/docs/components/table.md +138 -138
- package/docs/components/tabs.md +117 -117
- package/docs/components/textarea.md +86 -86
- package/docs/components/theme-toggle.md +73 -73
- package/docs/components/timeline.md +121 -121
- package/docs/components/toggle-group.md +68 -68
- package/docs/components/toggle.md +62 -62
- package/docs/components/tooltip.md +116 -116
- package/docs/components/tree-view.md +238 -238
- package/docs/components/use-mobile.md +96 -96
- package/docs/components/video-player.md +68 -68
- package/docs/components/xertica-logo.md +36 -36
- package/docs/components/xertica-orbe.md +35 -35
- package/docs/components/xertica-provider.md +65 -65
- package/docs/components/xertica-xlogo.md +35 -35
- package/docs/decision-tree.md +293 -293
- package/docs/doc-audit.md +244 -243
- package/docs/form-sizing.md +162 -162
- package/docs/getting-started.md +616 -591
- package/docs/guidelines.md +330 -328
- package/docs/i18n.md +61 -57
- package/docs/installation.md +268 -267
- package/docs/layout.md +143 -143
- package/docs/llms.md +295 -295
- package/docs/patterns/analytics.md +194 -194
- package/docs/patterns/crud.md +149 -149
- package/docs/patterns/dashboard.md +138 -138
- package/docs/patterns/detail-page.md +296 -296
- package/docs/patterns/form.md +241 -241
- package/docs/patterns/login.md +156 -156
- package/docs/patterns/settings.md +368 -368
- package/docs/patterns/wizard.md +213 -213
- package/docs/state-management.md +289 -289
- package/guidelines/Guidelines.md +409 -406
- package/hooks/useTheme.test.tsx +16 -16
- package/hooks/useTheme.ts +4 -4
- package/imports/Podcast.tsx +540 -540
- package/imports/XerticaAi.tsx +46 -46
- package/imports/XerticaX.tsx +15 -15
- package/imports/svg-aueiaqngck.ts +20 -20
- package/imports/svg-v9krss1ozd.ts +23 -23
- package/imports/svg-vhrdofe3qe.ts +6 -6
- package/llms-compact.txt +2 -1
- package/llms.txt +2 -1
- package/mcp/resources.json +22 -22
- package/mcp/tools.json +35 -35
- package/package.json +219 -213
- package/scripts/ai-validator.ts +91 -91
- package/scripts/cleanup-case-dupes.ts +62 -62
- package/scripts/generate-ai-manifests.ts +107 -107
- package/styles/globals.css +13 -13
- package/styles/xertica/app-overrides/chat.css +61 -61
- package/styles/xertica/app-overrides/scrollbar.css +33 -33
- package/styles/xertica/base.css +90 -71
- package/styles/xertica/integrations/google-maps.css +76 -76
- package/styles/xertica/integrations/sonner.css +73 -73
- package/styles/xertica/theme-map.css +102 -99
- package/styles/xertica/tokens.css +240 -236
- package/templates/CLAUDE.md +16 -1
- package/templates/eslint.config.js +26 -26
- package/templates/guidelines/Guidelines.md +577 -553
- package/templates/package.json +69 -69
- package/templates/postcss.config.js +6 -6
- package/templates/src/app/App.tsx +46 -46
- package/templates/src/app/components/AppLayout.tsx +55 -55
- package/templates/src/app/components/AuthGuard.tsx +131 -82
- package/templates/src/app/context/AuthContext.tsx +108 -108
- package/templates/src/features/assistant/index.ts +5 -5
- package/templates/src/features/auth/index.ts +4 -4
- package/templates/src/features/auth/ui/AuthPageShell.tsx +32 -32
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +70 -72
- package/templates/src/features/auth/ui/LoginContent.tsx +92 -92
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +6 -2
- package/templates/src/features/auth/ui/SocialLoginButtons.tsx +78 -78
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +2 -6
- package/templates/src/features/home/data/mock.ts +41 -35
- package/templates/src/features/home/index.ts +11 -11
- package/templates/src/features/home/store/dashboardStore.ts +25 -25
- package/templates/src/features/home/ui/HomeContent.tsx +117 -119
- package/templates/src/features/template/index.ts +5 -5
- package/templates/src/features/template/ui/CrudTemplate.tsx +1 -4
- package/templates/src/features/template/ui/LoginTemplate.tsx +1 -1
- package/templates/src/features/template/ui/TemplateContent.tsx +29 -21
- package/templates/src/locales/en/pages/templates.json +17 -17
- package/templates/src/locales/es/pages/templates.json +17 -17
- package/templates/src/locales/pt-BR/pages/templates.json +17 -17
- package/templates/src/main.tsx +11 -11
- package/templates/src/pages/AssistantPage.tsx +26 -20
- package/templates/src/pages/ForgotPasswordPage.tsx +6 -6
- package/templates/src/pages/HomePage.tsx +53 -49
- package/templates/src/pages/LoginPage.tsx +10 -10
- package/templates/src/pages/ResetPasswordPage.tsx +6 -6
- package/templates/src/pages/TemplatePage.tsx +28 -28
- package/templates/src/pages/VerifyEmailPage.tsx +6 -6
- package/templates/src/shared/config/navigation.ts +19 -19
- package/templates/src/shared/error-boundary.tsx +150 -154
- package/templates/src/shared/error-fallbacks.tsx +222 -226
- package/templates/src/shared/lib/auth.ts +20 -20
- package/templates/src/shared/types/auth.ts +3 -3
- package/templates/src/styles/index.css +95 -95
- package/templates/src/styles/xertica/tokens.css +240 -236
- package/templates/tsconfig.json +25 -25
- package/templates/tsconfig.node.json +12 -12
- package/templates/vite-env.d.ts +1 -1
- package/templates/vite.config.js +20 -20
- package/templates/vite.config.ts +54 -51
- package/utils/color-utils.ts +72 -72
- package/utils/demo-responses.test.ts +10 -10
- package/utils/demo-responses.ts +151 -151
- package/utils/gemini.test.ts +25 -25
- package/utils/gemini.ts +155 -155
|
@@ -1,456 +1,456 @@
|
|
|
1
|
-
# Relatório de Melhorias Arquiteturais
|
|
2
|
-
|
|
3
|
-
> **Objetivo:** Identificar oportunidades para melhorar a separação entre UI e lógica de negócio, tornando o projeto mais moderno, testável e manutenível.
|
|
4
|
-
>
|
|
5
|
-
> **Data da análise:** Maio 2026
|
|
6
|
-
> **Escopo:** Todos os componentes, hooks e contextos do projeto
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## Sumário Executivo
|
|
11
|
-
|
|
12
|
-
O projeto já adota boas práticas em vários pontos — o padrão **headless hook** está presente em `useRichTextEditor`, `useTreeView`, `useSidebar` e `useAssistant`. No entanto, existem **inconsistências** onde a mesma lógica é duplicada em múltiplos lugares e componentes com mais de 600 linhas misturam JSX com lógica de estado.
|
|
13
|
-
|
|
14
|
-
### Severidade dos Problemas
|
|
15
|
-
|
|
16
|
-
| Severidade | Quantidade | Exemplos |
|
|
17
|
-
| ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
-
| 🔴 Crítico | 1 ~~2~~ | ~~`ThemeToggle` bypassa `ThemeContext`~~ ✅ resolvido em v2.2.0; tipos duplicados entre contextos |
|
|
19
|
-
| 🟠 Alto | 3 | `AudioPlayer` sem hook; detecção mobile duplicada 4×; `xertica-assistant.tsx` com 1573 linhas |
|
|
20
|
-
| 🟡 Médio | 4 | `sidebar.tsx` com 1089 linhas; atalhos de teclado no `LayoutContext`; `BrandColorsContext` com utilitário inline; `header.tsx` sem hook |
|
|
21
|
-
| 🟢 Baixo | 3 | `SidebarTooltipContent` duplicado; `AssistantTooltipContent` inline; cookie management inline |
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## 🔴 Problemas Críticos
|
|
26
|
-
|
|
27
|
-
### C1 — `ThemeToggle` bypassa `ThemeContext` completamente ✅ RESOLVIDO (v2.2.0)
|
|
28
|
-
|
|
29
|
-
**Arquivo:** [`components/brand/theme-toggle/ThemeToggle.tsx`](../components/brand/theme-toggle/ThemeToggle.tsx)
|
|
30
|
-
|
|
31
|
-
**Resolução:** O `ThemeToggle` foi corrigido para usar `useTheme()` do `ThemeContext`. Adicionalmente, o bug `disableDarkMode` foi corrigido em `templates/src/app/App.tsx` e `bin/language-config.ts` — o prop estava hard-coded como `true`, tornando o `toggleTheme()` um no-op em projetos gerados pelo CLI. O `ThemeToggle` agora funciona corretamente tanto no projeto base quanto em projetos gerados.
|
|
32
|
-
|
|
33
|
-
<details>
|
|
34
|
-
<summary>Análise original (histórico)</summary>
|
|
35
|
-
|
|
36
|
-
**Problema original:** O componente `ThemeToggle` manipulava `document.documentElement.classList` diretamente e mantinha seu próprio estado local com `useState`, ignorando completamente o `ThemeContext` e o hook `useTheme()`. Isso criava um **risco de dessincronização**: se o tema fosse alterado via `ThemeContext` (ex: por outro componente ou por `defaultTheme`), o `ThemeToggle` não refletiria a mudança.
|
|
37
|
-
|
|
38
|
-
Adicionalmente, em projetos gerados pelo CLI, `XerticaProvider` era instanciado com `disableDarkMode` hard-coded, fazendo `toggleTheme()` retornar imediatamente sem efeito.
|
|
39
|
-
|
|
40
|
-
</details>
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
### C2 — Tipos duplicados entre `AssistenteContext` e `xertica-assistant.tsx`
|
|
45
|
-
|
|
46
|
-
**Arquivos:**
|
|
47
|
-
|
|
48
|
-
- [`contexts/AssistenteContext.tsx`](../contexts/AssistenteContext.tsx) — linhas 5–71
|
|
49
|
-
- [`components/assistant/xertica-assistant/xertica-assistant.tsx`](../components/assistant/xertica-assistant/xertica-assistant.tsx) — linhas 155–230
|
|
50
|
-
|
|
51
|
-
**Problema:** Os mesmos tipos são definidos em dois lugares com nomes e estruturas ligeiramente diferentes:
|
|
52
|
-
|
|
53
|
-
| `AssistenteContext.tsx` | `xertica-assistant.tsx` | Diferença |
|
|
54
|
-
| ----------------------- | ----------------------- | ------------------------------------ |
|
|
55
|
-
| `Message` | `Message` | Campos diferentes (`role` vs `type`) |
|
|
56
|
-
| `Conversa` | `Conversation` | Nome diferente, campos em PT vs EN |
|
|
57
|
-
| `SearchResult` | `SearchResult` | Duplicado idêntico |
|
|
58
|
-
| `SearchSource` | `SearchSource` | Duplicado idêntico |
|
|
59
|
-
| `SearchCommand` | `SearchCommand` | Duplicado idêntico |
|
|
60
|
-
|
|
61
|
-
Isso significa que existem **dois contratos de tipo incompatíveis** para os mesmos dados, forçando conversões implícitas e tornando refatorações perigosas.
|
|
62
|
-
|
|
63
|
-
**Solução:** Criar um arquivo de tipos compartilhado:
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
components/assistant/xertica-assistant/
|
|
67
|
-
├── types.ts ← NOVO: fonte única de verdade para todos os tipos
|
|
68
|
-
├── use-assistant.ts ← importa de types.ts
|
|
69
|
-
├── xertica-assistant.tsx ← importa de types.ts
|
|
70
|
-
contexts/
|
|
71
|
-
└── AssistenteContext.tsx ← importa de types.ts (re-exporta para compatibilidade)
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
```ts
|
|
75
|
-
// components/assistant/xertica-assistant/types.ts
|
|
76
|
-
export interface Message { ... }
|
|
77
|
-
export interface Conversation { ... }
|
|
78
|
-
export interface SearchResult { ... }
|
|
79
|
-
// ...
|
|
80
|
-
|
|
81
|
-
// contexts/AssistenteContext.tsx
|
|
82
|
-
export type { Message, Conversation, SearchResult } from '@/components/assistant/xertica-assistant/types';
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## 🟠 Problemas de Alta Prioridade
|
|
88
|
-
|
|
89
|
-
### A1 — Detecção de mobile duplicada em 4+ lugares
|
|
90
|
-
|
|
91
|
-
**Problema:** A lógica `window.innerWidth < 768` está duplicada em:
|
|
92
|
-
|
|
93
|
-
| Arquivo | Linha | Forma |
|
|
94
|
-
| ----------------------------------------------------------------------------------------------------------------------- | ----- | -------------------------------- |
|
|
95
|
-
| [`components/shared/use-mobile.ts`](../components/shared/use-mobile.ts) | 18 | `useIsMobile()` hook ✅ |
|
|
96
|
-
| [`components/assistant/xertica-assistant/use-assistant.ts`](../components/assistant/xertica-assistant/use-assistant.ts) | ~192 | `window.innerWidth < 768` inline |
|
|
97
|
-
| [`components/layout/sidebar/use-sidebar.ts`](../components/layout/sidebar/use-sidebar.ts) | ~46 | `window.innerWidth < 768` inline |
|
|
98
|
-
| [`components/media/audio-player/AudioPlayer.tsx`](../components/media/audio-player/AudioPlayer.tsx) | ~113 | `window.innerWidth < 768` inline |
|
|
99
|
-
| [`contexts/LayoutContext.tsx`](../contexts/LayoutContext.tsx) | ~60 | `window.innerWidth < 768` inline |
|
|
100
|
-
|
|
101
|
-
O hook `useIsMobile()` **já existe** em `components/shared/use-mobile.ts` e usa `matchMedia` (mais eficiente que `resize` events), mas não está sendo usado pelos outros arquivos.
|
|
102
|
-
|
|
103
|
-
**Solução:** Substituir todas as ocorrências pelo hook existente:
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
// ❌ ATUAL
|
|
107
|
-
const [isMobileViewport, setIsMobileViewport] = useState(window.innerWidth < 768);
|
|
108
|
-
useEffect(() => {
|
|
109
|
-
const handleResize = () => setIsMobileViewport(window.innerWidth < 768);
|
|
110
|
-
window.addEventListener('resize', handleResize);
|
|
111
|
-
return () => window.removeEventListener('resize', handleResize);
|
|
112
|
-
}, []);
|
|
113
|
-
|
|
114
|
-
// ✅ CORRETO
|
|
115
|
-
import { useIsMobile } from '@/components/shared/use-mobile';
|
|
116
|
-
const isMobileViewport = useIsMobile();
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**Impacto:** Reduz código duplicado, garante breakpoint consistente (768px), usa `matchMedia` que é mais performático que `resize` events.
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
### A2 — `AudioPlayer.tsx` (663 linhas) sem hook headless
|
|
124
|
-
|
|
125
|
-
**Arquivo:** [`components/media/audio-player/AudioPlayer.tsx`](../components/media/audio-player/AudioPlayer.tsx)
|
|
126
|
-
|
|
127
|
-
**Problema:** Todo o estado e lógica do player de áudio está inline no componente, misturado com JSX. Inclui:
|
|
128
|
-
|
|
129
|
-
- Gerenciamento de estado (`isPlaying`, `currentTime`, `duration`, `volume`, `playbackRate`, `isFloating`)
|
|
130
|
-
- `IntersectionObserver` para auto-float
|
|
131
|
-
- Sincronização de áudio com `useEffect`
|
|
132
|
-
- Detecção de mobile (duplicada)
|
|
133
|
-
- Formatação de tempo (`formatTime`)
|
|
134
|
-
- Handlers de eventos (`togglePlay`, `handleSeek`, `handleVolumeChange`, `handleSetFloating`)
|
|
135
|
-
|
|
136
|
-
**Solução:** Extrair para `use-audio-player.ts`:
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
// components/media/audio-player/use-audio-player.ts
|
|
140
|
-
export interface UseAudioPlayerProps {
|
|
141
|
-
src: string;
|
|
142
|
-
autoFloat?: boolean;
|
|
143
|
-
onEnded?: () => void;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export interface UseAudioPlayerReturn {
|
|
147
|
-
audioRef: React.RefObject<HTMLAudioElement>;
|
|
148
|
-
containerRef: React.RefObject<HTMLDivElement>;
|
|
149
|
-
isPlaying: boolean;
|
|
150
|
-
currentTime: number;
|
|
151
|
-
duration: number;
|
|
152
|
-
volume: number;
|
|
153
|
-
playbackRate: number;
|
|
154
|
-
isFloating: boolean;
|
|
155
|
-
isMobile: boolean;
|
|
156
|
-
togglePlay: () => void;
|
|
157
|
-
handleSeek: (value: number[]) => void;
|
|
158
|
-
handleVolumeChange: (value: number[]) => void;
|
|
159
|
-
handleSetFloating: (floating: boolean) => void;
|
|
160
|
-
setPlaybackRate: (rate: number) => void;
|
|
161
|
-
formatTime: (time: number) => string;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function useAudioPlayer(props: UseAudioPlayerProps): UseAudioPlayerReturn { ... }
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
**Benefícios:**
|
|
168
|
-
|
|
169
|
-
- `AudioPlayer.tsx` fica com ~200 linhas de JSX puro
|
|
170
|
-
- Lógica do player pode ser testada unitariamente sem renderizar DOM
|
|
171
|
-
- Permite criar variantes do player (mini, fullscreen) reusando o mesmo hook
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
### A3 — `xertica-assistant.tsx` com 1573 linhas — decomposição necessária
|
|
176
|
-
|
|
177
|
-
**Arquivo:** [`components/assistant/xertica-assistant/xertica-assistant.tsx`](../components/assistant/xertica-assistant/xertica-assistant.tsx)
|
|
178
|
-
|
|
179
|
-
**Problema:** Mesmo com o `useAssistant` hook extraindo a lógica de estado, o componente ainda tem ~1000 linhas de JSX com sub-componentes definidos inline e seções de renderização muito longas.
|
|
180
|
-
|
|
181
|
-
**Estrutura atual:**
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
xertica-assistant.tsx (1573 linhas)
|
|
185
|
-
├── AssistantTooltipContent (componente inline, linhas 105-131)
|
|
186
|
-
├── Tipos exportados (linhas 155-230) ← deveria estar em types.ts
|
|
187
|
-
├── XerticaAssistantProps (linhas 246-417) ← 171 linhas de interface!
|
|
188
|
-
└── XerticaAssistant (linhas 467-1572)
|
|
189
|
-
├── Document editor overlay (linhas 598-694)
|
|
190
|
-
├── Collapsed view (linhas 695-827)
|
|
191
|
-
├── Chat messages list (linhas 888-1394) ← 506 linhas!
|
|
192
|
-
├── History/Favorites tabs (linhas 1439-1511)
|
|
193
|
-
├── Chat input area (linhas 1515-1531)
|
|
194
|
-
└── Feedback dialog (linhas 1539-1570)
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
**Solução — decomposição em sub-componentes:**
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
components/assistant/xertica-assistant/
|
|
201
|
-
├── types.ts ← NOVO: todos os tipos
|
|
202
|
-
├── use-assistant.ts ← existente (manter)
|
|
203
|
-
├── xertica-assistant.tsx ← orquestrador (~200 linhas)
|
|
204
|
-
└── parts/
|
|
205
|
-
├── AssistantCollapsedView.tsx ← NOVO: view colapsada
|
|
206
|
-
├── AssistantDocumentEditor.tsx ← NOVO: overlay do editor
|
|
207
|
-
├── AssistantMessageList.tsx ← NOVO: lista de mensagens
|
|
208
|
-
├── AssistantMessage.tsx ← NOVO: mensagem individual
|
|
209
|
-
├── AssistantHistoryTab.tsx ← NOVO: aba de histórico
|
|
210
|
-
├── AssistantFeedbackDialog.tsx ← NOVO: dialog de feedback
|
|
211
|
-
└── AssistantTooltipContent.tsx ← NOVO: tooltip customizado
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**Impacto:** `xertica-assistant.tsx` passa de 1573 para ~200 linhas. Cada sub-componente é testável e manutenível independentemente.
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## 🟡 Problemas de Média Prioridade
|
|
219
|
-
|
|
220
|
-
### M1 — `sidebar.tsx` com 1089 linhas — decomposição parcial necessária
|
|
221
|
-
|
|
222
|
-
**Arquivo:** [`components/layout/sidebar/sidebar.tsx`](../components/layout/sidebar/sidebar.tsx)
|
|
223
|
-
|
|
224
|
-
**Problema:** O arquivo já usa compound components (`SidebarRoot`, `SidebarHeader`, `SidebarNav`, `SidebarSearch`, `SidebarFooter`), mas todos estão no mesmo arquivo de 1089 linhas. Além disso, `SidebarTooltipContent` é definido inline (linhas 40-64) — idêntico ao `AssistantTooltipContent` em `xertica-assistant.tsx`.
|
|
225
|
-
|
|
226
|
-
**Solução:**
|
|
227
|
-
|
|
228
|
-
```
|
|
229
|
-
components/layout/sidebar/
|
|
230
|
-
├── sidebar.tsx ← orquestrador + export público (~100 linhas)
|
|
231
|
-
├── use-sidebar.ts ← existente (manter)
|
|
232
|
-
├── SidebarContext.tsx ← NOVO: contexto interno extraído
|
|
233
|
-
└── parts/
|
|
234
|
-
├── SidebarRoot.tsx ← NOVO
|
|
235
|
-
├── SidebarHeader.tsx ← NOVO
|
|
236
|
-
├── SidebarNav.tsx ← NOVO
|
|
237
|
-
├── SidebarSearch.tsx ← NOVO
|
|
238
|
-
└── SidebarFooter.tsx ← NOVO
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
**Oportunidade adicional:** `SidebarTooltipContent` e `AssistantTooltipContent` são idênticos — extrair para componente compartilhado:
|
|
242
|
-
|
|
243
|
-
```
|
|
244
|
-
components/shared/
|
|
245
|
-
└── CustomTooltipContent.tsx ← NOVO: tooltip com portal e arrow
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
### M2 — Atalhos de teclado e cookies misturados no `LayoutContext`
|
|
251
|
-
|
|
252
|
-
**Arquivo:** [`contexts/LayoutContext.tsx`](../contexts/LayoutContext.tsx)
|
|
253
|
-
|
|
254
|
-
**Problema:** O `LayoutContext` mistura três responsabilidades distintas:
|
|
255
|
-
|
|
256
|
-
1. **Estado de layout** (sidebar expandida, assistente expandido)
|
|
257
|
-
2. **Atalhos de teclado** (`Ctrl+B` para toggle sidebar, linhas 78-86)
|
|
258
|
-
3. **Persistência em cookies** (funções `getCookie`/`setCookie`, linhas 25-45)
|
|
259
|
-
|
|
260
|
-
**Solução — separação de responsabilidades:**
|
|
261
|
-
|
|
262
|
-
```ts
|
|
263
|
-
// contexts/LayoutContext.tsx — apenas estado
|
|
264
|
-
export function LayoutProvider({ children }) {
|
|
265
|
-
const [sidebarExpanded, setSidebarExpanded] = usePersistentState('sidebar', true);
|
|
266
|
-
// ...
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// hooks/use-layout-shortcuts.ts — NOVO: atalhos de teclado
|
|
270
|
-
export function useLayoutShortcuts() {
|
|
271
|
-
const { toggleSidebar } = useLayout();
|
|
272
|
-
useEffect(() => {
|
|
273
|
-
const handler = (e: KeyboardEvent) => {
|
|
274
|
-
if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
|
|
275
|
-
e.preventDefault();
|
|
276
|
-
toggleSidebar();
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
document.addEventListener('keydown', handler);
|
|
280
|
-
return () => document.removeEventListener('keydown', handler);
|
|
281
|
-
}, [toggleSidebar]);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// hooks/use-persistent-state.ts — NOVO: estado com persistência em cookie
|
|
285
|
-
export function usePersistentState<T>(key: string, defaultValue: T) { ... }
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
### M3 — `BrandColorsContext` com utilitário `hexToRgb` inline
|
|
291
|
-
|
|
292
|
-
**Arquivo:** [`contexts/BrandColorsContext.tsx`](../contexts/BrandColorsContext.tsx) — linhas 79-88
|
|
293
|
-
|
|
294
|
-
**Problema:** A função `hexToRgb` é um utilitário puro definido dentro do componente Provider. Não pode ser testada isoladamente e não pode ser reutilizada.
|
|
295
|
-
|
|
296
|
-
**Solução:**
|
|
297
|
-
|
|
298
|
-
```ts
|
|
299
|
-
// utils/color-utils.ts — NOVO
|
|
300
|
-
export function hexToRgb(hex: string): { r: number; g: number; b: number } | null { ... }
|
|
301
|
-
export function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } { ... }
|
|
302
|
-
export function generateColorScale(baseHex: string): Record<string, string> { ... }
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
---
|
|
306
|
-
|
|
307
|
-
### M4 — `header.tsx` sem hook headless
|
|
308
|
-
|
|
309
|
-
**Arquivo:** [`components/layout/header/header.tsx`](../components/layout/header/header.tsx)
|
|
310
|
-
|
|
311
|
-
**Problema:** O `Header` não tem lógica de estado complexa, mas tem lógica de renderização condicional para breadcrumbs, ações e menu do usuário que poderia ser extraída para melhorar a testabilidade.
|
|
312
|
-
|
|
313
|
-
**Avaliação:** Prioridade baixa — o componente tem 337 linhas mas é principalmente JSX declarativo. Não há estado local significativo. A extração de um `useHeader` hook seria de valor limitado aqui.
|
|
314
|
-
|
|
315
|
-
**Recomendação:** Manter como está, mas considerar extrair `HeaderUserMenu` e `HeaderBreadcrumbs` como sub-componentes separados para melhorar a legibilidade.
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## 🟢 Problemas de Baixa Prioridade
|
|
320
|
-
|
|
321
|
-
### B1 — `SidebarTooltipContent` e `AssistantTooltipContent` são idênticos
|
|
322
|
-
|
|
323
|
-
**Arquivos:**
|
|
324
|
-
|
|
325
|
-
- [`components/layout/sidebar/sidebar.tsx`](../components/layout/sidebar/sidebar.tsx) — linhas 40-64
|
|
326
|
-
- [`components/assistant/xertica-assistant/xertica-assistant.tsx`](../components/assistant/xertica-assistant/xertica-assistant.tsx) — linhas 105-131
|
|
327
|
-
|
|
328
|
-
Ambos implementam um tooltip com `TooltipPrimitive.Portal` e `TooltipPrimitive.Arrow`. Extrair para componente compartilhado elimina duplicação.
|
|
329
|
-
|
|
330
|
-
---
|
|
331
|
-
|
|
332
|
-
### B2 — `use-sidebar.ts` tem alturas hardcoded
|
|
333
|
-
|
|
334
|
-
**Arquivo:** [`components/layout/sidebar/use-sidebar.ts`](../components/layout/sidebar/use-sidebar.ts) — linhas 57-91
|
|
335
|
-
|
|
336
|
-
A função `checkOverflow` usa valores hardcoded (`40px`, `32px`) para calcular overflow de itens de navegação. Esses valores deveriam ser constantes nomeadas ou derivados do DOM.
|
|
337
|
-
|
|
338
|
-
```ts
|
|
339
|
-
// ❌ ATUAL
|
|
340
|
-
const itemHeight = 40;
|
|
341
|
-
const groupHeaderHeight = 32;
|
|
342
|
-
|
|
343
|
-
// ✅ MELHOR
|
|
344
|
-
const SIDEBAR_ITEM_HEIGHT_PX = 40;
|
|
345
|
-
const SIDEBAR_GROUP_HEADER_HEIGHT_PX = 32;
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
### B3 — `use-assistant.ts` tem `setTimeout` para simular async ✅ RESOLVIDO
|
|
351
|
-
|
|
352
|
-
**Arquivo:** [`components/assistant/xertica-assistant/use-assistant.ts`](../components/assistant/xertica-assistant/use-assistant.ts)
|
|
353
|
-
|
|
354
|
-
**Resolução (v2.1.9):** Todo o dado mock foi extraído para a camada `features/*/data/mock.ts`. A estrutura implementada usa o padrão **swap point**: cada `features/<nome>/data/mock.ts` contém funções `async fetch*()` que simulam latência de rede e retornam dados tipados. Para conectar a uma API real, basta substituir o corpo dessas funções por chamadas `fetch()` ou `axios` — hooks, componentes e tipos permanecem inalterados.
|
|
355
|
-
|
|
356
|
-
```ts
|
|
357
|
-
// features/assistant/data/mock.ts — antes (mock)
|
|
358
|
-
export async function fetchAssistantConfig(): Promise<AssistantConfig> {
|
|
359
|
-
await new Promise(resolve => setTimeout(resolve, 150)); // simula latência
|
|
360
|
-
return MOCK_ASSISTANT_CONFIG;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// features/assistant/data/mock.ts — depois (API real)
|
|
364
|
-
export async function fetchAssistantConfig(): Promise<AssistantConfig> {
|
|
365
|
-
return fetch('/api/assistant/config').then(r => r.json());
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
A configuração do assistente (suggestions, richSuggestions, feedbackOptions) é agora consumida via `useAssistantConfig()` (TanStack React Query) com `staleTime: 30min`.
|
|
370
|
-
|
|
371
|
-
---
|
|
372
|
-
|
|
373
|
-
## Padrões Positivos — Manter e Expandir
|
|
374
|
-
|
|
375
|
-
O projeto já tem excelentes exemplos do padrão headless hook que devem servir como referência:
|
|
376
|
-
|
|
377
|
-
### ✅ `useRichTextEditor` — Exemplo de referência
|
|
378
|
-
|
|
379
|
-
**Arquivo:** [`components/ui/rich-text-editor/use-rich-text-editor.ts`](../components/ui/rich-text-editor/use-rich-text-editor.ts)
|
|
380
|
-
|
|
381
|
-
- Interface `UseRichTextEditorProps` bem definida
|
|
382
|
-
- Interface `UseRichTextEditorReturn` completa com todos os retornos tipados
|
|
383
|
-
- Lógica completamente separada do JSX
|
|
384
|
-
- `rich-text-editor.tsx` é JSX puro consumindo o hook
|
|
385
|
-
|
|
386
|
-
### ✅ `useTreeView` — Exemplo de referência para acessibilidade
|
|
387
|
-
|
|
388
|
-
**Arquivo:** [`components/ui/tree-view/use-tree-view.ts`](../components/ui/tree-view/use-tree-view.ts)
|
|
389
|
-
|
|
390
|
-
- WAI-ARIA keyboard navigation implementada no hook
|
|
391
|
-
- Componente de UI não precisa conhecer lógica de navegação por teclado
|
|
392
|
-
|
|
393
|
-
### ✅ `useAssistant` — Boa extração, mas incompleta
|
|
394
|
-
|
|
395
|
-
**Arquivo:** [`components/assistant/xertica-assistant/use-assistant.ts`](../components/assistant/xertica-assistant/use-assistant.ts)
|
|
396
|
-
|
|
397
|
-
- Toda a lógica de estado está no hook
|
|
398
|
-
- O componente ainda tem 1000+ linhas de JSX que precisam de decomposição
|
|
399
|
-
|
|
400
|
-
---
|
|
401
|
-
|
|
402
|
-
## Plano de Implementação Recomendado
|
|
403
|
-
|
|
404
|
-
### Fase 1 — Correções Críticas (1-2 dias)
|
|
405
|
-
|
|
406
|
-
| #
|
|
407
|
-
|
|
|
408
|
-
| ~~1.1~~ | ~~Corrigir `ThemeToggle` para usar `useTheme()`~~
|
|
409
|
-
| 1.2
|
|
410
|
-
| 1.3
|
|
411
|
-
|
|
412
|
-
### Fase 2 — Eliminação de Duplicações (2-3 dias)
|
|
413
|
-
|
|
414
|
-
| # | Tarefa | Arquivo(s) | Esforço |
|
|
415
|
-
| --- | ---------------------------------------------- | -------------- | ------- |
|
|
416
|
-
| 2.1 | Substituir detecção mobile por `useIsMobile()` | 4 arquivos | 2h |
|
|
417
|
-
| 2.2 | Extrair `CustomTooltipContent` compartilhado | 1 arquivo novo | 1h |
|
|
418
|
-
| 2.3 | Mover `hexToRgb` para `utils/color-utils.ts` | 2 arquivos | 30min |
|
|
419
|
-
|
|
420
|
-
### Fase 3 — Extração de Hooks (3-5 dias)
|
|
421
|
-
|
|
422
|
-
| # | Tarefa | Arquivo(s) | Esforço |
|
|
423
|
-
| --- | -------------------------------------------- | ----------------- | ------- |
|
|
424
|
-
| 3.1 | Criar `use-audio-player.ts` | 1 arquivo novo | 4h |
|
|
425
|
-
| 3.2 | Refatorar `AudioPlayer.tsx` para usar o hook | `AudioPlayer.tsx` | 2h |
|
|
426
|
-
| 3.3 | Extrair `use-layout-shortcuts.ts` | 1 arquivo novo | 1h |
|
|
427
|
-
| 3.4 | Extrair `use-persistent-state.ts` | 1 arquivo novo | 1h |
|
|
428
|
-
|
|
429
|
-
### Fase 4 — Decomposição de Componentes Grandes (5-8 dias)
|
|
430
|
-
|
|
431
|
-
| # | Tarefa | Arquivo(s) | Esforço |
|
|
432
|
-
| --- | --------------------------------------------------- | ---------------- | ------- |
|
|
433
|
-
| 4.1 | Decompor `xertica-assistant.tsx` em sub-componentes | 7 arquivos novos | 1 dia |
|
|
434
|
-
| 4.2 | Decompor `sidebar.tsx` em sub-componentes | 5 arquivos novos | 1 dia |
|
|
435
|
-
| 4.3 | Extrair `HeaderUserMenu` e `HeaderBreadcrumbs` | 2 arquivos novos | 3h |
|
|
436
|
-
|
|
437
|
-
---
|
|
438
|
-
|
|
439
|
-
## Métricas de Melhoria Esperadas
|
|
440
|
-
|
|
441
|
-
| Métrica | Atual | Após Fase 1-2 | Após Fase 3-4 |
|
|
442
|
-
| --------------------------------- | ----------------------- | ------------- | ------------- |
|
|
443
|
-
| Maior arquivo (linhas) | 1573 | 1573 | ~200 |
|
|
444
|
-
| Duplicações de detecção mobile | 4 | 1 | 1 |
|
|
445
|
-
| Definições de tipo duplicadas | 2 conjuntos | 1 conjunto | 1 conjunto |
|
|
446
|
-
| Componentes sem hook headless | 2 (AudioPlayer, Header) | 2 | 0-1 |
|
|
447
|
-
| Risco de dessincronização de tema | Eliminado (v2.2.0) | Eliminado | Eliminado |
|
|
448
|
-
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
## Referências
|
|
452
|
-
|
|
453
|
-
- [Padrão Headless Hook — Kent C. Dodds](https://kentcdodds.com/blog/inversion-of-control)
|
|
454
|
-
- [Compound Components Pattern](https://kentcdodds.com/blog/compound-components-with-react-hooks)
|
|
455
|
-
- [`docs/architecture.md`](./architecture.md) — Arquitetura atual do projeto
|
|
456
|
-
- [`docs/doc-audit.md`](./doc-audit.md) — Auditoria de documentação
|
|
1
|
+
# Relatório de Melhorias Arquiteturais
|
|
2
|
+
|
|
3
|
+
> **Objetivo:** Identificar oportunidades para melhorar a separação entre UI e lógica de negócio, tornando o projeto mais moderno, testável e manutenível.
|
|
4
|
+
>
|
|
5
|
+
> **Data da análise:** Maio 2026
|
|
6
|
+
> **Escopo:** Todos os componentes, hooks e contextos do projeto
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Sumário Executivo
|
|
11
|
+
|
|
12
|
+
O projeto já adota boas práticas em vários pontos — o padrão **headless hook** está presente em `useRichTextEditor`, `useTreeView`, `useSidebar` e `useAssistant`. No entanto, existem **inconsistências** onde a mesma lógica é duplicada em múltiplos lugares e componentes com mais de 600 linhas misturam JSX com lógica de estado.
|
|
13
|
+
|
|
14
|
+
### Severidade dos Problemas
|
|
15
|
+
|
|
16
|
+
| Severidade | Quantidade | Exemplos |
|
|
17
|
+
| ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| 🔴 Crítico | 1 ~~2~~ | ~~`ThemeToggle` bypassa `ThemeContext`~~ ✅ resolvido em v2.2.0; tipos duplicados entre contextos |
|
|
19
|
+
| 🟠 Alto | 3 | `AudioPlayer` sem hook; detecção mobile duplicada 4×; `xertica-assistant.tsx` com 1573 linhas |
|
|
20
|
+
| 🟡 Médio | 4 | `sidebar.tsx` com 1089 linhas; atalhos de teclado no `LayoutContext`; `BrandColorsContext` com utilitário inline; `header.tsx` sem hook |
|
|
21
|
+
| 🟢 Baixo | 3 | `SidebarTooltipContent` duplicado; `AssistantTooltipContent` inline; cookie management inline |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🔴 Problemas Críticos
|
|
26
|
+
|
|
27
|
+
### C1 — `ThemeToggle` bypassa `ThemeContext` completamente ✅ RESOLVIDO (v2.2.0)
|
|
28
|
+
|
|
29
|
+
**Arquivo:** [`components/brand/theme-toggle/ThemeToggle.tsx`](../components/brand/theme-toggle/ThemeToggle.tsx)
|
|
30
|
+
|
|
31
|
+
**Resolução:** O `ThemeToggle` foi corrigido para usar `useTheme()` do `ThemeContext`. Adicionalmente, o bug `disableDarkMode` foi corrigido em `templates/src/app/App.tsx` e `bin/language-config.ts` — o prop estava hard-coded como `true`, tornando o `toggleTheme()` um no-op em projetos gerados pelo CLI. O `ThemeToggle` agora funciona corretamente tanto no projeto base quanto em projetos gerados.
|
|
32
|
+
|
|
33
|
+
<details>
|
|
34
|
+
<summary>Análise original (histórico)</summary>
|
|
35
|
+
|
|
36
|
+
**Problema original:** O componente `ThemeToggle` manipulava `document.documentElement.classList` diretamente e mantinha seu próprio estado local com `useState`, ignorando completamente o `ThemeContext` e o hook `useTheme()`. Isso criava um **risco de dessincronização**: se o tema fosse alterado via `ThemeContext` (ex: por outro componente ou por `defaultTheme`), o `ThemeToggle` não refletiria a mudança.
|
|
37
|
+
|
|
38
|
+
Adicionalmente, em projetos gerados pelo CLI, `XerticaProvider` era instanciado com `disableDarkMode` hard-coded, fazendo `toggleTheme()` retornar imediatamente sem efeito.
|
|
39
|
+
|
|
40
|
+
</details>
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### C2 — Tipos duplicados entre `AssistenteContext` e `xertica-assistant.tsx`
|
|
45
|
+
|
|
46
|
+
**Arquivos:**
|
|
47
|
+
|
|
48
|
+
- [`contexts/AssistenteContext.tsx`](../contexts/AssistenteContext.tsx) — linhas 5–71
|
|
49
|
+
- [`components/assistant/xertica-assistant/xertica-assistant.tsx`](../components/assistant/xertica-assistant/xertica-assistant.tsx) — linhas 155–230
|
|
50
|
+
|
|
51
|
+
**Problema:** Os mesmos tipos são definidos em dois lugares com nomes e estruturas ligeiramente diferentes:
|
|
52
|
+
|
|
53
|
+
| `AssistenteContext.tsx` | `xertica-assistant.tsx` | Diferença |
|
|
54
|
+
| ----------------------- | ----------------------- | ------------------------------------ |
|
|
55
|
+
| `Message` | `Message` | Campos diferentes (`role` vs `type`) |
|
|
56
|
+
| `Conversa` | `Conversation` | Nome diferente, campos em PT vs EN |
|
|
57
|
+
| `SearchResult` | `SearchResult` | Duplicado idêntico |
|
|
58
|
+
| `SearchSource` | `SearchSource` | Duplicado idêntico |
|
|
59
|
+
| `SearchCommand` | `SearchCommand` | Duplicado idêntico |
|
|
60
|
+
|
|
61
|
+
Isso significa que existem **dois contratos de tipo incompatíveis** para os mesmos dados, forçando conversões implícitas e tornando refatorações perigosas.
|
|
62
|
+
|
|
63
|
+
**Solução:** Criar um arquivo de tipos compartilhado:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
components/assistant/xertica-assistant/
|
|
67
|
+
├── types.ts ← NOVO: fonte única de verdade para todos os tipos
|
|
68
|
+
├── use-assistant.ts ← importa de types.ts
|
|
69
|
+
├── xertica-assistant.tsx ← importa de types.ts
|
|
70
|
+
contexts/
|
|
71
|
+
└── AssistenteContext.tsx ← importa de types.ts (re-exporta para compatibilidade)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// components/assistant/xertica-assistant/types.ts
|
|
76
|
+
export interface Message { ... }
|
|
77
|
+
export interface Conversation { ... }
|
|
78
|
+
export interface SearchResult { ... }
|
|
79
|
+
// ...
|
|
80
|
+
|
|
81
|
+
// contexts/AssistenteContext.tsx
|
|
82
|
+
export type { Message, Conversation, SearchResult } from '@/components/assistant/xertica-assistant/types';
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 🟠 Problemas de Alta Prioridade
|
|
88
|
+
|
|
89
|
+
### A1 — Detecção de mobile duplicada em 4+ lugares
|
|
90
|
+
|
|
91
|
+
**Problema:** A lógica `window.innerWidth < 768` está duplicada em:
|
|
92
|
+
|
|
93
|
+
| Arquivo | Linha | Forma |
|
|
94
|
+
| ----------------------------------------------------------------------------------------------------------------------- | ----- | -------------------------------- |
|
|
95
|
+
| [`components/shared/use-mobile.ts`](../components/shared/use-mobile.ts) | 18 | `useIsMobile()` hook ✅ |
|
|
96
|
+
| [`components/assistant/xertica-assistant/use-assistant.ts`](../components/assistant/xertica-assistant/use-assistant.ts) | ~192 | `window.innerWidth < 768` inline |
|
|
97
|
+
| [`components/layout/sidebar/use-sidebar.ts`](../components/layout/sidebar/use-sidebar.ts) | ~46 | `window.innerWidth < 768` inline |
|
|
98
|
+
| [`components/media/audio-player/AudioPlayer.tsx`](../components/media/audio-player/AudioPlayer.tsx) | ~113 | `window.innerWidth < 768` inline |
|
|
99
|
+
| [`contexts/LayoutContext.tsx`](../contexts/LayoutContext.tsx) | ~60 | `window.innerWidth < 768` inline |
|
|
100
|
+
|
|
101
|
+
O hook `useIsMobile()` **já existe** em `components/shared/use-mobile.ts` e usa `matchMedia` (mais eficiente que `resize` events), mas não está sendo usado pelos outros arquivos.
|
|
102
|
+
|
|
103
|
+
**Solução:** Substituir todas as ocorrências pelo hook existente:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// ❌ ATUAL
|
|
107
|
+
const [isMobileViewport, setIsMobileViewport] = useState(window.innerWidth < 768);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const handleResize = () => setIsMobileViewport(window.innerWidth < 768);
|
|
110
|
+
window.addEventListener('resize', handleResize);
|
|
111
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
112
|
+
}, []);
|
|
113
|
+
|
|
114
|
+
// ✅ CORRETO
|
|
115
|
+
import { useIsMobile } from '@/components/shared/use-mobile';
|
|
116
|
+
const isMobileViewport = useIsMobile();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Impacto:** Reduz código duplicado, garante breakpoint consistente (768px), usa `matchMedia` que é mais performático que `resize` events.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
### A2 — `AudioPlayer.tsx` (663 linhas) sem hook headless
|
|
124
|
+
|
|
125
|
+
**Arquivo:** [`components/media/audio-player/AudioPlayer.tsx`](../components/media/audio-player/AudioPlayer.tsx)
|
|
126
|
+
|
|
127
|
+
**Problema:** Todo o estado e lógica do player de áudio está inline no componente, misturado com JSX. Inclui:
|
|
128
|
+
|
|
129
|
+
- Gerenciamento de estado (`isPlaying`, `currentTime`, `duration`, `volume`, `playbackRate`, `isFloating`)
|
|
130
|
+
- `IntersectionObserver` para auto-float
|
|
131
|
+
- Sincronização de áudio com `useEffect`
|
|
132
|
+
- Detecção de mobile (duplicada)
|
|
133
|
+
- Formatação de tempo (`formatTime`)
|
|
134
|
+
- Handlers de eventos (`togglePlay`, `handleSeek`, `handleVolumeChange`, `handleSetFloating`)
|
|
135
|
+
|
|
136
|
+
**Solução:** Extrair para `use-audio-player.ts`:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
// components/media/audio-player/use-audio-player.ts
|
|
140
|
+
export interface UseAudioPlayerProps {
|
|
141
|
+
src: string;
|
|
142
|
+
autoFloat?: boolean;
|
|
143
|
+
onEnded?: () => void;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface UseAudioPlayerReturn {
|
|
147
|
+
audioRef: React.RefObject<HTMLAudioElement>;
|
|
148
|
+
containerRef: React.RefObject<HTMLDivElement>;
|
|
149
|
+
isPlaying: boolean;
|
|
150
|
+
currentTime: number;
|
|
151
|
+
duration: number;
|
|
152
|
+
volume: number;
|
|
153
|
+
playbackRate: number;
|
|
154
|
+
isFloating: boolean;
|
|
155
|
+
isMobile: boolean;
|
|
156
|
+
togglePlay: () => void;
|
|
157
|
+
handleSeek: (value: number[]) => void;
|
|
158
|
+
handleVolumeChange: (value: number[]) => void;
|
|
159
|
+
handleSetFloating: (floating: boolean) => void;
|
|
160
|
+
setPlaybackRate: (rate: number) => void;
|
|
161
|
+
formatTime: (time: number) => string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function useAudioPlayer(props: UseAudioPlayerProps): UseAudioPlayerReturn { ... }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Benefícios:**
|
|
168
|
+
|
|
169
|
+
- `AudioPlayer.tsx` fica com ~200 linhas de JSX puro
|
|
170
|
+
- Lógica do player pode ser testada unitariamente sem renderizar DOM
|
|
171
|
+
- Permite criar variantes do player (mini, fullscreen) reusando o mesmo hook
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### A3 — `xertica-assistant.tsx` com 1573 linhas — decomposição necessária
|
|
176
|
+
|
|
177
|
+
**Arquivo:** [`components/assistant/xertica-assistant/xertica-assistant.tsx`](../components/assistant/xertica-assistant/xertica-assistant.tsx)
|
|
178
|
+
|
|
179
|
+
**Problema:** Mesmo com o `useAssistant` hook extraindo a lógica de estado, o componente ainda tem ~1000 linhas de JSX com sub-componentes definidos inline e seções de renderização muito longas.
|
|
180
|
+
|
|
181
|
+
**Estrutura atual:**
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
xertica-assistant.tsx (1573 linhas)
|
|
185
|
+
├── AssistantTooltipContent (componente inline, linhas 105-131)
|
|
186
|
+
├── Tipos exportados (linhas 155-230) ← deveria estar em types.ts
|
|
187
|
+
├── XerticaAssistantProps (linhas 246-417) ← 171 linhas de interface!
|
|
188
|
+
└── XerticaAssistant (linhas 467-1572)
|
|
189
|
+
├── Document editor overlay (linhas 598-694)
|
|
190
|
+
├── Collapsed view (linhas 695-827)
|
|
191
|
+
├── Chat messages list (linhas 888-1394) ← 506 linhas!
|
|
192
|
+
├── History/Favorites tabs (linhas 1439-1511)
|
|
193
|
+
├── Chat input area (linhas 1515-1531)
|
|
194
|
+
└── Feedback dialog (linhas 1539-1570)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Solução — decomposição em sub-componentes:**
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
components/assistant/xertica-assistant/
|
|
201
|
+
├── types.ts ← NOVO: todos os tipos
|
|
202
|
+
├── use-assistant.ts ← existente (manter)
|
|
203
|
+
├── xertica-assistant.tsx ← orquestrador (~200 linhas)
|
|
204
|
+
└── parts/
|
|
205
|
+
├── AssistantCollapsedView.tsx ← NOVO: view colapsada
|
|
206
|
+
├── AssistantDocumentEditor.tsx ← NOVO: overlay do editor
|
|
207
|
+
├── AssistantMessageList.tsx ← NOVO: lista de mensagens
|
|
208
|
+
├── AssistantMessage.tsx ← NOVO: mensagem individual
|
|
209
|
+
├── AssistantHistoryTab.tsx ← NOVO: aba de histórico
|
|
210
|
+
├── AssistantFeedbackDialog.tsx ← NOVO: dialog de feedback
|
|
211
|
+
└── AssistantTooltipContent.tsx ← NOVO: tooltip customizado
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Impacto:** `xertica-assistant.tsx` passa de 1573 para ~200 linhas. Cada sub-componente é testável e manutenível independentemente.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 🟡 Problemas de Média Prioridade
|
|
219
|
+
|
|
220
|
+
### M1 — `sidebar.tsx` com 1089 linhas — decomposição parcial necessária
|
|
221
|
+
|
|
222
|
+
**Arquivo:** [`components/layout/sidebar/sidebar.tsx`](../components/layout/sidebar/sidebar.tsx)
|
|
223
|
+
|
|
224
|
+
**Problema:** O arquivo já usa compound components (`SidebarRoot`, `SidebarHeader`, `SidebarNav`, `SidebarSearch`, `SidebarFooter`), mas todos estão no mesmo arquivo de 1089 linhas. Além disso, `SidebarTooltipContent` é definido inline (linhas 40-64) — idêntico ao `AssistantTooltipContent` em `xertica-assistant.tsx`.
|
|
225
|
+
|
|
226
|
+
**Solução:**
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
components/layout/sidebar/
|
|
230
|
+
├── sidebar.tsx ← orquestrador + export público (~100 linhas)
|
|
231
|
+
├── use-sidebar.ts ← existente (manter)
|
|
232
|
+
├── SidebarContext.tsx ← NOVO: contexto interno extraído
|
|
233
|
+
└── parts/
|
|
234
|
+
├── SidebarRoot.tsx ← NOVO
|
|
235
|
+
├── SidebarHeader.tsx ← NOVO
|
|
236
|
+
├── SidebarNav.tsx ← NOVO
|
|
237
|
+
├── SidebarSearch.tsx ← NOVO
|
|
238
|
+
└── SidebarFooter.tsx ← NOVO
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Oportunidade adicional:** `SidebarTooltipContent` e `AssistantTooltipContent` são idênticos — extrair para componente compartilhado:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
components/shared/
|
|
245
|
+
└── CustomTooltipContent.tsx ← NOVO: tooltip com portal e arrow
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
### M2 — Atalhos de teclado e cookies misturados no `LayoutContext`
|
|
251
|
+
|
|
252
|
+
**Arquivo:** [`contexts/LayoutContext.tsx`](../contexts/LayoutContext.tsx)
|
|
253
|
+
|
|
254
|
+
**Problema:** O `LayoutContext` mistura três responsabilidades distintas:
|
|
255
|
+
|
|
256
|
+
1. **Estado de layout** (sidebar expandida, assistente expandido)
|
|
257
|
+
2. **Atalhos de teclado** (`Ctrl+B` para toggle sidebar, linhas 78-86)
|
|
258
|
+
3. **Persistência em cookies** (funções `getCookie`/`setCookie`, linhas 25-45)
|
|
259
|
+
|
|
260
|
+
**Solução — separação de responsabilidades:**
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
// contexts/LayoutContext.tsx — apenas estado
|
|
264
|
+
export function LayoutProvider({ children }) {
|
|
265
|
+
const [sidebarExpanded, setSidebarExpanded] = usePersistentState('sidebar', true);
|
|
266
|
+
// ...
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// hooks/use-layout-shortcuts.ts — NOVO: atalhos de teclado
|
|
270
|
+
export function useLayoutShortcuts() {
|
|
271
|
+
const { toggleSidebar } = useLayout();
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
const handler = (e: KeyboardEvent) => {
|
|
274
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
|
|
275
|
+
e.preventDefault();
|
|
276
|
+
toggleSidebar();
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
document.addEventListener('keydown', handler);
|
|
280
|
+
return () => document.removeEventListener('keydown', handler);
|
|
281
|
+
}, [toggleSidebar]);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// hooks/use-persistent-state.ts — NOVO: estado com persistência em cookie
|
|
285
|
+
export function usePersistentState<T>(key: string, defaultValue: T) { ... }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
### M3 — `BrandColorsContext` com utilitário `hexToRgb` inline
|
|
291
|
+
|
|
292
|
+
**Arquivo:** [`contexts/BrandColorsContext.tsx`](../contexts/BrandColorsContext.tsx) — linhas 79-88
|
|
293
|
+
|
|
294
|
+
**Problema:** A função `hexToRgb` é um utilitário puro definido dentro do componente Provider. Não pode ser testada isoladamente e não pode ser reutilizada.
|
|
295
|
+
|
|
296
|
+
**Solução:**
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
// utils/color-utils.ts — NOVO
|
|
300
|
+
export function hexToRgb(hex: string): { r: number; g: number; b: number } | null { ... }
|
|
301
|
+
export function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } { ... }
|
|
302
|
+
export function generateColorScale(baseHex: string): Record<string, string> { ... }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### M4 — `header.tsx` sem hook headless
|
|
308
|
+
|
|
309
|
+
**Arquivo:** [`components/layout/header/header.tsx`](../components/layout/header/header.tsx)
|
|
310
|
+
|
|
311
|
+
**Problema:** O `Header` não tem lógica de estado complexa, mas tem lógica de renderização condicional para breadcrumbs, ações e menu do usuário que poderia ser extraída para melhorar a testabilidade.
|
|
312
|
+
|
|
313
|
+
**Avaliação:** Prioridade baixa — o componente tem 337 linhas mas é principalmente JSX declarativo. Não há estado local significativo. A extração de um `useHeader` hook seria de valor limitado aqui.
|
|
314
|
+
|
|
315
|
+
**Recomendação:** Manter como está, mas considerar extrair `HeaderUserMenu` e `HeaderBreadcrumbs` como sub-componentes separados para melhorar a legibilidade.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 🟢 Problemas de Baixa Prioridade
|
|
320
|
+
|
|
321
|
+
### B1 — `SidebarTooltipContent` e `AssistantTooltipContent` são idênticos
|
|
322
|
+
|
|
323
|
+
**Arquivos:**
|
|
324
|
+
|
|
325
|
+
- [`components/layout/sidebar/sidebar.tsx`](../components/layout/sidebar/sidebar.tsx) — linhas 40-64
|
|
326
|
+
- [`components/assistant/xertica-assistant/xertica-assistant.tsx`](../components/assistant/xertica-assistant/xertica-assistant.tsx) — linhas 105-131
|
|
327
|
+
|
|
328
|
+
Ambos implementam um tooltip com `TooltipPrimitive.Portal` e `TooltipPrimitive.Arrow`. Extrair para componente compartilhado elimina duplicação.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
### B2 — `use-sidebar.ts` tem alturas hardcoded
|
|
333
|
+
|
|
334
|
+
**Arquivo:** [`components/layout/sidebar/use-sidebar.ts`](../components/layout/sidebar/use-sidebar.ts) — linhas 57-91
|
|
335
|
+
|
|
336
|
+
A função `checkOverflow` usa valores hardcoded (`40px`, `32px`) para calcular overflow de itens de navegação. Esses valores deveriam ser constantes nomeadas ou derivados do DOM.
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
// ❌ ATUAL
|
|
340
|
+
const itemHeight = 40;
|
|
341
|
+
const groupHeaderHeight = 32;
|
|
342
|
+
|
|
343
|
+
// ✅ MELHOR
|
|
344
|
+
const SIDEBAR_ITEM_HEIGHT_PX = 40;
|
|
345
|
+
const SIDEBAR_GROUP_HEADER_HEIGHT_PX = 32;
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
### B3 — `use-assistant.ts` tem `setTimeout` para simular async ✅ RESOLVIDO
|
|
351
|
+
|
|
352
|
+
**Arquivo:** [`components/assistant/xertica-assistant/use-assistant.ts`](../components/assistant/xertica-assistant/use-assistant.ts)
|
|
353
|
+
|
|
354
|
+
**Resolução (v2.1.9):** Todo o dado mock foi extraído para a camada `features/*/data/mock.ts`. A estrutura implementada usa o padrão **swap point**: cada `features/<nome>/data/mock.ts` contém funções `async fetch*()` que simulam latência de rede e retornam dados tipados. Para conectar a uma API real, basta substituir o corpo dessas funções por chamadas `fetch()` ou `axios` — hooks, componentes e tipos permanecem inalterados.
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
// features/assistant/data/mock.ts — antes (mock)
|
|
358
|
+
export async function fetchAssistantConfig(): Promise<AssistantConfig> {
|
|
359
|
+
await new Promise(resolve => setTimeout(resolve, 150)); // simula latência
|
|
360
|
+
return MOCK_ASSISTANT_CONFIG;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// features/assistant/data/mock.ts — depois (API real)
|
|
364
|
+
export async function fetchAssistantConfig(): Promise<AssistantConfig> {
|
|
365
|
+
return fetch('/api/assistant/config').then(r => r.json());
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
A configuração do assistente (suggestions, richSuggestions, feedbackOptions) é agora consumida via `useAssistantConfig()` (TanStack React Query) com `staleTime: 30min`.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Padrões Positivos — Manter e Expandir
|
|
374
|
+
|
|
375
|
+
O projeto já tem excelentes exemplos do padrão headless hook que devem servir como referência:
|
|
376
|
+
|
|
377
|
+
### ✅ `useRichTextEditor` — Exemplo de referência
|
|
378
|
+
|
|
379
|
+
**Arquivo:** [`components/ui/rich-text-editor/use-rich-text-editor.ts`](../components/ui/rich-text-editor/use-rich-text-editor.ts)
|
|
380
|
+
|
|
381
|
+
- Interface `UseRichTextEditorProps` bem definida
|
|
382
|
+
- Interface `UseRichTextEditorReturn` completa com todos os retornos tipados
|
|
383
|
+
- Lógica completamente separada do JSX
|
|
384
|
+
- `rich-text-editor.tsx` é JSX puro consumindo o hook
|
|
385
|
+
|
|
386
|
+
### ✅ `useTreeView` — Exemplo de referência para acessibilidade
|
|
387
|
+
|
|
388
|
+
**Arquivo:** [`components/ui/tree-view/use-tree-view.ts`](../components/ui/tree-view/use-tree-view.ts)
|
|
389
|
+
|
|
390
|
+
- WAI-ARIA keyboard navigation implementada no hook
|
|
391
|
+
- Componente de UI não precisa conhecer lógica de navegação por teclado
|
|
392
|
+
|
|
393
|
+
### ✅ `useAssistant` — Boa extração, mas incompleta
|
|
394
|
+
|
|
395
|
+
**Arquivo:** [`components/assistant/xertica-assistant/use-assistant.ts`](../components/assistant/xertica-assistant/use-assistant.ts)
|
|
396
|
+
|
|
397
|
+
- Toda a lógica de estado está no hook
|
|
398
|
+
- O componente ainda tem 1000+ linhas de JSX que precisam de decomposição
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Plano de Implementação Recomendado
|
|
403
|
+
|
|
404
|
+
### Fase 1 — Correções Críticas (1-2 dias)
|
|
405
|
+
|
|
406
|
+
| # | Tarefa | Arquivo(s) | Esforço |
|
|
407
|
+
| ------- | ------------------------------------------------------------- | ---------------------- | ------- |
|
|
408
|
+
| ~~1.1~~ | ~~Corrigir `ThemeToggle` para usar `useTheme()`~~ | ✅ Resolvido em v2.2.0 | — |
|
|
409
|
+
| 1.2 | Criar `types.ts` compartilhado para o assistente | `types.ts` (novo) | 1h |
|
|
410
|
+
| 1.3 | Atualizar imports em `AssistenteContext` e `use-assistant.ts` | 2 arquivos | 30min |
|
|
411
|
+
|
|
412
|
+
### Fase 2 — Eliminação de Duplicações (2-3 dias)
|
|
413
|
+
|
|
414
|
+
| # | Tarefa | Arquivo(s) | Esforço |
|
|
415
|
+
| --- | ---------------------------------------------- | -------------- | ------- |
|
|
416
|
+
| 2.1 | Substituir detecção mobile por `useIsMobile()` | 4 arquivos | 2h |
|
|
417
|
+
| 2.2 | Extrair `CustomTooltipContent` compartilhado | 1 arquivo novo | 1h |
|
|
418
|
+
| 2.3 | Mover `hexToRgb` para `utils/color-utils.ts` | 2 arquivos | 30min |
|
|
419
|
+
|
|
420
|
+
### Fase 3 — Extração de Hooks (3-5 dias)
|
|
421
|
+
|
|
422
|
+
| # | Tarefa | Arquivo(s) | Esforço |
|
|
423
|
+
| --- | -------------------------------------------- | ----------------- | ------- |
|
|
424
|
+
| 3.1 | Criar `use-audio-player.ts` | 1 arquivo novo | 4h |
|
|
425
|
+
| 3.2 | Refatorar `AudioPlayer.tsx` para usar o hook | `AudioPlayer.tsx` | 2h |
|
|
426
|
+
| 3.3 | Extrair `use-layout-shortcuts.ts` | 1 arquivo novo | 1h |
|
|
427
|
+
| 3.4 | Extrair `use-persistent-state.ts` | 1 arquivo novo | 1h |
|
|
428
|
+
|
|
429
|
+
### Fase 4 — Decomposição de Componentes Grandes (5-8 dias)
|
|
430
|
+
|
|
431
|
+
| # | Tarefa | Arquivo(s) | Esforço |
|
|
432
|
+
| --- | --------------------------------------------------- | ---------------- | ------- |
|
|
433
|
+
| 4.1 | Decompor `xertica-assistant.tsx` em sub-componentes | 7 arquivos novos | 1 dia |
|
|
434
|
+
| 4.2 | Decompor `sidebar.tsx` em sub-componentes | 5 arquivos novos | 1 dia |
|
|
435
|
+
| 4.3 | Extrair `HeaderUserMenu` e `HeaderBreadcrumbs` | 2 arquivos novos | 3h |
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## Métricas de Melhoria Esperadas
|
|
440
|
+
|
|
441
|
+
| Métrica | Atual | Após Fase 1-2 | Após Fase 3-4 |
|
|
442
|
+
| --------------------------------- | ----------------------- | ------------- | ------------- |
|
|
443
|
+
| Maior arquivo (linhas) | 1573 | 1573 | ~200 |
|
|
444
|
+
| Duplicações de detecção mobile | 4 | 1 | 1 |
|
|
445
|
+
| Definições de tipo duplicadas | 2 conjuntos | 1 conjunto | 1 conjunto |
|
|
446
|
+
| Componentes sem hook headless | 2 (AudioPlayer, Header) | 2 | 0-1 |
|
|
447
|
+
| Risco de dessincronização de tema | Eliminado (v2.2.0) | Eliminado | Eliminado |
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Referências
|
|
452
|
+
|
|
453
|
+
- [Padrão Headless Hook — Kent C. Dodds](https://kentcdodds.com/blog/inversion-of-control)
|
|
454
|
+
- [Compound Components Pattern](https://kentcdodds.com/blog/compound-components-with-react-hooks)
|
|
455
|
+
- [`docs/architecture.md`](./architecture.md) — Arquitetura atual do projeto
|
|
456
|
+
- [`docs/doc-audit.md`](./doc-audit.md) — Auditoria de documentação
|