import { useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { ArrowLeft, Download, ExternalLink, FileSpreadsheet, FolderKanban, Loader2, RefreshCcw, } from "lucide-react"; import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { useAuthAccess } from "@/contexts/AuthAccessContext"; import { fechamentoCompetenciasService, type FechamentoDaCompetenciaItem } from "@/services/fechamento/competencias"; import { fechamentoFechamentosService } from "@/services/fechamento/fechamentos"; function getDisplayNome(row: FechamentoDaCompetenciaItem): string { if (row.parceiroCodinome?.trim()) { return `${row.parceiroNome} (${row.parceiroCodinome.trim()})`; } return row.parceiroNome; } function formatHoras(minutos: number | null): string { if (!minutos || minutos <= 0) return "0h"; const horas = Math.floor(minutos / 60); const mins = Math.round(minutos % 60); if (horas === 0) return `${mins}min`; if (mins === 0) return `${horas}h`; return `${horas}h ${mins}min`; } function AsanaImportLoadingCard() { return (

Sincronizando com o Asana

Buscando tarefas concluídas no período da competência e montando os fechamentos. Pode levar um minuto — não feche esta página.

); } function reprocessamentoLabel(modo: "reprocessar_tudo" | "reprocessar_alguns" | "buscar_novos_fechamentos"): string { if (modo === "reprocessar_tudo") return "Reimportando toda a competência a partir do Asana."; if (modo === "reprocessar_alguns") return "Atualizando os parceiros selecionados no Asana."; return "Buscando novos fechamentos no Asana."; } export default function CompetenciaFechamentos() { const { me } = useAuthAccess(); const { id: competenciaId = "" } = useParams(); const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [fechamentos, setFechamentos] = useState([]); /** Onde a operação longa do Asana foi disparada (para mensagem e layout de loading). */ const [importKind, setImportKind] = useState<"sheet" | "modal" | null>(null); const importing = importKind !== null; const [exportingFechamentoId, setExportingFechamentoId] = useState(null); const [isReprocessModalOpen, setIsReprocessModalOpen] = useState(false); const [isConcluirModalOpen, setIsConcluirModalOpen] = useState(false); const [isReabrirModalOpen, setIsReabrirModalOpen] = useState(false); const [concluindoCompetencia, setConcluindoCompetencia] = useState(false); const [reabrindoCompetencia, setReabrindoCompetencia] = useState(false); const [motivoReabertura, setMotivoReabertura] = useState(""); const [competenciaStatus, setCompetenciaStatus] = useState<"em_aberto" | "concluido">("em_aberto"); const [reprocessMode, setReprocessMode] = useState< "reprocessar_tudo" | "reprocessar_alguns" | "buscar_novos_fechamentos" >("reprocessar_tudo"); const [selectedParceiroIds, setSelectedParceiroIds] = useState([]); const loadFechamentos = async () => { try { setLoading(true); const [data, competencias] = await Promise.all([ fechamentoCompetenciasService.listarFechamentosDaCompetencia(competenciaId), fechamentoCompetenciasService.listarCompetencias({}), ]); const competenciaAtual = competencias.find((item) => item.id === competenciaId); setFechamentos(data); setCompetenciaStatus(competenciaAtual?.status ?? "em_aberto"); } catch (error) { const message = error instanceof Error ? error.message : "Erro ao carregar fechamentos da competência."; toast.error(message); setFechamentos([]); } finally { setLoading(false); } }; const handleImportarAsana = async () => { setImportKind("sheet"); try { const resultado = await fechamentoCompetenciasService.importarDoAsana(competenciaId); toast.success( `Importação concluída: ${resultado.tarefasImportadas} tasks, ${resultado.fechamentosCriados} fechamentos criados.`, ); await loadFechamentos(); } catch (error) { const message = error instanceof Error ? error.message : "Erro ao importar tasks do Asana."; toast.error(message); } finally { setImportKind(null); } }; const handleExecutarReprocessamento = async () => { if (reprocessMode === "reprocessar_alguns" && selectedParceiroIds.length === 0) { toast.error("Selecione ao menos um fechamento/parceiro para reprocessar."); return; } setImportKind("modal"); try { const resultado = await fechamentoCompetenciasService.importarDoAsana(competenciaId, { modo: reprocessMode, parceiroIds: reprocessMode === "reprocessar_alguns" ? selectedParceiroIds : undefined, }); if (reprocessMode === "buscar_novos_fechamentos") { toast.success( `Busca concluída: ${resultado.fechamentosCriados} novos fechamento(s) criado(s), sem alterar os atuais.`, ); } else { toast.success( `Reprocessamento concluído: ${resultado.tarefasImportadas} tasks processadas e ${resultado.fechamentosCriados} fechamento(s) criado(s).`, ); } setIsReprocessModalOpen(false); await loadFechamentos(); } catch (error) { const message = error instanceof Error ? error.message : "Erro ao executar reprocessamento do Asana."; toast.error(message); } finally { setImportKind(null); } }; const handleConcluirCompetencia = async () => { if (!me?.id) { toast.error("Não foi possível identificar o usuário para concluir a competência."); return; } try { setConcluindoCompetencia(true); await fechamentoCompetenciasService.concluirCompetencia(competenciaId, me.id); toast.success("Competência concluída com sucesso."); setIsConcluirModalOpen(false); await loadFechamentos(); } catch (error) { const message = error instanceof Error ? error.message : "Erro ao concluir competência."; toast.error(message); } finally { setConcluindoCompetencia(false); } }; const handleReabrirCompetencia = async () => { if (!me?.id) { toast.error("Não foi possível identificar o usuário para reabrir a competência."); return; } try { setReabrindoCompetencia(true); await fechamentoCompetenciasService.reabrirCompetencia(competenciaId, me.id, motivoReabertura.trim() || undefined); toast.success("Competência reaberta com sucesso."); setIsReabrirModalOpen(false); setMotivoReabertura(""); await loadFechamentos(); } catch (error) { const message = error instanceof Error ? error.message : "Erro ao reabrir competência."; toast.error(message); } finally { setReabrindoCompetencia(false); } }; const handleExportar = async (fechamentoId: string) => { try { setExportingFechamentoId(fechamentoId); const { buffer, filename } = await fechamentoFechamentosService.exportarPlanilha(fechamentoId); const blob = new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename ?? `fechamento-${fechamentoId}.xlsx`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast.success("Planilha exportada com sucesso."); } catch (error) { const message = error instanceof Error ? error.message : "Erro ao exportar planilha."; toast.error(message); } finally { setExportingFechamentoId(null); } }; useEffect(() => { let cancelled = false; if (competenciaId) { void loadFechamentos(); } return () => { cancelled = true; }; }, [competenciaId]); useEffect(() => { setSelectedParceiroIds(fechamentos.map((f) => f.parceiroId)); }, [fechamentos]); const temFechamentos = fechamentos.length > 0; return (

Fechamentos da Competência

{!loading && (

{fechamentos.length} fechamento(s)

{competenciaStatus === "concluido" ? ( ) : temFechamentos ? ( <> ) : null}
)}
{!loading && fechamentos.length === 0 ? ( Nenhum fechamento encontrado Importe as tasks do Asana para criar os fechamentos automaticamente.
{importKind === "sheet" ? ( ) : ( )}
) : (
Logo Nome Pontuação Total Horas Total Status Ações {loading ? (

Carregando fechamentos

Aguarde um instante.

) : ( fechamentos.map((row) => ( {row.parceiroLogoUrl ? ( {`Logo ) : ( "—" )} {getDisplayNome(row)} {row.pontuacaoTotalEntregue} {formatHoras(row.horasTotal * 60)} {row.status === "fechado" ? "Fechado" : "Em aberto"}
)) )}
)}
{ if (!open && importing) return; setIsReprocessModalOpen(open); }} > { if (importing) e.preventDefault(); }} onEscapeKeyDown={(e) => { if (importing) e.preventDefault(); }} > {importKind === "modal" ? (

Processando no Asana

{reprocessamentoLabel(reprocessMode)}

Não feche esta janela até a operação terminar.

) : null}
Reprocessar Asana Escolha uma estratégia de reprocessamento para esta competência.
{reprocessMode === "reprocessar_alguns" ? (

Selecione os fechamentos/parceiros

{fechamentos.map((f) => { const checked = selectedParceiroIds.includes(f.parceiroId); return ( ); })}
) : null}
Reabrir competência Ao reabrir, o reprocessamento do Asana e as edições dos fechamentos voltam a ficar disponíveis.
setMotivoReabertura(e.target.value)} placeholder="Ex.: correção de ajustes pós-fechamento" />
Concluir competência Deseja concluir esta competência? A operação só será permitida se todos os fechamentos estiverem fechados.
); }