xertica-ui 2.0.4 → 2.0.6
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 +298 -220
- package/README.md +73 -31
- package/bin/cli.ts +1 -1
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +2 -3
- package/components/blocks/card-patterns/ActivityCard.tsx +72 -0
- package/components/blocks/card-patterns/FeatureCard.tsx +100 -0
- package/components/blocks/card-patterns/NotificationCard.tsx +127 -0
- package/components/blocks/card-patterns/ProfileCard.tsx +84 -0
- package/components/blocks/card-patterns/ProjectCard.tsx +89 -0
- package/components/blocks/card-patterns/QuickActionCard.tsx +62 -0
- package/components/blocks/card-patterns/card-patterns.stories.tsx +445 -0
- package/components/blocks/card-patterns/index.ts +17 -0
- package/components/blocks/index.ts +1 -0
- package/components/brand/xertica-provider/XerticaProvider.tsx +48 -20
- package/components/brand/xertica-provider/xertica-provider.mdx +61 -59
- package/components/brand/xertica-provider/xertica-provider.test.tsx +52 -7
- package/components/index.ts +3 -0
- package/components/layout/header/header.test.tsx +8 -2
- package/components/layout/header/header.tsx +4 -3
- package/components/layout/sidebar/sidebar.stories.tsx +6 -6
- package/components/layout/sidebar/sidebar.test.tsx +11 -5
- package/components/layout/sidebar/sidebar.tsx +34 -36
- package/components/media/FloatingMediaWrapper.tsx +11 -8
- package/components/media/audio-player/AudioPlayer.tsx +22 -14
- package/components/pages/home-content/HomeContent.tsx +7 -5
- package/components/pages/home-content/home-content.mdx +62 -63
- package/components/pages/home-page/HomePage.tsx +12 -5
- package/components/pages/home-page/home-page.mdx +10 -13
- package/components/pages/template-content/TemplateContent.tsx +10 -4
- package/components/pages/template-content/template-content.mdx +61 -62
- package/components/pages/template-page/TemplatePage.tsx +11 -4
- package/components/pages/template-page/template-page.mdx +10 -11
- package/components/public-api-smoke.test.tsx +45 -0
- package/components/shared/assistant-utils.ts +43 -11
- package/components/ui/accordion/accordion.mdx +8 -20
- package/components/ui/accordion/accordion.stories.tsx +27 -13
- package/components/ui/alert/alert.mdx +8 -24
- package/components/ui/alert/alert.stories.tsx +1 -0
- package/components/ui/alert-dialog/alert-dialog.mdx +8 -13
- package/components/ui/alert-dialog/alert-dialog.stories.tsx +1 -0
- package/components/ui/aspect-ratio/aspect-ratio.mdx +8 -20
- package/components/ui/aspect-ratio/aspect-ratio.stories.tsx +1 -0
- package/components/ui/assistant-chart/assistant-chart.mdx +8 -13
- package/components/ui/assistant-chart/assistant-chart.stories.tsx +1 -0
- package/components/ui/avatar/avatar.mdx +8 -28
- package/components/ui/avatar/avatar.stories.tsx +1 -0
- package/components/ui/badge/badge.mdx +8 -23
- package/components/ui/badge/badge.stories.tsx +1 -0
- package/components/ui/badge/badge.tsx +9 -9
- package/components/ui/breadcrumb/breadcrumb.mdx +8 -20
- package/components/ui/breadcrumb/breadcrumb.stories.tsx +1 -0
- package/components/ui/button/button.mdx +8 -52
- package/components/ui/button/button.stories.tsx +1 -0
- package/components/ui/calendar/calendar.mdx +8 -27
- package/components/ui/calendar/calendar.stories.tsx +31 -4
- package/components/ui/calendar/calendar.test.tsx +6 -0
- package/components/ui/calendar/calendar.tsx +258 -249
- package/components/ui/card/card.mdx +8 -27
- package/components/ui/card/card.stories.tsx +245 -103
- package/components/ui/carousel/carousel.mdx +8 -20
- package/components/ui/carousel/carousel.stories.tsx +1 -0
- package/components/ui/chart/chart.mdx +8 -20
- package/components/ui/chart/chart.stories.tsx +277 -24
- package/components/ui/chart/chart.test.tsx +154 -20
- package/components/ui/chart/chart.tsx +1494 -368
- package/components/ui/checkbox/checkbox.mdx +8 -27
- package/components/ui/checkbox/checkbox.stories.tsx +1 -0
- package/components/ui/collapsible/collapsible.mdx +8 -13
- package/components/ui/collapsible/collapsible.stories.tsx +1 -0
- package/components/ui/command/command.mdx +8 -20
- package/components/ui/command/command.stories.tsx +1 -0
- package/components/ui/context-menu/context-menu.mdx +8 -13
- package/components/ui/context-menu/context-menu.stories.tsx +1 -0
- package/components/ui/dialog/dialog.mdx +8 -20
- package/components/ui/dialog/dialog.stories.tsx +1 -0
- package/components/ui/drawer/drawer.mdx +8 -13
- package/components/ui/drawer/drawer.stories.tsx +1 -0
- package/components/ui/dropdown-menu/dropdown-menu.mdx +8 -22
- package/components/ui/dropdown-menu/dropdown-menu.stories.tsx +1 -0
- package/components/ui/empty/empty.mdx +8 -27
- package/components/ui/empty/empty.stories.tsx +1 -0
- package/components/ui/file-upload/file-upload.mdx +8 -20
- package/components/ui/file-upload/file-upload.stories.tsx +1 -0
- package/components/ui/form/form.stories.tsx +1 -0
- package/components/ui/google-maps-loader/google-maps-loader.tsx +2 -2
- package/components/ui/hover-card/hover-card.mdx +8 -13
- package/components/ui/hover-card/hover-card.stories.tsx +1 -0
- package/components/ui/index.ts +41 -16
- package/components/ui/input/input.mdx +8 -46
- package/components/ui/input/input.stories.tsx +1 -0
- package/components/ui/input-otp/input-otp.mdx +8 -27
- package/components/ui/input-otp/input-otp.stories.tsx +7 -6
- package/components/ui/label/label.mdx +8 -27
- package/components/ui/label/label.stories.tsx +1 -0
- package/components/ui/map/map.mdx +8 -20
- package/components/ui/map/map.stories.tsx +51 -30
- package/components/ui/map/map.tsx +2 -2
- package/components/ui/menubar/menubar.mdx +8 -13
- package/components/ui/menubar/menubar.stories.tsx +1 -0
- package/components/ui/navigation-menu/navigation-menu.mdx +8 -13
- package/components/ui/navigation-menu/navigation-menu.stories.tsx +1 -0
- package/components/ui/notification-badge/notification-badge.mdx +8 -20
- package/components/ui/notification-badge/notification-badge.stories.tsx +1 -0
- package/components/ui/page-header/page-header.stories.tsx +1 -0
- package/components/ui/pagination/pagination.mdx +8 -20
- package/components/ui/pagination/pagination.stories.tsx +11 -8
- package/components/ui/popover/popover.mdx +8 -20
- package/components/ui/popover/popover.stories.tsx +1 -0
- package/components/ui/progress/progress.mdx +8 -16
- package/components/ui/progress/progress.stories.tsx +1 -0
- package/components/ui/radio-group/radio-group.mdx +8 -27
- package/components/ui/radio-group/radio-group.stories.tsx +1 -0
- package/components/ui/rating/rating.mdx +8 -20
- package/components/ui/rating/rating.stories.tsx +1 -0
- package/components/ui/resizable/resizable.mdx +8 -20
- package/components/ui/resizable/resizable.stories.tsx +1 -0
- package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +1 -0
- package/components/ui/rich-text-editor/rich-text-editor.tsx +2 -2
- package/components/ui/route-map/route-map.mdx +8 -20
- package/components/ui/route-map/route-map.stories.tsx +1 -0
- package/components/ui/route-map/route-map.tsx +2 -2
- package/components/ui/scroll-area/scroll-area.mdx +8 -13
- package/components/ui/scroll-area/scroll-area.stories.tsx +1 -0
- package/components/ui/search/search.mdx +8 -20
- package/components/ui/search/search.stories.tsx +1 -0
- package/components/ui/select/select.mdx +8 -27
- package/components/ui/select/select.stories.tsx +10 -9
- package/components/ui/separator/separator.mdx +8 -20
- package/components/ui/separator/separator.stories.tsx +1 -0
- package/components/ui/sheet/sheet.mdx +8 -20
- package/components/ui/sheet/sheet.stories.tsx +1 -0
- package/components/ui/simple-map/simple-map.mdx +8 -20
- package/components/ui/simple-map/simple-map.stories.tsx +1 -0
- package/components/ui/skeleton/skeleton.mdx +8 -20
- package/components/ui/skeleton/skeleton.stories.tsx +1 -0
- package/components/ui/slider/slider.mdx +8 -20
- package/components/ui/slider/slider.stories.tsx +1 -0
- package/components/ui/sonner/sonner.mdx +8 -13
- package/components/ui/sonner/sonner.stories.tsx +1 -0
- package/components/ui/stats-card/stats-card.mdx +8 -20
- package/components/ui/stats-card/stats-card.stories.tsx +1 -0
- package/components/ui/stepper/stepper.mdx +8 -13
- package/components/ui/stepper/stepper.stories.tsx +1 -0
- package/components/ui/switch/switch.mdx +8 -27
- package/components/ui/switch/switch.stories.tsx +1 -0
- package/components/ui/table/table.mdx +8 -13
- package/components/ui/table/table.stories.tsx +1 -0
- package/components/ui/tabs/tabs.mdx +8 -20
- package/components/ui/tabs/tabs.stories.tsx +1 -0
- package/components/ui/textarea/textarea.mdx +8 -20
- package/components/ui/textarea/textarea.stories.tsx +1 -0
- package/components/ui/timeline/timeline.mdx +8 -13
- package/components/ui/timeline/timeline.stories.tsx +1 -0
- package/components/ui/toggle/toggle.mdx +8 -20
- package/components/ui/toggle/toggle.stories.tsx +1 -0
- package/components/ui/toggle-group/toggle-group.mdx +8 -20
- package/components/ui/toggle-group/toggle-group.stories.tsx +1 -0
- package/components/ui/tooltip/tooltip.mdx +8 -27
- package/components/ui/tooltip/tooltip.stories.tsx +1 -0
- package/components/ui/tree-view/tree-view.mdx +8 -13
- package/components/ui/tree-view/tree-view.stories.tsx +1 -0
- package/components.json +7 -3
- package/contexts/ApiKeyContext.tsx +72 -26
- package/contexts/BrandColorsContext.tsx +26 -23
- package/contexts/LanguageContext.tsx +13 -10
- package/contexts/LayoutContext.test.tsx +11 -5
- package/contexts/LayoutContext.tsx +29 -21
- package/contexts/ThemeContext.tsx +26 -22
- package/contexts/theme-data.ts +4 -4
- package/dist/AudioPlayer-B1lt5cPl.cjs +989 -0
- package/dist/AudioPlayer-C12BjQBV.cjs +997 -0
- package/dist/AudioPlayer-DMcG_c7L.js +990 -0
- package/dist/AudioPlayer-DcFKRJE_.js +998 -0
- package/dist/CodeBlock-7TTgmdGG.cjs +2094 -0
- package/dist/CodeBlock-BeSt1h5P.js +2078 -0
- package/dist/CodeBlock-BgfYL_rD.cjs +2094 -0
- package/dist/CodeBlock-BlcqlA9M.cjs +2094 -0
- package/dist/CodeBlock-Bnmeu5ez.cjs +2094 -0
- package/dist/CodeBlock-BtfPlbAI.js +2078 -0
- package/dist/CodeBlock-CIySIuYr.js +2078 -0
- package/dist/CodeBlock-D8dcwbit.cjs +2094 -0
- package/dist/CodeBlock-DMZrFnlw.cjs +2094 -0
- package/dist/CodeBlock-DlBehYN8.js +2078 -0
- package/dist/CodeBlock-DnYNI8rQ.js +2078 -0
- package/dist/CodeBlock-DvKWbSnE.cjs +2094 -0
- package/dist/CodeBlock-DwMCfkFY.js +2078 -0
- package/dist/CodeBlock-Dy6CNYyj.js +2078 -0
- package/dist/CodeBlock-U1pPOQI7.cjs +2094 -0
- package/dist/CodeBlock-f_GpNhEB.js +2078 -0
- package/dist/CodeBlock-oB6u8nI1.js +2078 -0
- package/dist/CodeBlock-tZC31B73.cjs +2094 -0
- package/dist/LayoutContext-CwT5KLiW.cjs +104 -0
- package/dist/LayoutContext-DVLCsoQn.js +105 -0
- package/dist/ThemeContext-BoH4NLfN.js +734 -0
- package/dist/ThemeContext-C2EwAPDt.js +735 -0
- package/dist/ThemeContext-r69W20Xg.cjs +733 -0
- package/dist/ThemeContext-vTjumZeM.cjs +734 -0
- package/dist/XerticaProvider-BlY2limY.cjs +38 -0
- package/dist/XerticaProvider-DDuiIcKo.js +39 -0
- package/dist/XerticaProvider-cI9hSs27.cjs +38 -0
- package/dist/XerticaProvider-hSwhNQex.js +39 -0
- package/dist/assistant.cjs.js +1 -1
- package/dist/assistant.es.js +1 -1
- package/dist/brand.cjs.js +1 -1
- package/dist/brand.es.js +1 -1
- package/dist/cli.js +5 -5
- package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +1 -1
- package/dist/components/blocks/card-patterns/ActivityCard.d.ts +20 -0
- package/dist/components/blocks/card-patterns/FeatureCard.d.ts +16 -0
- package/dist/components/blocks/card-patterns/NotificationCard.d.ts +24 -0
- package/dist/components/blocks/card-patterns/ProfileCard.d.ts +22 -0
- package/dist/components/blocks/card-patterns/ProjectCard.d.ts +18 -0
- package/dist/components/blocks/card-patterns/QuickActionCard.d.ts +15 -0
- package/dist/components/blocks/card-patterns/index.d.ts +12 -0
- package/dist/components/blocks/index.d.ts +1 -0
- package/dist/components/brand/xertica-provider/XerticaProvider.d.ts +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/shared/assistant-utils.d.ts +35 -5
- package/dist/components/ui/alert/alert.d.ts +1 -1
- package/dist/components/ui/badge/badge.d.ts +1 -1
- package/dist/components/ui/button/button.d.ts +2 -2
- package/dist/components/ui/calendar/calendar.d.ts +4 -3
- package/dist/components/ui/chart/chart.d.ts +109 -1
- package/dist/components/ui/dialog/dialog.d.ts +1 -1
- package/dist/components/ui/index.d.ts +4 -2
- package/dist/contexts/ApiKeyContext.d.ts +6 -2
- package/dist/contexts/LayoutContext.d.ts +1 -0
- package/dist/google-maps-loader-CTYySAun.js +290 -0
- package/dist/google-maps-loader-Y-QkD-Li.cjs +290 -0
- package/dist/header-Cgy6vYPk.cjs +731 -0
- package/dist/header-DRlT4jgI.js +715 -0
- package/dist/header-WfEywpyc.cjs +731 -0
- package/dist/header-tifNQn2U.js +715 -0
- package/dist/hooks.cjs.js +13 -684
- package/dist/hooks.es.js +13 -684
- package/dist/index-COtD8bRW.cjs +7 -0
- package/dist/index-DW5tYe26.js +8 -0
- package/dist/index.cjs.js +520 -192
- package/dist/index.es.js +557 -228
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/media.cjs.js +1 -1
- package/dist/media.es.js +1 -1
- package/dist/rich-text-editor-0mraWT5y.cjs +2376 -0
- package/dist/rich-text-editor-B6jMRLzk.cjs +1939 -0
- package/dist/rich-text-editor-B8_oYcIR.js +1730 -0
- package/dist/rich-text-editor-BYuRBNBU.js +2373 -0
- package/dist/rich-text-editor-Bb9pySTs.cjs +2374 -0
- package/dist/rich-text-editor-BcL6L3cm.cjs +2374 -0
- package/dist/rich-text-editor-BoVZYtTs.cjs +2391 -0
- package/dist/rich-text-editor-CPV1lEPH.cjs +1748 -0
- package/dist/rich-text-editor-CoKqbCtu.cjs +1799 -0
- package/dist/rich-text-editor-Cw56T_mB.js +2356 -0
- package/dist/rich-text-editor-Cyt8qs2b.js +1921 -0
- package/dist/rich-text-editor-D76gD-QI.js +2328 -0
- package/dist/rich-text-editor-DKkokOnA.js +1781 -0
- package/dist/rich-text-editor-DNsdpN64.cjs +2359 -0
- package/dist/rich-text-editor-DfG8bCyY.js +2358 -0
- package/dist/rich-text-editor-Dxjw31Z4.js +2341 -0
- package/dist/rich-text-editor-DzP0Epmb.js +2356 -0
- package/dist/rich-text-editor-skplNlBM.cjs +2345 -0
- package/dist/ui.cjs.js +57 -47
- package/dist/ui.es.js +241 -231
- package/dist/use-mobile-B0hNy_Y6.cjs +4303 -0
- package/dist/use-mobile-BXuYROXM.js +4202 -0
- package/dist/use-mobile-Bbd51ASU.cjs +4392 -0
- package/dist/use-mobile-Bk6CX-TC.js +4359 -0
- package/dist/use-mobile-BvYdisLP.js +4202 -0
- package/dist/use-mobile-BzuxjzNX.cjs +4392 -0
- package/dist/use-mobile-CG2-SdXV.cjs +4235 -0
- package/dist/use-mobile-CKb5pqTs.js +4269 -0
- package/dist/use-mobile-CYuAuGDl.js +4202 -0
- package/dist/use-mobile-CbrYgJGJ.js +4203 -0
- package/dist/use-mobile-DRB3BQgD.cjs +4235 -0
- package/dist/use-mobile-DZvv7QMR.js +4359 -0
- package/dist/use-mobile-DdI_TXam.cjs +4235 -0
- package/dist/use-mobile-DlceKf8a.js +4359 -0
- package/dist/use-mobile-DsOnow1o.cjs +4236 -0
- package/dist/use-mobile-Kcj6jSnK.cjs +4392 -0
- package/dist/use-mobile-bnKcua_i.js +4202 -0
- package/dist/use-mobile-ncXBeE2z.cjs +4235 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/architecture.md +20 -2
- package/docs/components/calendar.md +154 -99
- package/docs/components/card-patterns.md +337 -0
- package/docs/components/card.md +235 -162
- package/docs/components/chart.md +186 -4
- package/docs/components/map.md +84 -76
- package/docs/components/xertica-provider.md +24 -12
- package/docs/llms.md +2 -2
- package/llms-compact.txt +1 -1
- package/llms.txt +3 -3
- package/package.json +2 -2
- package/styles/xertica/tokens.css +12 -12
- package/templates/CLAUDE.md +16 -4
- package/templates/guidelines/Guidelines.md +6 -2
- package/templates/package.json +19 -2
- package/templates/src/app/App.tsx +8 -17
- package/templates/src/features/template/ui/CrudTemplate.tsx +3 -3
- package/templates/src/features/template/ui/DashboardTemplate.tsx +3 -3
- package/templates/src/features/template/ui/FormTemplate.tsx +1 -1
- package/templates/src/features/template/ui/LoginTemplate.tsx +3 -3
- package/templates/tsconfig.json +5 -4
- package/templates/vite.config.js +21 -5
- package/templates/vite.config.ts +20 -5
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Enterprise-grade React design system** built on Tailwind CSS v4, Radix UI, and Lucide Icons — with a robust AI-first documentation layer for precise LLM-driven composition and autonomous agent interaction.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/xertica-ui)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -14,7 +14,8 @@ Xertica UI is specifically designed to be consumed by AI Agents (LLMs, code assi
|
|
|
14
14
|
| File | Purpose |
|
|
15
15
|
|---|---|
|
|
16
16
|
| [`llms.txt`](./llms.txt) | Standard index for AI crawlers and context-aware agents. |
|
|
17
|
-
| [`llms-
|
|
17
|
+
| [`llms-compact.txt`](./llms-compact.txt) | **Compact documentation** of all components in a single file for LLMs with limited context. |
|
|
18
|
+
| [`llms-full.txt`](./llms-full.txt) | **Complete documentation** of all components in a single file for LLMs with large-context. |
|
|
18
19
|
| `docs/llms.md` | Master index for agents to navigate the documentation folder. |
|
|
19
20
|
|
|
20
21
|
---
|
|
@@ -31,6 +32,8 @@ npm run dev
|
|
|
31
32
|
|
|
32
33
|
The CLI scaffolds a complete Vite + React + TypeScript project with Portuguese UI localization and English AI-ready documentation.
|
|
33
34
|
|
|
35
|
+
> **Note:** Always use `@latest` with npx. Without it, npx may execute a locally cached older version instead of fetching the latest from the registry.
|
|
36
|
+
|
|
34
37
|
---
|
|
35
38
|
|
|
36
39
|
## 📦 Installation as a Library
|
|
@@ -47,10 +50,24 @@ npm install xertica-ui
|
|
|
47
50
|
import 'xertica-ui/style.css';
|
|
48
51
|
```
|
|
49
52
|
|
|
50
|
-
**2.
|
|
51
|
-
|
|
52
|
-
```tsx
|
|
53
|
-
import {
|
|
53
|
+
**2. Import components from the matching subpath**:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { Button, Card } from 'xertica-ui/ui';
|
|
57
|
+
|
|
58
|
+
export function Example() {
|
|
59
|
+
return (
|
|
60
|
+
<Card className="p-4">
|
|
61
|
+
<Button>Continue</Button>
|
|
62
|
+
</Card>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**3. Optionally wrap your app** with `XerticaProvider` when you want coordinated theme, layout, assistant, maps, tooltip, API-key, and toast services:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { XerticaProvider } from 'xertica-ui/brand';
|
|
54
71
|
|
|
55
72
|
function App() {
|
|
56
73
|
return (
|
|
@@ -70,16 +87,24 @@ Xertica UI v2 exposes **granular subpath entries** — import only what your lay
|
|
|
70
87
|
```tsx
|
|
71
88
|
import { Button, Card, Input } from 'xertica-ui/ui'; // shared/ui — primitives
|
|
72
89
|
import { Sidebar, Header } from 'xertica-ui/layout'; // layout shell
|
|
73
|
-
import { XerticaProvider } from 'xertica-ui/brand'; // app-level — providers & brand
|
|
74
|
-
import { XerticaAssistant } from 'xertica-ui/assistant'; // feature — AI assistant
|
|
75
|
-
import { VideoPlayer, AudioPlayer } from 'xertica-ui/media'; // feature — media players
|
|
76
|
-
import { useLayout, useTheme }
|
|
77
|
-
import 'xertica-ui/style.css'; // styles — import once at root
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
The root `from 'xertica-ui'` barrel remains available for full backward compatibility.
|
|
81
|
-
|
|
82
|
-
> **TypeScript**: requires `"moduleResolution": "bundler"` (or `"node16"` / `"nodenext"`) in `tsconfig.json` to resolve subpath exports.
|
|
90
|
+
import { XerticaProvider } from 'xertica-ui/brand'; // app-level — providers & brand
|
|
91
|
+
import { XerticaAssistant } from 'xertica-ui/assistant'; // feature — AI assistant
|
|
92
|
+
import { VideoPlayer, AudioPlayer } from 'xertica-ui/media'; // feature — media players
|
|
93
|
+
import { useLayout, useOptionalLayout, useTheme } from 'xertica-ui/hooks'; // shared/lib — hooks & contexts
|
|
94
|
+
import 'xertica-ui/style.css'; // styles — import once at root
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The root `from 'xertica-ui'` barrel remains available for full backward compatibility.
|
|
98
|
+
|
|
99
|
+
> **TypeScript**: requires `"moduleResolution": "bundler"` (or `"node16"` / `"nodenext"`) in `tsconfig.json` to resolve subpath exports.
|
|
100
|
+
|
|
101
|
+
### Component Independence Contract
|
|
102
|
+
|
|
103
|
+
`xertica-ui/style.css` is the only required global import. Public components are designed to render independently whenever possible, so importing one component into a consumer project should also bring the runtime logic that component needs.
|
|
104
|
+
|
|
105
|
+
`XerticaProvider` remains the recommended app-level convenience wrapper, but it is not required for most primitives. It composes the library providers for theme, brand colors, language, layout, assistant state, API keys, Google Maps, tooltips, and toasts.
|
|
106
|
+
|
|
107
|
+
Components with unavoidable external configuration, such as Google Maps, should render a configuration or error state instead of crashing the app.
|
|
83
108
|
|
|
84
109
|
---
|
|
85
110
|
|
|
@@ -108,13 +133,22 @@ export function MyPage() {
|
|
|
108
133
|
}
|
|
109
134
|
```
|
|
110
135
|
|
|
111
|
-
### `useLayout()` Hook
|
|
112
|
-
Access the sidebar state, width, and toggle functions anywhere in the component tree:
|
|
113
|
-
```tsx
|
|
114
|
-
import { useLayout } from 'xertica-ui/hooks';
|
|
115
|
-
|
|
116
|
-
const { sidebarWidth, isSidebarOpen, toggleSidebar } = useLayout();
|
|
117
|
-
```
|
|
136
|
+
### `useLayout()` Hook
|
|
137
|
+
Access the sidebar state, width, and toggle functions anywhere in the component tree:
|
|
138
|
+
```tsx
|
|
139
|
+
import { useLayout } from 'xertica-ui/hooks';
|
|
140
|
+
|
|
141
|
+
const { sidebarWidth, isSidebarOpen, toggleSidebar } = useLayout();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use `useLayout()` when a page must fail early without a layout provider. Use `useOptionalLayout()` inside reusable components that should still render with internal fallbacks when imported in isolation.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { useOptionalLayout } from 'xertica-ui/hooks';
|
|
148
|
+
|
|
149
|
+
const layout = useOptionalLayout();
|
|
150
|
+
const sidebarWidth = layout?.sidebarWidth ?? 0;
|
|
151
|
+
```
|
|
118
152
|
|
|
119
153
|
---
|
|
120
154
|
|
|
@@ -160,7 +194,7 @@ Each feature only imports from `shared/` or its own domain. Pages only compose f
|
|
|
160
194
|
|
|
161
195
|
---
|
|
162
196
|
|
|
163
|
-
## 🌟 Specialized Modules
|
|
197
|
+
## 🌟 Specialized Modules
|
|
164
198
|
|
|
165
199
|
### 🤖 AI Assistant
|
|
166
200
|
Integrated AI chat panel with workspace support.
|
|
@@ -170,12 +204,20 @@ Integrated AI chat panel with workspace support.
|
|
|
170
204
|
First-class Google Maps integration.
|
|
171
205
|
- `Map` · `RouteMap` · `SimpleMap` · `GoogleMapsLoader`
|
|
172
206
|
|
|
173
|
-
### 🎙️ Media
|
|
174
|
-
- `AudioPlayer` · `VideoPlayer` · `FloatingMediaWrapper`
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
##
|
|
207
|
+
### 🎙️ Media
|
|
208
|
+
- `AudioPlayer` · `VideoPlayer` · `FloatingMediaWrapper`
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 📚 Storybook Documentation
|
|
213
|
+
|
|
214
|
+
Storybook Docs pages use each component's real story variations instead of repeating a single usage example. UI component MDX files now render the story list directly, so docs stay aligned with the component's public stories.
|
|
215
|
+
|
|
216
|
+
Map stories use a wider responsive preview frame, making `Map`, `RouteMap`, and related map examples readable in the Docs canvas.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 🎨 Design Tokens
|
|
179
221
|
|
|
180
222
|
Xertica UI uses semantic CSS tokens. **Never use raw colors or generic Tailwind color classes**:
|
|
181
223
|
|
|
@@ -225,5 +267,5 @@ Border: border-border
|
|
|
225
267
|
|
|
226
268
|
## ⚖️ License
|
|
227
269
|
|
|
228
|
-
Proprietary — Xertica
|
|
270
|
+
Proprietary — Xertica.ai Team.
|
|
229
271
|
|
package/bin/cli.ts
CHANGED
|
@@ -241,8 +241,7 @@ export type AssistantTab = 'chat' | 'historico' | 'favoritos';
|
|
|
241
241
|
// Component Props
|
|
242
242
|
// ============================================================================
|
|
243
243
|
|
|
244
|
-
import type
|
|
245
|
-
import { gerarResposta } from '../../shared/assistant-utils';
|
|
244
|
+
import { gerarResposta, type MockResponse } from '../../shared/assistant-utils';
|
|
246
245
|
|
|
247
246
|
export interface XerticaAssistantProps {
|
|
248
247
|
/**
|
|
@@ -1861,4 +1860,4 @@ export function XerticaAssistant({
|
|
|
1861
1860
|
</Dialog>
|
|
1862
1861
|
</>
|
|
1863
1862
|
);
|
|
1864
|
-
}
|
|
1863
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Card, CardHeader, CardTitle, CardContent, CardAction } from "../../ui/card"
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar"
|
|
6
|
+
import { Badge } from "../../ui/badge"
|
|
7
|
+
import { cn } from "../../shared/utils"
|
|
8
|
+
|
|
9
|
+
export interface ActivityItem {
|
|
10
|
+
id: string
|
|
11
|
+
user: { name: string; avatar?: string; initials: string }
|
|
12
|
+
action: string
|
|
13
|
+
target: string
|
|
14
|
+
time: string
|
|
15
|
+
type?: "create" | "update" | "delete" | "comment" | "deploy"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ActivityCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
19
|
+
title?: string
|
|
20
|
+
items: ActivityItem[]
|
|
21
|
+
action?: React.ReactNode
|
|
22
|
+
maxItems?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const activityTypeBadge: Record<NonNullable<ActivityItem["type"]>, { label: string; variant: React.ComponentProps<typeof Badge>["variant"] }> = {
|
|
26
|
+
create: { label: "Criado", variant: "success" },
|
|
27
|
+
update: { label: "Atualizado", variant: "info" },
|
|
28
|
+
delete: { label: "Removido", variant: "destructive" },
|
|
29
|
+
comment: { label: "Comentou", variant: "secondary" },
|
|
30
|
+
deploy: { label: "Deploy", variant: "default" },
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ActivityCard({ title = "Atividade Recente", items, action, maxItems = 5, className, ...props }: ActivityCardProps) {
|
|
34
|
+
const visible = items.slice(0, maxItems)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Card className={cn("w-full", className)} {...props}>
|
|
38
|
+
<CardHeader>
|
|
39
|
+
<CardTitle>{title}</CardTitle>
|
|
40
|
+
{action && <CardAction>{action}</CardAction>}
|
|
41
|
+
</CardHeader>
|
|
42
|
+
<CardContent className="px-6 pb-6">
|
|
43
|
+
<ul className="space-y-4">
|
|
44
|
+
{visible.map((item) => {
|
|
45
|
+
const badge = item.type ? activityTypeBadge[item.type] : null
|
|
46
|
+
return (
|
|
47
|
+
<li key={item.id} className="flex items-start gap-3">
|
|
48
|
+
<Avatar className="size-8 shrink-0">
|
|
49
|
+
{item.user.avatar && <AvatarImage src={item.user.avatar} alt={item.user.name} />}
|
|
50
|
+
<AvatarFallback className="text-xs">{item.user.initials}</AvatarFallback>
|
|
51
|
+
</Avatar>
|
|
52
|
+
<div className="flex-1 min-w-0">
|
|
53
|
+
<p className="text-sm leading-snug">
|
|
54
|
+
<span className="font-medium">{item.user.name}</span>
|
|
55
|
+
{" "}{item.action}{" "}
|
|
56
|
+
<span className="font-medium">{item.target}</span>
|
|
57
|
+
</p>
|
|
58
|
+
<p className="text-xs text-muted-foreground mt-0.5">{item.time}</p>
|
|
59
|
+
</div>
|
|
60
|
+
{badge && (
|
|
61
|
+
<Badge variant={badge.variant} className="shrink-0 text-xs">
|
|
62
|
+
{badge.label}
|
|
63
|
+
</Badge>
|
|
64
|
+
)}
|
|
65
|
+
</li>
|
|
66
|
+
)
|
|
67
|
+
})}
|
|
68
|
+
</ul>
|
|
69
|
+
</CardContent>
|
|
70
|
+
</Card>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "../../ui/card"
|
|
5
|
+
import { Button } from "../../ui/button"
|
|
6
|
+
import { Badge } from "../../ui/badge"
|
|
7
|
+
import { cn } from "../../shared/utils"
|
|
8
|
+
|
|
9
|
+
export type FeatureCardColor =
|
|
10
|
+
| "primary"
|
|
11
|
+
| "chart-1"
|
|
12
|
+
| "chart-2"
|
|
13
|
+
| "chart-3"
|
|
14
|
+
| "chart-4"
|
|
15
|
+
| "chart-5"
|
|
16
|
+
| "success"
|
|
17
|
+
| "info"
|
|
18
|
+
| "warning"
|
|
19
|
+
| "destructive"
|
|
20
|
+
|
|
21
|
+
const colorTokens: Record<FeatureCardColor, { bg: string; icon: string }> = {
|
|
22
|
+
primary: { bg: "bg-primary/10", icon: "text-primary" },
|
|
23
|
+
"chart-1": { bg: "bg-[var(--chart-1)]/15", icon: "text-[var(--chart-1)]" },
|
|
24
|
+
"chart-2": { bg: "bg-[var(--chart-2)]/15", icon: "text-[var(--chart-2)]" },
|
|
25
|
+
"chart-3": { bg: "bg-[var(--chart-3)]/15", icon: "text-[var(--chart-3)]" },
|
|
26
|
+
"chart-4": { bg: "bg-[var(--chart-4)]/15", icon: "text-[var(--chart-4)]" },
|
|
27
|
+
"chart-5": { bg: "bg-[var(--chart-5)]/15", icon: "text-[var(--chart-5)]" },
|
|
28
|
+
success: { bg: "bg-success/10", icon: "text-success" },
|
|
29
|
+
info: { bg: "bg-info/10", icon: "text-info" },
|
|
30
|
+
warning: { bg: "bg-warning/10", icon: "text-warning" },
|
|
31
|
+
destructive: { bg: "bg-destructive/10", icon: "text-destructive" },
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface FeatureCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
35
|
+
title: string
|
|
36
|
+
description: string
|
|
37
|
+
icon: React.ReactNode
|
|
38
|
+
color?: FeatureCardColor
|
|
39
|
+
badge?: string
|
|
40
|
+
badgeVariant?: React.ComponentProps<typeof Badge>["variant"]
|
|
41
|
+
actionLabel?: string
|
|
42
|
+
actionVariant?: React.ComponentProps<typeof Button>["variant"]
|
|
43
|
+
onAction?: () => void
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function FeatureCard({
|
|
47
|
+
title,
|
|
48
|
+
description,
|
|
49
|
+
icon,
|
|
50
|
+
color = "primary",
|
|
51
|
+
badge,
|
|
52
|
+
badgeVariant = "default",
|
|
53
|
+
actionLabel,
|
|
54
|
+
actionVariant = "outline",
|
|
55
|
+
onAction,
|
|
56
|
+
className,
|
|
57
|
+
...props
|
|
58
|
+
}: FeatureCardProps) {
|
|
59
|
+
const { bg, icon: iconColor } = colorTokens[color]
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Card
|
|
63
|
+
className={cn(
|
|
64
|
+
"flex flex-col h-full hover:shadow-md transition-shadow duration-200",
|
|
65
|
+
className,
|
|
66
|
+
)}
|
|
67
|
+
{...props}
|
|
68
|
+
>
|
|
69
|
+
<CardHeader>
|
|
70
|
+
<div className="flex items-center gap-3">
|
|
71
|
+
<div className={cn("p-2 rounded-[var(--radius)] shrink-0", bg)}>
|
|
72
|
+
<div className={cn("size-6 flex items-center justify-center [&>svg]:size-6", iconColor)}>
|
|
73
|
+
{icon}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 min-w-0">
|
|
77
|
+
<CardTitle className="text-sm">{title}</CardTitle>
|
|
78
|
+
{badge && (
|
|
79
|
+
<Badge variant={badgeVariant} className="text-xs">
|
|
80
|
+
{badge}
|
|
81
|
+
</Badge>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</CardHeader>
|
|
86
|
+
|
|
87
|
+
<CardContent className="flex-1">
|
|
88
|
+
<CardDescription className="text-sm leading-relaxed">{description}</CardDescription>
|
|
89
|
+
</CardContent>
|
|
90
|
+
|
|
91
|
+
{actionLabel && (
|
|
92
|
+
<CardFooter>
|
|
93
|
+
<Button variant={actionVariant} className="w-full" onClick={onAction}>
|
|
94
|
+
{actionLabel}
|
|
95
|
+
</Button>
|
|
96
|
+
</CardFooter>
|
|
97
|
+
)}
|
|
98
|
+
</Card>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Card, CardHeader, CardTitle, CardContent, CardFooter, CardAction } from "../../ui/card"
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar"
|
|
6
|
+
import { Badge } from "../../ui/badge"
|
|
7
|
+
import { Button } from "../../ui/button"
|
|
8
|
+
import { cn } from "../../shared/utils"
|
|
9
|
+
|
|
10
|
+
export type NotificationType = "info" | "warning" | "success" | "error" | "default"
|
|
11
|
+
|
|
12
|
+
export interface NotificationItem {
|
|
13
|
+
id: string
|
|
14
|
+
title: string
|
|
15
|
+
message: string
|
|
16
|
+
time: string
|
|
17
|
+
read?: boolean
|
|
18
|
+
type?: NotificationType
|
|
19
|
+
user?: { name: string; initials: string; avatar?: string }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface NotificationCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
23
|
+
title?: string
|
|
24
|
+
items: NotificationItem[]
|
|
25
|
+
unreadCount?: number
|
|
26
|
+
onMarkAllRead?: () => void
|
|
27
|
+
onViewAll?: () => void
|
|
28
|
+
maxItems?: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const typeVariant: Record<NotificationType, React.ComponentProps<typeof Badge>["variant"]> = {
|
|
32
|
+
info: "info",
|
|
33
|
+
warning: "warning",
|
|
34
|
+
success: "success",
|
|
35
|
+
error: "destructive",
|
|
36
|
+
default: "secondary",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function NotificationCard({
|
|
40
|
+
title = "Notificações",
|
|
41
|
+
items,
|
|
42
|
+
unreadCount,
|
|
43
|
+
onMarkAllRead,
|
|
44
|
+
onViewAll,
|
|
45
|
+
maxItems = 4,
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}: NotificationCardProps) {
|
|
49
|
+
const visible = items.slice(0, maxItems)
|
|
50
|
+
const count = unreadCount ?? items.filter((i) => !i.read).length
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Card className={cn("w-full", className)} {...props}>
|
|
54
|
+
<CardHeader>
|
|
55
|
+
<div className="flex items-center gap-2">
|
|
56
|
+
<CardTitle>{title}</CardTitle>
|
|
57
|
+
{count > 0 && (
|
|
58
|
+
<Badge variant="destructive" className="h-5 text-[11px] px-1.5">{count}</Badge>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
{onMarkAllRead && count > 0 && (
|
|
62
|
+
<CardAction>
|
|
63
|
+
<Button variant="ghost" size="sm" className="text-xs h-7" onClick={onMarkAllRead}>
|
|
64
|
+
Marcar todas como lidas
|
|
65
|
+
</Button>
|
|
66
|
+
</CardAction>
|
|
67
|
+
)}
|
|
68
|
+
</CardHeader>
|
|
69
|
+
|
|
70
|
+
<CardContent className="px-0 pb-0">
|
|
71
|
+
<ul className="divide-y divide-border">
|
|
72
|
+
{visible.map((item) => (
|
|
73
|
+
<li
|
|
74
|
+
key={item.id}
|
|
75
|
+
className={cn(
|
|
76
|
+
"flex items-start gap-3 px-6 py-3 transition-colors",
|
|
77
|
+
!item.read && "bg-muted/40",
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
{item.user ? (
|
|
81
|
+
<Avatar className="size-8 shrink-0 mt-0.5">
|
|
82
|
+
{item.user.avatar && <AvatarImage src={item.user.avatar} alt={item.user.name} />}
|
|
83
|
+
<AvatarFallback className="text-xs">{item.user.initials}</AvatarFallback>
|
|
84
|
+
</Avatar>
|
|
85
|
+
) : (
|
|
86
|
+
<div className="size-8 shrink-0 mt-0.5 rounded-full bg-muted flex items-center justify-center">
|
|
87
|
+
<div className={cn(
|
|
88
|
+
"size-2 rounded-full",
|
|
89
|
+
item.type === "error" ? "bg-destructive" :
|
|
90
|
+
item.type === "warning" ? "bg-warning" :
|
|
91
|
+
item.type === "success" ? "bg-success" :
|
|
92
|
+
item.type === "info" ? "bg-info" : "bg-muted-foreground",
|
|
93
|
+
)} />
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
<div className="flex-1 min-w-0">
|
|
98
|
+
<div className="flex items-center justify-between gap-2">
|
|
99
|
+
<p className="text-sm font-medium truncate">{item.title}</p>
|
|
100
|
+
{item.type && item.type !== "default" && (
|
|
101
|
+
<Badge variant={typeVariant[item.type]} className="text-[10px] px-1.5 h-4 shrink-0">
|
|
102
|
+
{item.type}
|
|
103
|
+
</Badge>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
<p className="text-xs text-muted-foreground line-clamp-2 mt-0.5">{item.message}</p>
|
|
107
|
+
<p className="text-[11px] text-muted-foreground/70 mt-1">{item.time}</p>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{!item.read && (
|
|
111
|
+
<div className="size-2 rounded-full bg-primary shrink-0 mt-2" />
|
|
112
|
+
)}
|
|
113
|
+
</li>
|
|
114
|
+
))}
|
|
115
|
+
</ul>
|
|
116
|
+
</CardContent>
|
|
117
|
+
|
|
118
|
+
{onViewAll && (
|
|
119
|
+
<CardFooter className="pt-2">
|
|
120
|
+
<Button variant="ghost" size="sm" className="w-full text-xs" onClick={onViewAll}>
|
|
121
|
+
Ver todas as notificações
|
|
122
|
+
</Button>
|
|
123
|
+
</CardFooter>
|
|
124
|
+
)}
|
|
125
|
+
</Card>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Card, CardContent } from "../../ui/card"
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar"
|
|
6
|
+
import { Badge } from "../../ui/badge"
|
|
7
|
+
import { Button } from "../../ui/button"
|
|
8
|
+
import { cn } from "../../shared/utils"
|
|
9
|
+
|
|
10
|
+
export interface ProfileCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
11
|
+
name: string
|
|
12
|
+
role: string
|
|
13
|
+
department?: string
|
|
14
|
+
avatar?: string
|
|
15
|
+
initials: string
|
|
16
|
+
status?: "online" | "offline" | "away" | "busy"
|
|
17
|
+
stats?: Array<{ label: string; value: string | number }>
|
|
18
|
+
primaryAction?: { label: string; onClick?: () => void }
|
|
19
|
+
secondaryAction?: { label: string; onClick?: () => void }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const statusConfig = {
|
|
23
|
+
online: { label: "Online", variant: "success" as const },
|
|
24
|
+
offline: { label: "Offline", variant: "secondary" as const },
|
|
25
|
+
away: { label: "Ausente", variant: "warning" as const },
|
|
26
|
+
busy: { label: "Ocupado", variant: "destructive" as const },
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function ProfileCard({
|
|
30
|
+
name, role, department, avatar, initials, status,
|
|
31
|
+
stats, primaryAction, secondaryAction, className, ...props
|
|
32
|
+
}: ProfileCardProps) {
|
|
33
|
+
const s = status ? statusConfig[status] : null
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Card className={cn("w-full", className)} {...props}>
|
|
37
|
+
<CardContent className="pt-6 flex flex-col items-center text-center gap-3">
|
|
38
|
+
<div className="relative">
|
|
39
|
+
<Avatar className="size-16">
|
|
40
|
+
{avatar && <AvatarImage src={avatar} alt={name} />}
|
|
41
|
+
<AvatarFallback className="text-lg font-semibold">{initials}</AvatarFallback>
|
|
42
|
+
</Avatar>
|
|
43
|
+
{s && (
|
|
44
|
+
<span className="absolute -bottom-0.5 -right-0.5">
|
|
45
|
+
<Badge variant={s.variant} className="text-[10px] px-1.5 py-0 h-5">{s.label}</Badge>
|
|
46
|
+
</span>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div className="space-y-0.5">
|
|
51
|
+
<p className="font-semibold text-base leading-tight">{name}</p>
|
|
52
|
+
<p className="text-sm text-muted-foreground">{role}</p>
|
|
53
|
+
{department && <p className="text-xs text-muted-foreground">{department}</p>}
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
{stats && stats.length > 0 && (
|
|
57
|
+
<div className="w-full grid grid-cols-3 divide-x divide-border border-y border-border py-3 mt-1">
|
|
58
|
+
{stats.map((stat) => (
|
|
59
|
+
<div key={stat.label} className="flex flex-col items-center gap-0.5 px-2">
|
|
60
|
+
<span className="text-base font-bold">{stat.value}</span>
|
|
61
|
+
<span className="text-[11px] text-muted-foreground">{stat.label}</span>
|
|
62
|
+
</div>
|
|
63
|
+
))}
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{(primaryAction || secondaryAction) && (
|
|
68
|
+
<div className="flex gap-2 w-full">
|
|
69
|
+
{secondaryAction && (
|
|
70
|
+
<Button variant="outline" className="flex-1" size="sm" onClick={secondaryAction.onClick}>
|
|
71
|
+
{secondaryAction.label}
|
|
72
|
+
</Button>
|
|
73
|
+
)}
|
|
74
|
+
{primaryAction && (
|
|
75
|
+
<Button className="flex-1" size="sm" onClick={primaryAction.onClick}>
|
|
76
|
+
{primaryAction.label}
|
|
77
|
+
</Button>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
</CardContent>
|
|
82
|
+
</Card>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, CardAction } from "../../ui/card"
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar"
|
|
6
|
+
import { Badge } from "../../ui/badge"
|
|
7
|
+
import { Progress } from "../../ui/progress"
|
|
8
|
+
import { cn } from "../../shared/utils"
|
|
9
|
+
import type { ProgressVariant } from "../../ui/progress"
|
|
10
|
+
|
|
11
|
+
export type ProjectStatus = "active" | "review" | "done" | "paused" | "at-risk"
|
|
12
|
+
|
|
13
|
+
export interface ProjectMember {
|
|
14
|
+
name: string
|
|
15
|
+
initials: string
|
|
16
|
+
avatar?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ProjectCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
20
|
+
title: string
|
|
21
|
+
description?: string
|
|
22
|
+
status: ProjectStatus
|
|
23
|
+
progress: number
|
|
24
|
+
dueDate?: string
|
|
25
|
+
members?: ProjectMember[]
|
|
26
|
+
maxMembers?: number
|
|
27
|
+
action?: React.ReactNode
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const statusConfig: Record<ProjectStatus, { label: string; variant: React.ComponentProps<typeof Badge>["variant"]; progressVariant: ProgressVariant }> = {
|
|
31
|
+
active: { label: "Em andamento", variant: "info", progressVariant: "info" },
|
|
32
|
+
review: { label: "Em revisão", variant: "warning", progressVariant: "warning" },
|
|
33
|
+
done: { label: "Concluído", variant: "success", progressVariant: "success" },
|
|
34
|
+
paused: { label: "Pausado", variant: "secondary", progressVariant: "default" },
|
|
35
|
+
"at-risk":{ label: "Em risco", variant: "destructive", progressVariant: "destructive" },
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function ProjectCard({
|
|
39
|
+
title, description, status, progress, dueDate,
|
|
40
|
+
members = [], maxMembers = 4, action, className, ...props
|
|
41
|
+
}: ProjectCardProps) {
|
|
42
|
+
const { label, variant, progressVariant } = statusConfig[status]
|
|
43
|
+
const visible = members.slice(0, maxMembers)
|
|
44
|
+
const overflow = members.length - maxMembers
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Card className={cn("w-full", className)} {...props}>
|
|
48
|
+
<CardHeader>
|
|
49
|
+
<div className="flex flex-col gap-1 min-w-0">
|
|
50
|
+
<CardTitle className="truncate">{title}</CardTitle>
|
|
51
|
+
{description && <CardDescription className="line-clamp-2">{description}</CardDescription>}
|
|
52
|
+
</div>
|
|
53
|
+
<CardAction className="flex items-center gap-2">
|
|
54
|
+
<Badge variant={variant}>{label}</Badge>
|
|
55
|
+
{action}
|
|
56
|
+
</CardAction>
|
|
57
|
+
</CardHeader>
|
|
58
|
+
|
|
59
|
+
<CardContent className="space-y-4">
|
|
60
|
+
<div className="space-y-1.5">
|
|
61
|
+
<div className="flex justify-between text-xs text-muted-foreground">
|
|
62
|
+
<span>Progresso</span>
|
|
63
|
+
<span>{progress}%</span>
|
|
64
|
+
</div>
|
|
65
|
+
<Progress value={progress} variant={progressVariant} className="h-2" />
|
|
66
|
+
</div>
|
|
67
|
+
</CardContent>
|
|
68
|
+
|
|
69
|
+
<CardFooter className="flex items-center justify-between">
|
|
70
|
+
<div className="flex -space-x-2">
|
|
71
|
+
{visible.map((m) => (
|
|
72
|
+
<Avatar key={m.name} className="size-7 border-2 border-background">
|
|
73
|
+
{m.avatar && <AvatarImage src={m.avatar} alt={m.name} />}
|
|
74
|
+
<AvatarFallback className="text-[10px]">{m.initials}</AvatarFallback>
|
|
75
|
+
</Avatar>
|
|
76
|
+
))}
|
|
77
|
+
{overflow > 0 && (
|
|
78
|
+
<div className="size-7 rounded-full border-2 border-background bg-muted flex items-center justify-center text-[10px] font-medium text-muted-foreground">
|
|
79
|
+
+{overflow}
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
{dueDate && (
|
|
84
|
+
<span className="text-xs text-muted-foreground">{dueDate}</span>
|
|
85
|
+
)}
|
|
86
|
+
</CardFooter>
|
|
87
|
+
</Card>
|
|
88
|
+
)
|
|
89
|
+
}
|