@greatapps/greatagents-ui 0.1.0 → 0.2.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/dist/index.d.ts +152 -1
- package/dist/index.js +3154 -0
- package/dist/index.js.map +1 -1
- package/package.json +13 -3
- package/src/components/agents/agent-edit-form.tsx +218 -0
- package/src/components/agents/agent-form-dialog.tsx +177 -0
- package/src/components/agents/agent-objectives-list.tsx +406 -0
- package/src/components/agents/agent-prompt-editor.tsx +406 -0
- package/src/components/agents/agent-tabs.tsx +63 -0
- package/src/components/agents/agent-tools-list.tsx +377 -0
- package/src/components/agents/agents-table.tsx +205 -0
- package/src/components/tools/tool-credentials-form.tsx +572 -0
- package/src/components/tools/tool-form-dialog.tsx +342 -0
- package/src/components/tools/tools-table.tsx +225 -0
- package/src/components/ui/sortable.tsx +577 -0
- package/src/index.ts +16 -0
- package/src/lib/compose-refs.ts +44 -0
- package/src/pages/agent-detail-page.tsx +112 -0
- package/src/pages/agents-page.tsx +45 -0
- package/src/pages/credentials-page.tsx +50 -0
- package/src/pages/index.ts +4 -0
- package/src/pages/tools-page.tsx +52 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
2
|
+
import type { Agent, PromptVersion, Objective, AgentTool, Tool } from "../../types";
|
|
3
|
+
import type { GagentsHookConfig } from "../../hooks/types";
|
|
4
|
+
import { usePromptVersions } from "../../hooks";
|
|
5
|
+
import { useUpdateAgent } from "../../hooks";
|
|
6
|
+
import { useAgentTools } from "../../hooks";
|
|
7
|
+
import { useObjectives } from "../../hooks";
|
|
8
|
+
import { useTools } from "../../hooks";
|
|
9
|
+
import { Button, Input, Skeleton, Badge } from "@greatapps/greatauth-ui/ui";
|
|
10
|
+
import { FileText, Loader2, ChevronDown, ChevronUp, RotateCcw } from "lucide-react";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
|
|
13
|
+
interface AgentPromptEditorProps {
|
|
14
|
+
config: GagentsHookConfig;
|
|
15
|
+
agent: Agent;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function formatDate(dateStr: string): string {
|
|
19
|
+
const date = new Date(dateStr);
|
|
20
|
+
return date.toLocaleDateString("pt-BR", {
|
|
21
|
+
day: "2-digit",
|
|
22
|
+
month: "2-digit",
|
|
23
|
+
year: "numeric",
|
|
24
|
+
hour: "2-digit",
|
|
25
|
+
minute: "2-digit",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function computeDiff(
|
|
30
|
+
oldText: string,
|
|
31
|
+
newText: string,
|
|
32
|
+
): { type: "added" | "removed" | "equal"; line: string }[] {
|
|
33
|
+
const oldLines = oldText.split("\n");
|
|
34
|
+
const newLines = newText.split("\n");
|
|
35
|
+
const result: { type: "added" | "removed" | "equal"; line: string }[] = [];
|
|
36
|
+
const maxLen = Math.max(oldLines.length, newLines.length);
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < maxLen; i++) {
|
|
39
|
+
const oldLine = i < oldLines.length ? oldLines[i] : undefined;
|
|
40
|
+
const newLine = i < newLines.length ? newLines[i] : undefined;
|
|
41
|
+
|
|
42
|
+
if (oldLine === newLine) {
|
|
43
|
+
result.push({ type: "equal", line: newLine! });
|
|
44
|
+
} else {
|
|
45
|
+
if (oldLine !== undefined) {
|
|
46
|
+
result.push({ type: "removed", line: oldLine });
|
|
47
|
+
}
|
|
48
|
+
if (newLine !== undefined) {
|
|
49
|
+
result.push({ type: "added", line: newLine });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function buildPreview(
|
|
58
|
+
promptText: string,
|
|
59
|
+
objectives: Objective[],
|
|
60
|
+
agentTools: AgentTool[],
|
|
61
|
+
allTools: Tool[],
|
|
62
|
+
): string {
|
|
63
|
+
let preview = promptText;
|
|
64
|
+
|
|
65
|
+
const activeObjectives = objectives.filter((o) => o.active);
|
|
66
|
+
if (activeObjectives.length > 0) {
|
|
67
|
+
preview += "\n\n[SKILLS DISPONÍVEIS]\n";
|
|
68
|
+
for (const obj of activeObjectives) {
|
|
69
|
+
preview += `- ${obj.title}`;
|
|
70
|
+
if (obj.prompt) preview += `: ${obj.prompt}`;
|
|
71
|
+
preview += "\n";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const enabledAgentTools = agentTools.filter((at) => at.enabled);
|
|
76
|
+
if (enabledAgentTools.length > 0) {
|
|
77
|
+
const toolMap = new Map(allTools.map((t) => [t.id, t]));
|
|
78
|
+
preview += "\n[TOOLS DISPONÍVEIS]\n";
|
|
79
|
+
for (const at of enabledAgentTools) {
|
|
80
|
+
const tool = toolMap.get(at.id_tool);
|
|
81
|
+
const name = tool?.name || `Tool #${at.id_tool}`;
|
|
82
|
+
const desc = tool?.description ? `: ${tool.description}` : "";
|
|
83
|
+
preview += `- ${name}${desc}`;
|
|
84
|
+
if (at.custom_instructions) {
|
|
85
|
+
preview += `\n Instruções: ${at.custom_instructions}`;
|
|
86
|
+
}
|
|
87
|
+
preview += "\n";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return preview;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function AgentPromptEditor({ config, agent }: AgentPromptEditorProps) {
|
|
95
|
+
const { data: versionsData, isLoading } = usePromptVersions(config, agent.id);
|
|
96
|
+
const updateAgent = useUpdateAgent(config);
|
|
97
|
+
const { data: objectivesData } = useObjectives(config, agent.id);
|
|
98
|
+
const { data: agentToolsData } = useAgentTools(config, agent.id);
|
|
99
|
+
const { data: toolsData } = useTools(config);
|
|
100
|
+
|
|
101
|
+
const [trackedAgentId, setTrackedAgentId] = useState(agent.id);
|
|
102
|
+
const [promptText, setPromptText] = useState(agent.prompt || "");
|
|
103
|
+
const [changeNotes, setChangeNotes] = useState("");
|
|
104
|
+
const [showPreview, setShowPreview] = useState(false);
|
|
105
|
+
const [compareVersionId, setCompareVersionId] = useState<number | null>(null);
|
|
106
|
+
|
|
107
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
108
|
+
|
|
109
|
+
// Reset prompt text when agent changes
|
|
110
|
+
if (trackedAgentId !== agent.id) {
|
|
111
|
+
setTrackedAgentId(agent.id);
|
|
112
|
+
setPromptText(agent.prompt || "");
|
|
113
|
+
setCompareVersionId(null);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Auto-resize textarea
|
|
117
|
+
const autoResize = useCallback(() => {
|
|
118
|
+
const el = textareaRef.current;
|
|
119
|
+
if (!el) return;
|
|
120
|
+
el.style.height = "auto";
|
|
121
|
+
el.style.height = `${Math.max(300, el.scrollHeight)}px`;
|
|
122
|
+
}, []);
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
autoResize();
|
|
126
|
+
}, [promptText, autoResize]);
|
|
127
|
+
|
|
128
|
+
// Tab key support
|
|
129
|
+
function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {
|
|
130
|
+
if (e.key === "Tab") {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
const el = e.currentTarget;
|
|
133
|
+
const start = el.selectionStart;
|
|
134
|
+
const end = el.selectionEnd;
|
|
135
|
+
const value = el.value;
|
|
136
|
+
const newValue = value.substring(0, start) + " " + value.substring(end);
|
|
137
|
+
setPromptText(newValue);
|
|
138
|
+
// Restore cursor position after React re-render
|
|
139
|
+
requestAnimationFrame(() => {
|
|
140
|
+
el.selectionStart = el.selectionEnd = start + 2;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const versions = versionsData?.data || [];
|
|
146
|
+
const sortedVersions = [...versions].sort(
|
|
147
|
+
(a: PromptVersion, b: PromptVersion) =>
|
|
148
|
+
new Date(b.datetime_add).getTime() - new Date(a.datetime_add).getTime(),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const currentVersion = sortedVersions.length > 0 ? sortedVersions[0] : null;
|
|
152
|
+
const compareVersion = sortedVersions.find((v) => v.id === compareVersionId);
|
|
153
|
+
|
|
154
|
+
// Diff: always compare selected older version against current
|
|
155
|
+
const diffLines =
|
|
156
|
+
currentVersion && compareVersion && compareVersion.id !== currentVersion.id
|
|
157
|
+
? computeDiff(compareVersion.prompt_content ?? "", currentVersion.prompt_content ?? "")
|
|
158
|
+
: null;
|
|
159
|
+
|
|
160
|
+
async function handleSave() {
|
|
161
|
+
const body: Record<string, unknown> = {
|
|
162
|
+
prompt: promptText.trim(),
|
|
163
|
+
};
|
|
164
|
+
if (changeNotes.trim()) {
|
|
165
|
+
body.change_notes = changeNotes.trim();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
await updateAgent.mutateAsync({ id: agent.id, body });
|
|
170
|
+
setChangeNotes("");
|
|
171
|
+
toast.success("Prompt salvo com sucesso");
|
|
172
|
+
} catch {
|
|
173
|
+
toast.error("Erro ao salvar prompt");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function handleRestore(version: PromptVersion) {
|
|
178
|
+
setPromptText(version.prompt_content ?? "");
|
|
179
|
+
setChangeNotes(`Restaurado da v${version.version_number}`);
|
|
180
|
+
toast.info("Prompt restaurado no editor. Clique em Salvar para confirmar.");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const charCount = promptText.length;
|
|
184
|
+
const tokenEstimate = Math.ceil(charCount / 4);
|
|
185
|
+
|
|
186
|
+
const objectives = objectivesData?.data || [];
|
|
187
|
+
const agentTools = agentToolsData?.data || [];
|
|
188
|
+
const allTools = toolsData?.data || [];
|
|
189
|
+
const previewText = buildPreview(promptText, objectives, agentTools, allTools);
|
|
190
|
+
|
|
191
|
+
if (isLoading) {
|
|
192
|
+
return (
|
|
193
|
+
<div className="space-y-3 p-4">
|
|
194
|
+
{Array.from({ length: 3 }).map((_, i) => (
|
|
195
|
+
<Skeleton key={i} className="h-14 w-full" />
|
|
196
|
+
))}
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<div className="flex flex-col gap-4 p-4 lg:flex-row">
|
|
203
|
+
{/* Editor section */}
|
|
204
|
+
<div className="min-w-0 flex-1 space-y-4">
|
|
205
|
+
{/* Textarea */}
|
|
206
|
+
<div className="space-y-2">
|
|
207
|
+
<textarea
|
|
208
|
+
ref={textareaRef}
|
|
209
|
+
value={promptText}
|
|
210
|
+
onChange={(e) => setPromptText(e.target.value)}
|
|
211
|
+
onKeyDown={handleKeyDown}
|
|
212
|
+
placeholder="Escreva o prompt do sistema aqui..."
|
|
213
|
+
disabled={updateAgent.isPending}
|
|
214
|
+
className="w-full resize-none rounded-lg border bg-background p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
|
|
215
|
+
style={{ minHeight: "300px" }}
|
|
216
|
+
/>
|
|
217
|
+
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
|
218
|
+
<span>{charCount.toLocaleString("pt-BR")} caracteres</span>
|
|
219
|
+
<span>·</span>
|
|
220
|
+
<span>~{tokenEstimate.toLocaleString("pt-BR")} tokens</span>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
{/* Save row */}
|
|
225
|
+
<div className="flex items-center gap-3">
|
|
226
|
+
<Input
|
|
227
|
+
value={changeNotes}
|
|
228
|
+
onChange={(e) => setChangeNotes(e.target.value)}
|
|
229
|
+
placeholder="O que mudou? (opcional)"
|
|
230
|
+
disabled={updateAgent.isPending}
|
|
231
|
+
className="flex-1"
|
|
232
|
+
onKeyDown={(e) => {
|
|
233
|
+
if (e.key === "Enter") {
|
|
234
|
+
e.preventDefault();
|
|
235
|
+
handleSave();
|
|
236
|
+
}
|
|
237
|
+
}}
|
|
238
|
+
/>
|
|
239
|
+
<Button
|
|
240
|
+
onClick={handleSave}
|
|
241
|
+
disabled={updateAgent.isPending || !promptText.trim()}
|
|
242
|
+
>
|
|
243
|
+
{updateAgent.isPending && (
|
|
244
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
245
|
+
)}
|
|
246
|
+
Salvar
|
|
247
|
+
</Button>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
{/* Preview section */}
|
|
251
|
+
<div className="rounded-lg border">
|
|
252
|
+
<button
|
|
253
|
+
type="button"
|
|
254
|
+
onClick={() => setShowPreview((prev) => !prev)}
|
|
255
|
+
className="flex w-full items-center justify-between px-4 py-3 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
|
256
|
+
>
|
|
257
|
+
<span>Preview do prompt final</span>
|
|
258
|
+
{showPreview ? (
|
|
259
|
+
<ChevronUp className="h-4 w-4" />
|
|
260
|
+
) : (
|
|
261
|
+
<ChevronDown className="h-4 w-4" />
|
|
262
|
+
)}
|
|
263
|
+
</button>
|
|
264
|
+
{showPreview && (
|
|
265
|
+
<div className="border-t px-4 py-3">
|
|
266
|
+
<pre className="max-h-96 overflow-auto whitespace-pre-wrap font-mono text-sm leading-relaxed">
|
|
267
|
+
{previewText.split("\n").map((line, i) => {
|
|
268
|
+
const isSection =
|
|
269
|
+
line.startsWith("[SKILLS DISPONÍVEIS]") ||
|
|
270
|
+
line.startsWith("[TOOLS DISPONÍVEIS]");
|
|
271
|
+
return (
|
|
272
|
+
<span
|
|
273
|
+
key={i}
|
|
274
|
+
className={isSection ? "font-semibold text-muted-foreground" : ""}
|
|
275
|
+
>
|
|
276
|
+
{line}
|
|
277
|
+
{"\n"}
|
|
278
|
+
</span>
|
|
279
|
+
);
|
|
280
|
+
})}
|
|
281
|
+
</pre>
|
|
282
|
+
</div>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
{/* Diff panel (when comparing) */}
|
|
287
|
+
{diffLines && compareVersion && currentVersion && (
|
|
288
|
+
<div>
|
|
289
|
+
<div className="mb-2 flex items-center justify-between">
|
|
290
|
+
<h3 className="text-sm font-medium text-muted-foreground">
|
|
291
|
+
Diferenças: v{compareVersion.version_number} → v{currentVersion.version_number} (actual)
|
|
292
|
+
</h3>
|
|
293
|
+
<Button
|
|
294
|
+
variant="ghost"
|
|
295
|
+
size="sm"
|
|
296
|
+
onClick={() => setCompareVersionId(null)}
|
|
297
|
+
className="text-xs"
|
|
298
|
+
>
|
|
299
|
+
Fechar
|
|
300
|
+
</Button>
|
|
301
|
+
</div>
|
|
302
|
+
<div className="max-h-64 overflow-auto rounded-lg border font-mono text-sm">
|
|
303
|
+
{diffLines.map((line, i) => (
|
|
304
|
+
<div
|
|
305
|
+
key={i}
|
|
306
|
+
className={`whitespace-pre-wrap px-3 py-0.5 ${
|
|
307
|
+
line.type === "added"
|
|
308
|
+
? "bg-green-500/10 text-green-700 dark:text-green-400"
|
|
309
|
+
: line.type === "removed"
|
|
310
|
+
? "bg-red-500/10 text-red-700 dark:text-red-400"
|
|
311
|
+
: ""
|
|
312
|
+
}`}
|
|
313
|
+
>
|
|
314
|
+
<span className="mr-2 inline-block w-4 select-none text-muted-foreground">
|
|
315
|
+
{line.type === "added" ? "+" : line.type === "removed" ? "-" : " "}
|
|
316
|
+
</span>
|
|
317
|
+
{line.line || " "}
|
|
318
|
+
</div>
|
|
319
|
+
))}
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
)}
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
{/* Timeline section (right) */}
|
|
326
|
+
<div className="w-full space-y-2 lg:w-80 lg:shrink-0">
|
|
327
|
+
<h3 className="text-sm font-medium text-muted-foreground">
|
|
328
|
+
Histórico de Versões
|
|
329
|
+
</h3>
|
|
330
|
+
{sortedVersions.length === 0 ? (
|
|
331
|
+
<div className="flex flex-col items-center justify-center rounded-lg border border-dashed p-8 text-center">
|
|
332
|
+
<FileText className="mb-2 h-8 w-8 text-muted-foreground" />
|
|
333
|
+
<p className="text-sm text-muted-foreground">
|
|
334
|
+
Nenhuma versão encontrada. Salve o prompt para criar a primeira versão.
|
|
335
|
+
</p>
|
|
336
|
+
</div>
|
|
337
|
+
) : (
|
|
338
|
+
<div className="space-y-1">
|
|
339
|
+
{sortedVersions.map((version, idx) => {
|
|
340
|
+
const isCurrent = idx === 0;
|
|
341
|
+
const isComparing = version.id === compareVersionId;
|
|
342
|
+
return (
|
|
343
|
+
<div
|
|
344
|
+
key={version.id}
|
|
345
|
+
className={`rounded-lg border p-3 transition-colors ${
|
|
346
|
+
isCurrent
|
|
347
|
+
? "border-primary bg-primary/5"
|
|
348
|
+
: isComparing
|
|
349
|
+
? "border-muted-foreground/30 bg-muted/50"
|
|
350
|
+
: ""
|
|
351
|
+
}`}
|
|
352
|
+
>
|
|
353
|
+
<div className="flex items-center justify-between gap-2">
|
|
354
|
+
<span className="text-sm font-medium">
|
|
355
|
+
v{version.version_number}
|
|
356
|
+
</span>
|
|
357
|
+
{isCurrent && (
|
|
358
|
+
<Badge variant="default" className="text-[10px] px-1.5 py-0">
|
|
359
|
+
Actual
|
|
360
|
+
</Badge>
|
|
361
|
+
)}
|
|
362
|
+
</div>
|
|
363
|
+
<div className="mt-1 text-xs text-muted-foreground">
|
|
364
|
+
{formatDate(version.datetime_add)}
|
|
365
|
+
</div>
|
|
366
|
+
<div className="mt-1 flex items-center gap-2 text-xs text-muted-foreground">
|
|
367
|
+
<span>{(version.prompt_content ?? "").length} chars</span>
|
|
368
|
+
<span>·</span>
|
|
369
|
+
<span className="truncate font-mono">
|
|
370
|
+
{(version.prompt_hash ?? "").slice(0, 8)}
|
|
371
|
+
</span>
|
|
372
|
+
</div>
|
|
373
|
+
{version.change_notes && (
|
|
374
|
+
<div className="mt-1.5 text-xs italic text-muted-foreground">
|
|
375
|
+
{version.change_notes}
|
|
376
|
+
</div>
|
|
377
|
+
)}
|
|
378
|
+
{!isCurrent && (
|
|
379
|
+
<div className="mt-2 flex items-center gap-3">
|
|
380
|
+
<button
|
|
381
|
+
type="button"
|
|
382
|
+
onClick={() => setCompareVersionId(isComparing ? null : version.id)}
|
|
383
|
+
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
|
|
384
|
+
>
|
|
385
|
+
<FileText className="h-3 w-3" />
|
|
386
|
+
{isComparing ? "Ocultar diff" : "Comparar"}
|
|
387
|
+
</button>
|
|
388
|
+
<button
|
|
389
|
+
type="button"
|
|
390
|
+
onClick={() => handleRestore(version)}
|
|
391
|
+
className="flex items-center gap-1 text-xs text-primary hover:underline"
|
|
392
|
+
>
|
|
393
|
+
<RotateCcw className="h-3 w-3" />
|
|
394
|
+
Restaurar
|
|
395
|
+
</button>
|
|
396
|
+
</div>
|
|
397
|
+
)}
|
|
398
|
+
</div>
|
|
399
|
+
);
|
|
400
|
+
})}
|
|
401
|
+
</div>
|
|
402
|
+
)}
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Agent } from "../../types";
|
|
2
|
+
import type { GagentsHookConfig } from "../../hooks/types";
|
|
3
|
+
import { AgentToolsList } from "./agent-tools-list";
|
|
4
|
+
import { AgentObjectivesList } from "./agent-objectives-list";
|
|
5
|
+
import { AgentPromptEditor } from "./agent-prompt-editor";
|
|
6
|
+
import {
|
|
7
|
+
Tabs,
|
|
8
|
+
TabsList,
|
|
9
|
+
TabsTrigger,
|
|
10
|
+
TabsContent,
|
|
11
|
+
} from "@greatapps/greatauth-ui/ui";
|
|
12
|
+
import { Wrench, Target, FileText, MessageCircle } from "lucide-react";
|
|
13
|
+
|
|
14
|
+
interface AgentTabsProps {
|
|
15
|
+
agent: Agent;
|
|
16
|
+
config: GagentsHookConfig;
|
|
17
|
+
renderConversationsTab?: (agent: Agent) => React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function AgentTabs({ agent, config, renderConversationsTab }: AgentTabsProps) {
|
|
21
|
+
return (
|
|
22
|
+
<Tabs defaultValue="prompt">
|
|
23
|
+
<TabsList>
|
|
24
|
+
<TabsTrigger value="prompt" className="flex items-center gap-1.5">
|
|
25
|
+
<FileText className="h-3.5 w-3.5" />
|
|
26
|
+
Prompt
|
|
27
|
+
</TabsTrigger>
|
|
28
|
+
<TabsTrigger value="objetivos" className="flex items-center gap-1.5">
|
|
29
|
+
<Target className="h-3.5 w-3.5" />
|
|
30
|
+
Objetivos
|
|
31
|
+
</TabsTrigger>
|
|
32
|
+
<TabsTrigger value="ferramentas" className="flex items-center gap-1.5">
|
|
33
|
+
<Wrench className="h-3.5 w-3.5" />
|
|
34
|
+
Ferramentas
|
|
35
|
+
</TabsTrigger>
|
|
36
|
+
{renderConversationsTab && (
|
|
37
|
+
<TabsTrigger value="conversas" className="flex items-center gap-1.5">
|
|
38
|
+
<MessageCircle className="h-3.5 w-3.5" />
|
|
39
|
+
Conversas
|
|
40
|
+
</TabsTrigger>
|
|
41
|
+
)}
|
|
42
|
+
</TabsList>
|
|
43
|
+
|
|
44
|
+
<TabsContent value="prompt" className="mt-4">
|
|
45
|
+
<AgentPromptEditor agent={agent} config={config} />
|
|
46
|
+
</TabsContent>
|
|
47
|
+
|
|
48
|
+
<TabsContent value="objetivos" className="mt-4">
|
|
49
|
+
<AgentObjectivesList agent={agent} config={config} />
|
|
50
|
+
</TabsContent>
|
|
51
|
+
|
|
52
|
+
<TabsContent value="ferramentas" className="mt-4">
|
|
53
|
+
<AgentToolsList agent={agent} config={config} />
|
|
54
|
+
</TabsContent>
|
|
55
|
+
|
|
56
|
+
{renderConversationsTab && (
|
|
57
|
+
<TabsContent value="conversas" className="mt-4">
|
|
58
|
+
{renderConversationsTab(agent)}
|
|
59
|
+
</TabsContent>
|
|
60
|
+
)}
|
|
61
|
+
</Tabs>
|
|
62
|
+
);
|
|
63
|
+
}
|