@greatapps/greatagents-ui 0.3.4 → 0.3.6
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 +49 -40
- package/dist/index.js +2402 -2319
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/agents/agent-form-dialog.tsx +116 -35
- package/src/components/agents/agent-tabs.tsx +39 -2
package/package.json
CHANGED
|
@@ -10,62 +10,112 @@ import {
|
|
|
10
10
|
DialogFooter,
|
|
11
11
|
Button,
|
|
12
12
|
Input,
|
|
13
|
-
|
|
14
13
|
Label,
|
|
14
|
+
Switch,
|
|
15
15
|
} from "@greatapps/greatauth-ui/ui";
|
|
16
16
|
import { Loader2 } from "lucide-react";
|
|
17
17
|
import { toast } from "sonner";
|
|
18
|
+
import { ImageCropUpload } from "@greatapps/greatauth-ui";
|
|
18
19
|
|
|
19
20
|
interface AgentFormDialogProps {
|
|
20
21
|
config: GagentsHookConfig;
|
|
21
22
|
open: boolean;
|
|
22
23
|
onOpenChange: (open: boolean) => void;
|
|
23
24
|
agent?: Agent;
|
|
25
|
+
idAccount?: string | number | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface FormState {
|
|
29
|
+
title: string;
|
|
30
|
+
photo: string;
|
|
31
|
+
active: boolean;
|
|
32
|
+
delayTyping: string;
|
|
33
|
+
waitingTime: string;
|
|
34
|
+
titleError: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function msToSeconds(ms: number | null | undefined): string {
|
|
38
|
+
if (ms == null || ms === 0) return "";
|
|
39
|
+
return String(Math.round(ms / 1000));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function secondsToMs(seconds: string): number | undefined {
|
|
43
|
+
const val = parseFloat(seconds);
|
|
44
|
+
if (isNaN(val) || val <= 0) return undefined;
|
|
45
|
+
return Math.round(val * 1000);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function agentToFormState(agent: Agent): FormState {
|
|
49
|
+
return {
|
|
50
|
+
title: agent.title,
|
|
51
|
+
photo: agent.photo || "",
|
|
52
|
+
active: agent.active,
|
|
53
|
+
delayTyping: msToSeconds(agent.delay_typing),
|
|
54
|
+
waitingTime: msToSeconds(agent.waiting_time),
|
|
55
|
+
titleError: false,
|
|
56
|
+
};
|
|
24
57
|
}
|
|
25
58
|
|
|
59
|
+
const emptyFormState: FormState = {
|
|
60
|
+
title: "",
|
|
61
|
+
photo: "",
|
|
62
|
+
active: true,
|
|
63
|
+
delayTyping: "",
|
|
64
|
+
waitingTime: "",
|
|
65
|
+
titleError: false,
|
|
66
|
+
};
|
|
67
|
+
|
|
26
68
|
export function AgentFormDialog({
|
|
27
69
|
config,
|
|
28
70
|
open,
|
|
29
71
|
onOpenChange,
|
|
30
72
|
agent,
|
|
73
|
+
idAccount,
|
|
31
74
|
}: AgentFormDialogProps) {
|
|
32
75
|
const isEditing = !!agent;
|
|
33
76
|
const createAgent = useCreateAgent(config);
|
|
34
77
|
const updateAgent = useUpdateAgent(config);
|
|
35
78
|
|
|
36
|
-
const [
|
|
37
|
-
const [photo, setPhoto] = useState("");
|
|
38
|
-
const [delayTyping, setDelayTyping] = useState("");
|
|
39
|
-
const [waitingTime, setWaitingTime] = useState("");
|
|
79
|
+
const [form, setForm] = useState<FormState>(emptyFormState);
|
|
40
80
|
|
|
41
81
|
/* eslint-disable react-hooks/set-state-in-effect -- form state sync from props */
|
|
42
82
|
useEffect(() => {
|
|
43
83
|
if (agent) {
|
|
44
|
-
|
|
45
|
-
setPhoto(agent.photo || "");
|
|
46
|
-
setDelayTyping(agent.delay_typing != null ? String(agent.delay_typing) : "");
|
|
47
|
-
setWaitingTime(agent.waiting_time != null ? String(agent.waiting_time) : "");
|
|
84
|
+
setForm(agentToFormState(agent));
|
|
48
85
|
} else {
|
|
49
|
-
|
|
50
|
-
setPhoto("");
|
|
51
|
-
setDelayTyping("");
|
|
52
|
-
setWaitingTime("");
|
|
86
|
+
setForm(emptyFormState);
|
|
53
87
|
}
|
|
54
88
|
}, [agent, open]);
|
|
55
89
|
/* eslint-enable react-hooks/set-state-in-effect */
|
|
56
90
|
|
|
91
|
+
function updateField<K extends keyof FormState>(key: K, value: FormState[K]) {
|
|
92
|
+
setForm((prev) => ({ ...prev, [key]: value }));
|
|
93
|
+
}
|
|
94
|
+
|
|
57
95
|
const isPending = createAgent.isPending || updateAgent.isPending;
|
|
58
96
|
|
|
59
97
|
async function handleSubmit(e: React.FormEvent) {
|
|
60
98
|
e.preventDefault();
|
|
61
|
-
|
|
99
|
+
|
|
100
|
+
if (!form.title.trim()) {
|
|
101
|
+
updateField("titleError", true);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
62
104
|
|
|
63
105
|
const body: Record<string, unknown> = {
|
|
64
|
-
title: title.trim(),
|
|
106
|
+
title: form.title.trim(),
|
|
107
|
+
active: form.active,
|
|
65
108
|
};
|
|
66
|
-
if (photo.trim()) body.photo = photo.trim();
|
|
67
|
-
if (
|
|
68
|
-
|
|
109
|
+
if (form.photo.trim()) body.photo = form.photo.trim();
|
|
110
|
+
else if (isEditing) body.photo = "";
|
|
111
|
+
|
|
112
|
+
const delayMs = secondsToMs(form.delayTyping);
|
|
113
|
+
if (delayMs !== undefined) body.delay_typing = delayMs;
|
|
114
|
+
else if (isEditing) body.delay_typing = 0;
|
|
115
|
+
|
|
116
|
+
const waitingMs = secondsToMs(form.waitingTime);
|
|
117
|
+
if (waitingMs !== undefined) body.waiting_time = waitingMs;
|
|
118
|
+
else if (isEditing) body.waiting_time = 0;
|
|
69
119
|
|
|
70
120
|
try {
|
|
71
121
|
if (isEditing) {
|
|
@@ -92,14 +142,15 @@ export function AgentFormDialog({
|
|
|
92
142
|
</DialogTitle>
|
|
93
143
|
</DialogHeader>
|
|
94
144
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
95
|
-
<div className="
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
145
|
+
<div className="flex justify-center">
|
|
146
|
+
<ImageCropUpload
|
|
147
|
+
value={form.photo || null}
|
|
148
|
+
onChange={(url) => updateField("photo", url)}
|
|
149
|
+
onRemove={() => updateField("photo", "")}
|
|
150
|
+
entityType="agents"
|
|
151
|
+
entityId={agent?.id}
|
|
152
|
+
idAccount={typeof idAccount === "string" ? Number(idAccount) : (idAccount ?? Number(config.accountId) ?? 0)}
|
|
153
|
+
name={form.title || null}
|
|
103
154
|
disabled={isPending}
|
|
104
155
|
/>
|
|
105
156
|
</div>
|
|
@@ -108,39 +159,69 @@ export function AgentFormDialog({
|
|
|
108
159
|
<Input
|
|
109
160
|
id="agent-title"
|
|
110
161
|
name="title"
|
|
111
|
-
value={title}
|
|
112
|
-
onChange={(e) =>
|
|
162
|
+
value={form.title}
|
|
163
|
+
onChange={(e) => {
|
|
164
|
+
setForm((prev) => ({
|
|
165
|
+
...prev,
|
|
166
|
+
title: e.target.value,
|
|
167
|
+
titleError: e.target.value.trim() ? false : prev.titleError,
|
|
168
|
+
}));
|
|
169
|
+
}}
|
|
113
170
|
placeholder="Ex: Assistente de Agendamento"
|
|
114
171
|
required
|
|
115
172
|
disabled={isPending}
|
|
116
173
|
/>
|
|
174
|
+
{form.titleError && (
|
|
175
|
+
<p className="text-sm text-destructive">Nome é obrigatório</p>
|
|
176
|
+
)}
|
|
117
177
|
</div>
|
|
178
|
+
|
|
179
|
+
<div className="flex items-center gap-3">
|
|
180
|
+
<Switch
|
|
181
|
+
id="agent-active"
|
|
182
|
+
checked={form.active}
|
|
183
|
+
onCheckedChange={(checked) => updateField("active", checked)}
|
|
184
|
+
disabled={isPending}
|
|
185
|
+
/>
|
|
186
|
+
<Label htmlFor="agent-active" className="cursor-pointer">
|
|
187
|
+
{form.active ? "Ativo" : "Inativo"}
|
|
188
|
+
</Label>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
118
191
|
<div className="grid grid-cols-2 gap-4">
|
|
119
192
|
<div className="space-y-2">
|
|
120
|
-
<Label htmlFor="agent-delay">Delay de Digitação (
|
|
193
|
+
<Label htmlFor="agent-delay">Delay de Digitação (s)</Label>
|
|
121
194
|
<Input
|
|
122
195
|
id="agent-delay"
|
|
123
196
|
name="delay"
|
|
124
197
|
type="number"
|
|
125
|
-
value={delayTyping}
|
|
126
|
-
onChange={(e) =>
|
|
198
|
+
value={form.delayTyping}
|
|
199
|
+
onChange={(e) => updateField("delayTyping", e.target.value)}
|
|
127
200
|
placeholder="0"
|
|
128
201
|
min="0"
|
|
202
|
+
step="0.5"
|
|
129
203
|
disabled={isPending}
|
|
130
204
|
/>
|
|
205
|
+
<p className="text-xs text-muted-foreground">
|
|
206
|
+
Tempo de simulação de digitação
|
|
207
|
+
</p>
|
|
131
208
|
</div>
|
|
132
209
|
<div className="space-y-2">
|
|
133
|
-
<Label htmlFor="agent-waiting">Tempo de Espera (
|
|
210
|
+
<Label htmlFor="agent-waiting">Tempo de Espera (s)</Label>
|
|
134
211
|
<Input
|
|
135
212
|
id="agent-waiting"
|
|
136
213
|
name="waiting"
|
|
137
214
|
type="number"
|
|
138
|
-
value={waitingTime}
|
|
139
|
-
onChange={(e) =>
|
|
215
|
+
value={form.waitingTime}
|
|
216
|
+
onChange={(e) => updateField("waitingTime", e.target.value)}
|
|
140
217
|
placeholder="0"
|
|
141
218
|
min="0"
|
|
219
|
+
step="0.5"
|
|
142
220
|
disabled={isPending}
|
|
143
221
|
/>
|
|
222
|
+
<p className="text-xs text-muted-foreground">
|
|
223
|
+
Espera por mensagens agrupadas
|
|
224
|
+
</p>
|
|
144
225
|
</div>
|
|
145
226
|
</div>
|
|
146
227
|
<DialogFooter>
|
|
@@ -152,7 +233,7 @@ export function AgentFormDialog({
|
|
|
152
233
|
>
|
|
153
234
|
Cancelar
|
|
154
235
|
</Button>
|
|
155
|
-
<Button type="submit" disabled={isPending || !title.trim()}>
|
|
236
|
+
<Button type="submit" disabled={isPending || !form.title.trim()}>
|
|
156
237
|
{isPending ? (
|
|
157
238
|
<Loader2 aria-hidden="true" className="mr-2 h-4 w-4 animate-spin" />
|
|
158
239
|
) : null}
|
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
import type { Agent } from "../../types";
|
|
2
2
|
import type { GagentsHookConfig } from "../../hooks/types";
|
|
3
|
+
import type { IntegrationCardData } from "../../hooks/use-integrations";
|
|
4
|
+
import type { WizardIntegrationMeta } from "../capabilities/types";
|
|
5
|
+
import type { ConfigOption } from "../capabilities/wizard-steps/config-step";
|
|
3
6
|
import { AgentToolsList } from "./agent-tools-list";
|
|
4
7
|
import { AgentObjectivesList } from "./agent-objectives-list";
|
|
5
8
|
import { AgentPromptEditor } from "./agent-prompt-editor";
|
|
6
9
|
import { AgentConversationsPanel } from "../conversations/agent-conversations-panel";
|
|
10
|
+
import { AgentCapabilitiesPage } from "../../pages/agent-capabilities-page";
|
|
7
11
|
import {
|
|
8
12
|
Tabs,
|
|
9
13
|
TabsList,
|
|
10
14
|
TabsTrigger,
|
|
11
15
|
TabsContent,
|
|
12
16
|
} from "@greatapps/greatauth-ui/ui";
|
|
13
|
-
import { Wrench, Target, FileText, MessageCircle } from "lucide-react";
|
|
17
|
+
import { Wrench, Target, FileText, MessageCircle, Blocks } from "lucide-react";
|
|
14
18
|
|
|
15
19
|
interface AgentTabsProps {
|
|
16
20
|
agent: Agent;
|
|
17
21
|
config: GagentsHookConfig;
|
|
18
22
|
renderChatLink?: (inboxId: number) => React.ReactNode;
|
|
23
|
+
/** Required for the Capacidades tab — gagents API URL for OAuth flows and advanced features. Falls back to config.baseUrl. */
|
|
24
|
+
gagentsApiUrl?: string;
|
|
25
|
+
/** Resolve wizard metadata for a given integration card. */
|
|
26
|
+
resolveWizardMeta?: (card: IntegrationCardData) => WizardIntegrationMeta;
|
|
27
|
+
/** Callback to load config options after OAuth completes. */
|
|
28
|
+
loadConfigOptions?: (credentialId: number) => Promise<ConfigOption[]>;
|
|
29
|
+
/** Called after wizard completes successfully. */
|
|
30
|
+
onWizardComplete?: () => void;
|
|
19
31
|
}
|
|
20
32
|
|
|
21
|
-
export function AgentTabs({
|
|
33
|
+
export function AgentTabs({
|
|
34
|
+
agent,
|
|
35
|
+
config,
|
|
36
|
+
renderChatLink,
|
|
37
|
+
gagentsApiUrl,
|
|
38
|
+
resolveWizardMeta,
|
|
39
|
+
loadConfigOptions,
|
|
40
|
+
onWizardComplete,
|
|
41
|
+
}: AgentTabsProps) {
|
|
42
|
+
const apiUrl = gagentsApiUrl || config.baseUrl;
|
|
43
|
+
|
|
22
44
|
return (
|
|
23
45
|
<Tabs defaultValue="prompt">
|
|
24
46
|
<TabsList>
|
|
@@ -34,6 +56,10 @@ export function AgentTabs({ agent, config, renderChatLink }: AgentTabsProps) {
|
|
|
34
56
|
<Wrench aria-hidden="true" className="h-3.5 w-3.5" />
|
|
35
57
|
Ferramentas
|
|
36
58
|
</TabsTrigger>
|
|
59
|
+
<TabsTrigger value="capacidades" className="flex items-center gap-1.5">
|
|
60
|
+
<Blocks aria-hidden="true" className="h-3.5 w-3.5" />
|
|
61
|
+
Capacidades
|
|
62
|
+
</TabsTrigger>
|
|
37
63
|
<TabsTrigger value="conversas" className="flex items-center gap-1.5">
|
|
38
64
|
<MessageCircle aria-hidden="true" className="h-3.5 w-3.5" />
|
|
39
65
|
Conversas
|
|
@@ -52,6 +78,17 @@ export function AgentTabs({ agent, config, renderChatLink }: AgentTabsProps) {
|
|
|
52
78
|
<AgentToolsList agent={agent} config={config} />
|
|
53
79
|
</TabsContent>
|
|
54
80
|
|
|
81
|
+
<TabsContent value="capacidades" className="mt-4">
|
|
82
|
+
<AgentCapabilitiesPage
|
|
83
|
+
config={config}
|
|
84
|
+
agentId={agent.id}
|
|
85
|
+
gagentsApiUrl={apiUrl}
|
|
86
|
+
resolveWizardMeta={resolveWizardMeta}
|
|
87
|
+
loadConfigOptions={loadConfigOptions}
|
|
88
|
+
onWizardComplete={onWizardComplete}
|
|
89
|
+
/>
|
|
90
|
+
</TabsContent>
|
|
91
|
+
|
|
55
92
|
<TabsContent value="conversas" className="mt-4">
|
|
56
93
|
<AgentConversationsPanel
|
|
57
94
|
agent={agent}
|