xertica-ui 1.2.6 → 1.2.8
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/App.tsx +33 -30
- package/README.md +37 -0
- package/components/Header.tsx +58 -0
- package/components/HomeContent.tsx +16 -42
- package/components/HomePage.tsx +16 -42
- package/components/Sidebar.tsx +27 -17
- package/components/TemplateContent.tsx +6 -6
- package/components/TemplatePage.tsx +16 -42
- package/components/XerticaProvider.tsx +43 -0
- package/components/index.ts +18 -1
- package/contexts/LayoutContext.tsx +73 -0
- package/contexts/ThemeContext.tsx +19 -12
- package/contexts/index.ts +7 -0
- package/dist/components/Header.d.ts +8 -0
- package/dist/components/HomeContent.d.ts +1 -5
- package/dist/components/HomePage.d.ts +6 -0
- package/dist/components/Sidebar.d.ts +9 -1
- package/dist/components/TemplateContent.d.ts +1 -5
- package/dist/components/TemplatePage.d.ts +6 -0
- package/dist/components/XerticaProvider.d.ts +8 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/contexts/BrandColorsContext.d.ts +15 -0
- package/dist/contexts/LayoutContext.d.ts +15 -0
- package/dist/contexts/index.d.ts +7 -0
- package/dist/contexts/theme-data.d.ts +81 -0
- package/dist/index.es.js +2970 -245
- package/dist/index.umd.js +2967 -242
- package/package.json +6 -4
package/App.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { ThemeProvider } from './contexts/ThemeContext';
|
|
|
10
10
|
import { LanguageProvider } from './contexts/LanguageContext';
|
|
11
11
|
import { ApiKeyProvider } from './contexts/ApiKeyContext';
|
|
12
12
|
import { AssistenteProvider } from './contexts/AssistenteContext';
|
|
13
|
+
import { LayoutProvider } from './contexts/LayoutContext';
|
|
13
14
|
import { BrandColorsProvider } from './contexts/BrandColorsContext';
|
|
14
15
|
import { GoogleMapsLoaderProvider } from './components/ui/google-maps-loader';
|
|
15
16
|
import { Toaster } from './components/ui/sonner';
|
|
@@ -19,7 +20,7 @@ import { XerticaAssistant } from './components/ui/xertica-assistant';
|
|
|
19
20
|
if (typeof window !== 'undefined') {
|
|
20
21
|
// Remover classe dark imediatamente
|
|
21
22
|
document.documentElement.classList.remove('dark');
|
|
22
|
-
|
|
23
|
+
|
|
23
24
|
// Se não há tema salvo, definir light mode
|
|
24
25
|
const savedTheme = localStorage.getItem('xertica-theme');
|
|
25
26
|
if (!savedTheme) {
|
|
@@ -78,69 +79,69 @@ function AuthWrapper() {
|
|
|
78
79
|
return (
|
|
79
80
|
<div className="min-h-screen bg-muted overflow-x-hidden max-w-full">
|
|
80
81
|
<Routes>
|
|
81
|
-
<Route
|
|
82
|
-
path="/login"
|
|
82
|
+
<Route
|
|
83
|
+
path="/login"
|
|
83
84
|
element={
|
|
84
85
|
user ? (
|
|
85
86
|
<Navigate to="/home" replace />
|
|
86
87
|
) : (
|
|
87
88
|
<LoginPage onLogin={handleLogin} />
|
|
88
89
|
)
|
|
89
|
-
}
|
|
90
|
+
}
|
|
90
91
|
/>
|
|
91
|
-
<Route
|
|
92
|
-
path="/forgot-password"
|
|
92
|
+
<Route
|
|
93
|
+
path="/forgot-password"
|
|
93
94
|
element={
|
|
94
95
|
user ? (
|
|
95
96
|
<Navigate to="/home" replace />
|
|
96
97
|
) : (
|
|
97
98
|
<ForgotPasswordPage />
|
|
98
99
|
)
|
|
99
|
-
}
|
|
100
|
+
}
|
|
100
101
|
/>
|
|
101
|
-
<Route
|
|
102
|
-
path="/verify-email"
|
|
102
|
+
<Route
|
|
103
|
+
path="/verify-email"
|
|
103
104
|
element={
|
|
104
105
|
user ? (
|
|
105
106
|
<Navigate to="/home" replace />
|
|
106
107
|
) : (
|
|
107
108
|
<VerifyEmailPage />
|
|
108
109
|
)
|
|
109
|
-
}
|
|
110
|
+
}
|
|
110
111
|
/>
|
|
111
|
-
<Route
|
|
112
|
-
path="/reset-password"
|
|
112
|
+
<Route
|
|
113
|
+
path="/reset-password"
|
|
113
114
|
element={
|
|
114
115
|
user ? (
|
|
115
116
|
<Navigate to="/home" replace />
|
|
116
117
|
) : (
|
|
117
118
|
<ResetPasswordPage />
|
|
118
119
|
)
|
|
119
|
-
}
|
|
120
|
+
}
|
|
120
121
|
/>
|
|
121
|
-
<Route
|
|
122
|
-
path="/home"
|
|
122
|
+
<Route
|
|
123
|
+
path="/home"
|
|
123
124
|
element={
|
|
124
125
|
<ProtectedRoute user={user}>
|
|
125
126
|
<HomePage user={user} onLogout={handleLogout} />
|
|
126
127
|
</ProtectedRoute>
|
|
127
|
-
}
|
|
128
|
+
}
|
|
128
129
|
/>
|
|
129
|
-
<Route
|
|
130
|
-
path="/template"
|
|
130
|
+
<Route
|
|
131
|
+
path="/template"
|
|
131
132
|
element={
|
|
132
133
|
<ProtectedRoute user={user}>
|
|
133
134
|
<TemplatePage user={user} onLogout={handleLogout} />
|
|
134
135
|
</ProtectedRoute>
|
|
135
|
-
}
|
|
136
|
+
}
|
|
136
137
|
/>
|
|
137
|
-
<Route
|
|
138
|
-
path="/"
|
|
139
|
-
element={<Navigate to={user ? "/home" : "/login"} replace />}
|
|
138
|
+
<Route
|
|
139
|
+
path="/"
|
|
140
|
+
element={<Navigate to={user ? "/home" : "/login"} replace />}
|
|
140
141
|
/>
|
|
141
|
-
<Route
|
|
142
|
-
path="*"
|
|
143
|
-
element={<Navigate to={user ? "/home" : "/login"} replace />}
|
|
142
|
+
<Route
|
|
143
|
+
path="*"
|
|
144
|
+
element={<Navigate to={user ? "/home" : "/login"} replace />}
|
|
144
145
|
/>
|
|
145
146
|
</Routes>
|
|
146
147
|
|
|
@@ -154,7 +155,7 @@ export default function App() {
|
|
|
154
155
|
useLayoutEffect(() => {
|
|
155
156
|
const root = document.documentElement;
|
|
156
157
|
const savedTheme = localStorage.getItem('xertica-theme');
|
|
157
|
-
|
|
158
|
+
|
|
158
159
|
if (!savedTheme || savedTheme === 'light') {
|
|
159
160
|
root.classList.remove('dark');
|
|
160
161
|
localStorage.setItem('xertica-theme', 'light');
|
|
@@ -168,10 +169,12 @@ export default function App() {
|
|
|
168
169
|
<BrandColorsProvider>
|
|
169
170
|
<ThemeProvider>
|
|
170
171
|
<AssistenteProvider>
|
|
171
|
-
<
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
<LayoutProvider>
|
|
173
|
+
<Router>
|
|
174
|
+
<AuthWrapper />
|
|
175
|
+
<Toaster position="top-right" richColors />
|
|
176
|
+
</Router>
|
|
177
|
+
</LayoutProvider>
|
|
175
178
|
</AssistenteProvider>
|
|
176
179
|
</ThemeProvider>
|
|
177
180
|
</BrandColorsProvider>
|
package/README.md
CHANGED
|
@@ -39,6 +39,43 @@ O método recomendado é usar o CLI:
|
|
|
39
39
|
npx xertica-ui init meu-app
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
### Instalação como Biblioteca
|
|
43
|
+
|
|
44
|
+
Se você deseja usar os componentes em um projeto React existente:
|
|
45
|
+
|
|
46
|
+
1. Instale o pacote:
|
|
47
|
+
```bash
|
|
48
|
+
npm install xertica-ui
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. Importe o CSS (obrigatório) no seu arquivo `main.tsx` ou `App.tsx`:
|
|
52
|
+
```tsx
|
|
53
|
+
import 'xertica-ui/style.css';
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
3. Envolva sua aplicação com o `XerticaProvider`:
|
|
57
|
+
```tsx
|
|
58
|
+
import { XerticaProvider } from 'xertica-ui';
|
|
59
|
+
|
|
60
|
+
function App() {
|
|
61
|
+
return (
|
|
62
|
+
<XerticaProvider>
|
|
63
|
+
<SeuApp />
|
|
64
|
+
</XerticaProvider>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
4. Comece a usar os componentes:
|
|
70
|
+
```tsx
|
|
71
|
+
import { Button } from 'xertica-ui';
|
|
72
|
+
|
|
73
|
+
export function MeuComponente() {
|
|
74
|
+
return <Button>Clique aqui</Button>;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Desenvolvimento do Projeto (Contribuição)
|
|
42
79
|
Para desenvolvimento do próprio pacote/biblioteca:
|
|
43
80
|
1. Clone o repositório:
|
|
44
81
|
```bash
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Menu, ChevronRight } from 'lucide-react';
|
|
3
|
+
import { Button } from './ui/button';
|
|
4
|
+
import { ThemeToggle } from './ThemeToggle';
|
|
5
|
+
import { LanguageSelector } from './LanguageSelector';
|
|
6
|
+
|
|
7
|
+
import { useLayout } from '../contexts/LayoutContext';
|
|
8
|
+
|
|
9
|
+
interface HeaderProps {
|
|
10
|
+
title: string;
|
|
11
|
+
showLanguageSelector?: boolean;
|
|
12
|
+
showThemeToggle?: boolean;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Header({
|
|
17
|
+
title,
|
|
18
|
+
showLanguageSelector = true,
|
|
19
|
+
showThemeToggle = true,
|
|
20
|
+
className
|
|
21
|
+
}: HeaderProps) {
|
|
22
|
+
const { sidebarExpanded, toggleSidebar } = useLayout();
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<header className={`bg-card shadow-sm border-b border-border px-[24px] py-[14px] flex-shrink-0 ${className || ''}`}>
|
|
27
|
+
<div className="flex items-center justify-between p-[0px]">
|
|
28
|
+
<div className="flex items-center gap-2 text-muted-foreground">
|
|
29
|
+
{/* Botão menu para mobile */}
|
|
30
|
+
<Button
|
|
31
|
+
variant="ghost"
|
|
32
|
+
size="sm"
|
|
33
|
+
onClick={toggleSidebar}
|
|
34
|
+
className="md:hidden mr-2 p-2"
|
|
35
|
+
>
|
|
36
|
+
<Menu className="w-5 h-5" />
|
|
37
|
+
</Button>
|
|
38
|
+
<span className="text-primary font-medium">Xertica.ai</span>
|
|
39
|
+
<ChevronRight className="w-4 h-4" />
|
|
40
|
+
<span className="text-foreground font-medium">{title}</span>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
{/* Controles do usuário */}
|
|
44
|
+
<div className="flex items-center gap-3">
|
|
45
|
+
{/* Seletor de idioma */}
|
|
46
|
+
{showLanguageSelector && (
|
|
47
|
+
<LanguageSelector variant="minimal" showIcon={false} className="hidden sm:flex" />
|
|
48
|
+
)}
|
|
49
|
+
|
|
50
|
+
{/* Toggle de tema */}
|
|
51
|
+
{showThemeToggle && (
|
|
52
|
+
<ThemeToggle className="hover:bg-accent" />
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</header>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from './ui/card';
|
|
3
|
+
import { Header } from './Header';
|
|
3
4
|
import { Button } from './ui/button';
|
|
4
5
|
import { ChevronRight, Menu, FileText } from 'lucide-react';
|
|
5
6
|
import { ScrollArea } from './ui/scroll-area';
|
|
@@ -9,17 +10,17 @@ import { ThemeToggle } from './ThemeToggle';
|
|
|
9
10
|
import { LanguageSelector } from './LanguageSelector';
|
|
10
11
|
import { Badge } from './ui/badge';
|
|
11
12
|
|
|
13
|
+
import { useLayout } from '../contexts/LayoutContext';
|
|
14
|
+
|
|
12
15
|
interface HomeContentProps {
|
|
13
|
-
|
|
14
|
-
assistenteExpanded?: boolean;
|
|
15
|
-
onToggleSidebar?: () => void;
|
|
16
|
-
onToggleAssistente?: () => void;
|
|
16
|
+
// Props de layout removidas em favor do LayoutContext
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function HomeContent({
|
|
19
|
+
export function HomeContent({ }: HomeContentProps) {
|
|
20
|
+
const { sidebarExpanded, assistenteExpanded } = useLayout();
|
|
20
21
|
const location = useLocation();
|
|
21
22
|
const navigate = useNavigate();
|
|
22
|
-
|
|
23
|
+
|
|
23
24
|
// Tradução direta para português
|
|
24
25
|
const labelTranslations: Record<string, string> = {
|
|
25
26
|
'home': 'Início',
|
|
@@ -28,48 +29,21 @@ export function HomeContent({ sidebarExpanded, assistenteExpanded = false, onTog
|
|
|
28
29
|
|
|
29
30
|
// Get current route info
|
|
30
31
|
const currentRoute = getRouteByPath(location.pathname);
|
|
31
|
-
const breadcrumbLabel = currentRoute?.label
|
|
32
|
+
const breadcrumbLabel = currentRoute?.label
|
|
32
33
|
? labelTranslations[currentRoute.label.toLowerCase()] || currentRoute.label
|
|
33
34
|
: 'Início';
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
|
-
<div
|
|
39
|
-
className={`flex-1 flex flex-col overflow-hidden transition-all duration-300 ${
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
assistenteExpanded ? 'md:pr-[420px]' : 'md:pr-20'
|
|
43
|
-
}`}
|
|
39
|
+
<div
|
|
40
|
+
className={`flex-1 flex flex-col overflow-hidden transition-all duration-300 ${sidebarExpanded ? 'md:pl-64' : 'md:pl-20'
|
|
41
|
+
} ${assistenteExpanded ? 'md:pr-[420px]' : 'md:pr-20'
|
|
42
|
+
}`}
|
|
44
43
|
>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<div className="flex items-center gap-2 text-muted-foreground">
|
|
49
|
-
{/* Botão menu para mobile */}
|
|
50
|
-
<Button
|
|
51
|
-
variant="ghost"
|
|
52
|
-
size="sm"
|
|
53
|
-
onClick={onToggleSidebar}
|
|
54
|
-
className="md:hidden mr-2 p-2"
|
|
55
|
-
>
|
|
56
|
-
<Menu className="w-5 h-5" />
|
|
57
|
-
</Button>
|
|
58
|
-
<span className="text-primary font-medium">Xertica.ai</span>
|
|
59
|
-
<ChevronRight className="w-4 h-4" />
|
|
60
|
-
<span className="text-foreground font-medium">{breadcrumbLabel}</span>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
{/* Controles do usuário */}
|
|
64
|
-
<div className="flex items-center gap-3">
|
|
65
|
-
{/* Seletor de idioma */}
|
|
66
|
-
<LanguageSelector variant="minimal" showIcon={false} className="hidden sm:flex" />
|
|
67
|
-
|
|
68
|
-
{/* Toggle de tema */}
|
|
69
|
-
<ThemeToggle className="hover:bg-accent" />
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
</header>
|
|
44
|
+
<Header
|
|
45
|
+
title={breadcrumbLabel}
|
|
46
|
+
/>
|
|
73
47
|
|
|
74
48
|
{/* Área de conteúdo */}
|
|
75
49
|
<main className="flex-1 overflow-hidden bg-muted">
|
|
@@ -104,7 +78,7 @@ export function HomeContent({ sidebarExpanded, assistenteExpanded = false, onTog
|
|
|
104
78
|
</p>
|
|
105
79
|
</CardContent>
|
|
106
80
|
<CardFooter>
|
|
107
|
-
<Button
|
|
81
|
+
<Button
|
|
108
82
|
variant="outline"
|
|
109
83
|
className="w-full"
|
|
110
84
|
onClick={() => navigate('/template')}
|
package/components/HomePage.tsx
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { useLocation, useNavigate } from 'react-router';
|
|
3
3
|
import { Sidebar } from './Sidebar';
|
|
4
4
|
import { HomeContent } from './HomeContent';
|
|
5
5
|
import { AssistenteXertica } from './AssistenteXertica';
|
|
6
|
+
import { routes } from '../routes';
|
|
7
|
+
|
|
8
|
+
interface HomePageProps {
|
|
9
|
+
user: { email: string } | null;
|
|
10
|
+
onLogout: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
import { useLayout } from '../contexts/LayoutContext';
|
|
6
14
|
|
|
7
15
|
interface HomePageProps {
|
|
8
16
|
user: { email: string } | null;
|
|
@@ -10,60 +18,26 @@ interface HomePageProps {
|
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
export function HomePage({ user, onLogout }: HomePageProps) {
|
|
13
|
-
const
|
|
14
|
-
const [assistenteExpanded, setAssistenteExpanded] = useState(false);
|
|
21
|
+
const { sidebarExpanded, assistenteExpanded, toggleSidebar, toggleAssistente, toggleAssistenteWithTab } = useLayout();
|
|
15
22
|
const location = useLocation();
|
|
16
23
|
const navigate = useNavigate();
|
|
17
24
|
|
|
18
|
-
// Função para alternar a sidebar - fecha o assistente se estiver aberto
|
|
19
|
-
const handleToggleSidebar = () => {
|
|
20
|
-
if (!sidebarExpanded && assistenteExpanded) {
|
|
21
|
-
// Se a sidebar vai abrir e o assistente está aberto, fechar o assistente
|
|
22
|
-
setAssistenteExpanded(false);
|
|
23
|
-
}
|
|
24
|
-
setSidebarExpanded(!sidebarExpanded);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// Função para alternar o assistente - fecha a sidebar se estiver aberta
|
|
28
|
-
const handleToggleAssistente = () => {
|
|
29
|
-
if (!assistenteExpanded && sidebarExpanded) {
|
|
30
|
-
// Se o assistente vai abrir e a sidebar está aberta, fechar a sidebar
|
|
31
|
-
setSidebarExpanded(false);
|
|
32
|
-
}
|
|
33
|
-
setAssistenteExpanded(!assistenteExpanded);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Função para abrir assistente com tab específica - fecha a sidebar se estiver aberta
|
|
37
|
-
const handleToggleAssistenteWithTab = (tab: string) => {
|
|
38
|
-
if (!assistenteExpanded) {
|
|
39
|
-
if (sidebarExpanded) {
|
|
40
|
-
// Se o assistente vai abrir e a sidebar está aberta, fechar a sidebar
|
|
41
|
-
setSidebarExpanded(false);
|
|
42
|
-
}
|
|
43
|
-
setAssistenteExpanded(true);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
25
|
return (
|
|
48
26
|
<div className="h-screen flex bg-muted overflow-hidden relative">
|
|
49
|
-
<Sidebar
|
|
27
|
+
<Sidebar
|
|
50
28
|
expanded={sidebarExpanded}
|
|
51
|
-
onToggle={
|
|
29
|
+
onToggle={toggleSidebar}
|
|
52
30
|
user={user}
|
|
53
31
|
onLogout={onLogout}
|
|
54
32
|
location={location}
|
|
55
33
|
navigate={navigate}
|
|
34
|
+
routes={routes}
|
|
56
35
|
/>
|
|
57
|
-
<HomeContent
|
|
58
|
-
sidebarExpanded={sidebarExpanded}
|
|
59
|
-
assistenteExpanded={assistenteExpanded}
|
|
60
|
-
onToggleSidebar={handleToggleSidebar}
|
|
61
|
-
onToggleAssistente={handleToggleAssistente}
|
|
62
|
-
/>
|
|
36
|
+
<HomeContent />
|
|
63
37
|
<AssistenteXertica
|
|
64
38
|
isExpanded={assistenteExpanded}
|
|
65
|
-
onToggle={
|
|
66
|
-
onToggleWithTab={
|
|
39
|
+
onToggle={toggleAssistente}
|
|
40
|
+
onToggleWithTab={toggleAssistenteWithTab}
|
|
67
41
|
/>
|
|
68
42
|
</div>
|
|
69
43
|
);
|
package/components/Sidebar.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
MoreHorizontal,
|
|
8
8
|
ChevronRight,
|
|
9
9
|
} from "lucide-react";
|
|
10
|
-
import
|
|
10
|
+
// routes import removed to make it reusable
|
|
11
11
|
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
|
|
12
12
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
|
13
13
|
import { Tooltip, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
|
|
@@ -15,8 +15,9 @@ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
|
15
15
|
import { cn } from "./ui/utils";
|
|
16
16
|
import { XerticaLogo } from "./XerticaLogo";
|
|
17
17
|
import { XerticaXLogo } from "./XerticaXLogo";
|
|
18
|
+
import { Button } from "./ui/button";
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
+
// Validar se SidebarTooltipContent já existe em outro lugar ou manter aqui
|
|
20
21
|
function SidebarTooltipContent({
|
|
21
22
|
className,
|
|
22
23
|
sideOffset = 0,
|
|
@@ -34,15 +35,23 @@ function SidebarTooltipContent({
|
|
|
34
35
|
{...props}
|
|
35
36
|
>
|
|
36
37
|
{children}
|
|
37
|
-
<TooltipPrimitive.Arrow
|
|
38
|
-
className="fill-popover z-50 drop-shadow-sm"
|
|
39
|
-
width={8}
|
|
38
|
+
<TooltipPrimitive.Arrow
|
|
39
|
+
className="fill-popover z-50 drop-shadow-sm"
|
|
40
|
+
width={8}
|
|
40
41
|
height={4}
|
|
41
42
|
/>
|
|
42
43
|
</TooltipPrimitive.Content>
|
|
43
44
|
);
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
// Interface for Route Config (copied/imported type)
|
|
48
|
+
export interface RouteConfig {
|
|
49
|
+
path: string;
|
|
50
|
+
label: string;
|
|
51
|
+
icon: React.ComponentType<any>;
|
|
52
|
+
component?: React.ComponentType<any>;
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
interface SubMenuItem {
|
|
47
56
|
id: string;
|
|
48
57
|
label: string;
|
|
@@ -64,6 +73,7 @@ interface SidebarProps {
|
|
|
64
73
|
onLogout: () => void;
|
|
65
74
|
location: { pathname: string };
|
|
66
75
|
navigate: (path: string) => void;
|
|
76
|
+
routes: RouteConfig[];
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
export function Sidebar({
|
|
@@ -73,6 +83,7 @@ export function Sidebar({
|
|
|
73
83
|
onLogout,
|
|
74
84
|
location,
|
|
75
85
|
navigate,
|
|
86
|
+
routes,
|
|
76
87
|
}: SidebarProps) {
|
|
77
88
|
const navRef = useRef<HTMLDivElement>(null);
|
|
78
89
|
const [hasOverflow, setHasOverflow] = useState(false);
|
|
@@ -154,9 +165,9 @@ export function Sidebar({
|
|
|
154
165
|
? "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
155
166
|
: "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
|
|
156
167
|
: "w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 " +
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
168
|
+
(item.active
|
|
169
|
+
? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
170
|
+
: "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground")
|
|
160
171
|
}
|
|
161
172
|
>
|
|
162
173
|
<Icon className="w-5 h-5 flex-shrink-0" />
|
|
@@ -210,7 +221,7 @@ export function Sidebar({
|
|
|
210
221
|
<span className="truncate">{item.label}</span>
|
|
211
222
|
</button>
|
|
212
223
|
)}
|
|
213
|
-
|
|
224
|
+
|
|
214
225
|
{/* Subitens */}
|
|
215
226
|
{hasSubItems && item.subItems && (
|
|
216
227
|
<>
|
|
@@ -246,9 +257,9 @@ export function Sidebar({
|
|
|
246
257
|
? "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
247
258
|
: "w-full h-10 flex items-center gap-3 px-3 justify-start rounded-[var(--radius-button)] transition-all duration-200 text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground"
|
|
248
259
|
: "w-full h-10 flex items-center justify-center px-0 rounded-[var(--radius-button)] transition-all duration-200 " +
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
260
|
+
(item.active
|
|
261
|
+
? "bg-sidebar-foreground/15 text-sidebar-foreground shadow-sm"
|
|
262
|
+
: "text-sidebar-foreground/80 hover:bg-sidebar-foreground/15 hover:text-sidebar-foreground")
|
|
252
263
|
}
|
|
253
264
|
>
|
|
254
265
|
<Icon className="w-5 h-5 flex-shrink-0" />
|
|
@@ -281,11 +292,10 @@ export function Sidebar({
|
|
|
281
292
|
<TooltipProvider delayDuration={300}>
|
|
282
293
|
{/* Sidebar */}
|
|
283
294
|
<div
|
|
284
|
-
className={`bg-sidebar text-sidebar-foreground transition-all duration-300 ease-in-out flex flex-col z-50 ${
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}`}
|
|
295
|
+
className={`bg-sidebar text-sidebar-foreground transition-all duration-300 ease-in-out flex flex-col z-50 ${expanded
|
|
296
|
+
? "fixed inset-0 md:fixed md:inset-y-0 md:left-0 md:w-64"
|
|
297
|
+
: "fixed inset-y-0 left-0 w-20 -translate-x-full md:translate-x-0"
|
|
298
|
+
}`}
|
|
289
299
|
>
|
|
290
300
|
{/* Botão Toggle Menu */}
|
|
291
301
|
<div className="p-[14px] pt-[13px] pr-[14px] pb-[12px] pl-[14px]">
|
|
@@ -21,14 +21,14 @@ import { ScrollArea } from './ui/scroll-area';
|
|
|
21
21
|
import { ThemeToggle } from './ThemeToggle';
|
|
22
22
|
import { LanguageSelector } from './LanguageSelector';
|
|
23
23
|
|
|
24
|
+
import { useLayout } from '../contexts/LayoutContext';
|
|
25
|
+
|
|
24
26
|
interface TemplateContentProps {
|
|
25
|
-
|
|
26
|
-
assistenteExpanded?: boolean;
|
|
27
|
-
onToggleSidebar?: () => void;
|
|
28
|
-
onToggleAssistente?: () => void;
|
|
27
|
+
// Props de layout removidas
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
export function TemplateContent({
|
|
30
|
+
export function TemplateContent({ }: TemplateContentProps) {
|
|
31
|
+
const { sidebarExpanded, assistenteExpanded, toggleSidebar } = useLayout();
|
|
32
32
|
const [progress, setProgress] = useState(45);
|
|
33
33
|
const [sliderValue, setSliderValue] = useState([50]);
|
|
34
34
|
const [switchEnabled, setSwitchEnabled] = useState(false);
|
|
@@ -52,7 +52,7 @@ export function TemplateContent({ sidebarExpanded, assistenteExpanded = false, o
|
|
|
52
52
|
<Button
|
|
53
53
|
variant="ghost"
|
|
54
54
|
size="sm"
|
|
55
|
-
onClick={
|
|
55
|
+
onClick={toggleSidebar}
|
|
56
56
|
className="md:hidden mr-2 p-2"
|
|
57
57
|
>
|
|
58
58
|
<Menu className="w-5 h-5" />
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { useLocation, useNavigate } from 'react-router';
|
|
3
3
|
import { Sidebar } from './Sidebar';
|
|
4
4
|
import { TemplateContent } from './TemplateContent';
|
|
5
5
|
import { AssistenteXertica } from './AssistenteXertica';
|
|
6
|
+
import { routes } from '../routes';
|
|
7
|
+
|
|
8
|
+
interface TemplatePageProps {
|
|
9
|
+
user: { email: string } | null;
|
|
10
|
+
onLogout: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
import { useLayout } from '../contexts/LayoutContext';
|
|
6
14
|
|
|
7
15
|
interface TemplatePageProps {
|
|
8
16
|
user: { email: string } | null;
|
|
@@ -10,60 +18,26 @@ interface TemplatePageProps {
|
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
export function TemplatePage({ user, onLogout }: TemplatePageProps) {
|
|
13
|
-
const
|
|
14
|
-
const [assistenteExpanded, setAssistenteExpanded] = useState(false);
|
|
21
|
+
const { sidebarExpanded, assistenteExpanded, toggleSidebar, toggleAssistente, toggleAssistenteWithTab } = useLayout();
|
|
15
22
|
const location = useLocation();
|
|
16
23
|
const navigate = useNavigate();
|
|
17
24
|
|
|
18
|
-
// Função para alternar a sidebar - fecha o assistente se estiver aberto
|
|
19
|
-
const handleToggleSidebar = () => {
|
|
20
|
-
if (!sidebarExpanded && assistenteExpanded) {
|
|
21
|
-
// Se a sidebar vai abrir e o assistente está aberto, fechar o assistente
|
|
22
|
-
setAssistenteExpanded(false);
|
|
23
|
-
}
|
|
24
|
-
setSidebarExpanded(!sidebarExpanded);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// Função para alternar o assistente - fecha a sidebar se estiver aberta
|
|
28
|
-
const handleToggleAssistente = () => {
|
|
29
|
-
if (!assistenteExpanded && sidebarExpanded) {
|
|
30
|
-
// Se o assistente vai abrir e a sidebar está aberta, fechar a sidebar
|
|
31
|
-
setSidebarExpanded(false);
|
|
32
|
-
}
|
|
33
|
-
setAssistenteExpanded(!assistenteExpanded);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Função para abrir assistente com tab específica - fecha a sidebar se estiver aberta
|
|
37
|
-
const handleToggleAssistenteWithTab = (tab: string) => {
|
|
38
|
-
if (!assistenteExpanded) {
|
|
39
|
-
if (sidebarExpanded) {
|
|
40
|
-
// Se o assistente vai abrir e a sidebar está aberta, fechar a sidebar
|
|
41
|
-
setSidebarExpanded(false);
|
|
42
|
-
}
|
|
43
|
-
setAssistenteExpanded(true);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
25
|
return (
|
|
48
26
|
<div className="h-screen flex bg-muted overflow-hidden relative">
|
|
49
|
-
<Sidebar
|
|
27
|
+
<Sidebar
|
|
50
28
|
expanded={sidebarExpanded}
|
|
51
|
-
onToggle={
|
|
29
|
+
onToggle={toggleSidebar}
|
|
52
30
|
user={user}
|
|
53
31
|
onLogout={onLogout}
|
|
54
32
|
location={location}
|
|
55
33
|
navigate={navigate}
|
|
34
|
+
routes={routes}
|
|
56
35
|
/>
|
|
57
|
-
<TemplateContent
|
|
58
|
-
sidebarExpanded={sidebarExpanded}
|
|
59
|
-
assistenteExpanded={assistenteExpanded}
|
|
60
|
-
onToggleSidebar={handleToggleSidebar}
|
|
61
|
-
onToggleAssistente={handleToggleAssistente}
|
|
62
|
-
/>
|
|
36
|
+
<TemplateContent />
|
|
63
37
|
<AssistenteXertica
|
|
64
38
|
isExpanded={assistenteExpanded}
|
|
65
|
-
onToggle={
|
|
66
|
-
onToggleWithTab={
|
|
39
|
+
onToggle={toggleAssistente}
|
|
40
|
+
onToggleWithTab={toggleAssistenteWithTab}
|
|
67
41
|
/>
|
|
68
42
|
</div>
|
|
69
43
|
);
|