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 ? (
) : (
"—"
)}
{getDisplayNome(row)}
{row.pontuacaoTotalEntregue}
{formatHoras(row.horasTotal * 60)}
{row.status === "fechado" ? "Fechado" : "Em aberto"}
))
)}
)}
);
}