diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
index e1bd47e..35b2ab4 100644
--- a/src/components/Layout.tsx
+++ b/src/components/Layout.tsx
@@ -1,16 +1,16 @@
import { useState } from "react";
-import { MessageSquare, Image, Mic, ArrowLeft, Plus, ChevronLeft, ChevronRight, Bot } from "lucide-react";
+import { MessageSquare, Image, Mic, ArrowLeft, Plus, ChevronLeft, ChevronRight, Bot, FileText } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { ThemeToggle } from "@/components/ThemeToggle";
interface LayoutProps {
children: React.ReactNode;
- activeTab: "chat" | "images" | "audio" | "bots";
- onTabChange: (tab: "chat" | "images" | "audio" | "bots") => void;
+ activeTab: "chat" | "images" | "audio" | "bots" | "agent";
+ onTabChange: (tab: "chat" | "images" | "audio" | "bots" | "agent") => void;
}
-type TabType = "chat" | "images" | "audio" | "bots";
+type TabType = "chat" | "images" | "audio" | "bots" | "agent";
export const Layout = ({ children, activeTab, onTabChange }: LayoutProps) => {
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
@@ -20,6 +20,7 @@ export const Layout = ({ children, activeTab, onTabChange }: LayoutProps) => {
{ id: "images" as TabType, label: "Imagens", icon: Image },
{ id: "audio" as TabType, label: "Áudio", icon: Mic },
{ id: "bots" as TabType, label: "Bots", icon: Bot },
+ { id: "agent" as TabType, label: "Agente de Parecer", icon: FileText },
];
return (
diff --git a/src/components/agent/AgentChat.tsx b/src/components/agent/AgentChat.tsx
new file mode 100644
index 0000000..1b92085
--- /dev/null
+++ b/src/components/agent/AgentChat.tsx
@@ -0,0 +1,195 @@
+import { useState } from "react";
+import { Button } from "@/components/ui/button";
+import { Textarea } from "@/components/ui/textarea";
+import { Input } from "@/components/ui/input";
+import { Download, Send, Sparkles } from "lucide-react";
+import { LegalOpinion } from "./AgentView";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { useToast } from "@/hooks/use-toast";
+
+interface AgentChatProps {
+ selectedOpinion: LegalOpinion | null;
+ isCreatingNew: boolean;
+ onOpinionCreated: (opinion: LegalOpinion) => void;
+}
+
+export const AgentChat = ({
+ selectedOpinion,
+ isCreatingNew,
+ onOpinionCreated,
+}: AgentChatProps) => {
+ const [title, setTitle] = useState("");
+ const [instructions, setInstructions] = useState("");
+ const [category, setCategory] = useState("");
+ const [generatedContent, setGeneratedContent] = useState("");
+ const [isGenerating, setIsGenerating] = useState(false);
+ const { toast } = useToast();
+
+ const handleGenerate = async () => {
+ if (!instructions.trim()) {
+ toast({
+ title: "Instruções necessárias",
+ description: "Por favor, forneça instruções para gerar o parecer.",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ setIsGenerating(true);
+
+ // Simulação de chamada à IA - aqui você integraria com a API real
+ setTimeout(() => {
+ const mockContent = `PARECER JURÍDICO
+
+TÍTULO: ${title || "Parecer Jurídico"}
+
+${instructions}
+
+ANÁLISE:
+
+Este parecer foi gerado com base nas instruções fornecidas. A análise considera os seguintes aspectos legais:
+
+1. Fundamentação Legal
+2. Precedentes Judiciais
+3. Doutrina Aplicável
+4. Conclusão e Recomendações
+
+CONCLUSÃO:
+
+Com base na análise realizada, conclui-se que...
+
+___________________________
+Parecer gerado em ${new Date().toLocaleDateString('pt-BR')}`;
+
+ setGeneratedContent(mockContent);
+ setIsGenerating(false);
+
+ if (isCreatingNew) {
+ const newOpinion: LegalOpinion = {
+ id: Date.now().toString(),
+ title: title || "Novo Parecer",
+ content: mockContent,
+ createdAt: new Date(),
+ category: category || undefined,
+ };
+ onOpinionCreated(newOpinion);
+ }
+
+ toast({
+ title: "Parecer gerado com sucesso!",
+ description: "O parecer está pronto para download.",
+ });
+ }, 2000);
+ };
+
+ const handleDownloadDocx = () => {
+ // Aqui você implementaria a geração real do DOCX
+ // Por enquanto, vamos criar um arquivo de texto
+ const content = generatedContent || selectedOpinion?.content || "";
+ const blob = new Blob([content], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `${title || selectedOpinion?.title || 'parecer'}.txt`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+
+ toast({
+ title: "Download iniciado",
+ description: "O parecer está sendo baixado.",
+ });
+ };
+
+ const displayContent = generatedContent || selectedOpinion?.content;
+
+ return (
+
+
+
+ {selectedOpinion ? selectedOpinion.title : "Novo Parecer Jurídico"}
+
+ {selectedOpinion && (
+
+ Criado em {new Date(selectedOpinion.createdAt).toLocaleDateString('pt-BR')}
+
+ )}
+
+
+
+
+ {(isCreatingNew || !selectedOpinion) && (
+ <>
+
+
+ setTitle(e.target.value)}
+ placeholder="Ex: Análise sobre Contrato de Prestação de Serviços"
+ />
+
+
+
+ setCategory(e.target.value)}
+ placeholder="Ex: Direito Civil, Trabalhista, etc."
+ />
+
+ >
+ )}
+
+
+
+
+
+
+
+ {displayContent && (
+
+
+
+
+
+
+
+ {displayContent}
+
+
+
+ )}
+
+
+
+ );
+};
diff --git a/src/components/agent/AgentSearch.tsx b/src/components/agent/AgentSearch.tsx
new file mode 100644
index 0000000..81043d4
--- /dev/null
+++ b/src/components/agent/AgentSearch.tsx
@@ -0,0 +1,129 @@
+import { useState } from "react";
+import { Search, FileText } from "lucide-react";
+import { Input } from "@/components/ui/input";
+import { Button } from "@/components/ui/button";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { LegalOpinion } from "./AgentView";
+
+interface AgentSearchProps {
+ onSelectOpinion: (opinion: LegalOpinion) => void;
+}
+
+// Simulação de base de pareceres
+const mockOpinionsDatabase: LegalOpinion[] = [
+ {
+ id: "base-1",
+ title: "Análise Contratual - Prestação de Serviços Continuados",
+ content: "Parecer completo sobre prestação de serviços...",
+ createdAt: new Date("2024-01-15"),
+ category: "Direito Civil",
+ },
+ {
+ id: "base-2",
+ title: "Rescisão de Contrato de Trabalho - Justa Causa",
+ content: "Análise jurídica sobre rescisão contratual...",
+ createdAt: new Date("2024-02-20"),
+ category: "Direito Trabalhista",
+ },
+ {
+ id: "base-3",
+ title: "Responsabilidade Civil - Acidente de Trânsito",
+ content: "Parecer sobre responsabilidade civil em acidentes...",
+ createdAt: new Date("2024-03-10"),
+ category: "Direito Civil",
+ },
+ {
+ id: "base-4",
+ title: "Dissolução de Sociedade - Procedimentos e Requisitos",
+ content: "Análise completa sobre dissolução societária...",
+ createdAt: new Date("2024-01-25"),
+ category: "Direito Empresarial",
+ },
+ {
+ id: "base-5",
+ title: "Direitos do Consumidor - Vícios em Produtos",
+ content: "Parecer sobre direitos do consumidor e garantias...",
+ createdAt: new Date("2024-02-05"),
+ category: "Direito do Consumidor",
+ },
+];
+
+export const AgentSearch = ({ onSelectOpinion }: AgentSearchProps) => {
+ const [searchTerm, setSearchTerm] = useState("");
+ const [searchResults, setSearchResults] = useState([]);
+
+ const handleSearch = () => {
+ if (!searchTerm.trim()) {
+ setSearchResults([]);
+ return;
+ }
+
+ const results = mockOpinionsDatabase.filter(
+ (opinion) =>
+ opinion.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ opinion.content.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ opinion.category?.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+
+ setSearchResults(results);
+ };
+
+ return (
+
+
+
Pesquisar Base de Pareceres
+
+ setSearchTerm(e.target.value)}
+ placeholder="Digite título, frase ou categoria..."
+ onKeyDown={(e) => e.key === "Enter" && handleSearch()}
+ />
+
+
+
+ Base com {mockOpinionsDatabase.length} pareceres disponíveis
+
+
+
+
+ {searchResults.length > 0 ? (
+
+ {searchResults.map((opinion) => (
+
onSelectOpinion(opinion)}
+ >
+
+
+
+
{opinion.title}
+
+ {opinion.category}
+ {new Date(opinion.createdAt).toLocaleDateString('pt-BR')}
+
+
+ {opinion.content}
+
+
+
+
+ ))}
+
+ ) : searchTerm ? (
+
+
Nenhum parecer encontrado para "{searchTerm}"
+
+ ) : (
+
+
Digite um termo para pesquisar na base de pareceres
+
+ )}
+
+
+ );
+};
diff --git a/src/components/agent/AgentSidebar.tsx b/src/components/agent/AgentSidebar.tsx
new file mode 100644
index 0000000..ac53a3e
--- /dev/null
+++ b/src/components/agent/AgentSidebar.tsx
@@ -0,0 +1,92 @@
+import { Plus, Search, Trash2, FileText } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { LegalOpinion } from "./AgentView";
+import { Input } from "@/components/ui/input";
+import { useState } from "react";
+
+interface AgentSidebarProps {
+ opinions: LegalOpinion[];
+ onCreateNew: () => void;
+ onSelectOpinion: (opinion: LegalOpinion) => void;
+ onDeleteOpinion: (id: string) => void;
+ onSearch: () => void;
+ selectedOpinionId?: string;
+}
+
+export const AgentSidebar = ({
+ opinions,
+ onCreateNew,
+ onSelectOpinion,
+ onDeleteOpinion,
+ onSearch,
+ selectedOpinionId,
+}: AgentSidebarProps) => {
+ const [searchTerm, setSearchTerm] = useState("");
+
+ const filteredOpinions = opinions.filter(op =>
+ op.title.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+
+ return (
+
+
+
Agente de Parecer
+
+
+
setSearchTerm(e.target.value)}
+ className="w-full"
+ />
+
+
+
+
+ {filteredOpinions.map((opinion) => (
+
onSelectOpinion(opinion)}
+ >
+
+
+
+
{opinion.title}
+
+ {new Date(opinion.createdAt).toLocaleDateString('pt-BR')}
+
+ {opinion.category && (
+
{opinion.category}
+ )}
+
+
+
+
+ ))}
+
+
+
+ );
+};
diff --git a/src/components/agent/AgentView.tsx b/src/components/agent/AgentView.tsx
new file mode 100644
index 0000000..e511698
--- /dev/null
+++ b/src/components/agent/AgentView.tsx
@@ -0,0 +1,74 @@
+import { useState } from "react";
+import { AgentSidebar } from "./AgentSidebar";
+import { AgentChat } from "./AgentChat";
+import { AgentSearch } from "./AgentSearch";
+
+export interface LegalOpinion {
+ id: string;
+ title: string;
+ content: string;
+ createdAt: Date;
+ category?: string;
+}
+
+export const AgentView = () => {
+ const [opinions, setOpinions] = useState([]);
+ const [selectedOpinion, setSelectedOpinion] = useState(null);
+ const [isCreatingNew, setIsCreatingNew] = useState(false);
+ const [showSearch, setShowSearch] = useState(false);
+
+ const handleCreateNew = () => {
+ setIsCreatingNew(true);
+ setSelectedOpinion(null);
+ setShowSearch(false);
+ };
+
+ const handleSelectOpinion = (opinion: LegalOpinion) => {
+ setSelectedOpinion(opinion);
+ setIsCreatingNew(false);
+ setShowSearch(false);
+ };
+
+ const handleSearch = () => {
+ setShowSearch(true);
+ setIsCreatingNew(false);
+ setSelectedOpinion(null);
+ };
+
+ const handleOpinionCreated = (newOpinion: LegalOpinion) => {
+ setOpinions([newOpinion, ...opinions]);
+ setSelectedOpinion(newOpinion);
+ setIsCreatingNew(false);
+ };
+
+ const handleDeleteOpinion = (id: string) => {
+ setOpinions(opinions.filter(op => op.id !== id));
+ if (selectedOpinion?.id === id) {
+ setSelectedOpinion(null);
+ }
+ };
+
+ return (
+
+
+
+ {showSearch ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx
index 09126cd..4b8fd37 100644
--- a/src/pages/Index.tsx
+++ b/src/pages/Index.tsx
@@ -5,6 +5,7 @@ import { ImageView } from "@/components/images/ImageView";
import { AudioView } from "@/components/audio/AudioView";
import { BotView } from "@/components/bots/BotView";
import { BotChat } from "@/components/bots/BotChat";
+import { AgentView } from "@/components/agent/AgentView";
interface Bot {
id: string;
@@ -14,7 +15,7 @@ interface Bot {
}
const Index = () => {
- const [activeView, setActiveView] = useState<"chat" | "images" | "audio" | "bots">("chat");
+ const [activeView, setActiveView] = useState<"chat" | "images" | "audio" | "bots" | "agent">("chat");
const [activeBotChat, setActiveBotChat] = useState(null);
const handleStartBotChat = (bot: Bot) => {
@@ -37,6 +38,7 @@ const Index = () => {
)
)}
+ {activeView === "agent" && }
);
};