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,553 +1,577 @@
|
|
|
1
|
-
# Project Guidelines — Xertica UI App
|
|
2
|
-
|
|
3
|
-
> **Scope**: These guidelines apply to React applications scaffolded with `npx xertica-ui@latest init`. They define the FSD/FDA architecture, import conventions, component rules, and AI agent behavior for this project.
|
|
4
|
-
>
|
|
5
|
-
> **AI agents**: Read this file first. Then read `node_modules/xertica-ui/llms-compact.txt` for component reference, and `node_modules/xertica-ui/docs/decision-tree.md` before choosing between similar components.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Architecture — Feature-Sliced Design + Feature-Driven Architecture
|
|
10
|
-
|
|
11
|
-
This project follows **FSD (Feature-Sliced Design)** layered architecture combined with **FDA (Feature-Driven Architecture)** vertical slicing. Layers can only import from layers below them.
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
src/
|
|
15
|
-
├── app/ # Layer 1 — Application shell (imports from all layers)
|
|
16
|
-
│ ├── App.tsx # XerticaProvider + BrowserRouter + AuthGuard (GENERATED — do not hand-edit)
|
|
17
|
-
│ ├── context/
|
|
18
|
-
│ │ └── AuthContext.tsx # AuthProvider + useAuth() hook
|
|
19
|
-
│ └── components/
|
|
20
|
-
│ ├── AppLayout.tsx # Sidebar + children + optional XerticaAssistant
|
|
21
|
-
│ └── AuthGuard.tsx # Route definitions
|
|
22
|
-
│
|
|
23
|
-
├── shared/ # Layer 2 — Shared utilities (no business domain)
|
|
24
|
-
│ ├── config/
|
|
25
|
-
│ │ └── navigation.ts # RouteConfig type, routes[], getRouteByPath()
|
|
26
|
-
│ ├── lib/
|
|
27
|
-
│ │ └── auth.ts # getStoredUser, storeUser, clearStoredUser
|
|
28
|
-
│ └── types/
|
|
29
|
-
│ └── auth.ts # User interface
|
|
30
|
-
│
|
|
31
|
-
├── features/ # Layer 3 — Vertical slices by business capability
|
|
32
|
-
│ ├── auth/
|
|
33
|
-
│ │ ├── index.ts # Public barrel — import from here, never from /ui/*
|
|
34
|
-
│ │ └── ui/
|
|
35
|
-
│ │ ├── AuthPageShell.tsx # Split-screen layout for auth pages
|
|
36
|
-
│ │ ├── SocialLoginButtons.tsx # Google / MT Login / gov.br buttons
|
|
37
|
-
│ │ ├── LoginContent.tsx
|
|
38
|
-
│ │ ├── ForgotPasswordContent.tsx
|
|
39
|
-
│ │ ├── VerifyEmailContent.tsx
|
|
40
|
-
│ │ └── ResetPasswordContent.tsx
|
|
41
|
-
│ ├── home/
|
|
42
|
-
│ │ ├── data/mock.ts # Types + async fetch + factory functions (getMockXxx)
|
|
43
|
-
│ │ ├── hooks/useFeatureCards.ts # React Query, queryKey includes `language`
|
|
44
|
-
│ │ ├── store/dashboardStore.ts # Zustand UI state (no server data)
|
|
45
|
-
│ │ ├── ui/HomeContent.tsx
|
|
46
|
-
│ │ └── index.ts
|
|
47
|
-
│ ├── template/
|
|
48
|
-
│ │ ├── ui/TemplateContent.tsx
|
|
49
|
-
│ │ └── index.ts
|
|
50
|
-
│ └── assistant/ # Always present — AppLayout depends on it
|
|
51
|
-
│ ├── data/mock.ts # AssistantConfig + getMockRichSuggestions() / getMockFeedbackOptions()
|
|
52
|
-
│ ├── hooks/useAssistantConfig.ts
|
|
53
|
-
│ └── index.ts
|
|
54
|
-
│
|
|
55
|
-
├── pages/ # Layer 4 — Route entrypoints (thin wrappers only)
|
|
56
|
-
│ ├── LoginPage.tsx
|
|
57
|
-
│ ├── ForgotPasswordPage.tsx
|
|
58
|
-
│ ├── VerifyEmailPage.tsx
|
|
59
|
-
│ ├── ResetPasswordPage.tsx
|
|
60
|
-
│ ├── HomePage.tsx # AppLayout + HomeContent + XerticaAssistant
|
|
61
|
-
│ ├── TemplatePage.tsx # AppLayout + TemplateContent + XerticaAssistant
|
|
62
|
-
│ └── AssistantPage.tsx
|
|
63
|
-
│
|
|
64
|
-
├── i18n.ts # i18next setup (GENERATED — covers only your selected languages)
|
|
65
|
-
├── locales/
|
|
66
|
-
│ ├── .languages.json # CLI-managed selection ({ "version": 1, "codes": [...] })
|
|
67
|
-
│ ├── pt-BR/ # one folder per language — only your selected languages exist
|
|
68
|
-
│ │ ├── common.json
|
|
69
|
-
│ │ ├── nav.json
|
|
70
|
-
│ │ ├── errors.json
|
|
71
|
-
│ │ ├── languageSelector.json
|
|
72
|
-
│ │ ├── themeToggle.json
|
|
73
|
-
│ │ ├── pages/
|
|
74
|
-
│ │ │ └── home.json, login.json, templates.json, resetPassword.json, verifyEmail.json,
|
|
75
|
-
│ │ │ loginTemplate.json, formTemplate.json, dashboardTemplate.json, crudTemplate.json
|
|
76
|
-
│ │ └── components/
|
|
77
|
-
│ │ └── assistant.json, sidebar.json, media.json, projectCard.json, ...
|
|
78
|
-
│ ├── en/ # same structure
|
|
79
|
-
│ └── es/ # same structure
|
|
80
|
-
└── styles/ # Visual tokens and Tailwind entry
|
|
81
|
-
├── index.css # Imports: xertica-ui/style.css, tokens.css, @source
|
|
82
|
-
└── xertica/
|
|
83
|
-
└── tokens.css # Brand CSS variables — edit here to customize theme
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
> **Files generated by the CLI** (`App.tsx`, `i18n.ts`, `locales/.languages.json`) are rewritten by `npx xertica-ui update`. Avoid hand-editing them; use the CLI to add/remove languages and let it regenerate them.
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## 2. Layer Responsibilities
|
|
91
|
-
|
|
92
|
-
### `app/`
|
|
93
|
-
|
|
94
|
-
Initializes the application. Contains only providers, the router, `AuthGuard` (auth state + routes), and `AppLayout` (layout shell). No business logic here.
|
|
95
|
-
|
|
96
|
-
### `shared/`
|
|
97
|
-
|
|
98
|
-
Framework-agnostic code with no business domain: primitive types, localStorage helpers, navigation config. Importable by any layer. Must not import from `features/` or `pages/`.
|
|
99
|
-
|
|
100
|
-
### `features/`
|
|
101
|
-
|
|
102
|
-
Each feature is a self-contained vertical slice. The `index.ts` is the only public interface — always import from the barrel, never from internal paths:
|
|
103
|
-
|
|
104
|
-
```tsx
|
|
105
|
-
// ✅ Correct
|
|
106
|
-
import { LoginContent } from '../features/auth';
|
|
107
|
-
|
|
108
|
-
// ❌ Wrong — never import from internal feature paths
|
|
109
|
-
import { LoginContent } from '../features/auth/ui/LoginContent';
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Features must not import from each other. If two features share code, extract it to `shared/`.
|
|
113
|
-
|
|
114
|
-
### `pages/`
|
|
115
|
-
|
|
116
|
-
Thin route wrappers that compose `AppLayout` + feature content + optional `XerticaAssistant`. No logic, no state, no API calls here:
|
|
117
|
-
|
|
118
|
-
```tsx
|
|
119
|
-
// ✅ Correct — thin wrapper
|
|
120
|
-
export function HomePage({ user, onLogout }: PageProps) {
|
|
121
|
-
const { assistenteExpanded, toggleAssistente } = useLayout();
|
|
122
|
-
return (
|
|
123
|
-
<AppLayout user={user} onLogout={onLogout} assistant={<XerticaAssistant ... />}>
|
|
124
|
-
<HomeContent user={user} onLogout={onLogout} />
|
|
125
|
-
</AppLayout>
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## 3. Import Rules
|
|
133
|
-
|
|
134
|
-
Always use the correct `xertica-ui` subpath for the layer you are in:
|
|
135
|
-
|
|
136
|
-
```tsx
|
|
137
|
-
// shared/ui → UI primitives
|
|
138
|
-
import { Button, Card, Input, Badge, Table, Dialog, Select } from 'xertica-ui/ui';
|
|
139
|
-
|
|
140
|
-
// app layer → providers and brand
|
|
141
|
-
import { XerticaProvider, XerticaLogo, ThemeToggle } from 'xertica-ui/brand';
|
|
142
|
-
|
|
143
|
-
// features/layout → navigation shell
|
|
144
|
-
import { Sidebar, Header } from 'xertica-ui/layout';
|
|
145
|
-
|
|
146
|
-
// features/assistant → AI assistant
|
|
147
|
-
import { XerticaAssistant, generateDemoResponse } from 'xertica-ui/assistant';
|
|
148
|
-
|
|
149
|
-
// features/media → media players
|
|
150
|
-
import { VideoPlayer, AudioPlayer } from 'xertica-ui/media';
|
|
151
|
-
|
|
152
|
-
// shared/lib → hooks and contexts
|
|
153
|
-
import { useLayout, useOptionalLayout, useTheme } from 'xertica-ui/hooks';
|
|
154
|
-
|
|
155
|
-
// styles → imported once by src/styles/index.css
|
|
156
|
-
import 'xertica-ui/style.css';
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
Icons always come from `lucide-react` — never from `xertica-ui`:
|
|
160
|
-
|
|
161
|
-
```tsx
|
|
162
|
-
import { Home, Settings, Plus, Trash2, ChevronRight } from 'lucide-react';
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
---
|
|
166
|
-
|
|
167
|
-
## 4. Adding a New Route (Step-by-Step)
|
|
168
|
-
|
|
169
|
-
1. **Create the feature content** — `src/features/<name>/ui/<NameContent>.tsx`
|
|
170
|
-
2. **Export from the barrel** — add to `src/features/<name>/index.ts`
|
|
171
|
-
3. **Create the page** — `src/pages/<NamePage>.tsx` (thin AppLayout wrapper)
|
|
172
|
-
4. **Register the route** — add to `routes` array in `src/shared/config/navigation.ts`:
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
import { MyIcon } from 'lucide-react';
|
|
176
|
-
|
|
177
|
-
export const routes: RouteConfig[] = [
|
|
178
|
-
{ path: '/home', label: 'Home', icon: Home },
|
|
179
|
-
{ path: '/my-feature', label: 'My Feature', icon: MyIcon }, // ← add here
|
|
180
|
-
];
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
5. **Register the `<Route>`** — add to `src/app/components/AuthGuard.tsx`:
|
|
184
|
-
|
|
185
|
-
```tsx
|
|
186
|
-
import { MyFeaturePage } from '../../pages/MyFeaturePage';
|
|
187
|
-
|
|
188
|
-
// inside <Routes>:
|
|
189
|
-
<Route
|
|
190
|
-
path="/my-feature"
|
|
191
|
-
element={
|
|
192
|
-
<ProtectedRoute user={user}>
|
|
193
|
-
<MyFeaturePage user={user} onLogout={handleLogout} />
|
|
194
|
-
</ProtectedRoute>
|
|
195
|
-
}
|
|
196
|
-
/>;
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
6. **Add a navigation card** to `HomeContent.tsx` if it's a primary module.
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## 5. Non-Negotiable Rules
|
|
204
|
-
|
|
205
|
-
### HTML — never use raw elements for UI
|
|
206
|
-
|
|
207
|
-
| ❌ Never | ✅ Always |
|
|
208
|
-
| -------------------------------- | ------------------------------------------------------ |
|
|
209
|
-
| `<button>` | `<Button>` from `xertica-ui/ui` |
|
|
210
|
-
| `<input>` | `<Input>` from `xertica-ui/ui` |
|
|
211
|
-
| `<select>` | `<Select>` from `xertica-ui/ui` |
|
|
212
|
-
| `<h1>` / `<h2>` for page headers | `<PageHeader><PageHeaderHeading>` from `xertica-ui/ui` |
|
|
213
|
-
| `<div class="card ...">` | `<Card>` from `xertica-ui/ui` |
|
|
214
|
-
| custom scrollable div | `<ScrollArea>` from `xertica-ui/ui` |
|
|
215
|
-
|
|
216
|
-
### Colors — context-dependent rules
|
|
217
|
-
|
|
218
|
-
| ❌ Never (any context) | Reason |
|
|
219
|
-
| ------------------------------------------ | -------------------------- |
|
|
220
|
-
| `#3b82f6`, `rgb(59, 130, 246)`, `hsl(...)` | Raw values — non-themeable |
|
|
221
|
-
| `style={{ color: '#...' }}` for theming | Non-themeable inline style |
|
|
222
|
-
|
|
223
|
-
| ❌ Wrong for semantic/status contexts | ✅ Required instead |
|
|
224
|
-
| ---------------------------------------- | ------------------------------------- |
|
|
225
|
-
| `bg-red-500` / `text-red-500` for errors | `bg-destructive` / `text-destructive` |
|
|
226
|
-
| `bg-green-500` for success states | `bg-success` |
|
|
227
|
-
| `bg-yellow-500` for warnings | `bg-warning` |
|
|
228
|
-
|
|
229
|
-
> For **layout, spacing, and general non-semantic UI** (custom components, decorative elements where no semantic token applies), standard Tailwind color utilities like `bg-blue-500` or `text-gray-700` are acceptable.
|
|
230
|
-
|
|
231
|
-
| ❌ Never | ✅ Always |
|
|
232
|
-
| -------------------------- | ------------------------- |
|
|
233
|
-
| `rounded-lg`, `rounded-xl` | `rounded-[var(--radius)]` |
|
|
234
|
-
|
|
235
|
-
### Layout state — never hardcode
|
|
236
|
-
|
|
237
|
-
```tsx
|
|
238
|
-
// ✅ Correct
|
|
239
|
-
const { sidebarExpanded, sidebarWidth } = useLayout();
|
|
240
|
-
<div style={{ paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px' }}>
|
|
241
|
-
|
|
242
|
-
// ✅ Correct for reusable components that may render outside XerticaProvider
|
|
243
|
-
const layout = useOptionalLayout();
|
|
244
|
-
const fallbackSidebarWidth = layout?.sidebarWidth ?? 80;
|
|
245
|
-
|
|
246
|
-
// ❌ Wrong
|
|
247
|
-
<div style={{ paddingLeft: '256px' }}>
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Destructive actions — always confirm
|
|
251
|
-
|
|
252
|
-
```tsx
|
|
253
|
-
// ✅ Always wrap in AlertDialog
|
|
254
|
-
<AlertDialog>
|
|
255
|
-
<AlertDialogTrigger asChild>
|
|
256
|
-
<Button variant="destructive">Delete</Button>
|
|
257
|
-
</AlertDialogTrigger>
|
|
258
|
-
<AlertDialogContent>
|
|
259
|
-
<AlertDialogHeader>
|
|
260
|
-
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
261
|
-
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
262
|
-
</AlertDialogHeader>
|
|
263
|
-
<AlertDialogFooter>
|
|
264
|
-
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
265
|
-
<AlertDialogAction onClick={handleDelete}>Delete</AlertDialogAction>
|
|
266
|
-
</AlertDialogFooter>
|
|
267
|
-
</AlertDialogContent>
|
|
268
|
-
</AlertDialog>
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### Page structure — always use PageHeader
|
|
272
|
-
|
|
273
|
-
Every page must use `<PageHeader>` for its title and primary actions. Never use raw `<h1>` or `<h2>`:
|
|
274
|
-
|
|
275
|
-
```tsx
|
|
276
|
-
<PageHeader>
|
|
277
|
-
<PageHeaderHeading>Users</PageHeaderHeading>
|
|
278
|
-
<PageHeaderDescription>Manage your team members.</PageHeaderDescription>
|
|
279
|
-
<Button>
|
|
280
|
-
<Plus className="size-4 mr-2" />
|
|
281
|
-
New User
|
|
282
|
-
</Button>
|
|
283
|
-
</PageHeader>
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Toast notifications
|
|
287
|
-
|
|
288
|
-
```tsx
|
|
289
|
-
import { toast } from 'sonner';
|
|
290
|
-
import { useTranslation } from 'react-i18next';
|
|
291
|
-
|
|
292
|
-
const { t } = useTranslation();
|
|
293
|
-
toast.success(t('users.createSuccess'));
|
|
294
|
-
toast.error(t('errors.somethingWentWrong'));
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Never render `<Toaster>` manually — it is auto-injected by `<XerticaProvider>` in `App.tsx`. Always translate the message text through `t()`.
|
|
298
|
-
|
|
299
|
-
### Translations — never hardcode strings
|
|
300
|
-
|
|
301
|
-
Every user-facing string must come from `useTranslation()`. Add new keys to the appropriate split JSON file under `src/locales/<lang>/` for all configured languages:
|
|
302
|
-
|
|
303
|
-
```tsx
|
|
304
|
-
// ❌ Wrong
|
|
305
|
-
<Button aria-label="Save">Save</Button
|
|
306
|
-
|
|
307
|
-
// ✅ Correct
|
|
308
|
-
const { t } = useTranslation();
|
|
309
|
-
<Button aria-label={t('common.save')}>{t('common.save')}</Button
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
This applies to `aria-label`, `placeholder`, `title`, toast messages, error text, dropdown items — everything the user can read.
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
## 6. Internationalization
|
|
317
|
-
|
|
318
|
-
### Selecting languages on init
|
|
319
|
-
|
|
320
|
-
The CLI's `init` command asks which languages your app should support (`pt-BR`, `en`, `es`). Pick **1, 2, or 3**. The CLI:
|
|
321
|
-
|
|
322
|
-
- Copies only the locale **folders** for the languages you chose (each is a directory tree with split JSON files)
|
|
323
|
-
- Generates `src/i18n.ts` with `import.meta.glob` calls for exactly those languages — Vite auto-discovers all JSON files in the folder at build time
|
|
324
|
-
- Injects the `availableLanguages` prop into `src/app/App.tsx` (omitted when all 3 defaults are selected)
|
|
325
|
-
- Persists the selection in `src/locales/.languages.json`
|
|
326
|
-
|
|
327
|
-
### Monolingual mode
|
|
328
|
-
|
|
329
|
-
When you select only one language, the `<LanguageSelector>` automatically renders `null` (there is nothing to switch to). A header comment in the generated `App.tsx` documents this. To force the selector visible anyway:
|
|
330
|
-
|
|
331
|
-
```tsx
|
|
332
|
-
<LanguageSelector showWhenMonolingual />
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
### Adding or removing languages later
|
|
336
|
-
|
|
337
|
-
```bash
|
|
338
|
-
npx xertica-ui update
|
|
339
|
-
# → select "Languages"
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
The CLI shows your current selection, prompts for the new set, displays the diff (`+ es`, `- en`), and on confirm:
|
|
343
|
-
|
|
344
|
-
-
|
|
345
|
-
-
|
|
346
|
-
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
###
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
```
|
|
427
|
-
//
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
{
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
1
|
+
# Project Guidelines — Xertica UI App
|
|
2
|
+
|
|
3
|
+
> **Scope**: These guidelines apply to React applications scaffolded with `npx xertica-ui@latest init`. They define the FSD/FDA architecture, import conventions, component rules, and AI agent behavior for this project.
|
|
4
|
+
>
|
|
5
|
+
> **AI agents**: Read this file first. Then read `node_modules/xertica-ui/llms-compact.txt` for component reference, and `node_modules/xertica-ui/docs/decision-tree.md` before choosing between similar components.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Architecture — Feature-Sliced Design + Feature-Driven Architecture
|
|
10
|
+
|
|
11
|
+
This project follows **FSD (Feature-Sliced Design)** layered architecture combined with **FDA (Feature-Driven Architecture)** vertical slicing. Layers can only import from layers below them.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
src/
|
|
15
|
+
├── app/ # Layer 1 — Application shell (imports from all layers)
|
|
16
|
+
│ ├── App.tsx # XerticaProvider + BrowserRouter + AuthGuard (GENERATED — do not hand-edit)
|
|
17
|
+
│ ├── context/
|
|
18
|
+
│ │ └── AuthContext.tsx # AuthProvider + useAuth() hook
|
|
19
|
+
│ └── components/
|
|
20
|
+
│ ├── AppLayout.tsx # Sidebar + children + optional XerticaAssistant
|
|
21
|
+
│ └── AuthGuard.tsx # Route definitions
|
|
22
|
+
│
|
|
23
|
+
├── shared/ # Layer 2 — Shared utilities (no business domain)
|
|
24
|
+
│ ├── config/
|
|
25
|
+
│ │ └── navigation.ts # RouteConfig type, routes[], getRouteByPath()
|
|
26
|
+
│ ├── lib/
|
|
27
|
+
│ │ └── auth.ts # getStoredUser, storeUser, clearStoredUser
|
|
28
|
+
│ └── types/
|
|
29
|
+
│ └── auth.ts # User interface
|
|
30
|
+
│
|
|
31
|
+
├── features/ # Layer 3 — Vertical slices by business capability
|
|
32
|
+
│ ├── auth/
|
|
33
|
+
│ │ ├── index.ts # Public barrel — import from here, never from /ui/*
|
|
34
|
+
│ │ └── ui/
|
|
35
|
+
│ │ ├── AuthPageShell.tsx # Split-screen layout for auth pages
|
|
36
|
+
│ │ ├── SocialLoginButtons.tsx # Google / MT Login / gov.br buttons
|
|
37
|
+
│ │ ├── LoginContent.tsx
|
|
38
|
+
│ │ ├── ForgotPasswordContent.tsx
|
|
39
|
+
│ │ ├── VerifyEmailContent.tsx
|
|
40
|
+
│ │ └── ResetPasswordContent.tsx
|
|
41
|
+
│ ├── home/
|
|
42
|
+
│ │ ├── data/mock.ts # Types + async fetch + factory functions (getMockXxx)
|
|
43
|
+
│ │ ├── hooks/useFeatureCards.ts # React Query, queryKey includes `language`
|
|
44
|
+
│ │ ├── store/dashboardStore.ts # Zustand UI state (no server data)
|
|
45
|
+
│ │ ├── ui/HomeContent.tsx
|
|
46
|
+
│ │ └── index.ts
|
|
47
|
+
│ ├── template/
|
|
48
|
+
│ │ ├── ui/TemplateContent.tsx
|
|
49
|
+
│ │ └── index.ts
|
|
50
|
+
│ └── assistant/ # Always present — AppLayout depends on it
|
|
51
|
+
│ ├── data/mock.ts # AssistantConfig + getMockRichSuggestions() / getMockFeedbackOptions()
|
|
52
|
+
│ ├── hooks/useAssistantConfig.ts
|
|
53
|
+
│ └── index.ts
|
|
54
|
+
│
|
|
55
|
+
├── pages/ # Layer 4 — Route entrypoints (thin wrappers only)
|
|
56
|
+
│ ├── LoginPage.tsx
|
|
57
|
+
│ ├── ForgotPasswordPage.tsx
|
|
58
|
+
│ ├── VerifyEmailPage.tsx
|
|
59
|
+
│ ├── ResetPasswordPage.tsx
|
|
60
|
+
│ ├── HomePage.tsx # AppLayout + HomeContent + XerticaAssistant
|
|
61
|
+
│ ├── TemplatePage.tsx # AppLayout + TemplateContent + XerticaAssistant
|
|
62
|
+
│ └── AssistantPage.tsx
|
|
63
|
+
│
|
|
64
|
+
├── i18n.ts # i18next setup (GENERATED — covers only your selected languages)
|
|
65
|
+
├── locales/
|
|
66
|
+
│ ├── .languages.json # CLI-managed selection ({ "version": 1, "codes": [...] })
|
|
67
|
+
│ ├── pt-BR/ # one folder per language — only your selected languages exist
|
|
68
|
+
│ │ ├── common.json
|
|
69
|
+
│ │ ├── nav.json
|
|
70
|
+
│ │ ├── errors.json
|
|
71
|
+
│ │ ├── languageSelector.json
|
|
72
|
+
│ │ ├── themeToggle.json
|
|
73
|
+
│ │ ├── pages/
|
|
74
|
+
│ │ │ └── home.json, login.json, templates.json, resetPassword.json, verifyEmail.json,
|
|
75
|
+
│ │ │ loginTemplate.json, formTemplate.json, dashboardTemplate.json, crudTemplate.json
|
|
76
|
+
│ │ └── components/
|
|
77
|
+
│ │ └── assistant.json, sidebar.json, media.json, projectCard.json, ...
|
|
78
|
+
│ ├── en/ # same structure
|
|
79
|
+
│ └── es/ # same structure
|
|
80
|
+
└── styles/ # Visual tokens and Tailwind entry
|
|
81
|
+
├── index.css # Imports: xertica-ui/style.css, tokens.css, @source
|
|
82
|
+
└── xertica/
|
|
83
|
+
└── tokens.css # Brand CSS variables — edit here to customize theme
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> **Files generated by the CLI** (`App.tsx`, `i18n.ts`, `locales/.languages.json`) are rewritten by `npx xertica-ui update`. Avoid hand-editing them; use the CLI to add/remove languages and let it regenerate them.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 2. Layer Responsibilities
|
|
91
|
+
|
|
92
|
+
### `app/`
|
|
93
|
+
|
|
94
|
+
Initializes the application. Contains only providers, the router, `AuthGuard` (auth state + routes), and `AppLayout` (layout shell). No business logic here.
|
|
95
|
+
|
|
96
|
+
### `shared/`
|
|
97
|
+
|
|
98
|
+
Framework-agnostic code with no business domain: primitive types, localStorage helpers, navigation config. Importable by any layer. Must not import from `features/` or `pages/`.
|
|
99
|
+
|
|
100
|
+
### `features/`
|
|
101
|
+
|
|
102
|
+
Each feature is a self-contained vertical slice. The `index.ts` is the only public interface — always import from the barrel, never from internal paths:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// ✅ Correct
|
|
106
|
+
import { LoginContent } from '../features/auth';
|
|
107
|
+
|
|
108
|
+
// ❌ Wrong — never import from internal feature paths
|
|
109
|
+
import { LoginContent } from '../features/auth/ui/LoginContent';
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Features must not import from each other. If two features share code, extract it to `shared/`.
|
|
113
|
+
|
|
114
|
+
### `pages/`
|
|
115
|
+
|
|
116
|
+
Thin route wrappers that compose `AppLayout` + feature content + optional `XerticaAssistant`. No logic, no state, no API calls here:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// ✅ Correct — thin wrapper
|
|
120
|
+
export function HomePage({ user, onLogout }: PageProps) {
|
|
121
|
+
const { assistenteExpanded, toggleAssistente } = useLayout();
|
|
122
|
+
return (
|
|
123
|
+
<AppLayout user={user} onLogout={onLogout} assistant={<XerticaAssistant ... />}>
|
|
124
|
+
<HomeContent user={user} onLogout={onLogout} />
|
|
125
|
+
</AppLayout>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 3. Import Rules
|
|
133
|
+
|
|
134
|
+
Always use the correct `xertica-ui` subpath for the layer you are in:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
// shared/ui → UI primitives
|
|
138
|
+
import { Button, Card, Input, Badge, Table, Dialog, Select } from 'xertica-ui/ui';
|
|
139
|
+
|
|
140
|
+
// app layer → providers and brand
|
|
141
|
+
import { XerticaProvider, XerticaLogo, ThemeToggle } from 'xertica-ui/brand';
|
|
142
|
+
|
|
143
|
+
// features/layout → navigation shell
|
|
144
|
+
import { Sidebar, Header } from 'xertica-ui/layout';
|
|
145
|
+
|
|
146
|
+
// features/assistant → AI assistant
|
|
147
|
+
import { XerticaAssistant, generateDemoResponse } from 'xertica-ui/assistant';
|
|
148
|
+
|
|
149
|
+
// features/media → media players
|
|
150
|
+
import { VideoPlayer, AudioPlayer } from 'xertica-ui/media';
|
|
151
|
+
|
|
152
|
+
// shared/lib → hooks and contexts
|
|
153
|
+
import { useLayout, useOptionalLayout, useTheme } from 'xertica-ui/hooks';
|
|
154
|
+
|
|
155
|
+
// styles → imported once by src/styles/index.css
|
|
156
|
+
import 'xertica-ui/style.css';
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Icons always come from `lucide-react` — never from `xertica-ui`:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { Home, Settings, Plus, Trash2, ChevronRight } from 'lucide-react';
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 4. Adding a New Route (Step-by-Step)
|
|
168
|
+
|
|
169
|
+
1. **Create the feature content** — `src/features/<name>/ui/<NameContent>.tsx`
|
|
170
|
+
2. **Export from the barrel** — add to `src/features/<name>/index.ts`
|
|
171
|
+
3. **Create the page** — `src/pages/<NamePage>.tsx` (thin AppLayout wrapper)
|
|
172
|
+
4. **Register the route** — add to `routes` array in `src/shared/config/navigation.ts`:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { MyIcon } from 'lucide-react';
|
|
176
|
+
|
|
177
|
+
export const routes: RouteConfig[] = [
|
|
178
|
+
{ path: '/home', label: 'Home', icon: Home },
|
|
179
|
+
{ path: '/my-feature', label: 'My Feature', icon: MyIcon }, // ← add here
|
|
180
|
+
];
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
5. **Register the `<Route>`** — add to `src/app/components/AuthGuard.tsx`:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { MyFeaturePage } from '../../pages/MyFeaturePage';
|
|
187
|
+
|
|
188
|
+
// inside <Routes>:
|
|
189
|
+
<Route
|
|
190
|
+
path="/my-feature"
|
|
191
|
+
element={
|
|
192
|
+
<ProtectedRoute user={user}>
|
|
193
|
+
<MyFeaturePage user={user} onLogout={handleLogout} />
|
|
194
|
+
</ProtectedRoute>
|
|
195
|
+
}
|
|
196
|
+
/>;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
6. **Add a navigation card** to `HomeContent.tsx` if it's a primary module.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 5. Non-Negotiable Rules
|
|
204
|
+
|
|
205
|
+
### HTML — never use raw elements for UI
|
|
206
|
+
|
|
207
|
+
| ❌ Never | ✅ Always |
|
|
208
|
+
| -------------------------------- | ------------------------------------------------------ |
|
|
209
|
+
| `<button>` | `<Button>` from `xertica-ui/ui` |
|
|
210
|
+
| `<input>` | `<Input>` from `xertica-ui/ui` |
|
|
211
|
+
| `<select>` | `<Select>` from `xertica-ui/ui` |
|
|
212
|
+
| `<h1>` / `<h2>` for page headers | `<PageHeader><PageHeaderHeading>` from `xertica-ui/ui` |
|
|
213
|
+
| `<div class="card ...">` | `<Card>` from `xertica-ui/ui` |
|
|
214
|
+
| custom scrollable div | `<ScrollArea>` from `xertica-ui/ui` |
|
|
215
|
+
|
|
216
|
+
### Colors — context-dependent rules
|
|
217
|
+
|
|
218
|
+
| ❌ Never (any context) | Reason |
|
|
219
|
+
| ------------------------------------------ | -------------------------- |
|
|
220
|
+
| `#3b82f6`, `rgb(59, 130, 246)`, `hsl(...)` | Raw values — non-themeable |
|
|
221
|
+
| `style={{ color: '#...' }}` for theming | Non-themeable inline style |
|
|
222
|
+
|
|
223
|
+
| ❌ Wrong for semantic/status contexts | ✅ Required instead |
|
|
224
|
+
| ---------------------------------------- | ------------------------------------- |
|
|
225
|
+
| `bg-red-500` / `text-red-500` for errors | `bg-destructive` / `text-destructive` |
|
|
226
|
+
| `bg-green-500` for success states | `bg-success` |
|
|
227
|
+
| `bg-yellow-500` for warnings | `bg-warning` |
|
|
228
|
+
|
|
229
|
+
> For **layout, spacing, and general non-semantic UI** (custom components, decorative elements where no semantic token applies), standard Tailwind color utilities like `bg-blue-500` or `text-gray-700` are acceptable.
|
|
230
|
+
|
|
231
|
+
| ❌ Never | ✅ Always |
|
|
232
|
+
| -------------------------- | ------------------------- |
|
|
233
|
+
| `rounded-lg`, `rounded-xl` | `rounded-[var(--radius)]` |
|
|
234
|
+
|
|
235
|
+
### Layout state — never hardcode
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
// ✅ Correct
|
|
239
|
+
const { sidebarExpanded, sidebarWidth } = useLayout();
|
|
240
|
+
<div style={{ paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px' }}>
|
|
241
|
+
|
|
242
|
+
// ✅ Correct for reusable components that may render outside XerticaProvider
|
|
243
|
+
const layout = useOptionalLayout();
|
|
244
|
+
const fallbackSidebarWidth = layout?.sidebarWidth ?? 80;
|
|
245
|
+
|
|
246
|
+
// ❌ Wrong
|
|
247
|
+
<div style={{ paddingLeft: '256px' }}>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Destructive actions — always confirm
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
// ✅ Always wrap in AlertDialog
|
|
254
|
+
<AlertDialog>
|
|
255
|
+
<AlertDialogTrigger asChild>
|
|
256
|
+
<Button variant="destructive">Delete</Button>
|
|
257
|
+
</AlertDialogTrigger>
|
|
258
|
+
<AlertDialogContent>
|
|
259
|
+
<AlertDialogHeader>
|
|
260
|
+
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
261
|
+
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
262
|
+
</AlertDialogHeader>
|
|
263
|
+
<AlertDialogFooter>
|
|
264
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
265
|
+
<AlertDialogAction onClick={handleDelete}>Delete</AlertDialogAction>
|
|
266
|
+
</AlertDialogFooter>
|
|
267
|
+
</AlertDialogContent>
|
|
268
|
+
</AlertDialog>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Page structure — always use PageHeader
|
|
272
|
+
|
|
273
|
+
Every page must use `<PageHeader>` for its title and primary actions. Never use raw `<h1>` or `<h2>`:
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
<PageHeader>
|
|
277
|
+
<PageHeaderHeading>Users</PageHeaderHeading>
|
|
278
|
+
<PageHeaderDescription>Manage your team members.</PageHeaderDescription>
|
|
279
|
+
<Button>
|
|
280
|
+
<Plus className="size-4 mr-2" />
|
|
281
|
+
New User
|
|
282
|
+
</Button>
|
|
283
|
+
</PageHeader>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Toast notifications
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
import { toast } from 'sonner';
|
|
290
|
+
import { useTranslation } from 'react-i18next';
|
|
291
|
+
|
|
292
|
+
const { t } = useTranslation();
|
|
293
|
+
toast.success(t('users.createSuccess'));
|
|
294
|
+
toast.error(t('errors.somethingWentWrong'));
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Never render `<Toaster>` manually — it is auto-injected by `<XerticaProvider>` in `App.tsx`. Always translate the message text through `t()`.
|
|
298
|
+
|
|
299
|
+
### Translations — never hardcode strings
|
|
300
|
+
|
|
301
|
+
Every user-facing string must come from `useTranslation()`. Add new keys to the appropriate split JSON file under `src/locales/<lang>/` for all configured languages:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
// ❌ Wrong
|
|
305
|
+
<Button aria-label="Save">Save</Button>;
|
|
306
|
+
|
|
307
|
+
// ✅ Correct
|
|
308
|
+
const { t } = useTranslation();
|
|
309
|
+
<Button aria-label={t('common.save')}>{t('common.save')}</Button>;
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
This applies to `aria-label`, `placeholder`, `title`, toast messages, error text, dropdown items — everything the user can read.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## 6. Internationalization
|
|
317
|
+
|
|
318
|
+
### Selecting languages on init
|
|
319
|
+
|
|
320
|
+
The CLI's `init` command asks which languages your app should support (`pt-BR`, `en`, `es`). Pick **1, 2, or 3**. The CLI:
|
|
321
|
+
|
|
322
|
+
- Copies only the locale **folders** for the languages you chose (each is a directory tree with split JSON files)
|
|
323
|
+
- Generates `src/i18n.ts` with `import.meta.glob` calls for exactly those languages — Vite auto-discovers all JSON files in the folder at build time
|
|
324
|
+
- Injects the `availableLanguages` prop into `src/app/App.tsx` (omitted when all 3 defaults are selected)
|
|
325
|
+
- Persists the selection in `src/locales/.languages.json`
|
|
326
|
+
|
|
327
|
+
### Monolingual mode
|
|
328
|
+
|
|
329
|
+
When you select only one language, the `<LanguageSelector>` automatically renders `null` (there is nothing to switch to). A header comment in the generated `App.tsx` documents this. To force the selector visible anyway:
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
<LanguageSelector showWhenMonolingual />
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Adding or removing languages later
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
npx xertica-ui update
|
|
339
|
+
# → select "Languages"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
The CLI shows your current selection, prompts for the new set, displays the diff (`+ es`, `- en`), and on confirm:
|
|
343
|
+
|
|
344
|
+
- copies any newly-added locale JSONs from `node_modules/xertica-ui/templates/src/locales/`
|
|
345
|
+
- removes JSONs of unselected languages
|
|
346
|
+
- regenerates `src/i18n.ts` and `src/app/App.tsx`
|
|
347
|
+
- updates `src/locales/.languages.json`
|
|
348
|
+
|
|
349
|
+
> The `update` → **Project files** flow also reads `.languages.json` and preserves your selection. Updating App.tsx and i18n.ts won't reset your languages.
|
|
350
|
+
|
|
351
|
+
### Enabling or disabling Dark Mode support later
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
npx xertica-ui update
|
|
355
|
+
# → select "Dark Mode"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The CLI reads your current setting from `.xertica.json` and prompts you to enable or disable dark mode. On confirmation, it updates `.xertica.json` and regenerates `App.tsx` to set `disableDarkMode={true}` or `false`.
|
|
359
|
+
|
|
360
|
+
### Adding or removing the AI Assistant later
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
npx xertica-ui update
|
|
364
|
+
# → select "Assistant"
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The CLI detects whether the assistant is currently active (via `.xertica.json` or page presence) and prompts to add or remove the feature. It automatically copies/deletes the feature files and updates `AuthGuard.tsx`, `HomePage.tsx`, and `TemplatePage.tsx` to reflect the change.
|
|
368
|
+
|
|
369
|
+
### Using translations in components
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
import { useTranslation } from 'react-i18next';
|
|
373
|
+
|
|
374
|
+
function MyComponent() {
|
|
375
|
+
const { t } = useTranslation();
|
|
376
|
+
return <h1>{t('home.welcome')}</h1>;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Reading the configured language set
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
import { useLanguage } from 'xertica-ui/hooks';
|
|
384
|
+
|
|
385
|
+
const { language, setLanguage, availableLanguages, isMonolingual } = useLanguage();
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
- Never hardcode the language list when building a custom settings UI — iterate over `availableLanguages` so it stays in sync with whatever you configured.
|
|
389
|
+
|
|
390
|
+
### Factory functions for translated mock data
|
|
391
|
+
|
|
392
|
+
Outside the React render cycle (mock fetch functions, fallback options), use **functions** so `i18n.t()` re-runs on every call instead of being frozen at module-load time:
|
|
393
|
+
|
|
394
|
+
```tsx
|
|
395
|
+
// ❌ Wrong — frozen in initial language
|
|
396
|
+
export const MOCK_OPTIONS = [i18n.t('feedback.notWhatIWanted')];
|
|
397
|
+
|
|
398
|
+
// ✅ Correct — re-evaluated every call
|
|
399
|
+
export function getMockOptions() {
|
|
400
|
+
return [i18n.t('feedback.notWhatIWanted')];
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Inside components, build label maps for enums with `useMemo([..., t])`:
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
const statusLabels = useMemo(
|
|
408
|
+
() => ({
|
|
409
|
+
active: t('status.active'),
|
|
410
|
+
inactive: t('status.inactive'),
|
|
411
|
+
}),
|
|
412
|
+
[t]
|
|
413
|
+
);
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## 7. Server State (React Query)
|
|
419
|
+
|
|
420
|
+
Each feature has its own `data/mock.ts` (typed fetch functions) and `hooks/use<Xxx>.ts` (React Query wrappers). Replace the mock fetch with a real API call without touching the consumer code.
|
|
421
|
+
|
|
422
|
+
### Language-aware queryKey — mandatory
|
|
423
|
+
|
|
424
|
+
Every hook whose response contains translated strings **must** include the active language in its `queryKey`:
|
|
425
|
+
|
|
426
|
+
```tsx
|
|
427
|
+
// features/home/hooks/useFeatureCards.ts
|
|
428
|
+
import { useQuery } from '@tanstack/react-query';
|
|
429
|
+
import { useLanguage } from 'xertica-ui/hooks';
|
|
430
|
+
import { fetchFeatureCards, type FeatureCard } from '../data/mock';
|
|
431
|
+
|
|
432
|
+
export function useFeatureCards() {
|
|
433
|
+
const { language } = useLanguage();
|
|
434
|
+
return useQuery<FeatureCard[]>({
|
|
435
|
+
queryKey: ['home', 'feature-cards', language], // ← language as third element
|
|
436
|
+
queryFn: fetchFeatureCards,
|
|
437
|
+
staleTime: 10 * 60 * 1000,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Switching language creates a new cache key → cache miss → automatic refetch in the new locale. Switching back is an instant cache hit. No page reload required.
|
|
443
|
+
|
|
444
|
+
### Connecting to a real API
|
|
445
|
+
|
|
446
|
+
Replace only the `fetch*` function in `data/mock.ts` — the hook and the components stay unchanged:
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
// data/mock.ts — after
|
|
450
|
+
export async function fetchFeatureCards(): Promise<FeatureCard[]> {
|
|
451
|
+
const res = await fetch('/api/feature-cards', {
|
|
452
|
+
headers: { 'Accept-Language': i18n.language },
|
|
453
|
+
});
|
|
454
|
+
if (!res.ok) throw new Error('Failed');
|
|
455
|
+
return res.json();
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Pass `Accept-Language: i18n.language` so the backend returns localized strings — mirroring the mock pattern.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## 8. Loading States — Skeletons
|
|
464
|
+
|
|
465
|
+
Always render a skeleton placeholder — never a spinner — for data-bearing surfaces. Spinner-only loading is acceptable **only** for inline actions (button submit, "saving…" state).
|
|
466
|
+
|
|
467
|
+
Every card pattern ships with a matching `*Skeleton`:
|
|
468
|
+
|
|
469
|
+
```tsx
|
|
470
|
+
import { ActivityCard, ActivityCardSkeleton } from 'xertica-ui';
|
|
471
|
+
|
|
472
|
+
{
|
|
473
|
+
isLoading ? <ActivityCardSkeleton rows={5} /> : <ActivityCard items={items} />;
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Available skeleton companions: `ActivityCardSkeleton`, `ProfileCardSkeleton`, `ProjectCardSkeleton`, `NotificationCardSkeleton`, `QuickActionCardSkeleton`, `FeatureCardSkeleton`, `StatsCardSkeleton`.
|
|
478
|
+
|
|
479
|
+
For grids, render one skeleton per expected card:
|
|
480
|
+
|
|
481
|
+
```tsx
|
|
482
|
+
{
|
|
483
|
+
isLoading ? (
|
|
484
|
+
<>
|
|
485
|
+
<FeatureCardSkeleton showAction />
|
|
486
|
+
<FeatureCardSkeleton showAction />
|
|
487
|
+
<FeatureCardSkeleton showAction />
|
|
488
|
+
</>
|
|
489
|
+
) : (
|
|
490
|
+
data.map(item => <FeatureCard key={item.id} {...item} />)
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
For tables, render skeleton rows with the `<Skeleton>` primitive (from `xertica-ui/ui`):
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
{isLoading
|
|
499
|
+
? Array.from({ length: 5 }).map((_, i) => (
|
|
500
|
+
<TableRow key={i}>
|
|
501
|
+
<TableCell><Skeleton className="h-3.5 w-28" /></TableCell>
|
|
502
|
+
<TableCell><Skeleton className="h-5 w-16 rounded-full" /></TableCell>
|
|
503
|
+
</TableRow>
|
|
504
|
+
))
|
|
505
|
+
: rows.map(...)}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Pass `rows={maxItems}` to list-style skeletons so the placeholder height matches the loaded state's row count.
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## 9. Theme & Token Customization
|
|
513
|
+
|
|
514
|
+
To change the color scheme, edit `src/styles/xertica/tokens.css`. To switch themes via CLI:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
npx xertica-ui update
|
|
518
|
+
# → select "Theme only"
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
The token file uses HSL values and supports both light and dark modes via the `.dark` class on `<html>`.
|
|
522
|
+
|
|
523
|
+
Never import or modify anything inside `node_modules/xertica-ui/styles/` — override via `tokens.css` only.
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## 10. Authentication Pattern
|
|
528
|
+
|
|
529
|
+
The auth flow is managed in `src/app/context/AuthContext.tsx` via the `useAuth()` hook:
|
|
530
|
+
|
|
531
|
+
- `getStoredUser()` / `storeUser()` / `clearStoredUser()` — from `src/shared/lib/auth.ts`
|
|
532
|
+
- Auth pages (`/login`, `/forgot-password`, etc.) redirect to `/home` when user is already logged in
|
|
533
|
+
- Protected routes use `<ProtectedRoute>` wrapper that redirects to `/login` if user is null
|
|
534
|
+
- `useAuth().login(email, password)` stores the user
|
|
535
|
+
- `useAuth().logout()` clears storage and navigates to `/login`
|
|
536
|
+
|
|
537
|
+
To add social login, extend the `login` logic in `AuthContext.tsx` and wire the provider button in `SocialLoginButtons.tsx`.
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## 11. AI Agent Documentation Reference
|
|
542
|
+
|
|
543
|
+
| What you need | Where to read |
|
|
544
|
+
| ----------------------------------- | --------------------------------------------------- |
|
|
545
|
+
| Quick import + rules reference | `node_modules/xertica-ui/llms-compact.txt` |
|
|
546
|
+
| Full component API | `node_modules/xertica-ui/llms-full.txt` |
|
|
547
|
+
| "Should I use Dialog or Sheet?" | `node_modules/xertica-ui/docs/decision-tree.md` |
|
|
548
|
+
| Specific component props | `node_modules/xertica-ui/docs/components/[name].md` |
|
|
549
|
+
| All components + their imports | `node_modules/xertica-ui/components.json` |
|
|
550
|
+
| Mandatory AI rules | `node_modules/xertica-ui/docs/ai-usage.md` |
|
|
551
|
+
| i18n & language configuration | `node_modules/xertica-ui/docs/i18n.md` |
|
|
552
|
+
| State management (React Query) | `node_modules/xertica-ui/docs/state-management.md` |
|
|
553
|
+
| This project's rules (you are here) | `CLAUDE.md` at project root |
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## 12. Development Checklist
|
|
558
|
+
|
|
559
|
+
Before completing any feature:
|
|
560
|
+
|
|
561
|
+
- [ ] Feature content is in `src/features/<name>/ui/`
|
|
562
|
+
- [ ] Feature exports via `src/features/<name>/index.ts`
|
|
563
|
+
- [ ] Page is a thin wrapper in `src/pages/`
|
|
564
|
+
- [ ] Route registered in `src/shared/config/navigation.ts`
|
|
565
|
+
- [ ] Route registered in `src/app/components/AuthGuard.tsx`
|
|
566
|
+
- [ ] Home navigation card added (if primary module)
|
|
567
|
+
- [ ] Only `xertica-ui` components used — no raw HTML elements
|
|
568
|
+
- [ ] No raw hex/rgb values — semantic tokens for semantic/status contexts; Tailwind utilities acceptable for non-semantic layout
|
|
569
|
+
- [ ] No hardcoded radii — use `rounded-[var(--radius)]`
|
|
570
|
+
- [ ] Icons from `lucide-react` only
|
|
571
|
+
- [ ] Destructive actions wrapped in `<AlertDialog>`
|
|
572
|
+
- [ ] Layout state from `useLayout()` — no hardcoded widths
|
|
573
|
+
- [ ] Toast used for action feedback (`toast.success`, `toast.error`) — message translated via `t()`
|
|
574
|
+
- [ ] **All user-facing strings go through `useTranslation()`** — added to all configured `locales/*.json`
|
|
575
|
+
- [ ] **Server-state hooks include `language` in their `queryKey`** when the response contains translated strings
|
|
576
|
+
- [ ] **Loading states render a `*Skeleton`** (not just a spinner) for cards/lists/tables
|
|
577
|
+
- [ ] Responsive on mobile, tablet, and desktop
|