[MVPTN-37] refactor(): Ajustes diversos

This commit is contained in:
Keven 2025-11-15 12:30:33 -03:00
parent 97c958cb00
commit b2e0d50dd6
12 changed files with 196 additions and 42 deletions

View file

@ -64,7 +64,7 @@ export default function TPessoaTableFormSubview({ params, servico, selectedTPess
return (
<div className="p-5">
<div className="p-5" key={index}>
<Item variant="outline">
<ItemMedia>
<Avatar className="size-10">

View file

@ -1,8 +1,15 @@
import HandleSelectTServicoTipoInterface from "./HandleSelectTServicoTipoInterface";
export default function HandleSelectTServicoTipoAction({ servico, emolumento, onOpenPessoaForm, onAddItem }: HandleSelectTServicoTipoInterface) {
if (!servico || !emolumento) return;
if (!servico || !emolumento) {
return {
'status': 422,
'detail': 'Serviço e emolumento devem ser selecionados'
};
};
if (servico?.tipo_pessoa) {

View file

@ -6,7 +6,7 @@ export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedid
data.valor_documento = 0
// Verifica dados obrigatórios de serviço e emolumento
if (!data?.emolumentoSelecionado?.emolumento_id || !data?.servicoSelecionado?.servico_tipo_id) {
if (!data?.emolumento?.emolumento_id || !data?.servico_tipo?.servico_tipo_id) {
return {
status: 400,
message: 'Dados informados inválidos: serviço ou emolumento não informado.'
@ -14,7 +14,7 @@ export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedid
}
// Valida sistema_id (padrão 2, mas ainda assim precisa ser válido)
if (!data?.emolumentoSelecionado.sistema_id || data.emolumentoSelecionado.sistema_id <= 0) {
if (!data?.emolumento.sistema_id || data.servico_tipo.sistema_id <= 0) {
return {
status: 400,
message: 'Sistema inválido ou não informado.'
@ -30,10 +30,10 @@ export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedid
}
return {
sistema_id: data.emolumentoSelecionado.sistema_id,
sistema_id: data.emolumento.sistema_id,
valor_documento: data.valor_documento,
quantidade: data.quantidade,
emolumento_id: data.emolumentoSelecionado.emolumento_id,
emolumento_id: data.emolumento.emolumento_id,
};
}

View file

@ -44,10 +44,10 @@ export function PrepareTServicoItemPedidoCalculoResponse(
const item: TServicoItemPedidoAddResponseInterface = {
emolumento_id: result.emolumento_id,
emolumento_item_id: result.emolumento_item_id ?? null,
servico_tipo_id: data.servicoSelecionado.servico_tipo_id ?? 0,
tipo_item: data.servicoSelecionado.tipo_item ?? "",
descricao: data.servicoSelecionado.descricao ?? "",
tabela: data.emolumentoSelecionado?.descricao ?? "",
servico_tipo_id: data.servico_tipo.servico_tipo_id ?? 0,
tipo_item: data.servico_tipo.tipo_item ?? "",
descricao: data.servico_tipo.descricao ?? "",
tabela: data.servico_tipo?.descricao ?? "",
situacao: "F",
qtd: 1,
valor: result.valor_total ?? 0,

View file

@ -1,6 +1,6 @@
'use client';
import { CreditCard, Package, PlusIcon, UserSquare2 } from 'lucide-react';
import { CreditCard, Package, UserSquare2 } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
@ -38,9 +38,12 @@ import { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPed
import { useTServicoPedidoShowHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook';
import { TServicoPedidoFormInterface } from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface';
import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface';
import { FormatCPFCNPJForm } from '@/shared/actions/CPF/FormatCPFCNPJForm';
import { UnmaskCPFCNPJForm } from '@/shared/actions/CPF/UnmaskCPFCNPJForm';
import { parseNumberInput } from '@/shared/actions/form/parseNumberInput';
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
import { useResponse } from '@/shared/components/response/ResponseContext';
import {
StepNavigator,
StepNavigatorRef,
@ -58,6 +61,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
const { setValue, reset, watch } = form;
const [isSaving, setIsSaving] = useState(false);
const [isAdding, setIsAdding] = useState(false);
const [isPessoaFormOpen, setIsPessoaFormOpen] = useState(false);
const [isSaveConfirmOpen, setIsSaveConfirmOpen] = useState(false);
const [isCancelDialogOpen, setIsCancelDialogOpen] = useState(false)
@ -72,6 +76,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
const handleCloseSaveConfirm = useCallback(() => setIsSaveConfirmOpen(false), []);
// Hooks
const { response, setResponse } = useResponse()
const { saveTServicoPedido } = useTServicoPedidoSaveHook();
const { showTServicoPedido } = useTServicoPedidoShowHook();
const { TServicoItemPedidoLocal, localAddTServicoItemPedido, setLocalTServicoItemPedido } = useTServicoItemPedidoLocalAddHook(setValue);
@ -158,6 +163,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
const handleAddItemWithPessoa = useCallback(async (selectedTPessoa: any) => {
setIsAdding(true)
// Constroi um novo item
const newItem: TServicoSubviewInterface = await addTServicoItemPedido({
servico_tipo: selectedServicoTipo,
@ -190,6 +197,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
// Atualiza os itens do formulário
form.setValue(`itens.${index}`, newItem);
setIsAdding(false)
}, [
addTServicoItemPedido,
selectedServicoTipo,
@ -239,10 +248,12 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
// Controle de itens
const handleAddItemBasic = useCallback(async () => {
setIsAdding(true)
// Prepara e valida os dados de item do pedido
const payload = {
servicoSelecionado: selectedServicoTipo,
emolumentoSelecionado: selectedEmolumento
servico_tipo: selectedServicoTipo,
emolumento: selectedEmolumento
}
// Verifica se os dados foram criados corretamente
@ -254,6 +265,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
// Se tiver um novo item, adiciona o mesmo na tela
if (newItem) localAddTServicoItemPedido(newItem);
setIsAdding(false)
}, [addTServicoItemPedido, selectedServicoTipo, selectedEmolumento, localAddTServicoItemPedido]);
// Habilita o formulário de pessoas
@ -267,15 +280,42 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
// Adiciona o item a tabela e verifica se deve ou não montar a subview da linha da tabela
const handleSelectServicoTipo = useCallback(() => {
HandleSelectTServicoTipoAction({
const response = HandleSelectTServicoTipoAction({
servico: selectedServicoTipo,
emolumento: selectedEmolumento,
onOpenPessoaForm: handleOpenPessoaForm,
onAddItem: handleAddItemBasic
})
// Verifica se existem erros
if (response?.status) {
setResponse(response)
}
}, [selectedServicoTipo, selectedEmolumento, handleOpenPessoaForm, handleAddItemBasic]);
// Cálculo automático dos totais
const calcularTotais = useCallback(() => {
if (!TServicoItemPedidoLocal || !TServicoItemPedidoLocal.length) {
setValue("valor_pedido", 0);
setValue("valor_pago", 0);
return;
}
const total = TServicoItemPedidoLocal.reduce((acc, item) => {
const valor = Number(item.valor_total ?? item.valor ?? 0);
return acc + valor;
}, 0);
setValue("valor_pedido", total, { shouldDirty: true });
// opcional: manter valor pago igual ao pedido
const valorPagoAtual = watch("valor_pago");
if (!valorPagoAtual || valorPagoAtual === 0) {
setValue("valor_pago", total, { shouldDirty: true });
}
}, [TServicoItemPedidoLocal, setValue, watch]);
// Dispara a busca do pedido
useEffect(() => {
@ -297,6 +337,11 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
}, []);
// Monitora mudanças na lista de itens
useEffect(() => {
calcularTotais();
}, [TServicoItemPedidoLocal, calcularTotais]);
// Memoriza os dados para não renderizar novamente
const sections: StepSection[] = useMemo(() => [
{
@ -375,8 +420,18 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
name="cpfcnpj_apresentante"
render={({ field }) => (
<FormItem>
<FormLabel>CPF</FormLabel>
<FormControl><Input {...field} type="text" /></FormControl>
<FormLabel>CPF/CNPJ</FormLabel>
<FormControl>
<Input {...field}
type="text"
value={FormatCPFCNPJForm(field.value)}
onChange={(e) => {
const raw = UnmaskCPFCNPJForm(e.target.value);
field.onChange(raw);
}}
maxLength={14}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
@ -403,8 +458,18 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
name="selo_pessoa_cpfcnpj"
render={({ field }) => (
<FormItem>
<FormLabel>CPF</FormLabel>
<FormControl><Input {...field} type="text" /></FormControl>
<FormLabel>CPF/CNPJ</FormLabel>
<FormControl>
<Input {...field}
type="text"
value={FormatCPFCNPJForm(field.value)}
onChange={(e) => {
const raw = UnmaskCPFCNPJForm(e.target.value);
field.onChange(raw);
}}
maxLength={14}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
@ -449,9 +514,12 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
/>
</div>
<div className="col-span-12 text-end">
<Button type="button" onClick={handleSelectServicoTipo}>
<PlusIcon /> Adicionar
</Button>
<LoadingButton
text={`+ Adicionar`}
textLoading="Adicionando..."
onClick={handleSelectServicoTipo}
loading={isAdding}
/>
</div>
<div className="col-span-12">
<TServicoItemPedidoFormTable data={TServicoItemPedidoLocal} />
@ -486,8 +554,17 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
name="pagador_cpfcnpj"
render={({ field }) => (
<FormItem>
<FormLabel>CPF/CNPJ Requerente</FormLabel>
<FormControl><Input {...field} type="text" /></FormControl>
<FormLabel>CPF/CNPJ</FormLabel>
<FormControl>
<Input {...field}
type="text"
value={FormatCPFCNPJForm(field.value)}
onChange={(e) => {
const raw = UnmaskCPFCNPJForm(e.target.value);
field.onChange(raw);
}}
maxLength={14}
/></FormControl>
<FormMessage />
</FormItem>
)}
@ -502,7 +579,9 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
<FormLabel>Valor do Pedido</FormLabel>
<FormControl>
<Input {...field} type="number"
onChange={(e) => field.onChange(parseNumberInput(e))} />
onChange={(e) => field.onChange(parseNumberInput(e))}
readOnly={true}
/>
</FormControl>
<FormMessage />
</FormItem>

View file

@ -7,10 +7,13 @@ import { GCalculoServicoService } from '@/packages/administrativo/services/GCalc
import PrepareTServicoItemPedidoPayload from '@/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload';
import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface';
import { default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface';
import { useResponse } from '@/shared/components/response/ResponseContext';
export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue<TServicoItemPedidoAddInterface>) => {
const { setResponse } = useResponse();
const [TServicoItemPedido, setTServicoItemPedido] = useState<TServicoItemPedidoIndexResponseInterface[]>([]);
const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => {
@ -18,6 +21,14 @@ export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue<TServico
// Trata os dados do payload
const payload = PrepareTServicoItemPedidoPayload(data);
if (payload.status) {
setResponse(payload)
return null;
}
// Realiza a busca do item
const response = await GCalculoServicoService(payload, data);

View file

@ -7,6 +7,19 @@ export function useTServicoPedidoFormHook(defaults?: Partial<TServicoPedidoFormV
return useForm<TServicoPedidoFormValues>({
resolver: zodResolver(TServicoPedidoFormSchema),
defaultValues: {
escrevente_id: 0,
apresentante: "",
cpfcnpj_apresentante: "",
selo_pessoa_nome: "",
selo_pessoa_cpfcnpj: "",
servico_tipo: null,
emolumento: null,
itens: [],
pagador_nome: "",
pagador_cpfcnpj: "",
valor_pedido: 0,
valor_pago: 0,
tipo_pagamento: null,
...defaults,
},
});

View file

@ -2,10 +2,9 @@
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface';
import { TServicoPedidoSaveService } from '@/packages/servicos/services/TServicoPedido/TServicoPedidoSaveService';
import { useResponse } from '@/shared/components/response/ResponseContext';
export const useTServicoPedidoSaveHook = () => {
@ -17,6 +16,7 @@ export const useTServicoPedidoSaveHook = () => {
const [isOpen, setIsOpen] = useState(false);
const saveTServicoPedido = async (data: TServicoPedidoInterface) => {
const response = await TServicoPedidoSaveService(data);
// Armazena os dados da resposta
@ -30,6 +30,7 @@ export const useTServicoPedidoSaveHook = () => {
// Retorna os valores de forma imediata
return response.data;
};
return { TServicoPedido, saveTServicoPedido, isOpen, setIsOpen };

View file

@ -2,8 +2,8 @@ import TServicoTipoInterface from "@/app/(protected)/(cadastros)/cadastros/_inte
import GEmolumentoInterface from "@/packages/administrativo/interfaces/GEmolumento/GEmolumentoInterface";
export default interface TServicoItemPedidoAddInterface {
emolumentoSelecionado: GEmolumentoInterface;
servicoSelecionado: TServicoTipoInterface;
emolumento: GEmolumentoInterface;
servico_tipo: TServicoTipoInterface;
sistema_id?: number;
valor_documento?: number;
quantidade?: number;

View file

@ -5,39 +5,54 @@ export const TServicoPedidoFormSchema = z.object({
.number()
.int()
.positive("Escrevente é obrigatório."),
apresentante: z
.string()
.optional(),
.min(1, "Apresentante é obrigatório."),
cpfcnpj_apresentante: z
.string()
.optional(),
.min(1, "CPF/CNPJ do apresentante é obrigatório."),
selo_pessoa_nome: z
.string()
.optional(),
.min(1, "Nome da pessoa do selo é obrigatório."),
selo_pessoa_cpfcnpj: z
.string()
.optional(),
.min(1, "CPF/CNPJ da pessoa do selo é obrigatório."),
servico_tipo: z
.any()
.refine((v) => !!v, "Selecione um serviço."),
emolumento: z
.any()
.refine((v) => !!v, "Selecione um emolumento."),
itens: z.array(z.any()).optional(),
pagador_nome: z.string().optional(),
pagador_cpfcnpj: z.string().optional(),
itens: z
.array(z.any())
.min(1, "Adicione ao menos um item ao pedido."),
pagador_nome: z
.string()
.min(1, "Nome do requerente é obrigatório."),
pagador_cpfcnpj: z
.string()
.min(1, "CPF/CNPJ do requerente é obrigatório."),
valor_pedido: z
.number()
.nonnegative("Valor do pedido inválido.")
.optional(),
.nonnegative("Valor do pedido inválido."),
valor_pago: z
.number()
.nonnegative("Valor pago inválido.")
.optional(),
.nonnegative("Valor pago inválido."),
tipo_pagamento: z
.any()
.refine((v) => !!v, "Selecione a forma de pagamento.")
.optional(),
.refine((v) => !!v, "Selecione a forma de pagamento."),
});
export type TServicoPedidoFormValues = z.infer<typeof TServicoPedidoFormSchema>;

View file

@ -0,0 +1,25 @@
import { UnmaskCPFCNPJForm } from "./UnmaskCPFCNPJForm";
export function isCpf(value: string): boolean {
return UnmaskCPFCNPJForm(value).length <= 11;
}
export function FormatCPFCNPJForm(value: string = ''): string {
const digits = UnmaskCPFCNPJForm(value);
// CPF — 11 dígitos
if (digits.length <= 11) {
return digits
.replace(/^(\d{3})(\d)/, "$1.$2")
.replace(/^(\d{3})\.(\d{3})(\d)/, "$1.$2.$3")
.replace(/^(\d{3})\.(\d{3})\.(\d{3})(\d{1,2})$/, "$1.$2.$3-$4");
}
// CNPJ — 14 dígitos
return digits
.slice(0, 14)
.replace(/^(\d{2})(\d)/, "$1.$2")
.replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3")
.replace(/^(\d{2})\.(\d{3})\.(\d{3})(\d)/, "$1.$2.$3/$4")
.replace(/^(\d{2})\.(\d{3})\.(\d{3})\/(\d{4})(\d{1,2})$/, "$1.$2.$3/$4-$5");
}

View file

@ -0,0 +1,3 @@
export function UnmaskCPFCNPJForm(value: string = ""): string {
return value.replace(/\D/g, "").slice(0, 11);
}