@greatapps/greatagents-ui 0.3.12 → 0.3.14

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.
@@ -3,8 +3,6 @@ import type { ColumnDef } from "@tanstack/react-table";
3
3
  import type { Tool, ToolCredential } from "../../types";
4
4
  import type { GagentsHookConfig } from "../../hooks/types";
5
5
  import {
6
- useCreateToolCredential,
7
- useUpdateToolCredential,
8
6
  useDeleteToolCredential,
9
7
  } from "../../hooks";
10
8
  import { useTools } from "../../hooks";
@@ -16,11 +14,6 @@ import {
16
14
  Tooltip,
17
15
  TooltipTrigger,
18
16
  TooltipContent,
19
- Dialog,
20
- DialogContent,
21
- DialogHeader,
22
- DialogTitle,
23
- DialogFooter,
24
17
  AlertDialog,
25
18
  AlertDialogAction,
26
19
  AlertDialogCancel,
@@ -29,13 +22,8 @@ import {
29
22
  AlertDialogFooter,
30
23
  AlertDialogHeader,
31
24
  AlertDialogTitle,
32
- Select,
33
- SelectContent,
34
- SelectItem,
35
- SelectTrigger,
36
- SelectValue,
37
25
  } from "@greatapps/greatauth-ui/ui";
38
- import { Trash2, Pencil, Link, Search } from "lucide-react";
26
+ import { Trash2, Search } from "lucide-react";
39
27
  import { format } from "date-fns";
40
28
  import { ptBR } from "date-fns/locale";
41
29
  import { toast } from "sonner";
@@ -45,8 +33,6 @@ interface ToolCredentialsFormProps {
45
33
  isLoading: boolean;
46
34
  config: GagentsHookConfig;
47
35
  gagentsApiUrl: string;
48
- createOpen?: boolean;
49
- onCreateOpenChange?: (open: boolean) => void;
50
36
  }
51
37
 
52
38
  function formatDate(dateStr: string | null): string {
@@ -56,8 +42,6 @@ function formatDate(dateStr: string | null): string {
56
42
 
57
43
  function useColumns(
58
44
  tools: Tool[],
59
- onEdit: (cred: ToolCredential) => void,
60
- onConnect: (cred: ToolCredential) => void,
61
45
  onRemove: (cred: ToolCredential) => void,
62
46
  ): ColumnDef<ToolCredential>[] {
63
47
  function getToolName(idTool: number | null): string {
@@ -66,12 +50,6 @@ function useColumns(
66
50
  return tool?.name || `Ferramenta #${idTool}`;
67
51
  }
68
52
 
69
- function getToolType(idTool: number | null): string | null {
70
- if (!idTool) return null;
71
- const tool = tools.find((t) => t.id === idTool);
72
- return tool?.type || null;
73
- }
74
-
75
53
  return [
76
54
  {
77
55
  accessorKey: "label",
@@ -123,36 +101,6 @@ function useColumns(
123
101
  enableSorting: false,
124
102
  cell: ({ row }) => (
125
103
  <div className="flex items-center gap-1">
126
- {getToolType(row.original.id_tool) === "oauth2" && (
127
- <Tooltip>
128
- <TooltipTrigger asChild>
129
- <Button
130
- variant="ghost"
131
- size="icon"
132
- className="h-8 w-8"
133
- aria-label="Vincular"
134
- disabled
135
- >
136
- <Link className="h-4 w-4" />
137
- </Button>
138
- </TooltipTrigger>
139
- <TooltipContent>Em breve</TooltipContent>
140
- </Tooltip>
141
- )}
142
- <Tooltip>
143
- <TooltipTrigger asChild>
144
- <Button
145
- variant="ghost"
146
- size="icon"
147
- className="h-8 w-8"
148
- aria-label="Editar"
149
- onClick={() => onEdit(row.original)}
150
- >
151
- <Pencil className="h-4 w-4" />
152
- </Button>
153
- </TooltipTrigger>
154
- <TooltipContent>Editar</TooltipContent>
155
- </Tooltip>
156
104
  <Tooltip>
157
105
  <TooltipTrigger asChild>
158
106
  <Button
@@ -178,35 +126,12 @@ export function ToolCredentialsForm({
178
126
  isLoading,
179
127
  config,
180
128
  gagentsApiUrl,
181
- createOpen: externalCreateOpen,
182
- onCreateOpenChange,
183
129
  }: ToolCredentialsFormProps) {
184
- const createMutation = useCreateToolCredential(config);
185
- const updateMutation = useUpdateToolCredential(config);
186
130
  const deleteMutation = useDeleteToolCredential(config);
187
131
  const { data: toolsData } = useTools(config);
188
132
  const tools: Tool[] = (toolsData?.data || []).filter((t: Tool) => !t.slug?.startsWith("gclinic_"));
189
133
 
190
134
  const [search, setSearch] = useState("");
191
- const [internalCreateOpen, setInternalCreateOpen] = useState(false);
192
- const showCreateDialog = externalCreateOpen ?? internalCreateOpen;
193
- const setShowCreateDialog = onCreateOpenChange ?? setInternalCreateOpen;
194
- const [createForm, setCreateForm] = useState({
195
- id_tool: "",
196
- label: "",
197
- credentials_encrypted: "",
198
- expires_at: "",
199
- });
200
-
201
- const [editTarget, setEditTarget] = useState<ToolCredential | null>(null);
202
- const [editForm, setEditForm] = useState({
203
- id_tool: "",
204
- label: "",
205
- credentials_encrypted: "",
206
- expires_at: "",
207
- status: "" as "active" | "expired" | "",
208
- });
209
-
210
135
  const [removeTarget, setRemoveTarget] = useState<ToolCredential | null>(null);
211
136
 
212
137
  // Build a set of internal tool IDs to exclude from credentials display
@@ -237,86 +162,9 @@ export function ToolCredentialsForm({
237
162
 
238
163
  const columns = useColumns(
239
164
  tools,
240
- (cred) => startEdit(cred),
241
- (cred) => handleConnect(cred),
242
165
  (cred) => setRemoveTarget(cred),
243
166
  );
244
167
 
245
- async function handleCreate() {
246
- const idTool = parseInt(createForm.id_tool, 10);
247
- if (!idTool || !createForm.label.trim() || !createForm.credentials_encrypted.trim()) return;
248
-
249
- try {
250
- const result = await createMutation.mutateAsync({
251
- id_tool: idTool,
252
- label: createForm.label.trim(),
253
- credentials_encrypted: createForm.credentials_encrypted.trim(),
254
- ...(createForm.expires_at ? { expires_at: createForm.expires_at } : {}),
255
- });
256
- if (result.status === 1) {
257
- toast.success("Credencial criada");
258
- setShowCreateDialog(false);
259
- setCreateForm({ id_tool: "", label: "", credentials_encrypted: "", expires_at: "" });
260
- } else {
261
- toast.error(result.message || "Erro ao criar credencial");
262
- }
263
- } catch {
264
- toast.error("Erro ao criar credencial");
265
- }
266
- }
267
-
268
- function startEdit(cred: ToolCredential) {
269
- setEditTarget(cred);
270
- setEditForm({
271
- id_tool: cred.id_tool ? String(cred.id_tool) : "",
272
- label: cred.label || "",
273
- credentials_encrypted: "",
274
- expires_at: cred.expires_at || "",
275
- status: cred.status,
276
- });
277
- }
278
-
279
- async function handleSaveEdit() {
280
- if (!editTarget) return;
281
- const body: Record<string, unknown> = {};
282
- const newIdTool = editForm.id_tool ? parseInt(editForm.id_tool, 10) : null;
283
- if (newIdTool && newIdTool !== editTarget.id_tool) {
284
- body.id_tool = newIdTool;
285
- }
286
- if (editForm.label.trim() && editForm.label.trim() !== (editTarget.label || "")) {
287
- body.label = editForm.label.trim();
288
- }
289
- if (editForm.credentials_encrypted.trim()) {
290
- body.credentials_encrypted = editForm.credentials_encrypted.trim();
291
- }
292
- if (editForm.expires_at !== (editTarget.expires_at || "")) {
293
- body.expires_at = editForm.expires_at || null;
294
- }
295
- if (editForm.status && editForm.status !== editTarget.status) {
296
- body.status = editForm.status;
297
- }
298
-
299
- if (Object.keys(body).length === 0) {
300
- setEditTarget(null);
301
- return;
302
- }
303
-
304
- try {
305
- const result = await updateMutation.mutateAsync({
306
- id: editTarget.id,
307
- body: body as Parameters<typeof updateMutation.mutateAsync>[0]["body"],
308
- });
309
- if (result.status === 1) {
310
- toast.success("Credencial atualizada");
311
- setEditTarget(null);
312
- } else {
313
- toast.error(result.message || "Erro ao atualizar credencial");
314
- }
315
- } catch {
316
- toast.error("Erro ao atualizar credencial");
317
- }
318
- }
319
-
320
168
  async function handleRemove() {
321
169
  if (!removeTarget) return;
322
170
  try {
@@ -333,14 +181,6 @@ export function ToolCredentialsForm({
333
181
  }
334
182
  }
335
183
 
336
- function handleConnect(cred: ToolCredential) {
337
- if (!config.accountId || !config.token) return;
338
- const language = config.language ?? "pt-br";
339
- const idWl = config.idWl ?? 1;
340
- const url = `${gagentsApiUrl}/v1/${language}/${idWl}/accounts/${config.accountId}/oauth/connect?id_tool=${cred.id_tool}`;
341
- window.open(url, "_blank");
342
- }
343
-
344
184
  return (
345
185
  <div className="space-y-4">
346
186
  <div className="flex items-center gap-3">
@@ -365,219 +205,6 @@ export function ToolCredentialsForm({
365
205
  emptyMessage="Nenhuma credencial encontrada"
366
206
  />
367
207
 
368
- {/* Create Dialog */}
369
- <Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
370
- <DialogContent>
371
- <DialogHeader>
372
- <DialogTitle>Nova Credencial</DialogTitle>
373
- </DialogHeader>
374
- <div className="space-y-4">
375
- <div>
376
- <label htmlFor="cred-tool" className="mb-1 block text-sm font-medium">
377
- Ferramenta *
378
- </label>
379
- <Select
380
- value={createForm.id_tool}
381
- onValueChange={(val) =>
382
- setCreateForm((f) => ({ ...f, id_tool: val }))
383
- }
384
- >
385
- <SelectTrigger id="cred-tool">
386
- <SelectValue placeholder="Selecione a ferramenta" />
387
- </SelectTrigger>
388
- <SelectContent>
389
- {tools.map((tool) => (
390
- <SelectItem key={tool.id} value={String(tool.id)}>
391
- {tool.name}
392
- </SelectItem>
393
- ))}
394
- </SelectContent>
395
- </Select>
396
- </div>
397
- <div>
398
- <label htmlFor="cred-label" className="mb-1 block text-sm font-medium">
399
- Label *
400
- </label>
401
- <Input
402
- id="cred-label"
403
- name="label"
404
- value={createForm.label}
405
- onChange={(e) =>
406
- setCreateForm((f) => ({ ...f, label: e.target.value }))
407
- }
408
- placeholder="Ex: Google Calendar - Clínica São Paulo"
409
- />
410
- </div>
411
- <div>
412
- <label htmlFor="cred-credential" className="mb-1 block text-sm font-medium">
413
- Credencial *
414
- </label>
415
- <Input
416
- id="cred-credential"
417
- name="credential"
418
- autoComplete="off"
419
- type="password"
420
- value={createForm.credentials_encrypted}
421
- onChange={(e) =>
422
- setCreateForm((f) => ({
423
- ...f,
424
- credentials_encrypted: e.target.value,
425
- }))
426
- }
427
- placeholder="Credencial encriptada"
428
- />
429
- </div>
430
- <div>
431
- <label htmlFor="cred-expires" className="mb-1 block text-sm font-medium">
432
- Data de Expiração (opcional)
433
- </label>
434
- <Input
435
- id="cred-expires"
436
- name="expires"
437
- type="date"
438
- value={createForm.expires_at}
439
- onChange={(e) =>
440
- setCreateForm((f) => ({ ...f, expires_at: e.target.value }))
441
- }
442
- />
443
- </div>
444
- </div>
445
- <DialogFooter>
446
- <Button
447
- variant="outline"
448
- onClick={() => setShowCreateDialog(false)}
449
- >
450
- Cancelar
451
- </Button>
452
- <Button
453
- onClick={handleCreate}
454
- disabled={
455
- !createForm.id_tool ||
456
- !createForm.label.trim() ||
457
- !createForm.credentials_encrypted.trim() ||
458
- createMutation.isPending
459
- }
460
- >
461
- Criar
462
- </Button>
463
- </DialogFooter>
464
- </DialogContent>
465
- </Dialog>
466
-
467
- {/* Edit Dialog */}
468
- <Dialog
469
- open={!!editTarget}
470
- onOpenChange={(open) => !open && setEditTarget(null)}
471
- >
472
- <DialogContent>
473
- <DialogHeader>
474
- <DialogTitle>Editar Credencial</DialogTitle>
475
- </DialogHeader>
476
- <div className="space-y-4">
477
- <div>
478
- <label htmlFor="edit-cred-tool" className="mb-1 block text-sm font-medium">
479
- Ferramenta *
480
- </label>
481
- <Select
482
- value={editForm.id_tool}
483
- onValueChange={(val) =>
484
- setEditForm((f) => ({ ...f, id_tool: val }))
485
- }
486
- >
487
- <SelectTrigger id="edit-cred-tool">
488
- <SelectValue placeholder="Selecione a ferramenta" />
489
- </SelectTrigger>
490
- <SelectContent>
491
- {tools.map((tool) => (
492
- <SelectItem key={tool.id} value={String(tool.id)}>
493
- {tool.name}
494
- </SelectItem>
495
- ))}
496
- </SelectContent>
497
- </Select>
498
- </div>
499
- <div>
500
- <label htmlFor="edit-cred-label" className="mb-1 block text-sm font-medium">
501
- Label
502
- </label>
503
- <Input
504
- id="edit-cred-label"
505
- name="label"
506
- value={editForm.label}
507
- onChange={(e) =>
508
- setEditForm((f) => ({ ...f, label: e.target.value }))
509
- }
510
- placeholder="Label da credencial"
511
- />
512
- </div>
513
- <div>
514
- <label htmlFor="edit-cred-credential" className="mb-1 block text-sm font-medium">
515
- Nova Credencial (vazio = manter atual)
516
- </label>
517
- <Input
518
- id="edit-cred-credential"
519
- name="credential"
520
- autoComplete="off"
521
- type="password"
522
- value={editForm.credentials_encrypted}
523
- onChange={(e) =>
524
- setEditForm((f) => ({
525
- ...f,
526
- credentials_encrypted: e.target.value,
527
- }))
528
- }
529
- placeholder="Nova credencial"
530
- />
531
- </div>
532
- <div>
533
- <label htmlFor="edit-cred-expires" className="mb-1 block text-sm font-medium">
534
- Data de Expiração
535
- </label>
536
- <Input
537
- id="edit-cred-expires"
538
- name="expires"
539
- type="date"
540
- value={editForm.expires_at}
541
- onChange={(e) =>
542
- setEditForm((f) => ({ ...f, expires_at: e.target.value }))
543
- }
544
- />
545
- </div>
546
- <div>
547
- <label htmlFor="edit-cred-status" className="mb-1 block text-sm font-medium">Status</label>
548
- <Select
549
- value={editForm.status || undefined}
550
- onValueChange={(val) =>
551
- setEditForm((f) => ({
552
- ...f,
553
- status: val as "active" | "expired",
554
- }))
555
- }
556
- >
557
- <SelectTrigger id="edit-cred-status">
558
- <SelectValue />
559
- </SelectTrigger>
560
- <SelectContent>
561
- <SelectItem value="active">Ativo</SelectItem>
562
- <SelectItem value="expired">Expirado</SelectItem>
563
- </SelectContent>
564
- </Select>
565
- </div>
566
- </div>
567
- <DialogFooter>
568
- <Button variant="outline" onClick={() => setEditTarget(null)}>
569
- Cancelar
570
- </Button>
571
- <Button
572
- onClick={handleSaveEdit}
573
- disabled={updateMutation.isPending}
574
- >
575
- Salvar
576
- </Button>
577
- </DialogFooter>
578
- </DialogContent>
579
- </Dialog>
580
-
581
208
  {/* Delete confirmation */}
582
209
  <AlertDialog
583
210
  open={!!removeTarget}
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
 
3
- import { useState, useCallback } from "react";
4
3
  import {
5
4
  Tabs,
6
5
  TabsList,
@@ -10,13 +9,9 @@ import {
10
9
  import { Blocks, Plug, Settings } from "lucide-react";
11
10
 
12
11
  import type { GagentsHookConfig } from "../hooks/types";
13
- import type { IntegrationCardData } from "../hooks/use-integrations";
14
- import type { WizardIntegrationMeta } from "../components/capabilities/types";
15
12
  import { CapabilitiesTab } from "../components/capabilities/capabilities-tab";
16
13
  import { IntegrationsTab } from "../components/capabilities/integrations-tab";
17
- import { IntegrationWizard } from "../components/capabilities/integration-wizard";
18
14
  import { AdvancedTab } from "../components/capabilities/advanced-tab";
19
- import type { ConfigOption } from "../components/capabilities/wizard-steps/config-step";
20
15
 
21
16
  // ---------------------------------------------------------------------------
22
17
  // Props
@@ -26,33 +21,6 @@ export interface AgentCapabilitiesPageProps {
26
21
  config: GagentsHookConfig;
27
22
  agentId: number;
28
23
  gagentsApiUrl: string;
29
- /**
30
- * Resolve wizard metadata for a given integration card.
31
- * The consuming app provides this so the wizard gets correct
32
- * capabilities, requirements, and config step flag.
33
- */
34
- resolveWizardMeta?: (card: IntegrationCardData) => WizardIntegrationMeta;
35
- /**
36
- * Callback to load config options after OAuth completes
37
- * (e.g. load Google Calendar list). Forwarded to IntegrationWizard.
38
- */
39
- loadConfigOptions?: (credentialId: number) => Promise<ConfigOption[]>;
40
- /** Called after wizard completes successfully. */
41
- onWizardComplete?: () => void;
42
- }
43
-
44
- // ---------------------------------------------------------------------------
45
- // Default wizard meta resolver
46
- // ---------------------------------------------------------------------------
47
-
48
- function defaultResolveWizardMeta(card: IntegrationCardData): WizardIntegrationMeta {
49
- return {
50
- capabilities: [
51
- { label: card.definition.name, description: card.definition.description },
52
- ],
53
- requirements: [],
54
- hasConfigStep: false,
55
- };
56
24
  }
57
25
 
58
26
  // ---------------------------------------------------------------------------
@@ -63,42 +31,13 @@ export function AgentCapabilitiesPage({
63
31
  config,
64
32
  agentId,
65
33
  gagentsApiUrl,
66
- resolveWizardMeta = defaultResolveWizardMeta,
67
- loadConfigOptions,
68
- onWizardComplete,
69
34
  }: AgentCapabilitiesPageProps) {
70
- // Wizard dialog state
71
- const [wizardOpen, setWizardOpen] = useState(false);
72
- const [activeCard, setActiveCard] = useState<IntegrationCardData | null>(null);
73
-
74
- const handleConnect = useCallback(
75
- (card: IntegrationCardData) => {
76
- setActiveCard(card);
77
- setWizardOpen(true);
78
- },
79
- [],
80
- );
81
-
82
- const handleWizardComplete = useCallback(() => {
83
- setWizardOpen(false);
84
- setActiveCard(null);
85
- onWizardComplete?.();
86
- }, [onWizardComplete]);
87
-
88
- const handleWizardOpenChange = useCallback((open: boolean) => {
89
- setWizardOpen(open);
90
- if (!open) setActiveCard(null);
91
- }, []);
92
-
93
- // Derive wizard meta from active card
94
- const wizardMeta = activeCard ? resolveWizardMeta(activeCard) : null;
95
-
96
35
  return (
97
36
  <div className="space-y-4">
98
37
  <div>
99
38
  <h2 className="text-lg font-semibold">Capacidades e Integrações</h2>
100
39
  <p className="text-sm text-muted-foreground">
101
- Configure o que este agente pode fazer e quais serviços externos ele utiliza.
40
+ Configure o que este agente pode fazer e quais integrações ele utiliza.
102
41
  </p>
103
42
  </div>
104
43
 
@@ -123,11 +62,7 @@ export function AgentCapabilitiesPage({
123
62
  </TabsContent>
124
63
 
125
64
  <TabsContent value="integracoes" className="mt-4">
126
- <IntegrationsTab
127
- config={config}
128
- agentId={agentId}
129
- onConnect={handleConnect}
130
- />
65
+ <IntegrationsTab config={config} agentId={agentId} />
131
66
  </TabsContent>
132
67
 
133
68
  <TabsContent value="avancado" className="mt-4">
@@ -138,22 +73,6 @@ export function AgentCapabilitiesPage({
138
73
  />
139
74
  </TabsContent>
140
75
  </Tabs>
141
-
142
- {/* Integration Wizard Dialog */}
143
- {activeCard && wizardMeta && (
144
- <IntegrationWizard
145
- open={wizardOpen}
146
- onOpenChange={handleWizardOpenChange}
147
- integration={activeCard.definition}
148
- meta={wizardMeta}
149
- agentId={agentId}
150
- config={config}
151
- onComplete={handleWizardComplete}
152
- gagentsApiUrl={gagentsApiUrl}
153
- existingCredentialId={activeCard.credential?.id}
154
- loadConfigOptions={loadConfigOptions}
155
- />
156
- )}
157
76
  </div>
158
77
  );
159
78
  }
@@ -1,8 +1,5 @@
1
- import { useState } from "react";
2
1
  import { useToolCredentials } from "../hooks";
3
2
  import { ToolCredentialsForm } from "../components/tools/tool-credentials-form";
4
- import { Button } from "@greatapps/greatauth-ui/ui";
5
- import { Plus } from "lucide-react";
6
3
  import type { GagentsHookConfig } from "../hooks/types";
7
4
 
8
5
  export interface CredentialsPageProps {
@@ -20,7 +17,6 @@ export function CredentialsPage({
20
17
  }: CredentialsPageProps) {
21
18
  const { data: credentialsData, isLoading: credentialsLoading } =
22
19
  useToolCredentials(config);
23
- const [createOpen, setCreateOpen] = useState(false);
24
20
 
25
21
  const credentials = credentialsData?.data || [];
26
22
 
@@ -31,10 +27,6 @@ export function CredentialsPage({
31
27
  <h1 className="text-xl font-semibold">{title}</h1>
32
28
  <p className="text-sm text-muted-foreground">{subtitle}</p>
33
29
  </div>
34
- <Button onClick={() => setCreateOpen(true)} size="sm">
35
- <Plus className="mr-2 h-4 w-4" />
36
- Nova Credencial
37
- </Button>
38
30
  </div>
39
31
 
40
32
  <ToolCredentialsForm
@@ -42,8 +34,6 @@ export function CredentialsPage({
42
34
  gagentsApiUrl={gagentsApiUrl}
43
35
  credentials={credentials}
44
36
  isLoading={credentialsLoading}
45
- createOpen={createOpen}
46
- onCreateOpenChange={setCreateOpen}
47
37
  />
48
38
  </div>
49
39
  );