@greatapps/greatagents-ui 0.3.3 → 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.
- package/dist/index.d.ts +204 -1
- package/dist/index.js +1675 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +14 -0
- package/src/components/capabilities/advanced-tab.tsx +82 -0
- package/src/components/capabilities/capabilities-tab.tsx +475 -0
- package/src/components/capabilities/integration-card.tsx +162 -0
- package/src/components/capabilities/integration-wizard.tsx +537 -0
- package/src/components/capabilities/integrations-tab.tsx +61 -0
- package/src/components/capabilities/types.ts +48 -0
- package/src/components/capabilities/wizard-steps/config-step.tsx +117 -0
- package/src/components/capabilities/wizard-steps/confirm-step.tsx +123 -0
- package/src/components/capabilities/wizard-steps/credentials-step.tsx +205 -0
- package/src/components/capabilities/wizard-steps/info-step.tsx +78 -0
- package/src/data/integrations-registry.ts +23 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/use-capabilities.ts +50 -0
- package/src/hooks/use-integrations.ts +114 -0
- package/src/index.ts +34 -0
- package/src/pages/agent-capabilities-page.tsx +159 -0
- package/src/pages/index.ts +2 -0
- package/src/pages/integrations-management-page.tsx +166 -0
- package/src/types/capabilities.ts +32 -0
- package/src/types/index.ts +10 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import type { GagentsHookConfig } from "./types";
|
|
3
|
+
import { useGagentsClient } from "./types";
|
|
4
|
+
import type { CapabilitiesResponse, AgentCapability, AgentCapabilitiesPayload } from "../types/capabilities";
|
|
5
|
+
|
|
6
|
+
export function useCapabilities(config: GagentsHookConfig) {
|
|
7
|
+
const client = useGagentsClient(config);
|
|
8
|
+
|
|
9
|
+
return useQuery({
|
|
10
|
+
queryKey: ["greatagents", "capabilities", config.accountId],
|
|
11
|
+
queryFn: async (): Promise<CapabilitiesResponse> => {
|
|
12
|
+
const res = await client.getCapabilities(config.accountId);
|
|
13
|
+
return (res.data as unknown as CapabilitiesResponse) ?? { product: null, categories: [] };
|
|
14
|
+
},
|
|
15
|
+
enabled: !!config.token && !!config.accountId,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useAgentCapabilities(config: GagentsHookConfig, agentId: number | null) {
|
|
20
|
+
const client = useGagentsClient(config);
|
|
21
|
+
|
|
22
|
+
return useQuery({
|
|
23
|
+
queryKey: ["greatagents", "agent-capabilities", config.accountId, agentId],
|
|
24
|
+
queryFn: async (): Promise<AgentCapability[]> => {
|
|
25
|
+
const res = await client.getAgentCapabilities(config.accountId, agentId!);
|
|
26
|
+
const d = res.data;
|
|
27
|
+
return (Array.isArray(d) ? d : []) as AgentCapability[];
|
|
28
|
+
},
|
|
29
|
+
enabled: !!config.token && !!config.accountId && !!agentId,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useUpdateAgentCapabilities(config: GagentsHookConfig) {
|
|
34
|
+
const client = useGagentsClient(config);
|
|
35
|
+
const queryClient = useQueryClient();
|
|
36
|
+
|
|
37
|
+
return useMutation({
|
|
38
|
+
mutationFn: ({ agentId, payload }: { agentId: number; payload: AgentCapabilitiesPayload }) =>
|
|
39
|
+
client.updateAgentCapabilities(config.accountId, agentId, payload),
|
|
40
|
+
onSuccess: (_data, variables) => {
|
|
41
|
+
queryClient.invalidateQueries({
|
|
42
|
+
queryKey: ["greatagents", "agent-capabilities", config.accountId, variables.agentId],
|
|
43
|
+
});
|
|
44
|
+
// Also invalidate agent-tools since capabilities map to tools
|
|
45
|
+
queryClient.invalidateQueries({
|
|
46
|
+
queryKey: ["greatagents", "agent-tools"],
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import type { GagentsHookConfig } from "./types";
|
|
3
|
+
import { useToolCredentials } from "./use-settings";
|
|
4
|
+
import { useAgentTools } from "./use-agent-tools";
|
|
5
|
+
import { useTools } from "./use-tools";
|
|
6
|
+
import {
|
|
7
|
+
INTEGRATIONS_REGISTRY,
|
|
8
|
+
type IntegrationDefinition,
|
|
9
|
+
} from "../data/integrations-registry";
|
|
10
|
+
import type { AgentTool, Tool, ToolCredential } from "../types";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Public types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export type IntegrationCardState =
|
|
17
|
+
| "available"
|
|
18
|
+
| "connected"
|
|
19
|
+
| "expired"
|
|
20
|
+
| "coming_soon";
|
|
21
|
+
|
|
22
|
+
export interface IntegrationCardData {
|
|
23
|
+
/** Static definition from registry */
|
|
24
|
+
definition: IntegrationDefinition;
|
|
25
|
+
/** Resolved visual state */
|
|
26
|
+
state: IntegrationCardState;
|
|
27
|
+
/** Matching credential if one exists */
|
|
28
|
+
credential: ToolCredential | null;
|
|
29
|
+
/** Matching tool record if one exists */
|
|
30
|
+
tool: Tool | null;
|
|
31
|
+
/** How many agents share this credential */
|
|
32
|
+
sharedByAgentsCount: number;
|
|
33
|
+
/** Whether this agent has a linked agent_tool for this integration */
|
|
34
|
+
linkedToAgent: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Hook
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cross-references the static integrations registry with live data
|
|
43
|
+
* (tools, tool_credentials, agent_tools) to produce card state for each
|
|
44
|
+
* integration entry.
|
|
45
|
+
*/
|
|
46
|
+
export function useIntegrationState(
|
|
47
|
+
config: GagentsHookConfig,
|
|
48
|
+
agentId: number | null,
|
|
49
|
+
) {
|
|
50
|
+
const { data: credentialsData, isLoading: loadingCredentials } =
|
|
51
|
+
useToolCredentials(config);
|
|
52
|
+
const { data: toolsData, isLoading: loadingTools } = useTools(config);
|
|
53
|
+
const { data: agentToolsData, isLoading: loadingAgentTools } = useAgentTools(
|
|
54
|
+
config,
|
|
55
|
+
agentId ?? 0,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const isLoading = loadingCredentials || loadingTools || loadingAgentTools;
|
|
59
|
+
|
|
60
|
+
const cards: IntegrationCardData[] = useMemo(() => {
|
|
61
|
+
const credentials: ToolCredential[] = credentialsData?.data ?? [];
|
|
62
|
+
const tools: Tool[] = toolsData?.data ?? [];
|
|
63
|
+
const agentTools: AgentTool[] = agentToolsData?.data ?? [];
|
|
64
|
+
|
|
65
|
+
return INTEGRATIONS_REGISTRY.map((def) => {
|
|
66
|
+
// coming_soon short-circuit
|
|
67
|
+
if (def.status === "coming_soon") {
|
|
68
|
+
return {
|
|
69
|
+
definition: def,
|
|
70
|
+
state: "coming_soon" as const,
|
|
71
|
+
credential: null,
|
|
72
|
+
tool: null,
|
|
73
|
+
sharedByAgentsCount: 0,
|
|
74
|
+
linkedToAgent: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Find tool record matching registry slug
|
|
79
|
+
const matchedTool = tools.find((t) => t.slug === def.slug) ?? null;
|
|
80
|
+
|
|
81
|
+
// Find credential for that tool
|
|
82
|
+
const matchedCredential = matchedTool
|
|
83
|
+
? credentials.find((c) => c.id_tool === matchedTool.id) ?? null
|
|
84
|
+
: null;
|
|
85
|
+
|
|
86
|
+
// Check if this agent has a linked agent_tool for this tool
|
|
87
|
+
const linkedToAgent = matchedTool
|
|
88
|
+
? agentTools.some((at) => at.id_tool === matchedTool.id)
|
|
89
|
+
: false;
|
|
90
|
+
|
|
91
|
+
// Sharing indicator: credential exists at account level (available to all agents)
|
|
92
|
+
// When a credential is account-scoped, any agent can use it — show as "shared"
|
|
93
|
+
const sharedByAgentsCount = matchedCredential ? 1 : 0;
|
|
94
|
+
|
|
95
|
+
// Determine state
|
|
96
|
+
let state: IntegrationCardState = "available";
|
|
97
|
+
if (matchedCredential) {
|
|
98
|
+
state =
|
|
99
|
+
matchedCredential.status === "expired" ? "expired" : "connected";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
definition: def,
|
|
104
|
+
state,
|
|
105
|
+
credential: matchedCredential,
|
|
106
|
+
tool: matchedTool,
|
|
107
|
+
sharedByAgentsCount,
|
|
108
|
+
linkedToAgent,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
}, [credentialsData, toolsData, agentToolsData]);
|
|
112
|
+
|
|
113
|
+
return { cards, isLoading };
|
|
114
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,12 @@ export type {
|
|
|
13
13
|
PromptVersion,
|
|
14
14
|
Tool,
|
|
15
15
|
ToolCredential,
|
|
16
|
+
CapabilityOperation,
|
|
17
|
+
CapabilityModule,
|
|
18
|
+
CapabilityCategory,
|
|
19
|
+
CapabilitiesResponse,
|
|
20
|
+
AgentCapability,
|
|
21
|
+
AgentCapabilitiesPayload,
|
|
16
22
|
} from "./types";
|
|
17
23
|
|
|
18
24
|
// Client
|
|
@@ -41,5 +47,33 @@ export { AgentConversationsPanel } from "./components/conversations/agent-conver
|
|
|
41
47
|
export { AgentConversationsTable } from "./components/conversations/agent-conversations-table";
|
|
42
48
|
export { ConversationView } from "./components/conversations/conversation-view";
|
|
43
49
|
|
|
50
|
+
// Capabilities
|
|
51
|
+
export { IntegrationCard } from "./components/capabilities/integration-card";
|
|
52
|
+
export type { IntegrationCardProps } from "./components/capabilities/integration-card";
|
|
53
|
+
export { IntegrationsTab } from "./components/capabilities/integrations-tab";
|
|
54
|
+
export type { IntegrationsTabProps } from "./components/capabilities/integrations-tab";
|
|
55
|
+
export { CapabilitiesTab } from "./components/capabilities/capabilities-tab";
|
|
56
|
+
export type { CapabilitiesTabProps } from "./components/capabilities/capabilities-tab";
|
|
57
|
+
export { AdvancedTab } from "./components/capabilities/advanced-tab";
|
|
58
|
+
export type { AdvancedTabProps } from "./components/capabilities/advanced-tab";
|
|
59
|
+
export { IntegrationWizard } from "./components/capabilities/integration-wizard";
|
|
60
|
+
export type { IntegrationWizardProps } from "./components/capabilities/integration-wizard";
|
|
61
|
+
export type {
|
|
62
|
+
WizardIntegrationMeta,
|
|
63
|
+
IntegrationCapability,
|
|
64
|
+
WizardStep,
|
|
65
|
+
OAuthStatus,
|
|
66
|
+
OAuthResult,
|
|
67
|
+
} from "./components/capabilities/types";
|
|
68
|
+
export type { ConfigOption } from "./components/capabilities/wizard-steps/config-step";
|
|
69
|
+
|
|
70
|
+
// Data
|
|
71
|
+
export { INTEGRATIONS_REGISTRY } from "./data/integrations-registry";
|
|
72
|
+
export type {
|
|
73
|
+
IntegrationDefinition,
|
|
74
|
+
IntegrationAuthType,
|
|
75
|
+
IntegrationStatus,
|
|
76
|
+
} from "./data/integrations-registry";
|
|
77
|
+
|
|
44
78
|
// Page Compositions
|
|
45
79
|
export * from "./pages";
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Tabs,
|
|
6
|
+
TabsList,
|
|
7
|
+
TabsTrigger,
|
|
8
|
+
TabsContent,
|
|
9
|
+
} from "@greatapps/greatauth-ui/ui";
|
|
10
|
+
import { Blocks, Plug, Settings } from "lucide-react";
|
|
11
|
+
|
|
12
|
+
import type { GagentsHookConfig } from "../hooks/types";
|
|
13
|
+
import type { IntegrationCardData } from "../hooks/use-integrations";
|
|
14
|
+
import type { WizardIntegrationMeta } from "../components/capabilities/types";
|
|
15
|
+
import { CapabilitiesTab } from "../components/capabilities/capabilities-tab";
|
|
16
|
+
import { IntegrationsTab } from "../components/capabilities/integrations-tab";
|
|
17
|
+
import { IntegrationWizard } from "../components/capabilities/integration-wizard";
|
|
18
|
+
import { AdvancedTab } from "../components/capabilities/advanced-tab";
|
|
19
|
+
import type { ConfigOption } from "../components/capabilities/wizard-steps/config-step";
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Props
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
export interface AgentCapabilitiesPageProps {
|
|
26
|
+
config: GagentsHookConfig;
|
|
27
|
+
agentId: number;
|
|
28
|
+
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
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Component
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
export function AgentCapabilitiesPage({
|
|
63
|
+
config,
|
|
64
|
+
agentId,
|
|
65
|
+
gagentsApiUrl,
|
|
66
|
+
resolveWizardMeta = defaultResolveWizardMeta,
|
|
67
|
+
loadConfigOptions,
|
|
68
|
+
onWizardComplete,
|
|
69
|
+
}: 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
|
+
return (
|
|
97
|
+
<div className="space-y-4">
|
|
98
|
+
<div>
|
|
99
|
+
<h2 className="text-lg font-semibold">Capacidades e Integrações</h2>
|
|
100
|
+
<p className="text-sm text-muted-foreground">
|
|
101
|
+
Configure o que este agente pode fazer e quais serviços externos ele utiliza.
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<Tabs defaultValue="capacidades">
|
|
106
|
+
<TabsList>
|
|
107
|
+
<TabsTrigger value="capacidades" className="flex items-center gap-1.5">
|
|
108
|
+
<Blocks aria-hidden="true" className="h-3.5 w-3.5" />
|
|
109
|
+
Capacidades
|
|
110
|
+
</TabsTrigger>
|
|
111
|
+
<TabsTrigger value="integracoes" className="flex items-center gap-1.5">
|
|
112
|
+
<Plug aria-hidden="true" className="h-3.5 w-3.5" />
|
|
113
|
+
Integrações
|
|
114
|
+
</TabsTrigger>
|
|
115
|
+
<TabsTrigger value="avancado" className="flex items-center gap-1.5">
|
|
116
|
+
<Settings aria-hidden="true" className="h-3.5 w-3.5" />
|
|
117
|
+
Avançado
|
|
118
|
+
</TabsTrigger>
|
|
119
|
+
</TabsList>
|
|
120
|
+
|
|
121
|
+
<TabsContent value="capacidades" className="mt-4">
|
|
122
|
+
<CapabilitiesTab config={config} agentId={agentId} />
|
|
123
|
+
</TabsContent>
|
|
124
|
+
|
|
125
|
+
<TabsContent value="integracoes" className="mt-4">
|
|
126
|
+
<IntegrationsTab
|
|
127
|
+
config={config}
|
|
128
|
+
agentId={agentId}
|
|
129
|
+
onConnect={handleConnect}
|
|
130
|
+
/>
|
|
131
|
+
</TabsContent>
|
|
132
|
+
|
|
133
|
+
<TabsContent value="avancado" className="mt-4">
|
|
134
|
+
<AdvancedTab
|
|
135
|
+
config={config}
|
|
136
|
+
agentId={agentId}
|
|
137
|
+
gagentsApiUrl={gagentsApiUrl}
|
|
138
|
+
/>
|
|
139
|
+
</TabsContent>
|
|
140
|
+
</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
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
package/src/pages/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { AgentsPage, type AgentsPageProps } from "./agents-page";
|
|
2
2
|
export { AgentDetailPage, type AgentDetailPageProps } from "./agent-detail-page";
|
|
3
|
+
export { AgentCapabilitiesPage, type AgentCapabilitiesPageProps } from "./agent-capabilities-page";
|
|
3
4
|
export { ToolsPage, type ToolsPageProps } from "./tools-page";
|
|
4
5
|
export { CredentialsPage, type CredentialsPageProps } from "./credentials-page";
|
|
6
|
+
export { IntegrationsManagementPage, type IntegrationsManagementPageProps } from "./integrations-management-page";
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useMemo } from "react";
|
|
4
|
+
import { useToolCredentials, useAgents, useTools } from "../hooks";
|
|
5
|
+
import { ToolCredentialsForm } from "../components/tools/tool-credentials-form";
|
|
6
|
+
import { IntegrationsTab } from "../components/capabilities/integrations-tab";
|
|
7
|
+
import {
|
|
8
|
+
Badge,
|
|
9
|
+
Button,
|
|
10
|
+
Tabs,
|
|
11
|
+
TabsContent,
|
|
12
|
+
TabsList,
|
|
13
|
+
TabsTrigger,
|
|
14
|
+
} from "@greatapps/greatauth-ui/ui";
|
|
15
|
+
import { Plus, Plug, KeyRound, Info } from "lucide-react";
|
|
16
|
+
import type { GagentsHookConfig } from "../hooks/types";
|
|
17
|
+
import type { IntegrationCardData } from "../hooks/use-integrations";
|
|
18
|
+
import type { Agent, Tool, ToolCredential } from "../types";
|
|
19
|
+
|
|
20
|
+
export interface IntegrationsManagementPageProps {
|
|
21
|
+
config: GagentsHookConfig;
|
|
22
|
+
gagentsApiUrl: string;
|
|
23
|
+
/** Called when user clicks connect/reconnect on an integration card. */
|
|
24
|
+
onConnect?: (card: IntegrationCardData) => void;
|
|
25
|
+
title?: string;
|
|
26
|
+
subtitle?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
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
|
+
*/
|
|
38
|
+
function useCredentialAgentSummary(
|
|
39
|
+
credentials: ToolCredential[],
|
|
40
|
+
tools: Tool[],
|
|
41
|
+
agents: Agent[],
|
|
42
|
+
) {
|
|
43
|
+
return useMemo(() => {
|
|
44
|
+
// Build a set of tool IDs that have credentials
|
|
45
|
+
const toolIdsWithCredentials = new Set(
|
|
46
|
+
credentials.map((c) => c.id_tool).filter(Boolean),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Count how many credentials are linked to tools that agents could use
|
|
50
|
+
const linkedCount = credentials.filter(
|
|
51
|
+
(c) => c.id_tool && toolIdsWithCredentials.has(c.id_tool),
|
|
52
|
+
).length;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
totalCredentials: credentials.length,
|
|
56
|
+
linkedToTools: linkedCount,
|
|
57
|
+
totalAgents: agents.length,
|
|
58
|
+
totalTools: tools.length,
|
|
59
|
+
};
|
|
60
|
+
}, [credentials, tools, agents]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function IntegrationsManagementPage({
|
|
64
|
+
config,
|
|
65
|
+
gagentsApiUrl,
|
|
66
|
+
onConnect,
|
|
67
|
+
title = "Integrações e Credenciais",
|
|
68
|
+
subtitle = "Gerencie todas as integrações e credenciais da conta.",
|
|
69
|
+
}: IntegrationsManagementPageProps) {
|
|
70
|
+
const { data: credentialsData, isLoading: credentialsLoading } =
|
|
71
|
+
useToolCredentials(config);
|
|
72
|
+
const { data: agentsData } = useAgents(config);
|
|
73
|
+
const { data: toolsData } = useTools(config);
|
|
74
|
+
const [createOpen, setCreateOpen] = useState(false);
|
|
75
|
+
|
|
76
|
+
const credentials = credentialsData?.data || [];
|
|
77
|
+
const agents: Agent[] = agentsData?.data || [];
|
|
78
|
+
const tools: Tool[] = toolsData?.data || [];
|
|
79
|
+
|
|
80
|
+
const summary = useCredentialAgentSummary(credentials, tools, agents);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="flex flex-col gap-4 p-4 md:p-6">
|
|
84
|
+
<div className="flex items-center justify-between">
|
|
85
|
+
<div>
|
|
86
|
+
<h1 className="text-xl font-semibold">{title}</h1>
|
|
87
|
+
<p className="text-sm text-muted-foreground">{subtitle}</p>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<Tabs defaultValue="integrations" className="w-full">
|
|
92
|
+
<TabsList>
|
|
93
|
+
<TabsTrigger value="integrations" className="gap-2">
|
|
94
|
+
<Plug className="h-4 w-4" />
|
|
95
|
+
Integrações
|
|
96
|
+
</TabsTrigger>
|
|
97
|
+
<TabsTrigger value="credentials" className="gap-2">
|
|
98
|
+
<KeyRound className="h-4 w-4" />
|
|
99
|
+
Credenciais
|
|
100
|
+
</TabsTrigger>
|
|
101
|
+
</TabsList>
|
|
102
|
+
|
|
103
|
+
<TabsContent value="integrations" className="mt-4">
|
|
104
|
+
<IntegrationsTab
|
|
105
|
+
config={config}
|
|
106
|
+
agentId={null}
|
|
107
|
+
onConnect={onConnect ?? (() => {})}
|
|
108
|
+
/>
|
|
109
|
+
</TabsContent>
|
|
110
|
+
|
|
111
|
+
<TabsContent value="credentials" className="mt-4">
|
|
112
|
+
{/* Summary bar */}
|
|
113
|
+
{!credentialsLoading && (
|
|
114
|
+
<div className="flex items-center gap-4 rounded-lg border bg-muted/50 px-4 py-3 mb-4">
|
|
115
|
+
<Info className="h-4 w-4 text-muted-foreground shrink-0" />
|
|
116
|
+
<div className="flex flex-wrap items-center gap-3 text-sm">
|
|
117
|
+
<span>
|
|
118
|
+
<Badge variant="secondary" className="mr-1">
|
|
119
|
+
{summary.totalCredentials}
|
|
120
|
+
</Badge>
|
|
121
|
+
{summary.totalCredentials === 1
|
|
122
|
+
? "credencial configurada"
|
|
123
|
+
: "credenciais configuradas"}
|
|
124
|
+
</span>
|
|
125
|
+
<span className="text-muted-foreground">|</span>
|
|
126
|
+
<span>
|
|
127
|
+
<Badge variant="secondary" className="mr-1">
|
|
128
|
+
{summary.linkedToTools}
|
|
129
|
+
</Badge>
|
|
130
|
+
{summary.linkedToTools === 1
|
|
131
|
+
? "vinculada a ferramentas"
|
|
132
|
+
: "vinculadas a ferramentas"}
|
|
133
|
+
</span>
|
|
134
|
+
<span className="text-muted-foreground">|</span>
|
|
135
|
+
<span>
|
|
136
|
+
<Badge variant="secondary" className="mr-1">
|
|
137
|
+
{summary.totalAgents}
|
|
138
|
+
</Badge>
|
|
139
|
+
{summary.totalAgents === 1
|
|
140
|
+
? "agente na conta"
|
|
141
|
+
: "agentes na conta"}
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
<div className="flex items-center justify-end mb-4">
|
|
148
|
+
<Button onClick={() => setCreateOpen(true)} size="sm">
|
|
149
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
150
|
+
Nova Credencial
|
|
151
|
+
</Button>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<ToolCredentialsForm
|
|
155
|
+
config={config}
|
|
156
|
+
gagentsApiUrl={gagentsApiUrl}
|
|
157
|
+
credentials={credentials}
|
|
158
|
+
isLoading={credentialsLoading}
|
|
159
|
+
createOpen={createOpen}
|
|
160
|
+
onCreateOpenChange={setCreateOpen}
|
|
161
|
+
/>
|
|
162
|
+
</TabsContent>
|
|
163
|
+
</Tabs>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface CapabilityOperation {
|
|
2
|
+
slug: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface CapabilityModule {
|
|
8
|
+
slug: string;
|
|
9
|
+
label: string;
|
|
10
|
+
description: string;
|
|
11
|
+
operations: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CapabilityCategory {
|
|
15
|
+
slug: string;
|
|
16
|
+
label: string;
|
|
17
|
+
modules: CapabilityModule[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CapabilitiesResponse {
|
|
21
|
+
product: string | null;
|
|
22
|
+
categories: CapabilityCategory[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AgentCapability {
|
|
26
|
+
module: string;
|
|
27
|
+
operations: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AgentCapabilitiesPayload {
|
|
31
|
+
capabilities: AgentCapability[];
|
|
32
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -154,3 +154,13 @@ export interface AvailabilityResult {
|
|
|
154
154
|
conflicts: AvailabilityConflict[];
|
|
155
155
|
failed_providers: Array<{ provider: string; error: string }>;
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
// Capabilities
|
|
159
|
+
export type {
|
|
160
|
+
CapabilityOperation,
|
|
161
|
+
CapabilityModule,
|
|
162
|
+
CapabilityCategory,
|
|
163
|
+
CapabilitiesResponse,
|
|
164
|
+
AgentCapability,
|
|
165
|
+
AgentCapabilitiesPayload,
|
|
166
|
+
} from "./capabilities";
|