atualizacoes modulo Score e Commander
This commit is contained in:
@@ -8,12 +8,26 @@ import {
|
|||||||
ExternalLink,
|
ExternalLink,
|
||||||
Landmark,
|
Landmark,
|
||||||
Loader2,
|
Loader2,
|
||||||
|
Trash2,
|
||||||
Wallet,
|
Wallet,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
|
import { useAuthAccess } from "@/contexts/AuthAccessContext";
|
||||||
import {
|
import {
|
||||||
fechamentoBancoPontosService,
|
fechamentoBancoPontosService,
|
||||||
type BancoPontosExtratoItem,
|
type BancoPontosExtratoItem,
|
||||||
@@ -59,6 +73,8 @@ function nomeExibicaoParceiro(p: BancoPontosParceiroExtrato): string {
|
|||||||
export default function BancoPontosExtrato() {
|
export default function BancoPontosExtrato() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const { me } = useAuthAccess();
|
||||||
|
const podeExcluirTransacao = me?.capabilities.bancoPontos.excluirTransacao ?? false;
|
||||||
const { parceiroId = "" } = useParams();
|
const { parceiroId = "" } = useParams();
|
||||||
const nomeDoState = (location.state as ExtratoLocationState | null)?.nomeExibicao?.trim();
|
const nomeDoState = (location.state as ExtratoLocationState | null)?.nomeExibicao?.trim();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -67,6 +83,9 @@ export default function BancoPontosExtrato() {
|
|||||||
const [linhas, setLinhas] = useState<BancoPontosExtratoItem[]>([]);
|
const [linhas, setLinhas] = useState<BancoPontosExtratoItem[]>([]);
|
||||||
const [meta, setMeta] = useState({ total: 0, paginaAtual: 1, totalPaginas: 1 });
|
const [meta, setMeta] = useState({ total: 0, paginaAtual: 1, totalPaginas: 1 });
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
|
const [linhaParaExcluir, setLinhaParaExcluir] = useState<BancoPontosExtratoItem | null>(null);
|
||||||
|
const [motivoExclusao, setMotivoExclusao] = useState("");
|
||||||
|
const [excluindo, setExcluindo] = useState(false);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
if (!parceiroId) return;
|
if (!parceiroId) return;
|
||||||
@@ -125,6 +144,28 @@ export default function BancoPontosExtrato() {
|
|||||||
return `Mostrando ${inicio}–${fim} de ${meta.total}`;
|
return `Mostrando ${inicio}–${fim} de ${meta.total}`;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const handleExcluirTransacao = async () => {
|
||||||
|
if (!linhaParaExcluir) return;
|
||||||
|
const motivo = motivoExclusao.trim();
|
||||||
|
if (motivo.length < 3) {
|
||||||
|
toast.error("Informe o motivo da exclusão (mínimo 3 caracteres).");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setExcluindo(true);
|
||||||
|
await fechamentoBancoPontosService.excluirTransacao(linhaParaExcluir.id, motivo);
|
||||||
|
toast.success("Lançamento excluído do extrato.");
|
||||||
|
setLinhaParaExcluir(null);
|
||||||
|
setMotivoExclusao("");
|
||||||
|
await load();
|
||||||
|
} catch (e) {
|
||||||
|
const msg = e instanceof Error ? e.message : "Erro ao excluir lançamento.";
|
||||||
|
toast.error(msg);
|
||||||
|
} finally {
|
||||||
|
setExcluindo(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleExportarXlsx = async () => {
|
const handleExportarXlsx = async () => {
|
||||||
if (!parceiroId) return;
|
if (!parceiroId) return;
|
||||||
try {
|
try {
|
||||||
@@ -220,19 +261,28 @@ export default function BancoPontosExtrato() {
|
|||||||
<TableHead className="min-w-[100px]">Tipo</TableHead>
|
<TableHead className="min-w-[100px]">Tipo</TableHead>
|
||||||
<TableHead className="min-w-[130px] text-right">Valor (Pontos)</TableHead>
|
<TableHead className="min-w-[130px] text-right">Valor (Pontos)</TableHead>
|
||||||
<TableHead className="min-w-[120px] text-center">Fechamento</TableHead>
|
<TableHead className="min-w-[120px] text-center">Fechamento</TableHead>
|
||||||
|
{podeExcluirTransacao ? (
|
||||||
|
<TableHead className="min-w-[80px] text-center">Ações</TableHead>
|
||||||
|
) : null}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={6} className="py-14 text-center text-muted-foreground">
|
<TableCell
|
||||||
|
colSpan={podeExcluirTransacao ? 7 : 6}
|
||||||
|
className="py-14 text-center text-muted-foreground"
|
||||||
|
>
|
||||||
<Loader2 className="mx-auto mb-2 h-8 w-8 animate-spin text-primary" />
|
<Loader2 className="mx-auto mb-2 h-8 w-8 animate-spin text-primary" />
|
||||||
Carregando extrato...
|
Carregando extrato...
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
) : linhasOrdenadas.length === 0 ? (
|
) : linhasOrdenadas.length === 0 ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={6} className="py-12 text-center text-muted-foreground">
|
<TableCell
|
||||||
|
colSpan={podeExcluirTransacao ? 7 : 6}
|
||||||
|
className="py-12 text-center text-muted-foreground"
|
||||||
|
>
|
||||||
Nenhuma movimentação registrada para este parceiro.
|
Nenhuma movimentação registrada para este parceiro.
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -287,6 +337,23 @@ export default function BancoPontosExtrato() {
|
|||||||
<span className="text-sm text-muted-foreground">—</span>
|
<span className="text-sm text-muted-foreground">—</span>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
{podeExcluirTransacao ? (
|
||||||
|
<TableCell className="text-center">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
title="Excluir lançamento"
|
||||||
|
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
|
||||||
|
onClick={() => {
|
||||||
|
setLinhaParaExcluir(linha);
|
||||||
|
setMotivoExclusao("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
) : null}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -330,6 +397,50 @@ export default function BancoPontosExtrato() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AlertDialog
|
||||||
|
open={linhaParaExcluir != null}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
setLinhaParaExcluir(null);
|
||||||
|
setMotivoExclusao("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Excluir lançamento</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
O lançamento será removido do extrato e do saldo, mas permanece registrado no banco com motivo e
|
||||||
|
auditoria. Esta ação não pode ser desfeita pela interface.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<div className="space-y-2 py-2">
|
||||||
|
<Label htmlFor="motivo-exclusao-banco">Motivo da exclusão</Label>
|
||||||
|
<Textarea
|
||||||
|
id="motivo-exclusao-banco"
|
||||||
|
value={motivoExclusao}
|
||||||
|
onChange={(e) => setMotivoExclusao(e.target.value)}
|
||||||
|
placeholder="Descreva o motivo (mínimo 3 caracteres)"
|
||||||
|
rows={3}
|
||||||
|
disabled={excluindo}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel disabled={excluindo}>Cancelar</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
disabled={excluindo || motivoExclusao.trim().length < 3}
|
||||||
|
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
void handleExcluirTransacao();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{excluindo ? "Excluindo..." : "Confirmar exclusão"}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -356,9 +356,10 @@ export default function CompetenciaFechamentos() {
|
|||||||
{podeImportarAsana ? (
|
{podeImportarAsana ? (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="outline"
|
||||||
onClick={() => setIsReprocessModalOpen(true)}
|
onClick={() => setIsReprocessModalOpen(true)}
|
||||||
disabled={importing}
|
disabled={importing}
|
||||||
|
className="hover:bg-muted/60 hover:text-foreground"
|
||||||
>
|
>
|
||||||
<RefreshCcw className="mr-2 h-4 w-4" />
|
<RefreshCcw className="mr-2 h-4 w-4" />
|
||||||
Reprocessar Asana
|
Reprocessar Asana
|
||||||
@@ -367,7 +368,7 @@ export default function CompetenciaFechamentos() {
|
|||||||
{podeConcluirCompetencia ? (
|
{podeConcluirCompetencia ? (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="default"
|
||||||
onClick={() => setIsConcluirModalOpen(true)}
|
onClick={() => setIsConcluirModalOpen(true)}
|
||||||
disabled={concluindoCompetencia}
|
disabled={concluindoCompetencia}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -689,8 +689,8 @@ export default function Configuracoes() {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="outline"
|
||||||
className="h-11 shrink-0 whitespace-nowrap px-5 lg:self-stretch"
|
className="h-11 shrink-0 whitespace-nowrap bg-background px-5 hover:bg-muted/60 hover:text-foreground dark:bg-muted/30 dark:hover:bg-muted/50 lg:self-stretch"
|
||||||
onClick={handleBuscarWorkspaces}
|
onClick={handleBuscarWorkspaces}
|
||||||
disabled={!podeSalvarConfig || !podeBuscarWorkspaces || Boolean(configAsanaError)}
|
disabled={!podeSalvarConfig || !podeBuscarWorkspaces || Boolean(configAsanaError)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ export default function FechamentoDetalhes() {
|
|||||||
) : podeConcluirFechamento ? (
|
) : podeConcluirFechamento ? (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="default"
|
||||||
onClick={handleAbrirConcluir}
|
onClick={handleAbrirConcluir}
|
||||||
disabled={tarefas.length === 0 || concluindo || isCompetenciaConcluida}
|
disabled={tarefas.length === 0 || concluindo || isCompetenciaConcluida}
|
||||||
className="min-w-[152px]"
|
className="min-w-[152px]"
|
||||||
@@ -1713,18 +1713,18 @@ export default function FechamentoDetalhes() {
|
|||||||
<div className="flex flex-col gap-2 sm:flex-row sm:flex-wrap">
|
<div className="flex flex-col gap-2 sm:flex-row sm:flex-wrap">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-9 justify-center font-normal"
|
className="h-9 justify-center bg-background font-normal hover:bg-muted/60 hover:text-foreground dark:bg-muted/30 dark:hover:bg-muted/50"
|
||||||
onClick={() => setPontuacaoPagaInput(pontuacaoTotalLabel)}
|
onClick={() => setPontuacaoPagaInput(pontuacaoTotalLabel)}
|
||||||
>
|
>
|
||||||
Igual aos aprovados
|
Igual aos aprovados
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-9 justify-center font-normal"
|
className="h-9 justify-center bg-background font-normal hover:bg-muted/60 hover:text-foreground dark:bg-muted/30 dark:hover:bg-muted/50"
|
||||||
onClick={() => setPontuacaoPagaInput(String(pontuacaoMeta ?? 0))}
|
onClick={() => setPontuacaoPagaInput(String(pontuacaoMeta ?? 0))}
|
||||||
>
|
>
|
||||||
Igual à meta
|
Igual à meta
|
||||||
|
|||||||
@@ -782,7 +782,13 @@ export default function Parceiros() {
|
|||||||
<TableCell>{parceiro.email}</TableCell>
|
<TableCell>{parceiro.email}</TableCell>
|
||||||
<TableCell>{getTipoPessoaLabel(parceiro.tipoPessoa)}</TableCell>
|
<TableCell>{getTipoPessoaLabel(parceiro.tipoPessoa)}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Badge variant={parceiro.estaAtivo ? "secondary" : "outline"}>
|
<Badge
|
||||||
|
className={
|
||||||
|
parceiro.estaAtivo
|
||||||
|
? "border-emerald-600/30 bg-emerald-50 font-normal text-emerald-800 hover:bg-emerald-50 dark:bg-emerald-950/40 dark:text-emerald-200"
|
||||||
|
: "border-border bg-muted/60 font-normal text-muted-foreground hover:bg-muted/60 dark:bg-muted/40 dark:text-muted-foreground"
|
||||||
|
}
|
||||||
|
>
|
||||||
{parceiro.estaAtivo ? "Ativo" : "Inativo"}
|
{parceiro.estaAtivo ? "Ativo" : "Inativo"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Edit, Plus, Power, Users, UserCheck, UserX } from "lucide-react";
|
import { Edit, Plus, Power, Trash2, Users, UserCheck, UserX } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -84,6 +84,7 @@ function getEmailUsuario(usuario: UsuarioItem | null | undefined): string {
|
|||||||
export default function Usuarios() {
|
export default function Usuarios() {
|
||||||
const { me } = useAuthAccess();
|
const { me } = useAuthAccess();
|
||||||
const podeEditarUsuarios = me?.capabilities.usuarios.criarEditarToggle ?? false;
|
const podeEditarUsuarios = me?.capabilities.usuarios.criarEditarToggle ?? false;
|
||||||
|
const podeExcluirUsuario = me?.capabilities.usuarios.excluir ?? false;
|
||||||
const [usuarios, setUsuarios] = useState<UsuarioItem[]>([]);
|
const [usuarios, setUsuarios] = useState<UsuarioItem[]>([]);
|
||||||
const [parceiros, setParceiros] = useState<ParceiroItem[]>([]);
|
const [parceiros, setParceiros] = useState<ParceiroItem[]>([]);
|
||||||
|
|
||||||
@@ -92,8 +93,10 @@ export default function Usuarios() {
|
|||||||
const [isCreateOpen, setIsCreateOpen] = useState(false);
|
const [isCreateOpen, setIsCreateOpen] = useState(false);
|
||||||
const [isEditOpen, setIsEditOpen] = useState(false);
|
const [isEditOpen, setIsEditOpen] = useState(false);
|
||||||
const [isToggleOpen, setIsToggleOpen] = useState(false);
|
const [isToggleOpen, setIsToggleOpen] = useState(false);
|
||||||
|
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [toggling, setToggling] = useState(false);
|
const [toggling, setToggling] = useState(false);
|
||||||
|
const [deleting, setDeleting] = useState(false);
|
||||||
|
|
||||||
const [selectedUsuario, setSelectedUsuario] = useState<UsuarioItem | null>(null);
|
const [selectedUsuario, setSelectedUsuario] = useState<UsuarioItem | null>(null);
|
||||||
const [form, setForm] = useState<UsuarioForm>(formInicial);
|
const [form, setForm] = useState<UsuarioForm>(formInicial);
|
||||||
@@ -279,6 +282,30 @@ export default function Usuarios() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openDeleteDialog = (usuario: UsuarioItem) => {
|
||||||
|
setSelectedUsuario(usuario);
|
||||||
|
setIsDeleteOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteUsuario = async () => {
|
||||||
|
if (!selectedUsuario) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setDeleting(true);
|
||||||
|
await fechamentoUsuariosService.excluirUsuario(selectedUsuario.id);
|
||||||
|
toast.success("Usuário excluído com sucesso.");
|
||||||
|
setIsDeleteOpen(false);
|
||||||
|
setSelectedUsuario(null);
|
||||||
|
setReloadNonce((prev) => prev + 1);
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : "Erro ao excluir usuário.";
|
||||||
|
toast.error(message);
|
||||||
|
} finally {
|
||||||
|
setDeleting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleToggleStatus = async () => {
|
const handleToggleStatus = async () => {
|
||||||
if (!selectedUsuario) {
|
if (!selectedUsuario) {
|
||||||
return;
|
return;
|
||||||
@@ -459,6 +486,8 @@ export default function Usuarios() {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center justify-center gap-1">
|
<div className="flex items-center justify-center gap-1">
|
||||||
|
{podeEditarUsuarios || podeExcluirUsuario ? (
|
||||||
|
<>
|
||||||
{podeEditarUsuarios ? (
|
{podeEditarUsuarios ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
@@ -478,6 +507,20 @@ export default function Usuarios() {
|
|||||||
<Power className="h-4 w-4" />
|
<Power className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
) : null}
|
||||||
|
{podeExcluirUsuario ? (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => openDeleteDialog(usuario)}
|
||||||
|
title="Excluir usuário"
|
||||||
|
disabled={me?.id === usuario.id}
|
||||||
|
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-xs text-muted-foreground">Somente leitura</span>
|
<span className="text-xs text-muted-foreground">Somente leitura</span>
|
||||||
)}
|
)}
|
||||||
@@ -741,6 +784,37 @@ export default function Usuarios() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<AlertDialog open={isDeleteOpen} onOpenChange={setIsDeleteOpen}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Excluir usuário</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription className="space-y-2">
|
||||||
|
<span className="block">
|
||||||
|
Deseja excluir o usuário <strong>{getNomeUsuario(selectedUsuario)}</strong> (
|
||||||
|
{getEmailUsuario(selectedUsuario)})?
|
||||||
|
</span>
|
||||||
|
<span className="block">
|
||||||
|
O usuário deixará de aparecer na lista e não poderá mais acessar o sistema. Se houver parceiro
|
||||||
|
vinculado, apenas o vínculo será removido — os dados do parceiro e dos fechamentos permanecem
|
||||||
|
intactos.
|
||||||
|
</span>
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel className="hover:bg-muted/60 hover:text-foreground" disabled={deleting}>
|
||||||
|
Cancelar
|
||||||
|
</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={handleDeleteUsuario}
|
||||||
|
disabled={deleting || !selectedUsuario || me?.id === selectedUsuario?.id}
|
||||||
|
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||||
|
>
|
||||||
|
{deleting ? "Excluindo..." : "Excluir usuário"}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
|
||||||
<AlertDialog open={isToggleOpen} onOpenChange={setIsToggleOpen}>
|
<AlertDialog open={isToggleOpen} onOpenChange={setIsToggleOpen}>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||||
|
|
||||||
|
/** Operações de IA (imagem, áudio, transcrição) podem levar vários minutos. */
|
||||||
|
export const LONG_RUNNING_REQUEST_TIMEOUT_MS = 300_000;
|
||||||
|
|
||||||
class ApiService {
|
class ApiService {
|
||||||
private readonly axiosInstance: AxiosInstance;
|
private readonly axiosInstance: AxiosInstance;
|
||||||
private readonly apiKey: string;
|
private readonly apiKey: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
||||||
import { apiService } from './api';
|
import { apiService, LONG_RUNNING_REQUEST_TIMEOUT_MS } from './api';
|
||||||
|
|
||||||
export type VoiceType = 'alloy' | 'echo' | 'fable' | 'nova' | 'onyx' | 'shimmer';
|
export type VoiceType = 'alloy' | 'echo' | 'fable' | 'nova' | 'onyx' | 'shimmer';
|
||||||
|
|
||||||
@@ -135,7 +135,8 @@ class AudioGenerationService {
|
|||||||
try {
|
try {
|
||||||
const response = await apiService.post<AudioGenerationResponse>(
|
const response = await apiService.post<AudioGenerationResponse>(
|
||||||
this.AUDIO_GENERATION_ENDPOINT,
|
this.AUDIO_GENERATION_ENDPOINT,
|
||||||
{ estabelecimento_id: estabId, user_email: email, message, voice }
|
{ estabelecimento_id: estabId, user_email: email, message, voice },
|
||||||
|
{ timeout: LONG_RUNNING_REQUEST_TIMEOUT_MS }
|
||||||
);
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
|||||||
@@ -303,6 +303,19 @@ class FechamentoBancoPontosService {
|
|||||||
throw error instanceof Error ? error : new Error("Erro ao exportar extrato do banco de pontos.");
|
throw error instanceof Error ? error : new Error("Erro ao exportar extrato do banco de pontos.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async excluirTransacao(transacaoId: string, motivo: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const headers = await buildCommanderHeaders();
|
||||||
|
const baseUrl = resolveCommanderBaseUrl();
|
||||||
|
await axios.delete(`${baseUrl}/banco-pontos/transacoes/${transacaoId}`, {
|
||||||
|
headers,
|
||||||
|
data: { motivo: motivo.trim() },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
this.handleAxiosError(error, "Erro ao excluir lançamento do banco de pontos.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fechamentoBancoPontosService = new FechamentoBancoPontosService();
|
export const fechamentoBancoPontosService = new FechamentoBancoPontosService();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export type Capabilities = {
|
|||||||
tarefaEditarRevisao: boolean;
|
tarefaEditarRevisao: boolean;
|
||||||
tarefaExcluirManual: boolean;
|
tarefaExcluirManual: boolean;
|
||||||
};
|
};
|
||||||
bancoPontos: { acessoAdmin: boolean };
|
bancoPontos: { acessoAdmin: boolean; excluirTransacao: boolean };
|
||||||
parceiros: {
|
parceiros: {
|
||||||
listar: boolean;
|
listar: boolean;
|
||||||
criar: boolean;
|
criar: boolean;
|
||||||
@@ -31,7 +31,7 @@ export type Capabilities = {
|
|||||||
verFator: boolean;
|
verFator: boolean;
|
||||||
editarFator: boolean;
|
editarFator: boolean;
|
||||||
};
|
};
|
||||||
usuarios: { listar: boolean; criarEditarToggle: boolean };
|
usuarios: { listar: boolean; criarEditarToggle: boolean; excluir: boolean };
|
||||||
configuracoes: { salvar: boolean };
|
configuracoes: { salvar: boolean };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ function adminCapabilities(): Capabilities {
|
|||||||
tarefaEditarRevisao: true,
|
tarefaEditarRevisao: true,
|
||||||
tarefaExcluirManual: true,
|
tarefaExcluirManual: true,
|
||||||
},
|
},
|
||||||
bancoPontos: { acessoAdmin: true },
|
bancoPontos: { acessoAdmin: true, excluirTransacao: true },
|
||||||
parceiros: {
|
parceiros: {
|
||||||
listar: true,
|
listar: true,
|
||||||
criar: true,
|
criar: true,
|
||||||
@@ -66,7 +66,7 @@ function adminCapabilities(): Capabilities {
|
|||||||
verFator: true,
|
verFator: true,
|
||||||
editarFator: true,
|
editarFator: true,
|
||||||
},
|
},
|
||||||
usuarios: { listar: true, criarEditarToggle: true },
|
usuarios: { listar: true, criarEditarToggle: true, excluir: true },
|
||||||
configuracoes: { salvar: true },
|
configuracoes: { salvar: true },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ function parceiroCapabilities(): Capabilities {
|
|||||||
tarefaEditarRevisao: false,
|
tarefaEditarRevisao: false,
|
||||||
tarefaExcluirManual: false,
|
tarefaExcluirManual: false,
|
||||||
},
|
},
|
||||||
bancoPontos: { acessoAdmin: false },
|
bancoPontos: { acessoAdmin: false, excluirTransacao: false },
|
||||||
parceiros: {
|
parceiros: {
|
||||||
listar: false,
|
listar: false,
|
||||||
criar: false,
|
criar: false,
|
||||||
@@ -102,7 +102,7 @@ function parceiroCapabilities(): Capabilities {
|
|||||||
verFator: false,
|
verFator: false,
|
||||||
editarFator: false,
|
editarFator: false,
|
||||||
},
|
},
|
||||||
usuarios: { listar: false, criarEditarToggle: false },
|
usuarios: { listar: false, criarEditarToggle: false, excluir: false },
|
||||||
configuracoes: { salvar: false },
|
configuracoes: { salvar: false },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ function supervisorCapabilities(): Capabilities {
|
|||||||
tarefaEditarRevisao: false,
|
tarefaEditarRevisao: false,
|
||||||
tarefaExcluirManual: false,
|
tarefaExcluirManual: false,
|
||||||
},
|
},
|
||||||
bancoPontos: { acessoAdmin: true },
|
bancoPontos: { acessoAdmin: true, excluirTransacao: false },
|
||||||
parceiros: {
|
parceiros: {
|
||||||
listar: true,
|
listar: true,
|
||||||
criar: true,
|
criar: true,
|
||||||
@@ -138,7 +138,7 @@ function supervisorCapabilities(): Capabilities {
|
|||||||
verFator: false,
|
verFator: false,
|
||||||
editarFator: false,
|
editarFator: false,
|
||||||
},
|
},
|
||||||
usuarios: { listar: true, criarEditarToggle: false },
|
usuarios: { listar: true, criarEditarToggle: false, excluir: false },
|
||||||
configuracoes: { salvar: false },
|
configuracoes: { salvar: false },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,16 @@ class FechamentoUsuariosService {
|
|||||||
this.handleAxiosError(error, "Erro ao atualizar status do usuário.");
|
this.handleAxiosError(error, "Erro ao atualizar status do usuário.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async excluirUsuario(id: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const headers = await buildCommanderHeaders();
|
||||||
|
const baseUrl = resolveCommanderBaseUrl();
|
||||||
|
await axios.delete(`${baseUrl}/usuarios/${id}`, { headers });
|
||||||
|
} catch (error) {
|
||||||
|
this.handleAxiosError(error, "Erro ao excluir usuário.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fechamentoUsuariosService = new FechamentoUsuariosService();
|
export const fechamentoUsuariosService = new FechamentoUsuariosService();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
||||||
import { apiService } from './api';
|
import { apiService, LONG_RUNNING_REQUEST_TIMEOUT_MS } from './api';
|
||||||
|
|
||||||
export type ImageSize = '1024x1024' | '1024x1792' | '1792x1024';
|
export type ImageSize = '1024x1024' | '1024x1792' | '1792x1024';
|
||||||
|
|
||||||
@@ -127,7 +127,8 @@ class ImageGenerationService {
|
|||||||
try {
|
try {
|
||||||
const response = await apiService.post<ImageGenerationResponse>(
|
const response = await apiService.post<ImageGenerationResponse>(
|
||||||
this.IMAGE_GENERATION_ENDPOINT,
|
this.IMAGE_GENERATION_ENDPOINT,
|
||||||
{ estabelecimento_id: estabId, user_email: email, description, size }
|
{ estabelecimento_id: estabId, user_email: email, description, size },
|
||||||
|
{ timeout: LONG_RUNNING_REQUEST_TIMEOUT_MS }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.data.success) {
|
if (!response.data.success) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
||||||
import { apiService } from './api';
|
import { apiService, LONG_RUNNING_REQUEST_TIMEOUT_MS } from './api';
|
||||||
|
|
||||||
export interface TranscriptionResponse {
|
export interface TranscriptionResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -82,7 +82,8 @@ class TranscriptionService {
|
|||||||
try {
|
try {
|
||||||
const response = await apiService.postFormData<TranscriptionResponse>(
|
const response = await apiService.postFormData<TranscriptionResponse>(
|
||||||
this.TRANSCRIPTION_ENDPOINT,
|
this.TRANSCRIPTION_ENDPOINT,
|
||||||
formData
|
formData,
|
||||||
|
{ timeout: LONG_RUNNING_REQUEST_TIMEOUT_MS }
|
||||||
);
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
|||||||
Reference in New Issue
Block a user