modulo de agente pessoal de IA

This commit is contained in:
Vitex Tecnologia
2026-01-14 20:10:42 -03:00
parent ee7204982c
commit 76cbd80eef
25 changed files with 3619 additions and 3 deletions
@@ -0,0 +1,291 @@
import { useState, useEffect } from "react";
import { Eye, EyeOff, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import asanaLogo from "@/modules/intelligence-ia/assets/asana-logo.png";
interface Workspace {
id: string;
name: string;
}
interface User {
id: string;
name: string;
}
interface AsanaCardProps {
apiKey?: string;
workspaces?: Workspace[];
users?: User[];
selectedWorkspaceId?: string;
selectedUserId?: string;
loadingWorkspaces?: boolean;
loadingUsers?: boolean;
loadingIntegration?: boolean;
hasIntegration?: boolean;
saving?: boolean;
onSave?: (apiKey: string, workspaceId: string, userId: string) => void | Promise<void>;
onConfirmApiKey?: (apiKey: string) => void | Promise<void>;
onWorkspaceChange?: (workspaceId: string) => void | Promise<void>;
}
export function AsanaCard({
apiKey = "",
workspaces = [],
users = [],
selectedWorkspaceId = "",
selectedUserId = "",
loadingWorkspaces = false,
loadingUsers = false,
loadingIntegration = false,
hasIntegration = false,
saving = false,
onSave,
onConfirmApiKey,
onWorkspaceChange,
}: AsanaCardProps) {
const [showKey, setShowKey] = useState(false);
const [key, setKey] = useState(apiKey);
const [selectedWorkspace, setSelectedWorkspace] = useState(selectedWorkspaceId);
const [selectedUser, setSelectedUser] = useState(selectedUserId);
const [isApiKeyConfirmed, setIsApiKeyConfirmed] = useState(!!apiKey);
// Atualiza os valores quando as props mudam (carregamento inicial)
useEffect(() => {
if (apiKey) {
setKey(apiKey);
setIsApiKeyConfirmed(true);
}
}, [apiKey]);
useEffect(() => {
if (selectedWorkspaceId) {
setSelectedWorkspace(selectedWorkspaceId);
}
}, [selectedWorkspaceId]);
useEffect(() => {
if (selectedUserId) {
setSelectedUser(selectedUserId);
}
}, [selectedUserId]);
const handleKeyChange = (value: string) => {
setKey(value);
setSelectedWorkspace("");
setSelectedUser("");
setIsApiKeyConfirmed(false);
};
const handleConfirmApiKey = async () => {
if (key.length >= 5) {
setIsApiKeyConfirmed(true);
// Chama a função assíncrona para buscar workspaces
await onConfirmApiKey?.(key);
}
};
const handleWorkspaceChange = async (value: string) => {
setSelectedWorkspace(value);
setSelectedUser("");
// Chama a função assíncrona para buscar usuários
await onWorkspaceChange?.(value);
};
const handleSave = async () => {
await onSave?.(key, selectedWorkspace, selectedUser);
};
return (
<div className="metric-card space-y-4">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-muted flex items-center justify-center">
<img src={asanaLogo} alt="Asana" className="w-6 h-6" />
</div>
<div>
<h3 className="text-foreground font-medium">Asana</h3>
<p className="text-muted-foreground text-sm">Gerencie tarefas e projetos</p>
</div>
</div>
<div className="space-y-3">
<div>
<label className="text-sm text-muted-foreground mb-1.5 block">
API Key / Token
</label>
<div className="relative">
{loadingIntegration ? (
<div className="relative">
<Skeleton className="h-10 w-full" />
<div className="absolute inset-0 flex items-center justify-center">
<Loader2 className="w-4 h-4 animate-spin text-muted-foreground" />
</div>
</div>
) : (
<div className="flex gap-2">
<div className="relative flex-1">
<Input
type={showKey ? "text" : "password"}
value={key}
onChange={(e) => handleKeyChange(e.target.value)}
placeholder="Insira sua chave de API"
className="pr-10 bg-card border-border"
disabled={loadingWorkspaces}
/>
<button
onClick={() => setShowKey(!showKey)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
disabled={loadingWorkspaces}
>
{showKey ? (
<EyeOff className="w-4 h-4" />
) : (
<Eye className="w-4 h-4" />
)}
</button>
</div>
<Button
size="sm"
variant="outline"
onClick={handleConfirmApiKey}
disabled={key.length < 5 || isApiKeyConfirmed || loadingWorkspaces}
className="shrink-0"
>
{loadingWorkspaces ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Carregando...
</>
) : isApiKeyConfirmed ? (
"Confirmado"
) : (
"Confirmar"
)}
</Button>
</div>
)}
</div>
</div>
<div>
<label className="text-sm text-muted-foreground mb-1.5 block">
Workspace
</label>
{loadingIntegration ? (
<div className="relative">
<Skeleton className="h-10 w-full" />
<div className="absolute inset-0 flex items-center justify-center gap-2">
<Loader2 className="w-4 h-4 animate-spin text-muted-foreground" />
<span className="text-sm text-muted-foreground">Carregando integração...</span>
</div>
</div>
) : (
<div className="relative">
<Select
value={selectedWorkspace}
onValueChange={handleWorkspaceChange}
disabled={loadingWorkspaces || loadingIntegration}
>
<SelectTrigger className="bg-card border-border">
<SelectValue
placeholder={
loadingWorkspaces
? "Carregando workspaces..."
: workspaces.length === 0
? "Insira o token e clique em Confirmar"
: "Selecione um workspace"
}
/>
</SelectTrigger>
<SelectContent className="bg-card border-border">
{workspaces.map((ws) => (
<SelectItem key={ws.id} value={ws.id}>
{ws.name}
</SelectItem>
))}
</SelectContent>
</Select>
{loadingWorkspaces && (
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
<Loader2 className="w-4 h-4 animate-spin text-muted-foreground" />
</div>
)}
</div>
)}
</div>
<div>
<label className="text-sm text-muted-foreground mb-1.5 block">
Usuário
</label>
{loadingUsers ? (
<div className="relative">
<Skeleton className="h-10 w-full" />
<div className="absolute inset-0 flex items-center justify-center gap-2">
<Loader2 className="w-4 h-4 animate-spin text-muted-foreground" />
<span className="text-sm text-muted-foreground">Carregando usuários...</span>
</div>
</div>
) : (
<div className="relative">
<Select
value={selectedUser}
onValueChange={setSelectedUser}
disabled={loadingUsers || !selectedWorkspace}
>
<SelectTrigger className="bg-card border-border">
<SelectValue
placeholder={
!selectedWorkspace
? "Selecione um workspace primeiro"
: users.length === 0
? "Nenhum usuário encontrado"
: "Selecione um usuário"
}
/>
</SelectTrigger>
<SelectContent className="bg-card border-border">
{users.map((user) => (
<SelectItem key={user.id} value={user.id}>
{user.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
</div>
</div>
<div className="flex items-center gap-2 pt-2">
<Button
size="sm"
onClick={handleSave}
disabled={!selectedWorkspace || !selectedUser || saving}
className="bg-primary text-primary-foreground hover:bg-primary/90"
>
{saving ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
{hasIntegration ? "Salvando..." : "Criando..."}
</>
) : hasIntegration ? (
"Salvar"
) : (
"Criar Integração"
)}
</Button>
</div>
</div>
);
}