xertica-ui 2.1.2 → 2.1.4
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 +46 -0
- package/README.md +1 -1
- package/bin/cli.ts +1 -1
- package/bin/generate-tokens.ts +13 -7
- package/components/assistant/xertica-assistant/index.ts +2 -0
- package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +97 -0
- package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -0
- package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -0
- package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +86 -0
- package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +77 -0
- package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +573 -0
- package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +65 -0
- package/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.tsx +41 -0
- package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +98 -0
- package/components/assistant/xertica-assistant/parts/index.ts +16 -0
- package/components/assistant/xertica-assistant/types.ts +139 -0
- package/components/assistant/xertica-assistant/use-assistant.ts +559 -0
- package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +198 -1460
- package/components/brand/theme-toggle/ThemeToggle.tsx +8 -27
- package/components/hooks/index.ts +3 -0
- package/components/hooks/use-layout-shortcuts.ts +46 -0
- package/components/layout/sidebar/index.ts +2 -0
- package/components/layout/sidebar/sidebar.stories.tsx +160 -8
- package/components/layout/sidebar/sidebar.tsx +606 -497
- package/components/layout/sidebar/use-sidebar.ts +104 -0
- package/components/media/audio-player/AudioPlayer.tsx +131 -206
- package/components/media/audio-player/use-audio-player.ts +298 -0
- package/components/pages/home-page/HomePage.tsx +1 -1
- package/components/pages/template-content/TemplateContent.tsx +5 -5
- package/components/pages/template-page/TemplatePage.tsx +5 -5
- package/components/shared/CustomTooltipContent.tsx +52 -0
- package/components/shared/layout-constants.ts +1 -1
- package/components/ui/chart/chart.stories.tsx +966 -7
- package/components/ui/chart/chart.tsx +918 -45
- package/components/ui/file-upload/file-upload.stories.tsx +100 -0
- package/components/ui/file-upload/file-upload.tsx +14 -74
- package/components/ui/file-upload/index.ts +1 -0
- package/components/ui/file-upload/use-file-upload.ts +181 -0
- package/components/ui/pagination/index.ts +2 -0
- package/components/ui/pagination/pagination.stories.tsx +94 -0
- package/components/ui/pagination/use-pagination.ts +194 -0
- package/components/ui/rich-text-editor/index.ts +2 -0
- package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
- package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
- package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
- package/components/ui/stepper/index.ts +3 -1
- package/components/ui/stepper/stepper.stories.tsx +116 -0
- package/components/ui/stepper/stepper.tsx +4 -4
- package/components/ui/stepper/use-stepper.ts +137 -0
- package/components/ui/tree-view/index.ts +4 -1
- package/components/ui/tree-view/tree-view.stories.tsx +110 -4
- package/components/ui/tree-view/tree-view.tsx +17 -125
- package/components/ui/tree-view/use-tree-view.ts +229 -0
- package/contexts/AssistenteContext.tsx +17 -54
- package/contexts/BrandColorsContext.tsx +6 -17
- package/contexts/LayoutContext.tsx +5 -31
- package/dist/AssistantChart-BAudAfne.cjs +3591 -0
- package/dist/AssistantChart-BP8upjMk.js +3565 -0
- package/dist/AudioPlayer-1ypwE2Wh.cjs +936 -0
- package/dist/AudioPlayer-DuKXrCfy.js +937 -0
- package/dist/CustomTooltipContent-DHjkY0ww.js +40 -0
- package/dist/CustomTooltipContent-c_K-DWRr.cjs +56 -0
- package/dist/LanguageContext-BwhwC3G2.js +657 -0
- package/dist/LanguageContext-DvUt5jBg.cjs +656 -0
- package/dist/LayoutContext-BDmcZfMH.cjs +84 -0
- package/dist/LayoutContext-dbQvdC4O.js +85 -0
- package/dist/ThemeContext-RTy1m2Uq.js +82 -0
- package/dist/ThemeContext-bSzuOit2.cjs +81 -0
- package/dist/VerifyEmailPage-C_ihbcth.js +2828 -0
- package/dist/VerifyEmailPage-Dt7zgA4w.cjs +2827 -0
- package/dist/XerticaProvider-CW9hpCdF.cjs +39 -0
- package/dist/XerticaProvider-siSt9uG2.js +40 -0
- package/dist/XerticaXLogo-D8jf0SNv.cjs +214 -0
- package/dist/XerticaXLogo-fAJMy3H4.js +215 -0
- package/dist/assistant.cjs.js +2 -1
- package/dist/assistant.es.js +3 -2
- package/dist/brand.cjs.js +2 -2
- package/dist/brand.es.js +2 -2
- package/dist/cli.js +14 -8
- package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantCollapsedView.d.ts +13 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantConversationList.d.ts +16 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.d.ts +17 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.d.ts +19 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantHeader.d.ts +11 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantMessageBubble.d.ts +29 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantTabBar.d.ts +13 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.d.ts +4 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.d.ts +17 -0
- package/dist/components/assistant/xertica-assistant/parts/index.d.ts +16 -0
- package/dist/components/assistant/xertica-assistant/types.d.ts +106 -0
- package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +125 -0
- package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +8 -97
- package/dist/components/hooks/index.d.ts +3 -0
- package/dist/components/hooks/use-layout-shortcuts.d.ts +22 -0
- package/dist/components/layout/sidebar/index.d.ts +2 -0
- package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
- package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
- package/dist/components/media/audio-player/AudioPlayer.d.ts +4 -1
- package/dist/components/media/audio-player/use-audio-player.d.ts +72 -0
- package/dist/components/shared/CustomTooltipContent.d.ts +20 -0
- package/dist/components/shared/layout-constants.d.ts +1 -1
- 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/chart/chart.d.ts +162 -5
- package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
- package/dist/components/ui/file-upload/index.d.ts +1 -0
- package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
- package/dist/components/ui/pagination/index.d.ts +2 -0
- package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
- package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
- package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
- package/dist/components/ui/stepper/index.d.ts +3 -1
- package/dist/components/ui/stepper/stepper.d.ts +2 -2
- package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
- package/dist/components/ui/tree-view/index.d.ts +4 -1
- package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
- package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
- package/dist/contexts/AssistenteContext.d.ts +10 -49
- package/dist/hooks.cjs.js +30 -10
- package/dist/hooks.es.js +25 -4
- package/dist/index.cjs.js +20 -9
- package/dist/index.es.js +38 -27
- package/dist/layout.cjs.js +82 -1
- package/dist/layout.es.js +83 -2
- package/dist/media.cjs.js +1 -1
- package/dist/media.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-BmsjY03B.js +2949 -0
- package/dist/rich-text-editor-GS2kpTAK.cjs +2966 -0
- package/dist/sidebar-CVUGHOS_.cjs +756 -0
- package/dist/sidebar-CmvwjnVb.js +757 -0
- package/dist/ui.cjs.js +12 -2
- package/dist/ui.es.js +24 -14
- package/dist/use-audio-player-Bkh23vQ3.js +177 -0
- package/dist/use-audio-player-Dn1NR9xN.cjs +176 -0
- package/dist/utils/color-utils.d.ts +51 -0
- package/dist/xertica-assistant-BMqdyRVi.js +2082 -0
- package/dist/xertica-assistant-Bj3vBCq_.cjs +2081 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/ai-usage.md +28 -10
- package/docs/architecture-improvements.md +463 -0
- package/docs/architecture.md +77 -1
- package/docs/components/assistant-chart.md +1 -1
- package/docs/components/assistant.md +159 -0
- package/docs/components/audio-player.md +46 -0
- package/docs/components/branding.md +251 -0
- package/docs/components/chart.md +354 -39
- package/docs/components/code-block.md +108 -0
- package/docs/components/file-upload.md +119 -2
- package/docs/components/formatted-document.md +113 -0
- package/docs/components/hooks.md +430 -0
- package/docs/components/image-with-fallback.md +106 -0
- package/docs/components/map-layers.md +140 -0
- package/docs/components/modern-chat-input.md +163 -0
- package/docs/components/pages.md +351 -0
- package/docs/components/pagination.md +187 -0
- package/docs/components/rich-text-editor.md +164 -0
- package/docs/components/sidebar.md +153 -4
- package/docs/components/stepper.md +157 -12
- package/docs/components/tree-view.md +164 -6
- package/docs/doc-audit.md +223 -0
- package/docs/getting-started.md +155 -1
- package/docs/guidelines.md +14 -8
- package/docs/layout.md +2 -2
- package/docs/llms.md +29 -9
- package/docs/patterns/detail-page.md +276 -0
- package/docs/patterns/settings.md +346 -0
- package/docs/patterns/wizard.md +217 -0
- package/guidelines/Guidelines.md +5 -3
- package/llms.txt +1 -1
- package/package.json +10 -10
- package/styles/xertica/tokens.css +41 -12
- package/templates/CLAUDE.md +16 -6
- package/templates/guidelines/Guidelines.md +16 -4
- package/templates/package.json +3 -3
- package/templates/src/styles/xertica/tokens.css +39 -10
- package/utils/color-utils.ts +72 -0
|
@@ -1,248 +1,64 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useAssistant } from './use-assistant';
|
|
2
3
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
-
import {
|
|
4
|
-
MessageSquare,
|
|
5
|
-
Heart,
|
|
6
|
-
History,
|
|
7
|
-
MoreHorizontal,
|
|
8
|
-
X,
|
|
9
|
-
ChevronLeft,
|
|
10
|
-
ChevronRight,
|
|
11
|
-
PanelRight,
|
|
12
|
-
Plus,
|
|
13
|
-
Copy,
|
|
14
|
-
Check,
|
|
15
|
-
FileText,
|
|
16
|
-
Music,
|
|
17
|
-
Image as ImageIcon,
|
|
18
|
-
Radio,
|
|
19
|
-
Loader2,
|
|
20
|
-
Edit,
|
|
21
|
-
Download,
|
|
22
|
-
Search,
|
|
23
|
-
Folder,
|
|
24
|
-
Users,
|
|
25
|
-
ExternalLink,
|
|
26
|
-
Star,
|
|
27
|
-
Clock,
|
|
28
|
-
FolderOpen,
|
|
29
|
-
Filter,
|
|
30
|
-
Mail,
|
|
31
|
-
BarChart3,
|
|
32
|
-
Bookmark,
|
|
33
|
-
Maximize2,
|
|
34
|
-
AlertCircle,
|
|
35
|
-
ThumbsUp,
|
|
36
|
-
ThumbsDown,
|
|
37
|
-
Table as TableIcon,
|
|
38
|
-
ArrowLeft,
|
|
39
|
-
Bold,
|
|
40
|
-
Italic,
|
|
41
|
-
Underline,
|
|
42
|
-
AlignLeft,
|
|
43
|
-
AlignCenter,
|
|
44
|
-
AlignRight,
|
|
45
|
-
List,
|
|
46
|
-
Type
|
|
47
|
-
} from 'lucide-react';
|
|
48
|
-
import {
|
|
49
|
-
Bar,
|
|
50
|
-
BarChart,
|
|
51
|
-
CartesianGrid,
|
|
52
|
-
XAxis,
|
|
53
|
-
Tooltip as RechartsTooltip,
|
|
54
|
-
ResponsiveContainer
|
|
55
|
-
} from "recharts";
|
|
56
|
-
import {
|
|
57
|
-
ChartConfig,
|
|
58
|
-
ChartContainer,
|
|
59
|
-
ChartTooltip,
|
|
60
|
-
ChartTooltipContent,
|
|
61
|
-
ChartLegend,
|
|
62
|
-
ChartLegendContent
|
|
63
|
-
} from '../../ui/chart';
|
|
64
|
-
import {
|
|
65
|
-
Table,
|
|
66
|
-
TableBody,
|
|
67
|
-
TableCaption,
|
|
68
|
-
TableCell,
|
|
69
|
-
TableHead,
|
|
70
|
-
TableHeader,
|
|
71
|
-
TableRow,
|
|
72
|
-
} from '../../ui/table';
|
|
73
|
-
import {
|
|
74
|
-
Dialog,
|
|
75
|
-
DialogContent,
|
|
76
|
-
DialogDescription,
|
|
77
|
-
DialogFooter,
|
|
78
|
-
DialogHeader,
|
|
79
|
-
DialogTitle,
|
|
80
|
-
} from '../../ui/dialog';
|
|
81
|
-
import {
|
|
82
|
-
DropdownMenu,
|
|
83
|
-
DropdownMenuContent,
|
|
84
|
-
DropdownMenuItem,
|
|
85
|
-
DropdownMenuTrigger,
|
|
86
|
-
} from '../../ui/dropdown-menu';
|
|
87
|
-
import { Textarea } from '../../ui/textarea';
|
|
88
|
-
import { Button } from '../../ui/button';
|
|
89
4
|
import { ScrollArea } from '../../ui/scroll-area';
|
|
90
|
-
import { Separator } from '../../ui/separator';
|
|
91
|
-
import { RichTextEditor } from '../../ui/rich-text-editor';
|
|
92
|
-
import { MarkdownMessage } from '../markdown-message';
|
|
93
|
-
import { FormattedDocument } from '../formatted-document';
|
|
94
5
|
import { ModernChatInput } from '../modern-chat-input';
|
|
95
|
-
import type { ActionType } from '../modern-chat-input';
|
|
96
6
|
import { XerticaOrbe } from '../../brand/xertica-orbe';
|
|
97
|
-
import {
|
|
98
|
-
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
7
|
+
import { Button } from '../../ui/button';
|
|
99
8
|
import { cn } from '../../shared/utils';
|
|
100
|
-
import { toast } from 'sonner';
|
|
101
9
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"bg-popover text-popover-foreground shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
116
|
-
className,
|
|
117
|
-
)}
|
|
118
|
-
{...props}
|
|
119
|
-
>
|
|
120
|
-
{children}
|
|
121
|
-
<TooltipPrimitive.Arrow
|
|
122
|
-
className="fill-popover z-50 drop-shadow-sm"
|
|
123
|
-
width={8}
|
|
124
|
-
height={4}
|
|
125
|
-
/>
|
|
126
|
-
</TooltipPrimitive.Content>
|
|
127
|
-
</TooltipPrimitive.Portal>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
10
|
+
// Sub-components
|
|
11
|
+
import {
|
|
12
|
+
AssistantCollapsedView,
|
|
13
|
+
AssistantHeader,
|
|
14
|
+
AssistantTabBar,
|
|
15
|
+
AssistantWelcomeScreen,
|
|
16
|
+
AssistantMessageBubble,
|
|
17
|
+
AssistantTypingIndicator,
|
|
18
|
+
AssistantConversationList,
|
|
19
|
+
AssistantFeedbackDialog,
|
|
20
|
+
AssistantDocumentEditor,
|
|
21
|
+
} from './parts';
|
|
22
|
+
import type { EvaluationState } from './parts';
|
|
130
23
|
|
|
131
24
|
// ============================================================================
|
|
132
|
-
// TypeScript Interfaces & Types
|
|
25
|
+
// TypeScript Interfaces & Types — imported from shared types.ts and re-exported
|
|
26
|
+
// for backward compatibility with consumers that import from xertica-assistant.
|
|
133
27
|
// ============================================================================
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Metadata about a search source (e.g., a document repository or knowledge base)
|
|
165
|
-
*/
|
|
166
|
-
export interface SearchSource {
|
|
167
|
-
name: string;
|
|
168
|
-
count: number;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* A quick-action command shown below a search result message
|
|
173
|
-
*/
|
|
174
|
-
export interface SearchCommand {
|
|
175
|
-
id: string;
|
|
176
|
-
icon: string;
|
|
177
|
-
label: string;
|
|
178
|
-
description: string;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* A single chat message in the assistant conversation
|
|
183
|
-
*/
|
|
184
|
-
export interface Message {
|
|
185
|
-
id: string;
|
|
186
|
-
type: MessageType;
|
|
187
|
-
content: string;
|
|
188
|
-
timestamp: Date;
|
|
189
|
-
isFavorite?: boolean;
|
|
190
|
-
attachmentType?: AttachmentType;
|
|
191
|
-
attachmentName?: string;
|
|
192
|
-
documentContent?: string;
|
|
193
|
-
documentTitle?: string;
|
|
194
|
-
audioUrl?: string;
|
|
195
|
-
searchResults?: SearchResult[];
|
|
196
|
-
searchSources?: SearchSource[];
|
|
197
|
-
searchCommands?: SearchCommand[];
|
|
198
|
-
chartData?: any[];
|
|
199
|
-
chartConfig?: any;
|
|
200
|
-
tableData?: {
|
|
201
|
-
caption?: string;
|
|
202
|
-
headers: string[];
|
|
203
|
-
rows: (string | React.ReactNode)[][];
|
|
204
|
-
};
|
|
205
|
-
evaluation?: 'like' | 'dislike';
|
|
206
|
-
evaluationReason?: string;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* A saved conversation entry shown in the history panel
|
|
211
|
-
*/
|
|
212
|
-
export interface Conversation {
|
|
213
|
-
id: string;
|
|
214
|
-
title: string;
|
|
215
|
-
lastMessage?: string;
|
|
216
|
-
timestamp: string;
|
|
217
|
-
isFavorite: boolean;
|
|
218
|
-
messages: Message[];
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* A suggested prompt shown to the user in the chat input area
|
|
223
|
-
*/
|
|
224
|
-
export interface Suggestion {
|
|
225
|
-
id: string;
|
|
226
|
-
text: string;
|
|
227
|
-
icon?: React.ReactNode;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Layout mode for the assistant panel
|
|
232
|
-
*/
|
|
233
|
-
export type AssistantMode = 'collapsed' | 'expanded' | 'fullPage';
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Available tabs in the assistant panel
|
|
237
|
-
*/
|
|
238
|
-
export type AssistantTab = 'chat' | 'historico' | 'favoritos';
|
|
28
|
+
import type {
|
|
29
|
+
MessageType,
|
|
30
|
+
AttachmentType,
|
|
31
|
+
SearchResultType,
|
|
32
|
+
AssistantMode,
|
|
33
|
+
AssistantTab,
|
|
34
|
+
SearchResult,
|
|
35
|
+
SearchSource,
|
|
36
|
+
SearchCommand,
|
|
37
|
+
Message,
|
|
38
|
+
Conversation,
|
|
39
|
+
Suggestion,
|
|
40
|
+
MockResponse,
|
|
41
|
+
} from './types';
|
|
42
|
+
|
|
43
|
+
export type {
|
|
44
|
+
MessageType,
|
|
45
|
+
AttachmentType,
|
|
46
|
+
SearchResultType,
|
|
47
|
+
AssistantMode,
|
|
48
|
+
AssistantTab,
|
|
49
|
+
SearchResult,
|
|
50
|
+
SearchSource,
|
|
51
|
+
SearchCommand,
|
|
52
|
+
Message,
|
|
53
|
+
Conversation,
|
|
54
|
+
Suggestion,
|
|
55
|
+
MockResponse,
|
|
56
|
+
};
|
|
239
57
|
|
|
240
58
|
// ============================================================================
|
|
241
59
|
// Component Props
|
|
242
60
|
// ============================================================================
|
|
243
61
|
|
|
244
|
-
import { gerarResposta, type MockResponse } from '../../shared/assistant-utils';
|
|
245
|
-
|
|
246
62
|
export interface XerticaAssistantProps {
|
|
247
63
|
/**
|
|
248
64
|
* Layout mode for the assistant panel
|
|
@@ -277,8 +93,6 @@ export interface XerticaAssistantProps {
|
|
|
277
93
|
*/
|
|
278
94
|
customResponses?: MockResponse[];
|
|
279
95
|
|
|
280
|
-
|
|
281
|
-
|
|
282
96
|
/**
|
|
283
97
|
* Callback fired when the user navigates to the settings page
|
|
284
98
|
*/
|
|
@@ -310,6 +124,12 @@ export interface XerticaAssistantProps {
|
|
|
310
124
|
*/
|
|
311
125
|
suggestions?: Suggestion[];
|
|
312
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Subtitle shown below the user's name in the empty state.
|
|
129
|
+
* @default 'Como posso ajudar?'
|
|
130
|
+
*/
|
|
131
|
+
welcomeMessage?: string;
|
|
132
|
+
|
|
313
133
|
/**
|
|
314
134
|
* Callback fired when the user sends a message
|
|
315
135
|
*/
|
|
@@ -480,6 +300,7 @@ export function XerticaAssistant({
|
|
|
480
300
|
customResponses = [],
|
|
481
301
|
responseGenerator,
|
|
482
302
|
richSuggestions = [],
|
|
303
|
+
welcomeMessage = 'Como posso ajudar?',
|
|
483
304
|
onRichAction,
|
|
484
305
|
onEvaluation,
|
|
485
306
|
feedbackOptions,
|
|
@@ -493,368 +314,68 @@ export function XerticaAssistant({
|
|
|
493
314
|
enableSearch = true,
|
|
494
315
|
}: XerticaAssistantProps) {
|
|
495
316
|
// ============================================================================
|
|
496
|
-
// State
|
|
497
|
-
// ============================================================================
|
|
498
|
-
|
|
499
|
-
const isFullPage = mode === 'fullPage';
|
|
500
|
-
const [internalIsExpanded, setInternalIsExpanded] = useState(controlledIsExpanded ?? true);
|
|
501
|
-
const isExpanded = controlledIsExpanded ?? internalIsExpanded;
|
|
502
|
-
|
|
503
|
-
const [isMobile, setIsMobile] = useState(false);
|
|
504
|
-
useEffect(() => {
|
|
505
|
-
const handleResize = () => setIsMobile(window.innerWidth < 768);
|
|
506
|
-
handleResize();
|
|
507
|
-
window.addEventListener('resize', handleResize);
|
|
508
|
-
return () => window.removeEventListener('resize', handleResize);
|
|
509
|
-
}, []);
|
|
510
|
-
|
|
511
|
-
const [abaSelecionada, setAbaSelecionada] = useState<AssistantTab>(defaultTab);
|
|
512
|
-
const [mensagens, setMensagens] = useState<Message[]>(initialMessages);
|
|
513
|
-
const [mensagem, setMensagem] = useState('');
|
|
514
|
-
const [conversas, setConversas] = useState<Conversation[]>(savedConversations);
|
|
515
|
-
const [conversaAtual, setConversaAtual] = useState<string | null>(null);
|
|
516
|
-
const [copiedId, setCopiedId] = useState<string | null>(null);
|
|
517
|
-
const [generatingPodcastId, setGeneratingPodcastId] = useState<string | null>(null);
|
|
518
|
-
const [executingCommand, setExecutingCommand] = useState<string | null>(null);
|
|
519
|
-
const [savedSearches, setSavedSearches] = useState<string[]>([]);
|
|
520
|
-
const [editingDocument, setEditingDocument] = useState<{
|
|
521
|
-
content: string;
|
|
522
|
-
title: string;
|
|
523
|
-
} | null>(null);
|
|
524
|
-
const [showMoreSuggestions, setShowMoreSuggestions] = useState(false);
|
|
525
|
-
const [evaluationState, setEvaluationState] = useState<{
|
|
526
|
-
isOpen: boolean;
|
|
527
|
-
messageId: string | null;
|
|
528
|
-
type: 'dislike' | null;
|
|
529
|
-
category?: string | null;
|
|
530
|
-
reason: string;
|
|
531
|
-
}>({
|
|
532
|
-
isOpen: false,
|
|
533
|
-
messageId: null,
|
|
534
|
-
type: null,
|
|
535
|
-
category: null,
|
|
536
|
-
reason: ''
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
// ============================================================================
|
|
540
|
-
// Refs
|
|
541
|
-
// ============================================================================
|
|
542
|
-
|
|
543
|
-
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
544
|
-
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
545
|
-
const audioInputRef = useRef<HTMLInputElement>(null);
|
|
546
|
-
|
|
547
|
-
// ============================================================================
|
|
548
|
-
// Sugestões padrão
|
|
549
|
-
// ============================================================================
|
|
550
|
-
|
|
551
|
-
const defaultSuggestions: Suggestion[] = [
|
|
552
|
-
{ id: '1', text: 'Me ajude a criar um documento profissional' },
|
|
553
|
-
{ id: '2', text: 'Buscar nos meus arquivos por "relatório"' },
|
|
554
|
-
{ id: '3', text: 'Resuma as conversas importantes desta semana' },
|
|
555
|
-
{ id: '4', text: 'Crie um podcast sobre o último projeto' },
|
|
556
|
-
];
|
|
557
|
-
|
|
558
|
-
const sugestoes = propSuggestions ?? defaultSuggestions;
|
|
559
|
-
|
|
560
|
-
// API Key Validation removed
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
// ============================================================================
|
|
564
|
-
// Effects
|
|
565
|
-
// ============================================================================
|
|
566
|
-
|
|
567
|
-
// Auto-scroll ao adicionar mensagens
|
|
568
|
-
useEffect(() => {
|
|
569
|
-
if (messagesEndRef.current && abaSelecionada === 'chat') {
|
|
570
|
-
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
571
|
-
}
|
|
572
|
-
}, [mensagens, abaSelecionada]);
|
|
573
|
-
|
|
574
|
-
// Sincronizar mensagens iniciais apenas quando elas mudarem e não forem vazias
|
|
575
|
-
useEffect(() => {
|
|
576
|
-
if (initialMessages && initialMessages.length > 0) {
|
|
577
|
-
setMensagens(initialMessages);
|
|
578
|
-
}
|
|
579
|
-
}, [initialMessages]);
|
|
580
|
-
|
|
581
|
-
// ============================================================================
|
|
582
|
-
// Handlers
|
|
583
|
-
// ============================================================================
|
|
584
|
-
|
|
585
|
-
const handleToggle = () => {
|
|
586
|
-
if (onToggle) {
|
|
587
|
-
onToggle();
|
|
588
|
-
} else {
|
|
589
|
-
setInternalIsExpanded(!internalIsExpanded);
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
const handleExpandWithTab = (tab: AssistantTab) => {
|
|
594
|
-
setAbaSelecionada(tab);
|
|
595
|
-
if (onToggle) {
|
|
596
|
-
onToggle();
|
|
597
|
-
} else {
|
|
598
|
-
setInternalIsExpanded(true);
|
|
599
|
-
}
|
|
600
|
-
};
|
|
601
|
-
|
|
602
|
-
const handleEnviarMensagem = async (arg?: string | ActionType) => {
|
|
603
|
-
let msgToSend = mensagem;
|
|
604
|
-
|
|
605
|
-
// Check if argument is a message text (from simple string) or ActionType
|
|
606
|
-
if (typeof arg === 'string' && !['document', 'podcast', 'search'].includes(arg)) {
|
|
607
|
-
msgToSend = arg;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
if (!msgToSend.trim() || isProcessing) return;
|
|
611
|
-
|
|
612
|
-
const novaMensagem: Message = {
|
|
613
|
-
id: `msg-${Date.now()}`,
|
|
614
|
-
type: 'user',
|
|
615
|
-
content: msgToSend,
|
|
616
|
-
timestamp: new Date(),
|
|
617
|
-
isFavorite: false,
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
setMensagens(prev => [...prev, novaMensagem]);
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (onSendMessage) {
|
|
624
|
-
onSendMessage(msgToSend);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
if (demoMode || responseGenerator) {
|
|
628
|
-
const mensagemAtual = msgToSend;
|
|
629
|
-
setTimeout(async () => {
|
|
630
|
-
let resposta: string | Partial<Message>;
|
|
631
|
-
|
|
632
|
-
if (responseGenerator) {
|
|
633
|
-
resposta = await responseGenerator(mensagemAtual);
|
|
634
|
-
} else {
|
|
635
|
-
resposta = gerarResposta(mensagemAtual, customResponses);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
let novaMensagemIA: Message = {
|
|
639
|
-
id: `msg-${Date.now()}-ia`,
|
|
640
|
-
type: 'assistant',
|
|
641
|
-
content: '',
|
|
642
|
-
timestamp: new Date(),
|
|
643
|
-
isFavorite: false
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
if (typeof resposta === 'string') {
|
|
647
|
-
novaMensagemIA.content = resposta;
|
|
648
|
-
} else {
|
|
649
|
-
novaMensagemIA = { ...novaMensagemIA, ...resposta };
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
setMensagens(prev => [...prev, novaMensagemIA]);
|
|
653
|
-
}, 1000 + Math.random() * 1000);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
setMensagem('');
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
const handleToggleFavorite = (messageId: string) => {
|
|
660
|
-
setMensagens(prev =>
|
|
661
|
-
prev.map(msg =>
|
|
662
|
-
msg.id === messageId ? { ...msg, isFavorite: !msg.isFavorite } : msg
|
|
663
|
-
)
|
|
664
|
-
);
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
const handleCopyMessage = async (content: string, messageId: string) => {
|
|
668
|
-
try {
|
|
669
|
-
// Try modern Clipboard API first
|
|
670
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
671
|
-
try {
|
|
672
|
-
await navigator.clipboard.writeText(content);
|
|
673
|
-
setCopiedId(messageId);
|
|
674
|
-
setTimeout(() => setCopiedId(null), 2000);
|
|
675
|
-
} catch (clipboardError: any) {
|
|
676
|
-
// If clipboard API fails due to permissions, fall back to execCommand
|
|
677
|
-
if (clipboardError.name === 'NotAllowedError' || clipboardError.message.includes('permissions policy')) {
|
|
678
|
-
throw new Error('Clipboard permission denied, falling back');
|
|
679
|
-
}
|
|
680
|
-
throw clipboardError;
|
|
681
|
-
}
|
|
682
|
-
} else {
|
|
683
|
-
throw new Error('Clipboard API not available');
|
|
684
|
-
}
|
|
685
|
-
} catch (err) {
|
|
686
|
-
// Fallback for browsers that don't support Clipboard API or when permissions are denied
|
|
687
|
-
try {
|
|
688
|
-
const textArea = document.createElement('textarea');
|
|
689
|
-
textArea.value = content;
|
|
690
|
-
textArea.style.position = 'fixed';
|
|
691
|
-
textArea.style.left = '-999999px';
|
|
692
|
-
textArea.style.top = '-999999px';
|
|
693
|
-
document.body.appendChild(textArea);
|
|
694
|
-
textArea.focus();
|
|
695
|
-
textArea.select();
|
|
696
|
-
|
|
697
|
-
const successful = document.execCommand('copy');
|
|
698
|
-
document.body.removeChild(textArea);
|
|
699
|
-
|
|
700
|
-
if (successful) {
|
|
701
|
-
setCopiedId(messageId);
|
|
702
|
-
setTimeout(() => setCopiedId(null), 2000);
|
|
703
|
-
} else {
|
|
704
|
-
console.error('Falha ao copiar: execCommand returned false');
|
|
705
|
-
}
|
|
706
|
-
} catch (fallbackErr) {
|
|
707
|
-
console.error('Falha ao copiar:', fallbackErr);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
const handleGeneratePodcast = async (messageId: string, content: string) => {
|
|
713
|
-
setGeneratingPodcastId(messageId);
|
|
714
|
-
|
|
715
|
-
// Simulação de geração de podcast
|
|
716
|
-
setTimeout(() => {
|
|
717
|
-
setMensagens(prev =>
|
|
718
|
-
prev.map(msg =>
|
|
719
|
-
msg.id === messageId
|
|
720
|
-
? {
|
|
721
|
-
...msg,
|
|
722
|
-
attachmentType: 'podcast',
|
|
723
|
-
attachmentName: 'Podcast - ' + content.substring(0, 30) + '...',
|
|
724
|
-
audioUrl: 'data:audio/mpeg;base64,//uQx...' // Mock
|
|
725
|
-
}
|
|
726
|
-
: msg
|
|
727
|
-
)
|
|
728
|
-
);
|
|
729
|
-
setGeneratingPodcastId(null);
|
|
730
|
-
}, 2000);
|
|
731
|
-
};
|
|
732
|
-
|
|
733
|
-
const handleDownloadDocument = (content: string, fileName: string) => {
|
|
734
|
-
const blob = new Blob([content], { type: 'text/markdown' });
|
|
735
|
-
const url = URL.createObjectURL(blob);
|
|
736
|
-
const a = document.createElement('a');
|
|
737
|
-
a.href = url;
|
|
738
|
-
a.download = fileName.endsWith('.md') ? fileName : fileName + '.md';
|
|
739
|
-
document.body.appendChild(a);
|
|
740
|
-
a.click();
|
|
741
|
-
document.body.removeChild(a);
|
|
742
|
-
URL.revokeObjectURL(url);
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
const handleDownloadPodcast = (audioUrl: string, fileName: string) => {
|
|
746
|
-
const a = document.createElement('a');
|
|
747
|
-
a.href = audioUrl;
|
|
748
|
-
a.download = fileName.endsWith('.mp3') ? fileName : fileName + '.mp3';
|
|
749
|
-
document.body.appendChild(a);
|
|
750
|
-
a.click();
|
|
751
|
-
document.body.removeChild(a);
|
|
752
|
-
};
|
|
753
|
-
|
|
754
|
-
const handleEditDocument = (content: string, title: string) => {
|
|
755
|
-
setEditingDocument({ content, title });
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
const handleNovaConversa = () => {
|
|
759
|
-
setMensagens([]);
|
|
760
|
-
setConversaAtual(null);
|
|
761
|
-
setAbaSelecionada('chat');
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
const handleSelecionarConversa = (conversaId: string) => {
|
|
765
|
-
const conversa = conversas.find(c => c.id === conversaId);
|
|
766
|
-
if (conversa) {
|
|
767
|
-
setMensagens(conversa.messages);
|
|
768
|
-
setConversaAtual(conversaId);
|
|
769
|
-
setAbaSelecionada('chat');
|
|
770
|
-
}
|
|
771
|
-
};
|
|
772
|
-
|
|
773
|
-
const handleToggleFavoritaConversa = (conversaId: string) => {
|
|
774
|
-
setConversas(prev =>
|
|
775
|
-
prev.map(conv =>
|
|
776
|
-
conv.id === conversaId ? { ...conv, isFavorite: !conv.isFavorite } : conv
|
|
777
|
-
)
|
|
778
|
-
);
|
|
779
|
-
};
|
|
780
|
-
|
|
781
|
-
const handleOpenSearchResult = (result: SearchResult) => {
|
|
782
|
-
console.log('Abrir resultado:', result);
|
|
783
|
-
// Implementar lógica de navegação ou abertura de resultado
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
const handleExecuteSearchCommand = async (
|
|
787
|
-
commandId: string,
|
|
788
|
-
searchTerm: string,
|
|
789
|
-
results: SearchResult[],
|
|
790
|
-
messageId: string
|
|
791
|
-
) => {
|
|
792
|
-
setExecutingCommand(commandId);
|
|
793
|
-
|
|
794
|
-
// Simular execução de comando
|
|
795
|
-
setTimeout(() => {
|
|
796
|
-
if (commandId === '5') {
|
|
797
|
-
setSavedSearches(prev => [...prev, messageId]);
|
|
798
|
-
}
|
|
799
|
-
setExecutingCommand(null);
|
|
800
|
-
}, 1500);
|
|
801
|
-
};
|
|
802
|
-
|
|
803
|
-
const handleRichSuggestionClick = (suggestion: Suggestion) => {
|
|
804
|
-
if (onRichAction) {
|
|
805
|
-
onRichAction(suggestion.id, suggestion.text);
|
|
806
|
-
} else {
|
|
807
|
-
// Fallback: send as message
|
|
808
|
-
handleEnviarMensagem(suggestion.text);
|
|
809
|
-
}
|
|
810
|
-
setShowMoreSuggestions(false);
|
|
811
|
-
};
|
|
812
|
-
|
|
813
|
-
const handleEvaluationClick = (messageId: string, type: 'like' | 'dislike') => {
|
|
814
|
-
if (type === 'like') {
|
|
815
|
-
if (onEvaluation) onEvaluation(messageId, 'like');
|
|
816
|
-
// Update local state
|
|
817
|
-
setMensagens(prev => prev.map(m => m.id === messageId ? { ...m, evaluation: 'like' } : m));
|
|
818
|
-
toast.success("Obrigado por avaliar positivamente!");
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
|
|
822
|
-
const openFeedbackDialog = (messageId: string, category: string | null) => {
|
|
823
|
-
setEvaluationState({
|
|
824
|
-
isOpen: true,
|
|
825
|
-
messageId,
|
|
826
|
-
type: 'dislike',
|
|
827
|
-
category,
|
|
828
|
-
reason: ''
|
|
829
|
-
});
|
|
830
|
-
};
|
|
831
|
-
|
|
832
|
-
const handleSubmitDislike = () => {
|
|
833
|
-
if (evaluationState.messageId) {
|
|
834
|
-
const finalReason = evaluationState.category
|
|
835
|
-
? (evaluationState.reason ? `${evaluationState.category}: ${evaluationState.reason}` : evaluationState.category)
|
|
836
|
-
: evaluationState.reason;
|
|
837
|
-
|
|
838
|
-
if (onEvaluation) {
|
|
839
|
-
onEvaluation(evaluationState.messageId, 'dislike', finalReason);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
// Update local state
|
|
843
|
-
setMensagens(prev => prev.map(m => m.id === evaluationState.messageId ? { ...m, evaluation: 'dislike', evaluationReason: finalReason } : m));
|
|
844
|
-
toast.success("Obrigado pelo seu feedback. Vamos melhorar.");
|
|
845
|
-
}
|
|
846
|
-
setEvaluationState({ isOpen: false, messageId: null, type: null, category: null, reason: '' });
|
|
847
|
-
};
|
|
848
|
-
|
|
849
|
-
// ============================================================================
|
|
850
|
-
// Computations
|
|
317
|
+
// State & Logic — delegated to useAssistant hook
|
|
851
318
|
// ============================================================================
|
|
852
319
|
|
|
853
|
-
const
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
320
|
+
const {
|
|
321
|
+
isFullPage,
|
|
322
|
+
isExpanded,
|
|
323
|
+
isMobile,
|
|
324
|
+
abaSelecionada,
|
|
325
|
+
setAbaSelecionada,
|
|
326
|
+
mensagens,
|
|
327
|
+
mensagem,
|
|
328
|
+
setMensagem,
|
|
329
|
+
conversaAtual,
|
|
330
|
+
conversasFiltradas,
|
|
331
|
+
copiedId,
|
|
332
|
+
generatingPodcastId,
|
|
333
|
+
executingCommand,
|
|
334
|
+
savedSearches,
|
|
335
|
+
editingDocument,
|
|
336
|
+
setEditingDocument,
|
|
337
|
+
showMoreSuggestions,
|
|
338
|
+
setShowMoreSuggestions,
|
|
339
|
+
evaluationState,
|
|
340
|
+
setEvaluationState,
|
|
341
|
+
sugestoes,
|
|
342
|
+
messagesEndRef,
|
|
343
|
+
fileInputRef,
|
|
344
|
+
audioInputRef,
|
|
345
|
+
handleToggle,
|
|
346
|
+
handleExpandWithTab,
|
|
347
|
+
handleEnviarMensagem,
|
|
348
|
+
handleToggleFavorite,
|
|
349
|
+
handleCopyMessage,
|
|
350
|
+
handleGeneratePodcast,
|
|
351
|
+
handleDownloadDocument,
|
|
352
|
+
handleDownloadPodcast,
|
|
353
|
+
handleEditDocument,
|
|
354
|
+
handleNovaConversa,
|
|
355
|
+
handleSelecionarConversa,
|
|
356
|
+
handleToggleFavoritaConversa,
|
|
357
|
+
handleOpenSearchResult,
|
|
358
|
+
handleExecuteSearchCommand,
|
|
359
|
+
handleRichSuggestionClick,
|
|
360
|
+
handleEvaluationClick,
|
|
361
|
+
openFeedbackDialog,
|
|
362
|
+
handleSubmitDislike,
|
|
363
|
+
} = useAssistant({
|
|
364
|
+
mode,
|
|
365
|
+
isExpanded: controlledIsExpanded,
|
|
366
|
+
onToggle,
|
|
367
|
+
defaultTab,
|
|
368
|
+
demoMode,
|
|
369
|
+
customResponses,
|
|
370
|
+
initialMessages,
|
|
371
|
+
savedConversations,
|
|
372
|
+
suggestions: propSuggestions,
|
|
373
|
+
onSendMessage,
|
|
374
|
+
isProcessing,
|
|
375
|
+
responseGenerator,
|
|
376
|
+
richSuggestions,
|
|
377
|
+
onRichAction,
|
|
378
|
+
onEvaluation,
|
|
858
379
|
});
|
|
859
380
|
|
|
860
381
|
// ============================================================================
|
|
@@ -865,11 +386,12 @@ export function XerticaAssistant({
|
|
|
865
386
|
isFullPage
|
|
866
387
|
? '100%'
|
|
867
388
|
: isExpanded
|
|
868
|
-
?
|
|
389
|
+
? '420px'
|
|
869
390
|
: '80px' // Compacto quando fechado - apenas ícones
|
|
870
391
|
);
|
|
871
392
|
const containerHeight = height ?? 'h-full';
|
|
872
393
|
|
|
394
|
+
// Mobile floating button — shown when collapsed on mobile
|
|
873
395
|
if (isMobile && !isExpanded) {
|
|
874
396
|
return (
|
|
875
397
|
<Button
|
|
@@ -927,195 +449,37 @@ export function XerticaAssistant({
|
|
|
927
449
|
width: isFullPage ? '100%' : containerWidth,
|
|
928
450
|
} : undefined}
|
|
929
451
|
>
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
{/* Header Toolbar */}
|
|
941
|
-
<div className="px-4 h-[64px] border-b border-border bg-card flex items-center justify-between">
|
|
942
|
-
<div className="flex items-center gap-3">
|
|
943
|
-
<div className="p-2 bg-primary/10 rounded-md">
|
|
944
|
-
<FileText className="w-4 h-4 text-primary" />
|
|
945
|
-
</div>
|
|
946
|
-
<h3 className="font-semibold text-card-foreground text-sm">{editingDocument.title}</h3>
|
|
947
|
-
</div>
|
|
948
|
-
<Button
|
|
949
|
-
variant="ghost"
|
|
950
|
-
size="icon"
|
|
951
|
-
className="h-8 w-8 rounded-full"
|
|
952
|
-
onClick={() => setEditingDocument(null)}
|
|
953
|
-
aria-label="Fechar editor de documento"
|
|
954
|
-
>
|
|
955
|
-
<X className="w-4 h-4" />
|
|
956
|
-
</Button>
|
|
957
|
-
</div>
|
|
958
|
-
|
|
959
|
-
{/* Rich Text Editor */}
|
|
960
|
-
<div className="flex-1 bg-muted/20 p-0 sm:p-6 overflow-hidden">
|
|
961
|
-
<RichTextEditor
|
|
962
|
-
value={editingDocument.content || ''}
|
|
963
|
-
onChange={(newVal) => setEditingDocument(prev => prev ? { ...prev, content: newVal } : null)}
|
|
964
|
-
placeholder="Comece a digitar o conteúdo do seu documento aqui..."
|
|
965
|
-
className="max-w-4xl mx-auto h-full"
|
|
966
|
-
actionButton={
|
|
967
|
-
<Button size="sm" className="gap-2 h-8">
|
|
968
|
-
<Check className="w-3.5 h-3.5" />
|
|
969
|
-
Salvar
|
|
970
|
-
</Button>
|
|
971
|
-
}
|
|
972
|
-
/>
|
|
973
|
-
</div>
|
|
974
|
-
</div>
|
|
975
|
-
</div>
|
|
976
|
-
)}
|
|
977
|
-
{/* Header - Toggle Button - Apenas visível quando não está em fullPage */}
|
|
978
|
-
{!isFullPage && (
|
|
979
|
-
<div
|
|
980
|
-
className={cn(
|
|
981
|
-
"border-b border-border flex items-center h-[64px]",
|
|
982
|
-
isExpanded ? "justify-between px-4" : "justify-center px-0"
|
|
983
|
-
)}
|
|
984
|
-
>
|
|
985
|
-
{isExpanded && (
|
|
986
|
-
<motion.div
|
|
987
|
-
initial={{ opacity: 0, x: -10 }}
|
|
988
|
-
animate={{ opacity: 1, x: 0 }}
|
|
989
|
-
exit={{ opacity: 0, x: -10 }}
|
|
990
|
-
className="flex items-center gap-2 overflow-hidden"
|
|
991
|
-
>
|
|
992
|
-
<div className="flex-shrink-0">
|
|
993
|
-
<XerticaOrbe size={32} />
|
|
994
|
-
</div>
|
|
995
|
-
<span className="text-foreground font-medium truncate">Assistente Xertica</span>
|
|
996
|
-
</motion.div>
|
|
997
|
-
)}
|
|
998
|
-
|
|
999
|
-
<div className="flex items-center gap-1">
|
|
1000
|
-
{isExpanded && onNavigateFullPage && (
|
|
1001
|
-
<Button
|
|
1002
|
-
variant="ghost"
|
|
1003
|
-
size="sm"
|
|
1004
|
-
onClick={onNavigateFullPage}
|
|
1005
|
-
className="h-8 w-8 p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-full"
|
|
1006
|
-
title="Expandir assistente"
|
|
1007
|
-
aria-label="Expandir assistente"
|
|
1008
|
-
>
|
|
1009
|
-
<Maximize2 className="w-4 h-4" />
|
|
1010
|
-
</Button>
|
|
1011
|
-
)}
|
|
452
|
+
{/* Document Editor Overlay */}
|
|
453
|
+
{editingDocument && !isFullPage && (
|
|
454
|
+
<AssistantDocumentEditor
|
|
455
|
+
document={editingDocument}
|
|
456
|
+
isMobile={isMobile}
|
|
457
|
+
containerWidth={containerWidth}
|
|
458
|
+
onClose={() => setEditingDocument(null)}
|
|
459
|
+
onChange={(doc) => setEditingDocument(doc)}
|
|
460
|
+
/>
|
|
461
|
+
)}
|
|
1012
462
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
)}
|
|
1021
|
-
aria-label={isExpanded ? "Recolher assistente" : "Expandir assistente"}
|
|
1022
|
-
>
|
|
1023
|
-
{isExpanded ? (
|
|
1024
|
-
<ChevronRight className="w-4 h-4" />
|
|
1025
|
-
) : (
|
|
1026
|
-
<PanelRight className="w-4 h-4" />
|
|
1027
|
-
)}
|
|
1028
|
-
</Button>
|
|
1029
|
-
</div>
|
|
1030
|
-
</div>
|
|
463
|
+
{/* Header — only visible when not in fullPage mode */}
|
|
464
|
+
{!isFullPage && (
|
|
465
|
+
<AssistantHeader
|
|
466
|
+
isExpanded={isExpanded}
|
|
467
|
+
onToggle={handleToggle}
|
|
468
|
+
onNavigateFullPage={onNavigateFullPage}
|
|
469
|
+
/>
|
|
1031
470
|
)}
|
|
1032
471
|
|
|
1033
|
-
{/* Collapsed State
|
|
472
|
+
{/* Collapsed State — Icons Only */}
|
|
1034
473
|
{!isExpanded && !isFullPage && (
|
|
1035
|
-
<
|
|
1036
|
-
{
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
className="w-10 h-10 rounded-full flex items-center justify-center hover:bg-accent/50 transition-colors duration-200 cursor-pointer mx-auto"
|
|
1042
|
-
aria-label="Abrir assistente"
|
|
1043
|
-
>
|
|
1044
|
-
<XerticaOrbe size={32} />
|
|
1045
|
-
</button>
|
|
1046
|
-
</TooltipTrigger>
|
|
1047
|
-
<AssistantTooltipContent
|
|
1048
|
-
side="left"
|
|
1049
|
-
sideOffset={8}
|
|
1050
|
-
>
|
|
1051
|
-
<p>Assistente Xertica</p>
|
|
1052
|
-
</AssistantTooltipContent>
|
|
1053
|
-
</Tooltip>
|
|
1054
|
-
|
|
1055
|
-
<Tooltip>
|
|
1056
|
-
<TooltipTrigger asChild>
|
|
1057
|
-
<Button
|
|
1058
|
-
variant="ghost"
|
|
1059
|
-
size="sm"
|
|
1060
|
-
onClick={() => handleExpandWithTab('chat')}
|
|
1061
|
-
className="w-8 h-8 p-0 text-muted-foreground"
|
|
1062
|
-
>
|
|
1063
|
-
<MessageSquare className="w-4 h-4" />
|
|
1064
|
-
</Button>
|
|
1065
|
-
</TooltipTrigger>
|
|
1066
|
-
<AssistantTooltipContent
|
|
1067
|
-
side="left"
|
|
1068
|
-
sideOffset={8}
|
|
1069
|
-
>
|
|
1070
|
-
<p>Chat</p>
|
|
1071
|
-
</AssistantTooltipContent>
|
|
1072
|
-
</Tooltip>
|
|
1073
|
-
|
|
1074
|
-
{showFavorites && (
|
|
1075
|
-
<Tooltip>
|
|
1076
|
-
<TooltipTrigger asChild>
|
|
1077
|
-
<Button
|
|
1078
|
-
variant="ghost"
|
|
1079
|
-
size="sm"
|
|
1080
|
-
onClick={() => handleExpandWithTab('favoritos')}
|
|
1081
|
-
className="w-8 h-8 p-0 text-muted-foreground"
|
|
1082
|
-
>
|
|
1083
|
-
<Heart className="w-4 h-4" />
|
|
1084
|
-
</Button>
|
|
1085
|
-
</TooltipTrigger>
|
|
1086
|
-
<AssistantTooltipContent
|
|
1087
|
-
side="left"
|
|
1088
|
-
sideOffset={8}
|
|
1089
|
-
>
|
|
1090
|
-
<p>Favoritos</p>
|
|
1091
|
-
</AssistantTooltipContent>
|
|
1092
|
-
</Tooltip>
|
|
1093
|
-
)}
|
|
1094
|
-
|
|
1095
|
-
{showHistory && (
|
|
1096
|
-
<Tooltip>
|
|
1097
|
-
<TooltipTrigger asChild>
|
|
1098
|
-
<Button
|
|
1099
|
-
variant="ghost"
|
|
1100
|
-
size="sm"
|
|
1101
|
-
onClick={() => handleExpandWithTab('historico')}
|
|
1102
|
-
className="w-8 h-8 p-0 text-muted-foreground"
|
|
1103
|
-
>
|
|
1104
|
-
<History className="w-4 h-4" />
|
|
1105
|
-
</Button>
|
|
1106
|
-
</TooltipTrigger>
|
|
1107
|
-
<AssistantTooltipContent
|
|
1108
|
-
side="left"
|
|
1109
|
-
sideOffset={8}
|
|
1110
|
-
>
|
|
1111
|
-
<p>Histórico</p>
|
|
1112
|
-
</AssistantTooltipContent>
|
|
1113
|
-
</Tooltip>
|
|
1114
|
-
)}
|
|
1115
|
-
</div>
|
|
474
|
+
<AssistantCollapsedView
|
|
475
|
+
showHistory={showHistory}
|
|
476
|
+
showFavorites={showFavorites}
|
|
477
|
+
onToggle={handleToggle}
|
|
478
|
+
onExpandWithTab={handleExpandWithTab}
|
|
479
|
+
/>
|
|
1116
480
|
)}
|
|
1117
481
|
|
|
1118
|
-
{/* Expanded State
|
|
482
|
+
{/* Expanded State — Full Content */}
|
|
1119
483
|
<AnimatePresence>
|
|
1120
484
|
{(isExpanded || isFullPage) && (
|
|
1121
485
|
<motion.div
|
|
@@ -1125,600 +489,61 @@ export function XerticaAssistant({
|
|
|
1125
489
|
transition={{ duration: 0.2 }}
|
|
1126
490
|
className="flex-1 flex flex-col overflow-hidden"
|
|
1127
491
|
>
|
|
1128
|
-
{/* Navigation Tabs
|
|
492
|
+
{/* Navigation Tabs — hidden in fullPage mode and when no secondary tabs are enabled */}
|
|
1129
493
|
{!isFullPage && (showHistory || showFavorites) && (
|
|
1130
|
-
<
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
size="sm"
|
|
1137
|
-
onClick={() => setAbaSelecionada('chat')}
|
|
1138
|
-
className="flex-1 h-8"
|
|
1139
|
-
aria-label="Ver chat"
|
|
1140
|
-
>
|
|
1141
|
-
<MessageSquare className="w-3 h-3 mr-1" />
|
|
1142
|
-
Chat
|
|
1143
|
-
</Button>
|
|
1144
|
-
{showHistory && (
|
|
1145
|
-
<Button
|
|
1146
|
-
variant={abaSelecionada === 'historico' ? 'default' : 'ghost'}
|
|
1147
|
-
size="sm"
|
|
1148
|
-
onClick={() => setAbaSelecionada('historico')}
|
|
1149
|
-
className="flex-1 h-8"
|
|
1150
|
-
aria-label="Ver histórico de conversas"
|
|
1151
|
-
>
|
|
1152
|
-
<History className="w-3 h-3 mr-1" />
|
|
1153
|
-
Histórico
|
|
1154
|
-
</Button>
|
|
1155
|
-
)}
|
|
1156
|
-
{showFavorites && (
|
|
1157
|
-
<Button
|
|
1158
|
-
variant={abaSelecionada === 'favoritos' ? 'default' : 'ghost'}
|
|
1159
|
-
size="sm"
|
|
1160
|
-
onClick={() => setAbaSelecionada('favoritos')}
|
|
1161
|
-
className="flex-1 h-8"
|
|
1162
|
-
aria-label="Ver mensagens favoritas"
|
|
1163
|
-
>
|
|
1164
|
-
<Heart className="w-3 h-3 mr-1" />
|
|
1165
|
-
Favoritos
|
|
1166
|
-
</Button>
|
|
1167
|
-
)}
|
|
1168
|
-
</div>
|
|
1169
|
-
</div>
|
|
494
|
+
<AssistantTabBar
|
|
495
|
+
activeTab={abaSelecionada}
|
|
496
|
+
showHistory={showHistory}
|
|
497
|
+
showFavorites={showFavorites}
|
|
498
|
+
onTabChange={setAbaSelecionada}
|
|
499
|
+
/>
|
|
1170
500
|
)}
|
|
1171
501
|
|
|
1172
502
|
{/* Content Area */}
|
|
1173
503
|
<div className="flex-1 overflow-hidden flex flex-col">
|
|
504
|
+
|
|
505
|
+
{/* Chat Tab */}
|
|
1174
506
|
{abaSelecionada === 'chat' && (
|
|
1175
507
|
<div className={`flex-1 flex flex-col min-h-0 ${isFullPage ? 'mx-auto w-full max-w-6xl' : ''}`}>
|
|
1176
|
-
{/* API Key Warning Banner */}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
508
|
{mensagens.length === 0 ? (
|
|
1180
|
-
<
|
|
1181
|
-
{
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
Como posso ajudar?
|
|
1191
|
-
</p>
|
|
1192
|
-
</div>
|
|
1193
|
-
|
|
1194
|
-
{/* Suggestions */}
|
|
1195
|
-
<div className="px-4 pb-4 space-y-2">
|
|
1196
|
-
{sugestoes.map((sugestao) => (
|
|
1197
|
-
<button
|
|
1198
|
-
key={sugestao.id}
|
|
1199
|
-
onClick={() => handleEnviarMensagem(sugestao.text)}
|
|
1200
|
-
className="w-full p-3 text-left rounded-[var(--radius-card)] bg-muted text-foreground transition-colors duration-200 hover:bg-muted/80"
|
|
1201
|
-
>
|
|
1202
|
-
{sugestao.text}
|
|
1203
|
-
</button>
|
|
1204
|
-
))}
|
|
1205
|
-
|
|
1206
|
-
{!showMoreSuggestions ? (
|
|
1207
|
-
<Button
|
|
1208
|
-
variant="ghost"
|
|
1209
|
-
size="sm"
|
|
1210
|
-
onClick={() => setShowMoreSuggestions(true)}
|
|
1211
|
-
className="w-full justify-start text-muted-foreground"
|
|
1212
|
-
>
|
|
1213
|
-
<MoreHorizontal className="w-4 h-4 mr-2" />
|
|
1214
|
-
Mais sugestões
|
|
1215
|
-
</Button>
|
|
1216
|
-
) : (
|
|
1217
|
-
<div className="space-y-2 pt-2 border-t border-border mt-2 animate-in slide-in-from-top-2">
|
|
1218
|
-
{richSuggestions.length > 0 ? (
|
|
1219
|
-
richSuggestions.map((sugestao) => (
|
|
1220
|
-
<button
|
|
1221
|
-
key={sugestao.id}
|
|
1222
|
-
onClick={() => handleRichSuggestionClick(sugestao)}
|
|
1223
|
-
className="w-full p-3 text-left rounded-[var(--radius-card)] bg-muted/50 border border-border text-foreground transition-all duration-200 hover:bg-primary/5 hover:border-primary/50 group"
|
|
1224
|
-
>
|
|
1225
|
-
<div className="flex items-center gap-2">
|
|
1226
|
-
{sugestao.id.includes('chart') && <BarChart3 className="w-4 h-4 text-primary" />}
|
|
1227
|
-
{sugestao.id.includes('table') && <TableIcon className="w-4 h-4 text-primary" />}
|
|
1228
|
-
<span className="font-medium">{sugestao.text}</span>
|
|
1229
|
-
</div>
|
|
1230
|
-
</button>
|
|
1231
|
-
))
|
|
1232
|
-
) : (
|
|
1233
|
-
<p className="text-sm text-muted-foreground text-center py-2">Nenhuma sugestão extra.</p>
|
|
1234
|
-
)}
|
|
1235
|
-
<Button
|
|
1236
|
-
variant="ghost"
|
|
1237
|
-
size="sm"
|
|
1238
|
-
onClick={() => setShowMoreSuggestions(false)}
|
|
1239
|
-
className="w-full justify-center text-muted-foreground mt-2"
|
|
1240
|
-
>
|
|
1241
|
-
<ChevronLeft className="w-4 h-4 mr-2" />
|
|
1242
|
-
Voltar
|
|
1243
|
-
</Button>
|
|
1244
|
-
</div>
|
|
1245
|
-
)}
|
|
1246
|
-
</div>
|
|
1247
|
-
</div>
|
|
509
|
+
<AssistantWelcomeScreen
|
|
510
|
+
userName={userName}
|
|
511
|
+
welcomeMessage={welcomeMessage}
|
|
512
|
+
suggestions={sugestoes}
|
|
513
|
+
richSuggestions={richSuggestions}
|
|
514
|
+
showMoreSuggestions={showMoreSuggestions}
|
|
515
|
+
onSetShowMoreSuggestions={setShowMoreSuggestions}
|
|
516
|
+
onSendSuggestion={handleEnviarMensagem}
|
|
517
|
+
onRichSuggestionClick={handleRichSuggestionClick}
|
|
518
|
+
/>
|
|
1248
519
|
) : (
|
|
1249
520
|
<ScrollArea className="flex-1 min-h-0 overflow-x-hidden [&_[data-radix-scroll-area-viewport]>div]:!block">
|
|
1250
521
|
<div className="space-y-4 px-4 py-4 max-w-6xl mx-auto !block !w-full">
|
|
1251
522
|
{mensagens.map((msg) => (
|
|
1252
|
-
<
|
|
523
|
+
<AssistantMessageBubble
|
|
1253
524
|
key={msg.id}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
{
|
|
1259
|
-
{
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
? "w-full max-w-full"
|
|
1273
|
-
: "w-fit max-w-[95%] md:max-w-[90%]"
|
|
1274
|
-
)
|
|
1275
|
-
)}>
|
|
1276
|
-
<div
|
|
1277
|
-
className={cn(
|
|
1278
|
-
"px-4 py-2 break-words overflow-hidden overflow-x-hidden w-full min-w-0 rounded-2xl",
|
|
1279
|
-
msg.type === 'user'
|
|
1280
|
-
? "bg-primary text-primary-foreground shadow-sm"
|
|
1281
|
-
: "bg-muted text-foreground"
|
|
1282
|
-
)}
|
|
1283
|
-
>
|
|
1284
|
-
{/* Document Header with Edit and Download Buttons */}
|
|
1285
|
-
{msg.attachmentType === 'document' && (
|
|
1286
|
-
<div
|
|
1287
|
-
className="flex items-center justify-between mb-2 pb-2 border-b border-border min-w-0 overflow-hidden"
|
|
1288
|
-
>
|
|
1289
|
-
<div className="flex items-center gap-2 min-w-0 flex-1 overflow-hidden">
|
|
1290
|
-
<FileText className="w-4 h-4 flex-shrink-0" />
|
|
1291
|
-
<span className="text-sm font-medium break-words">{msg.attachmentName}</span>
|
|
1292
|
-
</div>
|
|
1293
|
-
<div className="flex items-center gap-1 flex-shrink-0">
|
|
1294
|
-
<Button
|
|
1295
|
-
variant="ghost"
|
|
1296
|
-
size="sm"
|
|
1297
|
-
onClick={() => msg.documentContent && msg.attachmentName && handleDownloadDocument(msg.documentContent, msg.attachmentName)}
|
|
1298
|
-
className="h-6 px-2"
|
|
1299
|
-
title="Baixar"
|
|
1300
|
-
>
|
|
1301
|
-
<Download className="w-3 h-3" />
|
|
1302
|
-
</Button>
|
|
1303
|
-
<Button
|
|
1304
|
-
variant="ghost"
|
|
1305
|
-
size="sm"
|
|
1306
|
-
onClick={() => msg.documentContent && msg.documentTitle && handleEditDocument(msg.documentContent, msg.documentTitle)}
|
|
1307
|
-
className="h-6 px-2"
|
|
1308
|
-
>
|
|
1309
|
-
<Edit className="w-3 h-3 mr-1" />
|
|
1310
|
-
Editar
|
|
1311
|
-
</Button>
|
|
1312
|
-
</div>
|
|
1313
|
-
</div>
|
|
1314
|
-
)}
|
|
1315
|
-
|
|
1316
|
-
{/* Attachments */}
|
|
1317
|
-
{msg.attachmentType && msg.attachmentType !== 'podcast' && msg.attachmentType !== 'document' && (
|
|
1318
|
-
<div
|
|
1319
|
-
className="flex items-center gap-2 mb-2 pb-2 border-b border-border min-w-0 overflow-hidden"
|
|
1320
|
-
>
|
|
1321
|
-
{msg.attachmentType === 'file' && <FileText className="w-4 h-4 flex-shrink-0" />}
|
|
1322
|
-
{msg.attachmentType === 'audio' && <Music className="w-4 h-4 flex-shrink-0" />}
|
|
1323
|
-
{msg.attachmentType === 'image' && <ImageIcon className="w-4 h-4 flex-shrink-0" />}
|
|
1324
|
-
<span className="text-small break-words">{msg.attachmentName}</span>
|
|
1325
|
-
</div>
|
|
1326
|
-
)}
|
|
1327
|
-
|
|
1328
|
-
{/* Message Content */}
|
|
1329
|
-
{msg.type === 'user' ? (
|
|
1330
|
-
<p className="whitespace-pre-wrap break-words">{msg.content}</p>
|
|
1331
|
-
) : (
|
|
1332
|
-
<>
|
|
1333
|
-
{(msg.content.includes('🔐') || msg.content.includes('❌')) && (
|
|
1334
|
-
<div
|
|
1335
|
-
className="mb-3 p-3 border rounded-[var(--radius)] overflow-hidden bg-[var(--toast-error-bg)]/20 border-[var(--toast-error-border)]/30"
|
|
1336
|
-
>
|
|
1337
|
-
<div className="flex items-start gap-2 min-w-0">
|
|
1338
|
-
<AlertCircle className="w-5 h-5 flex-shrink-0 mt-0.5 text-[var(--toast-error-icon)]" />
|
|
1339
|
-
<div className="flex-1 min-w-0 overflow-hidden">
|
|
1340
|
-
<MarkdownMessage
|
|
1341
|
-
content={msg.content}
|
|
1342
|
-
className="text-foreground"
|
|
1343
|
-
/>
|
|
1344
|
-
</div>
|
|
1345
|
-
</div>
|
|
1346
|
-
</div>
|
|
1347
|
-
)}
|
|
1348
|
-
{!(msg.content.includes('🔐') || msg.content.includes('❌')) && (
|
|
1349
|
-
<MarkdownMessage
|
|
1350
|
-
content={msg.content}
|
|
1351
|
-
className="text-foreground"
|
|
1352
|
-
/>
|
|
1353
|
-
)}
|
|
1354
|
-
</>
|
|
1355
|
-
)}
|
|
1356
|
-
|
|
1357
|
-
{/* Chart Rendering */}
|
|
1358
|
-
{msg.chartData && msg.chartConfig && (
|
|
1359
|
-
<div className="mt-4 w-full h-[300px] min-w-[300px]">
|
|
1360
|
-
<ChartContainer config={msg.chartConfig} className="h-full w-full">
|
|
1361
|
-
<BarChart accessibilityLayer data={msg.chartData}>
|
|
1362
|
-
<CartesianGrid vertical={false} />
|
|
1363
|
-
<XAxis
|
|
1364
|
-
dataKey="month"
|
|
1365
|
-
tickLine={false}
|
|
1366
|
-
tickMargin={10}
|
|
1367
|
-
axisLine={false}
|
|
1368
|
-
tickFormatter={(value) => value.slice(0, 3)}
|
|
1369
|
-
/>
|
|
1370
|
-
<ChartTooltip content={<ChartTooltipContent />} />
|
|
1371
|
-
<ChartLegend content={<ChartLegendContent />} />
|
|
1372
|
-
{Object.keys(msg.chartConfig).map((key) => (
|
|
1373
|
-
<Bar key={key} dataKey={key} fill={`var(--color-${key})`} radius={4} />
|
|
1374
|
-
))}
|
|
1375
|
-
</BarChart>
|
|
1376
|
-
</ChartContainer>
|
|
1377
|
-
</div>
|
|
1378
|
-
)}
|
|
1379
|
-
|
|
1380
|
-
{/* Table Rendering */}
|
|
1381
|
-
{msg.tableData && (
|
|
1382
|
-
<div className="mt-4 w-full max-w-full border rounded-[var(--radius)] overflow-x-auto custom-scrollbar relative">
|
|
1383
|
-
<Table>
|
|
1384
|
-
{msg.tableData.caption && <TableCaption>{msg.tableData.caption}</TableCaption>}
|
|
1385
|
-
<TableHeader>
|
|
1386
|
-
<TableRow>
|
|
1387
|
-
{msg.tableData.headers.map((header, i) => (
|
|
1388
|
-
<TableHead key={i}>{header}</TableHead>
|
|
1389
|
-
))}
|
|
1390
|
-
</TableRow>
|
|
1391
|
-
</TableHeader>
|
|
1392
|
-
<TableBody>
|
|
1393
|
-
{msg.tableData.rows.map((row, i) => (
|
|
1394
|
-
<TableRow key={i}>
|
|
1395
|
-
{row.map((cell, j) => (
|
|
1396
|
-
<TableCell key={j}>{cell}</TableCell>
|
|
1397
|
-
))}
|
|
1398
|
-
</TableRow>
|
|
1399
|
-
))}
|
|
1400
|
-
</TableBody>
|
|
1401
|
-
</Table>
|
|
1402
|
-
</div>
|
|
1403
|
-
)}
|
|
1404
|
-
|
|
1405
|
-
{/* Document Preview */}
|
|
1406
|
-
{msg.attachmentType === 'document' && msg.documentContent && (
|
|
1407
|
-
<div
|
|
1408
|
-
className="mt-3 pt-3 border-t border-border overflow-hidden"
|
|
1409
|
-
>
|
|
1410
|
-
<FormattedDocument
|
|
1411
|
-
content={msg.documentContent}
|
|
1412
|
-
maxPreviewLength={250}
|
|
1413
|
-
/>
|
|
1414
|
-
</div>
|
|
1415
|
-
)}
|
|
1416
|
-
|
|
1417
|
-
{/* Podcast Player */}
|
|
1418
|
-
{msg.attachmentType === 'podcast' && msg.audioUrl && (
|
|
1419
|
-
<div
|
|
1420
|
-
className="mt-3 pt-3 border-t border-border overflow-hidden"
|
|
1421
|
-
>
|
|
1422
|
-
<div className="flex items-center justify-between mb-3 min-w-0 overflow-hidden">
|
|
1423
|
-
<div className="flex items-center gap-2 min-w-0 flex-1 overflow-hidden">
|
|
1424
|
-
<div
|
|
1425
|
-
className="w-6 h-6 flex items-center justify-center flex-shrink-0 rounded-[var(--radius-button)] bg-gradient-to-br from-[var(--chart-1)] via-[var(--chart-4)] to-[var(--chart-1)]"
|
|
1426
|
-
>
|
|
1427
|
-
<Radio className="w-3 h-3 text-white" />
|
|
1428
|
-
</div>
|
|
1429
|
-
<span className="text-sm font-medium break-words">
|
|
1430
|
-
{msg.attachmentName}
|
|
1431
|
-
</span>
|
|
1432
|
-
</div>
|
|
1433
|
-
<Button
|
|
1434
|
-
variant="ghost"
|
|
1435
|
-
size="sm"
|
|
1436
|
-
onClick={() => msg.audioUrl && msg.attachmentName && handleDownloadPodcast(msg.audioUrl, msg.attachmentName)}
|
|
1437
|
-
className="h-6 px-2 flex-shrink-0"
|
|
1438
|
-
title="Baixar"
|
|
1439
|
-
>
|
|
1440
|
-
<Download className="w-3 h-3" />
|
|
1441
|
-
</Button>
|
|
1442
|
-
</div>
|
|
1443
|
-
<div
|
|
1444
|
-
className="rounded-[var(--radius)] p-2 overflow-hidden bg-gradient-to-r from-[var(--chart-1)]/10 to-[var(--chart-4)]/10"
|
|
1445
|
-
>
|
|
1446
|
-
<audio
|
|
1447
|
-
controls
|
|
1448
|
-
className="w-full max-w-full h-10 outline-none"
|
|
1449
|
-
>
|
|
1450
|
-
<source src={msg.audioUrl} type="audio/mpeg" />
|
|
1451
|
-
Seu navegador não suporta o elemento de áudio.
|
|
1452
|
-
</audio>
|
|
1453
|
-
</div>
|
|
1454
|
-
</div>
|
|
1455
|
-
)}
|
|
1456
|
-
|
|
1457
|
-
{/* Search Results */}
|
|
1458
|
-
{msg.attachmentType === 'search' && msg.searchResults && (
|
|
1459
|
-
<div
|
|
1460
|
-
className="mt-3 pt-3 border-t border-border space-y-4 overflow-hidden"
|
|
1461
|
-
>
|
|
1462
|
-
<div className="overflow-hidden">
|
|
1463
|
-
<div className="flex items-center gap-2 mb-3">
|
|
1464
|
-
<Search className="w-4 h-4 text-[var(--chart-4)]" />
|
|
1465
|
-
<h4 className="text-foreground">
|
|
1466
|
-
Resultados Encontrados ({msg.searchResults.length})
|
|
1467
|
-
</h4>
|
|
1468
|
-
</div>
|
|
1469
|
-
<div className="space-y-2 overflow-hidden">
|
|
1470
|
-
{msg.searchResults.map((result) => (
|
|
1471
|
-
<div
|
|
1472
|
-
key={result.id}
|
|
1473
|
-
onClick={() => handleOpenSearchResult(result)}
|
|
1474
|
-
className="p-3 rounded-[var(--radius)] border border-border transition-all cursor-pointer group overflow-hidden hover:bg-muted/50"
|
|
1475
|
-
>
|
|
1476
|
-
<div className="flex items-start justify-between gap-2 min-w-0">
|
|
1477
|
-
<div className="flex items-start gap-2 flex-1 min-w-0 overflow-hidden">
|
|
1478
|
-
<div className="mt-0.5 flex-shrink-0">
|
|
1479
|
-
{result.type === 'document' && <FileText className="w-4 h-4 text-[var(--chart-4)]" />}
|
|
1480
|
-
{result.type === 'project' && <FolderOpen className="w-4 h-4 text-[var(--chart-1)]" />}
|
|
1481
|
-
{result.type === 'conversation' && <MessageSquare className="w-4 h-4 text-[var(--chart-2)]" />}
|
|
1482
|
-
{result.type === 'file' && <Folder className="w-4 h-4 text-[var(--chart-3)]" />}
|
|
1483
|
-
{result.type === 'contact' && <Users className="w-4 h-4 text-[var(--chart-5)]" />}
|
|
1484
|
-
</div>
|
|
1485
|
-
<div className="flex-1 min-w-0 overflow-hidden">
|
|
1486
|
-
<div className="flex items-start gap-2 min-w-0">
|
|
1487
|
-
<h5 className="text-sm font-medium break-words flex-1 min-w-0 text-foreground">
|
|
1488
|
-
{result.title}
|
|
1489
|
-
</h5>
|
|
1490
|
-
<span
|
|
1491
|
-
className="px-1.5 py-0.5 rounded-sm flex-shrink-0 self-start bg-[var(--chart-4)]/10 text-[var(--chart-4)]"
|
|
1492
|
-
>
|
|
1493
|
-
{result.relevance}%
|
|
1494
|
-
</span>
|
|
1495
|
-
</div>
|
|
1496
|
-
<p className="text-sm text-muted-foreground mt-1 line-clamp-2 break-words">
|
|
1497
|
-
{result.description}
|
|
1498
|
-
</p>
|
|
1499
|
-
<div className="flex items-center gap-3 mt-2 flex-wrap min-w-0">
|
|
1500
|
-
<span className="text-sm text-muted-foreground flex items-center gap-1 min-w-0 max-w-full">
|
|
1501
|
-
<ExternalLink className="w-3 h-3 flex-shrink-0" />
|
|
1502
|
-
<span className="truncate">{result.path}</span>
|
|
1503
|
-
</span>
|
|
1504
|
-
{result.lastModified && (
|
|
1505
|
-
<span className="text-sm text-muted-foreground flex items-center gap-1 flex-shrink-0">
|
|
1506
|
-
<Clock className="w-3 h-3" />
|
|
1507
|
-
{result.lastModified}
|
|
1508
|
-
</span>
|
|
1509
|
-
)}
|
|
1510
|
-
</div>
|
|
1511
|
-
</div>
|
|
1512
|
-
</div>
|
|
1513
|
-
</div>
|
|
1514
|
-
</div>
|
|
1515
|
-
))}
|
|
1516
|
-
</div>
|
|
1517
|
-
</div>
|
|
1518
|
-
|
|
1519
|
-
{/* Search Sources */}
|
|
1520
|
-
{msg.searchSources && (
|
|
1521
|
-
<div>
|
|
1522
|
-
<h4 className="mb-2 text-foreground">
|
|
1523
|
-
Fontes Consultadas
|
|
1524
|
-
</h4>
|
|
1525
|
-
<div className="flex flex-wrap gap-2 overflow-hidden">
|
|
1526
|
-
{msg.searchSources.map((source, index) => (
|
|
1527
|
-
<div
|
|
1528
|
-
key={index}
|
|
1529
|
-
className="px-3 py-1.5 border max-w-full rounded-[var(--radius-button)] bg-muted border-border"
|
|
1530
|
-
>
|
|
1531
|
-
<span className="text-sm font-medium text-foreground break-words">{source.name}</span>
|
|
1532
|
-
<span className="text-sm text-muted-foreground ml-1 whitespace-nowrap">({source.count})</span>
|
|
1533
|
-
</div>
|
|
1534
|
-
))}
|
|
1535
|
-
</div>
|
|
1536
|
-
</div>
|
|
1537
|
-
)}
|
|
1538
|
-
|
|
1539
|
-
{/* Search Commands */}
|
|
1540
|
-
{msg.searchCommands && msg.searchResults && (
|
|
1541
|
-
<div>
|
|
1542
|
-
<h4 className="mb-2 text-foreground">
|
|
1543
|
-
O que você pode fazer com estes resultados
|
|
1544
|
-
</h4>
|
|
1545
|
-
<div className="grid grid-cols-1 gap-2">
|
|
1546
|
-
{msg.searchCommands.map((command) => (
|
|
1547
|
-
<button
|
|
1548
|
-
key={command.id}
|
|
1549
|
-
onClick={() => handleExecuteSearchCommand(
|
|
1550
|
-
command.id,
|
|
1551
|
-
msg.content.replace('🔍 Pesquisa realizada com sucesso!', '').split('"')[1] || 'pesquisa',
|
|
1552
|
-
msg.searchResults!,
|
|
1553
|
-
msg.id
|
|
1554
|
-
)}
|
|
1555
|
-
disabled={executingCommand === command.id}
|
|
1556
|
-
className={cn(
|
|
1557
|
-
"flex items-start gap-2 p-2 rounded-[var(--radius)] border transition-all text-left disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1558
|
-
savedSearches.includes(msg.id) && command.id === '5'
|
|
1559
|
-
? "border-[var(--toast-warning-border)] bg-[var(--toast-warning-bg)]/20"
|
|
1560
|
-
: "border-border bg-transparent hover:bg-muted/50"
|
|
1561
|
-
)}
|
|
1562
|
-
>
|
|
1563
|
-
{executingCommand === command.id ? (
|
|
1564
|
-
<Loader2 className="w-5 h-5 animate-spin flex-shrink-0 text-primary" />
|
|
1565
|
-
) : (
|
|
1566
|
-
<span className="flex-shrink-0">{command.icon}</span>
|
|
1567
|
-
)}
|
|
1568
|
-
<div className="flex-1 min-w-0 overflow-hidden">
|
|
1569
|
-
<div className="text-sm font-medium text-foreground break-words">
|
|
1570
|
-
{command.id === '5' && savedSearches.includes(msg.id) ? '⭐ Pesquisa salva' : command.label}
|
|
1571
|
-
</div>
|
|
1572
|
-
<div className="text-sm text-muted-foreground break-words">
|
|
1573
|
-
{executingCommand === command.id ? 'Processando...' : command.description}
|
|
1574
|
-
</div>
|
|
1575
|
-
</div>
|
|
1576
|
-
</button>
|
|
1577
|
-
))}
|
|
1578
|
-
</div>
|
|
1579
|
-
</div>
|
|
1580
|
-
)}
|
|
1581
|
-
</div>
|
|
1582
|
-
)}
|
|
1583
|
-
</div>
|
|
1584
|
-
|
|
1585
|
-
{/* Message Actions */}
|
|
1586
|
-
<div className="flex items-center gap-2 mt-1 px-2">
|
|
1587
|
-
<span className="text-sm text-muted-foreground">
|
|
1588
|
-
{msg.timestamp.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })}
|
|
1589
|
-
</span>
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
{enablePodcastGeneration && msg.type === 'assistant' && msg.attachmentType !== 'podcast' && (
|
|
1593
|
-
<Button
|
|
1594
|
-
variant="ghost"
|
|
1595
|
-
size="sm"
|
|
1596
|
-
onClick={() => handleGeneratePodcast(msg.id, msg.content)}
|
|
1597
|
-
disabled={generatingPodcastId === msg.id}
|
|
1598
|
-
className="h-6 w-6 p-0 disabled:opacity-50 text-muted-foreground"
|
|
1599
|
-
title="Gerar Podcast"
|
|
1600
|
-
aria-label="Gerar Podcast"
|
|
1601
|
-
>
|
|
1602
|
-
{generatingPodcastId === msg.id ? (
|
|
1603
|
-
<Loader2 className="w-3 h-3 animate-spin" />
|
|
1604
|
-
) : (
|
|
1605
|
-
<Radio className="w-3 h-3" />
|
|
1606
|
-
)}
|
|
1607
|
-
</Button>
|
|
1608
|
-
)}
|
|
1609
|
-
|
|
1610
|
-
{msg.type === 'assistant' && (
|
|
1611
|
-
<div className="flex items-center gap-1 border-l border-border pl-2 ml-1">
|
|
1612
|
-
<Button
|
|
1613
|
-
variant="ghost"
|
|
1614
|
-
size="icon"
|
|
1615
|
-
className={cn(
|
|
1616
|
-
"h-6 w-6 rounded-full hover:bg-green-100 dark:hover:bg-green-900/20 hover:text-green-600",
|
|
1617
|
-
msg.evaluation === 'like' && "text-green-600 bg-green-100 dark:bg-green-900/20"
|
|
1618
|
-
)}
|
|
1619
|
-
onClick={() => handleEvaluationClick(msg.id, 'like')}
|
|
1620
|
-
title="Gostei"
|
|
1621
|
-
aria-label="Gostei"
|
|
1622
|
-
>
|
|
1623
|
-
<ThumbsUp className="h-3.5 w-3.5" />
|
|
1624
|
-
</Button>
|
|
1625
|
-
|
|
1626
|
-
<DropdownMenu>
|
|
1627
|
-
<DropdownMenuTrigger asChild>
|
|
1628
|
-
<Button
|
|
1629
|
-
variant="ghost"
|
|
1630
|
-
size="icon"
|
|
1631
|
-
className={cn(
|
|
1632
|
-
"h-6 w-6 rounded-full hover:bg-red-100 dark:hover:bg-red-900/20 hover:text-red-600",
|
|
1633
|
-
msg.evaluation === 'dislike' && "text-red-600 bg-red-100 dark:bg-red-900/20"
|
|
1634
|
-
)}
|
|
1635
|
-
title="Não gostei"
|
|
1636
|
-
aria-label="Não gostei"
|
|
1637
|
-
>
|
|
1638
|
-
<ThumbsDown className="h-3.5 w-3.5" />
|
|
1639
|
-
</Button>
|
|
1640
|
-
</DropdownMenuTrigger>
|
|
1641
|
-
<DropdownMenuContent align="start">
|
|
1642
|
-
{feedbackOptions && feedbackOptions.length > 0 ? (
|
|
1643
|
-
feedbackOptions.map((option, idx) => (
|
|
1644
|
-
<DropdownMenuItem key={idx} onClick={() => openFeedbackDialog(msg.id, option)}>
|
|
1645
|
-
{option}
|
|
1646
|
-
</DropdownMenuItem>
|
|
1647
|
-
))
|
|
1648
|
-
) : (
|
|
1649
|
-
<>
|
|
1650
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Não era o que eu procurava')}>
|
|
1651
|
-
Não era o que eu procurava
|
|
1652
|
-
</DropdownMenuItem>
|
|
1653
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Informação incorreta')}>
|
|
1654
|
-
Informação incorreta
|
|
1655
|
-
</DropdownMenuItem>
|
|
1656
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, 'Resposta incompleta')}>
|
|
1657
|
-
Resposta incompleta
|
|
1658
|
-
</DropdownMenuItem>
|
|
1659
|
-
</>
|
|
1660
|
-
)}
|
|
1661
|
-
<DropdownMenuItem onClick={() => openFeedbackDialog(msg.id, null)}>
|
|
1662
|
-
Outros...
|
|
1663
|
-
</DropdownMenuItem>
|
|
1664
|
-
</DropdownMenuContent>
|
|
1665
|
-
</DropdownMenu>
|
|
1666
|
-
</div>
|
|
1667
|
-
)}
|
|
1668
|
-
|
|
1669
|
-
<Button
|
|
1670
|
-
variant="ghost"
|
|
1671
|
-
size="sm"
|
|
1672
|
-
onClick={() => handleCopyMessage(msg.content, msg.id)}
|
|
1673
|
-
className={`h-6 w-6 p-0 ${copiedId === msg.id ? 'text-[var(--toast-success-icon)]' : 'text-muted-foreground'}`}
|
|
1674
|
-
aria-label={copiedId === msg.id ? "Copiado" : "Copiar mensagem"}
|
|
1675
|
-
>
|
|
1676
|
-
{copiedId === msg.id ? (
|
|
1677
|
-
<Check className="w-3 h-3" />
|
|
1678
|
-
) : (
|
|
1679
|
-
<Copy className="w-3 h-3" />
|
|
1680
|
-
)}
|
|
1681
|
-
</Button>
|
|
1682
|
-
</div>
|
|
1683
|
-
</div>
|
|
1684
|
-
</motion.div>
|
|
525
|
+
msg={msg}
|
|
526
|
+
copiedId={copiedId}
|
|
527
|
+
generatingPodcastId={generatingPodcastId}
|
|
528
|
+
executingCommand={executingCommand}
|
|
529
|
+
savedSearches={savedSearches}
|
|
530
|
+
enablePodcastGeneration={enablePodcastGeneration}
|
|
531
|
+
feedbackOptions={feedbackOptions}
|
|
532
|
+
onCopyMessage={handleCopyMessage}
|
|
533
|
+
onToggleFavorite={handleToggleFavorite}
|
|
534
|
+
onGeneratePodcast={handleGeneratePodcast}
|
|
535
|
+
onDownloadDocument={handleDownloadDocument}
|
|
536
|
+
onDownloadPodcast={handleDownloadPodcast}
|
|
537
|
+
onEditDocument={handleEditDocument}
|
|
538
|
+
onOpenSearchResult={handleOpenSearchResult}
|
|
539
|
+
onExecuteSearchCommand={handleExecuteSearchCommand}
|
|
540
|
+
onEvaluationClick={handleEvaluationClick}
|
|
541
|
+
onOpenFeedbackDialog={openFeedbackDialog}
|
|
542
|
+
/>
|
|
1685
543
|
))}
|
|
1686
544
|
|
|
1687
545
|
{/* Typing Indicator */}
|
|
1688
|
-
{isProcessing &&
|
|
1689
|
-
<motion.div
|
|
1690
|
-
initial={{ opacity: 0, y: 10 }}
|
|
1691
|
-
animate={{ opacity: 1, y: 0 }}
|
|
1692
|
-
className="flex gap-2 justify-start"
|
|
1693
|
-
>
|
|
1694
|
-
{/* Avatar do Assistente */}
|
|
1695
|
-
<div className="flex-shrink-0 pt-1">
|
|
1696
|
-
<XerticaOrbe size={32} />
|
|
1697
|
-
</div>
|
|
1698
|
-
|
|
1699
|
-
<div
|
|
1700
|
-
className="px-4 py-3 bg-muted rounded-2xl"
|
|
1701
|
-
>
|
|
1702
|
-
<div className="flex gap-1">
|
|
1703
|
-
<motion.div
|
|
1704
|
-
className="w-2 h-2 rounded-full bg-muted-foreground"
|
|
1705
|
-
animate={{ y: [0, -8, 0] }}
|
|
1706
|
-
transition={{ repeat: Infinity, duration: 0.6, delay: 0 }}
|
|
1707
|
-
/>
|
|
1708
|
-
<motion.div
|
|
1709
|
-
className="w-2 h-2 rounded-full bg-muted-foreground"
|
|
1710
|
-
animate={{ y: [0, -8, 0] }}
|
|
1711
|
-
transition={{ repeat: Infinity, duration: 0.6, delay: 0.2 }}
|
|
1712
|
-
/>
|
|
1713
|
-
<motion.div
|
|
1714
|
-
className="w-2 h-2 rounded-full bg-muted-foreground"
|
|
1715
|
-
animate={{ y: [0, -8, 0] }}
|
|
1716
|
-
transition={{ repeat: Infinity, duration: 0.6, delay: 0.4 }}
|
|
1717
|
-
/>
|
|
1718
|
-
</div>
|
|
1719
|
-
</div>
|
|
1720
|
-
</motion.div>
|
|
1721
|
-
)}
|
|
546
|
+
{isProcessing && <AssistantTypingIndicator />}
|
|
1722
547
|
|
|
1723
548
|
<div ref={messagesEndRef} />
|
|
1724
549
|
</div>
|
|
@@ -1727,82 +552,21 @@ export function XerticaAssistant({
|
|
|
1727
552
|
</div>
|
|
1728
553
|
)}
|
|
1729
554
|
|
|
555
|
+
{/* History / Favorites Tab */}
|
|
1730
556
|
{((abaSelecionada === 'historico' && showHistory) || (abaSelecionada === 'favoritos' && showFavorites)) && (
|
|
1731
|
-
<
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
<Plus className="w-4 h-4 mr-2" />
|
|
1741
|
-
Nova Conversa
|
|
1742
|
-
</Button>
|
|
1743
|
-
|
|
1744
|
-
{/* Conversations List */}
|
|
1745
|
-
<div className="space-y-2">
|
|
1746
|
-
{conversasFiltradas.length === 0 ? (
|
|
1747
|
-
<div className="text-center py-8">
|
|
1748
|
-
<Heart className="w-12 h-12 mx-auto text-muted-foreground/50 mb-2" />
|
|
1749
|
-
<p className="text-muted-foreground">
|
|
1750
|
-
{abaSelecionada === 'favoritos'
|
|
1751
|
-
? 'Nenhuma conversa favorita ainda'
|
|
1752
|
-
: 'Nenhuma conversa no histórico'}
|
|
1753
|
-
</p>
|
|
1754
|
-
</div>
|
|
1755
|
-
) : (
|
|
1756
|
-
conversasFiltradas.map((conversa) => (
|
|
1757
|
-
<div
|
|
1758
|
-
key={conversa.id}
|
|
1759
|
-
onClick={() => handleSelecionarConversa(conversa.id)}
|
|
1760
|
-
className={cn(
|
|
1761
|
-
"p-3 rounded-[var(--radius)] cursor-pointer transition-colors duration-200 border",
|
|
1762
|
-
conversa.id === conversaAtual
|
|
1763
|
-
? "border-primary bg-primary/10"
|
|
1764
|
-
: "border-border hover:bg-muted"
|
|
1765
|
-
)}
|
|
1766
|
-
>
|
|
1767
|
-
<div className="flex items-start justify-between mb-1">
|
|
1768
|
-
<h4 className="text-sm font-medium text-foreground truncate flex-1">
|
|
1769
|
-
{conversa.title}
|
|
1770
|
-
</h4>
|
|
1771
|
-
<Button
|
|
1772
|
-
variant="ghost"
|
|
1773
|
-
size="sm"
|
|
1774
|
-
onClick={(e) => {
|
|
1775
|
-
e.stopPropagation();
|
|
1776
|
-
handleToggleFavoritaConversa(conversa.id);
|
|
1777
|
-
}}
|
|
1778
|
-
className="h-6 w-6 p-0 flex-shrink-0 ml-1"
|
|
1779
|
-
>
|
|
1780
|
-
<Heart
|
|
1781
|
-
className={cn(
|
|
1782
|
-
"w-3 h-3",
|
|
1783
|
-
conversa.isFavorite ? "text-destructive fill-current" : "text-muted-foreground"
|
|
1784
|
-
)}
|
|
1785
|
-
/>
|
|
1786
|
-
</Button>
|
|
1787
|
-
</div>
|
|
1788
|
-
{conversa.lastMessage && (
|
|
1789
|
-
<p className="text-sm text-muted-foreground truncate mb-1">
|
|
1790
|
-
{conversa.lastMessage}
|
|
1791
|
-
</p>
|
|
1792
|
-
)}
|
|
1793
|
-
<p className="text-sm text-muted-foreground">
|
|
1794
|
-
{conversa.timestamp}
|
|
1795
|
-
</p>
|
|
1796
|
-
</div>
|
|
1797
|
-
))
|
|
1798
|
-
)}
|
|
1799
|
-
</div>
|
|
1800
|
-
</div>
|
|
1801
|
-
</ScrollArea>
|
|
557
|
+
<AssistantConversationList
|
|
558
|
+
conversations={conversasFiltradas}
|
|
559
|
+
currentConversationId={conversaAtual}
|
|
560
|
+
activeTab={abaSelecionada}
|
|
561
|
+
isFullPage={isFullPage}
|
|
562
|
+
onNewConversation={handleNovaConversa}
|
|
563
|
+
onSelectConversation={handleSelecionarConversa}
|
|
564
|
+
onToggleFavorite={handleToggleFavoritaConversa}
|
|
565
|
+
/>
|
|
1802
566
|
)}
|
|
1803
567
|
</div>
|
|
1804
568
|
|
|
1805
|
-
{/* Modern Input Area
|
|
569
|
+
{/* Modern Input Area — only visible in chat mode */}
|
|
1806
570
|
{abaSelecionada === 'chat' && (
|
|
1807
571
|
<ModernChatInput
|
|
1808
572
|
value={mensagem}
|
|
@@ -1810,7 +574,7 @@ export function XerticaAssistant({
|
|
|
1810
574
|
onSubmit={handleEnviarMensagem}
|
|
1811
575
|
onFileUpload={enableFileAttachment ? () => fileInputRef.current?.click() : undefined}
|
|
1812
576
|
onAudioUpload={enableAudioInput ? () => audioInputRef.current?.click() : undefined}
|
|
1813
|
-
placeholder="Envie uma mensagem
|
|
577
|
+
placeholder="Envie uma mensagem"
|
|
1814
578
|
disabled={isProcessing}
|
|
1815
579
|
isFullPage={isFullPage}
|
|
1816
580
|
enableAudioInput={enableAudioInput}
|
|
@@ -1826,38 +590,12 @@ export function XerticaAssistant({
|
|
|
1826
590
|
</div>
|
|
1827
591
|
|
|
1828
592
|
{/* Feedback Dialog */}
|
|
1829
|
-
<
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
<DialogDescription>
|
|
1836
|
-
{evaluationState.category
|
|
1837
|
-
? "Gostaria de adicionar algum comentário? (Opcional)"
|
|
1838
|
-
: "Conte-nos por que essa resposta não foi útil para que possamos melhorar."}
|
|
1839
|
-
</DialogDescription>
|
|
1840
|
-
</DialogHeader>
|
|
1841
|
-
<div className="grid gap-4 py-4 px-6">
|
|
1842
|
-
<Textarea
|
|
1843
|
-
className="min-h-[100px]"
|
|
1844
|
-
placeholder={evaluationState.category ? "Comentário adicional..." : "Descreva o motivo..."}
|
|
1845
|
-
aria-label={evaluationState.category ? "Comentário adicional" : "Descreva o motivo"}
|
|
1846
|
-
value={evaluationState.reason}
|
|
1847
|
-
onChange={(e) => setEvaluationState(prev => ({ ...prev, reason: e.target.value }))}
|
|
1848
|
-
rows={4}
|
|
1849
|
-
/>
|
|
1850
|
-
</div>
|
|
1851
|
-
<DialogFooter>
|
|
1852
|
-
<Button variant="outline" onClick={() => setEvaluationState(prev => ({ ...prev, isOpen: false }))}>
|
|
1853
|
-
Cancelar
|
|
1854
|
-
</Button>
|
|
1855
|
-
<Button onClick={handleSubmitDislike} disabled={!evaluationState.category && !evaluationState.reason.trim()}>
|
|
1856
|
-
{evaluationState.category ? 'Confirmar e Enviar' : 'Enviar Feedback'}
|
|
1857
|
-
</Button>
|
|
1858
|
-
</DialogFooter>
|
|
1859
|
-
</DialogContent>
|
|
1860
|
-
</Dialog>
|
|
593
|
+
<AssistantFeedbackDialog
|
|
594
|
+
state={evaluationState}
|
|
595
|
+
onReasonChange={(reason) => setEvaluationState(prev => ({ ...prev, reason }))}
|
|
596
|
+
onClose={() => setEvaluationState(prev => ({ ...prev, isOpen: false }))}
|
|
597
|
+
onSubmit={handleSubmitDislike}
|
|
598
|
+
/>
|
|
1861
599
|
</>
|
|
1862
600
|
);
|
|
1863
601
|
}
|