Create HGTX PromptLab UI
This commit is contained in:
+5
-5
@@ -3,12 +3,12 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>promptlab-hub</title>
|
||||
<meta name="description" content="Lovable Generated Project" />
|
||||
<meta name="author" content="Lovable" />
|
||||
<title>HGTX PromptLab - Laboratório de Prompts de IA</title>
|
||||
<meta name="description" content="Crie, edite, teste e versione prompts para agentes de IA de forma profissional e eficiente" />
|
||||
<meta name="author" content="HGTX" />
|
||||
|
||||
<meta property="og:title" content="promptlab-hub" />
|
||||
<meta property="og:description" content="Lovable Generated Project" />
|
||||
<meta property="og:title" content="HGTX PromptLab - Laboratório de Prompts de IA" />
|
||||
<meta property="og:description" content="Crie, edite, teste e versione prompts para agentes de IA de forma profissional e eficiente" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
|
||||
|
||||
|
||||
+13
-2
@@ -3,7 +3,12 @@ import { Toaster as Sonner } from "@/components/ui/sonner";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import Index from "./pages/Index";
|
||||
import { AppLayout } from "./components/layout/AppLayout";
|
||||
import Agents from "./pages/Agents";
|
||||
import Prompts from "./pages/Prompts";
|
||||
import TestPrompt from "./pages/TestPrompt";
|
||||
import History from "./pages/History";
|
||||
import LinkPrompt from "./pages/LinkPrompt";
|
||||
import NotFound from "./pages/NotFound";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
@@ -15,7 +20,13 @@ const App = () => (
|
||||
<Sonner />
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Index />} />
|
||||
<Route element={<AppLayout />}>
|
||||
<Route path="/" element={<Agents />} />
|
||||
<Route path="/prompts" element={<Prompts />} />
|
||||
<Route path="/test" element={<TestPrompt />} />
|
||||
<Route path="/history" element={<History />} />
|
||||
<Route path="/link" element={<LinkPrompt />} />
|
||||
</Route>
|
||||
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Bot, ExternalLink } from "lucide-react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
interface AgentCardProps {
|
||||
name: string;
|
||||
description: string;
|
||||
promptName?: string;
|
||||
status: "active" | "inactive";
|
||||
onLinkPrompt: () => void;
|
||||
}
|
||||
|
||||
export function AgentCard({ name, description, promptName, status, onLinkPrompt }: AgentCardProps) {
|
||||
return (
|
||||
<Card className="p-6 hover:shadow-hover transition-all duration-200 animate-fade-in">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center">
|
||||
<Bot className="h-6 w-6 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-foreground">{name}</h3>
|
||||
<Badge variant={status === "active" ? "default" : "secondary"} className="mt-1">
|
||||
{status === "active" ? "Ativo" : "Inativo"}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground mb-4">{description}</p>
|
||||
|
||||
{promptName ? (
|
||||
<div className="flex items-center justify-between p-3 bg-muted rounded-lg">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Prompt vinculado</p>
|
||||
<p className="text-sm font-medium text-foreground">{promptName}</p>
|
||||
</div>
|
||||
<ExternalLink className="h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
) : (
|
||||
<Button onClick={onLinkPrompt} variant="outline" className="w-full">
|
||||
Vincular Prompt
|
||||
</Button>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { AppSidebar } from "./AppSidebar";
|
||||
import { Outlet } from "react-router-dom";
|
||||
|
||||
export function AppLayout() {
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<div className="min-h-screen flex w-full bg-background">
|
||||
<AppSidebar />
|
||||
<main className="flex-1 p-6 lg:p-8">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { NavLink } from "react-router-dom";
|
||||
import {
|
||||
Bot,
|
||||
FileText,
|
||||
TestTube,
|
||||
History,
|
||||
Link,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarHeader,
|
||||
} from "@/components/ui/sidebar";
|
||||
|
||||
const menuItems = [
|
||||
{ title: "Agentes", url: "/", icon: Bot },
|
||||
{ title: "Prompts", url: "/prompts", icon: FileText },
|
||||
{ title: "Testar Prompt", url: "/test", icon: TestTube },
|
||||
{ title: "Histórico", url: "/history", icon: History },
|
||||
{ title: "Vincular", url: "/link", icon: Link },
|
||||
];
|
||||
|
||||
export function AppSidebar() {
|
||||
return (
|
||||
<Sidebar className="border-r border-border">
|
||||
<SidebarHeader className="border-b border-border px-6 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-8 w-8 rounded-lg bg-primary flex items-center justify-center">
|
||||
<Bot className="h-5 w-5 text-primary-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold text-foreground">HGTX</h1>
|
||||
<p className="text-xs text-muted-foreground">PromptLab</p>
|
||||
</div>
|
||||
</div>
|
||||
</SidebarHeader>
|
||||
|
||||
<SidebarContent>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel className="text-xs font-medium text-muted-foreground px-3">
|
||||
Navegação
|
||||
</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{menuItems.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton asChild>
|
||||
<NavLink
|
||||
to={item.url}
|
||||
end
|
||||
className={({ isActive }) =>
|
||||
`flex items-center gap-3 px-3 py-2 rounded-md transition-colors ${
|
||||
isActive
|
||||
? "bg-primary text-primary-foreground font-medium"
|
||||
: "text-foreground hover:bg-muted"
|
||||
}`
|
||||
}
|
||||
>
|
||||
<item.icon className="h-4 w-4" />
|
||||
<span>{item.title}</span>
|
||||
</NavLink>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { FileText, Edit, History, Copy, TestTube } from "lucide-react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
interface PromptCardProps {
|
||||
title: string;
|
||||
creator: string;
|
||||
lastModified: string;
|
||||
status: "draft" | "published";
|
||||
onEdit: () => void;
|
||||
onHistory: () => void;
|
||||
onTest: () => void;
|
||||
onDuplicate: () => void;
|
||||
}
|
||||
|
||||
export function PromptCard({
|
||||
title,
|
||||
creator,
|
||||
lastModified,
|
||||
status,
|
||||
onEdit,
|
||||
onHistory,
|
||||
onTest,
|
||||
onDuplicate,
|
||||
}: PromptCardProps) {
|
||||
return (
|
||||
<Card className="p-5 hover:shadow-hover transition-all duration-200 animate-fade-in">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-10 w-10 rounded-lg bg-accent/10 flex items-center justify-center">
|
||||
<FileText className="h-5 w-5 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-foreground">{title}</h3>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
por {creator} • {lastModified}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant={status === "published" ? "default" : "secondary"}>
|
||||
{status === "published" ? "Publicado" : "Rascunho"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Button onClick={onEdit} variant="outline" size="sm" className="flex-1">
|
||||
<Edit className="h-4 w-4 mr-2" />
|
||||
Editar
|
||||
</Button>
|
||||
<Button onClick={onHistory} variant="outline" size="sm">
|
||||
<History className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button onClick={onTest} variant="outline" size="sm">
|
||||
<TestTube className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button onClick={onDuplicate} variant="outline" size="sm">
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
+50
-52
@@ -8,89 +8,87 @@ All colors MUST be HSL.
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--background: 0 0% 98%;
|
||||
--foreground: 222 47% 11%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--card-foreground: 222 47% 11%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--primary: 221 83% 53%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 220 14% 96%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--muted: 220 14% 96%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--accent: 262 83% 58%;
|
||||
--accent-foreground: 0 0% 100%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 221 83% 53%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
||||
--sidebar-background: 0 0% 98%;
|
||||
|
||||
--sidebar-foreground: 240 5.3% 26.1%;
|
||||
|
||||
--sidebar-primary: 240 5.9% 10%;
|
||||
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
|
||||
--sidebar-accent: 240 4.8% 95.9%;
|
||||
|
||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||
--radius: 0.75rem;
|
||||
|
||||
--sidebar-background: 0 0% 100%;
|
||||
--sidebar-foreground: 222 47% 11%;
|
||||
--sidebar-primary: 221 83% 53%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 220 14% 96%;
|
||||
--sidebar-accent-foreground: 222 47% 11%;
|
||||
--sidebar-border: 220 13% 91%;
|
||||
--sidebar-ring: 221 83% 53%;
|
||||
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
--shadow-card: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-hover: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--transition-smooth: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--background: 222 47% 11%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card: 224 71% 4%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover: 224 71% 4%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--primary: 221 83% 53%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary: 217 33% 17%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--muted: 217 33% 17%;
|
||||
--muted-foreground: 215 20% 65%;
|
||||
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--accent: 262 83% 58%;
|
||||
--accent-foreground: 0 0% 100%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive: 0 63% 31%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
--sidebar-background: 240 5.9% 10%;
|
||||
--sidebar-foreground: 240 4.8% 95.9%;
|
||||
--sidebar-primary: 224.3 76.3% 48%;
|
||||
--border: 217 33% 17%;
|
||||
--input: 217 33% 17%;
|
||||
--ring: 224 76% 48%;
|
||||
|
||||
--sidebar-background: 224 71% 4%;
|
||||
--sidebar-foreground: 210 40% 98%;
|
||||
--sidebar-primary: 221 83% 53%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 240 3.7% 15.9%;
|
||||
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||
--sidebar-border: 240 3.7% 15.9%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
--sidebar-accent: 217 33% 17%;
|
||||
--sidebar-accent-foreground: 210 40% 98%;
|
||||
--sidebar-border: 217 33% 17%;
|
||||
--sidebar-ring: 224 76% 48%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useState } from "react";
|
||||
import { AgentCard } from "@/components/agents/AgentCard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Plus } from "lucide-react";
|
||||
|
||||
export default function Agents() {
|
||||
const [agents] = useState([
|
||||
{
|
||||
id: 1,
|
||||
name: "Agente de Suporte",
|
||||
description: "Responde dúvidas de clientes sobre produtos e serviços da empresa",
|
||||
promptName: "Prompt de Atendimento v2.1",
|
||||
status: "active" as const,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Agente de Vendas",
|
||||
description: "Qualifica leads e apresenta produtos de forma personalizada",
|
||||
promptName: "Prompt de Vendas v1.5",
|
||||
status: "active" as const,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Agente de Análise",
|
||||
description: "Analisa dados e gera insights para tomada de decisão",
|
||||
status: "inactive" as const,
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">Agentes de IA</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Gerencie os agentes de IA e seus prompts vinculados
|
||||
</p>
|
||||
</div>
|
||||
<Button className="gap-2">
|
||||
<Plus className="h-4 w-4" />
|
||||
Novo Agente
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{agents.map((agent) => (
|
||||
<AgentCard
|
||||
key={agent.id}
|
||||
name={agent.name}
|
||||
description={agent.description}
|
||||
promptName={agent.promptName}
|
||||
status={agent.status}
|
||||
onLinkPrompt={() => console.log("Link prompt", agent.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { RotateCcw, GitBranch } from "lucide-react";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
export default function History() {
|
||||
const versions = [
|
||||
{
|
||||
id: 1,
|
||||
version: "v2.1",
|
||||
author: "João Silva",
|
||||
date: "2 dias atrás",
|
||||
summary: "Melhorias na formatação de respostas e tom mais amigável",
|
||||
current: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
version: "v2.0",
|
||||
author: "João Silva",
|
||||
date: "1 semana atrás",
|
||||
summary: "Adicionado contexto sobre produtos lançados em 2024",
|
||||
current: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
version: "v1.9",
|
||||
author: "Maria Santos",
|
||||
date: "2 semanas atrás",
|
||||
summary: "Ajustes no limite de caracteres da resposta",
|
||||
current: false,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
version: "v1.8",
|
||||
author: "Pedro Costa",
|
||||
date: "3 semanas atrás",
|
||||
summary: "Versão inicial com estrutura básica do prompt",
|
||||
current: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in max-w-4xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">Histórico de Versões</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Visualize e restaure versões anteriores dos seus prompts
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Card className="p-6 mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
<label className="text-sm font-medium text-foreground mb-2 block">
|
||||
Selecione o Prompt
|
||||
</label>
|
||||
<Select defaultValue="atendimento">
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="atendimento">Prompt de Atendimento</SelectItem>
|
||||
<SelectItem value="vendas">Prompt de Vendas</SelectItem>
|
||||
<SelectItem value="analise">Prompt de Análise</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute left-8 top-0 bottom-0 w-0.5 bg-border" />
|
||||
|
||||
<div className="space-y-6">
|
||||
{versions.map((version) => (
|
||||
<div key={version.id} className="relative pl-16 animate-fade-in">
|
||||
<div
|
||||
className={`absolute left-6 w-5 h-5 rounded-full border-2 ${
|
||||
version.current
|
||||
? "bg-primary border-primary"
|
||||
: "bg-background border-border"
|
||||
}`}
|
||||
/>
|
||||
|
||||
<Card className="p-5 hover:shadow-hover transition-shadow">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge variant={version.current ? "default" : "secondary"}>
|
||||
{version.version}
|
||||
</Badge>
|
||||
{version.current && (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<GitBranch className="h-3 w-3" />
|
||||
Atual
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{!version.current && (
|
||||
<Button variant="outline" size="sm" className="gap-2">
|
||||
<RotateCcw className="h-4 w-4" />
|
||||
Restaurar
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-foreground font-medium mb-2">{version.summary}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
por {version.author} • {version.date}
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useState } from "react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Link as LinkIcon, Check } from "lucide-react";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
|
||||
export default function LinkPrompt() {
|
||||
const [selectedAgent, setSelectedAgent] = useState("");
|
||||
const [selectedPrompt, setSelectedPrompt] = useState("");
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleLink = () => {
|
||||
if (selectedAgent && selectedPrompt) {
|
||||
toast({
|
||||
title: "Prompt vinculado com sucesso!",
|
||||
description: "O agente já está usando o novo prompt.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in max-w-3xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">
|
||||
Vincular Prompt ao Agente
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Associe um prompt específico a um agente de IA
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Card className="p-8">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<Label htmlFor="agent-select">Selecione o Agente</Label>
|
||||
<Select value={selectedAgent} onValueChange={setSelectedAgent}>
|
||||
<SelectTrigger id="agent-select">
|
||||
<SelectValue placeholder="Escolha um agente" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="suporte">Agente de Suporte</SelectItem>
|
||||
<SelectItem value="vendas">Agente de Vendas</SelectItem>
|
||||
<SelectItem value="analise">Agente de Análise</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="prompt-select">Selecione o Prompt</Label>
|
||||
<Select value={selectedPrompt} onValueChange={setSelectedPrompt}>
|
||||
<SelectTrigger id="prompt-select">
|
||||
<SelectValue placeholder="Escolha um prompt" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="atendimento">
|
||||
Prompt de Atendimento v2.1
|
||||
</SelectItem>
|
||||
<SelectItem value="vendas">Prompt de Vendas v1.5</SelectItem>
|
||||
<SelectItem value="analise">Prompt de Análise</SelectItem>
|
||||
<SelectItem value="marketing">Prompt de Marketing</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{selectedAgent && selectedPrompt && (
|
||||
<Card className="p-4 bg-muted animate-scale-in">
|
||||
<h3 className="font-medium text-foreground mb-2">Resumo da Vinculação</h3>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Agente:</span>
|
||||
<span className="text-foreground font-medium">
|
||||
{selectedAgent === "suporte" && "Agente de Suporte"}
|
||||
{selectedAgent === "vendas" && "Agente de Vendas"}
|
||||
{selectedAgent === "analise" && "Agente de Análise"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Prompt:</span>
|
||||
<span className="text-foreground font-medium">
|
||||
{selectedPrompt === "atendimento" && "Prompt de Atendimento v2.1"}
|
||||
{selectedPrompt === "vendas" && "Prompt de Vendas v1.5"}
|
||||
{selectedPrompt === "analise" && "Prompt de Análise"}
|
||||
{selectedPrompt === "marketing" && "Prompt de Marketing"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Data:</span>
|
||||
<span className="text-foreground font-medium">
|
||||
{new Date().toLocaleDateString("pt-BR")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onClick={handleLink}
|
||||
disabled={!selectedAgent || !selectedPrompt}
|
||||
className="w-full gap-2"
|
||||
size="lg"
|
||||
>
|
||||
<LinkIcon className="h-4 w-4" />
|
||||
Vincular Prompt
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { useState } from "react";
|
||||
import { PromptCard } from "@/components/prompts/PromptCard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Plus } from "lucide-react";
|
||||
|
||||
export default function Prompts() {
|
||||
const [prompts] = useState([
|
||||
{
|
||||
id: 1,
|
||||
title: "Prompt de Atendimento v2.1",
|
||||
creator: "João Silva",
|
||||
lastModified: "2 dias atrás",
|
||||
status: "published" as const,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Prompt de Vendas v1.5",
|
||||
creator: "Maria Santos",
|
||||
lastModified: "5 dias atrás",
|
||||
status: "published" as const,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Prompt de Análise",
|
||||
creator: "Pedro Costa",
|
||||
lastModified: "1 semana atrás",
|
||||
status: "draft" as const,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Prompt de Marketing",
|
||||
creator: "Ana Lima",
|
||||
lastModified: "3 dias atrás",
|
||||
status: "published" as const,
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">Gestão de Prompts</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Crie, edite e gerencie os prompts dos seus agentes de IA
|
||||
</p>
|
||||
</div>
|
||||
<Button className="gap-2">
|
||||
<Plus className="h-4 w-4" />
|
||||
Criar Prompt
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{prompts.map((prompt) => (
|
||||
<PromptCard
|
||||
key={prompt.id}
|
||||
title={prompt.title}
|
||||
creator={prompt.creator}
|
||||
lastModified={prompt.lastModified}
|
||||
status={prompt.status}
|
||||
onEdit={() => console.log("Edit", prompt.id)}
|
||||
onHistory={() => console.log("History", prompt.id)}
|
||||
onTest={() => console.log("Test", prompt.id)}
|
||||
onDuplicate={() => console.log("Duplicate", prompt.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import { useState } from "react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { ThumbsUp, ThumbsDown, Send, Sparkles } from "lucide-react";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
export default function TestPrompt() {
|
||||
const [input, setInput] = useState("");
|
||||
const [output, setOutput] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleTest = () => {
|
||||
setIsLoading(true);
|
||||
setTimeout(() => {
|
||||
setOutput(
|
||||
"Esta é uma resposta simulada do agente de IA. Em produção, esta resposta seria gerada pelo modelo de IA configurado com o prompt selecionado."
|
||||
);
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in max-w-5xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">Teste de Prompt</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Teste seus prompts em tempo real e avalie as respostas geradas
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="prompt-select">Selecione o Prompt</Label>
|
||||
<Select>
|
||||
<SelectTrigger id="prompt-select">
|
||||
<SelectValue placeholder="Escolha um prompt" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="atendimento">Prompt de Atendimento v2.1</SelectItem>
|
||||
<SelectItem value="vendas">Prompt de Vendas v1.5</SelectItem>
|
||||
<SelectItem value="analise">Prompt de Análise</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="input">Entrada de Teste</Label>
|
||||
<Textarea
|
||||
id="input"
|
||||
placeholder="Digite sua mensagem de teste aqui..."
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
rows={4}
|
||||
className="resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button onClick={handleTest} disabled={!input || isLoading} className="w-full gap-2">
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Sparkles className="h-4 w-4 animate-spin" />
|
||||
Processando...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send className="h-4 w-4" />
|
||||
Testar Prompt
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{output && (
|
||||
<Card className="p-6 animate-scale-in">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>Resposta Gerada</Label>
|
||||
<div className="mt-2 p-4 bg-muted rounded-lg">
|
||||
<p className="text-foreground">{output}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="mb-2 block">Avaliar Resposta</Label>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" className="flex-1 gap-2">
|
||||
<ThumbsUp className="h-4 w-4" />
|
||||
Boa Resposta
|
||||
</Button>
|
||||
<Button variant="outline" className="flex-1 gap-2">
|
||||
<ThumbsDown className="h-4 w-4" />
|
||||
Precisa Melhorar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -80,10 +80,39 @@ export default {
|
||||
height: "0",
|
||||
},
|
||||
},
|
||||
"fade-in": {
|
||||
"0%": {
|
||||
opacity: "0",
|
||||
transform: "translateY(10px)"
|
||||
},
|
||||
"100%": {
|
||||
opacity: "1",
|
||||
transform: "translateY(0)"
|
||||
}
|
||||
},
|
||||
"scale-in": {
|
||||
"0%": {
|
||||
transform: "scale(0.95)",
|
||||
opacity: "0"
|
||||
},
|
||||
"100%": {
|
||||
transform: "scale(1)",
|
||||
opacity: "1"
|
||||
}
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"fade-in": "fade-in 0.3s ease-out",
|
||||
"scale-in": "scale-in 0.2s ease-out",
|
||||
},
|
||||
boxShadow: {
|
||||
'card': 'var(--shadow-card)',
|
||||
'hover': 'var(--shadow-hover)',
|
||||
},
|
||||
transitionTimingFunction: {
|
||||
'smooth': 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user