@greatapps/greatagents-ui 0.3.11 → 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.
- package/dist/index.d.ts +49 -69
- package/dist/index.js +2129 -2093
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/agents/agent-tabs.tsx +12 -27
- package/src/components/capabilities/integrations-tab.tsx +124 -69
- package/src/pages/agent-capabilities-page.tsx +2 -83
- package/src/pages/integrations-management-page.tsx +130 -21
package/package.json
CHANGED
|
@@ -1,45 +1,29 @@
|
|
|
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";
|
|
6
3
|
import { AgentObjectivesList } from "./agent-objectives-list";
|
|
7
4
|
import { AgentPromptEditor } from "./agent-prompt-editor";
|
|
8
5
|
import { AgentConversationsPanel } from "../conversations/agent-conversations-panel";
|
|
9
|
-
import {
|
|
6
|
+
import { CapabilitiesTab } from "../capabilities/capabilities-tab";
|
|
7
|
+
import { IntegrationsTab } from "../capabilities/integrations-tab";
|
|
10
8
|
import {
|
|
11
9
|
Tabs,
|
|
12
10
|
TabsList,
|
|
13
11
|
TabsTrigger,
|
|
14
12
|
TabsContent,
|
|
15
13
|
} from "@greatapps/greatauth-ui/ui";
|
|
16
|
-
import { Target, FileText, MessageCircle, Blocks } from "lucide-react";
|
|
14
|
+
import { Target, FileText, MessageCircle, Blocks, Plug } from "lucide-react";
|
|
17
15
|
|
|
18
16
|
interface AgentTabsProps {
|
|
19
17
|
agent: Agent;
|
|
20
18
|
config: GagentsHookConfig;
|
|
21
19
|
renderChatLink?: (inboxId: number) => React.ReactNode;
|
|
22
|
-
/** Required for the Capacidades tab — gagents API URL for OAuth flows and advanced features. Falls back to config.baseUrl. */
|
|
23
|
-
gagentsApiUrl?: string;
|
|
24
|
-
/** Resolve wizard metadata for a given integration card. */
|
|
25
|
-
resolveWizardMeta?: (card: IntegrationCardData) => WizardIntegrationMeta;
|
|
26
|
-
/** Callback to load config options after OAuth completes. */
|
|
27
|
-
loadConfigOptions?: (credentialId: number) => Promise<ConfigOption[]>;
|
|
28
|
-
/** Called after wizard completes successfully. */
|
|
29
|
-
onWizardComplete?: () => void;
|
|
30
20
|
}
|
|
31
21
|
|
|
32
22
|
export function AgentTabs({
|
|
33
23
|
agent,
|
|
34
24
|
config,
|
|
35
25
|
renderChatLink,
|
|
36
|
-
gagentsApiUrl,
|
|
37
|
-
resolveWizardMeta,
|
|
38
|
-
loadConfigOptions,
|
|
39
|
-
onWizardComplete,
|
|
40
26
|
}: AgentTabsProps) {
|
|
41
|
-
const apiUrl = gagentsApiUrl || config.baseUrl;
|
|
42
|
-
|
|
43
27
|
return (
|
|
44
28
|
<Tabs defaultValue="prompt">
|
|
45
29
|
<TabsList>
|
|
@@ -55,6 +39,10 @@ export function AgentTabs({
|
|
|
55
39
|
<Blocks aria-hidden="true" className="h-3.5 w-3.5" />
|
|
56
40
|
Capacidades
|
|
57
41
|
</TabsTrigger>
|
|
42
|
+
<TabsTrigger value="integracoes" className="flex items-center gap-1.5">
|
|
43
|
+
<Plug aria-hidden="true" className="h-3.5 w-3.5" />
|
|
44
|
+
Integrações
|
|
45
|
+
</TabsTrigger>
|
|
58
46
|
<TabsTrigger value="conversas" className="flex items-center gap-1.5">
|
|
59
47
|
<MessageCircle aria-hidden="true" className="h-3.5 w-3.5" />
|
|
60
48
|
Conversas
|
|
@@ -70,14 +58,11 @@ export function AgentTabs({
|
|
|
70
58
|
</TabsContent>
|
|
71
59
|
|
|
72
60
|
<TabsContent value="capacidades" className="mt-4">
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
loadConfigOptions={loadConfigOptions}
|
|
79
|
-
onWizardComplete={onWizardComplete}
|
|
80
|
-
/>
|
|
61
|
+
<CapabilitiesTab config={config} agentId={agent.id} />
|
|
62
|
+
</TabsContent>
|
|
63
|
+
|
|
64
|
+
<TabsContent value="integracoes" className="mt-4">
|
|
65
|
+
<IntegrationsTab config={config} agentId={agent.id} />
|
|
81
66
|
</TabsContent>
|
|
82
67
|
|
|
83
68
|
<TabsContent value="conversas" className="mt-4">
|
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { useCallback } from "react";
|
|
3
4
|
import type { GagentsHookConfig } from "../../hooks/types";
|
|
4
|
-
import { useIntegrationState
|
|
5
|
-
import {
|
|
5
|
+
import { useIntegrationState } from "../../hooks/use-integrations";
|
|
6
|
+
import { useAgentTools, useAddAgentTool, useRemoveAgentTool } from "../../hooks/use-agent-tools";
|
|
7
|
+
import { useTools } from "../../hooks/use-tools";
|
|
8
|
+
import { Switch, Tooltip, TooltipContent, TooltipTrigger } from "@greatapps/greatauth-ui/ui";
|
|
6
9
|
import { Plug, Loader2 } from "lucide-react";
|
|
10
|
+
import type { LucideIcon } from "lucide-react";
|
|
11
|
+
import { CalendarSync } from "lucide-react";
|
|
12
|
+
import { cn } from "../../lib";
|
|
7
13
|
|
|
8
14
|
// ---------------------------------------------------------------------------
|
|
9
|
-
//
|
|
15
|
+
// Icon mapping
|
|
10
16
|
// ---------------------------------------------------------------------------
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const ICON_MAP: Record<string, LucideIcon> = {
|
|
19
|
+
CalendarSync,
|
|
20
|
+
Plug,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function resolveIcon(name: string): LucideIcon {
|
|
24
|
+
return ICON_MAP[name] ?? Plug;
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
// ---------------------------------------------------------------------------
|
|
21
|
-
//
|
|
28
|
+
// Props
|
|
22
29
|
// ---------------------------------------------------------------------------
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
if (card.isAddNew) {
|
|
29
|
-
return `${card.definition.slug}-add-new`;
|
|
30
|
-
}
|
|
31
|
-
return card.definition.slug;
|
|
31
|
+
export interface IntegrationsTabProps {
|
|
32
|
+
config: GagentsHookConfig;
|
|
33
|
+
agentId: number;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
// ---------------------------------------------------------------------------
|
|
@@ -38,12 +40,46 @@ function getCardKey(card: IntegrationCardData): string {
|
|
|
38
40
|
export function IntegrationsTab({
|
|
39
41
|
config,
|
|
40
42
|
agentId,
|
|
41
|
-
onConnect,
|
|
42
43
|
}: IntegrationsTabProps) {
|
|
43
|
-
const { cards, isLoading } = useIntegrationState(config,
|
|
44
|
+
const { cards, isLoading } = useIntegrationState(config, null);
|
|
45
|
+
const { data: toolsData } = useTools(config);
|
|
46
|
+
const { data: agentToolsData, isLoading: agentToolsLoading } = useAgentTools(config, agentId);
|
|
47
|
+
const addAgentTool = useAddAgentTool(config);
|
|
48
|
+
const removeAgentTool = useRemoveAgentTool(config);
|
|
49
|
+
|
|
50
|
+
const tools = toolsData?.data ?? [];
|
|
51
|
+
const agentTools = agentToolsData?.data ?? [];
|
|
52
|
+
|
|
53
|
+
// Only show connected credentials (account-level)
|
|
54
|
+
const connectedCards = cards.filter(
|
|
55
|
+
(c) => !c.isAddNew && (c.state === "connected" || c.state === "expired"),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const handleToggle = useCallback(
|
|
59
|
+
(credentialId: number, toolSlug: string, checked: boolean) => {
|
|
60
|
+
// Find the tool record matching this integration slug
|
|
61
|
+
const tool = tools.find((t) => t.slug === toolSlug);
|
|
62
|
+
if (!tool) return;
|
|
63
|
+
|
|
64
|
+
if (checked) {
|
|
65
|
+
// Add agent_tool linking this agent to this tool
|
|
66
|
+
addAgentTool.mutate({
|
|
67
|
+
idAgent: agentId,
|
|
68
|
+
body: { id_tool: tool.id, enabled: true },
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
// Find the agent_tool to remove
|
|
72
|
+
const agentTool = agentTools.find((at) => at.id_tool === tool.id);
|
|
73
|
+
if (agentTool) {
|
|
74
|
+
removeAgentTool.mutate({ idAgent: agentId, id: agentTool.id });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[tools, agentTools, agentId, addAgentTool, removeAgentTool],
|
|
79
|
+
);
|
|
44
80
|
|
|
45
81
|
// Loading state
|
|
46
|
-
if (isLoading) {
|
|
82
|
+
if (isLoading || agentToolsLoading) {
|
|
47
83
|
return (
|
|
48
84
|
<div className="flex items-center justify-center py-16">
|
|
49
85
|
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
|
@@ -51,63 +87,82 @@ export function IntegrationsTab({
|
|
|
51
87
|
);
|
|
52
88
|
}
|
|
53
89
|
|
|
54
|
-
// Empty state
|
|
55
|
-
if (
|
|
90
|
+
// Empty state — no integrations connected at account level
|
|
91
|
+
if (connectedCards.length === 0) {
|
|
56
92
|
return (
|
|
57
93
|
<div className="flex flex-col items-center justify-center gap-3 py-16 text-muted-foreground">
|
|
58
94
|
<Plug className="h-10 w-10" />
|
|
59
|
-
<p className="text-sm">Nenhuma integração
|
|
95
|
+
<p className="text-sm font-medium">Nenhuma integração conectada</p>
|
|
96
|
+
<p className="text-xs text-center max-w-sm">
|
|
97
|
+
Conecte integrações na página de Integrações da conta para que possam
|
|
98
|
+
ser ativadas neste agente.
|
|
99
|
+
</p>
|
|
60
100
|
</div>
|
|
61
101
|
);
|
|
62
102
|
}
|
|
63
103
|
|
|
64
|
-
// Split into connected/expired cards and add-new/coming-soon cards
|
|
65
|
-
const connectedCards = cards.filter(
|
|
66
|
-
(c) => !c.isAddNew && (c.state === "connected" || c.state === "expired"),
|
|
67
|
-
);
|
|
68
|
-
const otherCards = cards.filter(
|
|
69
|
-
(c) => c.isAddNew || c.state === "coming_soon",
|
|
70
|
-
);
|
|
71
|
-
|
|
72
104
|
return (
|
|
73
|
-
<div className="space-y-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
<div className="space-y-4">
|
|
106
|
+
<p className="text-xs text-muted-foreground">
|
|
107
|
+
Ative ou desative as integrações conectadas na conta para este agente.
|
|
108
|
+
</p>
|
|
109
|
+
|
|
110
|
+
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
111
|
+
{connectedCards.map((card) => {
|
|
112
|
+
const Icon = resolveIcon(card.definition.icon);
|
|
113
|
+
const tool = tools.find((t) => t.slug === card.definition.slug);
|
|
114
|
+
const isLinked = tool
|
|
115
|
+
? agentTools.some((at) => at.id_tool === tool.id)
|
|
116
|
+
: false;
|
|
117
|
+
const isMutating = addAgentTool.isPending || removeAgentTool.isPending;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div
|
|
121
|
+
key={`${card.definition.slug}-cred-${card.credentialId}`}
|
|
122
|
+
className={cn(
|
|
123
|
+
"flex items-center gap-3 rounded-xl border bg-card p-4 transition-shadow",
|
|
124
|
+
isLinked ? "border-primary/30 shadow-sm" : "opacity-75",
|
|
125
|
+
)}
|
|
126
|
+
>
|
|
127
|
+
{/* Icon */}
|
|
128
|
+
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
|
129
|
+
<Icon className="h-4.5 w-4.5" />
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
{/* Info */}
|
|
133
|
+
<div className="flex-1 min-w-0">
|
|
134
|
+
<h4 className="text-sm font-medium leading-tight truncate">
|
|
135
|
+
{card.definition.name}
|
|
136
|
+
</h4>
|
|
137
|
+
{card.accountLabel && (
|
|
138
|
+
<Tooltip>
|
|
139
|
+
<TooltipTrigger asChild>
|
|
140
|
+
<p className="text-xs text-muted-foreground truncate" title={card.accountLabel}>
|
|
141
|
+
{card.accountLabel}
|
|
142
|
+
</p>
|
|
143
|
+
</TooltipTrigger>
|
|
144
|
+
<TooltipContent>{card.accountLabel}</TooltipContent>
|
|
145
|
+
</Tooltip>
|
|
146
|
+
)}
|
|
147
|
+
{card.state === "expired" && (
|
|
148
|
+
<p className="text-xs text-amber-600 dark:text-amber-400">Expirado</p>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{/* Toggle */}
|
|
153
|
+
<Switch
|
|
154
|
+
checked={isLinked}
|
|
155
|
+
disabled={isMutating}
|
|
156
|
+
onCheckedChange={(checked) =>
|
|
157
|
+
card.credentialId &&
|
|
158
|
+
handleToggle(card.credentialId, card.definition.slug, checked)
|
|
159
|
+
}
|
|
160
|
+
aria-label={`${isLinked ? "Desativar" : "Ativar"} ${card.definition.name} para este agente`}
|
|
106
161
|
/>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
})}
|
|
165
|
+
</div>
|
|
111
166
|
</div>
|
|
112
167
|
);
|
|
113
168
|
}
|
|
@@ -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
|
|
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,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 {
|
|
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
|
-
/**
|
|
24
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
}
|