diff --git a/src/App.tsx b/src/App.tsx
index f506e32..8a2211b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -11,13 +11,18 @@ import React from "react";
import IntelligenceIAApp from "./modules/intelligence-ia/App";
import FechamentoHgtxApp from "./modules/fechamento-hgtx/App";
+const INTELLIGENCE_SCORE_PREFIX = "/intelligence-score";
const FECHAMENTO_LEGACY_PREFIX = "/fechamento-hgtx";
+const FECHAMENTO_PREFIX = "/fechamento";
-/** Redireciona URLs antigas `/fechamento-hgtx/...` para `/fechamento/...` (mesmo sufixo, query e hash). */
+/** Redireciona URLs antigas para `/intelligence-score/...` (mesmo sufixo, query e hash). */
function FechamentoLegacyRedirect() {
const location = useLocation();
- const tail = location.pathname.slice(FECHAMENTO_LEGACY_PREFIX.length);
- const to = `/fechamento${tail}`;
+ const fromPrefix = location.pathname.startsWith(FECHAMENTO_LEGACY_PREFIX)
+ ? FECHAMENTO_LEGACY_PREFIX
+ : FECHAMENTO_PREFIX;
+ const tail = location.pathname.slice(fromPrefix.length);
+ const to = `${INTELLIGENCE_SCORE_PREFIX}${tail}`;
return ;
}
@@ -44,7 +49,8 @@ const App = () => (
/>
} />
- } />
+ } />
+ } />
} />
} />
diff --git a/src/components/fechamento/UnidadeGate.tsx b/src/components/fechamento/UnidadeGate.tsx
index 3f31f73..7098500 100644
--- a/src/components/fechamento/UnidadeGate.tsx
+++ b/src/components/fechamento/UnidadeGate.tsx
@@ -105,7 +105,7 @@ export function UnidadeGate({ children }: UnidadeGateProps) {
O código do estabelecimento não foi enviado pelo ambiente (TransferArea). Abra o módulo
- Fechamento HGTX a partir do Codex com o estabelecimento carregado no transfer.
+ HGTX Intelligence Score a partir do Codex com o estabelecimento carregado no transfer.
@@ -134,7 +134,7 @@ export function UnidadeGate({ children }: UnidadeGateProps) {
Cadastre o nome da unidade em Configurações (integração com o código atual do transfer).
- Ir para Configurações
+ Ir para Configurações
>
) : (
diff --git a/src/modules/fechamento-hgtx/App.tsx b/src/modules/fechamento-hgtx/App.tsx
index e725629..3614a79 100644
--- a/src/modules/fechamento-hgtx/App.tsx
+++ b/src/modules/fechamento-hgtx/App.tsx
@@ -23,7 +23,7 @@ function RequireAdminRoute({ children }: { children: JSX.Element }) {
return children;
}
- return ;
+ return ;
}
function RequireFechamentoDetalheRoute({ children }: { children: JSX.Element }) {
@@ -39,7 +39,7 @@ function RequireFechamentoDetalheRoute({ children }: { children: JSX.Element })
return children;
}
- return ;
+ return ;
}
const FechamentoHgtxApp = () => {
diff --git a/src/modules/fechamento-hgtx/components/layout/AppSidebar.tsx b/src/modules/fechamento-hgtx/components/layout/AppSidebar.tsx
index bc98cb2..596c6e8 100644
--- a/src/modules/fechamento-hgtx/components/layout/AppSidebar.tsx
+++ b/src/modules/fechamento-hgtx/components/layout/AppSidebar.tsx
@@ -97,7 +97,7 @@ export function AppSidebar() {
-
Fechamento HGTX
+
HGTX Intelligence Score
{isAdmin ? "Painel Admin" : "Painel Parceiro"}
diff --git a/src/modules/fechamento-hgtx/pages/BancoPontos.tsx b/src/modules/fechamento-hgtx/pages/BancoPontos.tsx
index 27b7ea1..e4ce6c7 100644
--- a/src/modules/fechamento-hgtx/pages/BancoPontos.tsx
+++ b/src/modules/fechamento-hgtx/pages/BancoPontos.tsx
@@ -213,7 +213,7 @@ export default function BancoPontos() {
variant="outline"
className="border-border bg-background text-foreground hover:bg-muted/50 hover:text-foreground"
onClick={() =>
- navigate(`/fechamento/banco-pontos/${row.parceiroId}`, {
+ navigate(`/intelligence-score/banco-pontos/${row.parceiroId}`, {
state: { nomeExibicao: getDisplayNome(row) },
})
}
diff --git a/src/modules/fechamento-hgtx/pages/BancoPontosExtrato.tsx b/src/modules/fechamento-hgtx/pages/BancoPontosExtrato.tsx
index 7a7fd5b..3adb508 100644
--- a/src/modules/fechamento-hgtx/pages/BancoPontosExtrato.tsx
+++ b/src/modules/fechamento-hgtx/pages/BancoPontosExtrato.tsx
@@ -154,7 +154,7 @@ export default function BancoPontosExtrato() {
-
navigate("/fechamento/banco-pontos")}>
+ navigate("/intelligence-score/banco-pontos")}>
Voltar ao banco de pontos
@@ -275,7 +275,7 @@ export default function BancoPontosExtrato() {
variant="ghost"
className="gap-1 text-foreground hover:bg-zinc-200 hover:text-foreground dark:hover:bg-zinc-700"
onClick={() =>
- navigate(`/fechamento/fechamentos/${linha.fechamentoId}`, {
+ navigate(`/intelligence-score/fechamentos/${linha.fechamentoId}`, {
state: { competenciaId: undefined, status: undefined },
})
}
diff --git a/src/modules/fechamento-hgtx/pages/CompetenciaFechamentos.tsx b/src/modules/fechamento-hgtx/pages/CompetenciaFechamentos.tsx
index 88e8638..06bde7e 100644
--- a/src/modules/fechamento-hgtx/pages/CompetenciaFechamentos.tsx
+++ b/src/modules/fechamento-hgtx/pages/CompetenciaFechamentos.tsx
@@ -36,15 +36,6 @@ function getDisplayNome(row: FechamentoDaCompetenciaItem): string {
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 (
-
navigate("/fechamento")}>
+ navigate("/intelligence-score")}>
Voltar
@@ -334,7 +325,6 @@ export default function CompetenciaFechamentos() {
Logo
Nome
Pontuação Total
- Horas Total
Status
Ações
@@ -342,7 +332,7 @@ export default function CompetenciaFechamentos() {
{loading ? (
-
+
{getDisplayNome(row)}
{row.pontuacaoTotalEntregue}
-
{formatHoras(row.horasTotal * 60)}
- navigate(`/fechamento/fechamentos/${row.id}`, {
+ navigate(`/intelligence-score/fechamentos/${row.id}`, {
state: { competenciaId, status: row.status },
})
}
diff --git a/src/modules/fechamento-hgtx/pages/Configuracoes.tsx b/src/modules/fechamento-hgtx/pages/Configuracoes.tsx
index af9783e..2d0acc5 100644
--- a/src/modules/fechamento-hgtx/pages/Configuracoes.tsx
+++ b/src/modules/fechamento-hgtx/pages/Configuracoes.tsx
@@ -211,7 +211,7 @@ export default function Configuracoes() {
await fechamentoUnidadesService.createUnidade({ nome, estabelecimentoId: codigo });
toast.success("Unidade cadastrada.");
clearCommanderUnidadeIdCache();
- navigate("/fechamento", { replace: true });
+ navigate("/intelligence-score", { replace: true });
}
} catch (error) {
const message = error instanceof Error ? error.message : "Erro ao salvar unidade.";
diff --git a/src/modules/fechamento-hgtx/pages/FechamentoDetalhes.tsx b/src/modules/fechamento-hgtx/pages/FechamentoDetalhes.tsx
index 4cc294f..ad71d29 100644
--- a/src/modules/fechamento-hgtx/pages/FechamentoDetalhes.tsx
+++ b/src/modules/fechamento-hgtx/pages/FechamentoDetalhes.tsx
@@ -1,9 +1,8 @@
-import { useEffect, useMemo, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import {
ArrowLeft,
CheckCircle2,
- Clock3,
ExternalLink,
ListChecks,
Loader2,
@@ -41,23 +40,6 @@ import { fechamentoBancoPontosService } from "@/services/fechamento/bancoPontos"
import { fechamentoCompetenciasService, type CompetenciaItem } from "@/services/fechamento/competencias";
import { fechamentoFechamentosService, type FechamentoTarefaItem } from "@/services/fechamento/fechamentos";
-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 formatHorasResumo(minutos: number): string {
- 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 formatPontos(valor: number): string {
return valor.toLocaleString("pt-BR", { minimumFractionDigits: 0, maximumFractionDigits: 4 });
}
@@ -146,10 +128,33 @@ function formatDateTime(value: string | null): string {
});
}
+function normalizeTextFilter(value: string): string {
+ return value.trim().toLocaleLowerCase("pt-BR");
+}
+
+function toFilterDate(value: string | null): string {
+ if (!value) return "";
+ const isoMatch = value.match(/^\d{4}-\d{2}-\d{2}/);
+ if (isoMatch) return isoMatch[0];
+
+ const parsed = new Date(value);
+ if (Number.isNaN(parsed.getTime())) return "";
+ const year = parsed.getFullYear();
+ const month = String(parsed.getMonth() + 1).padStart(2, "0");
+ const day = String(parsed.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+}
+
function formatCompetenciaMesAno(item: Pick): string {
return `${String(item.mes).padStart(2, "0")}/${item.ano}`;
}
+function getTipoLancamentoLabel(tipo: string): string {
+ if (tipo === "bonus") return "Bônus Comercial";
+ if (tipo === "desconto") return "Desconto Comercial";
+ return tipo;
+}
+
type FechamentoDetalhesLocationState = {
competenciaId?: string;
status?: "em_aberto" | "fechado";
@@ -206,6 +211,15 @@ export default function FechamentoDetalhes() {
const [edicaoCliente, setEdicaoCliente] = useState("");
const [edicaoTempoMinutos, setEdicaoTempoMinutos] = useState("");
const [edicaoPontuacao, setEdicaoPontuacao] = useState("");
+ const tableScrollRef = useRef(null);
+ const stickyScrollRef = useRef(null);
+ const stickyScrollSpacerRef = useRef(null);
+ const [showStickyScrollbar, setShowStickyScrollbar] = useState(false);
+ const [showFilters, setShowFilters] = useState(false);
+ const [filtroDescricao, setFiltroDescricao] = useState("");
+ const [filtroTicket, setFiltroTicket] = useState("");
+ const [filtroCliente, setFiltroCliente] = useState("");
+ const [filtroDataConclusao, setFiltroDataConclusao] = useState("");
const [competenciaStatus, setCompetenciaStatus] = useState<"em_aberto" | "concluido">("em_aberto");
const isFechado = fechamentoStatus === "fechado";
const isCompetenciaConcluida = competenciaStatus === "concluido";
@@ -215,10 +229,8 @@ export default function FechamentoDetalhes() {
const totais = useMemo(() => {
const aprovadas = tarefas.filter((t) => t.estaRevisada);
const pontos = aprovadas.reduce((acc, t) => acc + Number(t.pontuacao || 0), 0);
- const minutos = aprovadas.reduce((acc, t) => acc + Number(t.tempoMinutos || 0), 0);
return {
pontos,
- horas: formatHorasResumo(minutos),
aprovadas: aprovadas.length,
};
}, [tarefas]);
@@ -244,6 +256,31 @@ export default function FechamentoDetalhes() {
() => competenciasAbertas.filter((item) => item.id !== competenciaId),
[competenciasAbertas, competenciaId],
);
+ const hasActiveFilters = Boolean(
+ filtroDescricao ||
+ filtroTicket ||
+ filtroCliente ||
+ filtroDataConclusao,
+ );
+ const tarefasFiltradas = useMemo(() => {
+ const descricaoFilter = normalizeTextFilter(filtroDescricao);
+ const ticketFilter = normalizeTextFilter(filtroTicket);
+ const clienteFilter = normalizeTextFilter(filtroCliente);
+
+ return tarefas.filter((tarefa) => {
+ if (descricaoFilter && !normalizeTextFilter(tarefa.descricao ?? "").includes(descricaoFilter)) return false;
+ if (ticketFilter && !normalizeTextFilter(tarefa.numeroTicket ?? "").includes(ticketFilter)) return false;
+ if (clienteFilter && !normalizeTextFilter(tarefa.cliente ?? "").includes(clienteFilter)) return false;
+ if (filtroDataConclusao && toFilterDate(tarefa.dataConclusao) !== filtroDataConclusao) return false;
+ return true;
+ });
+ }, [
+ tarefas,
+ filtroDescricao,
+ filtroTicket,
+ filtroCliente,
+ filtroDataConclusao,
+ ]);
const loadTarefas = async () => {
try {
@@ -344,6 +381,51 @@ export default function FechamentoDetalhes() {
}
}, [isConcluirOpen, parceiroId]);
+ useEffect(() => {
+ const main = tableScrollRef.current;
+ const sticky = stickyScrollRef.current;
+ if (!main || !sticky) return;
+
+ let syncingFromMain = false;
+ let syncingFromSticky = false;
+
+ const syncMetricsAndVisibility = () => {
+ const spacer = stickyScrollSpacerRef.current;
+ const canScrollX = main.scrollWidth > main.clientWidth + 1;
+ if (spacer) spacer.style.width = `${main.scrollWidth}px`;
+ if (sticky.scrollLeft !== main.scrollLeft) sticky.scrollLeft = main.scrollLeft;
+ setShowStickyScrollbar(canScrollX);
+ };
+
+ const onMainScroll = () => {
+ if (syncingFromSticky) return;
+ syncingFromMain = true;
+ sticky.scrollLeft = main.scrollLeft;
+ syncingFromMain = false;
+ };
+
+ const onStickyScroll = () => {
+ if (syncingFromMain) return;
+ syncingFromSticky = true;
+ main.scrollLeft = sticky.scrollLeft;
+ syncingFromSticky = false;
+ };
+
+ syncMetricsAndVisibility();
+ main.addEventListener("scroll", onMainScroll, { passive: true });
+ sticky.addEventListener("scroll", onStickyScroll, { passive: true });
+ window.addEventListener("resize", syncMetricsAndVisibility);
+ const resizeObserver = new ResizeObserver(() => syncMetricsAndVisibility());
+ resizeObserver.observe(main);
+
+ return () => {
+ main.removeEventListener("scroll", onMainScroll);
+ sticky.removeEventListener("scroll", onStickyScroll);
+ window.removeEventListener("resize", syncMetricsAndVisibility);
+ resizeObserver.disconnect();
+ };
+ }, [tarefas.length, loading]);
+
const handleToggleAprovada = async (tarefa: FechamentoTarefaItem, approved: boolean) => {
if (isCompetenciaConcluida) {
toastBloqueioCompetenciaConcluida();
@@ -365,6 +447,13 @@ export default function FechamentoDetalhes() {
);
};
+ const handleLimparFiltros = () => {
+ setFiltroDescricao("");
+ setFiltroTicket("");
+ setFiltroCliente("");
+ setFiltroDataConclusao("");
+ };
+
const handleToggleTodasAprovadas = async () => {
if (isCompetenciaConcluida) {
toastBloqueioCompetenciaConcluida();
@@ -501,7 +590,7 @@ export default function FechamentoDetalhes() {
});
toast.success(`Fechamento concluído. Banco de pontos: ${formatPontosAte2Casas(data.pontuacaoBanco)}.`);
setFechamentoStatus("fechado");
- navigate(`/fechamento/competencias/${data.competenciaId}`);
+ navigate(`/intelligence-score/competencias/${data.competenciaId}`);
} catch (error) {
const message = error instanceof Error ? error.message : "Erro ao concluir fechamento.";
toast.error(message);
@@ -824,7 +913,7 @@ export default function FechamentoDetalhes() {
{!loading ? (
-
+
{isCompetenciaConcluida ? (
Competência concluída. Reabra a competência para editar este fechamento.
@@ -857,15 +946,6 @@ export default function FechamentoDetalhes() {
{formatPontos(totais.pontos)}
-
-
-
- Horas aprovadas
-
-
- {totais.horas}
-
-
) : null}
@@ -881,24 +961,78 @@ export default function FechamentoDetalhes() {
) : (
{!loading ? (
-
-
void handleToggleTodasAprovadas()}
- disabled={isReadonly || bulkUpdatingRevisao}
- >
- {bulkUpdatingRevisao ? (
-
- ) : (
-
- )}
- {todasTarefasRevisadas ? "Desmarcar todas" : "Marcar todas"}
-
+
+
+ setShowFilters((prev) => !prev)}>
+ {showFilters ? "Ocultar filtros" : "Exibir filtros"}
+
+
+ Limpar filtros
+
+ void handleToggleTodasAprovadas()}
+ disabled={isReadonly || bulkUpdatingRevisao}
+ >
+ {bulkUpdatingRevisao ? (
+
+ ) : (
+
+ )}
+ {todasTarefasRevisadas ? "Desmarcar todas" : "Marcar todas"}
+
+
+ {showFilters ? (
+
+ ) : null}
) : null}
-
+
@@ -911,7 +1045,6 @@ export default function FechamentoDetalhes() {
Data vencimento
Data conclusão
Etiquetas
- Horas
Pontuação
Ações
@@ -919,12 +1052,18 @@ export default function FechamentoDetalhes() {
{loading ? (
-
+
Carregando tarefas...
+ ) : tarefasFiltradas.length === 0 ? (
+
+
+ Nenhuma tarefa encontrada com os filtros aplicados.
+
+
) : (
- tarefas.map((tarefa) => (
+ tarefasFiltradas.map((tarefa) => (
@@ -938,7 +1077,7 @@ export default function FechamentoDetalhes() {
{tarefa.numeroTicket || "—"}
{tarefa.descricao}
{tarefa.cliente || "—"}
-
{tarefa.tipo}
+
{getTipoLancamentoLabel(tarefa.tipo)}
{formatDateOnly(tarefa.dataInicio)}
{formatDateOnly(tarefa.dataVencimento)}
{formatDateTime(tarefa.dataConclusao)}
@@ -955,7 +1094,6 @@ export default function FechamentoDetalhes() {
"—"
)}
-
{formatHoras(tarefa.tempoMinutos)}
{Number(tarefa.pontuacao || 0)}
@@ -1014,6 +1152,13 @@ export default function FechamentoDetalhes() {
+ {showStickyScrollbar ? (
+
+ ) : null}
)}
@@ -1038,8 +1183,8 @@ export default function FechamentoDetalhes() {
- Bonificação
- Desconto
+ {getTipoLancamentoLabel("bonus")}
+ {getTipoLancamentoLabel("desconto")}
diff --git a/src/modules/fechamento-hgtx/pages/Fechamentos.tsx b/src/modules/fechamento-hgtx/pages/Fechamentos.tsx
index ad1ed4b..3e60a31 100644
--- a/src/modules/fechamento-hgtx/pages/Fechamentos.tsx
+++ b/src/modules/fechamento-hgtx/pages/Fechamentos.tsx
@@ -258,7 +258,7 @@ export default function Fechamentos() {
navigate(`/fechamento/competencias/${competencia.id}`)}
+ onClick={() => navigate(`/intelligence-score/competencias/${competencia.id}`)}
>
Acessar
diff --git a/src/modules/fechamento-hgtx/pages/MeuFechamento.tsx b/src/modules/fechamento-hgtx/pages/MeuFechamento.tsx
index dd0dc61..fe27c44 100644
--- a/src/modules/fechamento-hgtx/pages/MeuFechamento.tsx
+++ b/src/modules/fechamento-hgtx/pages/MeuFechamento.tsx
@@ -159,7 +159,7 @@ export default function MeuFechamento() {
Meu Fechamento
- navigate("/fechamento/meu-fechamento/banco-pontos")} className="gap-2">
+ navigate("/intelligence-score/meu-fechamento/banco-pontos")} className="gap-2">
Banco de Pontos
@@ -266,7 +266,7 @@ export default function MeuFechamento() {
variant="outline"
disabled={!item.fechamento || item.fechamento.status !== "fechado"}
onClick={() =>
- navigate(`/fechamento/fechamentos/${item.fechamento?.id}`, {
+ navigate(`/intelligence-score/fechamentos/${item.fechamento?.id}`, {
state: {
competenciaId: item.competenciaId,
status: item.fechamento?.status,
diff --git a/src/modules/fechamento-hgtx/pages/MeuFechamentoBancoPontos.tsx b/src/modules/fechamento-hgtx/pages/MeuFechamentoBancoPontos.tsx
index 5261226..93b2979 100644
--- a/src/modules/fechamento-hgtx/pages/MeuFechamentoBancoPontos.tsx
+++ b/src/modules/fechamento-hgtx/pages/MeuFechamentoBancoPontos.tsx
@@ -114,7 +114,7 @@ export default function MeuFechamentoBancoPontos() {
if (semVinculo) {
return (
-
navigate("/fechamento/meu-fechamento")}>
+ navigate("/intelligence-score/meu-fechamento")}>
Voltar
@@ -133,7 +133,7 @@ export default function MeuFechamentoBancoPontos() {
-
navigate("/fechamento/meu-fechamento")}>
+ navigate("/intelligence-score/meu-fechamento")}>
Voltar ao meu fechamento
diff --git a/src/modules/fechamento-hgtx/pages/NotFound.tsx b/src/modules/fechamento-hgtx/pages/NotFound.tsx
index 5733afa..32d3446 100644
--- a/src/modules/fechamento-hgtx/pages/NotFound.tsx
+++ b/src/modules/fechamento-hgtx/pages/NotFound.tsx
@@ -16,7 +16,7 @@ export default function NotFound() {
404
Rota não encontrada neste módulo.
-
+
Voltar para Fechamentos