xertica-ui 2.1.5 → 2.1.7
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 +28 -0
- package/README.md +1 -1
- package/bin/cli.ts +1 -1
- package/dist/cli.js +1 -1
- package/package.json +1 -1
- package/templates/package.json +2 -2
- package/templates/src/features/home/ui/HomeContent.tsx +6 -1
- package/templates/src/features/settings/ui/SettingsContent.tsx +5 -9
- package/templates/src/features/template/ui/DashboardTemplate.tsx +33 -21
- package/templates/src/features/template/ui/TemplateContent.tsx +10 -20
- package/templates/src/pages/AssistantPage.tsx +3 -2
- package/templates/src/pages/HomePage.tsx +1 -1
- package/templates/src/pages/TemplatePage.tsx +21 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [2.1.7] — 2026-05-19
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`templates/TemplateContent.tsx` — wrapper raiz não era SPA-router-aware** — o `paddingLeft` manual foi substituído por `<PageContent>` (wrapper compartilhado de `shared/ui/PageContent.tsx`) eliminando código duplicado e garantindo que a lógica de offset do sidebar fique em um único lugar.
|
|
15
|
+
- **`templates/TemplateContent.tsx` — `renderLink` tipado como `any`** — corrigido para `Record<string, unknown>` prevenindo erros silenciosos de tipos no spread `{...props}`.
|
|
16
|
+
- **`templates/TemplateContent.tsx` — imports mortos removidos** — `Menu`, `ChevronRight`, `Trash2`, `Archive`, `ArrowRightLeft`, `History`, `FileEdit`, `Filter`, `Map` estavam no import mas não eram usados (ou eram usados mas faltavam). Sincronizado imports com uso real.
|
|
17
|
+
- **`templates/TemplateContent.tsx` — `<h2>` raw na página** — substituído por `<PageHeader title=... subtitle=... />` para consistência com as regras do projeto.
|
|
18
|
+
- **`templates/TemplatePage.tsx` — `user` tipado como `{ email: string }` em vez de `User`** — tipo corrigido para `User | null`; `onEvaluation` migrado de `console.log` para `toast.success/info`; componente agora usa `generateDemoResponse`, `richSuggestions`, `feedbackOptions` e `userName` dinâmico.
|
|
19
|
+
- **`templates/HomePage.tsx` + `templates/AssistantPage.tsx` — `user` tipado como `{ email: string }`** — tipo corrigido para `User | null` em ambas as páginas.
|
|
20
|
+
- **`templates/AssistantPage.tsx` — `userName="Ariel Santos"` hardcoded** — substituído por `user?.name ?? 'Usuário'` para usar o dado real do usuário autenticado.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## [2.1.6] — 2026-05-19
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **`templates/HomeContent.tsx` — JSX malformado causava crash da página** — tags `</div>`, `</ScrollArea>` e `</main>` estavam faltando após refatoração para `<PageContent>`, fazendo a página Home quebrar completamente ao renderizar.
|
|
29
|
+
- **`templates/DashboardTemplate.tsx` — `StatsCard.trend` com tipo errado** — `trend` recebia um `number` primitivo (`trend={20.1}`) mas o prop exige um objeto `{ value: number; label?: string }`. Corrigido para `trend={{ value: 20.1, label: 'vs mês anterior' }}` em todos os 4 cards.
|
|
30
|
+
- **`templates/DashboardTemplate.tsx` — `DonutBreakdownChart` com chaves acentuadas** — o `channelConfig` usava chaves como `Orgânico` que falhavam no lookup interno do chart. Renomeadas para ASCII (`organico`, `pago`, `social`, `direto`) com labels legíveis mantidas via `label:`.
|
|
31
|
+
- **`templates/DashboardTemplate.tsx` — cast de `Badge.variant` sem type safety** — `event.status as 'default' | ...` foi substituído por um mapa `EVENT_BADGE_VARIANT` com tipagem `EventStatus` explícita.
|
|
32
|
+
- **`templates/SettingsContent.tsx` — imports duplicados de `xertica-ui/hooks`** — `useTheme` e `useLayout` eram importados em duas linhas separadas do mesmo módulo. Unificados em um único import.
|
|
33
|
+
- **`templates/SettingsContent.tsx` — `Separator`, `Badge` e `useNavigate` importados mas nunca usados** — removidos para evitar erros de lint/build.
|
|
34
|
+
- **`templates/SettingsContent.tsx` — `Switch.onCheckedChange` com tipo incompatível** — `toggleTheme` é `() => void` mas `onCheckedChange` exige `(checked: boolean) => void`. Corrigido com wrapper arrow `() => toggleTheme()`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
10
38
|
## [2.1.5] — 2026-05-19
|
|
11
39
|
|
|
12
40
|
### Fixed
|
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
|
---
|
package/bin/cli.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -534,7 +534,7 @@ var generateTokensCss = (theme) => {
|
|
|
534
534
|
var __filename = fileURLToPath(import.meta.url);
|
|
535
535
|
var __dirname = path.dirname(__filename);
|
|
536
536
|
var program = new Command();
|
|
537
|
-
program.name("xertica-ui").description("CLI to initialize Xertica UI projects").version("2.1.
|
|
537
|
+
program.name("xertica-ui").description("CLI to initialize Xertica UI projects").version("2.1.7");
|
|
538
538
|
program.command("init").description("Initialize a new Xertica UI project").argument("[directory]", "Directory to initialize in", ".").action(async (directory) => {
|
|
539
539
|
const targetDir = path.resolve(process.cwd(), directory);
|
|
540
540
|
const templatesDir = path.resolve(__dirname, "../templates");
|
package/package.json
CHANGED
package/templates/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xertica-ui-template",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"react-hook-form": "^7.54.2",
|
|
20
20
|
"react-router-dom": "^7.1.3",
|
|
21
21
|
"sonner": "^1.7.3",
|
|
22
|
-
"xertica-ui": "^2.1.
|
|
22
|
+
"xertica-ui": "^2.1.7",
|
|
23
23
|
"zod": "^3.24.1"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
@@ -9,9 +9,8 @@ import {
|
|
|
9
9
|
Button,
|
|
10
10
|
Input,
|
|
11
11
|
Label,
|
|
12
|
-
Separator,
|
|
13
12
|
Switch,
|
|
14
|
-
|
|
13
|
+
ScrollArea,
|
|
15
14
|
Form,
|
|
16
15
|
FormField,
|
|
17
16
|
FormItem,
|
|
@@ -20,15 +19,12 @@ import {
|
|
|
20
19
|
FormDescription,
|
|
21
20
|
FormMessage,
|
|
22
21
|
} from 'xertica-ui/ui';
|
|
23
|
-
import {
|
|
22
|
+
import { Header } from 'xertica-ui/layout';
|
|
23
|
+
import { useLayout, useTheme } from 'xertica-ui/hooks';
|
|
24
24
|
import { useForm } from 'react-hook-form';
|
|
25
25
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
26
26
|
import { z } from 'zod';
|
|
27
27
|
import { toast } from 'sonner';
|
|
28
|
-
import { useNavigate } from 'react-router-dom';
|
|
29
|
-
import { useLayout } from 'xertica-ui/hooks';
|
|
30
|
-
import { Header } from 'xertica-ui/layout';
|
|
31
|
-
import { ScrollArea } from 'xertica-ui/ui';
|
|
32
28
|
import { Link } from 'react-router-dom';
|
|
33
29
|
import type { User } from '../../../shared/types/auth';
|
|
34
30
|
|
|
@@ -53,7 +49,6 @@ interface SettingsContentProps {
|
|
|
53
49
|
export function SettingsContent({ user, onLogout }: SettingsContentProps) {
|
|
54
50
|
const { sidebarExpanded, sidebarWidth } = useLayout();
|
|
55
51
|
const { theme, toggleTheme } = useTheme();
|
|
56
|
-
const navigate = useNavigate();
|
|
57
52
|
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
|
|
58
53
|
|
|
59
54
|
const form = useForm<ProfileValues>({
|
|
@@ -157,9 +152,10 @@ export function SettingsContent({ user, onLogout }: SettingsContentProps) {
|
|
|
157
152
|
Alterna entre tema claro e escuro.
|
|
158
153
|
</p>
|
|
159
154
|
</div>
|
|
155
|
+
{/* toggleTheme is () => void; Switch.onCheckedChange expects (checked: boolean) => void */}
|
|
160
156
|
<Switch
|
|
161
157
|
checked={theme === 'dark'}
|
|
162
|
-
onCheckedChange={toggleTheme}
|
|
158
|
+
onCheckedChange={() => toggleTheme()}
|
|
163
159
|
/>
|
|
164
160
|
</div>
|
|
165
161
|
</CardContent>
|
|
@@ -45,18 +45,28 @@ const accessConfig = {
|
|
|
45
45
|
acessos: { label: 'Acessos', color: 'var(--chart-2)' },
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
// Keys without accents so the chart's internal config lookup is reliable
|
|
48
49
|
const channelData = [
|
|
49
|
-
{ name: '
|
|
50
|
-
{ name: '
|
|
51
|
-
{ name: '
|
|
52
|
-
{ name: '
|
|
50
|
+
{ name: 'organico', value: 42 },
|
|
51
|
+
{ name: 'pago', value: 28 },
|
|
52
|
+
{ name: 'social', value: 18 },
|
|
53
|
+
{ name: 'direto', value: 12 },
|
|
53
54
|
];
|
|
54
55
|
|
|
55
56
|
const channelConfig = {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
organico: { label: 'Orgânico', color: 'var(--chart-1)' },
|
|
58
|
+
pago: { label: 'Pago', color: 'var(--chart-2)' },
|
|
59
|
+
social: { label: 'Social', color: 'var(--chart-3)' },
|
|
60
|
+
direto: { label: 'Direto', color: 'var(--chart-4)' },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Explicit Badge variant map — avoids unsafe string casts
|
|
64
|
+
type EventStatus = 'info' | 'success' | 'destructive' | 'default';
|
|
65
|
+
const EVENT_BADGE_VARIANT: Record<EventStatus, EventStatus> = {
|
|
66
|
+
info: 'info',
|
|
67
|
+
success: 'success',
|
|
68
|
+
destructive: 'destructive',
|
|
69
|
+
default: 'default',
|
|
60
70
|
};
|
|
61
71
|
|
|
62
72
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
@@ -75,30 +85,30 @@ export function DashboardTemplate() {
|
|
|
75
85
|
}
|
|
76
86
|
/>
|
|
77
87
|
|
|
78
|
-
{/* ── KPI cards —
|
|
88
|
+
{/* ── KPI cards — StatsCard.trend expects { value: number } not a raw number ── */}
|
|
79
89
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
80
90
|
<StatsCard
|
|
81
91
|
title="Receita Total"
|
|
82
92
|
value="R$ 45.231,89"
|
|
83
|
-
trend={20.1}
|
|
93
|
+
trend={{ value: 20.1, label: 'vs mês anterior' }}
|
|
84
94
|
icon={<DollarSign className="size-4" />}
|
|
85
95
|
/>
|
|
86
96
|
<StatsCard
|
|
87
97
|
title="Usuários Ativos"
|
|
88
98
|
value="2.350"
|
|
89
|
-
trend={8.4}
|
|
99
|
+
trend={{ value: 8.4, label: 'vs mês anterior' }}
|
|
90
100
|
icon={<Users className="size-4" />}
|
|
91
101
|
/>
|
|
92
102
|
<StatsCard
|
|
93
103
|
title="Vendas"
|
|
94
104
|
value="12.234"
|
|
95
|
-
trend={19.2}
|
|
105
|
+
trend={{ value: 19.2, label: 'vs mês anterior' }}
|
|
96
106
|
icon={<Activity className="size-4" />}
|
|
97
107
|
/>
|
|
98
108
|
<StatsCard
|
|
99
109
|
title="Taxa de Retenção"
|
|
100
110
|
value="89,3%"
|
|
101
|
-
trend={1.2}
|
|
111
|
+
trend={{ value: 1.2, label: 'vs mês anterior' }}
|
|
102
112
|
icon={<TrendingUp className="size-4" />}
|
|
103
113
|
/>
|
|
104
114
|
</div>
|
|
@@ -165,17 +175,19 @@ export function DashboardTemplate() {
|
|
|
165
175
|
</CardHeader>
|
|
166
176
|
<CardContent>
|
|
167
177
|
<div className="space-y-4">
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
{(
|
|
179
|
+
[
|
|
180
|
+
{ time: '09:00', label: 'Login de João Silva', status: 'info' },
|
|
181
|
+
{ time: '10:45', label: 'Fatura #INV004 paga', status: 'success' },
|
|
182
|
+
{ time: '11:30', label: 'Usuário bloqueado', status: 'destructive' },
|
|
183
|
+
{ time: '13:15', label: 'Relatório gerado', status: 'default' },
|
|
184
|
+
{ time: '15:00', label: 'Deploy v2.1.5', status: 'success' },
|
|
185
|
+
] as Array<{ time: string; label: string; status: EventStatus }>
|
|
186
|
+
).map(event => (
|
|
175
187
|
<div key={event.time} className="flex items-center gap-3 text-sm">
|
|
176
188
|
<Badge variant="outline" className="font-mono shrink-0">{event.time}</Badge>
|
|
177
189
|
<span className="flex-1 text-muted-foreground">{event.label}</span>
|
|
178
|
-
<Badge variant={event.status
|
|
190
|
+
<Badge variant={EVENT_BADGE_VARIANT[event.status]} className="text-[10px]">
|
|
179
191
|
{event.status}
|
|
180
192
|
</Badge>
|
|
181
193
|
</div>
|
|
@@ -59,6 +59,7 @@ import { Header, Sidebar } from 'xertica-ui/layout';
|
|
|
59
59
|
import { useLayout } from 'xertica-ui/hooks';
|
|
60
60
|
import { toast } from 'sonner';
|
|
61
61
|
import { Link } from 'react-router-dom';
|
|
62
|
+
import { PageContent } from '../../../shared/ui/PageContent';
|
|
62
63
|
import {
|
|
63
64
|
Settings,
|
|
64
65
|
User,
|
|
@@ -67,23 +68,19 @@ import {
|
|
|
67
68
|
Calendar,
|
|
68
69
|
Search,
|
|
69
70
|
PanelLeft,
|
|
70
|
-
Menu,
|
|
71
|
-
ChevronRight,
|
|
72
71
|
Home,
|
|
73
72
|
Users,
|
|
74
73
|
Plus,
|
|
74
|
+
Clock,
|
|
75
75
|
Trash2,
|
|
76
76
|
Archive,
|
|
77
77
|
ArrowRightLeft,
|
|
78
|
-
History,
|
|
79
78
|
FileEdit,
|
|
80
|
-
Filter,
|
|
81
|
-
Clock,
|
|
82
79
|
Map,
|
|
83
80
|
} from 'lucide-react';
|
|
84
81
|
|
|
85
82
|
export function TemplateContent() {
|
|
86
|
-
const {
|
|
83
|
+
const { sidebarWidth } = useLayout();
|
|
87
84
|
const [progress, setProgress] = useState(45);
|
|
88
85
|
const [sliderValue, setSliderValue] = useState([50]);
|
|
89
86
|
const [switchEnabled, setSwitchEnabled] = useState(false);
|
|
@@ -94,12 +91,7 @@ export function TemplateContent() {
|
|
|
94
91
|
};
|
|
95
92
|
|
|
96
93
|
return (
|
|
97
|
-
<
|
|
98
|
-
style={{
|
|
99
|
-
paddingLeft: sidebarExpanded ? `${sidebarWidth}px` : '80px',
|
|
100
|
-
}}
|
|
101
|
-
className="flex-1 flex flex-col overflow-hidden transition-all duration-300"
|
|
102
|
-
>
|
|
94
|
+
<PageContent>
|
|
103
95
|
<Header
|
|
104
96
|
showThemeToggle={true}
|
|
105
97
|
showLanguageSelector={true}
|
|
@@ -107,19 +99,17 @@ export function TemplateContent() {
|
|
|
107
99
|
{ label: 'Design System', href: '/home' },
|
|
108
100
|
{ label: 'Página de Template' },
|
|
109
101
|
]}
|
|
110
|
-
renderLink={(href: string, props:
|
|
102
|
+
renderLink={(href: string, props: Record<string, unknown>) => <Link to={href} {...props} />}
|
|
111
103
|
/>
|
|
112
104
|
|
|
113
105
|
<main className="flex-1 overflow-hidden bg-muted">
|
|
114
106
|
<ScrollArea className="h-full">
|
|
115
107
|
<div className="p-2 sm:p-4 md:p-6">
|
|
116
108
|
<div className="max-w-6xl mx-auto space-y-8">
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
</p>
|
|
122
|
-
</div>
|
|
109
|
+
<PageHeader
|
|
110
|
+
title="Template Page"
|
|
111
|
+
subtitle="Template completo com componentes do Xertica UI Design System"
|
|
112
|
+
/>
|
|
123
113
|
|
|
124
114
|
{/* Alert Examples */}
|
|
125
115
|
<section>
|
|
@@ -865,6 +855,6 @@ export function TemplateContent() {
|
|
|
865
855
|
</div>
|
|
866
856
|
</ScrollArea>
|
|
867
857
|
</main>
|
|
868
|
-
</
|
|
858
|
+
</PageContent>
|
|
869
859
|
);
|
|
870
860
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useMemo, useEffect } from 'react';
|
|
2
2
|
import { toast } from 'sonner';
|
|
3
3
|
import { useNavigate, Link } from 'react-router-dom';
|
|
4
|
+
import type { User } from '../shared/types/auth';
|
|
4
5
|
import { XerticaAssistant, generateDemoResponse } from 'xertica-ui/assistant';
|
|
5
6
|
import { useLayout } from 'xertica-ui/hooks';
|
|
6
7
|
import { Header } from 'xertica-ui/layout';
|
|
@@ -140,7 +141,7 @@ const GROUP_CONFIG = [
|
|
|
140
141
|
// ─── Component ───────────────────────────────────────────────────────────────
|
|
141
142
|
|
|
142
143
|
interface AssistantPageProps {
|
|
143
|
-
user:
|
|
144
|
+
user: User | null;
|
|
144
145
|
onLogout: () => void;
|
|
145
146
|
}
|
|
146
147
|
|
|
@@ -271,7 +272,7 @@ export function AssistantPage({ user, onLogout }: AssistantPageProps) {
|
|
|
271
272
|
key={selectedId ?? 'new'}
|
|
272
273
|
mode="fullPage"
|
|
273
274
|
demoMode={true}
|
|
274
|
-
userName=
|
|
275
|
+
userName={user?.name ?? 'Usuário'}
|
|
275
276
|
responseGenerator={generateDemoResponse}
|
|
276
277
|
initialMessages={selectedConversation?.messages as any}
|
|
277
278
|
richSuggestions={richSuggestions}
|
|
@@ -8,7 +8,7 @@ import { HomeContent } from '../features/home';
|
|
|
8
8
|
import { richSuggestions, feedbackOptions } from '../shared/config/assistant';
|
|
9
9
|
|
|
10
10
|
interface HomePageProps {
|
|
11
|
-
user:
|
|
11
|
+
user: import('../shared/types/auth').User | null;
|
|
12
12
|
onLogout: () => void;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { XerticaAssistant } from 'xertica-ui/assistant';
|
|
2
|
+
import { XerticaAssistant, generateDemoResponse } from 'xertica-ui/assistant';
|
|
3
3
|
import { useLayout } from 'xertica-ui/hooks';
|
|
4
|
+
import { useNavigate } from 'react-router-dom';
|
|
5
|
+
import { toast } from 'sonner';
|
|
4
6
|
import { AppLayout } from '../app/components/AppLayout';
|
|
5
7
|
import { TemplateContent } from '../features/template';
|
|
8
|
+
import { richSuggestions, feedbackOptions } from '../shared/config/assistant';
|
|
9
|
+
import type { User } from '../shared/types/auth';
|
|
6
10
|
|
|
7
11
|
interface TemplatePageProps {
|
|
8
|
-
user:
|
|
12
|
+
user: User | null;
|
|
9
13
|
onLogout: () => void;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export function TemplatePage({ user, onLogout }: TemplatePageProps) {
|
|
13
17
|
const { assistenteExpanded, toggleAssistente } = useLayout();
|
|
18
|
+
const navigate = useNavigate();
|
|
14
19
|
|
|
15
20
|
return (
|
|
16
21
|
<AppLayout
|
|
@@ -20,7 +25,20 @@ export function TemplatePage({ user, onLogout }: TemplatePageProps) {
|
|
|
20
25
|
<XerticaAssistant
|
|
21
26
|
isExpanded={assistenteExpanded}
|
|
22
27
|
onToggle={toggleAssistente}
|
|
23
|
-
|
|
28
|
+
defaultTab="chat"
|
|
29
|
+
demoMode={true}
|
|
30
|
+
userName={user?.name ?? 'Usuário'}
|
|
31
|
+
responseGenerator={generateDemoResponse}
|
|
32
|
+
richSuggestions={richSuggestions}
|
|
33
|
+
feedbackOptions={feedbackOptions}
|
|
34
|
+
showHistory={false}
|
|
35
|
+
showFavorites={false}
|
|
36
|
+
onNavigateSettings={() => navigate('/settings')}
|
|
37
|
+
onNavigateFullPage={() => navigate('/assistente')}
|
|
38
|
+
onEvaluation={(_messageId, type, _reason) => {
|
|
39
|
+
if (type === 'like') toast.success('Obrigado pelo feedback positivo!');
|
|
40
|
+
else toast.info('Obrigado! Seu feedback nos ajuda a melhorar.');
|
|
41
|
+
}}
|
|
24
42
|
/>
|
|
25
43
|
}
|
|
26
44
|
>
|