@greatapps/greatagents-ui 0.3.22 → 0.3.23
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.js +122 -123
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/agents/agent-definition-editor.tsx +47 -42
- package/src/components/agents/agent-edit-form.tsx +14 -14
- package/src/components/agents/agent-form-dialog.tsx +15 -15
- package/src/components/agents/agent-objectives-list.tsx +16 -16
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { useState, useRef, useCallback, useEffect } from "react";
|
|
1
|
+
import { useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
2
2
|
import type { Agent, ConversationFlowStep } from "../../types";
|
|
3
3
|
import type { GagentsHookConfig } from "../../hooks/types";
|
|
4
4
|
import { useUpdateAgent } from "../../hooks";
|
|
5
|
-
import { Button,
|
|
5
|
+
import { Button, Label } from "@greatapps/greatauth-ui/ui";
|
|
6
6
|
import { Loader2 } from "lucide-react";
|
|
7
7
|
import { toast } from "sonner";
|
|
8
8
|
import { ConversationFlowEditor } from "./conversation-flow-editor";
|
|
@@ -15,6 +15,7 @@ interface SectionDef {
|
|
|
15
15
|
key: "identity" | "mission" | "tone_style_format" | "rules" | "conversation_flow" | "context";
|
|
16
16
|
label: string;
|
|
17
17
|
helper: string;
|
|
18
|
+
placeholder: string;
|
|
18
19
|
required?: boolean;
|
|
19
20
|
field: keyof Agent;
|
|
20
21
|
type: "textarea" | "conversation_flow";
|
|
@@ -28,6 +29,7 @@ const SECTIONS: SectionDef[] = [
|
|
|
28
29
|
required: true,
|
|
29
30
|
field: "identity",
|
|
30
31
|
type: "textarea",
|
|
32
|
+
placeholder: "Você é a Ana, assistente virtual da Clínica Saúde & Bem-Estar. Você é simpática, profissional e sempre disposta a ajudar os pacientes...",
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
key: "mission",
|
|
@@ -36,6 +38,7 @@ const SECTIONS: SectionDef[] = [
|
|
|
36
38
|
required: true,
|
|
37
39
|
field: "mission",
|
|
38
40
|
type: "textarea",
|
|
41
|
+
placeholder: "Sua missão é ajudar pacientes a agendar consultas, tirar dúvidas sobre horários e especialidades, e fornecer informações sobre a clínica...",
|
|
39
42
|
},
|
|
40
43
|
{
|
|
41
44
|
key: "tone_style_format",
|
|
@@ -44,6 +47,7 @@ const SECTIONS: SectionDef[] = [
|
|
|
44
47
|
required: false,
|
|
45
48
|
field: "tone_style_format",
|
|
46
49
|
type: "textarea",
|
|
50
|
+
placeholder: "Use tom empático e acolhedor. Responda de forma clara e objetiva, em no máximo 3 parágrafos. Use listas quando houver múltiplas opções...",
|
|
47
51
|
},
|
|
48
52
|
{
|
|
49
53
|
key: "rules",
|
|
@@ -52,6 +56,7 @@ const SECTIONS: SectionDef[] = [
|
|
|
52
56
|
required: false,
|
|
53
57
|
field: "rules",
|
|
54
58
|
type: "textarea",
|
|
59
|
+
placeholder: "Nunca forneça diagnósticos médicos. Sempre recomende consulta presencial para urgências. Não agende consultas fora do horário de funcionamento (08h-18h)...",
|
|
55
60
|
},
|
|
56
61
|
{
|
|
57
62
|
key: "conversation_flow",
|
|
@@ -60,6 +65,7 @@ const SECTIONS: SectionDef[] = [
|
|
|
60
65
|
required: false,
|
|
61
66
|
field: "conversation_flow",
|
|
62
67
|
type: "conversation_flow",
|
|
68
|
+
placeholder: "",
|
|
63
69
|
},
|
|
64
70
|
{
|
|
65
71
|
key: "context",
|
|
@@ -68,6 +74,7 @@ const SECTIONS: SectionDef[] = [
|
|
|
68
74
|
required: false,
|
|
69
75
|
field: "context",
|
|
70
76
|
type: "textarea",
|
|
77
|
+
placeholder: "A clínica funciona de segunda a sexta, das 08h às 18h. Especialidades disponíveis: Cardiologia, Dermatologia, Ortopedia, Pediatria...",
|
|
71
78
|
},
|
|
72
79
|
];
|
|
73
80
|
|
|
@@ -181,8 +188,6 @@ export function AgentDefinitionEditor({ agent, config }: AgentDefinitionEditorPr
|
|
|
181
188
|
() => parseConversationFlow(agent.conversation_flow),
|
|
182
189
|
);
|
|
183
190
|
|
|
184
|
-
const [changeNotes, setChangeNotes] = useState("");
|
|
185
|
-
|
|
186
191
|
// Reset state when agent changes
|
|
187
192
|
if (trackedAgentId !== agent.id) {
|
|
188
193
|
setTrackedAgentId(agent.id);
|
|
@@ -194,7 +199,6 @@ export function AgentDefinitionEditor({ agent, config }: AgentDefinitionEditorPr
|
|
|
194
199
|
context: agent.context ?? "",
|
|
195
200
|
});
|
|
196
201
|
setConversationFlowSteps(parseConversationFlow(agent.conversation_flow));
|
|
197
|
-
setChangeNotes("");
|
|
198
202
|
}
|
|
199
203
|
|
|
200
204
|
function updateField(key: string, value: string) {
|
|
@@ -203,6 +207,28 @@ export function AgentDefinitionEditor({ agent, config }: AgentDefinitionEditorPr
|
|
|
203
207
|
|
|
204
208
|
const { chars, tokens } = computeTotals(fields, conversationFlowSteps);
|
|
205
209
|
|
|
210
|
+
const hasChanges = useMemo(() => {
|
|
211
|
+
if (fields.identity !== (agent.identity ?? "")) return true;
|
|
212
|
+
if (fields.mission !== (agent.mission ?? "")) return true;
|
|
213
|
+
if (fields.tone_style_format !== (agent.tone_style_format ?? "")) return true;
|
|
214
|
+
if (fields.rules !== (agent.rules ?? "")) return true;
|
|
215
|
+
if (fields.context !== (agent.context ?? "")) return true;
|
|
216
|
+
const originalSteps = parseConversationFlow(agent.conversation_flow);
|
|
217
|
+
if (JSON.stringify(conversationFlowSteps) !== JSON.stringify(originalSteps)) return true;
|
|
218
|
+
return false;
|
|
219
|
+
}, [fields, conversationFlowSteps, agent]);
|
|
220
|
+
|
|
221
|
+
function discard() {
|
|
222
|
+
setFields({
|
|
223
|
+
identity: agent.identity ?? "",
|
|
224
|
+
mission: agent.mission ?? "",
|
|
225
|
+
tone_style_format: agent.tone_style_format ?? "",
|
|
226
|
+
rules: agent.rules ?? "",
|
|
227
|
+
context: agent.context ?? "",
|
|
228
|
+
});
|
|
229
|
+
setConversationFlowSteps(parseConversationFlow(agent.conversation_flow));
|
|
230
|
+
}
|
|
231
|
+
|
|
206
232
|
async function handleSave() {
|
|
207
233
|
if (!fields.identity.trim() || !fields.mission.trim()) {
|
|
208
234
|
toast.error("Identidade e Missão são campos obrigatórios");
|
|
@@ -220,13 +246,8 @@ export function AgentDefinitionEditor({ agent, config }: AgentDefinitionEditorPr
|
|
|
220
246
|
context: fields.context.trim() || null,
|
|
221
247
|
};
|
|
222
248
|
|
|
223
|
-
if (changeNotes.trim()) {
|
|
224
|
-
body.change_notes = changeNotes.trim();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
249
|
try {
|
|
228
250
|
await updateAgent.mutateAsync({ id: agent.id, body });
|
|
229
|
-
setChangeNotes("");
|
|
230
251
|
toast.success("Definição do agente salva com sucesso");
|
|
231
252
|
} catch {
|
|
232
253
|
toast.error("Erro ao salvar definição do agente");
|
|
@@ -249,7 +270,7 @@ export function AgentDefinitionEditor({ agent, config }: AgentDefinitionEditorPr
|
|
|
249
270
|
value={fields[section.key] ?? ""}
|
|
250
271
|
onChange={(v) => updateField(section.key, v)}
|
|
251
272
|
disabled={updateAgent.isPending}
|
|
252
|
-
placeholder={
|
|
273
|
+
placeholder={section.placeholder}
|
|
253
274
|
ariaLabel={section.label}
|
|
254
275
|
/>
|
|
255
276
|
) : (
|
|
@@ -269,37 +290,21 @@ export function AgentDefinitionEditor({ agent, config }: AgentDefinitionEditorPr
|
|
|
269
290
|
<span className="tabular-nums">~{tokens.toLocaleString("pt-BR")} tokens</span>
|
|
270
291
|
</div>
|
|
271
292
|
|
|
272
|
-
{/*
|
|
273
|
-
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}}
|
|
288
|
-
/>
|
|
289
|
-
<Button
|
|
290
|
-
onClick={handleSave}
|
|
291
|
-
disabled={
|
|
292
|
-
updateAgent.isPending ||
|
|
293
|
-
!fields.identity.trim() ||
|
|
294
|
-
!fields.mission.trim()
|
|
295
|
-
}
|
|
296
|
-
>
|
|
297
|
-
{updateAgent.isPending && (
|
|
298
|
-
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
299
|
-
)}
|
|
300
|
-
Salvar
|
|
301
|
-
</Button>
|
|
302
|
-
</div>
|
|
293
|
+
{/* Sticky save/discard bar */}
|
|
294
|
+
{hasChanges && (
|
|
295
|
+
<div className="sticky bottom-0 z-10 flex items-center justify-between gap-2 rounded-lg border bg-background p-3 shadow-sm">
|
|
296
|
+
<p className="text-sm text-muted-foreground">Você tem alterações não salvas.</p>
|
|
297
|
+
<div className="flex gap-2">
|
|
298
|
+
<Button variant="ghost" size="sm" onClick={discard} disabled={updateAgent.isPending}>
|
|
299
|
+
Descartar
|
|
300
|
+
</Button>
|
|
301
|
+
<Button size="sm" onClick={handleSave} disabled={updateAgent.isPending}>
|
|
302
|
+
{updateAgent.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
303
|
+
Salvar
|
|
304
|
+
</Button>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
303
308
|
</div>
|
|
304
309
|
);
|
|
305
310
|
}
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
Input,
|
|
8
8
|
Label,
|
|
9
9
|
Switch,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
Sheet,
|
|
11
|
+
SheetContent,
|
|
12
|
+
SheetHeader,
|
|
13
|
+
SheetTitle,
|
|
14
|
+
SheetFooter,
|
|
15
15
|
} from "@greatapps/greatauth-ui/ui";
|
|
16
16
|
import { Loader2 } from "lucide-react";
|
|
17
17
|
import { toast } from "sonner";
|
|
@@ -185,7 +185,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
|
|
|
185
185
|
</div>
|
|
186
186
|
</div>
|
|
187
187
|
|
|
188
|
-
<
|
|
188
|
+
<SheetFooter>
|
|
189
189
|
<Button
|
|
190
190
|
type="button"
|
|
191
191
|
variant="outline"
|
|
@@ -200,20 +200,20 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
|
|
|
200
200
|
)}
|
|
201
201
|
Salvar
|
|
202
202
|
</Button>
|
|
203
|
-
</
|
|
203
|
+
</SheetFooter>
|
|
204
204
|
</form>
|
|
205
205
|
);
|
|
206
206
|
|
|
207
207
|
if (open !== undefined && onOpenChange) {
|
|
208
208
|
return (
|
|
209
|
-
<
|
|
210
|
-
<
|
|
211
|
-
<
|
|
212
|
-
<
|
|
213
|
-
</
|
|
209
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
210
|
+
<SheetContent className="sm:max-w-md overflow-y-auto">
|
|
211
|
+
<SheetHeader>
|
|
212
|
+
<SheetTitle>Editar Agente</SheetTitle>
|
|
213
|
+
</SheetHeader>
|
|
214
214
|
{formContent}
|
|
215
|
-
</
|
|
216
|
-
</
|
|
215
|
+
</SheetContent>
|
|
216
|
+
</Sheet>
|
|
217
217
|
);
|
|
218
218
|
}
|
|
219
219
|
|
|
@@ -3,11 +3,11 @@ import { useCreateAgent, useUpdateAgent } from "../../hooks";
|
|
|
3
3
|
import type { Agent } from "../../types";
|
|
4
4
|
import type { GagentsHookConfig } from "../../hooks/types";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
Sheet,
|
|
7
|
+
SheetContent,
|
|
8
|
+
SheetHeader,
|
|
9
|
+
SheetTitle,
|
|
10
|
+
SheetFooter,
|
|
11
11
|
Button,
|
|
12
12
|
Input,
|
|
13
13
|
Label,
|
|
@@ -134,13 +134,13 @@ export function AgentFormDialog({
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
return (
|
|
137
|
-
<
|
|
138
|
-
<
|
|
139
|
-
<
|
|
140
|
-
<
|
|
137
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
138
|
+
<SheetContent className="sm:max-w-lg overflow-y-auto">
|
|
139
|
+
<SheetHeader>
|
|
140
|
+
<SheetTitle>
|
|
141
141
|
{isEditing ? "Editar Agente" : "Novo Agente"}
|
|
142
|
-
</
|
|
143
|
-
</
|
|
142
|
+
</SheetTitle>
|
|
143
|
+
</SheetHeader>
|
|
144
144
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
145
145
|
<div className="flex justify-center">
|
|
146
146
|
<ImageCropUpload
|
|
@@ -224,7 +224,7 @@ export function AgentFormDialog({
|
|
|
224
224
|
</p>
|
|
225
225
|
</div>
|
|
226
226
|
</div>
|
|
227
|
-
<
|
|
227
|
+
<SheetFooter>
|
|
228
228
|
<Button
|
|
229
229
|
type="button"
|
|
230
230
|
variant="outline"
|
|
@@ -239,9 +239,9 @@ export function AgentFormDialog({
|
|
|
239
239
|
) : null}
|
|
240
240
|
{isEditing ? "Salvar" : "Criar"}
|
|
241
241
|
</Button>
|
|
242
|
-
</
|
|
242
|
+
</SheetFooter>
|
|
243
243
|
</form>
|
|
244
|
-
</
|
|
245
|
-
</
|
|
244
|
+
</SheetContent>
|
|
245
|
+
</Sheet>
|
|
246
246
|
);
|
|
247
247
|
}
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
Textarea,
|
|
16
16
|
Label,
|
|
17
17
|
Badge,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
Sheet,
|
|
19
|
+
SheetContent,
|
|
20
|
+
SheetHeader,
|
|
21
|
+
SheetTitle,
|
|
22
|
+
SheetFooter,
|
|
23
23
|
AlertDialog,
|
|
24
24
|
AlertDialogAction,
|
|
25
25
|
AlertDialogCancel,
|
|
@@ -324,14 +324,14 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
|
|
|
324
324
|
</Sortable>
|
|
325
325
|
)}
|
|
326
326
|
|
|
327
|
-
{/* Create/Edit
|
|
328
|
-
<
|
|
329
|
-
<
|
|
330
|
-
<
|
|
331
|
-
<
|
|
327
|
+
{/* Create/Edit Sheet */}
|
|
328
|
+
<Sheet open={formOpen} onOpenChange={setFormOpen}>
|
|
329
|
+
<SheetContent className="sm:max-w-lg overflow-y-auto">
|
|
330
|
+
<SheetHeader>
|
|
331
|
+
<SheetTitle>
|
|
332
332
|
{editTarget ? "Editar Objetivo" : "Novo Objetivo"}
|
|
333
|
-
</
|
|
334
|
-
</
|
|
333
|
+
</SheetTitle>
|
|
334
|
+
</SheetHeader>
|
|
335
335
|
<div className="space-y-4">
|
|
336
336
|
<div className="space-y-2">
|
|
337
337
|
<Label htmlFor="objective-title">Título *</Label>
|
|
@@ -430,7 +430,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
|
|
|
430
430
|
</p>
|
|
431
431
|
</div>
|
|
432
432
|
</div>
|
|
433
|
-
<
|
|
433
|
+
<SheetFooter>
|
|
434
434
|
<Button
|
|
435
435
|
variant="outline"
|
|
436
436
|
onClick={() => setFormOpen(false)}
|
|
@@ -447,9 +447,9 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
|
|
|
447
447
|
>
|
|
448
448
|
{editTarget ? "Salvar" : "Criar"}
|
|
449
449
|
</Button>
|
|
450
|
-
</
|
|
451
|
-
</
|
|
452
|
-
</
|
|
450
|
+
</SheetFooter>
|
|
451
|
+
</SheetContent>
|
|
452
|
+
</Sheet>
|
|
453
453
|
|
|
454
454
|
{/* Delete confirmation */}
|
|
455
455
|
<AlertDialog
|