@greatapps/greatagents-ui 0.3.12 → 0.3.13

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,9 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { useState, useMemo } from "react";
3
+ import { useState, useMemo, useCallback } from "react";
4
4
  import { useToolCredentials, useAgents, useTools } from "../hooks";
5
5
  import { ToolCredentialsForm } from "../components/tools/tool-credentials-form";
6
- import { IntegrationsTab } from "../components/capabilities/integrations-tab";
6
+ import { IntegrationCard } from "../components/capabilities/integration-card";
7
+ import { IntegrationWizard } from "../components/capabilities/integration-wizard";
8
+ import { useIntegrationState, type IntegrationCardData } from "../hooks/use-integrations";
9
+ import type { WizardIntegrationMeta } from "../components/capabilities/types";
10
+ import type { ConfigOption } from "../components/capabilities/wizard-steps/config-step";
7
11
  import {
8
12
  Badge,
9
13
  Button,
@@ -12,28 +16,25 @@ import {
12
16
  TabsList,
13
17
  TabsTrigger,
14
18
  } from "@greatapps/greatauth-ui/ui";
15
- import { Plus, Plug, KeyRound, Info } from "lucide-react";
19
+ import { Plus, Plug, KeyRound, Info, Loader2 } from "lucide-react";
16
20
  import type { GagentsHookConfig } from "../hooks/types";
17
- import type { IntegrationCardData } from "../hooks/use-integrations";
18
21
  import type { Agent, Tool, ToolCredential } from "../types";
19
22
 
20
23
  export interface IntegrationsManagementPageProps {
21
24
  config: GagentsHookConfig;
22
25
  gagentsApiUrl: string;
23
- /** Called when user clicks connect/reconnect on an integration card. */
24
- onConnect?: (card: IntegrationCardData) => void;
26
+ /** Resolve wizard metadata for a given integration card. */
27
+ resolveWizardMeta?: (card: IntegrationCardData) => WizardIntegrationMeta;
28
+ /** Load config options after OAuth completes (e.g. list of calendars). */
29
+ loadConfigOptions?: (credentialId: number) => Promise<ConfigOption[]>;
30
+ /** Called after wizard completes successfully. */
31
+ onWizardComplete?: () => void;
25
32
  title?: string;
26
33
  subtitle?: string;
27
34
  }
28
35
 
29
36
  /**
30
- * Build a map of credential id list of agent names that use it.
31
- * Cross-references tools (which link credential via id) with agents
32
- * that have agent_tools referencing those tools.
33
- *
34
- * Since we don't have an account-level agent_tools endpoint, we
35
- * approximate by checking which tools are linked to credentials and
36
- * showing credential-level stats.
37
+ * Build a map of credential id -> list of agent names that use it.
37
38
  */
38
39
  function useCredentialAgentSummary(
39
40
  credentials: ToolCredential[],
@@ -41,12 +42,10 @@ function useCredentialAgentSummary(
41
42
  agents: Agent[],
42
43
  ) {
43
44
  return useMemo(() => {
44
- // Build a set of tool IDs that have credentials
45
45
  const toolIdsWithCredentials = new Set(
46
46
  credentials.map((c) => c.id_tool).filter(Boolean),
47
47
  );
48
48
 
49
- // Count how many credentials are linked to tools that agents could use
50
49
  const linkedCount = credentials.filter(
51
50
  (c) => c.id_tool && toolIdsWithCredentials.has(c.id_tool),
52
51
  ).length;
@@ -63,7 +62,9 @@ function useCredentialAgentSummary(
63
62
  export function IntegrationsManagementPage({
64
63
  config,
65
64
  gagentsApiUrl,
66
- onConnect,
65
+ resolveWizardMeta,
66
+ loadConfigOptions,
67
+ onWizardComplete,
67
68
  title = "Integrações e Credenciais",
68
69
  subtitle = "Gerencie todas as integrações e credenciais da conta.",
69
70
  }: IntegrationsManagementPageProps) {
@@ -73,12 +74,54 @@ export function IntegrationsManagementPage({
73
74
  const { data: toolsData } = useTools(config);
74
75
  const [createOpen, setCreateOpen] = useState(false);
75
76
 
77
+ // Integration cards state (account-level, agentId=null)
78
+ const { cards, isLoading: cardsLoading } = useIntegrationState(config, null);
79
+
80
+ // Wizard state
81
+ const [wizardOpen, setWizardOpen] = useState(false);
82
+ const [activeCard, setActiveCard] = useState<IntegrationCardData | null>(null);
83
+
76
84
  const credentials = credentialsData?.data || [];
77
85
  const agents: Agent[] = agentsData?.data || [];
78
86
  const tools: Tool[] = toolsData?.data || [];
79
87
 
80
88
  const summary = useCredentialAgentSummary(credentials, tools, agents);
81
89
 
90
+ const handleConnect = useCallback((card: IntegrationCardData) => {
91
+ setActiveCard(card);
92
+ setWizardOpen(true);
93
+ }, []);
94
+
95
+ const handleWizardClose = useCallback((open: boolean) => {
96
+ if (!open) {
97
+ setActiveCard(null);
98
+ }
99
+ setWizardOpen(open);
100
+ }, []);
101
+
102
+ const handleWizardComplete = useCallback(() => {
103
+ setWizardOpen(false);
104
+ setActiveCard(null);
105
+ onWizardComplete?.();
106
+ }, [onWizardComplete]);
107
+
108
+ // Resolve wizard meta with sensible defaults
109
+ const wizardMeta: WizardIntegrationMeta | null = activeCard
110
+ ? (resolveWizardMeta?.(activeCard) ?? {
111
+ capabilities: [],
112
+ requirements: [],
113
+ hasConfigStep: false,
114
+ })
115
+ : null;
116
+
117
+ // Split integration cards
118
+ const connectedCards = cards.filter(
119
+ (c) => !c.isAddNew && (c.state === "connected" || c.state === "expired"),
120
+ );
121
+ const otherCards = cards.filter(
122
+ (c) => c.isAddNew || c.state === "coming_soon",
123
+ );
124
+
82
125
  return (
83
126
  <div className="flex flex-col gap-4 p-4 md:p-6">
84
127
  <div className="flex items-center justify-between">
@@ -101,11 +144,61 @@ export function IntegrationsManagementPage({
101
144
  </TabsList>
102
145
 
103
146
  <TabsContent value="integrations" className="mt-4">
104
- <IntegrationsTab
105
- config={config}
106
- agentId={null}
107
- onConnect={onConnect ?? (() => {})}
108
- />
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
+ )}
200
+ </div>
201
+ )}
109
202
  </TabsContent>
110
203
 
111
204
  <TabsContent value="credentials" className="mt-4">
@@ -161,6 +254,22 @@ export function IntegrationsManagementPage({
161
254
  />
162
255
  </TabsContent>
163
256
  </Tabs>
257
+
258
+ {/* Integration Wizard */}
259
+ {activeCard && wizardMeta && (
260
+ <IntegrationWizard
261
+ open={wizardOpen}
262
+ onOpenChange={handleWizardClose}
263
+ integration={activeCard.definition}
264
+ meta={wizardMeta}
265
+ agentId={0}
266
+ config={config}
267
+ gagentsApiUrl={gagentsApiUrl}
268
+ existingCredentialId={activeCard.credentialId}
269
+ onComplete={handleWizardComplete}
270
+ loadConfigOptions={loadConfigOptions}
271
+ />
272
+ )}
164
273
  </div>
165
274
  );
166
275
  }