@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/greatagents-ui",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "description": "Shared agents UI components for Great platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,15 +1,10 @@
1
- import { useState, useCallback } from "react";
2
1
  import type { Agent } from "../../types";
3
2
  import type { GagentsHookConfig } from "../../hooks/types";
4
- import type { IntegrationCardData } from "../../hooks/use-integrations";
5
- import type { WizardIntegrationMeta } from "../capabilities/types";
6
- import type { ConfigOption } from "../capabilities/wizard-steps/config-step";
7
3
  import { AgentObjectivesList } from "./agent-objectives-list";
8
4
  import { AgentPromptEditor } from "./agent-prompt-editor";
9
5
  import { AgentConversationsPanel } from "../conversations/agent-conversations-panel";
10
6
  import { CapabilitiesTab } from "../capabilities/capabilities-tab";
11
7
  import { IntegrationsTab } from "../capabilities/integrations-tab";
12
- import { IntegrationWizard } from "../capabilities/integration-wizard";
13
8
  import {
14
9
  Tabs,
15
10
  TabsList,
@@ -22,119 +17,61 @@ interface AgentTabsProps {
22
17
  agent: Agent;
23
18
  config: GagentsHookConfig;
24
19
  renderChatLink?: (inboxId: number) => React.ReactNode;
25
- gagentsApiUrl?: string;
26
- resolveWizardMeta?: (card: IntegrationCardData) => WizardIntegrationMeta;
27
- loadConfigOptions?: (credentialId: number) => Promise<ConfigOption[]>;
28
- onWizardComplete?: () => void;
29
20
  }
30
21
 
31
22
  export function AgentTabs({
32
23
  agent,
33
24
  config,
34
25
  renderChatLink,
35
- gagentsApiUrl,
36
- resolveWizardMeta,
37
- loadConfigOptions,
38
- onWizardComplete,
39
26
  }: AgentTabsProps) {
40
- const apiUrl = gagentsApiUrl || config.baseUrl;
41
-
42
- // Wizard state for Integrações tab
43
- const [wizardOpen, setWizardOpen] = useState(false);
44
- const [activeCard, setActiveCard] = useState<IntegrationCardData | null>(null);
45
-
46
- const handleConnect = useCallback((card: IntegrationCardData) => {
47
- setActiveCard(card);
48
- setWizardOpen(true);
49
- }, []);
50
-
51
- const handleWizardClose = useCallback((open: boolean) => {
52
- if (!open) {
53
- setActiveCard(null);
54
- }
55
- setWizardOpen(open);
56
- }, []);
57
-
58
- const handleWizardComplete = useCallback(() => {
59
- setWizardOpen(false);
60
- setActiveCard(null);
61
- onWizardComplete?.();
62
- }, [onWizardComplete]);
63
-
64
- // Resolve wizard meta with sensible defaults
65
- const wizardMeta: WizardIntegrationMeta | null = activeCard
66
- ? (resolveWizardMeta?.(activeCard) ?? {
67
- capabilities: [],
68
- requirements: [],
69
- hasConfigStep: false,
70
- })
71
- : null;
72
-
73
27
  return (
74
- <>
75
- <Tabs defaultValue="prompt">
76
- <TabsList>
77
- <TabsTrigger value="prompt" className="flex items-center gap-1.5">
78
- <FileText aria-hidden="true" className="h-3.5 w-3.5" />
79
- Prompt
80
- </TabsTrigger>
81
- <TabsTrigger value="objetivos" className="flex items-center gap-1.5">
82
- <Target aria-hidden="true" className="h-3.5 w-3.5" />
83
- Objetivos
84
- </TabsTrigger>
85
- <TabsTrigger value="capacidades" className="flex items-center gap-1.5">
86
- <Blocks aria-hidden="true" className="h-3.5 w-3.5" />
87
- Capacidades
88
- </TabsTrigger>
89
- <TabsTrigger value="integracoes" className="flex items-center gap-1.5">
90
- <Plug aria-hidden="true" className="h-3.5 w-3.5" />
91
- Integrações
92
- </TabsTrigger>
93
- <TabsTrigger value="conversas" className="flex items-center gap-1.5">
94
- <MessageCircle aria-hidden="true" className="h-3.5 w-3.5" />
95
- Conversas
96
- </TabsTrigger>
97
- </TabsList>
98
-
99
- <TabsContent value="prompt" className="mt-4">
100
- <AgentPromptEditor agent={agent} config={config} />
101
- </TabsContent>
28
+ <Tabs defaultValue="prompt">
29
+ <TabsList>
30
+ <TabsTrigger value="prompt" className="flex items-center gap-1.5">
31
+ <FileText aria-hidden="true" className="h-3.5 w-3.5" />
32
+ Prompt
33
+ </TabsTrigger>
34
+ <TabsTrigger value="objetivos" className="flex items-center gap-1.5">
35
+ <Target aria-hidden="true" className="h-3.5 w-3.5" />
36
+ Objetivos
37
+ </TabsTrigger>
38
+ <TabsTrigger value="capacidades" className="flex items-center gap-1.5">
39
+ <Blocks aria-hidden="true" className="h-3.5 w-3.5" />
40
+ Capacidades
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>
46
+ <TabsTrigger value="conversas" className="flex items-center gap-1.5">
47
+ <MessageCircle aria-hidden="true" className="h-3.5 w-3.5" />
48
+ Conversas
49
+ </TabsTrigger>
50
+ </TabsList>
102
51
 
103
- <TabsContent value="objetivos" className="mt-4">
104
- <AgentObjectivesList agent={agent} config={config} />
105
- </TabsContent>
52
+ <TabsContent value="prompt" className="mt-4">
53
+ <AgentPromptEditor agent={agent} config={config} />
54
+ </TabsContent>
106
55
 
107
- <TabsContent value="capacidades" className="mt-4">
108
- <CapabilitiesTab config={config} agentId={agent.id} />
109
- </TabsContent>
56
+ <TabsContent value="objetivos" className="mt-4">
57
+ <AgentObjectivesList agent={agent} config={config} />
58
+ </TabsContent>
110
59
 
111
- <TabsContent value="integracoes" className="mt-4">
112
- <IntegrationsTab config={config} agentId={agent.id} onConnect={handleConnect} />
113
- </TabsContent>
60
+ <TabsContent value="capacidades" className="mt-4">
61
+ <CapabilitiesTab config={config} agentId={agent.id} />
62
+ </TabsContent>
114
63
 
115
- <TabsContent value="conversas" className="mt-4">
116
- <AgentConversationsPanel
117
- agent={agent}
118
- config={config}
119
- renderChatLink={renderChatLink}
120
- />
121
- </TabsContent>
122
- </Tabs>
64
+ <TabsContent value="integracoes" className="mt-4">
65
+ <IntegrationsTab config={config} agentId={agent.id} />
66
+ </TabsContent>
123
67
 
124
- {activeCard && wizardMeta && (
125
- <IntegrationWizard
126
- open={wizardOpen}
127
- onOpenChange={handleWizardClose}
128
- integration={activeCard.definition}
129
- meta={wizardMeta}
130
- agentId={agent.id}
68
+ <TabsContent value="conversas" className="mt-4">
69
+ <AgentConversationsPanel
70
+ agent={agent}
131
71
  config={config}
132
- gagentsApiUrl={apiUrl}
133
- existingCredentialId={activeCard.credentialId}
134
- onComplete={handleWizardComplete}
135
- loadConfigOptions={loadConfigOptions}
72
+ renderChatLink={renderChatLink}
136
73
  />
137
- )}
138
- </>
74
+ </TabsContent>
75
+ </Tabs>
139
76
  );
140
77
  }
@@ -25,7 +25,7 @@ import {
25
25
  Skeleton,
26
26
  } from "@greatapps/greatauth-ui/ui";
27
27
  import {
28
- Calendar,
28
+ CalendarCheck,
29
29
  Users,
30
30
  Settings,
31
31
  HeartHandshake,
@@ -55,7 +55,7 @@ function getOperationLabel(slug: string): string {
55
55
  // ---------------------------------------------------------------------------
56
56
 
57
57
  const CATEGORY_ICONS: Record<string, React.ElementType> = {
58
- agenda: Calendar,
58
+ agenda: CalendarCheck,
59
59
  cadastros: Users,
60
60
  infraestrutura: Settings,
61
61
  relacionamentos: HeartHandshake,
@@ -1,13 +1,12 @@
1
1
  'use client';
2
2
 
3
3
  import type { IntegrationCardData, IntegrationCardState } from "../../hooks/use-integrations";
4
- import { Badge, Button, Tooltip, TooltipContent, TooltipTrigger } from "@greatapps/greatauth-ui/ui";
4
+ import { Badge, Button } from "@greatapps/greatauth-ui/ui";
5
5
  import {
6
6
  CalendarSync,
7
7
  Plug,
8
8
  Settings,
9
9
  RefreshCw,
10
- Users,
11
10
  Clock,
12
11
  Plus,
13
12
  } from "lucide-react";
@@ -23,7 +22,6 @@ const ICON_MAP: Record<string, LucideIcon> = {
23
22
  Plug,
24
23
  Settings,
25
24
  RefreshCw,
26
- Users,
27
25
  Clock,
28
26
  Plus,
29
27
  };
@@ -84,7 +82,7 @@ export interface IntegrationCardProps {
84
82
  }
85
83
 
86
84
  export function IntegrationCard({ card, onConnect }: IntegrationCardProps) {
87
- const { definition, state, sharedByAgentsCount, isAddNew, accountLabel } = card;
85
+ const { definition, state, isAddNew, accountLabel } = card;
88
86
  const Icon = resolveIcon(definition.icon);
89
87
  const isComingSoon = state === "coming_soon";
90
88
  const actionLabel = getActionLabel(card);
@@ -195,23 +193,7 @@ export function IntegrationCard({ card, onConnect }: IntegrationCardProps) {
195
193
  </div>
196
194
 
197
195
  {/* Footer */}
198
- <div className="mt-auto flex items-center justify-between gap-2 pt-1">
199
- {sharedByAgentsCount > 0 ? (
200
- <Tooltip>
201
- <TooltipTrigger asChild>
202
- <span className="inline-flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400">
203
- <Users className="h-3.5 w-3.5" />
204
- Compartilhada
205
- </span>
206
- </TooltipTrigger>
207
- <TooltipContent>
208
- Esta credencial está disponível para todos os agentes da conta
209
- </TooltipContent>
210
- </Tooltip>
211
- ) : (
212
- <span />
213
- )}
214
-
196
+ <div className="mt-auto flex items-center justify-end gap-2 pt-1">
215
197
  {!isComingSoon && (
216
198
  <Button
217
199
  variant={state === "expired" ? "destructive" : "outline"}
@@ -1,34 +1,35 @@
1
1
  'use client';
2
2
 
3
+ import { useCallback } from "react";
3
4
  import type { GagentsHookConfig } from "../../hooks/types";
4
- import { useIntegrationState, type IntegrationCardData } from "../../hooks/use-integrations";
5
- import { IntegrationCard } from "./integration-card";
5
+ import { useIntegrationState } from "../../hooks/use-integrations";
6
+ import { useAgentTools, useAddAgentTool, useRemoveAgentTool } from "../../hooks/use-agent-tools";
7
+ import { Switch, Tooltip, TooltipContent, TooltipTrigger } from "@greatapps/greatauth-ui/ui";
6
8
  import { Plug, Loader2 } from "lucide-react";
9
+ import type { LucideIcon } from "lucide-react";
10
+ import { CalendarSync } from "lucide-react";
11
+ import { cn } from "../../lib";
7
12
 
8
13
  // ---------------------------------------------------------------------------
9
- // Props
14
+ // Icon mapping
10
15
  // ---------------------------------------------------------------------------
11
16
 
12
- export interface IntegrationsTabProps {
13
- config: GagentsHookConfig;
14
- agentId: number | null;
15
- /** Called when user clicks a card action (connect / configure / reconnect).
16
- * The consuming app wires this to the wizard (Story 18.9). */
17
- onConnect: (card: IntegrationCardData) => void;
17
+ const ICON_MAP: Record<string, LucideIcon> = {
18
+ CalendarSync,
19
+ Plug,
20
+ };
21
+
22
+ function resolveIcon(name: string): LucideIcon {
23
+ return ICON_MAP[name] ?? Plug;
18
24
  }
19
25
 
20
26
  // ---------------------------------------------------------------------------
21
- // Helpers
27
+ // Props
22
28
  // ---------------------------------------------------------------------------
23
29
 
24
- function getCardKey(card: IntegrationCardData): string {
25
- if (card.credentialId) {
26
- return `${card.definition.slug}-cred-${card.credentialId}`;
27
- }
28
- if (card.isAddNew) {
29
- return `${card.definition.slug}-add-new`;
30
- }
31
- return card.definition.slug;
30
+ export interface IntegrationsTabProps {
31
+ config: GagentsHookConfig;
32
+ agentId: number;
32
33
  }
33
34
 
34
35
  // ---------------------------------------------------------------------------
@@ -38,12 +39,40 @@ function getCardKey(card: IntegrationCardData): string {
38
39
  export function IntegrationsTab({
39
40
  config,
40
41
  agentId,
41
- onConnect,
42
42
  }: IntegrationsTabProps) {
43
43
  const { cards, isLoading } = useIntegrationState(config, agentId);
44
+ const { data: agentToolsData, isLoading: agentToolsLoading } = useAgentTools(config, agentId);
45
+ const addAgentTool = useAddAgentTool(config);
46
+ const removeAgentTool = useRemoveAgentTool(config);
47
+
48
+ const agentTools = agentToolsData?.data ?? [];
49
+
50
+ // Only show connected credentials (account-level)
51
+ const connectedCards = cards.filter(
52
+ (c) => !c.isAddNew && (c.state === "connected" || c.state === "expired"),
53
+ );
54
+
55
+ const handleToggle = useCallback(
56
+ (toolId: number, checked: boolean) => {
57
+ if (checked) {
58
+ // Add agent_tool linking this agent to this tool
59
+ addAgentTool.mutate({
60
+ idAgent: agentId,
61
+ body: { id_tool: toolId, enabled: true },
62
+ });
63
+ } else {
64
+ // Find the agent_tool to remove
65
+ const agentTool = agentTools.find((at) => at.id_tool === toolId);
66
+ if (agentTool) {
67
+ removeAgentTool.mutate({ idAgent: agentId, id: agentTool.id });
68
+ }
69
+ }
70
+ },
71
+ [agentTools, agentId, addAgentTool, removeAgentTool],
72
+ );
44
73
 
45
74
  // Loading state
46
- if (isLoading) {
75
+ if (isLoading || agentToolsLoading) {
47
76
  return (
48
77
  <div className="flex items-center justify-center py-16">
49
78
  <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
@@ -51,63 +80,78 @@ export function IntegrationsTab({
51
80
  );
52
81
  }
53
82
 
54
- // Empty state
55
- if (cards.length === 0) {
83
+ // Empty state — no integrations connected at account level
84
+ if (connectedCards.length === 0) {
56
85
  return (
57
86
  <div className="flex flex-col items-center justify-center gap-3 py-16 text-muted-foreground">
58
87
  <Plug className="h-10 w-10" />
59
- <p className="text-sm">Nenhuma integração disponível</p>
88
+ <p className="text-sm font-medium">Nenhuma integração conectada</p>
89
+ <p className="text-xs text-center max-w-sm">
90
+ Conecte integrações na página de Integrações da conta para que possam
91
+ ser ativadas neste agente.
92
+ </p>
60
93
  </div>
61
94
  );
62
95
  }
63
96
 
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
97
  return (
73
- <div className="space-y-6">
74
- {/* Connected accounts */}
75
- {connectedCards.length > 0 && (
76
- <div>
77
- <h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-muted-foreground">
78
- Contas conectadas
79
- </h3>
80
- <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
81
- {connectedCards.map((card) => (
82
- <IntegrationCard
83
- key={getCardKey(card)}
84
- card={card}
85
- onConnect={onConnect}
86
- />
87
- ))}
88
- </div>
89
- </div>
90
- )}
91
-
92
- {/* Add new / coming soon */}
93
- {otherCards.length > 0 && (
94
- <div>
95
- {connectedCards.length > 0 && (
96
- <h3 className="mb-3 text-xs font-medium uppercase tracking-wider text-muted-foreground">
97
- Adicionar integração
98
- </h3>
99
- )}
100
- <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
101
- {otherCards.map((card) => (
102
- <IntegrationCard
103
- key={getCardKey(card)}
104
- card={card}
105
- onConnect={onConnect}
98
+ <div className="space-y-4">
99
+ <p className="text-xs text-muted-foreground">
100
+ Ative ou desative as integrações conectadas na conta para este agente.
101
+ </p>
102
+
103
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
104
+ {connectedCards.map((card) => {
105
+ const Icon = resolveIcon(card.definition.icon);
106
+ const isLinked = card.linkedToAgent;
107
+ const isMutating = addAgentTool.isPending || removeAgentTool.isPending;
108
+
109
+ return (
110
+ <div
111
+ key={`${card.definition.slug}-cred-${card.credentialId}`}
112
+ className={cn(
113
+ "flex items-center gap-3 rounded-xl border bg-card p-4 transition-shadow",
114
+ isLinked ? "border-primary/30 shadow-sm" : "opacity-75",
115
+ )}
116
+ >
117
+ {/* Icon */}
118
+ <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
119
+ <Icon className="h-4.5 w-4.5" />
120
+ </div>
121
+
122
+ {/* Info */}
123
+ <div className="flex-1 min-w-0">
124
+ <h4 className="text-sm font-medium leading-tight truncate">
125
+ {card.definition.name}
126
+ </h4>
127
+ {card.accountLabel && (
128
+ <Tooltip>
129
+ <TooltipTrigger asChild>
130
+ <p className="text-xs text-muted-foreground truncate" title={card.accountLabel}>
131
+ {card.accountLabel}
132
+ </p>
133
+ </TooltipTrigger>
134
+ <TooltipContent>{card.accountLabel}</TooltipContent>
135
+ </Tooltip>
136
+ )}
137
+ {card.state === "expired" && (
138
+ <p className="text-xs text-amber-600 dark:text-amber-400">Expirado</p>
139
+ )}
140
+ </div>
141
+
142
+ {/* Toggle */}
143
+ <Switch
144
+ checked={isLinked}
145
+ disabled={isMutating || !card.tool}
146
+ onCheckedChange={(checked) =>
147
+ card.tool && handleToggle(card.tool.id, checked)
148
+ }
149
+ aria-label={`${isLinked ? "Desativar" : "Ativar"} ${card.definition.name} para este agente`}
106
150
  />
107
- ))}
108
- </div>
109
- </div>
110
- )}
151
+ </div>
152
+ );
153
+ })}
154
+ </div>
111
155
  </div>
112
156
  );
113
157
  }