[MVPTN-86] feat(CRUD): Adicionado recursos dinâmicos de select
This commit is contained in:
parent
6c7967587a
commit
56bca3ba41
23 changed files with 1306 additions and 235 deletions
|
|
@ -1,9 +1,10 @@
|
|||
'use client';
|
||||
|
||||
import React from "react";
|
||||
import z from 'zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -15,7 +16,7 @@ import {
|
|||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { DollarSign, Settings, SquarePen } from 'lucide-react';
|
||||
import { CirclePlus, DollarSign, Settings, SquarePen, Trash } from 'lucide-react';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
|
|
@ -33,49 +34,143 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
|
||||
import { TServicoTipoSchema } from '../../_schemas/TServicoTipoSchema';
|
||||
import { TServicoTipoSchema, TServicoTipoFormValues } from '../../_schemas/TServicoTipoSchema';
|
||||
import { useEffect } from 'react';
|
||||
import GMarcacaoTipoSelect from '@/packages/administrativo/components/GMarcacaoTipo/GMarcacaoTipoSelect';
|
||||
|
||||
// Tipo inferido a partir do schema Zod
|
||||
type FormValues = z.infer<typeof TServicoTipoSchema>;
|
||||
import GEmolumentoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoSelect';
|
||||
import { useGEmolumentoItemReadHook } from "@/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook";
|
||||
import { GEmolumentoItemReadInterface } from "@/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoItemReadInterface";
|
||||
import CategoriaServicoSelect from "@/shared/components/categoriaServicoSelect/CategoriaServicoSelect";
|
||||
import CCaixaServicoSelect from "@/packages/administrativo/components/CCaixaServico/CCaixaServicoSelect";
|
||||
|
||||
// Propriedades esperadas pelo componente
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
data: FormValues | null;
|
||||
data: TServicoTipoFormValues | null;
|
||||
onClose: (item: null, isFormStatus: boolean) => void;
|
||||
onSave: (data: FormValues) => void;
|
||||
onSave: (data: TServicoTipoFormValues) => void;
|
||||
}
|
||||
|
||||
// Componente principal do formulário
|
||||
export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Props) {
|
||||
|
||||
// Estado para gerenciar os itens da tabela de etiquetas/carimbos
|
||||
const [itensTabela, setItensTabela] = useState<{ marcacao_tipo_id: string; descricao: string }[]>([]);
|
||||
|
||||
// Função para adicionar um novo item à tabela
|
||||
const handleAddEtiquetaCarimbo = () => {
|
||||
const valorSelecionado = form.getValues('etiquetas_carimbos');
|
||||
|
||||
if (!valorSelecionado) return;
|
||||
|
||||
// Aqui você pode buscar o texto/descrição com base no seu componente GMarcacaoTipoSelect
|
||||
// Exemplo: supondo que o valor seja um objeto { id, descricao }
|
||||
const item = {
|
||||
|
||||
marcacao_tipo_id: valorSelecionado.key ?? valorSelecionado, // ou ajusta conforme a estrutura do seu componente
|
||||
descricao: valorSelecionado.value ?? 'Sem descrição',
|
||||
};
|
||||
|
||||
// Evita duplicatas
|
||||
setItensTabela((prev) => {
|
||||
if (prev.some((p) => p.marcacao_tipo_id === item.marcacao_tipo_id)) return prev;
|
||||
return [...prev, item];
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveItem = (marcacao_tipo_id: string) => {
|
||||
setItensTabela((prev) => prev.filter((p) => p.marcacao_tipo_id !== marcacao_tipo_id));
|
||||
};
|
||||
|
||||
|
||||
// Parâmetros para o hook de leitura de emolumento_item
|
||||
// Inicializa com valores padrão (0) para evitar uso de propriedades não declaradas.
|
||||
const gEmolumentoItemReadParams: GEmolumentoItemReadInterface = { emolumento_id: 0, valor: 0 };
|
||||
|
||||
// Estados locais
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
|
||||
// Hook que realiza a leitura do emolumento_item
|
||||
const { gGEmolumentoItem, fetchGEmolumentoItem } = useGEmolumentoItemReadHook();
|
||||
|
||||
// Busca os dados ao montar o componente (somente se houver ID válido)
|
||||
React.useEffect(() => {
|
||||
const loadData = async () => {
|
||||
// Validação: só executa se houver emolumento_id informado e válido
|
||||
if (
|
||||
!gEmolumentoItemReadParams?.emolumento_id || // se não existir o campo
|
||||
isNaN(Number(gEmolumentoItemReadParams.emolumento_id)) || // se não for número
|
||||
Number(gEmolumentoItemReadParams.emolumento_id) <= 0 // se for zero ou negativo
|
||||
) {
|
||||
return; // encerra sem executar a busca
|
||||
}
|
||||
|
||||
// Executa a busca apenas se ainda não houver dados carregados
|
||||
if (!gGEmolumentoItem.length) {
|
||||
setIsLoading(true);
|
||||
await fetchGEmolumentoItem(gEmolumentoItemReadParams);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadData();
|
||||
}, [gEmolumentoItemReadParams.emolumento_id]);
|
||||
|
||||
|
||||
// Inicializa o react-hook-form com validação via Zod
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(TServicoTipoSchema),
|
||||
defaultValues: {
|
||||
servico_tipo_id: 0,
|
||||
descricao: '',
|
||||
categoria: '',
|
||||
servico_padrao: '',
|
||||
servico_caixa: '',
|
||||
averbacao: false,
|
||||
transferencia_veiculo: false,
|
||||
usar_a4: false,
|
||||
etiqueta_unica: '',
|
||||
},
|
||||
});
|
||||
const form = useForm<TServicoTipoFormValues>({
|
||||
resolver: zodResolver(TServicoTipoSchema),
|
||||
defaultValues: {
|
||||
servico_tipo_id: 0,
|
||||
descricao: "",
|
||||
categoria: "",
|
||||
frenteverso: "N",
|
||||
averbacao: "N",
|
||||
transferencia_veiculo: "N",
|
||||
usar_a4: "N",
|
||||
etiqueta_unica: "N",
|
||||
situacao: "A",
|
||||
selar: "N",
|
||||
valor_emolumento: 0,
|
||||
valor_taxa_judiciaria: 0,
|
||||
fundesp_valor: 0,
|
||||
valor_total: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// Captura o ID do serviço para uso local
|
||||
const servicoTipoId = Number(form.watch("servico_tipo_id") || data?.servico_tipo_id || 0);
|
||||
|
||||
// Atualiza os valores quando há dados para edição
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
|
||||
if (data && Number(data.servico_tipo_id) > 0) {
|
||||
|
||||
//Carrega os valores no form
|
||||
form.reset(data);
|
||||
} else {
|
||||
form.reset();
|
||||
|
||||
// Se não houver dados, reseta o
|
||||
// formulário para os valores iniciais
|
||||
// Modo novo cadastro → zera o ID manualmente
|
||||
form.reset({
|
||||
...form.getValues(), // mantém outros valores default
|
||||
servico_tipo_id: 0,
|
||||
descricao: '',
|
||||
});
|
||||
}
|
||||
}, [data, form]);
|
||||
}, [data, form]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
|
@ -103,11 +198,19 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
<SquarePen className="me-1 inline" />
|
||||
Dados do Tipo de Serviço
|
||||
</TabsTrigger>
|
||||
<TabsTrigger className="flex-1 text-center cursor-pointer" value="configuracoes">
|
||||
<TabsTrigger
|
||||
className="flex-1 text-center cursor-pointer"
|
||||
value="configuracoes"
|
||||
disabled={servicoTipoId <= 0}
|
||||
>
|
||||
<Settings className="inline" />
|
||||
Configurações
|
||||
</TabsTrigger>
|
||||
<TabsTrigger className="flex-1 text-center cursor-pointer" value="valores">
|
||||
<TabsTrigger
|
||||
className="flex-1 text-center cursor-pointer"
|
||||
value="valores"
|
||||
disabled={servicoTipoId <= 0}
|
||||
>
|
||||
<DollarSign className="inline" />
|
||||
Valores
|
||||
</TabsTrigger>
|
||||
|
|
@ -141,25 +244,11 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
control={form.control}
|
||||
name="categoria"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormItem className="w-full cursor-pointer">
|
||||
<FormLabel>Categoria</FormLabel>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<FormControl className="w-full">
|
||||
<SelectTrigger className="w-full cursor-pointer">
|
||||
<SelectValue placeholder="Selecione uma categoria" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="1" className="cursor-pointer">Autenticação</SelectItem>
|
||||
<SelectItem value="2" className="cursor-pointer">Certidão</SelectItem>
|
||||
<SelectItem value="3" className="cursor-pointer">Serviços Gerais</SelectItem>
|
||||
<SelectItem value="4" className="cursor-pointer">Reconhecimento</SelectItem>
|
||||
<SelectItem value="5" className="cursor-pointer">Geral</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<CategoriaServicoSelect
|
||||
field={field}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
|
@ -198,27 +287,14 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
<div className="col-span-12 sm:col-span-6 md:col-span-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="servico_caixa"
|
||||
name="servico_caixa_id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Serviço Caixa</FormLabel>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<FormControl className="w-full">
|
||||
<SelectTrigger className="w-full cursor-pointer">
|
||||
<SelectValue placeholder="Selecione uma opção" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="1" className="cursor-pointer">Autenticação</SelectItem>
|
||||
<SelectItem value="2" className="cursor-pointer">Reconhecimento</SelectItem>
|
||||
<SelectItem value="3" className="cursor-pointer">Aluguel</SelectItem>
|
||||
<SelectItem value="4" className="cursor-pointer">Papelaria</SelectItem>
|
||||
<SelectItem value="5" className="cursor-pointer">Limpeza Cartório</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<CCaixaServicoSelect
|
||||
sistema_id={2}
|
||||
field={field}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
|
@ -233,9 +309,10 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox className="cursor-pointer"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
<Checkbox
|
||||
className="cursor-pointer"
|
||||
checked={field.value === "S"} // marcado se for "S"
|
||||
onCheckedChange={(checked) => field.onChange(checked ? "S" : "N")} // salva "S" ou "N"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal cursor-pointer">Averbação</FormLabel>
|
||||
|
|
@ -252,9 +329,10 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox className="cursor-pointer"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
<Checkbox
|
||||
className="cursor-pointer"
|
||||
checked={field.value === "S"} // marcado se o valor for "S"
|
||||
onCheckedChange={(checked) => field.onChange(checked ? "S" : "N")} // salva "S" ou "N"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal cursor-pointer">Transferência Veículo</FormLabel>
|
||||
|
|
@ -271,9 +349,10 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox className="cursor-pointer"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
<Checkbox
|
||||
className="cursor-pointer"
|
||||
checked={field.value === "S"} // marcado se o valor for "S"
|
||||
onCheckedChange={(checked) => field.onChange(checked ? "S" : "N")} // grava "S" ou "N"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal cursor-pointer">Usar A4</FormLabel>
|
||||
|
|
@ -290,9 +369,10 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox className="cursor-pointer"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
<Checkbox
|
||||
className="cursor-pointer"
|
||||
checked={field.value === "S"} // marcado se o valor for "S"
|
||||
onCheckedChange={(checked) => field.onChange(checked ? "S" : "N")} // grava "S" ou "N"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal cursor-pointer">Etiqueta Única</FormLabel>
|
||||
|
|
@ -300,6 +380,63 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Campo: Frente/Verso (Texto normal) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="frenteverso"
|
||||
render={({ field }) => {
|
||||
const categoriaSelecionada = form.watch("categoria");
|
||||
const isEnabled = categoriaSelecionada === "A";
|
||||
|
||||
return (
|
||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
className="cursor-pointer"
|
||||
checked={field.value === "S"} // marca quando o valor for "S"
|
||||
onCheckedChange={(checked) => field.onChange(checked ? "S" : "N")} // grava "S" ou "N"
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal cursor-pointer">Frente/Verso</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Campo: Etiqueta Única (Texto normal) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="situacao"
|
||||
render={({ field }) => {
|
||||
// Considera "A" ou vazio como marcado
|
||||
const isChecked = field.value === "A" || !field.value;
|
||||
|
||||
return (
|
||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
className="cursor-pointer"
|
||||
checked={isChecked}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange(checked ? "A" : "I"); // grava "A" ou "I" no form
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal cursor-pointer">Ativo</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
|
|
@ -408,7 +545,7 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-12">Requerer</div>
|
||||
<div className="col-span-12 underline">Requerer:</div>
|
||||
|
||||
{/* Campo: Biometria (Select) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||
|
|
@ -526,30 +663,283 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
|
||||
|
||||
{/* Campo: etiquetas/carimbos (Select) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-12">
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-10">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="etiquetas_carimbos"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Etiquetas/Carimbos</FormLabel>
|
||||
<GMarcacaoTipoSelect
|
||||
grupo="MODELO_ETIQUETA"
|
||||
sistema_id={2}
|
||||
situacao="A"
|
||||
field={field}
|
||||
/>
|
||||
<GMarcacaoTipoSelect
|
||||
grupo="MODELO_ETIQUETA"
|
||||
sistema_id={2}
|
||||
situacao="A"
|
||||
field={field}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Campo: etiquetas/carimbos botão adicionar */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-2">
|
||||
<FormItem className="w-full">
|
||||
<FormLabel> </FormLabel>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full cursor-pointer"
|
||||
type="button"
|
||||
onClick={handleAddEtiquetaCarimbo}
|
||||
>
|
||||
<CirclePlus /> Adicionar
|
||||
</Button>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Campo: etiquetas/carimbos (Select) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-12">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[90px] text-center">#</TableHead>
|
||||
<TableHead>Descrição</TableHead>
|
||||
<TableHead className="text-right"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{itensTabela.map((item, index) => (
|
||||
<TableRow key={item.marcacao_tipo_id}>
|
||||
<TableCell className="w-[90px] text-center">
|
||||
{String(index + 1).padStart(3, '0')}
|
||||
</TableCell>
|
||||
<TableCell>{item.descricao}</TableCell>
|
||||
<TableCell className="w-[100px]">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full cursor-pointer"
|
||||
type="button"
|
||||
onClick={() => handleRemoveItem(item.marcacao_tipo_id)}
|
||||
>
|
||||
<Trash /> Remover
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
|
||||
{itensTabela.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="text-center text-gray-500">
|
||||
Nenhum item adicionado
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="valores" className="space-y-4">
|
||||
003
|
||||
|
||||
<div className="grid w-full grid-cols-12 gap-4 border p-4 rounded-md">
|
||||
|
||||
{/* Campo: Biometria (Select) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="selar"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Selar</FormLabel>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<FormControl className="w-full">
|
||||
<SelectTrigger className="w-full cursor-pointer">
|
||||
<SelectValue placeholder="Selecione uma opção" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="S" className="cursor-pointer">Sim</SelectItem>
|
||||
<SelectItem value="N" className="cursor-pointer">Não</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Campo: emolumentos (Select) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-10">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="emolumento"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Emolumento</FormLabel>
|
||||
<GEmolumentoSelect
|
||||
className="w-full"
|
||||
sistema_id={2}
|
||||
field={field}
|
||||
onSelectChange={async (selected) => {
|
||||
// Extrai o ID do emolumento selecionado
|
||||
const emolumentoId = Number(selected?.key);
|
||||
|
||||
// Se o ID for inválido, encerra sem fazer a requisição
|
||||
if (!emolumentoId || isNaN(emolumentoId) || emolumentoId <= 0) return;
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
// Faz a requisição para buscar o item do emolumento
|
||||
const response = await fetchGEmolumentoItem({
|
||||
emolumento_id: emolumentoId,
|
||||
valor: 0,
|
||||
});
|
||||
|
||||
// Extrai o primeiro item do array "data"
|
||||
const item = response?.data?.[0];
|
||||
|
||||
// Se existir item, atualiza os campos do formulário
|
||||
if (item) {
|
||||
form.setValue("valor_emolumento", item.valor_emolumento ?? 0);
|
||||
form.setValue("valor_taxa_judiciaria", item.valor_taxa_judiciaria ?? 0);
|
||||
// form.setValue("fundesp_valor", item.valor_outra_taxa1 ?? 0);
|
||||
// form.setValue("valor_pagina_extra", item.valor_pagina_extra ?? 0);
|
||||
// form.setValue("emolumento_item_id", item.emolumento_item_id ?? 0);
|
||||
|
||||
// (💡 opcional) Atualiza o total automaticamente
|
||||
const total =
|
||||
(item.valor_emolumento ?? 0) +
|
||||
(item.valor_taxa_judiciaria ?? 0) +
|
||||
(item.valor_outra_taxa1 ?? 0);
|
||||
|
||||
form.setValue("valor_total", total);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar item de emolumento:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{/* Campo: emolumentos adicional (Select) */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-12">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="emolumento_auxiliar"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Emolumento Adicional (Mesma etiqueta)</FormLabel>
|
||||
<GEmolumentoSelect
|
||||
className="w-full"
|
||||
sistema_id={2}
|
||||
field={field}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Campo: Quantidade de pessoas */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="valor_emolumento"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Emolumento R$</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value ?? ''}
|
||||
disabled
|
||||
placeholder="0,00"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Campo: Quantidade de pessoas */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="valor_taxa_judiciaria"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Taxa Judiciária R$</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value ?? ''}
|
||||
disabled
|
||||
placeholder="0,00"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Campo: Quantidade de pessoas */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="fundesp_valor"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Fundesp R$</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value ?? ''}
|
||||
disabled
|
||||
placeholder="0,00"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Campo: Quantidade de pessoas */}
|
||||
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="valor_total"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Total R$</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value ?? ''} placeholder="0,00" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
{/* Campo oculto: ID */}
|
||||
|
|
@ -559,11 +949,19 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
|
|||
{/* Rodapé do diálogo */}
|
||||
<DialogFooter className="mt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline" type="button" onClick={() => onClose(null, false)}>
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={() => onClose(null, false)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button type="submit">Salvar</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
className="cursor-pointer"
|
||||
>Salvar</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// Importa o utilitário responsável por tratar erros de forma padronizada no cliente
|
||||
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
|
||||
|
||||
// Importa a classe de serviço que gerencia requisições HTTP para a API
|
||||
import API from '@/shared/services/api/Api';
|
||||
|
||||
// Importa o enum que define os métodos HTTP disponíveis (GET, POST, PUT, DELETE, etc.)
|
||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
||||
import { CCaixaServicoReadInterface } from '../../_interfaces/CCaixaServicoReadInterface';
|
||||
|
||||
// Função assíncrona responsável por executar a requisição para listar os tipos de marcação
|
||||
async function executeCCaixaServicoIndexData(data: CCaixaServicoReadInterface) {
|
||||
// Cria uma nova instância da classe API para enviar a requisição
|
||||
const api = new API();
|
||||
|
||||
// Concatena o endpoint com a query string (caso existam parâmetros)
|
||||
const endpoint = `administrativo/c_caixa_servico/sistema/${data.sistema_id}`;
|
||||
|
||||
// Envia uma requisição GET para o endpoint 'administrativo/g_marcacao_tipo/'
|
||||
return await api.send({
|
||||
method: Methods.GET,
|
||||
endpoint: endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
// Exporta a função encapsulada pelo handler de erro, garantindo tratamento uniforme em caso de falhas
|
||||
export const CCaixaServicoIndexData = withClientErrorHandler(executeCCaixaServicoIndexData);
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// Importa o utilitário responsável por tratar erros de forma padronizada no cliente
|
||||
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
|
||||
|
||||
// Importa a classe de serviço que gerencia requisições HTTP para a API
|
||||
import API from '@/shared/services/api/Api';
|
||||
|
||||
// Importa o enum que define os métodos HTTP disponíveis (GET, POST, PUT, DELETE, etc.)
|
||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
||||
import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInterface';
|
||||
|
||||
// Função assíncrona responsável por executar a requisição para listar os tipos de marcação
|
||||
async function executeGEmolumentoIndexData(data: GEmolumentoReadInterface) {
|
||||
// Cria uma nova instância da classe API para enviar a requisição
|
||||
const api = new API();
|
||||
|
||||
// Concatena o endpoint com a query string (caso existam parâmetros)
|
||||
const endpoint = `administrativo/g_emolumento/${data.sistema_id}`;
|
||||
|
||||
// Envia uma requisição GET para o endpoint 'administrativo/g_marcacao_tipo/'
|
||||
return await api.send({
|
||||
method: Methods.GET,
|
||||
endpoint: endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
// Exporta a função encapsulada pelo handler de erro, garantindo tratamento uniforme em caso de falhas
|
||||
export const GEmolumentoIndexData = withClientErrorHandler(executeGEmolumentoIndexData);
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// Importa o utilitário responsável por tratar erros de forma padronizada no cliente
|
||||
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
|
||||
|
||||
// Importa a classe de serviço que gerencia requisições HTTP para a API
|
||||
import API from '@/shared/services/api/Api';
|
||||
|
||||
// Importa o enum que define os métodos HTTP disponíveis (GET, POST, PUT, DELETE, etc.)
|
||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
||||
import { GEmolumentoItemReadInterface } from '../../_interfaces/GEmolumentoItemReadInterface';
|
||||
|
||||
// Função assíncrona responsável por executar a requisição para listar os tipos de marcação
|
||||
async function executeGEmolumentoItemValorData(data: GEmolumentoItemReadInterface) {
|
||||
// Cria uma nova instância da classe API para enviar a requisição
|
||||
const api = new API();
|
||||
|
||||
// Concatena o endpoint com a query string (caso existam parâmetros)
|
||||
const endpoint = `administrativo/g_emolumento_item/faixa/${data.emolumento_id}/${data.valor}`;
|
||||
|
||||
// Envia uma requisição GET para o endpoint 'administrativo/g_marcacao_tipo/'
|
||||
return await api.send({
|
||||
method: Methods.GET,
|
||||
endpoint: endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
// Exporta a função encapsulada pelo handler de erro, garantindo tratamento uniforme em caso de falhas
|
||||
export const GEmolumentoItemValorData = withClientErrorHandler(executeGEmolumentoItemValorData);
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Importa o hook responsável por gerenciar e exibir respostas globais (sucesso, erro, etc.)
|
||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||
|
||||
// Importa hooks do React para gerenciamento de estado e memorização de valores
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
// Importa a interface que define a estrutura dos dados de "CCaixaServico"
|
||||
import { CCaixaServicoReadInterface } from '../../_interfaces/CCaixaServicoReadInterface';
|
||||
|
||||
// Importa o serviço responsável por buscar os dados de "CCaixaServico" na API
|
||||
import { CCaixaServicoIndexService } from '../../_services/c_caixa_servico/CCaixaServicoIndexService';
|
||||
import { CCaixaServicoInterface } from '../../_interfaces/CCaixaServicoInterface';
|
||||
|
||||
|
||||
// Hook personalizado para leitura (consulta) dos tipos de marcação
|
||||
export const useCCaixaServicoReadHook = () => {
|
||||
// Obtém a função que atualiza a resposta global do sistema
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
// Define o estado local que armazenará a lista de tipos de marcação
|
||||
const [cCaixaServico, setCCaixaServico] = useState<CCaixaServicoInterface[]>([]);
|
||||
|
||||
// Função responsável por buscar os dados da API e atualizar o estado
|
||||
const fetchCCaixaServico = async (data: CCaixaServicoReadInterface) => {
|
||||
// Executa o serviço que faz a requisição à API
|
||||
const response = await CCaixaServicoIndexService(data);
|
||||
|
||||
// Atualiza o estado local com os dados retornados
|
||||
setCCaixaServico(response.data);
|
||||
|
||||
// Atualiza o contexto global de resposta (ex: para exibir alertas ou mensagens)
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
// Retorna os dados e a função de busca, memorizando o valor para evitar recriações desnecessárias
|
||||
return useMemo(() => ({ cCaixaServico, fetchCCaixaServico }), [cCaixaServico, fetchCCaixaServico]);
|
||||
};
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Importa o hook responsável por gerenciar e exibir respostas globais (sucesso, erro, etc.)
|
||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||
|
||||
// Importa hooks do React para gerenciamento de estado e memorização de valores
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
// Importa a interface que define a estrutura dos dados de "GEmolumento"
|
||||
import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInterface';
|
||||
|
||||
// Importa o serviço responsável por buscar os dados de "GEmolumento" na API
|
||||
import { GEmolumentoIndexService } from '../../_services/g_emolumento/GEmolumentoIndexService';
|
||||
import { GEmolumentoInterface } from '../../_interfaces/GEmolumentoInterface';
|
||||
|
||||
|
||||
// Hook personalizado para leitura (consulta) dos emolumentos
|
||||
export const useGEmolumentoReadHook = () => {
|
||||
// Obtém a função que atualiza a resposta global do sistema
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
// Define o estado local que armazenará a lista de emolumentos
|
||||
const [gEmolumento, setGEmolumento] = useState<GEmolumentoInterface[]>([]);
|
||||
|
||||
// Função responsável por buscar os dados da API e atualizar o estado
|
||||
const fetchGEmolumento = async (data: GEmolumentoReadInterface) => {
|
||||
// Executa o serviço que faz a requisição à API
|
||||
const response = await GEmolumentoIndexService(data);
|
||||
|
||||
// Atualiza o estado local com os dados retornados
|
||||
setGEmolumento(response.data);
|
||||
|
||||
// Atualiza o contexto global de resposta (ex: para exibir alertas ou mensagens)
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
// Retorna os dados e a função de busca, memorizando o valor para evitar recriações desnecessárias
|
||||
return useMemo(() => ({ gEmolumento, fetchGEmolumento }), [gEmolumento, fetchGEmolumento]);
|
||||
};
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Importa o hook responsável por gerenciar e exibir respostas globais (sucesso, erro, etc.)
|
||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||
|
||||
// Importa hooks do React para gerenciamento de estado e memorização de valores
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
// Importa a interface que define a estrutura dos dados de "gEmolumentoItem"
|
||||
import { GEmolumentoItemReadInterface } from '../../_interfaces/GEmolumentoItemReadInterface';
|
||||
|
||||
// Importa o serviço responsável por buscar os dados de "GEmolumentoItem" na API
|
||||
import { GEmolumentoItemValorService } from '../../_services/g_emolumento_item/GEmolumentoItemValorService';
|
||||
import { GEmolumentoItemInterface } from '../../_interfaces/GEmolumentoItemInterface';
|
||||
|
||||
|
||||
// Hook personalizado para leitura (consulta) dos emolumentos
|
||||
export const useGEmolumentoItemReadHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
const [gGEmolumentoItem, setGEmolumentoItem] = useState<GEmolumentoItemInterface[]>([]);
|
||||
|
||||
const fetchGEmolumentoItem = async (data: GEmolumentoItemReadInterface) => {
|
||||
try {
|
||||
const response = await GEmolumentoItemValorService(data);
|
||||
|
||||
// Atualiza estado e contexto
|
||||
setGEmolumentoItem(response.data);
|
||||
setResponse(response);
|
||||
|
||||
// Retorna a resposta completa (para uso externo)
|
||||
return response;
|
||||
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar item de emolumento:", error);
|
||||
setResponse({
|
||||
message: "Erro ao buscar item de emolumento",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return null; // Retorna nulo para segurança
|
||||
}
|
||||
};
|
||||
|
||||
// Retorna função e dados memorizados
|
||||
return useMemo(
|
||||
() => ({ gGEmolumentoItem, fetchGEmolumentoItem }),
|
||||
[gGEmolumentoItem]
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Interface que representa a tabela C_CAIXA_SERVICO
|
||||
export interface CCaixaServicoInterface {
|
||||
interno_sistema?: string; // VARCHAR(1)
|
||||
caixa_servico_id: number; // NUMERIC(10,2) NOT NULL - Chave primária
|
||||
descricao?: string; // VARCHAR(60)
|
||||
situacao?: string; // VARCHAR(1)
|
||||
tipo_transacao?: string; // VARCHAR(1)
|
||||
sistema_id?: number; // NUMERIC(14,3)
|
||||
selo_grupo_id?: number; // NUMERIC(10,2)
|
||||
emitir_relatorio?: string; // VARCHAR(1)
|
||||
repasse?: string; // VARCHAR(1)
|
||||
repetir_descricao?: string; // VARCHAR(1)
|
||||
codigo_conta?: number; // NUMERIC(10,2)
|
||||
tipo_conta_carneleao?: string; // VARCHAR(60)
|
||||
centro_de_custa_id?: number; // NUMERIC(10,2) - Chave estrangeira
|
||||
devolucao_juizo?: string; // VARCHAR(1)
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export interface CCaixaServicoReadInterface {
|
||||
sistema_id?: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Interface que representa a tabela G_EMOLUMENTO
|
||||
export interface GEmolumentoInterface {
|
||||
emolumento_id?: number; // NUMERIC(10,2) - Chave primária
|
||||
descricao?: string; // VARCHAR(260)
|
||||
tipo?: string; // VARCHAR(1)
|
||||
sistema_id?: number; // NUMERIC(10,2)
|
||||
selo_grupo_id?: number; // NUMERIC(10,2)
|
||||
reg_averb?: string; // VARCHAR(1)
|
||||
pre_definido?: string; // VARCHAR(1)
|
||||
situacao?: string; // VARCHAR(1)
|
||||
situacao_ri?: string; // VARCHAR(1)
|
||||
com_reducao?: string; // VARCHAR(1)
|
||||
motivo_reducao?: string; // VARCHAR(120)
|
||||
valor_maximo_certidao?: number; // NUMERIC(14,3)
|
||||
tipo_objetivo?: string; // VARCHAR(3)
|
||||
modelo_tag?: string; // VARCHAR(3)
|
||||
codigo_nota_id?: number; // NUMERIC(10,2)
|
||||
convenio_codhab?: string; // VARCHAR(1)
|
||||
item_df?: string; // VARCHAR(10)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Interface que representa a tabela G_EMOLUMENTO_ITEM (inferido)
|
||||
export interface GEmolumentoItemInterface {
|
||||
valor_emolumento?: number; // NUMERIC(14,3)
|
||||
emolumento_item_id: number; // NUMERIC(10,2) NOT NULL - Chave primária (assumida)
|
||||
emolumento_id?: number; // NUMERIC(10,2)
|
||||
valor_inicio?: number; // NUMERIC(14,3)
|
||||
valor_fim?: number; // NUMERIC(14,3)
|
||||
valor_taxa_judiciaria?: number; // NUMERIC(14,3)
|
||||
emolumento_periodo_id?: number; // NUMERIC(10,2)
|
||||
codigo?: number; // NUMERIC(10,2)
|
||||
pagina_extra?: number; // NUMERIC(10,2)
|
||||
valor_pagina_extra?: number; // NUMERIC(14,3)
|
||||
valor_outra_taxa1?: number; // NUMERIC(14,3)
|
||||
codigo_selo?: string; // VARCHAR(30)
|
||||
valor_fundo_ri?: number; // NUMERIC(14,3)
|
||||
codigo_tabela?: string; // VARCHAR(30)
|
||||
selo_grupo_id?: number; // NUMERIC(10,2)
|
||||
codigo_km?: string; // VARCHAR(30)
|
||||
emolumento_acresce?: number; // NUMERIC(14,3)
|
||||
taxa_acresce?: number; // NUMERIC(14,3)
|
||||
funcivil_acresce?: number; // NUMERIC(14,3)
|
||||
valor_fracao?: number; // NUMERIC(14,3)
|
||||
valor_por_excedente_emol?: number; // NUMERIC(14,3)
|
||||
valor_por_excedente_tj?: number; // NUMERIC(14,3)
|
||||
valor_por_excedente_fundo?: number; // NUMERIC(14,3)
|
||||
valor_limite_excedente_emol?: number; // NUMERIC(14,3)
|
||||
valor_limite_excedente_tj?: number; // NUMERIC(14,3)
|
||||
valor_limite_excedente_fundo?: number; // NUMERIC(14,3)
|
||||
fundo_selo?: number; // NUMERIC(14,3)
|
||||
distribuicao?: number; // NUMERIC(14,3)
|
||||
vrc_ext?: number; // NUMERIC(10,2) - Renomeado de VRCEXT para vrc_ext (convenção)
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export interface GEmolumentoItemReadInterface {
|
||||
emolumento_id?: number,
|
||||
valor?: number
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export interface GEmolumentoReadInterface {
|
||||
sistema_id?: number
|
||||
}
|
||||
|
|
@ -1,122 +1,78 @@
|
|||
import z from 'zod';
|
||||
import z from "zod";
|
||||
|
||||
// Define um esquema para campos de 1 caractere (sim/não ou tipo)
|
||||
// Poderia ser um z.enum(['S', 'N']) mais restritivo dependendo da regra de negócio,
|
||||
// mas seguindo o padrão da DDL (VARCHAR(1)), o string.max(1) é mais flexível.
|
||||
const OneCharString = z.string().max(1, 'Deve ter no máximo 1 caractere').optional();
|
||||
|
||||
// Define um esquema para campos string obrigatórios
|
||||
const RequiredString = z.string().min(1, 'O campo é obrigatório');
|
||||
|
||||
// Define um esquema para campos numéricos opcionais, baseados em NUMERIC(10,2) ou similar
|
||||
/**
|
||||
* Tipos utilitários para campos simples
|
||||
*/
|
||||
const SN = z.enum(["S", "N"]).default("N"); // Campos do tipo Sim/Não
|
||||
const AI = z.enum(["A", "I"]).default("A"); // Situação Ativo/Inativo
|
||||
const OneCharString = z.string().max(1, "Deve ter no máximo 1 caractere").optional();
|
||||
const RequiredString = z.string().min(1, "O campo é obrigatório");
|
||||
const OptionalNumber = z.number().optional();
|
||||
|
||||
// Define um esquema para campos numéricos obrigatórios
|
||||
const RequiredNumber = z.number();
|
||||
|
||||
/**
|
||||
* Schema principal baseado na DDL e adaptado ao formulário React
|
||||
*/
|
||||
export const TServicoTipoSchema = z.object({
|
||||
// Chave primária: NOT NULL na DDL
|
||||
servico_tipo_id: RequiredNumber.describe('ID do Tipo de Serviço').optional(), // Mantido optional seguindo o padrão de GCidadeSchema para chaves ID
|
||||
|
||||
// VARCHAR(60)
|
||||
descricao: z.string().max(60, 'A descrição deve ter no máximo 60 caracteres').optional(),
|
||||
|
||||
// NUMERIC(14,3)
|
||||
valor: z.number().optional(),
|
||||
|
||||
// VARCHAR(1)
|
||||
tipo_item: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
requer_autorizacao: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
requer_biometria: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
tipo_pessoa: OneCharString,
|
||||
|
||||
// NUMERIC(10,2) - Chave estrangeira
|
||||
tb_reconhecimentotipo_id: OptionalNumber,
|
||||
|
||||
// VARCHAR(1)
|
||||
tipo_permissao_cpf: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
requer_abonador: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
requer_representante: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
situacao: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
requer_cpf: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
servico_padrao: OneCharString,
|
||||
|
||||
// NUMERIC(10,2)
|
||||
// Identificador
|
||||
servico_tipo_id: RequiredNumber.describe("ID do Tipo de Serviço").optional(),
|
||||
|
||||
// Campos principais
|
||||
descricao: z.string().max(60, "A descrição deve ter no máximo 60 caracteres").optional(),
|
||||
categoria: z.string().optional(),
|
||||
|
||||
// Controle de flags (S/N)
|
||||
frenteverso: SN.optional(),
|
||||
averbacao: SN.optional(),
|
||||
transferencia_veiculo: SN.optional(),
|
||||
usar_a4: SN.optional(),
|
||||
etiqueta_unica: SN.optional(),
|
||||
selar: SN.optional(),
|
||||
servico_padrao: SN.optional(),
|
||||
lancar_taxa: SN.optional(),
|
||||
lancar_fundesp: SN.optional(),
|
||||
liberar_desconto: SN.optional(),
|
||||
fundesp_automatica: SN.optional(),
|
||||
lancar_valor_documento: SN.optional(),
|
||||
valor_fixo: SN.optional(),
|
||||
ato_praticado: SN.optional(),
|
||||
apresentante_selo: SN.optional(),
|
||||
renovacao_cartao: SN.optional(),
|
||||
|
||||
// Situação
|
||||
situacao: AI,
|
||||
|
||||
// Campos numéricos
|
||||
valor: OptionalNumber,
|
||||
maximo_pessoa: OptionalNumber,
|
||||
|
||||
// VARCHAR(1)
|
||||
alterar_valor: OneCharString,
|
||||
|
||||
// NUMERIC(10,2)
|
||||
servico_caixa_id: OptionalNumber,
|
||||
|
||||
// VARCHAR(1)
|
||||
lancar_taxa: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
lancar_fundesp: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
liberar_desconto: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
fundesp_automatica: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
lancar_valor_documento: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
valor_fixo: OneCharString,
|
||||
|
||||
// NUMERIC(10,2) - Chave estrangeira
|
||||
emolumento_id: OptionalNumber,
|
||||
|
||||
// VARCHAR(1)
|
||||
ato_praticado: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
selar: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
frenteverso: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
pagina_acrescida: OneCharString,
|
||||
|
||||
// NUMERIC(10,2)
|
||||
emolumento_obrigatorio: OptionalNumber,
|
||||
|
||||
// VARCHAR(1)
|
||||
apresentante_selo: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
renovacao_cartao: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
etiqueta_unica: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
transferencia_veiculo: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
usar_a4: OneCharString,
|
||||
|
||||
// VARCHAR(1)
|
||||
averbacao: OneCharString,
|
||||
});
|
||||
|
||||
// Relacionamentos e permissões
|
||||
tipo_item: OneCharString,
|
||||
requer_autorizacao: OneCharString,
|
||||
requer_biometria: OneCharString,
|
||||
tipo_pessoa: OneCharString,
|
||||
tb_reconhecimentotipo_id: OptionalNumber,
|
||||
tipo_permissao_cpf: OneCharString,
|
||||
requer_abonador: OneCharString,
|
||||
requer_representante: OneCharString,
|
||||
requer_cpf: OneCharString,
|
||||
alterar_valor: OneCharString,
|
||||
pagina_acrescida: OneCharString,
|
||||
|
||||
// Campos auxiliares usados apenas no formulário (não persistidos)
|
||||
valor_emolumento: z.number().optional(),
|
||||
valor_taxa_judiciaria: z.number().optional(),
|
||||
fundesp_valor: z.number().optional(),
|
||||
valor_total: z.number().optional(),
|
||||
etiquetas_carimbos: z.any().optional(),
|
||||
emolumento: z.any().optional(),
|
||||
emolumento_auxiliar: z.any().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Tipo inferido do schema — usado diretamente no useForm
|
||||
*/
|
||||
export type TServicoTipoFormValues = z.infer<typeof TServicoTipoSchema>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
// Importa o utilitário responsável por lidar com erros de forma padronizada no cliente
|
||||
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
|
||||
|
||||
// Importa a função que realiza a requisição de listagem dos tipos de marcação
|
||||
import { CCaixaServicoIndexData } from '../../_data/CCaixaServico/CCaixaServicoIndexData';
|
||||
import { CCaixaServicoReadInterface } from '../../_interfaces/CCaixaServicoReadInterface';
|
||||
|
||||
// Função assíncrona responsável por executar o serviço de listagem de tipos de marcação
|
||||
async function executeCCaixaServicoIndexService(data: CCaixaServicoReadInterface) {
|
||||
// Chama a função que realiza a requisição à API e aguarda a resposta
|
||||
const response = await CCaixaServicoIndexData(data);
|
||||
|
||||
// Retorna a resposta obtida da requisição
|
||||
return response;
|
||||
}
|
||||
|
||||
// Exporta o serviço encapsulado pelo handler de erro, garantindo tratamento uniforme em caso de falhas
|
||||
export const CCaixaServicoIndexService = withClientErrorHandler(
|
||||
executeCCaixaServicoIndexService,
|
||||
);
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Importa o utilitário responsável por lidar com erros de forma padronizada no cliente
|
||||
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
|
||||
|
||||
// Importa a função que realiza a requisição de listagem dos tipos de marcação
|
||||
import { GEmolumentoIndexData } from '../../_data/GEmolumento/GEmolumentoIndexData';
|
||||
import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInterface';
|
||||
|
||||
// Função assíncrona responsável por executar o serviço de listagem de tipos de marcação
|
||||
async function executeGEmolumentoIndexService(data: GEmolumentoReadInterface) {
|
||||
// Chama a função que realiza a requisição à API e aguarda a resposta
|
||||
const response = await GEmolumentoIndexData(data);
|
||||
|
||||
// Retorna a resposta obtida da requisição
|
||||
return response;
|
||||
}
|
||||
|
||||
// Exporta o serviço encapsulado pelo handler de erro, garantindo tratamento uniforme em caso de falhas
|
||||
export const GEmolumentoIndexService = withClientErrorHandler(
|
||||
executeGEmolumentoIndexService,
|
||||
);
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Importa o utilitário responsável por lidar com erros de forma padronizada no cliente
|
||||
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
|
||||
|
||||
// Importa a função que realiza a requisição de listagem dos tipos de marcação
|
||||
import { GEmolumentoItemValorData } from '../../_data/GEmolumentoItem/GEmolumentoItemValorData';
|
||||
import { GEmolumentoItemReadInterface } from '../../_interfaces/GEmolumentoItemReadInterface';
|
||||
|
||||
// Função assíncrona responsável por executar o serviço de listagem de tipos de marcação
|
||||
async function executeGEmolumentoItemValorService(data: GEmolumentoItemReadInterface) {
|
||||
// Chama a função que realiza a requisição à API e aguarda a resposta
|
||||
const response = await GEmolumentoItemValorData(data);
|
||||
|
||||
// Retorna a resposta obtida da requisição
|
||||
return response;
|
||||
}
|
||||
|
||||
// Exporta o serviço encapsulado pelo handler de erro, garantindo tratamento uniforme em caso de falhas
|
||||
export const GEmolumentoItemValorService = withClientErrorHandler(
|
||||
executeGEmolumentoItemValorService,
|
||||
);
|
||||
|
|
@ -1,92 +1,116 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import * as React from 'react';
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Table({ className, ...props }: React.ComponentProps<'table'>) {
|
||||
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||
return (
|
||||
<div data-slot="table-container" className="relative w-full overflow-x-auto">
|
||||
<div
|
||||
data-slot="table-container"
|
||||
className="relative w-full overflow-x-auto"
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
className={cn('w-full caption-bottom text-sm', className)}
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
|
||||
return <thead data-slot="table-header" className={cn('[&_tr]:border-b', className)} {...props} />;
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
className={cn("[&_tr]:border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
|
||||
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
||||
return (
|
||||
<tbody
|
||||
data-slot="table-body"
|
||||
className={cn('[&_tr:last-child]:border-0', className)}
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
||||
return (
|
||||
<tfoot
|
||||
data-slot="table-footer"
|
||||
className={cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', className)}
|
||||
className={cn(
|
||||
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
|
||||
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||
return (
|
||||
<tr
|
||||
data-slot="table-row"
|
||||
className={cn(
|
||||
'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
|
||||
className,
|
||||
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
|
||||
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||
return (
|
||||
<th
|
||||
data-slot="table-head"
|
||||
className={cn(
|
||||
'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
className,
|
||||
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
|
||||
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||
return (
|
||||
<td
|
||||
data-slot="table-cell"
|
||||
className={cn(
|
||||
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
className,
|
||||
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function TableCaption({ className, ...props }: React.ComponentProps<'caption'>) {
|
||||
function TableCaption({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"caption">) {
|
||||
return (
|
||||
<caption
|
||||
data-slot="table-caption"
|
||||
className={cn('text-muted-foreground mt-4 text-sm', className)}
|
||||
className={cn("text-muted-foreground mt-4 text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
'use client';
|
||||
|
||||
import React from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command";
|
||||
import { FormControl } from "@/components/ui/form";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
|
||||
import GetCapitalize from "@/shared/actions/text/GetCapitalize";
|
||||
import { useCCaixaServicoReadHook } from "@/app/(protected)/(cadastros)/cadastros/_hooks/c_caixa_servico/useCCaixaServicoReadHook";
|
||||
import { CCaixaServicoReadInterface } from "@/app/(protected)/(cadastros)/cadastros/_interfaces/CCaixaServicoReadInterface";
|
||||
|
||||
export default function CCaixaServicoSelect({ sistema_id, field }: any) {
|
||||
|
||||
const cCaixaServicoReadParams: CCaixaServicoReadInterface = { sistema_id };
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const { cCaixaServico, fetchCCaixaServico } = useCCaixaServicoReadHook();
|
||||
// Busca os dados uma única vez ao montar
|
||||
React.useEffect(() => {
|
||||
const loadData = async () => {
|
||||
if (!cCaixaServico.length) {
|
||||
setIsLoading(true);
|
||||
await fetchCCaixaServico(cCaixaServicoReadParams);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
loadData();
|
||||
}, []);
|
||||
const selected = cCaixaServico?.find(
|
||||
(item) => String(item.caixa_servico_id) === String(field.value)
|
||||
);
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl className="w-full">
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
disabled={isLoading}
|
||||
className="justify-between cursor-pointer"
|
||||
>
|
||||
{isLoading
|
||||
? "Carregando..."
|
||||
: field.value && typeof field.value === 'object' && field.value.value
|
||||
? GetCapitalize(field.value.value) // Exibe a descrição do objeto
|
||||
: field.value && typeof field.value !== 'object'
|
||||
? field.value // Se for um ID (valor antigo), exibe ele
|
||||
: "Selecione o serviço"}
|
||||
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Buscar etiquetas/carimbos..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>
|
||||
{isLoading ? "Carregando..." : "Nenhum resultado encontrado."}
|
||||
</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{cCaixaServico?.map((item) => (
|
||||
<CommandItem
|
||||
className="cursor-pointer"
|
||||
key={item.caixa_servico_id}
|
||||
value={item.descricao?.toLowerCase() ?? ""}
|
||||
onSelect={() => {
|
||||
field.onChange({
|
||||
key: Number(item.caixa_servico_id),
|
||||
value: item.descricao,
|
||||
});
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
String(field.value) === String(item.caixa_servico_id)
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{GetCapitalize(item.descricao)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
'use client'; // Garante execução no cliente (Next.js App Router)
|
||||
|
||||
import React from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command";
|
||||
import { FormControl } from "@/components/ui/form";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
|
||||
import GetCapitalize from "@/shared/actions/text/GetCapitalize";
|
||||
import { useGEmolumentoReadHook } from "@/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook";
|
||||
import { GEmolumentoReadInterface } from "@/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoReadInterface";
|
||||
|
||||
|
||||
// Tipagem das props do componente
|
||||
interface GEmolumentoSelectProps {
|
||||
sistema_id: number; // ID do sistema usado para buscar os emolumentos
|
||||
field: any; // Objeto de controle do react-hook-form
|
||||
onSelectChange?: (emolumento: { key: number; value: string }) => void; // Função callback opcional para disparar eventos externos
|
||||
className?: string; // Classe CSS opcional para customização
|
||||
}
|
||||
|
||||
|
||||
// Componente principal do select de emolumentos
|
||||
export default function GEmolumentoSelect({ sistema_id, field, onSelectChange, className }: GEmolumentoSelectProps) {
|
||||
|
||||
// Define parâmetros de leitura para o hook que busca os emolumentos
|
||||
const gEmolumentoReadParams: GEmolumentoReadInterface = { sistema_id };
|
||||
|
||||
// Estados locais do componente
|
||||
const [open, setOpen] = React.useState(false); // Controla abertura do popover
|
||||
const [isLoading, setIsLoading] = React.useState(false); // Exibe “Carregando...” enquanto busca dados
|
||||
|
||||
// Hook responsável por buscar emolumentos no backend
|
||||
const { gEmolumento, fetchGEmolumento } = useGEmolumentoReadHook();
|
||||
|
||||
// Carrega os dados de emolumentos apenas uma vez ao montar o componente
|
||||
React.useEffect(() => {
|
||||
const loadData = async () => {
|
||||
if (!gEmolumento.length) {
|
||||
setIsLoading(true);
|
||||
await fetchGEmolumento(gEmolumentoReadParams);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
loadData();
|
||||
}, []); // ← executa apenas uma vez
|
||||
|
||||
|
||||
// Obtém o item selecionado com base no valor atual do campo
|
||||
const selected = gEmolumento?.find(
|
||||
(item) => String(item.emolumento_id) === String(field.value?.key ?? field.value)
|
||||
);
|
||||
|
||||
|
||||
// Estrutura visual do componente
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
{/* === Botão principal (exibe valor selecionado) === */}
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl className="w-full">
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
disabled={isLoading}
|
||||
className={cn(
|
||||
"justify-between cursor-pointer w-full whitespace-normal text-left break-words min-h-[2.5rem]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* Exibe o texto do botão */}
|
||||
{isLoading
|
||||
? "Carregando..."
|
||||
: selected
|
||||
? GetCapitalize(selected.descricao)
|
||||
: "Selecione emolumento"}
|
||||
|
||||
{/* Ícone de seta */}
|
||||
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
|
||||
{/* === Conteúdo do Popover (lista de opções) === */}
|
||||
<PopoverContent
|
||||
align="start"
|
||||
side="bottom"
|
||||
className="w-[var(--radix-popover-trigger-width)] max-w-full p-0"
|
||||
>
|
||||
<Command>
|
||||
{/* Campo de busca dentro do popover */}
|
||||
<CommandInput placeholder="Buscar emolumentos..." />
|
||||
|
||||
<CommandList>
|
||||
{/* Estado vazio ou carregando */}
|
||||
<CommandEmpty>
|
||||
{isLoading ? "Carregando..." : "Nenhum resultado encontrado."}
|
||||
</CommandEmpty>
|
||||
|
||||
{/* Grupo de opções */}
|
||||
<CommandGroup>
|
||||
{gEmolumento?.map((item) => (
|
||||
<CommandItem
|
||||
className="cursor-pointer w-full"
|
||||
key={item.emolumento_id}
|
||||
value={item.descricao?.toLowerCase() ?? ""}
|
||||
// Quando o item é selecionado
|
||||
onSelect={() => {
|
||||
// Cria objeto com ID e descrição
|
||||
const selectedValue = {
|
||||
key: Number(item.emolumento_id),
|
||||
value: item.descricao,
|
||||
};
|
||||
|
||||
// Atualiza o valor no react-hook-form
|
||||
field.onChange(selectedValue);
|
||||
|
||||
// Dispara callback externo, se existir (ex: fetchGEmolumentoItem)
|
||||
if (onSelectChange) onSelectChange(selectedValue);
|
||||
|
||||
// Fecha o popover
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{/* Ícone de seleção (check) */}
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
String(field.value?.key ?? field.value) === String(item.emolumento_id)
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{/* Nome formatado do emolumento */}
|
||||
{GetCapitalize(item.descricao)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
|
@ -52,9 +52,11 @@ export default function GMarcacaoTipoSelect({ grupo, sistema_id, situacao, field
|
|||
>
|
||||
{isLoading
|
||||
? "Carregando..."
|
||||
: selected
|
||||
? GetCapitalize(selected.descricao)
|
||||
: "Selecione..."}
|
||||
: field.value && typeof field.value === 'object' && field.value.value
|
||||
? GetCapitalize(field.value.value) // Exibe a descrição do objeto
|
||||
: field.value && typeof field.value !== 'object'
|
||||
? field.value // Se for um ID (valor antigo), exibe ele
|
||||
: "Selecione a etiqueta/carimbo"}
|
||||
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
|
|
@ -73,7 +75,10 @@ export default function GMarcacaoTipoSelect({ grupo, sistema_id, situacao, field
|
|||
key={item.marcacao_tipo_id}
|
||||
value={item.descricao?.toLowerCase() ?? ""}
|
||||
onSelect={() => {
|
||||
field.onChange(Number(item.marcacao_tipo_id));
|
||||
field.onChange({
|
||||
key: Number(item.marcacao_tipo_id),
|
||||
value: item.descricao,
|
||||
});
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command";
|
||||
import { FormControl } from "@/components/ui/form";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CategoriaServicoEnum } from "@/shared/enums/CategoriaServicoEnum";
|
||||
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
|
||||
import React from "react";
|
||||
|
||||
type CategoriaServicoSelectProps = {
|
||||
field: {
|
||||
value?: string | null;
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
};
|
||||
|
||||
export default function CategoriaServicoSelect({ field }: CategoriaServicoSelectProps) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
// Cria as opções a partir do enum
|
||||
const options = Object.entries(CategoriaServicoEnum).map(([key, label]) => ({
|
||||
value: String(key),
|
||||
label,
|
||||
}));
|
||||
|
||||
const selectedLabel =
|
||||
field.value != null
|
||||
? options.find((o) => o.value === String(field.value))?.label ?? "Selecione..."
|
||||
: "Selecione...";
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl className="w-full">
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="justify-between cursor-pointer"
|
||||
>
|
||||
|
||||
{selectedLabel}
|
||||
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Buscar categoria..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((item) => (
|
||||
<CommandItem
|
||||
className="cursor-pointer"
|
||||
key={item.value}
|
||||
value={item.label.toLowerCase()}
|
||||
onSelect={() => {
|
||||
field.onChange(item.value); // envia número
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
String(field.value) === item.value
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{item.label}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
7
src/shared/enums/CategoriaServicoEnum.ts
Normal file
7
src/shared/enums/CategoriaServicoEnum.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export const CategoriaServicoEnum = {
|
||||
A: 'Autenticação',
|
||||
C: 'Certidão',
|
||||
G: 'Serviços Gerais',
|
||||
R: 'Reconhecimento',
|
||||
B: 'Geral',
|
||||
} as const;
|
||||
Loading…
Add table
Reference in a new issue