[new/parecer juridico]

This commit is contained in:
luisfepsale
2025-11-19 15:19:41 -03:00
parent 0fa382ef0e
commit a28b780718
3 changed files with 152 additions and 34 deletions
+77 -12
View File
@@ -1,7 +1,8 @@
import { useState, useEffect } from "react";
import { Plus, Search, Eye, Trash2, ArrowUpDown, Download } from "lucide-react";
import { useState, useEffect, useCallback } from "react";
import { Plus, Search, Eye, Trash2, ArrowUpDown, Download, Loader2, CheckCircle2, XCircle } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
@@ -43,6 +44,7 @@ type SortOrder = "asc" | "desc";
export const AgentView = () => {
const [opinions, setOpinions] = useState<OpinionRecord[]>([]);
const [pendingOpinions, setPendingOpinions] = useState<OpinionRecord[]>([]); // Registros temporários sendo processados
const [searchTerm, setSearchTerm] = useState("");
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(10);
@@ -57,7 +59,7 @@ export const AgentView = () => {
const { toast } = useToast();
// Carrega pareceres da API
const loadOpinions = async () => {
const loadOpinions = useCallback(async () => {
setIsLoading(true);
try {
const data = await agentService.getOpinions({
@@ -65,7 +67,24 @@ export const AgentView = () => {
per_page: itemsPerPage,
search: searchTerm,
});
setOpinions(data);
// Adiciona status aos pareceres da API
const opinionsWithStatus = data.map(opinion => ({
...opinion,
status: (opinion.file_url || opinion.file_url_melhoria) ? 'concluido' : 'processando' as const,
}));
setOpinions(opinionsWithStatus);
// Remove registros temporários que agora estão na API
setPendingOpinions(prev =>
prev.filter(pending =>
!opinionsWithStatus.some(opinion =>
opinion.titulo === pending.titulo &&
opinion.created_at.substring(0, 10) === pending.created_at.substring(0, 10)
)
)
);
} catch (error: any) {
console.error('Erro ao carregar pareceres:', error);
toast({
@@ -76,12 +95,23 @@ export const AgentView = () => {
} finally {
setIsLoading(false);
}
};
}, [currentPage, itemsPerPage, searchTerm, toast]);
// Carrega pareceres ao montar o componente e quando mudar página/busca
// Polling automático para atualizar status dos pareceres
useEffect(() => {
// Carrega imediatamente
loadOpinions();
}, [currentPage, itemsPerPage]);
// Configura polling a cada 10 segundos se houver pareceres pendentes
const interval = setInterval(() => {
if (pendingOpinions.length > 0) {
console.log('Polling: Atualizando lista de pareceres...');
loadOpinions();
}
}, 10000); // 10 segundos
return () => clearInterval(interval);
}, [currentPage, itemsPerPage, pendingOpinions.length, loadOpinions]);
// Debounce para busca
useEffect(() => {
@@ -94,7 +124,7 @@ export const AgentView = () => {
}, 500);
return () => clearTimeout(timer);
}, [searchTerm]);
}, [searchTerm, loadOpinions]);
const handleSort = (field: SortField) => {
if (sortField === field) {
@@ -105,7 +135,10 @@ export const AgentView = () => {
}
};
const sortedOpinions = [...opinions].sort((a, b) => {
// Mescla pareceres da API com registros temporários
const allOpinions = [...pendingOpinions, ...opinions];
const sortedOpinions = [...allOpinions].sort((a, b) => {
const multiplier = sortOrder === "asc" ? 1 : -1;
if (sortField === "created_at") {
@@ -119,8 +152,13 @@ export const AgentView = () => {
const totalPages = Math.ceil(sortedOpinions.length / itemsPerPage);
// Callback quando um parecer está sendo criado (registro temporário)
const handleOpinionCreating = (tempOpinion: OpinionRecord) => {
setPendingOpinions(prev => [tempOpinion, ...prev]);
};
// Callback quando um parecer foi criado (recarrega da API)
const handleOpinionCreated = () => {
// Recarrega a lista após criar um novo parecer
loadOpinions();
};
@@ -284,6 +322,9 @@ export const AgentView = () => {
<ArrowUpDown className="w-4 h-4" />
</Button>
</TableHead>
<TableHead className="hidden lg:table-cell text-center">
<span className="font-semibold text-xs md:text-sm">Status</span>
</TableHead>
<TableHead className="hidden sm:table-cell">
<Button
variant="ghost"
@@ -300,13 +341,13 @@ export const AgentView = () => {
<TableBody>
{isLoading ? (
<TableRow>
<TableCell colSpan={4} className="text-center py-12 text-muted-foreground">
<TableCell colSpan={5} className="text-center py-12 text-muted-foreground">
Carregando pareceres...
</TableCell>
</TableRow>
) : sortedOpinions.length === 0 ? (
<TableRow>
<TableCell colSpan={4} className="text-center py-12 text-muted-foreground">
<TableCell colSpan={5} className="text-center py-12 text-muted-foreground">
{searchTerm
? "Nenhum parecer encontrado"
: "Nenhum parecer criado ainda. Clique em 'Novo Parecer' para começar."}
@@ -317,6 +358,26 @@ export const AgentView = () => {
<TableRow key={opinion.id}>
<TableCell className="font-medium text-xs md:text-sm">{opinion.titulo}</TableCell>
<TableCell className="hidden md:table-cell text-sm">{opinion.categoria || "-"}</TableCell>
<TableCell className="hidden lg:table-cell text-center">
{opinion.status === 'processando' && (
<Badge variant="secondary" className="gap-1 bg-yellow-100 text-yellow-800 hover:bg-yellow-100">
<Loader2 className="w-3 h-3 animate-spin" />
Gerando...
</Badge>
)}
{opinion.status === 'concluido' && (
<Badge variant="secondary" className="gap-1 bg-green-100 text-green-800 hover:bg-green-100">
<CheckCircle2 className="w-3 h-3" />
Concluído
</Badge>
)}
{opinion.status === 'erro' && (
<Badge variant="destructive" className="gap-1">
<XCircle className="w-3 h-3" />
Erro
</Badge>
)}
</TableCell>
<TableCell className="hidden sm:table-cell text-xs md:text-sm">
{new Date(opinion.created_at).toLocaleDateString("pt-BR")}
</TableCell>
@@ -328,6 +389,7 @@ export const AgentView = () => {
onClick={() => handleViewOpinion(opinion)}
title="Visualizar"
className="h-7 w-7 md:h-9 md:w-9"
disabled={opinion.isLocalPending || opinion.status === 'processando'}
>
<Eye className="w-3 h-3 md:w-4 md:h-4" />
</Button>
@@ -338,6 +400,7 @@ export const AgentView = () => {
size="icon"
title="Baixar"
className="h-7 w-7 md:h-9 md:w-9"
disabled={opinion.isLocalPending || opinion.status === 'processando'}
>
<Download className="w-3 h-3 md:w-4 md:h-4" />
</Button>
@@ -365,6 +428,7 @@ export const AgentView = () => {
onClick={() => setOpinionToDelete(opinion)}
title="Excluir"
className="h-7 w-7 md:h-9 md:w-9 text-destructive hover:text-destructive"
disabled={opinion.isLocalPending}
>
<Trash2 className="w-3 h-3 md:w-4 md:h-4" />
</Button>
@@ -438,6 +502,7 @@ export const AgentView = () => {
onOpenChange={setIsDialogOpen}
selectedOpinion={selectedOpinion}
onOpinionCreated={handleOpinionCreated}
onOpinionCreating={handleOpinionCreating}
/>
<AlertDialog open={!!opinionToDelete} onOpenChange={(open) => !open && setOpinionToDelete(null)}>