xertica-ui 1.2.10 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/AssistenteXertica.tsx +390 -28
- package/contexts/AssistenteContext.tsx +20 -4
- package/dist/components/ui/alert.d.ts +1 -1
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/contexts/AssistenteContext.d.ts +17 -4
- package/dist/index.es.js +39561 -21269
- package/dist/index.umd.js +39552 -21260
- package/package.json +1 -1
|
@@ -32,15 +32,36 @@ import {
|
|
|
32
32
|
BarChart3,
|
|
33
33
|
Bookmark,
|
|
34
34
|
Maximize2,
|
|
35
|
-
AlertCircle
|
|
35
|
+
AlertCircle,
|
|
36
|
+
ThumbsUp,
|
|
37
|
+
ThumbsDown,
|
|
38
|
+
BookOpen,
|
|
39
|
+
ArrowLeft,
|
|
40
|
+
Table as TableIcon
|
|
36
41
|
} from 'lucide-react';
|
|
37
42
|
import { Button } from './ui/button';
|
|
38
43
|
import { ScrollArea } from './ui/scroll-area';
|
|
39
|
-
import { ModernChatInput, ActionType } from './ModernChatInput';
|
|
40
44
|
import { Separator } from './ui/separator';
|
|
41
45
|
import { DocumentEditor } from './DocumentEditor';
|
|
42
|
-
import { FormattedDocument } from './FormattedDocument';
|
|
43
46
|
import { MarkdownMessage } from './MarkdownMessage';
|
|
47
|
+
import { FormattedDocument } from './FormattedDocument';
|
|
48
|
+
import { ModernChatInput, ActionType } from './ModernChatInput';
|
|
49
|
+
import {
|
|
50
|
+
DropdownMenu,
|
|
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";
|
|
64
|
+
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
|
44
65
|
import { XerticaOrbe } from './XerticaOrbe';
|
|
45
66
|
import { useApiKey } from '../contexts/ApiKeyContext';
|
|
46
67
|
import { useAssistente } from '../contexts/AssistenteContext';
|
|
@@ -50,6 +71,18 @@ import { Tooltip, TooltipTrigger } from './ui/tooltip';
|
|
|
50
71
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
51
72
|
import { cn } from './ui/utils';
|
|
52
73
|
import { ASSISTANT_EXPANDED_WIDTH, ASSISTANT_COLLAPSED_WIDTH } from './layout-constants';
|
|
74
|
+
// Import Chart Components
|
|
75
|
+
import { Bar, BarChart, CartesianGrid, XAxis, Tooltip as RechartsTooltip, ResponsiveContainer } from "recharts";
|
|
76
|
+
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "./ui/chart";
|
|
77
|
+
import {
|
|
78
|
+
Table,
|
|
79
|
+
TableBody,
|
|
80
|
+
TableCaption,
|
|
81
|
+
TableCell,
|
|
82
|
+
TableHead,
|
|
83
|
+
TableHeader,
|
|
84
|
+
TableRow,
|
|
85
|
+
} from "./ui/table";
|
|
53
86
|
|
|
54
87
|
// Tooltip customizado estilo sidebar (branco)
|
|
55
88
|
function AssistantTooltipContent({
|
|
@@ -87,12 +120,7 @@ interface AssistenteXerticaProps {
|
|
|
87
120
|
isFullPage?: boolean;
|
|
88
121
|
}
|
|
89
122
|
|
|
90
|
-
import type { Message, Conversa, SearchResult, SearchSource, SearchCommand } from '../contexts/AssistenteContext';
|
|
91
|
-
|
|
92
|
-
interface Sugestao {
|
|
93
|
-
id: string;
|
|
94
|
-
texto: string;
|
|
95
|
-
}
|
|
123
|
+
import type { Message, Conversa, SearchResult, SearchSource, SearchCommand, Sugestao } from '../contexts/AssistenteContext';
|
|
96
124
|
|
|
97
125
|
const sugestoesPadrao: Sugestao[] = [
|
|
98
126
|
{ id: '1', texto: 'O que posso pedir para você fazer?' },
|
|
@@ -102,6 +130,13 @@ const sugestoesPadrao: Sugestao[] = [
|
|
|
102
130
|
{ id: '5', texto: 'Qual projetos está tendo o melhor desempenho?' }
|
|
103
131
|
];
|
|
104
132
|
|
|
133
|
+
const sugestoesRicas: Sugestao[] = [
|
|
134
|
+
{ id: 'rich-1', texto: 'Ver exemplo de Gráfico', icon: <BarChart3 className="w-4 h-4 mr-2" /> },
|
|
135
|
+
{ id: 'rich-2', texto: 'Ver exemplo de Imagem', icon: <ImageIcon className="w-4 h-4 mr-2" /> },
|
|
136
|
+
{ id: 'rich-3', texto: 'Ver exemplo de Tabela', icon: <TableIcon className="w-4 h-4 mr-2" /> },
|
|
137
|
+
{ id: 'rich-4', texto: 'Ver exemplo de Documento', icon: <FileText className="w-4 h-4 mr-2" /> }
|
|
138
|
+
];
|
|
139
|
+
|
|
105
140
|
// Respostas simuladas da IA
|
|
106
141
|
const gerarResposta = (mensagemUsuario: string): string => {
|
|
107
142
|
const mensagemLower = mensagemUsuario.toLowerCase();
|
|
@@ -188,13 +223,164 @@ export function AssistenteXertica({ isExpanded, onToggle, onToggleWithTab, isFul
|
|
|
188
223
|
|
|
189
224
|
const [mensagem, setMensagem] = useState('');
|
|
190
225
|
const [sugestoes] = useState<Sugestao[]>(sugestoesPadrao);
|
|
226
|
+
const [showMoreSuggestions, setShowMoreSuggestions] = useState(false);
|
|
191
227
|
const [copiedId, setCopiedId] = useState<string | null>(null);
|
|
192
228
|
const [generatingPodcastId, setGeneratingPodcastId] = useState<string | null>(null);
|
|
193
229
|
const [executingCommand, setExecutingCommand] = useState<string | null>(null);
|
|
230
|
+
const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false);
|
|
231
|
+
const [feedbackMessageId, setFeedbackMessageId] = useState<string | null>(null);
|
|
232
|
+
const [feedbackCategory, setFeedbackCategory] = useState<string | null>(null);
|
|
233
|
+
const [feedbackReason, setFeedbackReason] = useState("");
|
|
194
234
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
195
235
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
196
236
|
const audioInputRef = useRef<HTMLInputElement>(null);
|
|
197
237
|
|
|
238
|
+
const handleRichResponse = (suggestionId: string, suggestionText: string) => {
|
|
239
|
+
setIsTyping(true);
|
|
240
|
+
setMensagem(''); // Clear input if any
|
|
241
|
+
|
|
242
|
+
// Simulate network delay
|
|
243
|
+
setTimeout(() => {
|
|
244
|
+
let novaMensagem: Message = {
|
|
245
|
+
id: Date.now().toString() + '-ia-rich',
|
|
246
|
+
type: 'assistant',
|
|
247
|
+
content: '',
|
|
248
|
+
timestamp: new Date(),
|
|
249
|
+
isFavorite: false
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
if (suggestionId === 'rich-1') { // Gráfico
|
|
253
|
+
const chartData = [
|
|
254
|
+
{ month: "Jan", desktop: 186, mobile: 80 },
|
|
255
|
+
{ month: "Feb", desktop: 305, mobile: 200 },
|
|
256
|
+
{ month: "Mar", desktop: 237, mobile: 120 },
|
|
257
|
+
{ month: "Apr", desktop: 73, mobile: 190 },
|
|
258
|
+
{ month: "May", desktop: 209, mobile: 130 },
|
|
259
|
+
{ month: "Jun", desktop: 214, mobile: 140 },
|
|
260
|
+
];
|
|
261
|
+
const chartConfig = {
|
|
262
|
+
desktop: {
|
|
263
|
+
label: "Desktop",
|
|
264
|
+
color: "hsl(var(--chart-1))",
|
|
265
|
+
},
|
|
266
|
+
mobile: {
|
|
267
|
+
label: "Mobile",
|
|
268
|
+
color: "hsl(var(--chart-2))",
|
|
269
|
+
},
|
|
270
|
+
} satisfies ChartConfig;
|
|
271
|
+
|
|
272
|
+
novaMensagem = {
|
|
273
|
+
...novaMensagem,
|
|
274
|
+
content: 'Aqui está o gráfico de desempenho do projeto nos últimos 6 meses, comparando acessos Desktop vs Mobile.',
|
|
275
|
+
chartData: chartData,
|
|
276
|
+
chartConfig: chartConfig
|
|
277
|
+
};
|
|
278
|
+
} else if (suggestionId === 'rich-2') { // Imagem
|
|
279
|
+
novaMensagem = {
|
|
280
|
+
...novaMensagem,
|
|
281
|
+
content: 'Aqui está a imagem prévia do novo layout do dashboard que você solicitou.',
|
|
282
|
+
attachmentType: 'image',
|
|
283
|
+
attachmentName: 'dashboard-preview-v2.jpg',
|
|
284
|
+
// Placeholder image URL
|
|
285
|
+
documentContent: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070&auto=format&fit=crop'
|
|
286
|
+
};
|
|
287
|
+
} else if (suggestionId === 'rich-3') { // Tabela
|
|
288
|
+
novaMensagem = {
|
|
289
|
+
...novaMensagem,
|
|
290
|
+
content: 'Aqui está a tabela com os status dos servidores:',
|
|
291
|
+
tableData: {
|
|
292
|
+
caption: "Status dos Servidores em Tempo Real",
|
|
293
|
+
headers: ["Servidor", "Status", "Uptime", "Link"],
|
|
294
|
+
rows: [
|
|
295
|
+
["Alpha-1", "✅ Online", "99.9%", <a href="https://example.com/log1" target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline flex items-center gap-1">Log <ExternalLink size={12} /></a>],
|
|
296
|
+
["Beta-2", "⚠️ Warning", "98.5%", <a href="https://example.com/log2" target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline flex items-center gap-1">Log <ExternalLink size={12} /></a>],
|
|
297
|
+
["Gamma-3", "❌ Offline", "0%", <a href="https://example.com/log3" target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline flex items-center gap-1">Log <ExternalLink size={12} /></a>]
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
} else if (suggestionId === 'rich-4') { // Documento para Download
|
|
302
|
+
novaMensagem = {
|
|
303
|
+
...novaMensagem,
|
|
304
|
+
content: 'Gerei o relatório completo de auditoria de segurança. Você pode baixá-lo ou editá-lo abaixo.',
|
|
305
|
+
attachmentType: 'document',
|
|
306
|
+
attachmentName: 'auditoria-seguranca-2024.md',
|
|
307
|
+
documentTitle: 'Relatório de Auditoria de Segurança',
|
|
308
|
+
documentContent: '# Auditoria de Segurança 2024\n\n## Resumo Executivo\nForam encontradas 3 vulnerabilidades de nível baixo...'
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
setConversas(prev => prev.map(conv => {
|
|
313
|
+
if (conv.id === conversaAtual) {
|
|
314
|
+
return {
|
|
315
|
+
...conv,
|
|
316
|
+
mensagens: [...conv.mensagens,
|
|
317
|
+
{ // User message triggers the response
|
|
318
|
+
id: Date.now().toString() + '-user',
|
|
319
|
+
type: 'user',
|
|
320
|
+
content: suggestionText,
|
|
321
|
+
timestamp: new Date()
|
|
322
|
+
},
|
|
323
|
+
novaMensagem
|
|
324
|
+
],
|
|
325
|
+
ultimaMensagem: suggestionText,
|
|
326
|
+
timestamp: new Date().toLocaleString('pt-BR')
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return conv;
|
|
330
|
+
}));
|
|
331
|
+
|
|
332
|
+
setIsTyping(false);
|
|
333
|
+
setShowMoreSuggestions(false); // Close suggestions
|
|
334
|
+
}, 1500);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const handleEvaluation = (messageId: string, evaluation: 'like' | 'dislike', reason?: string) => {
|
|
338
|
+
setConversas(prev => prev.map(conv => {
|
|
339
|
+
if (conv.id === conversaAtual) {
|
|
340
|
+
return {
|
|
341
|
+
...conv,
|
|
342
|
+
mensagens: conv.mensagens.map(msg =>
|
|
343
|
+
msg.id === messageId ? { ...msg, evaluation, evaluationReason: reason } : msg
|
|
344
|
+
)
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return conv;
|
|
348
|
+
}));
|
|
349
|
+
|
|
350
|
+
if (evaluation === 'like') {
|
|
351
|
+
toast.success("Obrigado por avaliar positivamente!");
|
|
352
|
+
} else if (evaluation === 'dislike' && reason) {
|
|
353
|
+
toast.success("Obrigado pelo seu feedback. Vamos melhorar.");
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const openFeedbackDialog = (messageId: string, category: string | null = null) => {
|
|
358
|
+
setFeedbackMessageId(messageId);
|
|
359
|
+
setFeedbackCategory(category);
|
|
360
|
+
setFeedbackReason("");
|
|
361
|
+
setFeedbackDialogOpen(true);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const submitFeedbackDialog = () => {
|
|
365
|
+
if (!feedbackMessageId) return;
|
|
366
|
+
|
|
367
|
+
let finalReason = "";
|
|
368
|
+
if (feedbackCategory) {
|
|
369
|
+
// Se tem categoria pré-definida, o comentário é opcional
|
|
370
|
+
finalReason = feedbackReason.trim() ? `${feedbackCategory}: ${feedbackReason}` : feedbackCategory;
|
|
371
|
+
} else {
|
|
372
|
+
// Se é "Outros", o motivo é obrigatório
|
|
373
|
+
if (!feedbackReason.trim()) return;
|
|
374
|
+
finalReason = feedbackReason;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
handleEvaluation(feedbackMessageId, 'dislike', finalReason);
|
|
378
|
+
setFeedbackDialogOpen(false);
|
|
379
|
+
setFeedbackMessageId(null);
|
|
380
|
+
setFeedbackReason("");
|
|
381
|
+
setFeedbackCategory(null);
|
|
382
|
+
};
|
|
383
|
+
|
|
198
384
|
const conversaAtiva = conversas.find(c => c.id === conversaAtual);
|
|
199
385
|
const mensagens = conversaAtiva?.mensagens || [];
|
|
200
386
|
|
|
@@ -1489,24 +1675,55 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
1489
1675
|
|
|
1490
1676
|
{/* Suggestions */}
|
|
1491
1677
|
<div className="px-4 pb-4 space-y-2">
|
|
1492
|
-
{
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1678
|
+
{!showMoreSuggestions ? (
|
|
1679
|
+
<>
|
|
1680
|
+
{sugestoes.map((sugestao) => (
|
|
1681
|
+
<button
|
|
1682
|
+
key={sugestao.id}
|
|
1683
|
+
onClick={() => setMensagem(sugestao.texto)}
|
|
1684
|
+
className="w-full p-3 text-left bg-muted hover:bg-accent rounded-lg transition-colors duration-200 text-foreground"
|
|
1685
|
+
>
|
|
1686
|
+
{sugestao.texto}
|
|
1687
|
+
</button>
|
|
1688
|
+
))}
|
|
1689
|
+
|
|
1690
|
+
<Button
|
|
1691
|
+
variant="ghost"
|
|
1692
|
+
size="sm"
|
|
1693
|
+
onClick={() => setShowMoreSuggestions(true)}
|
|
1694
|
+
className="w-full justify-start text-muted-foreground hover:text-foreground"
|
|
1695
|
+
>
|
|
1696
|
+
<MoreHorizontal className="w-4 h-4 mr-2" />
|
|
1697
|
+
Mais sugestões
|
|
1698
|
+
</Button>
|
|
1699
|
+
</>
|
|
1700
|
+
) : (
|
|
1701
|
+
<>
|
|
1702
|
+
<div className="flex items-center gap-2 mb-2 px-2">
|
|
1703
|
+
<span className="text-xs font-medium text-muted-foreground">Exemplos de respostas ricas</span>
|
|
1704
|
+
</div>
|
|
1705
|
+
{sugestoesRicas.map((sugestao) => (
|
|
1706
|
+
<button
|
|
1707
|
+
key={sugestao.id}
|
|
1708
|
+
onClick={() => handleRichResponse(sugestao.id, sugestao.texto)}
|
|
1709
|
+
className="w-full p-3 text-left bg-muted hover:bg-accent rounded-lg transition-colors duration-200 text-foreground flex items-center"
|
|
1710
|
+
>
|
|
1711
|
+
{sugestao.icon}
|
|
1712
|
+
{sugestao.texto}
|
|
1713
|
+
</button>
|
|
1714
|
+
))}
|
|
1501
1715
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1716
|
+
<Button
|
|
1717
|
+
variant="ghost"
|
|
1718
|
+
size="sm"
|
|
1719
|
+
onClick={() => setShowMoreSuggestions(false)}
|
|
1720
|
+
className="w-full justify-start text-muted-foreground hover:text-foreground"
|
|
1721
|
+
>
|
|
1722
|
+
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
1723
|
+
Voltar
|
|
1724
|
+
</Button>
|
|
1725
|
+
</>
|
|
1726
|
+
)}
|
|
1510
1727
|
</div>
|
|
1511
1728
|
</div>
|
|
1512
1729
|
) : (
|
|
@@ -1607,8 +1824,70 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
1607
1824
|
</>
|
|
1608
1825
|
)}
|
|
1609
1826
|
|
|
1610
|
-
{/*
|
|
1611
|
-
{msg.attachmentType === '
|
|
1827
|
+
{/* Image Attachment */}
|
|
1828
|
+
{msg.attachmentType === 'image' && msg.documentContent && (
|
|
1829
|
+
<div className="mt-3 rounded-lg overflow-hidden border border-border">
|
|
1830
|
+
<img
|
|
1831
|
+
src={msg.documentContent}
|
|
1832
|
+
alt={msg.attachmentName || 'Image'}
|
|
1833
|
+
className="w-full h-auto max-h-[300px] object-cover"
|
|
1834
|
+
/>
|
|
1835
|
+
</div>
|
|
1836
|
+
)}
|
|
1837
|
+
|
|
1838
|
+
{/* Chart Rendering */}
|
|
1839
|
+
{msg.chartData && msg.chartConfig && (
|
|
1840
|
+
<div className="mt-4 mb-2 p-2 bg-background/50 rounded-lg border border-border">
|
|
1841
|
+
<ChartContainer config={msg.chartConfig}>
|
|
1842
|
+
<BarChart accessibilityLayer data={msg.chartData}>
|
|
1843
|
+
<CartesianGrid vertical={false} />
|
|
1844
|
+
<XAxis
|
|
1845
|
+
dataKey="month"
|
|
1846
|
+
tickLine={false}
|
|
1847
|
+
tickMargin={10}
|
|
1848
|
+
axisLine={false}
|
|
1849
|
+
tickFormatter={(value) => value.slice(0, 3)}
|
|
1850
|
+
/>
|
|
1851
|
+
<ChartTooltip
|
|
1852
|
+
cursor={false}
|
|
1853
|
+
content={<ChartTooltipContent indicator="dashed" />}
|
|
1854
|
+
/>
|
|
1855
|
+
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
|
1856
|
+
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
|
1857
|
+
</BarChart>
|
|
1858
|
+
</ChartContainer>
|
|
1859
|
+
</div>
|
|
1860
|
+
)}
|
|
1861
|
+
|
|
1862
|
+
{/* Table Rendering */}
|
|
1863
|
+
{msg.tableData && (
|
|
1864
|
+
<div className="mt-4 mb-2 rounded-md border">
|
|
1865
|
+
<Table>
|
|
1866
|
+
{msg.tableData.caption && <TableCaption>{msg.tableData.caption}</TableCaption>}
|
|
1867
|
+
<TableHeader>
|
|
1868
|
+
<TableRow>
|
|
1869
|
+
{msg.tableData.headers.map((header, index) => (
|
|
1870
|
+
<TableHead key={index}>{header}</TableHead>
|
|
1871
|
+
))}
|
|
1872
|
+
</TableRow>
|
|
1873
|
+
</TableHeader>
|
|
1874
|
+
<TableBody>
|
|
1875
|
+
{msg.tableData.rows.map((row, rowIndex) => (
|
|
1876
|
+
<TableRow key={rowIndex}>
|
|
1877
|
+
{row.map((cell, cellIndex) => (
|
|
1878
|
+
<TableCell key={cellIndex} className="font-medium">
|
|
1879
|
+
{cell}
|
|
1880
|
+
</TableCell>
|
|
1881
|
+
))}
|
|
1882
|
+
</TableRow>
|
|
1883
|
+
))}
|
|
1884
|
+
</TableBody>
|
|
1885
|
+
</Table>
|
|
1886
|
+
</div>
|
|
1887
|
+
)}
|
|
1888
|
+
|
|
1889
|
+
{/* Document Preview */}
|
|
1890
|
+
{msg.attachmentType === 'document' && msg.documentContent && !msg.chartData && (
|
|
1612
1891
|
<div className="mt-3 pt-3 border-t border-border overflow-hidden">
|
|
1613
1892
|
<FormattedDocument
|
|
1614
1893
|
content={msg.documentContent}
|
|
@@ -1818,6 +2097,57 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
1818
2097
|
)}
|
|
1819
2098
|
</Button>
|
|
1820
2099
|
</div>
|
|
2100
|
+
|
|
2101
|
+
{/* Feedback UI */}
|
|
2102
|
+
{msg.type === 'assistant' && (
|
|
2103
|
+
<div className="px-2 pb-2">
|
|
2104
|
+
<div className="flex items-center gap-1 mt-2">
|
|
2105
|
+
<span className="text-xs text-muted-foreground mr-2">Essa resposta foi útil?</span>
|
|
2106
|
+
<Button
|
|
2107
|
+
variant="ghost"
|
|
2108
|
+
size="icon"
|
|
2109
|
+
className={cn(
|
|
2110
|
+
"h-6 w-6 rounded-full hover:bg-green-100 dark:hover:bg-green-900/20 hover:text-green-600",
|
|
2111
|
+
msg.evaluation === 'like' && "text-green-600 bg-green-100 dark:bg-green-900/20"
|
|
2112
|
+
)}
|
|
2113
|
+
onClick={() => handleEvaluation(msg.id, 'like')}
|
|
2114
|
+
title="Gostei"
|
|
2115
|
+
>
|
|
2116
|
+
<ThumbsUp className="h-3.5 w-3.5" />
|
|
2117
|
+
</Button>
|
|
2118
|
+
|
|
2119
|
+
<DropdownMenu>
|
|
2120
|
+
<DropdownMenuTrigger asChild>
|
|
2121
|
+
<Button
|
|
2122
|
+
variant="ghost"
|
|
2123
|
+
size="icon"
|
|
2124
|
+
className={cn(
|
|
2125
|
+
"h-6 w-6 rounded-full hover:bg-red-100 dark:hover:bg-red-900/20 hover:text-red-600",
|
|
2126
|
+
msg.evaluation === 'dislike' && "text-red-600 bg-red-100 dark:bg-red-900/20"
|
|
2127
|
+
)}
|
|
2128
|
+
title="Não gostei"
|
|
2129
|
+
>
|
|
2130
|
+
<ThumbsDown className="h-3.5 w-3.5" />
|
|
2131
|
+
</Button>
|
|
2132
|
+
</DropdownMenuTrigger>
|
|
2133
|
+
<DropdownMenuContent align="start">
|
|
2134
|
+
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Não era o que eu procurava')}>
|
|
2135
|
+
Não era o que eu procurava
|
|
2136
|
+
</DropdownMenuItem>
|
|
2137
|
+
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Informação incorreta')}>
|
|
2138
|
+
Informação incorreta
|
|
2139
|
+
</DropdownMenuItem>
|
|
2140
|
+
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Resposta incompleta')}>
|
|
2141
|
+
Resposta incompleta
|
|
2142
|
+
</DropdownMenuItem>
|
|
2143
|
+
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, null)}>
|
|
2144
|
+
Outros...
|
|
2145
|
+
</DropdownMenuItem>
|
|
2146
|
+
</DropdownMenuContent>
|
|
2147
|
+
</DropdownMenu>
|
|
2148
|
+
</div>
|
|
2149
|
+
</div>
|
|
2150
|
+
)}
|
|
1821
2151
|
</div>
|
|
1822
2152
|
</motion.div>
|
|
1823
2153
|
))}
|
|
@@ -1863,6 +2193,38 @@ Este documento fornece uma base sólida para entender e trabalhar com ${temaLimp
|
|
|
1863
2193
|
</div>
|
|
1864
2194
|
)}
|
|
1865
2195
|
|
|
2196
|
+
<Dialog open={feedbackDialogOpen} onOpenChange={setFeedbackDialogOpen}>
|
|
2197
|
+
<DialogContent className="sm:max-w-[600px]">
|
|
2198
|
+
<DialogHeader>
|
|
2199
|
+
<DialogTitle className="pr-8">
|
|
2200
|
+
{feedbackCategory ? `Enviar feedback: ${feedbackCategory}` : 'Enviar feedback'}
|
|
2201
|
+
</DialogTitle>
|
|
2202
|
+
<DialogDescription>
|
|
2203
|
+
{feedbackCategory
|
|
2204
|
+
? "Gostaria de adicionar algum comentário? (Opcional)"
|
|
2205
|
+
: "Conte-nos por que essa resposta não foi útil para que possamos melhorar."}
|
|
2206
|
+
</DialogDescription>
|
|
2207
|
+
</DialogHeader>
|
|
2208
|
+
<div className="grid gap-4 py-4 px-6">
|
|
2209
|
+
<Textarea
|
|
2210
|
+
className="min-h-[100px]"
|
|
2211
|
+
placeholder={feedbackCategory ? "Comentário adicional..." : "Descreva o motivo..."}
|
|
2212
|
+
value={feedbackReason}
|
|
2213
|
+
onChange={(e) => setFeedbackReason(e.target.value)}
|
|
2214
|
+
/>
|
|
2215
|
+
</div>
|
|
2216
|
+
<DialogFooter>
|
|
2217
|
+
<Button variant="outline" onClick={() => setFeedbackDialogOpen(false)}>Cancelar</Button>
|
|
2218
|
+
<Button
|
|
2219
|
+
onClick={submitFeedbackDialog}
|
|
2220
|
+
disabled={!feedbackCategory && !feedbackReason.trim()}
|
|
2221
|
+
>
|
|
2222
|
+
{feedbackCategory ? 'Confirmar e Enviar' : 'Enviar feedback'}
|
|
2223
|
+
</Button>
|
|
2224
|
+
</DialogFooter>
|
|
2225
|
+
</DialogContent>
|
|
2226
|
+
</Dialog>
|
|
2227
|
+
|
|
1866
2228
|
{(abaSelecionada === 'historico' || abaSelecionada === 'favoritos') && (
|
|
1867
2229
|
<ScrollArea className="flex-1 min-h-0">
|
|
1868
2230
|
<div className={`p-4 ${isFullPage ? 'mx-auto w-full max-w-6xl' : ''}`}>
|
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export interface Sugestao {
|
|
6
|
+
id: string;
|
|
7
|
+
texto: string;
|
|
8
|
+
icon?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
export interface Message {
|
|
4
12
|
id: string;
|
|
5
|
-
type: 'user' | 'assistant';
|
|
13
|
+
type: 'user' | 'assistant' | 'system';
|
|
6
14
|
content: string;
|
|
7
15
|
timestamp: Date;
|
|
8
16
|
isFavorite?: boolean;
|
|
9
|
-
|
|
17
|
+
chartData?: any[];
|
|
18
|
+
chartConfig?: any;
|
|
19
|
+
attachmentType?: 'image' | 'video' | 'audio' | 'file' | 'document' | 'podcast' | 'search';
|
|
10
20
|
attachmentName?: string;
|
|
11
|
-
audioUrl?: string;
|
|
12
|
-
isPodcastGenerating?: boolean;
|
|
13
21
|
documentContent?: string;
|
|
14
22
|
documentTitle?: string;
|
|
23
|
+
tableData?: {
|
|
24
|
+
caption?: string;
|
|
25
|
+
headers: string[];
|
|
26
|
+
rows: (string | ReactNode)[][];
|
|
27
|
+
};
|
|
28
|
+
audioUrl?: string;
|
|
15
29
|
searchResults?: SearchResult[];
|
|
16
30
|
searchSources?: SearchSource[];
|
|
17
31
|
searchCommands?: SearchCommand[];
|
|
32
|
+
evaluation?: 'like' | 'dislike';
|
|
33
|
+
evaluationReason?: string;
|
|
18
34
|
}
|
|
19
35
|
|
|
20
36
|
export interface Conversa {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const alertVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "error" | "success" | "
|
|
4
|
+
variant?: "default" | "error" | "success" | "info" | "warning" | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
6
|
declare function Alert({ className, variant, children, ...props }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
declare function AlertTitle({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const badgeVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "destructive" | "outline" | "secondary" | "success" | "
|
|
4
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | "success" | "info" | "warning" | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
6
|
export declare function Badge({ className, variant, asChild, children, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
|
|
7
7
|
asChild?: boolean;
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
|
+
export interface Sugestao {
|
|
3
|
+
id: string;
|
|
4
|
+
texto: string;
|
|
5
|
+
icon?: ReactNode;
|
|
6
|
+
}
|
|
2
7
|
export interface Message {
|
|
3
8
|
id: string;
|
|
4
|
-
type: 'user' | 'assistant';
|
|
9
|
+
type: 'user' | 'assistant' | 'system';
|
|
5
10
|
content: string;
|
|
6
11
|
timestamp: Date;
|
|
7
12
|
isFavorite?: boolean;
|
|
8
|
-
|
|
13
|
+
chartData?: any[];
|
|
14
|
+
chartConfig?: any;
|
|
15
|
+
attachmentType?: 'image' | 'video' | 'audio' | 'file' | 'document' | 'podcast' | 'search';
|
|
9
16
|
attachmentName?: string;
|
|
10
|
-
audioUrl?: string;
|
|
11
|
-
isPodcastGenerating?: boolean;
|
|
12
17
|
documentContent?: string;
|
|
13
18
|
documentTitle?: string;
|
|
19
|
+
tableData?: {
|
|
20
|
+
caption?: string;
|
|
21
|
+
headers: string[];
|
|
22
|
+
rows: (string | ReactNode)[][];
|
|
23
|
+
};
|
|
24
|
+
audioUrl?: string;
|
|
14
25
|
searchResults?: SearchResult[];
|
|
15
26
|
searchSources?: SearchSource[];
|
|
16
27
|
searchCommands?: SearchCommand[];
|
|
28
|
+
evaluation?: 'like' | 'dislike';
|
|
29
|
+
evaluationReason?: string;
|
|
17
30
|
}
|
|
18
31
|
export interface Conversa {
|
|
19
32
|
id: string;
|