novas atualizacoes
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||
import { ArrowLeft, CheckCircle2, Clock3, ExternalLink, ListChecks, Loader2, Pencil, Plus, Repeat, RotateCcw, Target, Trash2, TrendingUp } from "lucide-react";
|
||||
import {
|
||||
ArrowLeft,
|
||||
CheckCircle2,
|
||||
Clock3,
|
||||
ExternalLink,
|
||||
ListChecks,
|
||||
Loader2,
|
||||
Pencil,
|
||||
Plus,
|
||||
Repeat,
|
||||
RotateCcw,
|
||||
Target,
|
||||
Trash2,
|
||||
TrendingUp,
|
||||
User,
|
||||
Wallet,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -17,8 +33,11 @@ import {
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { useAuthAccess } from "@/contexts/AuthAccessContext";
|
||||
import { fechamentoBancoPontosService } from "@/services/fechamento/bancoPontos";
|
||||
import { fechamentoCompetenciasService, type CompetenciaItem } from "@/services/fechamento/competencias";
|
||||
import { fechamentoFechamentosService, type FechamentoTarefaItem } from "@/services/fechamento/fechamentos";
|
||||
|
||||
@@ -43,6 +62,28 @@ function formatPontos(valor: number): string {
|
||||
return valor.toLocaleString("pt-BR", { minimumFractionDigits: 0, maximumFractionDigits: 4 });
|
||||
}
|
||||
|
||||
/** Ex.: toast após concluir — vírgula pt-BR e no máximo 2 casas decimais. */
|
||||
function formatPontosAte2Casas(valor: number): string {
|
||||
if (!Number.isFinite(valor)) return "—";
|
||||
return valor.toLocaleString("pt-BR", { minimumFractionDigits: 0, maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
/** Alinhado ao `requerMotivoAjuste`: evita "-0" e cor errada por ruído de float. */
|
||||
const SALDO_PONTOS_EPS = 0.0001;
|
||||
|
||||
function corSaldoPontos(valor: number): string {
|
||||
if (!Number.isFinite(valor)) return "text-foreground";
|
||||
if (valor > SALDO_PONTOS_EPS) return "text-emerald-600";
|
||||
if (valor < -SALDO_PONTOS_EPS) return "text-red-600";
|
||||
return "text-foreground";
|
||||
}
|
||||
|
||||
function formatPontosSaldoModal(valor: number): string {
|
||||
if (!Number.isFinite(valor)) return "—";
|
||||
const v = Math.abs(valor) < SALDO_PONTOS_EPS ? 0 : valor;
|
||||
return formatPontos(v);
|
||||
}
|
||||
|
||||
function parsePontuacaoInput(value: string): number {
|
||||
const normalized = value.trim().replace(/\s/g, "").replace(",", ".");
|
||||
const parsed = Number(normalized);
|
||||
@@ -141,6 +182,11 @@ export default function FechamentoDetalhes() {
|
||||
const [lancamentoDescricao, setLancamentoDescricao] = useState("");
|
||||
const [lancamentoPontuacao, setLancamentoPontuacao] = useState("0");
|
||||
const [pontuacaoMeta, setPontuacaoMeta] = useState<number | null>(null);
|
||||
const [parceiroId, setParceiroId] = useState<string | null>(null);
|
||||
const [parceiroNome, setParceiroNome] = useState<string | null>(null);
|
||||
const [saldoBancoAtual, setSaldoBancoAtual] = useState<number | null>(null);
|
||||
const [loadingSaldoBanco, setLoadingSaldoBanco] = useState(false);
|
||||
const [erroSaldoBanco, setErroSaldoBanco] = useState(false);
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
const [deletingTarefa, setDeletingTarefa] = useState<FechamentoTarefaItem | null>(null);
|
||||
const [isReprocessAsanaOpen, setIsReprocessAsanaOpen] = useState(false);
|
||||
@@ -183,6 +229,16 @@ export default function FechamentoDetalhes() {
|
||||
const requerMotivoAjuste = Number.isFinite(bancoCalculado) && Math.abs(bancoCalculado) > 0.0001;
|
||||
const pontuacaoTotalLabel = toPontuacaoInput(totais.pontos);
|
||||
const isValorEditado = pontuacaoPagaInput.trim() !== pontuacaoTotalLabel;
|
||||
const saldoBancoPosFechamento =
|
||||
saldoBancoAtual != null ? saldoBancoAtual + bancoCalculado : null;
|
||||
const dividaParaQuitar =
|
||||
saldoBancoAtual != null && saldoBancoAtual < 0 ? -saldoBancoAtual : 0;
|
||||
const saldoPositivoDisponivel =
|
||||
saldoBancoAtual != null && saldoBancoAtual > 0 ? saldoBancoAtual : 0;
|
||||
/** Zera o banco: saldo + (aprovados − pago) = 0 ⇒ pago = aprovados + saldo (saldo negativo = dívida). */
|
||||
const sugestaoQuitarDivida = totais.pontos - dividaParaQuitar;
|
||||
/** Zera o banco com saldo positivo: precisa movimento negativo no banco ⇒ pago = aprovados + saldo. */
|
||||
const sugestaoUsarSaldoPositivo = totais.pontos + saldoPositivoDisponivel;
|
||||
const todasTarefasRevisadas = tarefas.length > 0 && tarefas.every((tarefa) => tarefa.estaRevisada);
|
||||
const competenciasDestino = useMemo(
|
||||
() => competenciasAbertas.filter((item) => item.id !== competenciaId),
|
||||
@@ -227,6 +283,8 @@ export default function FechamentoDetalhes() {
|
||||
setFechamentoStatus(current.status);
|
||||
setPontuacaoMeta(current.pontuacaoMeta);
|
||||
setCompetenciaStatus(competenciaAtual?.status ?? "em_aberto");
|
||||
setParceiroId(current.parceiroId);
|
||||
setParceiroNome(current.parceiroNome);
|
||||
if (!competenciaId) {
|
||||
setCompetenciaId(current.competenciaId);
|
||||
}
|
||||
@@ -235,6 +293,20 @@ export default function FechamentoDetalhes() {
|
||||
}
|
||||
};
|
||||
|
||||
const loadSaldoBanco = async (id: string) => {
|
||||
setLoadingSaldoBanco(true);
|
||||
setErroSaldoBanco(false);
|
||||
try {
|
||||
const response = await fechamentoBancoPontosService.listarExtrato(id, { page: 1, perPage: 1 });
|
||||
setSaldoBancoAtual(response.parceiro?.saldo ?? 0);
|
||||
} catch {
|
||||
setErroSaldoBanco(true);
|
||||
setSaldoBancoAtual(null);
|
||||
} finally {
|
||||
setLoadingSaldoBanco(false);
|
||||
}
|
||||
};
|
||||
|
||||
const toastBloqueioFechado = () => {
|
||||
toast.error("Fechamento está fechado. Reabra para editar.");
|
||||
};
|
||||
@@ -262,6 +334,16 @@ export default function FechamentoDetalhes() {
|
||||
}
|
||||
}, [isFechado]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isConcluirOpen && parceiroId) {
|
||||
void loadSaldoBanco(parceiroId);
|
||||
}
|
||||
if (!isConcluirOpen) {
|
||||
setSaldoBancoAtual(null);
|
||||
setErroSaldoBanco(false);
|
||||
}
|
||||
}, [isConcluirOpen, parceiroId]);
|
||||
|
||||
const handleToggleAprovada = async (tarefa: FechamentoTarefaItem, approved: boolean) => {
|
||||
if (isCompetenciaConcluida) {
|
||||
toastBloqueioCompetenciaConcluida();
|
||||
@@ -417,7 +499,7 @@ export default function FechamentoDetalhes() {
|
||||
pontuacaoPaga: pontuacaoPagaRaw,
|
||||
motivoAjuste: motivoAjuste.trim() || undefined,
|
||||
});
|
||||
toast.success(`Fechamento concluído. Banco de pontos: ${data.pontuacaoBanco}.`);
|
||||
toast.success(`Fechamento concluído. Banco de pontos: ${formatPontosAte2Casas(data.pontuacaoBanco)}.`);
|
||||
setFechamentoStatus("fechado");
|
||||
navigate(`/fechamento/competencias/${data.competenciaId}`);
|
||||
} catch (error) {
|
||||
@@ -437,11 +519,15 @@ export default function FechamentoDetalhes() {
|
||||
toast.error("Não foi possível identificar o usuário para reabertura.");
|
||||
return;
|
||||
}
|
||||
if (!motivoReabertura.trim()) {
|
||||
toast.error("Informe o motivo da reabertura do fechamento.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setReabrindo(true);
|
||||
const data = await fechamentoFechamentosService.reabrirFechamento(fechamentoId, {
|
||||
reabertoPorId: me.id,
|
||||
motivo: motivoReabertura,
|
||||
motivo: motivoReabertura.trim(),
|
||||
});
|
||||
toast.success("Fechamento reaberto com sucesso.");
|
||||
setFechamentoStatus(data.status);
|
||||
@@ -1082,93 +1168,214 @@ export default function FechamentoDetalhes() {
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={isConcluirOpen} onOpenChange={setIsConcluirOpen}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Concluir fechamento</DialogTitle>
|
||||
<DialogDescription>
|
||||
Revise os totais e confirme a pontuação paga para concluir este fechamento.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="rounded-lg border bg-card p-3">
|
||||
<p className="text-xs text-muted-foreground">Aprovada</p>
|
||||
<p className="text-2xl font-bold">{formatPontos(totais.pontos)}</p>
|
||||
</div>
|
||||
<div className="rounded-lg border bg-card p-3">
|
||||
<p className="text-xs text-muted-foreground">Meta</p>
|
||||
<p className="text-2xl font-bold">{formatPontos(Number(pontuacaoMeta ?? 0))}</p>
|
||||
</div>
|
||||
<div className="rounded-lg border bg-card p-3">
|
||||
<p className="text-xs text-muted-foreground">Diferença</p>
|
||||
<p className={`text-2xl font-bold ${diferencaParaMeta >= 0 ? "text-emerald-600" : "text-red-600"}`}>
|
||||
{formatPontos(diferencaParaMeta)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border bg-card p-3">
|
||||
<p className="text-xs text-muted-foreground">Banco de Pontos</p>
|
||||
<p className={`text-2xl font-bold ${bancoCalculado >= 0 ? "text-emerald-600" : "text-red-600"}`}>
|
||||
{formatPontos(bancoCalculado)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8"
|
||||
onClick={() => setPontuacaoPagaInput(pontuacaoTotalLabel)}
|
||||
>
|
||||
Pagar total aprovado
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8"
|
||||
onClick={() => setPontuacaoPagaInput(String(pontuacaoMeta ?? 0))}
|
||||
>
|
||||
Pagar meta
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="pontuacao-paga">Pontuação paga</Label>
|
||||
{isValorEditado ? (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs text-muted-foreground underline underline-offset-2 transition hover:text-foreground"
|
||||
onClick={() => setPontuacaoPagaInput(pontuacaoTotalLabel)}
|
||||
>
|
||||
Usar valor total
|
||||
</button>
|
||||
<DialogContent className="gap-0 overflow-hidden p-0 sm:max-w-2xl">
|
||||
<div className="border-b border-border bg-muted/40 px-6 pb-4 pt-6 sm:px-8">
|
||||
<DialogHeader className="space-y-1 text-left">
|
||||
<DialogTitle className="text-xl font-semibold tracking-tight">Concluir fechamento</DialogTitle>
|
||||
<DialogDescription className="text-sm text-muted-foreground">
|
||||
Informe a pontuação paga e confirme.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{parceiroNome ? (
|
||||
<div className="mt-4 flex items-center gap-2 rounded-lg border border-border/80 bg-background/80 px-3 py-2.5">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted">
|
||||
<User className="h-4 w-4 text-muted-foreground" aria-hidden />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">Parceiro</p>
|
||||
<p className="truncate text-sm font-semibold text-foreground">{parceiroNome}</p>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="max-h-[min(75vh,36rem)] space-y-0 overflow-y-auto px-6 py-5 sm:px-8">
|
||||
<div className="grid gap-4 md:grid-cols-2 md:gap-5">
|
||||
<section className="rounded-xl border border-border bg-card p-4 shadow-sm">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-md bg-muted">
|
||||
<ListChecks className="h-4 w-4 text-muted-foreground" aria-hidden />
|
||||
</div>
|
||||
<h3 className="text-sm font-semibold text-foreground">Este mês</h3>
|
||||
</div>
|
||||
<dl className="space-y-0">
|
||||
<div className="flex items-baseline justify-between gap-4 border-b border-border/60 py-2.5 first:pt-0">
|
||||
<dt className="text-sm text-muted-foreground">Aprovados</dt>
|
||||
<dd className="text-right text-lg font-semibold tabular-nums tracking-tight text-foreground">
|
||||
{formatPontos(totais.pontos)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-4 border-b border-border/60 py-2.5">
|
||||
<dt className="text-sm text-muted-foreground">Meta</dt>
|
||||
<dd className="text-right text-lg font-semibold tabular-nums tracking-tight text-foreground">
|
||||
{formatPontos(Number(pontuacaoMeta ?? 0))}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-4 py-2.5 last:pb-0">
|
||||
<dt className="text-sm text-muted-foreground">Diferença</dt>
|
||||
<dd
|
||||
className={`text-right text-lg font-semibold tabular-nums tracking-tight ${
|
||||
diferencaParaMeta >= 0 ? "text-emerald-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{diferencaParaMeta >= 0 ? "+" : ""}
|
||||
{formatPontos(diferencaParaMeta)}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section className="rounded-xl border border-primary/20 bg-primary/[0.06] p-4">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-md bg-background shadow-sm">
|
||||
<Wallet className="h-4 w-4 text-primary" aria-hidden />
|
||||
</div>
|
||||
<h3 className="text-sm font-semibold text-foreground">Banco de pontos</h3>
|
||||
</div>
|
||||
<dl className="space-y-0">
|
||||
<div className="flex items-baseline justify-between gap-4 border-b border-primary/10 py-2.5 first:pt-0">
|
||||
<dt className="text-sm text-muted-foreground">Saldo hoje</dt>
|
||||
<dd className="text-right">
|
||||
{loadingSaldoBanco ? (
|
||||
<Skeleton className="ml-auto h-7 w-24 rounded-md" />
|
||||
) : erroSaldoBanco ? (
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<span className="text-sm font-medium text-destructive">Não foi possível carregar</span>
|
||||
{parceiroId ? (
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs font-medium text-primary underline-offset-2 hover:underline"
|
||||
onClick={() => void loadSaldoBanco(parceiroId)}
|
||||
>
|
||||
Tentar de novo
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<span
|
||||
className={`text-lg font-semibold tabular-nums tracking-tight ${corSaldoPontos(saldoBancoAtual ?? 0)}`}
|
||||
>
|
||||
{formatPontosSaldoModal(saldoBancoAtual ?? 0)}
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-4 border-b border-primary/10 py-2.5">
|
||||
<dt className="text-sm text-muted-foreground">Movimento</dt>
|
||||
<dd
|
||||
className={`text-right text-lg font-semibold tabular-nums tracking-tight ${corSaldoPontos(bancoCalculado)}`}
|
||||
>
|
||||
{bancoCalculado > SALDO_PONTOS_EPS ? "+" : ""}
|
||||
{formatPontosSaldoModal(bancoCalculado)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-4 py-2.5 last:pb-0">
|
||||
<dt className="text-sm font-medium text-foreground">Depois</dt>
|
||||
<dd className="text-right">
|
||||
{saldoBancoPosFechamento == null ? (
|
||||
<span className="text-lg font-semibold tabular-nums text-muted-foreground">—</span>
|
||||
) : (
|
||||
<span
|
||||
className={`text-lg font-semibold tabular-nums tracking-tight ${corSaldoPontos(saldoBancoPosFechamento)}`}
|
||||
>
|
||||
{formatPontosSaldoModal(saldoBancoPosFechamento)}
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<Separator className="my-5" />
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="pontuacao-paga" className="text-sm font-semibold">
|
||||
Pontuação paga
|
||||
</Label>
|
||||
<Input
|
||||
id="pontuacao-paga"
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
className="h-11 text-base font-medium tabular-nums"
|
||||
value={pontuacaoPagaInput}
|
||||
onChange={(e) => setPontuacaoPagaInput(e.target.value)}
|
||||
placeholder="Ex.: 10,5"
|
||||
/>
|
||||
{isValorEditado ? (
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs font-medium text-primary underline-offset-2 hover:underline"
|
||||
onClick={() => setPontuacaoPagaInput(pontuacaoTotalLabel)}
|
||||
>
|
||||
Voltar para o total aprovado ({formatPontos(totais.pontos)})
|
||||
</button>
|
||||
) : null}
|
||||
<div className="rounded-lg border border-dashed border-border bg-muted/30 p-3">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:flex-wrap">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="h-9 justify-center font-normal"
|
||||
onClick={() => setPontuacaoPagaInput(pontuacaoTotalLabel)}
|
||||
>
|
||||
Igual aos aprovados
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="h-9 justify-center font-normal"
|
||||
onClick={() => setPontuacaoPagaInput(String(pontuacaoMeta ?? 0))}
|
||||
>
|
||||
Igual à meta
|
||||
</Button>
|
||||
{dividaParaQuitar > 0 ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="h-9 justify-center border border-red-200 bg-red-50 font-normal text-red-800 hover:bg-red-100 dark:border-red-900/50 dark:bg-red-950/40 dark:text-red-200 dark:hover:bg-red-950/60"
|
||||
title={`${formatPontos(sugestaoQuitarDivida)} pontos — aprovados menos a dívida; zera o banco`}
|
||||
onClick={() => setPontuacaoPagaInput(toPontuacaoInput(sugestaoQuitarDivida))}
|
||||
>
|
||||
Quitar dívida ({formatPontos(dividaParaQuitar)})
|
||||
</Button>
|
||||
) : null}
|
||||
{saldoPositivoDisponivel > 0 ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="h-9 justify-center border border-emerald-200 bg-emerald-50 font-normal text-emerald-900 hover:bg-emerald-100 dark:border-emerald-900/50 dark:bg-emerald-950/40 dark:text-emerald-100 dark:hover:bg-emerald-950/60"
|
||||
title={`${formatPontos(sugestaoUsarSaldoPositivo)} pontos — aprovados + saldo; zera o banco`}
|
||||
onClick={() => setPontuacaoPagaInput(toPontuacaoInput(sugestaoUsarSaldoPositivo))}
|
||||
>
|
||||
Usar saldo positivo ({formatPontos(saldoPositivoDisponivel)})
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{requerMotivoAjuste ? (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="motivo-ajuste">Motivo do ajuste *</Label>
|
||||
<div className="mt-5 space-y-2 rounded-lg border border-amber-200/80 bg-amber-50/80 p-3 dark:border-amber-900/40 dark:bg-amber-950/25">
|
||||
<Label htmlFor="motivo-ajuste" className="text-sm font-semibold text-amber-950 dark:text-amber-100">
|
||||
Motivo do ajuste *
|
||||
</Label>
|
||||
<Input
|
||||
id="motivo-ajuste"
|
||||
value={motivoAjuste}
|
||||
onChange={(e) => setMotivoAjuste(e.target.value)}
|
||||
placeholder="Ex.: pagamento parcial acordado com o parceiro"
|
||||
className="bg-background"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogFooter className="gap-2 border-t border-border bg-muted/30 px-6 py-4 sm:justify-end sm:px-8">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hover:bg-muted/60 hover:text-foreground"
|
||||
@@ -1180,15 +1387,15 @@ export default function FechamentoDetalhes() {
|
||||
<Button
|
||||
onClick={() => void handleConcluirFechamento()}
|
||||
disabled={concluindo || isReadonly}
|
||||
className="min-w-[172px] shadow-lg hover:shadow-primary/50"
|
||||
className="min-w-[160px] shadow-md hover:shadow-primary/40"
|
||||
>
|
||||
{concluindo ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Concluindo...
|
||||
</>
|
||||
) : (
|
||||
"Confirmar conclusão"
|
||||
"Concluir"
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@@ -1204,12 +1411,13 @@ export default function FechamentoDetalhes() {
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="motivo-reabertura">Motivo (opcional)</Label>
|
||||
<Label htmlFor="motivo-reabertura">Motivo *</Label>
|
||||
<Input
|
||||
id="motivo-reabertura"
|
||||
value={motivoReabertura}
|
||||
onChange={(e) => setMotivoReabertura(e.target.value)}
|
||||
placeholder="Ex.: ajuste após revisão financeira"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
@@ -1221,7 +1429,10 @@ export default function FechamentoDetalhes() {
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button onClick={() => void handleReabrirFechamento()} disabled={reabrindo}>
|
||||
<Button
|
||||
onClick={() => void handleReabrirFechamento()}
|
||||
disabled={reabrindo || !motivoReabertura.trim()}
|
||||
>
|
||||
{reabrindo ? "Reabrindo..." : "Confirmar reabertura"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
Reference in New Issue
Block a user