[MVPTN-23] feat(CRUD): Adiciona CRUD para manipular o G_NATUREZA

This commit is contained in:
Keven Willian Pereira de Souza 2025-10-14 08:05:21 -03:00
parent d3f8bebe1d
commit 4559de0187
26 changed files with 1385 additions and 371 deletions

916
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,7 @@
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-table": "^8.21.3",
@ -68,6 +69,6 @@
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4",
"tw-animate-css": "^1.3.7",
"typescript": "^5"
"typescript": "5.9.3"
}
}
}

View file

@ -0,0 +1,11 @@
import GNaturezaIndex from "@/packages/administrativo/components/GNatureza/GNaturezaIndex";
export default function GNaturezaPage() {
return (
<GNaturezaIndex
sistema_id={2}
/>
);
}

View file

@ -125,6 +125,10 @@ const data = {
title: 'Minuta',
url: '/cadastros/minuta/',
},
{
title: 'Minuta/Naturezas',
url: '/administrativo/minutas/naturezas',
},
{
title: "Censec/Tipo do Ato",
url: "/cadastros/censec-tipoato"

View file

@ -0,0 +1,31 @@
"use client"
import * as React from "react"
import * as SwitchPrimitive from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
function Switch({
className,
...props
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
return (
<SwitchPrimitive.Root
data-slot="switch"
className={cn(
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className={cn(
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitive.Root>
)
}
export { Switch }

View file

@ -0,0 +1,123 @@
import { ColumnDef } from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
EllipsisIcon,
PencilIcon,
Trash2Icon,
} from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { SortableHeader } from "@/shared/components/dataTable/SortableHeader";
import GetCapitalize from "@/shared/actions/text/GetCapitalize";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
export default function GNaturezaColumns(
onEdit: (item: GNaturezaInterface, isEditingFormStatus: boolean) => void,
onDelete: (item: GNaturezaInterface, isEditingFormStatus: boolean) => void
): ColumnDef<GNaturezaInterface>[] {
return [
// ID
{
accessorKey: "natureza_id",
header: ({ column }) => SortableHeader("ID", column),
cell: ({ row }) => Number(row.getValue("natureza_id")),
enableSorting: true,
},
// Descrição
{
accessorKey: "descricao",
header: ({ column }) => SortableHeader("Descrição", column),
cell: ({ row }) =>
GetCapitalize(String(row.getValue("descricao") || "")),
},
// Situação
{
accessorKey: "situacao",
header: ({ column }) => SortableHeader("Situação", column),
cell: ({ row }) => {
const value = String(row.getValue("situacao") || "").toUpperCase();
if (value === "A") return "Ativo";
if (value === "I") return "Inativo";
return "-";
},
},
// Sistema ID
{
accessorKey: "sistema_id",
header: ({ column }) => SortableHeader("Sistema ID", column),
cell: ({ row }) => {
const value = row.getValue("sistema_id");
return value ? Number(value) : "-";
},
},
// Pedir Número Imóvel
{
accessorKey: "pedir_numero_imovel",
header: ({ column }) => SortableHeader("Pedir Nº Imóvel", column),
cell: ({ row }) => {
const value = String(row.getValue("pedir_numero_imovel") || "").toUpperCase();
return value === "S" ? "Sim" : "Não";
},
},
// Controle Frente/Verso
{
accessorKey: "controle_frenteverso",
header: ({ column }) => SortableHeader("Controle Frente/Verso", column),
cell: ({ row }) => {
const value = String(row.getValue("controle_frenteverso") || "").toUpperCase();
return value === "S" ? "Sim" : "Não";
},
},
// Ações
{
id: "actions",
header: "Ações",
cell: ({ row }) => {
const natureza = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => onEdit(natureza, true)}>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-red-600"
onSelect={() => onDelete(natureza, true)}
>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
},
enableSorting: false,
enableHiding: false,
},
];
}

View file

@ -0,0 +1,175 @@
'use client';
import React, { useEffect } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import LoadingButton from "@/shared/components/loadingButton/LoadingButton";
import { ResetFormIfData } from "@/shared/actions/form/ResetFormIfData";
import { useGNaturezaFormHook } from "../../hooks/GNatureza/useGNaturezaFormHook";
import { GNaturezaFormInterface } from "../../interfaces/GNatureza/GNaturezaFormInterface";
import SituacoesSelect from "@/shared/components/situacoes/SituacoesSelect";
/**
* Formulário de cadastro/edição de Natureza
* Baseado nos campos da tabela G_NATUREZA
*/
export default function GNaturezaForm({
isOpen,
data,
onClose,
onSave,
buttonIsLoading,
}: GNaturezaFormInterface) {
const form = useGNaturezaFormHook({});
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
ResetFormIfData(form, data);
}, [data, form]);
function onError(error: any) {
console.log("Erro no formulário:", error);
}
return (
<Dialog
open={isOpen}
onOpenChange={(open) => {
if (!open) onClose(null, false);
}}
>
<DialogContent className="w-full max-w-full p-6 sm:max-w-3xl md:max-w-2xl lg:max-w-2xl">
<DialogHeader>
<DialogTitle className="text-lg sm:text-xl">
Natureza de Minutas
</DialogTitle>
<DialogDescription className="text-sm text-muted-foreground">
Natureza de MInutas
</DialogDescription>
</DialogHeader>
{/* Formulário principal */}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSave, onError)}
className="space-y-6"
>
{/* GRID MOBILE FIRST */}
<div className="grid w-full grid-cols-12 gap-4">
{/* DESCRIÇÃO */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="descricao"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Descrição</FormLabel>
<FormControl>
<Input
{...field}
type="text"
placeholder="Digite a descrição da natureza"
maxLength={60}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* SITUAÇÃO */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="situacao"
render={({ field }) => (
<FormItem>
<FormLabel>Situação</FormLabel>
<SituacoesSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* PEDIR_NUMERO_IMOVEL */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="pedir_numero_imovel"
render={({ field }) => (
<FormItem className="flex flex-col space-y-1">
<FormLabel>Pedir do Imóvel</FormLabel>
<FormControl>
<Switch
checked={field.value === 'S'}
onCheckedChange={(checked: boolean) =>
field.onChange(checked ? 'S' : 'N')
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* CONTROLE_FRENTEVERSO */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="controle_frenteverso"
render={({ field }) => (
<FormItem className="flex flex-col space-y-1">
<FormLabel>Controle Frente/Verso</FormLabel>
<FormControl>
<Switch
checked={field.value === 'S'}
onCheckedChange={(checked: boolean) =>
field.onChange(checked ? 'S' : 'N')
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
{/* Rodapé */}
<DialogFooter className="flex flex-col sm:flex-row gap-2 justify-end mt-6">
<DialogClose asChild>
<Button variant="outline" type="button">
Cancelar
</Button>
</DialogClose>
<LoadingButton
text="Salvar"
textLoading="Salvando..."
type="submit"
loading={buttonIsLoading}
/>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View file

@ -0,0 +1,177 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
import Loading from '@/shared/components/loading/loading';
import { useGNaturezaIndexHook } from '@/packages/administrativo/hooks/GNatureza/useGNaturezaIndexHook';
import { useGNaturezaSaveHook } from '@/packages/administrativo/hooks/GNatureza/useGNaturezaSaveHook';
import { useGNaturezaDeleteHook } from '@/packages/administrativo/hooks/GNatureza/useGNaturezaDeleteHook';
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
import GNaturezaInterface from '@/packages/administrativo/interfaces/GNatureza/GNaturezaInterface';
import Header from '@/shared/components/structure/Header';
import GNaturezaTable from './GNaturezaTable';
import GNaturezaForm from './GNaturezaForm';
import { GNaturezaIndexInterface } from '../../interfaces/GNatureza/GNaturezaIndexInterface';
export default function GNaturezaIndex({ sistema_id }: GNaturezaIndexInterface) {
const GNaturezaIndexParams: GNaturezaIndexInterface = {
sistema_id: sistema_id
}
// Controle de estado do botão
const [buttonIsLoading, setButtonIsLoading] = useState(false);
// Hooks para leitura e salvamento
const { gNatureza, indexGNatureza } = useGNaturezaIndexHook();
const { saveGNatureza } = useGNaturezaSaveHook();
const { deleteGNatureza } = useGNaturezaDeleteHook();
// Estados
const [selectedData, setSelectedData] = useState<GNaturezaInterface | null>(null);
const [isFormOpen, setIsFormOpen] = useState(false);
// Estado para saber qual item será deletado
const [itemToDelete, setItemToDelete] = useState<GNaturezaInterface | null>(null);
/**
* Hook do modal de confirmação
*/
const {
isOpen: isConfirmOpen,
openDialog: openConfirmDialog,
handleCancel,
} = useConfirmDialog();
/**
* Abre o formulário no modo de edição ou criação
*/
const handleOpenForm = useCallback((data: GNaturezaInterface | null) => {
// Se não houver dados (criação), cria um objeto inicial com pessoa_tipo
const initialData: GNaturezaInterface = data ?? { sistema_id: GNaturezaIndexParams.sistema_id } as GNaturezaInterface;
setSelectedData(initialData);
setIsFormOpen(true);
}, []);
/**
* Fecha o formulário e limpa o andamento selecionado
*/
const handleCloseForm = useCallback(() => {
setSelectedData(null);
setIsFormOpen(false);
}, []);
/**
* Salva os dados do formulário
*/
const handleSave = useCallback(
async (formData: GNaturezaInterface) => {
// Coloca o botão em estado de loading
setButtonIsLoading(true);
// Aguarda salvar o registro
await saveGNatureza(formData);
// Remove o botão em estado de loading
setButtonIsLoading(false);
// Atualiza a lista de dados
indexGNatureza(GNaturezaIndexParams);
},
[saveGNatureza, indexGNatureza, handleCloseForm],
);
/**
* Quando o usuário clica em "remover" na tabela
*/
const handleConfirmDelete = useCallback(
(item: GNaturezaInterface) => {
// Define o item atual para remoção
setItemToDelete(item);
// Abre o modal de confirmação
openConfirmDialog();
},
[openConfirmDialog],
);
/**
* Executa a exclusão de fato quando o usuário confirma
*/
const handleDelete = useCallback(async () => {
// Protege contra null
if (!itemToDelete) return;
// Executa o Hook de remoção
await deleteGNatureza(itemToDelete);
// Atualiza a lista
await indexGNatureza(GNaturezaIndexParams);
// Limpa o item selecionado
setItemToDelete(null);
// Fecha o modal
handleCancel();
}, [itemToDelete, indexGNatureza, handleCancel]);
/**
* Busca inicial dos dados
*/
useEffect(() => {
indexGNatureza(GNaturezaIndexParams);
}, []);
/**
* Tela de loading enquanto carrega os dados
*/
if (gNatureza?.length == 0) {
return <Loading type={2} />;
}
return (
<div>
{/* Cabeçalho */}
<Header
title={'Naturezas de Minutas'}
description={'Naturezas de Minutas'}
buttonText={'Novo imóvel'}
buttonAction={() => {
handleOpenForm(null);
}}
/>
{/* Tabela de andamentos */}
<GNaturezaTable
data={gNatureza}
onEdit={handleOpenForm}
onDelete={handleConfirmDelete}
/>
{/* Modal de confirmação */}
{isConfirmOpen && (
<ConfirmDialog
isOpen={isConfirmOpen}
title="Confirmar exclusão"
description="Atenção"
message={`Deseja realmente excluir o imóvel "${itemToDelete?.descricao}"?`}
confirmText="Sim, excluir"
cancelText="Cancelar"
onConfirm={handleDelete}
onCancel={handleCancel}
/>
)}
{/* Formulário de criação/edição */}
{isFormOpen && (
<GNaturezaForm
isOpen={isFormOpen}
data={selectedData}
onClose={handleCloseForm}
onSave={handleSave}
buttonIsLoading={buttonIsLoading}
/>
)}
</div>
);
}

View file

@ -0,0 +1,26 @@
'use client';
import { DataTable } from "@/shared/components/dataTable/DataTable";
import GNaturezaColumns from "./GNaturezaColumns";
import GNaturezaTableInterface from "../../interfaces/GNatureza/GNaturezaTableInterface";
/**
* Componente principal da tabela de Naturezas
*/
export default function GNaturezaTable({
data,
onEdit,
onDelete,
}: GNaturezaTableInterface) {
const columns = GNaturezaColumns(onEdit, onDelete);
return (
<div>
<DataTable
data={data}
columns={columns}
filterColumn="descricao"
filterPlaceholder="Buscar por descrição da natureza..."
/>
</div>
);
}

View file

@ -0,0 +1,18 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
import API from "@/shared/services/api/Api";
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
async function executeGNaturezaDeleteData(
data: GNaturezaInterface
): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.DELETE,
endpoint: `administrativo/g_natureza/${data.natureza_id}`,
});
}
export const GNaturezaDeleteData = withClientErrorHandler(executeGNaturezaDeleteData);

View file

@ -0,0 +1,18 @@
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
import API from "@/shared/services/api/Api";
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
async function executeGNaturezaIndexData(
data: GNaturezaInterface
): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.GET,
endpoint: `administrativo/g_natureza/sistema/${data.sistema_id}`,
});
}
export const GNaturezaIndexData = withClientErrorHandler(executeGNaturezaIndexData);

View file

@ -0,0 +1,24 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
import API from "@/shared/services/api/Api";
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
async function executeGNaturezaSaveData(
data: GNaturezaInterface
): Promise<ApiResponseInterface> {
// Verifica se existe ID para decidir se é atualização (PUT) ou criação (POST)
const isUpdate = Boolean(data.natureza_id);
// Instancia o cliente da API
const api = new API();
// Executa a requisição para a API com o método apropriado e envia os dados no corpo
return api.send({
method: isUpdate ? Methods.PUT : Methods.POST, // PUT se atualizar, POST se criar
endpoint: `administrativo/g_natureza/${data.natureza_id || ""}`, // endpoint dinâmico
body: data, // payload enviado para a API
});
}
export const GNaturezaSaveData = withClientErrorHandler(executeGNaturezaSaveData);

View file

@ -0,0 +1,19 @@
import { useResponse } from "@/shared/components/response/ResponseContext";
import { useState } from "react";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
import { GNaturezaDeleteService } from "../../services/GNatureza/GNaturezaDeleteService";
export const useGNaturezaDeleteHook = () => {
const { setResponse } = useResponse();
const [gNatureza, setGNatureza] = useState<GNaturezaInterface>();
const deleteGNatureza = async (data: GNaturezaInterface) => {
const response = await GNaturezaDeleteService(data);
setGNatureza(data);
setResponse(response);
};
return { gNatureza, deleteGNatureza };
};

View file

@ -0,0 +1,13 @@
import { useForm } from "react-hook-form";
import { GNaturezaFormValues, GNaturezaSchema } from "../../schemas/GNatureza/GNaturezaSchema";
import { zodResolver } from "@hookform/resolvers/zod";
export function useGNaturezaFormHook(defaults?: Partial<GNaturezaFormValues>) {
return useForm<GNaturezaFormValues>({
resolver: zodResolver(GNaturezaSchema),
defaultValues: {
natureza_id: 0,
...defaults,
},
});
}

View file

@ -0,0 +1,25 @@
'use client';
import { useResponse } from "@/shared/components/response/ResponseContext";
import { useState } from "react";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
import { GNaturezaIndexService } from "../../services/GNatureza/GNaturezaIndexService";
export const useGNaturezaIndexHook = () => {
const { setResponse } = useResponse();
const [gNatureza, setGNatureza] = useState<GNaturezaInterface[]>([]);
const indexGNatureza = async (data: GNaturezaInterface) => {
const response = await GNaturezaIndexService(data);
// Armazena os dados consultados
setGNatureza(response.data);
// Define a resposta (toast, modal, feedback, etc.)
setResponse(response);
};
return {
gNatureza,
indexGNatureza,
};
};

View file

@ -0,0 +1,33 @@
'use client';
import { useResponse } from "@/shared/components/response/ResponseContext";
import { useState } from "react";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
import { GNaturezaSaveService } from "../../services/GNatureza/GNaturezaSaveService";
export const useGNaturezaSaveHook = () => {
const { setResponse } = useResponse();
const [gNatureza, setGNatureza] = useState<GNaturezaInterface>();
// controla se o formulário está aberto ou fechado
const [isOpen, setIsOpen] = useState(false);
const saveGNatureza = async (data: GNaturezaInterface) => {
const response = await GNaturezaSaveService(data);
// Armazena os dados da resposta
setGNatureza(response.data);
// Define os dados da resposta (toast, modal, etc.)
setResponse(response);
// Fecha o formulário automaticamente após salvar
setIsOpen(false);
// Retorna os valores de forma imediata
return response.data;
};
return { gNatureza, saveGNatureza, isOpen, setIsOpen };
};

View file

@ -0,0 +1,9 @@
import { GNaturezaFormValues } from "../../schemas/GNatureza/GNaturezaSchema";
export interface GNaturezaFormInterface {
isOpen: boolean;
data: GNaturezaFormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: GNaturezaFormValues) => void;
buttonIsLoading: boolean;
}

View file

@ -0,0 +1,3 @@
export interface GNaturezaIndexInterface {
sistema_id: number
}

View file

@ -0,0 +1,8 @@
export default interface GNaturezaInterface {
natureza_id?: number;
descricao?: string;
situacao?: string;
sistema_id?: number;
pedir_numero_imovel?: string;
controle_frenteverso?: string;
}

View file

@ -0,0 +1,7 @@
import GNaturezaInterface from "./GNaturezaInterface";
export default interface GNaturezaTableInterface {
data?: GNaturezaInterface[];
onEdit: (item: GNaturezaInterface, isEditingFormStatus: boolean) => void;
onDelete: (item: GNaturezaInterface, isEditingFormStatus: boolean) => void;
}

View file

@ -0,0 +1,12 @@
import z from "zod";
export const GNaturezaSchema = z.object({
natureza_id: z.number().optional(),
descricao: z.string().optional(),
situacao: z.string().optional(),
sistema_id: z.number().optional(),
pedir_numero_imovel: z.string().optional(),
controle_frenteverso: z.string().optional(),
});
export type GNaturezaFormValues = z.infer<typeof GNaturezaSchema>;

View file

@ -0,0 +1,11 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import { GNaturezaDeleteData } from "../../data/GNatureza/GNaturezaDeleteData";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
async function executeGNaturezaDeleteService(data: GNaturezaInterface) {
const response = await GNaturezaDeleteData(data);
return response;
}
export const GNaturezaDeleteService = withClientErrorHandler(executeGNaturezaDeleteService);

View file

@ -0,0 +1,11 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import { GNaturezaIndexData } from "../../data/GNatureza/GNaturezaIndexData";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
export default async function executeGNaturezaIndexService(data: GNaturezaInterface) {
const response = await GNaturezaIndexData(data);
return response;
}
export const GNaturezaIndexService = withClientErrorHandler(executeGNaturezaIndexService);

View file

@ -0,0 +1,11 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import { GNaturezaSaveData } from "../../data/GNatureza/GNaturezaSaveData";
import GNaturezaInterface from "../../interfaces/GNatureza/GNaturezaInterface";
async function executeGNaturezaSaveService(data: GNaturezaInterface) {
const response = await GNaturezaSaveData(data);
return response;
}
export const GNaturezaSaveService = withClientErrorHandler(executeGNaturezaSaveService);

View file

@ -0,0 +1,62 @@
import { Button } from "@/components/ui/button";
import { 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 { SituacoesEnum } from "@/shared/enums/SituacoesEnum";
import { Command } from "cmdk";
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
import React from "react";
export default function SituacoesSelect({ field }: any) {
const [open, setOpen] = React.useState(false);
// ✅ Corrigido para enums string-based
const options = Object.entries(SituacoesEnum).map(([value, label]) => ({
value,
label,
}));
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl className="w-full">
<Button variant="outline" role="combobox" aria-expanded={open} className="justify-between">
{field.value
? options.find((item) => item.value === field.value)?.label
: "Selecione..."}
<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 situação..." />
<CommandList>
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
<CommandGroup>
{options.map((item) => (
<CommandItem
key={item.value}
value={item.label.toLowerCase()}
onSelect={() => {
field.onChange(item.value);
setOpen(false);
}}
>
<CheckIcon
className={cn(
"mr-2 h-4 w-4",
field.value === item.value ? "opacity-100" : "opacity-0"
)}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View file

@ -1,4 +1,4 @@
export enum SituacoesEnum {
A = 'A',
I = 'I',
A = 'Ativo',
I = 'Inativo',
}