xertica-ui 1.3.4 → 1.3.5
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/components/AssistenteXertica.tsx +28 -295
- package/components/HomeContent.tsx +2 -5
- package/components/assets/xertica-orbe-animation.ts +804 -0
- package/components/index.ts +1 -1
- package/components/ui/AssistantChart.tsx +29 -0
- package/components/ui/xertica-assistant.tsx +15 -66
- package/dist/AssistantChart-BkwfdilF.js +22 -0
- package/dist/AssistantChart-G8I1Kfu4.js +22 -0
- package/dist/components/assets/xertica-orbe-animation.d.ts +617 -0
- package/dist/components/index.d.ts +0 -1
- package/dist/components/ui/AssistantChart.d.ts +7 -0
- package/dist/components/ui/xertica-assistant.d.ts +1 -10
- package/dist/index-D6V_7QbH.js +99663 -0
- package/dist/index-DHlP-IBS.js +99663 -0
- package/dist/index.es.js +302 -101207
- package/dist/index.umd.js +760 -2289
- package/package.json +1 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
2
1
|
import { useNavigate } from 'react-router';
|
|
3
2
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
+
import React, { useState, useEffect, useRef, Suspense } from 'react';
|
|
4
4
|
import {
|
|
5
5
|
MessageSquare,
|
|
6
6
|
Heart,
|
|
@@ -33,8 +33,7 @@ import {
|
|
|
33
33
|
Bookmark,
|
|
34
34
|
Maximize2,
|
|
35
35
|
AlertCircle,
|
|
36
|
-
|
|
37
|
-
ThumbsDown,
|
|
36
|
+
|
|
38
37
|
BookOpen,
|
|
39
38
|
ArrowLeft,
|
|
40
39
|
Table as TableIcon
|
|
@@ -46,34 +45,19 @@ import { DocumentEditor } from './DocumentEditor';
|
|
|
46
45
|
import { MarkdownMessage } from './MarkdownMessage';
|
|
47
46
|
import { FormattedDocument } from './FormattedDocument';
|
|
48
47
|
import { ModernChatInput, ActionType } from './ModernChatInput';
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
DropdownMenuContent,
|
|
52
|
-
DropdownMenuItem,
|
|
53
|
-
DropdownMenuTrigger,
|
|
54
|
-
} from "./ui/dropdown-menu";
|
|
55
|
-
import {
|
|
56
|
-
Dialog,
|
|
57
|
-
DialogContent,
|
|
58
|
-
DialogDescription,
|
|
59
|
-
DialogFooter,
|
|
60
|
-
DialogHeader,
|
|
61
|
-
DialogTitle,
|
|
62
|
-
} from "./ui/dialog";
|
|
63
|
-
import { Textarea } from "./ui/textarea";
|
|
48
|
+
|
|
49
|
+
|
|
64
50
|
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
|
65
51
|
import { XerticaOrbe } from './XerticaOrbe';
|
|
66
|
-
import { useApiKey } from '../contexts/ApiKeyContext';
|
|
67
52
|
import { useAssistente } from '../contexts/AssistenteContext';
|
|
68
|
-
import { callGeminiAPI, buildSystemPrompt, GeminiMessage } from '../utils/gemini';
|
|
69
53
|
import { toast } from 'sonner';
|
|
70
54
|
import { Tooltip, TooltipTrigger } from './ui/tooltip';
|
|
71
55
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
72
56
|
import { cn } from './ui/utils';
|
|
73
57
|
import { ASSISTANT_EXPANDED_WIDTH, ASSISTANT_COLLAPSED_WIDTH } from './layout-constants';
|
|
74
|
-
// Import Chart Components
|
|
75
|
-
|
|
76
|
-
import { ChartConfig
|
|
58
|
+
// Import Chart Components - Lazy Loaded
|
|
59
|
+
const AssistantChart = React.lazy(() => import('./ui/AssistantChart'));
|
|
60
|
+
import { ChartConfig } from "./ui/chart";
|
|
77
61
|
import {
|
|
78
62
|
Table,
|
|
79
63
|
TableBody,
|
|
@@ -155,7 +139,8 @@ export function AssistenteXertica({
|
|
|
155
139
|
customResponses = []
|
|
156
140
|
}: AssistenteXerticaProps) {
|
|
157
141
|
const navigate = useNavigate();
|
|
158
|
-
|
|
142
|
+
|
|
143
|
+
|
|
159
144
|
const {
|
|
160
145
|
conversaAtual,
|
|
161
146
|
setConversaAtual,
|
|
@@ -179,10 +164,7 @@ export function AssistenteXertica({
|
|
|
179
164
|
const [copiedId, setCopiedId] = useState<string | null>(null);
|
|
180
165
|
const [generatingPodcastId, setGeneratingPodcastId] = useState<string | null>(null);
|
|
181
166
|
const [executingCommand, setExecutingCommand] = useState<string | null>(null);
|
|
182
|
-
|
|
183
|
-
const [feedbackMessageId, setFeedbackMessageId] = useState<string | null>(null);
|
|
184
|
-
const [feedbackCategory, setFeedbackCategory] = useState<string | null>(null);
|
|
185
|
-
const [feedbackReason, setFeedbackReason] = useState("");
|
|
167
|
+
|
|
186
168
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
187
169
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
188
170
|
const audioInputRef = useRef<HTMLInputElement>(null);
|
|
@@ -286,52 +268,7 @@ export function AssistenteXertica({
|
|
|
286
268
|
}, 1500);
|
|
287
269
|
};
|
|
288
270
|
|
|
289
|
-
const handleEvaluation = (messageId: string, evaluation: 'like' | 'dislike', reason?: string) => {
|
|
290
|
-
setConversas(prev => prev.map(conv => {
|
|
291
|
-
if (conv.id === conversaAtual) {
|
|
292
|
-
return {
|
|
293
|
-
...conv,
|
|
294
|
-
mensagens: conv.mensagens.map(msg =>
|
|
295
|
-
msg.id === messageId ? { ...msg, evaluation, evaluationReason: reason } : msg
|
|
296
|
-
)
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
return conv;
|
|
300
|
-
}));
|
|
301
|
-
|
|
302
|
-
if (evaluation === 'like') {
|
|
303
|
-
toast.success("Obrigado por avaliar positivamente!");
|
|
304
|
-
} else if (evaluation === 'dislike' && reason) {
|
|
305
|
-
toast.success("Obrigado pelo seu feedback. Vamos melhorar.");
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
const openFeedbackDialog = (messageId: string, category: string | null = null) => {
|
|
310
|
-
setFeedbackMessageId(messageId);
|
|
311
|
-
setFeedbackCategory(category);
|
|
312
|
-
setFeedbackReason("");
|
|
313
|
-
setFeedbackDialogOpen(true);
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
const submitFeedbackDialog = () => {
|
|
317
|
-
if (!feedbackMessageId) return;
|
|
318
271
|
|
|
319
|
-
let finalReason = "";
|
|
320
|
-
if (feedbackCategory) {
|
|
321
|
-
// Se tem categoria pré-definida, o comentário é opcional
|
|
322
|
-
finalReason = feedbackReason.trim() ? `${feedbackCategory}: ${feedbackReason}` : feedbackCategory;
|
|
323
|
-
} else {
|
|
324
|
-
// Se é "Outros", o motivo é obrigatório
|
|
325
|
-
if (!feedbackReason.trim()) return;
|
|
326
|
-
finalReason = feedbackReason;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
handleEvaluation(feedbackMessageId, 'dislike', finalReason);
|
|
330
|
-
setFeedbackDialogOpen(false);
|
|
331
|
-
setFeedbackMessageId(null);
|
|
332
|
-
setFeedbackReason("");
|
|
333
|
-
setFeedbackCategory(null);
|
|
334
|
-
};
|
|
335
272
|
|
|
336
273
|
const conversaAtiva = conversas.find(c => c.id === conversaAtual);
|
|
337
274
|
const mensagens = conversaAtiva?.mensagens || [];
|
|
@@ -345,100 +282,26 @@ export function AssistenteXertica({
|
|
|
345
282
|
scrollToBottom();
|
|
346
283
|
}, [mensagens, isTyping]);
|
|
347
284
|
|
|
348
|
-
// Resposta da IA
|
|
285
|
+
// Resposta simulada da IA
|
|
349
286
|
const simularRespostaIA = async (mensagemUsuario: string) => {
|
|
350
287
|
setIsTyping(true);
|
|
351
288
|
|
|
352
289
|
try {
|
|
353
|
-
let novaMensagemParcial: Partial<Message> = {};
|
|
354
290
|
|
|
355
|
-
// Se a chave da API for válida e não estiver em modo demo, usar Gemini
|
|
356
|
-
if (isApiKeyValid && geminiApiKey && !demoMode) {
|
|
357
|
-
// Construir histórico da conversa para o Gemini
|
|
358
|
-
const conversaAtiva = conversas.find(c => c.id === conversaAtual);
|
|
359
|
-
const historico: GeminiMessage[] = [];
|
|
360
|
-
|
|
361
|
-
// Adicionar prompt do sistema
|
|
362
|
-
const systemPrompt = buildSystemPrompt();
|
|
363
|
-
|
|
364
|
-
// Converter mensagens anteriores para formato Gemini (últimas 10 mensagens)
|
|
365
|
-
const mensagensRecentes = (conversaAtiva?.mensagens || []).slice(-10);
|
|
366
|
-
for (const msg of mensagensRecentes) {
|
|
367
|
-
if (msg.type === 'user') {
|
|
368
|
-
// Remover prefixos de ação antes de enviar ao Gemini
|
|
369
|
-
let conteudoLimpo = msg.content
|
|
370
|
-
.replace(/^📄 \[Criar documento\] /, '')
|
|
371
|
-
.replace(/^🎙️ \[Gerar podcast\] /, '')
|
|
372
|
-
.replace(/^🔍 \[Pesquisar\] /, '');
|
|
373
|
-
|
|
374
|
-
historico.push({
|
|
375
|
-
role: 'user',
|
|
376
|
-
parts: [{ text: conteudoLimpo }]
|
|
377
|
-
});
|
|
378
|
-
} else if (msg.type === 'assistant') {
|
|
379
|
-
historico.push({
|
|
380
|
-
role: 'model',
|
|
381
|
-
parts: [{ text: msg.content }]
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
291
|
|
|
386
|
-
|
|
387
|
-
if (historico.length === 0) {
|
|
388
|
-
historico.push({
|
|
389
|
-
role: 'user',
|
|
390
|
-
parts: [{ text: systemPrompt }]
|
|
391
|
-
});
|
|
392
|
-
historico.push({
|
|
393
|
-
role: 'model',
|
|
394
|
-
parts: [{ text: 'Entendido! Estou pronto para ajudar você com a plataforma Xertica.ai. Como posso ajudá-lo hoje?' }]
|
|
395
|
-
});
|
|
396
|
-
}
|
|
292
|
+
let novaMensagemParcial: Partial<Message> = {};
|
|
397
293
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
novaMensagemParcial = { content: respostaTexto };
|
|
401
|
-
} catch (error) {
|
|
402
|
-
console.error('Erro ao chamar API Gemini:', error);
|
|
403
|
-
// Usar a mensagem de erro do utilitário se disponível
|
|
404
|
-
const errorMessage = error instanceof Error ? error.message : '❌ Erro desconhecido';
|
|
405
|
-
|
|
406
|
-
// Verificar se é erro de chave vazada ou 403
|
|
407
|
-
const isLeakedKeyError = errorMessage.toLowerCase().includes('leaked') ||
|
|
408
|
-
errorMessage.toLowerCase().includes('reported') ||
|
|
409
|
-
errorMessage.toLowerCase().includes('403');
|
|
410
|
-
|
|
411
|
-
if (isLeakedKeyError) {
|
|
412
|
-
toast.error('Chave de API desativada por segurança', {
|
|
413
|
-
description: 'Sua chave foi reportada como vazada. Gere uma nova em Google AI Studio.',
|
|
414
|
-
duration: 6000,
|
|
415
|
-
});
|
|
416
|
-
novaMensagemParcial = {
|
|
417
|
-
content: `🔐 **Chave de API Desativada por Segurança**\n\n${errorMessage}\n\n**Como resolver:**\n1. Acesse [Google AI Studio](https://aistudio.google.com/apikey)\n2. Gere uma nova chave de API\n3. Configure a nova chave em **Configurações > API**\n\n⚠️ **Importante:** Não compartilhe sua chave de API publicamente.`
|
|
418
|
-
};
|
|
419
|
-
} else {
|
|
420
|
-
toast.error('Erro ao processar mensagem', {
|
|
421
|
-
description: 'Verifique sua chave de API nas Configurações.',
|
|
422
|
-
duration: 4000,
|
|
423
|
-
});
|
|
424
|
-
novaMensagemParcial = {
|
|
425
|
-
content: `**Erro ao processar sua mensagem**\n\n${errorMessage}\n\n💡 **Dica:** Verifique se sua chave de API está configurada corretamente em **Configurações > API**.`
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
} else {
|
|
430
|
-
// Fallback para respostas simuladas se não houver API key ou em modo demo
|
|
431
|
-
await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 1000));
|
|
294
|
+
// Fallback para respostas simuladas se não houver API key ou em modo demo
|
|
295
|
+
await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 1000));
|
|
432
296
|
|
|
433
|
-
|
|
434
|
-
|
|
297
|
+
// Passar customResponses para gerarResposta
|
|
298
|
+
const resultadoMock = gerarResposta(mensagemUsuario, customResponses);
|
|
435
299
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
300
|
+
if (typeof resultadoMock === 'string') {
|
|
301
|
+
novaMensagemParcial = { content: resultadoMock };
|
|
302
|
+
} else {
|
|
303
|
+
// Se for objeto (MockResponse), usar diretamente
|
|
304
|
+
novaMensagemParcial = resultadoMock;
|
|
442
305
|
}
|
|
443
306
|
|
|
444
307
|
const novaMensagem: Message = {
|
|
@@ -1587,43 +1450,7 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
1587
1450
|
{abaSelecionada === 'chat' && (
|
|
1588
1451
|
<div className="flex-1 flex flex-col min-h-0 w-full">
|
|
1589
1452
|
{/* API Key Warning Banner */}
|
|
1590
|
-
|
|
1591
|
-
<div className="mx-4 mt-3 p-4 bg-[var(--toast-warning-bg)]/20 border-2 border-[var(--toast-warning-border)] rounded-lg">
|
|
1592
|
-
<div className="flex items-start gap-3">
|
|
1593
|
-
<AlertCircle className="w-5 h-5 text-[var(--toast-warning-icon)] flex-shrink-0 mt-0.5" />
|
|
1594
|
-
<div className="flex-1 space-y-3">
|
|
1595
|
-
<p className="text-small text-foreground">
|
|
1596
|
-
{!geminiApiKey ? (
|
|
1597
|
-
<strong>Nenhuma chave de API configurada</strong>
|
|
1598
|
-
) : (
|
|
1599
|
-
<strong>🔐 Chave de API inválida ou vazada</strong>
|
|
1600
|
-
)}
|
|
1601
|
-
</p>
|
|
1602
|
-
<p className="text-small text-muted-foreground">
|
|
1603
|
-
Configure uma nova chave do Google Gemini para ativar respostas inteligentes do assistente.
|
|
1604
|
-
</p>
|
|
1605
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
1606
|
-
<Button
|
|
1607
|
-
onClick={() => navigate('/settings')}
|
|
1608
|
-
size="sm"
|
|
1609
|
-
className="bg-[var(--toast-warning-icon)] hover:opacity-90 text-white rounded-[12px] shadow-sm"
|
|
1610
|
-
>
|
|
1611
|
-
Ir para Configurações
|
|
1612
|
-
</Button>
|
|
1613
|
-
<a
|
|
1614
|
-
href="https://aistudio.google.com/apikey"
|
|
1615
|
-
target="_blank"
|
|
1616
|
-
rel="noopener noreferrer"
|
|
1617
|
-
className="inline-flex items-center gap-1 text-small text-[var(--toast-warning-icon)] underline hover:opacity-80"
|
|
1618
|
-
>
|
|
1619
|
-
Obter chave gratuitamente
|
|
1620
|
-
<ExternalLink className="w-3 h-3" />
|
|
1621
|
-
</a>
|
|
1622
|
-
</div>
|
|
1623
|
-
</div>
|
|
1624
|
-
</div>
|
|
1625
|
-
</div>
|
|
1626
|
-
)}
|
|
1453
|
+
|
|
1627
1454
|
|
|
1628
1455
|
{mensagens.length === 0 ? (
|
|
1629
1456
|
<div className="flex-1 overflow-y-auto min-h-0">
|
|
@@ -1805,30 +1632,15 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
1805
1632
|
{/* Chart Rendering */}
|
|
1806
1633
|
{msg.chartData && msg.chartConfig && (
|
|
1807
1634
|
<div className="mt-4 mb-2 p-2 bg-background/50 rounded-lg border border-border">
|
|
1808
|
-
<
|
|
1809
|
-
<
|
|
1810
|
-
|
|
1811
|
-
<XAxis
|
|
1812
|
-
dataKey="month"
|
|
1813
|
-
tickLine={false}
|
|
1814
|
-
tickMargin={10}
|
|
1815
|
-
axisLine={false}
|
|
1816
|
-
tickFormatter={(value) => value.slice(0, 3)}
|
|
1817
|
-
/>
|
|
1818
|
-
<ChartTooltip
|
|
1819
|
-
cursor={false}
|
|
1820
|
-
content={<ChartTooltipContent indicator="dashed" />}
|
|
1821
|
-
/>
|
|
1822
|
-
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
|
1823
|
-
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
|
1824
|
-
</BarChart>
|
|
1825
|
-
</ChartContainer>
|
|
1635
|
+
<Suspense fallback={<div className="h-[200px] w-full flex items-center justify-center bg-muted/20 rounded-lg"><Loader2 className="w-8 h-8 animate-spin text-muted-foreground" /></div>}>
|
|
1636
|
+
<AssistantChart data={msg.chartData} config={msg.chartConfig} />
|
|
1637
|
+
</Suspense>
|
|
1826
1638
|
</div>
|
|
1827
1639
|
)}
|
|
1828
1640
|
|
|
1829
1641
|
{/* Table Rendering */}
|
|
1830
1642
|
{msg.tableData && (
|
|
1831
|
-
<div className="mt-4 mb-2 rounded-md border">
|
|
1643
|
+
<div className="mt-4 mb-2 rounded-md border w-full max-w-full overflow-hidden">
|
|
1832
1644
|
<Table>
|
|
1833
1645
|
{msg.tableData.caption && <TableCaption>{msg.tableData.caption}</TableCaption>}
|
|
1834
1646
|
<TableHeader>
|
|
@@ -2065,56 +1877,7 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
2065
1877
|
</Button>
|
|
2066
1878
|
</div>
|
|
2067
1879
|
|
|
2068
|
-
|
|
2069
|
-
{msg.type === 'assistant' && (
|
|
2070
|
-
<div className="px-2 pb-2">
|
|
2071
|
-
<div className="flex items-center gap-1 mt-2">
|
|
2072
|
-
<span className="text-xs text-muted-foreground mr-2">Essa resposta foi útil?</span>
|
|
2073
|
-
<Button
|
|
2074
|
-
variant="ghost"
|
|
2075
|
-
size="icon"
|
|
2076
|
-
className={cn(
|
|
2077
|
-
"h-6 w-6 rounded-full hover:bg-green-100 dark:hover:bg-green-900/20 hover:text-green-600",
|
|
2078
|
-
msg.evaluation === 'like' && "text-green-600 bg-green-100 dark:bg-green-900/20"
|
|
2079
|
-
)}
|
|
2080
|
-
onClick={() => handleEvaluation(msg.id, 'like')}
|
|
2081
|
-
title="Gostei"
|
|
2082
|
-
>
|
|
2083
|
-
<ThumbsUp className="h-3.5 w-3.5" />
|
|
2084
|
-
</Button>
|
|
2085
|
-
|
|
2086
|
-
<DropdownMenu>
|
|
2087
|
-
<DropdownMenuTrigger asChild>
|
|
2088
|
-
<Button
|
|
2089
|
-
variant="ghost"
|
|
2090
|
-
size="icon"
|
|
2091
|
-
className={cn(
|
|
2092
|
-
"h-6 w-6 rounded-full hover:bg-red-100 dark:hover:bg-red-900/20 hover:text-red-600",
|
|
2093
|
-
msg.evaluation === 'dislike' && "text-red-600 bg-red-100 dark:bg-red-900/20"
|
|
2094
|
-
)}
|
|
2095
|
-
title="Não gostei"
|
|
2096
|
-
>
|
|
2097
|
-
<ThumbsDown className="h-3.5 w-3.5" />
|
|
2098
|
-
</Button>
|
|
2099
|
-
</DropdownMenuTrigger>
|
|
2100
|
-
<DropdownMenuContent align="start">
|
|
2101
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Não era o que eu procurava')}>
|
|
2102
|
-
Não era o que eu procurava
|
|
2103
|
-
</DropdownMenuItem>
|
|
2104
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Informação incorreta')}>
|
|
2105
|
-
Informação incorreta
|
|
2106
|
-
</DropdownMenuItem>
|
|
2107
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Resposta incompleta')}>
|
|
2108
|
-
Resposta incompleta
|
|
2109
|
-
</DropdownMenuItem>
|
|
2110
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, null)}>
|
|
2111
|
-
Outros...
|
|
2112
|
-
</DropdownMenuItem>
|
|
2113
|
-
</DropdownMenuContent>
|
|
2114
|
-
</DropdownMenu>
|
|
2115
|
-
</div>
|
|
2116
|
-
</div>
|
|
2117
|
-
)}
|
|
1880
|
+
|
|
2118
1881
|
</div>
|
|
2119
1882
|
</motion.div>
|
|
2120
1883
|
))}
|
|
@@ -2160,37 +1923,7 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
2160
1923
|
</div>
|
|
2161
1924
|
)}
|
|
2162
1925
|
|
|
2163
|
-
|
|
2164
|
-
<DialogContent className="sm:max-w-[600px]">
|
|
2165
|
-
<DialogHeader>
|
|
2166
|
-
<DialogTitle className="pr-8">
|
|
2167
|
-
{feedbackCategory ? `Enviar feedback: ${feedbackCategory}` : 'Enviar feedback'}
|
|
2168
|
-
</DialogTitle>
|
|
2169
|
-
<DialogDescription>
|
|
2170
|
-
{feedbackCategory
|
|
2171
|
-
? "Gostaria de adicionar algum comentário? (Opcional)"
|
|
2172
|
-
: "Conte-nos por que essa resposta não foi útil para que possamos melhorar."}
|
|
2173
|
-
</DialogDescription>
|
|
2174
|
-
</DialogHeader>
|
|
2175
|
-
<div className="grid gap-4 py-4 px-6">
|
|
2176
|
-
<Textarea
|
|
2177
|
-
className="min-h-[100px]"
|
|
2178
|
-
placeholder={feedbackCategory ? "Comentário adicional..." : "Descreva o motivo..."}
|
|
2179
|
-
value={feedbackReason}
|
|
2180
|
-
onChange={(e) => setFeedbackReason(e.target.value)}
|
|
2181
|
-
/>
|
|
2182
|
-
</div>
|
|
2183
|
-
<DialogFooter>
|
|
2184
|
-
<Button variant="outline" onClick={() => setFeedbackDialogOpen(false)}>Cancelar</Button>
|
|
2185
|
-
<Button
|
|
2186
|
-
onClick={submitFeedbackDialog}
|
|
2187
|
-
disabled={!feedbackCategory && !feedbackReason.trim()}
|
|
2188
|
-
>
|
|
2189
|
-
{feedbackCategory ? 'Confirmar e Enviar' : 'Enviar feedback'}
|
|
2190
|
-
</Button>
|
|
2191
|
-
</DialogFooter>
|
|
2192
|
-
</DialogContent>
|
|
2193
|
-
</Dialog>
|
|
1926
|
+
|
|
2194
1927
|
|
|
2195
1928
|
{(abaSelecionada === 'historico' || abaSelecionada === 'favoritos') && (
|
|
2196
1929
|
<ScrollArea className="flex-1 min-h-0">
|
|
@@ -13,9 +13,7 @@ import { Badge } from './ui/badge';
|
|
|
13
13
|
import { useLayout } from '../contexts/LayoutContext';
|
|
14
14
|
import {
|
|
15
15
|
SIDEBAR_EXPANDED_WIDTH,
|
|
16
|
-
SIDEBAR_COLLAPSED_WIDTH
|
|
17
|
-
ASSISTANT_EXPANDED_WIDTH,
|
|
18
|
-
ASSISTANT_COLLAPSED_WIDTH
|
|
16
|
+
SIDEBAR_COLLAPSED_WIDTH
|
|
19
17
|
} from './layout-constants';
|
|
20
18
|
|
|
21
19
|
interface HomeContentProps {
|
|
@@ -44,8 +42,7 @@ export function HomeContent({ }: HomeContentProps) {
|
|
|
44
42
|
return (
|
|
45
43
|
<div
|
|
46
44
|
style={{
|
|
47
|
-
paddingLeft: sidebarExpanded ? SIDEBAR_EXPANDED_WIDTH : SIDEBAR_COLLAPSED_WIDTH
|
|
48
|
-
paddingRight: assistenteExpanded ? ASSISTANT_EXPANDED_WIDTH : ASSISTANT_COLLAPSED_WIDTH
|
|
45
|
+
paddingLeft: sidebarExpanded ? SIDEBAR_EXPANDED_WIDTH : SIDEBAR_COLLAPSED_WIDTH
|
|
49
46
|
}}
|
|
50
47
|
className={`flex-1 flex flex-col overflow-hidden transition-all duration-300`}
|
|
51
48
|
>
|