@greatapps/greatagents-ui 0.3.13 → 0.3.15

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.
@@ -1,22 +1,15 @@
1
1
  'use client';
2
2
 
3
- import { useState, useMemo, useCallback } from "react";
3
+ import { useMemo, useCallback, useState } from "react";
4
+ import { useQueryClient } from "@tanstack/react-query";
4
5
  import { useToolCredentials, useAgents, useTools } from "../hooks";
5
- import { ToolCredentialsForm } from "../components/tools/tool-credentials-form";
6
6
  import { IntegrationCard } from "../components/capabilities/integration-card";
7
7
  import { IntegrationWizard } from "../components/capabilities/integration-wizard";
8
8
  import { useIntegrationState, type IntegrationCardData } from "../hooks/use-integrations";
9
+ import { useDeleteToolCredential, useUpdateToolCredential } from "../hooks/use-settings";
9
10
  import type { WizardIntegrationMeta } from "../components/capabilities/types";
10
11
  import type { ConfigOption } from "../components/capabilities/wizard-steps/config-step";
11
- import {
12
- Badge,
13
- Button,
14
- Tabs,
15
- TabsContent,
16
- TabsList,
17
- TabsTrigger,
18
- } from "@greatapps/greatauth-ui/ui";
19
- import { Plus, Plug, KeyRound, Info, Loader2 } from "lucide-react";
12
+ import { Plug, Loader2 } from "lucide-react";
20
13
  import type { GagentsHookConfig } from "../hooks/types";
21
14
  import type { Agent, Tool, ToolCredential } from "../types";
22
15
 
@@ -33,46 +26,16 @@ export interface IntegrationsManagementPageProps {
33
26
  subtitle?: string;
34
27
  }
35
28
 
36
- /**
37
- * Build a map of credential id -> list of agent names that use it.
38
- */
39
- function useCredentialAgentSummary(
40
- credentials: ToolCredential[],
41
- tools: Tool[],
42
- agents: Agent[],
43
- ) {
44
- return useMemo(() => {
45
- const toolIdsWithCredentials = new Set(
46
- credentials.map((c) => c.id_tool).filter(Boolean),
47
- );
48
-
49
- const linkedCount = credentials.filter(
50
- (c) => c.id_tool && toolIdsWithCredentials.has(c.id_tool),
51
- ).length;
52
-
53
- return {
54
- totalCredentials: credentials.length,
55
- linkedToTools: linkedCount,
56
- totalAgents: agents.length,
57
- totalTools: tools.length,
58
- };
59
- }, [credentials, tools, agents]);
60
- }
61
-
62
29
  export function IntegrationsManagementPage({
63
30
  config,
64
31
  gagentsApiUrl,
65
32
  resolveWizardMeta,
66
33
  loadConfigOptions,
67
34
  onWizardComplete,
68
- title = "Integrações e Credenciais",
69
- subtitle = "Gerencie todas as integrações e credenciais da conta.",
35
+ title = "Integrações",
36
+ subtitle = "Gerencie as integrações da conta.",
70
37
  }: IntegrationsManagementPageProps) {
71
- const { data: credentialsData, isLoading: credentialsLoading } =
72
- useToolCredentials(config);
73
- const { data: agentsData } = useAgents(config);
74
- const { data: toolsData } = useTools(config);
75
- const [createOpen, setCreateOpen] = useState(false);
38
+ const queryClient = useQueryClient();
76
39
 
77
40
  // Integration cards state (account-level, agentId=null)
78
41
  const { cards, isLoading: cardsLoading } = useIntegrationState(config, null);
@@ -81,17 +44,33 @@ export function IntegrationsManagementPage({
81
44
  const [wizardOpen, setWizardOpen] = useState(false);
82
45
  const [activeCard, setActiveCard] = useState<IntegrationCardData | null>(null);
83
46
 
84
- const credentials = credentialsData?.data || [];
85
- const agents: Agent[] = agentsData?.data || [];
86
- const tools: Tool[] = toolsData?.data || [];
87
-
88
- const summary = useCredentialAgentSummary(credentials, tools, agents);
47
+ // Mutations for card actions
48
+ const deleteCredential = useDeleteToolCredential(config);
49
+ const updateCredential = useUpdateToolCredential(config);
89
50
 
90
51
  const handleConnect = useCallback((card: IntegrationCardData) => {
91
52
  setActiveCard(card);
92
53
  setWizardOpen(true);
93
54
  }, []);
94
55
 
56
+ const handleReconnect = useCallback((card: IntegrationCardData) => {
57
+ setActiveCard(card);
58
+ setWizardOpen(true);
59
+ }, []);
60
+
61
+ const handleDisconnect = useCallback((card: IntegrationCardData) => {
62
+ if (!card.credentialId) return;
63
+ updateCredential.mutate({
64
+ id: card.credentialId,
65
+ body: { status: "inactive" },
66
+ });
67
+ }, [updateCredential]);
68
+
69
+ const handleDelete = useCallback((card: IntegrationCardData) => {
70
+ if (!card.credentialId) return;
71
+ deleteCredential.mutate(card.credentialId);
72
+ }, [deleteCredential]);
73
+
95
74
  const handleWizardClose = useCallback((open: boolean) => {
96
75
  if (!open) {
97
76
  setActiveCard(null);
@@ -100,10 +79,15 @@ export function IntegrationsManagementPage({
100
79
  }, []);
101
80
 
102
81
  const handleWizardComplete = useCallback(() => {
82
+ // Invalidate queries BEFORE closing so data refreshes
83
+ queryClient.invalidateQueries({ queryKey: ["greatagents", "tool-credentials"] });
84
+ queryClient.invalidateQueries({ queryKey: ["greatagents", "tools"] });
85
+ queryClient.invalidateQueries({ queryKey: ["greatagents", "agent-tools"] });
86
+
103
87
  setWizardOpen(false);
104
88
  setActiveCard(null);
105
89
  onWizardComplete?.();
106
- }, [onWizardComplete]);
90
+ }, [onWizardComplete, queryClient]);
107
91
 
108
92
  // Resolve wizard meta with sensible defaults
109
93
  const wizardMeta: WizardIntegrationMeta | null = activeCard
@@ -131,129 +115,64 @@ export function IntegrationsManagementPage({
131
115
  </div>
132
116
  </div>
133
117
 
134
- <Tabs defaultValue="integrations" className="w-full">
135
- <TabsList>
136
- <TabsTrigger value="integrations" className="gap-2">
137
- <Plug className="h-4 w-4" />
138
- Integrações
139
- </TabsTrigger>
140
- <TabsTrigger value="credentials" className="gap-2">
141
- <KeyRound className="h-4 w-4" />
142
- Credenciais
143
- </TabsTrigger>
144
- </TabsList>
145
-
146
- <TabsContent value="integrations" className="mt-4">
147
- {cardsLoading ? (
148
- <div className="flex items-center justify-center py-16">
149
- <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
150
- </div>
151
- ) : cards.length === 0 ? (
152
- <div className="flex flex-col items-center justify-center gap-3 py-16 text-muted-foreground">
153
- <Plug className="h-10 w-10" />
154
- <p className="text-sm">Nenhuma integração disponível</p>
155
- </div>
156
- ) : (
157
- <div className="space-y-6">
158
- {/* Connected accounts */}
159
- {connectedCards.length > 0 && (
160
- <div>
161
- <h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-muted-foreground">
162
- Contas conectadas
163
- </h3>
164
- <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
165
- {connectedCards.map((card) => (
166
- <IntegrationCard
167
- key={`${card.definition.slug}-cred-${card.credentialId}`}
168
- card={card}
169
- onConnect={handleConnect}
170
- />
171
- ))}
172
- </div>
173
- </div>
174
- )}
175
-
176
- {/* Add new / coming soon */}
177
- {otherCards.length > 0 && (
178
- <div>
179
- {connectedCards.length > 0 && (
180
- <h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-muted-foreground">
181
- Adicionar integração
182
- </h3>
183
- )}
184
- <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
185
- {otherCards.map((card) => {
186
- const key = card.isAddNew
187
- ? `${card.definition.slug}-add-new`
188
- : card.definition.slug;
189
- return (
190
- <IntegrationCard
191
- key={key}
192
- card={card}
193
- onConnect={handleConnect}
194
- />
195
- );
196
- })}
197
- </div>
198
- </div>
199
- )}
118
+ {cardsLoading ? (
119
+ <div className="flex items-center justify-center py-16">
120
+ <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
121
+ </div>
122
+ ) : cards.length === 0 ? (
123
+ <div className="flex flex-col items-center justify-center gap-3 py-16 text-muted-foreground">
124
+ <Plug className="h-10 w-10" />
125
+ <p className="text-sm">Nenhuma integração disponível</p>
126
+ </div>
127
+ ) : (
128
+ <div className="space-y-6">
129
+ {/* Connected accounts */}
130
+ {connectedCards.length > 0 && (
131
+ <div>
132
+ <h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-muted-foreground">
133
+ Contas conectadas
134
+ </h3>
135
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
136
+ {connectedCards.map((card) => (
137
+ <IntegrationCard
138
+ key={`${card.definition.slug}-cred-${card.credentialId}`}
139
+ card={card}
140
+ onConnect={handleConnect}
141
+ onReconnect={handleReconnect}
142
+ onDisconnect={handleDisconnect}
143
+ onDelete={handleDelete}
144
+ />
145
+ ))}
146
+ </div>
200
147
  </div>
201
148
  )}
202
- </TabsContent>
203
149
 
204
- <TabsContent value="credentials" className="mt-4">
205
- {/* Summary bar */}
206
- {!credentialsLoading && (
207
- <div className="flex items-center gap-4 rounded-lg border bg-muted/50 px-4 py-3 mb-4">
208
- <Info className="h-4 w-4 text-muted-foreground shrink-0" />
209
- <div className="flex flex-wrap items-center gap-3 text-sm">
210
- <span>
211
- <Badge variant="secondary" className="mr-1">
212
- {summary.totalCredentials}
213
- </Badge>
214
- {summary.totalCredentials === 1
215
- ? "credencial configurada"
216
- : "credenciais configuradas"}
217
- </span>
218
- <span className="text-muted-foreground">|</span>
219
- <span>
220
- <Badge variant="secondary" className="mr-1">
221
- {summary.linkedToTools}
222
- </Badge>
223
- {summary.linkedToTools === 1
224
- ? "vinculada a ferramentas"
225
- : "vinculadas a ferramentas"}
226
- </span>
227
- <span className="text-muted-foreground">|</span>
228
- <span>
229
- <Badge variant="secondary" className="mr-1">
230
- {summary.totalAgents}
231
- </Badge>
232
- {summary.totalAgents === 1
233
- ? "agente na conta"
234
- : "agentes na conta"}
235
- </span>
150
+ {/* Add new / coming soon */}
151
+ {otherCards.length > 0 && (
152
+ <div>
153
+ {connectedCards.length > 0 && (
154
+ <h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-muted-foreground">
155
+ Adicionar integração
156
+ </h3>
157
+ )}
158
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
159
+ {otherCards.map((card) => {
160
+ const key = card.isAddNew
161
+ ? `${card.definition.slug}-add-new`
162
+ : card.definition.slug;
163
+ return (
164
+ <IntegrationCard
165
+ key={key}
166
+ card={card}
167
+ onConnect={handleConnect}
168
+ />
169
+ );
170
+ })}
236
171
  </div>
237
172
  </div>
238
173
  )}
239
-
240
- <div className="flex items-center justify-end mb-4">
241
- <Button onClick={() => setCreateOpen(true)} size="sm">
242
- <Plus className="mr-2 h-4 w-4" />
243
- Nova Credencial
244
- </Button>
245
- </div>
246
-
247
- <ToolCredentialsForm
248
- config={config}
249
- gagentsApiUrl={gagentsApiUrl}
250
- credentials={credentials}
251
- isLoading={credentialsLoading}
252
- createOpen={createOpen}
253
- onCreateOpenChange={setCreateOpen}
254
- />
255
- </TabsContent>
256
- </Tabs>
174
+ </div>
175
+ )}
257
176
 
258
177
  {/* Integration Wizard */}
259
178
  {activeCard && wizardMeta && (
@@ -105,7 +105,7 @@ export interface ToolCredential {
105
105
  label: string;
106
106
  credentials_encrypted: string;
107
107
  expires_at: string | null;
108
- status: "active" | "expired";
108
+ status: "active" | "expired" | "inactive";
109
109
  authorized_by_contact: number | null;
110
110
  deleted: number;
111
111
  datetime_add: string;