@greatapps/greatagents-ui 0.3.2 → 0.3.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.
Files changed (38) hide show
  1. package/dist/index.d.ts +204 -1
  2. package/dist/index.js +1797 -125
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/client/index.ts +14 -0
  6. package/src/components/agents/agent-edit-form.tsx +4 -1
  7. package/src/components/agents/agent-form-dialog.tsx +5 -1
  8. package/src/components/agents/agent-objectives-list.tsx +15 -6
  9. package/src/components/agents/agent-prompt-editor.tsx +9 -5
  10. package/src/components/agents/agent-tabs.tsx +4 -4
  11. package/src/components/agents/agent-tools-list.tsx +12 -5
  12. package/src/components/agents/agents-table.tsx +7 -2
  13. package/src/components/capabilities/advanced-tab.tsx +82 -0
  14. package/src/components/capabilities/capabilities-tab.tsx +475 -0
  15. package/src/components/capabilities/integration-card.tsx +162 -0
  16. package/src/components/capabilities/integration-wizard.tsx +537 -0
  17. package/src/components/capabilities/integrations-tab.tsx +61 -0
  18. package/src/components/capabilities/types.ts +48 -0
  19. package/src/components/capabilities/wizard-steps/config-step.tsx +117 -0
  20. package/src/components/capabilities/wizard-steps/confirm-step.tsx +123 -0
  21. package/src/components/capabilities/wizard-steps/credentials-step.tsx +205 -0
  22. package/src/components/capabilities/wizard-steps/info-step.tsx +78 -0
  23. package/src/components/conversations/agent-conversations-table.tsx +13 -2
  24. package/src/components/conversations/conversation-view.tsx +2 -2
  25. package/src/components/tools/tool-credentials-form.tsx +34 -14
  26. package/src/components/tools/tool-form-dialog.tsx +9 -5
  27. package/src/components/tools/tools-table.tsx +8 -3
  28. package/src/data/integrations-registry.ts +23 -0
  29. package/src/hooks/index.ts +10 -0
  30. package/src/hooks/use-capabilities.ts +50 -0
  31. package/src/hooks/use-integrations.ts +114 -0
  32. package/src/index.ts +34 -0
  33. package/src/pages/agent-capabilities-page.tsx +159 -0
  34. package/src/pages/agent-detail-page.tsx +1 -0
  35. package/src/pages/index.ts +2 -0
  36. package/src/pages/integrations-management-page.tsx +166 -0
  37. package/src/types/capabilities.ts +32 -0
  38. package/src/types/index.ts +10 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/greatagents-ui",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Shared agents UI components for Great platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,6 +12,7 @@ import type {
12
12
  Tool,
13
13
  ToolCredential,
14
14
  } from "../types";
15
+ import type { AgentCapability, AgentCapabilitiesPayload, CapabilitiesResponse } from "../types/capabilities";
15
16
 
16
17
  export interface GagentsClientConfig {
17
18
  baseUrl: string;
@@ -248,6 +249,19 @@ export function createGagentsClient(config: GagentsClientConfig) {
248
249
  undefined,
249
250
  { provider },
250
251
  ),
252
+
253
+ // --- Capabilities ---
254
+ getCapabilities: (idAccount: number) =>
255
+ request<CapabilitiesResponse>("GET", idAccount, "capabilities"),
256
+
257
+ getAgentCapabilities: (idAccount: number, idAgent: number) =>
258
+ request<AgentCapability[]>("GET", idAccount, `agents/${idAgent}/capabilities`),
259
+
260
+ updateAgentCapabilities: (
261
+ idAccount: number,
262
+ idAgent: number,
263
+ body: AgentCapabilitiesPayload,
264
+ ) => request<AgentCapability[]>("PUT", idAccount, `agents/${idAgent}/capabilities`, body),
251
265
  };
252
266
  }
253
267
 
@@ -119,6 +119,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
119
119
  <Label htmlFor="edit-title">Nome do Agente *</Label>
120
120
  <Input
121
121
  id="edit-title"
122
+ name="title"
122
123
  value={form.title}
123
124
  onChange={(e) => {
124
125
  setForm((prev) => ({
@@ -152,6 +153,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
152
153
  <Label htmlFor="edit-delay">Delay de Digitação (s)</Label>
153
154
  <Input
154
155
  id="edit-delay"
156
+ name="delay"
155
157
  type="number"
156
158
  value={form.delayTyping}
157
159
  onChange={(e) => updateField("delayTyping", e.target.value)}
@@ -168,6 +170,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
168
170
  <Label htmlFor="edit-waiting">Tempo de Espera (s)</Label>
169
171
  <Input
170
172
  id="edit-waiting"
173
+ name="waiting"
171
174
  type="number"
172
175
  value={form.waitingTime}
173
176
  onChange={(e) => updateField("waitingTime", e.target.value)}
@@ -193,7 +196,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
193
196
  </Button>
194
197
  <Button type="submit" disabled={updateAgent.isPending}>
195
198
  {updateAgent.isPending && (
196
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
199
+ <Loader2 aria-hidden="true" className="mr-2 h-4 w-4 animate-spin" />
197
200
  )}
198
201
  Salvar
199
202
  </Button>
@@ -96,6 +96,7 @@ export function AgentFormDialog({
96
96
  <Label htmlFor="agent-photo">Foto (URL)</Label>
97
97
  <Input
98
98
  id="agent-photo"
99
+ name="photo"
99
100
  value={photo}
100
101
  onChange={(e) => setPhoto(e.target.value)}
101
102
  placeholder="https://exemplo.com/foto.jpg"
@@ -106,6 +107,7 @@ export function AgentFormDialog({
106
107
  <Label htmlFor="agent-title">Nome do Agente *</Label>
107
108
  <Input
108
109
  id="agent-title"
110
+ name="title"
109
111
  value={title}
110
112
  onChange={(e) => setTitle(e.target.value)}
111
113
  placeholder="Ex: Assistente de Agendamento"
@@ -118,6 +120,7 @@ export function AgentFormDialog({
118
120
  <Label htmlFor="agent-delay">Delay de Digitação (ms)</Label>
119
121
  <Input
120
122
  id="agent-delay"
123
+ name="delay"
121
124
  type="number"
122
125
  value={delayTyping}
123
126
  onChange={(e) => setDelayTyping(e.target.value)}
@@ -130,6 +133,7 @@ export function AgentFormDialog({
130
133
  <Label htmlFor="agent-waiting">Tempo de Espera (ms)</Label>
131
134
  <Input
132
135
  id="agent-waiting"
136
+ name="waiting"
133
137
  type="number"
134
138
  value={waitingTime}
135
139
  onChange={(e) => setWaitingTime(e.target.value)}
@@ -150,7 +154,7 @@ export function AgentFormDialog({
150
154
  </Button>
151
155
  <Button type="submit" disabled={isPending || !title.trim()}>
152
156
  {isPending ? (
153
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
157
+ <Loader2 aria-hidden="true" className="mr-2 h-4 w-4 animate-spin" />
154
158
  ) : null}
155
159
  {isEditing ? "Salvar" : "Criar"}
156
160
  </Button>
@@ -238,7 +238,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
238
238
  className="flex items-center gap-3 rounded-lg border bg-card p-3"
239
239
  >
240
240
  <SortableItemHandle className="shrink-0 text-muted-foreground hover:text-foreground">
241
- <GripVertical className="h-5 w-5" />
241
+ <GripVertical aria-hidden="true" className="h-5 w-5" />
242
242
  </SortableItemHandle>
243
243
 
244
244
  <div className="flex flex-1 flex-col gap-1 min-w-0">
@@ -260,6 +260,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
260
260
  </div>
261
261
 
262
262
  <Switch
263
+ aria-label="Ativar/Desativar"
263
264
  checked={objective.active}
264
265
  onCheckedChange={(checked) =>
265
266
  handleToggleActive(objective, checked)
@@ -270,6 +271,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
270
271
  <Button
271
272
  variant="ghost"
272
273
  size="icon"
274
+ aria-label="Editar"
273
275
  className="shrink-0 text-muted-foreground hover:text-foreground"
274
276
  onClick={() => openEdit(objective)}
275
277
  >
@@ -279,6 +281,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
279
281
  <Button
280
282
  variant="ghost"
281
283
  size="icon"
284
+ aria-label="Excluir"
282
285
  className="shrink-0 text-muted-foreground hover:text-destructive"
283
286
  onClick={() => setRemoveTarget(objective)}
284
287
  >
@@ -292,7 +295,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
292
295
  const obj = sortedObjectives.find((o) => o.id === value);
293
296
  return (
294
297
  <div className="flex items-center gap-3 rounded-lg border bg-card p-3 shadow-lg">
295
- <GripVertical className="h-5 w-5 text-muted-foreground" />
298
+ <GripVertical aria-hidden="true" className="h-5 w-5 text-muted-foreground" />
296
299
  <span className="font-medium">{obj?.title}</span>
297
300
  </div>
298
301
  );
@@ -311,8 +314,10 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
311
314
  </DialogHeader>
312
315
  <div className="space-y-4">
313
316
  <div className="space-y-2">
314
- <Label>Título *</Label>
317
+ <Label htmlFor="objective-title">Título *</Label>
315
318
  <Input
319
+ id="objective-title"
320
+ name="title"
316
321
  value={form.title}
317
322
  onChange={(e) => {
318
323
  const title = e.target.value;
@@ -327,8 +332,10 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
327
332
  </div>
328
333
 
329
334
  <div className="space-y-2">
330
- <Label>Slug (identificador) *</Label>
335
+ <Label htmlFor="objective-slug">Slug (identificador) *</Label>
331
336
  <Input
337
+ id="objective-slug"
338
+ name="slug"
332
339
  value={form.slug}
333
340
  onChange={(e) => {
334
341
  setSlugManual(true);
@@ -343,13 +350,15 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
343
350
  </div>
344
351
 
345
352
  <div className="space-y-2">
346
- <Label>Instruções do Objetivo</Label>
353
+ <Label htmlFor="objective-prompt">Instruções do Objetivo</Label>
347
354
  <Textarea
355
+ id="objective-prompt"
356
+ name="prompt"
348
357
  value={form.prompt}
349
358
  onChange={(e) =>
350
359
  setForm((f) => ({ ...f, prompt: e.target.value }))
351
360
  }
352
- placeholder="Instruções detalhadas que o agente seguirá quando este objetivo for ativado. Ex: passos para agendar consulta, perguntas a fazer, validações necessárias..."
361
+ placeholder="Instru\u00e7\u00f5es detalhadas que o agente seguir\u00e1 quando este objetivo for ativado\u2026"
353
362
  rows={8}
354
363
  />
355
364
  <p className="text-xs text-muted-foreground">
@@ -214,24 +214,28 @@ export function AgentPromptEditor({ config, agent }: AgentPromptEditorProps) {
214
214
  <div className="space-y-2">
215
215
  <textarea
216
216
  ref={textareaRef}
217
+ aria-label="Prompt do sistema"
218
+ name="prompt"
217
219
  value={promptText}
218
220
  onChange={(e) => setPromptText(e.target.value)}
219
221
  onKeyDown={handleKeyDown}
220
- placeholder="Escreva o prompt do sistema aqui..."
222
+ placeholder="Escreva o prompt do sistema aqui\u2026"
221
223
  disabled={updateAgent.isPending}
222
224
  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"
223
225
  style={{ minHeight: "300px" }}
224
226
  />
225
227
  <div className="flex items-center gap-3 text-xs text-muted-foreground">
226
- <span>{charCount.toLocaleString("pt-BR")} caracteres</span>
228
+ <span className="tabular-nums">{charCount.toLocaleString("pt-BR")} caracteres</span>
227
229
  <span>·</span>
228
- <span>~{tokenEstimate.toLocaleString("pt-BR")} tokens</span>
230
+ <span className="tabular-nums">~{tokenEstimate.toLocaleString("pt-BR")} tokens</span>
229
231
  </div>
230
232
  </div>
231
233
 
232
234
  {/* Save row */}
233
235
  <div className="flex items-center gap-3">
234
236
  <Input
237
+ aria-label="Notas da alteração"
238
+ name="changeNotes"
235
239
  value={changeNotes}
236
240
  onChange={(e) => setChangeNotes(e.target.value)}
237
241
  placeholder="O que mudou? (opcional)"
@@ -390,7 +394,7 @@ export function AgentPromptEditor({ config, agent }: AgentPromptEditorProps) {
390
394
  onClick={() => setCompareVersionId(isComparing ? null : version.id)}
391
395
  className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
392
396
  >
393
- <FileText className="h-3 w-3" />
397
+ <FileText aria-hidden="true" className="h-3 w-3" />
394
398
  {isComparing ? "Ocultar diff" : "Comparar"}
395
399
  </button>
396
400
  <button
@@ -398,7 +402,7 @@ export function AgentPromptEditor({ config, agent }: AgentPromptEditorProps) {
398
402
  onClick={() => handleRestore(version)}
399
403
  className="flex items-center gap-1 text-xs text-primary hover:underline"
400
404
  >
401
- <RotateCcw className="h-3 w-3" />
405
+ <RotateCcw aria-hidden="true" className="h-3 w-3" />
402
406
  Restaurar
403
407
  </button>
404
408
  </div>
@@ -23,19 +23,19 @@ export function AgentTabs({ agent, config, renderChatLink }: AgentTabsProps) {
23
23
  <Tabs defaultValue="prompt">
24
24
  <TabsList>
25
25
  <TabsTrigger value="prompt" className="flex items-center gap-1.5">
26
- <FileText className="h-3.5 w-3.5" />
26
+ <FileText aria-hidden="true" className="h-3.5 w-3.5" />
27
27
  Prompt
28
28
  </TabsTrigger>
29
29
  <TabsTrigger value="objetivos" className="flex items-center gap-1.5">
30
- <Target className="h-3.5 w-3.5" />
30
+ <Target aria-hidden="true" className="h-3.5 w-3.5" />
31
31
  Objetivos
32
32
  </TabsTrigger>
33
33
  <TabsTrigger value="ferramentas" className="flex items-center gap-1.5">
34
- <Wrench className="h-3.5 w-3.5" />
34
+ <Wrench aria-hidden="true" className="h-3.5 w-3.5" />
35
35
  Ferramentas
36
36
  </TabsTrigger>
37
37
  <TabsTrigger value="conversas" className="flex items-center gap-1.5">
38
- <MessageCircle className="h-3.5 w-3.5" />
38
+ <MessageCircle aria-hidden="true" className="h-3.5 w-3.5" />
39
39
  Conversas
40
40
  </TabsTrigger>
41
41
  </TabsList>
@@ -182,7 +182,9 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
182
182
  <PopoverContent className="w-72 p-0" align="end">
183
183
  <div className="p-2">
184
184
  <Input
185
- placeholder="Buscar ferramenta..."
185
+ placeholder="Buscar ferramenta\u2026"
186
+ aria-label="Buscar ferramenta"
187
+ name="search"
186
188
  value={search}
187
189
  onChange={(e) => setSearch(e.target.value)}
188
190
  className="h-8"
@@ -250,6 +252,7 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
250
252
  </div>
251
253
 
252
254
  <Switch
255
+ aria-label="Ativar/Desativar"
253
256
  checked={agentTool.enabled}
254
257
  onCheckedChange={(checked) =>
255
258
  handleToggleEnabled(agentTool, checked)
@@ -260,6 +263,7 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
260
263
  <Button
261
264
  variant="ghost"
262
265
  size="icon"
266
+ aria-label="Configurar"
263
267
  className="shrink-0 text-muted-foreground hover:text-foreground"
264
268
  onClick={() => openConfig(agentTool)}
265
269
  title="Configurar instruções"
@@ -270,6 +274,7 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
270
274
  <Button
271
275
  variant="ghost"
272
276
  size="icon"
277
+ aria-label="Remover"
273
278
  className="shrink-0 text-muted-foreground hover:text-destructive"
274
279
  onClick={() => setRemoveTarget(agentTool)}
275
280
  >
@@ -295,12 +300,12 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
295
300
  <div className="space-y-4">
296
301
  {configTarget && getToolInfo(configTarget.id_tool)?.type !== "none" && (
297
302
  <div className="space-y-2">
298
- <Label>Credencial</Label>
303
+ <Label htmlFor="tool-credential">Credencial</Label>
299
304
  <Select
300
305
  value={configCredentialId || undefined}
301
306
  onValueChange={(val) => setConfigCredentialId(val === "__none__" ? "" : val)}
302
307
  >
303
- <SelectTrigger>
308
+ <SelectTrigger id="tool-credential">
304
309
  <SelectValue placeholder="Selecione uma credencial (opcional)" />
305
310
  </SelectTrigger>
306
311
  <SelectContent>
@@ -320,11 +325,13 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
320
325
  </div>
321
326
  )}
322
327
  <div className="space-y-2">
323
- <Label>Instruções Personalizadas</Label>
328
+ <Label htmlFor="tool-instructions">Instruções Personalizadas</Label>
324
329
  <Textarea
330
+ id="tool-instructions"
331
+ name="instructions"
325
332
  value={configInstructions}
326
333
  onChange={(e) => setConfigInstructions(e.target.value)}
327
- placeholder="Instruções sobre como e quando o agente deve usar esta ferramenta..."
334
+ placeholder="Instru\u00e7\u00f5es sobre como e quando o agente deve usar esta ferramenta\u2026"
328
335
  rows={6}
329
336
  />
330
337
  <p className="text-xs text-muted-foreground">
@@ -78,6 +78,7 @@ function useColumns(
78
78
  variant="ghost"
79
79
  size="icon"
80
80
  className="h-8 w-8"
81
+ aria-label="Editar"
81
82
  onClick={() => onEdit(row.original)}
82
83
  >
83
84
  <Pencil className="h-4 w-4" />
@@ -91,6 +92,7 @@ function useColumns(
91
92
  variant="ghost"
92
93
  size="icon"
93
94
  className="h-8 w-8 text-destructive hover:text-destructive"
95
+ aria-label="Excluir"
94
96
  onClick={() => onDelete(row.original.id)}
95
97
  >
96
98
  <Trash2 className="h-4 w-4" />
@@ -151,9 +153,12 @@ export function AgentsTable({ config, onNavigateToAgent }: { config: GagentsHook
151
153
  <>
152
154
  <div className="flex items-center gap-3">
153
155
  <div className="relative flex-1 max-w-md">
154
- <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
156
+ <Search aria-hidden="true" className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
155
157
  <Input
156
- placeholder="Buscar agentes..."
158
+ placeholder="Buscar agentes\u2026"
159
+ aria-label="Buscar agentes"
160
+ name="search"
161
+ autoComplete="off"
157
162
  value={search}
158
163
  onChange={(e) => handleSearchChange(e.target.value)}
159
164
  className="pl-9"
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ import { useState } from "react";
4
+ import type { GagentsHookConfig } from "../../hooks/types";
5
+ import { useToolCredentials } from "../../hooks";
6
+ import { ToolsTable } from "../tools/tools-table";
7
+ import { ToolCredentialsForm } from "../tools/tool-credentials-form";
8
+ import { ToolFormDialog } from "../tools/tool-form-dialog";
9
+ import type { Tool } from "../../types";
10
+ import { Info } from "lucide-react";
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Props
14
+ // ---------------------------------------------------------------------------
15
+
16
+ export interface AdvancedTabProps {
17
+ config: GagentsHookConfig;
18
+ agentId: number;
19
+ gagentsApiUrl: string;
20
+ }
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Component
24
+ // ---------------------------------------------------------------------------
25
+
26
+ export function AdvancedTab({ config, agentId, gagentsApiUrl }: AdvancedTabProps) {
27
+ const { data: credentialsData, isLoading: isLoadingCredentials } =
28
+ useToolCredentials(config);
29
+ const credentials = credentialsData?.data ?? [];
30
+
31
+ const [editingTool, setEditingTool] = useState<Tool | null>(null);
32
+ const [showToolForm, setShowToolForm] = useState(false);
33
+
34
+ function handleEditTool(tool: Tool) {
35
+ setEditingTool(tool);
36
+ setShowToolForm(true);
37
+ }
38
+
39
+ function handleToolFormOpenChange(open: boolean) {
40
+ setShowToolForm(open);
41
+ if (!open) setEditingTool(null);
42
+ }
43
+
44
+ return (
45
+ <div className="space-y-8">
46
+ {/* Info banner */}
47
+ <div className="flex items-start gap-3 rounded-lg border border-blue-200 bg-blue-50 p-4 dark:border-blue-900 dark:bg-blue-950/30">
48
+ <Info className="mt-0.5 h-4 w-4 shrink-0 text-blue-600 dark:text-blue-400" />
49
+ <p className="text-sm text-blue-800 dark:text-blue-300">
50
+ Use as abas <strong>Capacidades</strong> e <strong>Integrações</strong> para
51
+ configuração simplificada. Esta aba oferece controlo manual avançado sobre
52
+ ferramentas e credenciais.
53
+ </p>
54
+ </div>
55
+
56
+ {/* Ferramentas section */}
57
+ <section className="space-y-3">
58
+ <h3 className="text-sm font-medium">Ferramentas</h3>
59
+ <ToolsTable onEdit={handleEditTool} config={config} />
60
+ </section>
61
+
62
+ {/* Credenciais section */}
63
+ <section className="space-y-3">
64
+ <h3 className="text-sm font-medium">Credenciais</h3>
65
+ <ToolCredentialsForm
66
+ credentials={credentials}
67
+ isLoading={isLoadingCredentials}
68
+ config={config}
69
+ gagentsApiUrl={gagentsApiUrl}
70
+ />
71
+ </section>
72
+
73
+ {/* Tool edit dialog */}
74
+ <ToolFormDialog
75
+ open={showToolForm}
76
+ onOpenChange={handleToolFormOpenChange}
77
+ tool={editingTool ?? undefined}
78
+ config={config}
79
+ />
80
+ </div>
81
+ );
82
+ }