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,368 +1,368 @@
|
|
|
1
|
-
# Pattern: Settings Page
|
|
2
|
-
|
|
3
|
-
A structured settings page with categorized sections, form fields, and save/cancel actions. Suitable for user preferences, application configuration, and account management.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## When to Use
|
|
8
|
-
|
|
9
|
-
Use the Settings pattern when:
|
|
10
|
-
|
|
11
|
-
- Users need to configure personal preferences (theme, language, notifications)
|
|
12
|
-
- Administrators need to manage application-level configuration
|
|
13
|
-
- Account management (profile, password, API keys) is required
|
|
14
|
-
- Settings are grouped into logical categories (General, Security, Notifications, etc.)
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Components Used
|
|
19
|
-
|
|
20
|
-
- [`PageHeader`](../components/page-header.md) — page title and breadcrumb
|
|
21
|
-
- [`Card`](../components/card.md) — settings section container
|
|
22
|
-
- [`Tabs`](../components/tabs.md) — category navigation (sidebar or top tabs)
|
|
23
|
-
- [`Form`](../components/form.md) + form elements — settings fields
|
|
24
|
-
- [`Switch`](../components/switch.md) — boolean toggles
|
|
25
|
-
- [`Select`](../components/select.md) — option dropdowns
|
|
26
|
-
- [`Input`](../components/input.md) — text settings
|
|
27
|
-
- [`Button`](../components/button.md) — Save and Cancel actions
|
|
28
|
-
- [`Separator`](../components/separator.md) — section dividers
|
|
29
|
-
- [`Sonner`](../components/sonner.md) — success/error toast after save
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Basic Structure
|
|
34
|
-
|
|
35
|
-
```tsx
|
|
36
|
-
import {
|
|
37
|
-
Card,
|
|
38
|
-
CardContent,
|
|
39
|
-
CardDescription,
|
|
40
|
-
CardFooter,
|
|
41
|
-
CardHeader,
|
|
42
|
-
CardTitle,
|
|
43
|
-
Tabs,
|
|
44
|
-
TabsContent,
|
|
45
|
-
TabsList,
|
|
46
|
-
TabsTrigger,
|
|
47
|
-
Button,
|
|
48
|
-
Input,
|
|
49
|
-
Label,
|
|
50
|
-
Switch,
|
|
51
|
-
Select,
|
|
52
|
-
SelectContent,
|
|
53
|
-
SelectItem,
|
|
54
|
-
SelectTrigger,
|
|
55
|
-
SelectValue,
|
|
56
|
-
Separator,
|
|
57
|
-
} from 'xertica-ui/ui';
|
|
58
|
-
import { PageHeader, PageHeaderHeading, PageHeaderDescription } from 'xertica-ui/ui';
|
|
59
|
-
import { toast } from 'sonner';
|
|
60
|
-
|
|
61
|
-
export function SettingsPage() {
|
|
62
|
-
return (
|
|
63
|
-
<div className="flex flex-col h-full overflow-hidden">
|
|
64
|
-
<PageHeader>
|
|
65
|
-
<div>
|
|
66
|
-
<PageHeaderHeading>Configurações</PageHeaderHeading>
|
|
67
|
-
<PageHeaderDescription>
|
|
68
|
-
Gerencie suas preferências e configurações da conta.
|
|
69
|
-
</PageHeaderDescription>
|
|
70
|
-
</div>
|
|
71
|
-
</PageHeader>
|
|
72
|
-
|
|
73
|
-
<div className="flex-1 overflow-y-auto p-6">
|
|
74
|
-
<Tabs defaultValue="general" className="space-y-6">
|
|
75
|
-
<TabsList>
|
|
76
|
-
<TabsTrigger value="general">Geral</TabsTrigger>
|
|
77
|
-
<TabsTrigger value="security">Segurança</TabsTrigger>
|
|
78
|
-
<TabsTrigger value="notifications">Notificações</TabsTrigger>
|
|
79
|
-
<TabsTrigger value="api">API Keys</TabsTrigger>
|
|
80
|
-
</TabsList>
|
|
81
|
-
|
|
82
|
-
<TabsContent value="general">
|
|
83
|
-
<GeneralSettings />
|
|
84
|
-
</TabsContent>
|
|
85
|
-
|
|
86
|
-
<TabsContent value="security">
|
|
87
|
-
<SecuritySettings />
|
|
88
|
-
</TabsContent>
|
|
89
|
-
|
|
90
|
-
<TabsContent value="notifications">
|
|
91
|
-
<NotificationSettings />
|
|
92
|
-
</TabsContent>
|
|
93
|
-
|
|
94
|
-
<TabsContent value="api">
|
|
95
|
-
<ApiKeySettings />
|
|
96
|
-
</TabsContent>
|
|
97
|
-
</Tabs>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## General Settings Section
|
|
107
|
-
|
|
108
|
-
```tsx
|
|
109
|
-
function GeneralSettings() {
|
|
110
|
-
const [name, setName] = useState('João Silva');
|
|
111
|
-
const [language, setLanguage] = useState('pt-BR');
|
|
112
|
-
const [theme, setTheme] = useState('system');
|
|
113
|
-
|
|
114
|
-
const handleSave = () => {
|
|
115
|
-
// persist settings
|
|
116
|
-
toast.success('Configurações salvas com sucesso.');
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
<div className="space-y-6 max-w-2xl">
|
|
121
|
-
{/* Profile */}
|
|
122
|
-
<Card>
|
|
123
|
-
<CardHeader>
|
|
124
|
-
<CardTitle>Perfil</CardTitle>
|
|
125
|
-
<CardDescription>Informações básicas da sua conta.</CardDescription>
|
|
126
|
-
</CardHeader>
|
|
127
|
-
<CardContent className="space-y-4">
|
|
128
|
-
<div className="space-y-2">
|
|
129
|
-
<Label htmlFor="name">Nome completo</Label>
|
|
130
|
-
<Input id="name" value={name} onChange={e => setName(e.target.value)} />
|
|
131
|
-
</div>
|
|
132
|
-
</CardContent>
|
|
133
|
-
</Card>
|
|
134
|
-
|
|
135
|
-
{/* Preferences */}
|
|
136
|
-
<Card>
|
|
137
|
-
<CardHeader>
|
|
138
|
-
<CardTitle>Preferências</CardTitle>
|
|
139
|
-
<CardDescription>Idioma e aparência da interface.</CardDescription>
|
|
140
|
-
</CardHeader>
|
|
141
|
-
<CardContent className="space-y-4">
|
|
142
|
-
<div className="space-y-2">
|
|
143
|
-
<Label>Idioma</Label>
|
|
144
|
-
<Select value={language} onValueChange={setLanguage}>
|
|
145
|
-
<SelectTrigger>
|
|
146
|
-
<SelectValue />
|
|
147
|
-
</SelectTrigger>
|
|
148
|
-
<SelectContent>
|
|
149
|
-
<SelectItem value="pt-BR">Português (Brasil)</SelectItem>
|
|
150
|
-
<SelectItem value="en">English</SelectItem>
|
|
151
|
-
<SelectItem value="es">Español</SelectItem>
|
|
152
|
-
</SelectContent>
|
|
153
|
-
</Select>
|
|
154
|
-
</div>
|
|
155
|
-
|
|
156
|
-
<div className="space-y-2">
|
|
157
|
-
<Label>Tema</Label>
|
|
158
|
-
<Select value={theme} onValueChange={setTheme}>
|
|
159
|
-
<SelectTrigger>
|
|
160
|
-
<SelectValue />
|
|
161
|
-
</SelectTrigger>
|
|
162
|
-
<SelectContent>
|
|
163
|
-
<SelectItem value="light">Claro</SelectItem>
|
|
164
|
-
<SelectItem value="dark">Escuro</SelectItem>
|
|
165
|
-
<SelectItem value="system">Sistema</SelectItem>
|
|
166
|
-
</SelectContent>
|
|
167
|
-
</Select>
|
|
168
|
-
</div>
|
|
169
|
-
</CardContent>
|
|
170
|
-
<CardFooter className="flex justify-end gap-2">
|
|
171
|
-
<Button variant="outline">Cancelar</Button>
|
|
172
|
-
<Button onClick={handleSave}>Salvar alterações</Button>
|
|
173
|
-
</CardFooter>
|
|
174
|
-
</Card>
|
|
175
|
-
</div>
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## Notification Settings Section
|
|
183
|
-
|
|
184
|
-
```tsx
|
|
185
|
-
function NotificationSettings() {
|
|
186
|
-
const [notifications, setNotifications] = useState({
|
|
187
|
-
email: true,
|
|
188
|
-
push: false,
|
|
189
|
-
weeklyReport: true,
|
|
190
|
-
securityAlerts: true,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const toggle = (key: keyof typeof notifications) => {
|
|
194
|
-
setNotifications(prev => ({ ...prev, [key]: !prev[key] }));
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const items = [
|
|
198
|
-
{
|
|
199
|
-
key: 'email',
|
|
200
|
-
label: 'Notificações por e-mail',
|
|
201
|
-
description: 'Receba atualizações no seu e-mail',
|
|
202
|
-
},
|
|
203
|
-
{ key: 'push', label: 'Notificações push', description: 'Notificações no navegador' },
|
|
204
|
-
{
|
|
205
|
-
key: 'weeklyReport',
|
|
206
|
-
label: 'Relatório semanal',
|
|
207
|
-
description: 'Resumo de atividades toda segunda-feira',
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
key: 'securityAlerts',
|
|
211
|
-
label: 'Alertas de segurança',
|
|
212
|
-
description: 'Notificações sobre acessos suspeitos',
|
|
213
|
-
},
|
|
214
|
-
];
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<Card className="max-w-2xl">
|
|
218
|
-
<CardHeader>
|
|
219
|
-
<CardTitle>Notificações</CardTitle>
|
|
220
|
-
<CardDescription>Escolha quais notificações deseja receber.</CardDescription>
|
|
221
|
-
</CardHeader>
|
|
222
|
-
<CardContent className="space-y-0">
|
|
223
|
-
{items.map(({ key, label, description }, index) => (
|
|
224
|
-
<div key={key}>
|
|
225
|
-
<div className="flex items-center justify-between py-4">
|
|
226
|
-
<div className="space-y-0.5">
|
|
227
|
-
<Label className="text-base">{label}</Label>
|
|
228
|
-
<p className="text-sm text-muted-foreground">{description}</p>
|
|
229
|
-
</div>
|
|
230
|
-
<Switch
|
|
231
|
-
checked={notifications[key as keyof typeof notifications]}
|
|
232
|
-
onCheckedChange={() => toggle(key as keyof typeof notifications)}
|
|
233
|
-
/>
|
|
234
|
-
</div>
|
|
235
|
-
{index < items.length - 1 && <Separator />}
|
|
236
|
-
</div>
|
|
237
|
-
))}
|
|
238
|
-
</CardContent>
|
|
239
|
-
<CardFooter className="flex justify-end">
|
|
240
|
-
<Button onClick={() => toast.success('Preferências de notificação salvas.')}>Salvar</Button>
|
|
241
|
-
</CardFooter>
|
|
242
|
-
</Card>
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## Security Settings Section
|
|
250
|
-
|
|
251
|
-
```tsx
|
|
252
|
-
function SecuritySettings() {
|
|
253
|
-
const [currentPassword, setCurrentPassword] = useState('');
|
|
254
|
-
const [newPassword, setNewPassword] = useState('');
|
|
255
|
-
const [confirmPassword, setConfirmPassword] = useState('');
|
|
256
|
-
|
|
257
|
-
const handleChangePassword = () => {
|
|
258
|
-
if (newPassword !== confirmPassword) {
|
|
259
|
-
toast.error('As senhas não coincidem.');
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
// call API
|
|
263
|
-
toast.success('Senha alterada com sucesso.');
|
|
264
|
-
setCurrentPassword('');
|
|
265
|
-
setNewPassword('');
|
|
266
|
-
setConfirmPassword('');
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
return (
|
|
270
|
-
<Card className="max-w-2xl">
|
|
271
|
-
<CardHeader>
|
|
272
|
-
<CardTitle>Alterar Senha</CardTitle>
|
|
273
|
-
<CardDescription>Use uma senha forte com pelo menos 8 caracteres.</CardDescription>
|
|
274
|
-
</CardHeader>
|
|
275
|
-
<CardContent className="space-y-4">
|
|
276
|
-
<div className="space-y-2">
|
|
277
|
-
<Label htmlFor="current-password">Senha atual</Label>
|
|
278
|
-
<Input
|
|
279
|
-
id="current-password"
|
|
280
|
-
type="password"
|
|
281
|
-
value={currentPassword}
|
|
282
|
-
onChange={e => setCurrentPassword(e.target.value)}
|
|
283
|
-
/>
|
|
284
|
-
</div>
|
|
285
|
-
<div className="space-y-2">
|
|
286
|
-
<Label htmlFor="new-password">Nova senha</Label>
|
|
287
|
-
<Input
|
|
288
|
-
id="new-password"
|
|
289
|
-
type="password"
|
|
290
|
-
value={newPassword}
|
|
291
|
-
onChange={e => setNewPassword(e.target.value)}
|
|
292
|
-
/>
|
|
293
|
-
</div>
|
|
294
|
-
<div className="space-y-2">
|
|
295
|
-
<Label htmlFor="confirm-password">Confirmar nova senha</Label>
|
|
296
|
-
<Input
|
|
297
|
-
id="confirm-password"
|
|
298
|
-
type="password"
|
|
299
|
-
value={confirmPassword}
|
|
300
|
-
onChange={e => setConfirmPassword(e.target.value)}
|
|
301
|
-
/>
|
|
302
|
-
</div>
|
|
303
|
-
</CardContent>
|
|
304
|
-
<CardFooter className="flex justify-end">
|
|
305
|
-
<Button onClick={handleChangePassword}>Alterar senha</Button>
|
|
306
|
-
</CardFooter>
|
|
307
|
-
</Card>
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## API Key Settings Section
|
|
315
|
-
|
|
316
|
-
```tsx
|
|
317
|
-
import { useApiKey } from 'xertica-ui/hooks';
|
|
318
|
-
|
|
319
|
-
function ApiKeySettings() {
|
|
320
|
-
const { geminiApiKey, setGeminiApiKey } = useApiKey();
|
|
321
|
-
const [localKey, setLocalKey] = useState(geminiApiKey);
|
|
322
|
-
|
|
323
|
-
const handleSave = () => {
|
|
324
|
-
setGeminiApiKey(localKey);
|
|
325
|
-
toast.success('API key salva com sucesso.');
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
return (
|
|
329
|
-
<Card className="max-w-2xl">
|
|
330
|
-
<CardHeader>
|
|
331
|
-
<CardTitle>Chaves de API</CardTitle>
|
|
332
|
-
<CardDescription>Configure as chaves de API para os serviços integrados.</CardDescription>
|
|
333
|
-
</CardHeader>
|
|
334
|
-
<CardContent className="space-y-4">
|
|
335
|
-
<div className="space-y-2">
|
|
336
|
-
<Label htmlFor="gemini-key">Google Gemini API Key</Label>
|
|
337
|
-
<Input
|
|
338
|
-
id="gemini-key"
|
|
339
|
-
type="password"
|
|
340
|
-
value={localKey}
|
|
341
|
-
onChange={e => setLocalKey(e.target.value)}
|
|
342
|
-
placeholder="AIza..."
|
|
343
|
-
/>
|
|
344
|
-
<p className="text-xs text-muted-foreground">
|
|
345
|
-
Necessária para o assistente de IA em modo real.
|
|
346
|
-
</p>
|
|
347
|
-
</div>
|
|
348
|
-
</CardContent>
|
|
349
|
-
<CardFooter className="flex justify-end">
|
|
350
|
-
<Button onClick={handleSave}>Salvar chave</Button>
|
|
351
|
-
</CardFooter>
|
|
352
|
-
</Card>
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
---
|
|
358
|
-
|
|
359
|
-
## AI Rules
|
|
360
|
-
|
|
361
|
-
> [!IMPORTANT]
|
|
362
|
-
>
|
|
363
|
-
> - **One `Card` per settings group**: Group related settings into a single `Card` with a `CardHeader` (title + description), `CardContent` (fields), and `CardFooter` (Save/Cancel buttons).
|
|
364
|
-
> - **Use `Switch` for boolean settings**: Never use `Checkbox` for on/off toggles in settings. `Switch` is the correct component for this context.
|
|
365
|
-
> - **Always show a toast after save**: Use `toast.success()` from `sonner` to confirm that settings were saved. Use `toast.error()` for validation failures.
|
|
366
|
-
> - **`Tabs` for categories**: When there are more than 2 settings categories, use `Tabs` to organize them. Do not use a custom sidebar navigation.
|
|
367
|
-
> - **Password fields use `type="password"`**: Never render password inputs as plain text. Always use `<Input type="password">`.
|
|
368
|
-
> - **API keys via `useApiKey`**: Use the `useApiKey()` hook from `xertica-ui/hooks` to read and write API keys. Do not store them in component state or `localStorage` directly.
|
|
1
|
+
# Pattern: Settings Page
|
|
2
|
+
|
|
3
|
+
A structured settings page with categorized sections, form fields, and save/cancel actions. Suitable for user preferences, application configuration, and account management.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
Use the Settings pattern when:
|
|
10
|
+
|
|
11
|
+
- Users need to configure personal preferences (theme, language, notifications)
|
|
12
|
+
- Administrators need to manage application-level configuration
|
|
13
|
+
- Account management (profile, password, API keys) is required
|
|
14
|
+
- Settings are grouped into logical categories (General, Security, Notifications, etc.)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Components Used
|
|
19
|
+
|
|
20
|
+
- [`PageHeader`](../components/page-header.md) — page title and breadcrumb
|
|
21
|
+
- [`Card`](../components/card.md) — settings section container
|
|
22
|
+
- [`Tabs`](../components/tabs.md) — category navigation (sidebar or top tabs)
|
|
23
|
+
- [`Form`](../components/form.md) + form elements — settings fields
|
|
24
|
+
- [`Switch`](../components/switch.md) — boolean toggles
|
|
25
|
+
- [`Select`](../components/select.md) — option dropdowns
|
|
26
|
+
- [`Input`](../components/input.md) — text settings
|
|
27
|
+
- [`Button`](../components/button.md) — Save and Cancel actions
|
|
28
|
+
- [`Separator`](../components/separator.md) — section dividers
|
|
29
|
+
- [`Sonner`](../components/sonner.md) — success/error toast after save
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Basic Structure
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import {
|
|
37
|
+
Card,
|
|
38
|
+
CardContent,
|
|
39
|
+
CardDescription,
|
|
40
|
+
CardFooter,
|
|
41
|
+
CardHeader,
|
|
42
|
+
CardTitle,
|
|
43
|
+
Tabs,
|
|
44
|
+
TabsContent,
|
|
45
|
+
TabsList,
|
|
46
|
+
TabsTrigger,
|
|
47
|
+
Button,
|
|
48
|
+
Input,
|
|
49
|
+
Label,
|
|
50
|
+
Switch,
|
|
51
|
+
Select,
|
|
52
|
+
SelectContent,
|
|
53
|
+
SelectItem,
|
|
54
|
+
SelectTrigger,
|
|
55
|
+
SelectValue,
|
|
56
|
+
Separator,
|
|
57
|
+
} from 'xertica-ui/ui';
|
|
58
|
+
import { PageHeader, PageHeaderHeading, PageHeaderDescription } from 'xertica-ui/ui';
|
|
59
|
+
import { toast } from 'sonner';
|
|
60
|
+
|
|
61
|
+
export function SettingsPage() {
|
|
62
|
+
return (
|
|
63
|
+
<div className="flex flex-col h-full overflow-hidden">
|
|
64
|
+
<PageHeader>
|
|
65
|
+
<div>
|
|
66
|
+
<PageHeaderHeading>Configurações</PageHeaderHeading>
|
|
67
|
+
<PageHeaderDescription>
|
|
68
|
+
Gerencie suas preferências e configurações da conta.
|
|
69
|
+
</PageHeaderDescription>
|
|
70
|
+
</div>
|
|
71
|
+
</PageHeader>
|
|
72
|
+
|
|
73
|
+
<div className="flex-1 overflow-y-auto p-6">
|
|
74
|
+
<Tabs defaultValue="general" className="space-y-6">
|
|
75
|
+
<TabsList>
|
|
76
|
+
<TabsTrigger value="general">Geral</TabsTrigger>
|
|
77
|
+
<TabsTrigger value="security">Segurança</TabsTrigger>
|
|
78
|
+
<TabsTrigger value="notifications">Notificações</TabsTrigger>
|
|
79
|
+
<TabsTrigger value="api">API Keys</TabsTrigger>
|
|
80
|
+
</TabsList>
|
|
81
|
+
|
|
82
|
+
<TabsContent value="general">
|
|
83
|
+
<GeneralSettings />
|
|
84
|
+
</TabsContent>
|
|
85
|
+
|
|
86
|
+
<TabsContent value="security">
|
|
87
|
+
<SecuritySettings />
|
|
88
|
+
</TabsContent>
|
|
89
|
+
|
|
90
|
+
<TabsContent value="notifications">
|
|
91
|
+
<NotificationSettings />
|
|
92
|
+
</TabsContent>
|
|
93
|
+
|
|
94
|
+
<TabsContent value="api">
|
|
95
|
+
<ApiKeySettings />
|
|
96
|
+
</TabsContent>
|
|
97
|
+
</Tabs>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## General Settings Section
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
function GeneralSettings() {
|
|
110
|
+
const [name, setName] = useState('João Silva');
|
|
111
|
+
const [language, setLanguage] = useState('pt-BR');
|
|
112
|
+
const [theme, setTheme] = useState('system');
|
|
113
|
+
|
|
114
|
+
const handleSave = () => {
|
|
115
|
+
// persist settings
|
|
116
|
+
toast.success('Configurações salvas com sucesso.');
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div className="space-y-6 max-w-2xl">
|
|
121
|
+
{/* Profile */}
|
|
122
|
+
<Card>
|
|
123
|
+
<CardHeader>
|
|
124
|
+
<CardTitle>Perfil</CardTitle>
|
|
125
|
+
<CardDescription>Informações básicas da sua conta.</CardDescription>
|
|
126
|
+
</CardHeader>
|
|
127
|
+
<CardContent className="space-y-4">
|
|
128
|
+
<div className="space-y-2">
|
|
129
|
+
<Label htmlFor="name">Nome completo</Label>
|
|
130
|
+
<Input id="name" value={name} onChange={e => setName(e.target.value)} />
|
|
131
|
+
</div>
|
|
132
|
+
</CardContent>
|
|
133
|
+
</Card>
|
|
134
|
+
|
|
135
|
+
{/* Preferences */}
|
|
136
|
+
<Card>
|
|
137
|
+
<CardHeader>
|
|
138
|
+
<CardTitle>Preferências</CardTitle>
|
|
139
|
+
<CardDescription>Idioma e aparência da interface.</CardDescription>
|
|
140
|
+
</CardHeader>
|
|
141
|
+
<CardContent className="space-y-4">
|
|
142
|
+
<div className="space-y-2">
|
|
143
|
+
<Label>Idioma</Label>
|
|
144
|
+
<Select value={language} onValueChange={setLanguage}>
|
|
145
|
+
<SelectTrigger>
|
|
146
|
+
<SelectValue />
|
|
147
|
+
</SelectTrigger>
|
|
148
|
+
<SelectContent>
|
|
149
|
+
<SelectItem value="pt-BR">Português (Brasil)</SelectItem>
|
|
150
|
+
<SelectItem value="en">English</SelectItem>
|
|
151
|
+
<SelectItem value="es">Español</SelectItem>
|
|
152
|
+
</SelectContent>
|
|
153
|
+
</Select>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div className="space-y-2">
|
|
157
|
+
<Label>Tema</Label>
|
|
158
|
+
<Select value={theme} onValueChange={setTheme}>
|
|
159
|
+
<SelectTrigger>
|
|
160
|
+
<SelectValue />
|
|
161
|
+
</SelectTrigger>
|
|
162
|
+
<SelectContent>
|
|
163
|
+
<SelectItem value="light">Claro</SelectItem>
|
|
164
|
+
<SelectItem value="dark">Escuro</SelectItem>
|
|
165
|
+
<SelectItem value="system">Sistema</SelectItem>
|
|
166
|
+
</SelectContent>
|
|
167
|
+
</Select>
|
|
168
|
+
</div>
|
|
169
|
+
</CardContent>
|
|
170
|
+
<CardFooter className="flex justify-end gap-2">
|
|
171
|
+
<Button variant="outline">Cancelar</Button>
|
|
172
|
+
<Button onClick={handleSave}>Salvar alterações</Button>
|
|
173
|
+
</CardFooter>
|
|
174
|
+
</Card>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Notification Settings Section
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
function NotificationSettings() {
|
|
186
|
+
const [notifications, setNotifications] = useState({
|
|
187
|
+
email: true,
|
|
188
|
+
push: false,
|
|
189
|
+
weeklyReport: true,
|
|
190
|
+
securityAlerts: true,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const toggle = (key: keyof typeof notifications) => {
|
|
194
|
+
setNotifications(prev => ({ ...prev, [key]: !prev[key] }));
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const items = [
|
|
198
|
+
{
|
|
199
|
+
key: 'email',
|
|
200
|
+
label: 'Notificações por e-mail',
|
|
201
|
+
description: 'Receba atualizações no seu e-mail',
|
|
202
|
+
},
|
|
203
|
+
{ key: 'push', label: 'Notificações push', description: 'Notificações no navegador' },
|
|
204
|
+
{
|
|
205
|
+
key: 'weeklyReport',
|
|
206
|
+
label: 'Relatório semanal',
|
|
207
|
+
description: 'Resumo de atividades toda segunda-feira',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
key: 'securityAlerts',
|
|
211
|
+
label: 'Alertas de segurança',
|
|
212
|
+
description: 'Notificações sobre acessos suspeitos',
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<Card className="max-w-2xl">
|
|
218
|
+
<CardHeader>
|
|
219
|
+
<CardTitle>Notificações</CardTitle>
|
|
220
|
+
<CardDescription>Escolha quais notificações deseja receber.</CardDescription>
|
|
221
|
+
</CardHeader>
|
|
222
|
+
<CardContent className="space-y-0">
|
|
223
|
+
{items.map(({ key, label, description }, index) => (
|
|
224
|
+
<div key={key}>
|
|
225
|
+
<div className="flex items-center justify-between py-4">
|
|
226
|
+
<div className="space-y-0.5">
|
|
227
|
+
<Label className="text-base">{label}</Label>
|
|
228
|
+
<p className="text-sm text-muted-foreground">{description}</p>
|
|
229
|
+
</div>
|
|
230
|
+
<Switch
|
|
231
|
+
checked={notifications[key as keyof typeof notifications]}
|
|
232
|
+
onCheckedChange={() => toggle(key as keyof typeof notifications)}
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
{index < items.length - 1 && <Separator />}
|
|
236
|
+
</div>
|
|
237
|
+
))}
|
|
238
|
+
</CardContent>
|
|
239
|
+
<CardFooter className="flex justify-end">
|
|
240
|
+
<Button onClick={() => toast.success('Preferências de notificação salvas.')}>Salvar</Button>
|
|
241
|
+
</CardFooter>
|
|
242
|
+
</Card>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Security Settings Section
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
function SecuritySettings() {
|
|
253
|
+
const [currentPassword, setCurrentPassword] = useState('');
|
|
254
|
+
const [newPassword, setNewPassword] = useState('');
|
|
255
|
+
const [confirmPassword, setConfirmPassword] = useState('');
|
|
256
|
+
|
|
257
|
+
const handleChangePassword = () => {
|
|
258
|
+
if (newPassword !== confirmPassword) {
|
|
259
|
+
toast.error('As senhas não coincidem.');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// call API
|
|
263
|
+
toast.success('Senha alterada com sucesso.');
|
|
264
|
+
setCurrentPassword('');
|
|
265
|
+
setNewPassword('');
|
|
266
|
+
setConfirmPassword('');
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<Card className="max-w-2xl">
|
|
271
|
+
<CardHeader>
|
|
272
|
+
<CardTitle>Alterar Senha</CardTitle>
|
|
273
|
+
<CardDescription>Use uma senha forte com pelo menos 8 caracteres.</CardDescription>
|
|
274
|
+
</CardHeader>
|
|
275
|
+
<CardContent className="space-y-4">
|
|
276
|
+
<div className="space-y-2">
|
|
277
|
+
<Label htmlFor="current-password">Senha atual</Label>
|
|
278
|
+
<Input
|
|
279
|
+
id="current-password"
|
|
280
|
+
type="password"
|
|
281
|
+
value={currentPassword}
|
|
282
|
+
onChange={e => setCurrentPassword(e.target.value)}
|
|
283
|
+
/>
|
|
284
|
+
</div>
|
|
285
|
+
<div className="space-y-2">
|
|
286
|
+
<Label htmlFor="new-password">Nova senha</Label>
|
|
287
|
+
<Input
|
|
288
|
+
id="new-password"
|
|
289
|
+
type="password"
|
|
290
|
+
value={newPassword}
|
|
291
|
+
onChange={e => setNewPassword(e.target.value)}
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
<div className="space-y-2">
|
|
295
|
+
<Label htmlFor="confirm-password">Confirmar nova senha</Label>
|
|
296
|
+
<Input
|
|
297
|
+
id="confirm-password"
|
|
298
|
+
type="password"
|
|
299
|
+
value={confirmPassword}
|
|
300
|
+
onChange={e => setConfirmPassword(e.target.value)}
|
|
301
|
+
/>
|
|
302
|
+
</div>
|
|
303
|
+
</CardContent>
|
|
304
|
+
<CardFooter className="flex justify-end">
|
|
305
|
+
<Button onClick={handleChangePassword}>Alterar senha</Button>
|
|
306
|
+
</CardFooter>
|
|
307
|
+
</Card>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## API Key Settings Section
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { useApiKey } from 'xertica-ui/hooks';
|
|
318
|
+
|
|
319
|
+
function ApiKeySettings() {
|
|
320
|
+
const { geminiApiKey, setGeminiApiKey } = useApiKey();
|
|
321
|
+
const [localKey, setLocalKey] = useState(geminiApiKey);
|
|
322
|
+
|
|
323
|
+
const handleSave = () => {
|
|
324
|
+
setGeminiApiKey(localKey);
|
|
325
|
+
toast.success('API key salva com sucesso.');
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<Card className="max-w-2xl">
|
|
330
|
+
<CardHeader>
|
|
331
|
+
<CardTitle>Chaves de API</CardTitle>
|
|
332
|
+
<CardDescription>Configure as chaves de API para os serviços integrados.</CardDescription>
|
|
333
|
+
</CardHeader>
|
|
334
|
+
<CardContent className="space-y-4">
|
|
335
|
+
<div className="space-y-2">
|
|
336
|
+
<Label htmlFor="gemini-key">Google Gemini API Key</Label>
|
|
337
|
+
<Input
|
|
338
|
+
id="gemini-key"
|
|
339
|
+
type="password"
|
|
340
|
+
value={localKey}
|
|
341
|
+
onChange={e => setLocalKey(e.target.value)}
|
|
342
|
+
placeholder="AIza..."
|
|
343
|
+
/>
|
|
344
|
+
<p className="text-xs text-muted-foreground">
|
|
345
|
+
Necessária para o assistente de IA em modo real.
|
|
346
|
+
</p>
|
|
347
|
+
</div>
|
|
348
|
+
</CardContent>
|
|
349
|
+
<CardFooter className="flex justify-end">
|
|
350
|
+
<Button onClick={handleSave}>Salvar chave</Button>
|
|
351
|
+
</CardFooter>
|
|
352
|
+
</Card>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## AI Rules
|
|
360
|
+
|
|
361
|
+
> [!IMPORTANT]
|
|
362
|
+
>
|
|
363
|
+
> - **One `Card` per settings group**: Group related settings into a single `Card` with a `CardHeader` (title + description), `CardContent` (fields), and `CardFooter` (Save/Cancel buttons).
|
|
364
|
+
> - **Use `Switch` for boolean settings**: Never use `Checkbox` for on/off toggles in settings. `Switch` is the correct component for this context.
|
|
365
|
+
> - **Always show a toast after save**: Use `toast.success()` from `sonner` to confirm that settings were saved. Use `toast.error()` for validation failures.
|
|
366
|
+
> - **`Tabs` for categories**: When there are more than 2 settings categories, use `Tabs` to organize them. Do not use a custom sidebar navigation.
|
|
367
|
+
> - **Password fields use `type="password"`**: Never render password inputs as plain text. Always use `<Input type="password">`.
|
|
368
|
+
> - **API keys via `useApiKey`**: Use the `useApiKey()` hook from `xertica-ui/hooks` to read and write API keys. Do not store them in component state or `localStorage` directly.
|