From 96f591f02f6e2e9e4e82152d8c93e43aeaf2aff4 Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 18 Sep 2025 15:32:10 -0300 Subject: [PATCH] [MVPTN-70] feat(CRUD): Implementando Cadastro de Bairros --- .../cadastros/(g_tb_bairro)/bairro/page.tsx | 130 ++++++++++++++++++ .../_components/g_tb_bairro/GTBBairroForm.tsx | 129 +++++++++++++++++ .../g_tb_bairro/GTBBairroTable.tsx | 123 +++++++++++++++++ .../GTBTipoLogradouroForm.tsx | 15 ++ .../_data/GTBBairro/GTBBairroIndexData.ts | 25 ++++ .../_data/GTBBairro/GTBBairroRemoveData.ts | 14 ++ .../_data/GTBBairro/GTBBairroSaveData.ts | 17 +++ .../cadastros/_data/GTBBairro/mockBairro.ts | 46 +++++++ .../g_tb_bairro/useGTBBairroReadHook.ts | 27 ++++ .../g_tb_bairro/useGTBBairroRemoveHook.ts | 19 +++ .../g_tb_bairro/useGTBBairroSaveHook.ts | 31 +++++ .../_interfaces/GTBBairroInterface.ts | 6 + .../cadastros/_schemas/GTBBairroSchema.ts | 8 ++ .../g_tb_bairro/GTBBairroIndexService.ts | 12 ++ .../g_tb_bairro/GTBBairroRemoveService.ts | 10 ++ .../g_tb_bairro/GTBBairroSaveService.ts | 12 ++ .../GTBTipoLogradouroSaveService.ts | 2 +- src/components/app-sidebar.tsx | 4 + 18 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 src/app/(protected)/(cadastros)/cadastros/(g_tb_bairro)/bairro/page.tsx create mode 100644 src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroForm.tsx create mode 100644 src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroTable.tsx create mode 100644 src/app/(protected)/(cadastros)/cadastros/_data/GTBBairro/GTBBairroIndexData.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_data/GTBBairro/GTBBairroRemoveData.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_data/GTBBairro/GTBBairroSaveData.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_data/GTBBairro/mockBairro.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroReadHook.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroRemoveHook.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroSaveHook.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_interfaces/GTBBairroInterface.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_schemas/GTBBairroSchema.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroIndexService.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroRemoveService.ts create mode 100644 src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroSaveService.ts diff --git a/src/app/(protected)/(cadastros)/cadastros/(g_tb_bairro)/bairro/page.tsx b/src/app/(protected)/(cadastros)/cadastros/(g_tb_bairro)/bairro/page.tsx new file mode 100644 index 0000000..841f229 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/(g_tb_bairro)/bairro/page.tsx @@ -0,0 +1,130 @@ +'use client'; + +import { useEffect, useState, useCallback } from "react"; +import { Card, CardContent } from "@/components/ui/card"; +import { useConfirmDialog } from "@/app/_components/confirm_dialog/useConfirmDialog"; + +import Header from "@/app/_components/structure/Header"; +import ConfirmDialog from "@/app/_components/confirm_dialog/ConfirmDialog"; +import Loading from "@/app/_components/loading/loading"; +import GTBBairroTable from "../../_components/g_tb_bairro/GTBBairroTable"; +import GTBBairroForm from "../../_components/g_tb_bairro/GTBBairroForm"; + +import { useGTBBairroReadHook } from "../../_hooks/g_tb_bairro/useGTBBairroReadHook"; +import { useGTBBairroSaveHook } from "../../_hooks/g_tb_bairro/useGTBBairroSaveHook"; +import { useGTBBairroRemoveHook } from "../../_hooks/g_tb_bairro/useGTBBairroRemoveHook"; + +import { GTBBairroInterface } from "../../_interfaces/GTBBairroInterface"; + +const initialBairro: GTBBairroInterface = { + sistema_id: null, + tb_bairro_id: 0, + descricao: '', + situacao: 'A' +} + +export default function TTBAndamentoServico() { + + // Hooks para leitura e salvamento + const { gTBBairro, fetchGTBBairro } = useGTBBairroReadHook(); + const { saveGTBBairro } = useGTBBairroSaveHook(); + const { removeGTBBairro } = useGTBBairroRemoveHook(); + + // Estado para controlar o formulário e item selecionado + const [selectedBairro, setBairro] = useState(null); + const [isFormOpen, setIsFormOpen] = useState(false); + const [itemToDelete, setItemToDelete] = useState(null); + + // Hook para o modal de confirmação + const { + isOpen: isConfirmOpen, + openDialog: openConfirmDialog, + handleConfirm, + handleCancel, + } = useConfirmDialog(); + + // Ações do formulário + const handleOpenForm = useCallback((data: GTBBairroInterface | null) => { + setBairro(data); + setIsFormOpen(true); + }, []); + + const handleCloseForm = useCallback(() => { + setBairro(null); + setIsFormOpen(false); + }, []); + + const handleSave = useCallback(async (data: GTBBairroInterface) => { + await saveGTBBairro(data); + fetchGTBBairro(); // Atualiza a tabela após salvar + }, [saveGTBBairro, fetchGTBBairro]); + + // Ações de deleção + const handleConfirmDelete = useCallback((item: GTBBairroInterface) => { + setItemToDelete(item); + openConfirmDialog(); + }, [openConfirmDialog]); + + const handleDelete = useCallback(async () => { + if (!itemToDelete) return; + + await removeGTBBairro(itemToDelete); + await fetchGTBBairro(); // Atualiza a tabela após remover + + setItemToDelete(null) + + handleCancel(); + }, [itemToDelete, fetchGTBBairro, handleCancel]); + + // Efeito para carregar os dados na montagem do componente + useEffect(() => { + fetchGTBBairro(); + }, []); + + if (!gTBBairro) { + return ; + } + + return ( +
+ {/* Cabeçalho */} +
{ handleOpenForm(data = initialBairro) }} + /> + + {/* Tabela de Bairros */} + + + + + + + {/* Modal de confirmação */} + + + {/* Formulário de criação/edição */} + +
+ ); +} diff --git a/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroForm.tsx b/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroForm.tsx new file mode 100644 index 0000000..3cb3cd2 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroForm.tsx @@ -0,0 +1,129 @@ +'use client'; + +import z from "zod"; +import { useEffect } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; + +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +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 { Label } from "@/components/ui/label"; + +import { GTBBairroSchema } from "../../_schemas/GTBBairroSchema"; +import { GTBBairroInterface } from "../../_interfaces/GTBBairroInterface"; + +type FormValues = z.infer; + +interface GTBBairroFormProps { + isOpen: boolean; + data: FormValues | null; + onClose: (item: null, isFormStatus: boolean) => void; + onSave: (data: FormValues) => void; +} + +export default function GTBBairroForm({ isOpen, data, onClose, onSave }: GTBBairroFormProps) { + // Inicializa o react-hook-form com o schema Zod + const form = useForm({ + resolver: zodResolver(GTBBairroSchema), + defaultValues: { + sistema_id: null, + tb_bairro_id: 0, + descricao: "", + situacao: "A", + }, + }); + + // Atualiza o formulário quando recebe dados para edição + useEffect(() => { + if (data) form.reset(data); + }, [data, form]); + + return ( + { + if (!open) onClose(null, false); + }} + > + + + + Bairro + + + Crie ou edite um bairro + + + +
+ + + {/* Descrição */} + ( + + Descrição + + + + + + )} + /> + + {/* Situação */} + ( +
+ field.onChange(checked ? "A" : "I")} + /> + +
+ )} + /> + + {/* Rodapé do Dialog */} + + + + + + + + {/* Campos ocultos */} + + + + +
+
+ ); +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroTable.tsx b/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroTable.tsx new file mode 100644 index 0000000..7aeb15c --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_bairro/GTBBairroTable.tsx @@ -0,0 +1,123 @@ +'use client'; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow +} from "@/components/ui/table"; +import { EllipsisIcon, PencilIcon, Trash2Icon } from "lucide-react"; +import { GTBBairroInterface } from "../../_interfaces/GTBBairroInterface"; + +interface GTBBairroTableProps { + data: GTBBairroInterface[]; + onEdit: (item: GTBBairroInterface, isEditingFormStatus: boolean) => void; + onDelete: (item: GTBBairroInterface, isEditingFormStatus: boolean) => void; +} + +/** + * Renderiza o badge de situação + */ +function StatusBadge({ situacao }: { situacao: 'A' | 'I' }) { + const isActive = situacao === "A"; + + const baseClasses = + "text-xs font-medium px-2.5 py-0.5 rounded-sm me-2"; + + const activeClasses = + "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300"; + + const inactiveClasses = + "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300"; + + return ( + + {isActive ? "Ativo" : "Inativo"} + + ); +} + +export default function GTBBairroTable({ + data, + onEdit, + onDelete +}: GTBBairroTableProps) { + return ( + + + + # + Situação + Descrição + Ações + + + + + {data.map((item) => ( + + + {item.tb_bairro_id} + + + + + + + {item.descricao} + + + + + + + + + + onEdit(item, true)} + > + + Editar + + + + + onDelete(item, true)} + > + + Remover + + + + + + + ))} + +
+ ); +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_tipologradouro/GTBTipoLogradouroForm.tsx b/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_tipologradouro/GTBTipoLogradouroForm.tsx index 8cc9866..30e26af 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_tipologradouro/GTBTipoLogradouroForm.tsx +++ b/src/app/(protected)/(cadastros)/cadastros/_components/g_tb_tipologradouro/GTBTipoLogradouroForm.tsx @@ -93,6 +93,21 @@ export default function GTBTipoLogradouroForm({ isOpen, data, onClose, onSave }: )} /> + {/* ID do tipo logradouro na ONR */} + ( + + ID do tipo logradouro na ONR + + + + + + )} + /> + {/* Situação */} { + + const { setResponse } = useResponse(); + const [gTBBairro, setGTBBairro] = useState([]); + + const fetchGTBBairro = async () => { + + try { + const response = await GTBBairroIndexService(); + + setGTBBairro(response.data); + + setResponse(response); + } catch (error) { + console.log(error) + } + + } + + return { gTBBairro, fetchGTBBairro } + +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroRemoveHook.ts b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroRemoveHook.ts new file mode 100644 index 0000000..00ed2d0 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroRemoveHook.ts @@ -0,0 +1,19 @@ +import { useResponse } from "@/app/_response/ResponseContext" +import { GTBBairroInterface } from "../../_interfaces/GTBBairroInterface"; +import GTBBairroRemoveService from "../../_services/g_tb_bairro/GTBBairroRemoveService"; + +export const useGTBBairroRemoveHook = () => { + + const { setResponse } = useResponse(); + + const removeGTBBairro = async (data: GTBBairroInterface) => { + + const response = await GTBBairroRemoveService(data); + + setResponse(response); + + } + + return { removeGTBBairro } + +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroSaveHook.ts b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroSaveHook.ts new file mode 100644 index 0000000..8873c44 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroSaveHook.ts @@ -0,0 +1,31 @@ +import { useResponse } from "@/app/_response/ResponseContext" +import { useState } from "react"; +import { GTBBairroInterface } from "../../_interfaces/GTBBairroInterface"; +import GTBBairroSaveService from "../../_services/g_tb_bairro/GTBBairroSaveService"; + +export const useGTBBairroSaveHook = () => { + + const { setResponse } = useResponse(); + const [gTBBairro, setGTBBairro] = useState(null); + // controla se o formulário está aberto ou fechado + const [isOpen, setIsOpen] = useState(false); + + const saveGTBBairro = async (data: GTBBairroInterface) => { + + const response = await GTBBairroSaveService(data); + + setGTBBairro(response.data); + + setResponse(response); + + // Fecha o formulário automaticamente após salvar + setIsOpen(false); + + // Retorna os dados imediatamente + return response; + + } + + return { gTBBairro, saveGTBBairro } + +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_interfaces/GTBBairroInterface.ts b/src/app/(protected)/(cadastros)/cadastros/_interfaces/GTBBairroInterface.ts new file mode 100644 index 0000000..e3a8c68 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_interfaces/GTBBairroInterface.ts @@ -0,0 +1,6 @@ +export interface GTBBairroInterface { + sistema_id: number | null; + tb_bairro_id: number; + descricao: string; + situacao: 'A' | 'I'; +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_schemas/GTBBairroSchema.ts b/src/app/(protected)/(cadastros)/cadastros/_schemas/GTBBairroSchema.ts new file mode 100644 index 0000000..50fcf25 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_schemas/GTBBairroSchema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +export const GTBBairroSchema = z.object({ + sistema_id: z.number().nullable(), + tb_bairro_id: z.number(), + descricao: z.string(), + situacao: z.enum(['A', 'I']), +}); \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroIndexService.ts b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroIndexService.ts new file mode 100644 index 0000000..fbc3e01 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroIndexService.ts @@ -0,0 +1,12 @@ +import GTBBairroIndexData from "../../_data/GTBBairro/GTBBairroIndexData"; + +export default async function GTBBairroIndexService() { + + try { + const response = await GTBBairroIndexData(); + return response; + } catch (error) { + console.log(error) + return error + } +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroRemoveService.ts b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroRemoveService.ts new file mode 100644 index 0000000..59bbe42 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroRemoveService.ts @@ -0,0 +1,10 @@ +import GTBBairroRemoveData from "../../_data/GTBBairro/GTBBairroRemoveData"; +import { GTBBairroInterface } from "../../_interfaces/GTBBairroInterface"; + +export default async function GTBBairroRemoveService(data: GTBBairroInterface) { + + const response = await GTBBairroRemoveData(data); + + return response; + +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroSaveService.ts b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroSaveService.ts new file mode 100644 index 0000000..f7e8da4 --- /dev/null +++ b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_bairro/GTBBairroSaveService.ts @@ -0,0 +1,12 @@ +import GTBBairroSaveData from "../../_data/GTBBairro/GTBBairroSaveData"; +import { GTBBairroInterface } from "../../_interfaces/GTBBairroInterface"; + +export default async function GTBBairroSaveService(data: GTBBairroInterface) { + + const response = await GTBBairroSaveData(data); + + console.log('GTBRegimeComunhaoSaveData', response) + + return response; + +} \ No newline at end of file diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_tipologradouro/GTBTipoLogradouroSaveService.ts b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_tipologradouro/GTBTipoLogradouroSaveService.ts index 5f0542d..ce514bc 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_tipologradouro/GTBTipoLogradouroSaveService.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_services/g_tb_tipologradouro/GTBTipoLogradouroSaveService.ts @@ -5,7 +5,7 @@ export default async function GTBTipoLogradouroSaveService(data: GTBTipoLogradou const response = await GTBTipoLogradouroSaveData(data); - console.log('GTBRegimeComunhaoSaveData', response) + console.log('GTBTipoLogradouroSaveData', response) return response; diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index 926c914..b959708 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -100,6 +100,10 @@ const data = { { title: "Tipos de Logradouros", url: "/cadastros/tipo-logradouro" + }, + { + title: "Bairro", + url: "/cadastros/bairro" } ], },