feat(): Refatoração de código, e preparação para criação do dashboard do cliente

This commit is contained in:
Kenio 2025-11-08 10:46:48 -03:00
parent bed3989eda
commit bee7779e39
59 changed files with 2628 additions and 385 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
'use client';
import { useParams } from 'next/navigation';
import { useLogServerHook } from '@/packages/administrativo/hooks/Log/useLogServerHook'; // importa seu hook customizado
import { useEffect } from 'react';
export default function ClientePage() {
// Captura o ID da rota (ex: /administrativo/clientes/6)
const { id } = useParams();
// Hook customizado para carregar dados do log/server
const { log, fetchLogServer } = useLogServerHook();
// Quando o componente montar, busca os dados do servidor/log
useEffect(() => {
if (id) fetchLogServer(Number(id));
}, [id]);
// Caso não exista log
if (!log) {
return <p>Nenhum dado encontrado para o cliente {id}.</p>;
}
return (
<div className="p-4">
<h1 className="text-2xl font-semibold mb-4">
Detalhes do Log do Cliente #{id}
</h1>
<div className="space-y-2">
<p><strong>Nome:</strong> {log.name}</p>
<p><strong>CNS:</strong> {log.cns}</p>
<p><strong>Status:</strong> {log.status}</p>
<p><strong>Email:</strong> {log.email}</p>
{/* outros campos conforme LogInterface */}
</div>
</div>
);
}

View file

@ -0,0 +1,156 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
// Componentes de UI Genéricos
import z from 'zod';
import { Card, CardContent } from '@/components/ui/card';
import Loading from '@/shared/components/loading/loading';
import Header from '@/shared/components/structure/Header';
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
// Componentes de UI Específicos de cliente
import ClientTable from '@/packages/administrativo/components/Client/ClientTable';
import ClientForm from '@/packages/administrativo/components/Client/ClientForm';
// Hooks Específicos de cliente
import { useClientIndexHook } from '@/packages/administrativo/hooks/Client/useClientIndexHook';
import { useClientSaveHook } from '@/packages/administrativo/hooks/Client/useClientSaveHook';
import { useClientDeleteHook } from '@/packages/administrativo/hooks/Client/useClientDeleteHook';
// Interface
import { ClientInterface } from '@/packages/administrativo/interfaces/Client/ClientInterface';
import { ClientSchema } from '@/packages/administrativo/schemas/Client/ClientSchema';
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
const initialClient: ClientInterface = {
client_id: 0, // ID inicial padrão (auto incremento no banco)
cns: '', // Código opcional de 10 caracteres
name: '', // Nome obrigatório
date_register: new Date().toISOString(), // Data atual no formato ISO
state: '', // UF (2 caracteres)
city: '', // Nome da cidade
responsible: '', // Responsável pelo cliente
consultant: '', // Consultor associado
type_contract: '', // Tipo de contrato (ex: A, M, S...)
}
export default function ClientsPage() {
const [buttonIsLoading, setButtonIsLoading] = useState(false);
// 1. Hooks de dados para cliente
const { clients, fetchClients } = useClientIndexHook();
const { saveClient } = useClientSaveHook();
const { removeClient } = useClientDeleteHook(); // Presumindo que o hook existe e expõe `removeclient`
// 2. Estados da página
const [selectedClient, setSelectedClient] = useState<ClientInterface | null>(null);
const [isFormOpen, setIsFormOpen] = useState(false);
const [clientToDelete, setClientToDelete] = useState<ClientInterface | null>(null);
// Hook do modal de confirmação
const {
isOpen: isConfirmOpen,
openDialog: openConfirmDialog,
handleCancel,
} = useConfirmDialog();
// 3. Funções de manipulação do formulário
const handleOpenForm = useCallback((client: ClientInterface | null) => {
setSelectedClient(client);
setIsFormOpen(true);
}, []);
const handleCloseForm = useCallback(() => {
setSelectedClient(null);
setIsFormOpen(false);
}, []);
// 4. Função para salvar (criar ou editar)
const handleSave = useCallback(
async (formData: ClientInterface) => {
setButtonIsLoading(true);
await saveClient(formData);
setButtonIsLoading(false);
setIsFormOpen(false);
fetchClients(); // Atualiza a lista de clientes
},
[saveClient, fetchClients, handleCloseForm],
);
// 5. Funções para exclusão
const handleConfirmDelete = useCallback(
(client: ClientInterface) => {
setClientToDelete(client);
openConfirmDialog();
},
[openConfirmDialog],
);
const handleDelete = useCallback(async () => {
if (!clientToDelete) return;
await removeClient(clientToDelete); // Chama o hook de remoção
await fetchClients(); // Atualiza a lista
setClientToDelete(null); // Limpa o estado
handleCancel(); // Fecha o modal de confirmação
}, [clientToDelete, fetchClients, handleCancel]);
// 6. Busca inicial dos dados
useEffect(() => {
fetchClients();
}, []);
// 7. Renderização condicional de loading
if (clients?.length == 0) {
return <Loading type={2} />;
}
// 8. Renderização da página
return (
<div>
<Header
title={'Clientes'}
description={'Gerenciamento de Clientes do Sistema'}
buttonText={'Novo Cliente'}
buttonAction={(data) => handleOpenForm(data = initialClient)}
/>
<Card>
<CardContent>
<ClientTable
data={clients}
onEdit={handleOpenForm}
onDelete={handleConfirmDelete}
/>
</CardContent>
</Card>
<ConfirmDialog
isOpen={isConfirmOpen}
title="Confirmar exclusão"
description="Esta ação não pode ser desfeita."
message={`Deseja realmente excluir o Cliente "${clientToDelete?.name}"?`}
confirmText="Sim, excluir"
cancelText="Cancelar"
onConfirm={handleDelete}
onCancel={handleCancel}
/>
<ClientForm
isOpen={isFormOpen}
data={selectedClient}
onClose={handleCloseForm}
onSave={handleSave}
buttonIsLoading={buttonIsLoading}
/>
</div>
);
}

View file

@ -0,0 +1,17 @@
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
/**
* Exibe um selo (badge) visual para status do usuário: Ativo / Inativo
*/
export function StatusBadge({ status }: { status: SituacoesEnum }) {
const isActive = status === '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 (
<span className={`${baseClasses} ${isActive ? activeClasses : inactiveClasses}`}>
{isActive ? 'Ativo' : 'Inativo'}
</span>
);
}

View file

@ -35,8 +35,8 @@ const data = {
isActive: false,
items: [
{
title: 'Dashboard',
url: '/dashboard/',
title: 'Clientes',
url: '/administrativo/clientes/',
},
],
},
@ -48,7 +48,7 @@ const data = {
items: [
{
title: 'Usuários',
url: '/usuarios/',
url: '/administrativo/usuarios/',
},
],
},

View file

@ -37,10 +37,12 @@ export function NavUser({ user }: { user: UserAuthenticatedInterface }) {
setIsConfirmOpen(true);
}, []);
// Consifrma ação de log-out
const handleLogoutConfirm = useCallback(async () => {
logoutUsuario();
}, []);
// Cancela ação de log-out
const handleLogoutCancel = useCallback(async () => {
setIsConfirmOpen(false);
}, []);
@ -110,7 +112,7 @@ export function NavUser({ user }: { user: UserAuthenticatedInterface }) {
<DropdownMenuItem className="cursor-pointer" onClick={() => handleConfirmOpen()}>
<LogOut />
Log out
Sair do sistema
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View file

@ -0,0 +1,195 @@
'use client'; // Define que este componente será executado no lado do cliente (Client Component)
import z from 'zod';
import { useEffect } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
// Importação dos componentes da UI (botões, inputs, dialogs, etc.)
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';
// Componente de botão com estado de carregamento
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
// Importa a interface tipada separadamente
import { UserFormInterface, FormValues } from '../../interfaces/User/UserFormInterface';
// Importa o hook que gerencia o formulário
import { useUserFormHook } from '../../hooks/User/useUserFormHook';
// Componente principal do formulário de usuário
export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoading }: UserFormInterface) {
// Usa o hook que centraliza toda a lógica do form
const form = useUserFormHook(data);
// Callback de erro da submissão do formulário
function onError(error: any) {
console.log("error", error);
}
return (
// Componente de diálogo (modal)
<Dialog
open={isOpen} // Define se o diálogo está aberto
onOpenChange={(open) => {
if (!open) onClose(null, false); // Fecha o diálogo quando necessário
}}
>
{/* Conteúdo do modal */}
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>{data?.user_id ? 'Editar Usuário' : 'Novo Usuário'}</DialogTitle>
<DialogDescription>Gerencie os dados do usuário aqui.</DialogDescription>
</DialogHeader>
{/* Wrapper do formulário com react-hook-form */}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave, onError)} className="space-y-4">
{/* Campo: Nome */}
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Nome</FormLabel>
<FormControl>
<Input {...field} placeholder="Digite o nome completo" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Campo: Email */}
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" {...field} placeholder="exemplo@email.com" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Campo: Senha */}
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Senha</FormLabel>
<FormControl>
<Input
type="password"
{...field}
placeholder={data ? 'Deixe em branco para não alterar' : 'Digite a senha'}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Campo: Equipe */}
<FormField
control={form.control}
name="team"
render={({ field }) => (
<FormItem>
<FormLabel>Equipe</FormLabel>
<FormControl>
<Input {...field} placeholder="Digite o nome da equipe" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Campo: Cargo */}
<FormField
control={form.control}
name="cargo"
render={({ field }) => (
<FormItem>
<FormLabel>Cargo</FormLabel>
<FormControl>
<Input {...field} value={field.value ?? ''} placeholder="Digite o cargo do usuário" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Campo: Status (Ativo/Inativo) */}
<Controller
name="status"
control={form.control}
render={({ field }) => (
<div className="flex items-center space-x-2">
<Checkbox
checked={field.value === 'A'}
onCheckedChange={(checked) => field.onChange(checked ? 'A' : 'I')}
/>
<Label>Ativo</Label>
</div>
)}
/>
{/* Rodapé do formulário (botões) */}
<DialogFooter className="mt-4">
<DialogClose asChild>
<Button
variant="outline"
type="button"
onClick={() => onClose(null, false)}
className="cursor-pointer"
>
Cancelar
</Button>
</DialogClose>
{/* Botão de salvar com loading */}
<LoadingButton
text={data?.user_id ? 'Salvar' : 'Cadastrar'}
textLoading="Aguarde..."
type="submit"
loading={buttonIsLoading}
/>
</DialogFooter>
{/* Campos ocultos para dados técnicos */}
<input type="hidden" {...form.register('user_id')} />
<input type="hidden" {...form.register('date_register')} />
<input type="hidden" {...form.register('date_update')} />
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View file

@ -0,0 +1,104 @@
'use client';
import { useRouter } from 'next/navigation';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { ChartPie, EllipsisIcon, PencilIcon, Trash2Icon } from 'lucide-react';
import { ClientTableInterface } from '../../interfaces/Client/ClienteTableInterface';
import { StatusBadge } from '@/components/StatusBadge';
export default function ClientTable({ data, pagination, onEdit, onDelete }: ClientTableInterface) {
// Inicializa o roteador
const router = useRouter();
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>#</TableHead>
<TableHead>Status</TableHead>
<TableHead>CNS</TableHead>
<TableHead>Nome</TableHead>
<TableHead className="text-right">Ações</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((client) => (
<TableRow key={client.client_id}>
<TableCell className="font-medium">{client.client_id}</TableCell>
<TableCell>
<StatusBadge status={client.status as any} />
</TableCell>
<TableCell>{client.cns}</TableCell>
<TableCell>{client.name}</TableCell>
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon" className="cursor-pointer">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => onEdit(client)}
>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => router.push(`/administrativo/clientes/${client.client_id}`)}
>
<ChartPie className="mr-2 h-4 w-4" />
Dashboard
</DropdownMenuItem>
{/* <DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => onDelete(client)}
>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem> */}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={5}>
{/* Se existir paginação, mostra o total de registros; senão, usa o tamanho da lista */}
Total de clientes: {pagination?.total_records ?? data.length}
</TableCell>
</TableRow>
</TableFooter>
</Table>
);
}

View file

@ -1,10 +1,11 @@
'use client';
'use client'; // Define que este componente será executado no lado do cliente (Client Component)
import z from 'zod';
import { useEffect } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
// Importação dos componentes da UI (botões, inputs, dialogs, etc.)
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import {
@ -26,68 +27,47 @@ import {
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
import { UserSchema } from '../../schemas/User/UserSchema';
// Componente de botão com estado de carregamento
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
type FormValues = z.infer<typeof UserSchema>;
// Importa a interface tipada separadamente
import { UserFormInterface, FormValues } from '../../interfaces/User/UserFormInterface';
interface Props {
isOpen: boolean;
data: FormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: FormValues) => void;
buttonIsLoading: boolean;
}
// Importa o hook que gerencia o formulário
import { useUserFormHook } from '../../hooks/User/useUserFormHook';
export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoading }: Props) {
const form = useForm<FormValues>({
resolver: zodResolver(UserSchema),
defaultValues: {
user_id: 0,
name: '',
email: '',
password: '',
team: '',
position: 'string',
status: SituacoesEnum.ATIVO,
user_id_create: null,
user_id_update: null,
date_register: new Date().toISOString(),
date_update: null
},
});
// Componente principal do formulário de usuário
export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoading }: UserFormInterface) {
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
if (data) form.reset(data);
console.log("form", form.getValues())
}, [data, form]);
// Usa o hook que centraliza toda a lógica do form
const form = useUserFormHook(data);
// Callback de erro da submissão do formulário
function onError(error: any) {
console.log("error", error)
}
function submit(response: any) {
console.log("submit", response)
console.log("error", error);
}
return (
// Componente de diálogo (modal)
<Dialog
open={isOpen}
open={isOpen} // Define se o diálogo está aberto
onOpenChange={(open) => {
if (!open) onClose(null, false);
if (!open) onClose(null, false); // Fecha o diálogo quando necessário
}}
>
{/* Conteúdo do modal */}
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>{data?.user_id ? 'Editar Usuário' : 'Novo Usuário'}</DialogTitle>
<DialogDescription>Gerencie os dados do usuário aqui.</DialogDescription>
</DialogHeader>
{/* Wrapper do formulário com react-hook-form */}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave, onError)} className="space-y-4">
{/* Nome */}
{/* Campo: Nome */}
<FormField
control={form.control}
name="name"
@ -101,7 +81,8 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
</FormItem>
)}
/>
{/* Email */}
{/* Campo: Email */}
<FormField
control={form.control}
name="email"
@ -115,7 +96,8 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
</FormItem>
)}
/>
{/* Senha */}
{/* Campo: Senha */}
<FormField
control={form.control}
name="password"
@ -123,13 +105,18 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
<FormItem>
<FormLabel>Senha</FormLabel>
<FormControl>
<Input type="password" {...field} placeholder={data ? 'Deixe em branco para não alterar' : 'Digite a senha'} />
<Input
type="password"
{...field}
placeholder={data ? 'Deixe em branco para não alterar' : 'Digite a senha'}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Equipe */}
{/* Campo: Equipe */}
<FormField
control={form.control}
name="team"
@ -144,22 +131,22 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
)}
/>
{/* Cargo */}
{/* Campo: Cargo */}
<FormField
control={form.control}
name="position"
name="cargo"
render={({ field }) => (
<FormItem>
<FormLabel>Cargo</FormLabel>
<FormControl>
<Input {...field} placeholder="Digite o cargo do usuário" />
<Input {...field} value={field.value ?? ''} placeholder="Digite o cargo do usuário" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Status */}
{/* Campo: Status (Ativo/Inativo) */}
<Controller
name="status"
control={form.control}
@ -173,7 +160,8 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
</div>
)}
/>
{/* Rodapé */}
{/* Rodapé do formulário (botões) */}
<DialogFooter className="mt-4">
<DialogClose asChild>
<Button
@ -185,6 +173,8 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
Cancelar
</Button>
</DialogClose>
{/* Botão de salvar com loading */}
<LoadingButton
text={data?.user_id ? 'Salvar' : 'Cadastrar'}
textLoading="Aguarde..."
@ -192,7 +182,8 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
loading={buttonIsLoading}
/>
</DialogFooter>
{/* Campo oculto para o ID */}
{/* Campos ocultos para dados técnicos */}
<input type="hidden" {...form.register('user_id')} />
<input type="hidden" {...form.register('date_register')} />
<input type="hidden" {...form.register('date_update')} />
@ -201,4 +192,4 @@ export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoadin
</DialogContent>
</Dialog>
);
}
}

View file

@ -4,41 +4,17 @@ import Image from 'next/image';
import { cn } from '@/lib/utils';
import { Card, CardContent } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import z from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import UserLoginService from '@/packages/administrativo/services/User/UserLoginService';
import { useForm } from 'react-hook-form';
import { useState } from 'react';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../../../../components/ui/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Checkbox } from '@/components/ui/checkbox';
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
import { Button } from '../../../../components/ui/button';
import { UserLoginSchema } from '@/packages/administrativo/schemas/User/UserLoginSchema';
import { useUserFormLoginHook } from '../../hooks/User/useUserFormLoginHook';
import { UserFormLoginInterface } from '../../interfaces/User/UserFormLoginInterface';
type FormValues = z.infer<typeof UserLoginSchema>;
export function LoginForm({ className, ...props }: React.ComponentProps<'div'>) {
const [loading, setLoading] = useState(false);
const form = useForm<FormValues>({
resolver: zodResolver(UserLoginSchema),
defaultValues: {
email: '',
password: '',
},
});
// onSubmit agora recebe o evento do form através do handleSubmit
const onSubmit = async (values: FormValues) => {
console.log("dados para login",values)
// Ativa o estado de loading do botão
setLoading(true);
// Realiza o login
await UserLoginService(values);
// Removo o estado de loading do botão
setLoading(false);
};
/**
* Componente de login utiliza hook para gerenciar estado e lógica.
*/
export function LoginForm({ className, ...props }: UserFormLoginInterface) {
const { form, onSubmit, loading } = useUserFormLoginHook(); // Hook customizado
return (
<div className={cn('flex flex-col gap-6', className)} {...props}>
@ -46,12 +22,15 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
<CardContent className="grid p-0 md:grid-cols-2">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3 p-6 md:p-8">
{/* Cabeçalho */}
<div className="mb-6 flex flex-col items-center text-center">
<h1 className="text-2xl font-bold">Bem vindo de volta!</h1>
<h1 className="text-2xl font-bold">Bem-vindo de volta!</h1>
<p className="text-muted-foreground text-balance">
Entre na sua conta Orius Tecnologia.
</p>
</div>
{/* Campo: Login */}
<FormField
control={form.control}
name="email"
@ -65,6 +44,8 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
</FormItem>
)}
/>
{/* Campo: Senha */}
<FormField
control={form.control}
name="password"
@ -78,28 +59,35 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
</FormItem>
)}
/>
{/* Botão de loading */}
{/* Checkbox: Lembrar acesso */}
<FormField
control={form.control}
name="rememberMe"
render={({ field }) => (
<FormItem className="flex items-center space-x-2 cursor-pointer">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel className="text-sm font-normal cursor-pointer">Lembrar acesso</FormLabel>
</FormItem>
)}
/>
{/* Botão de envio com loading */}
<LoadingButton
text="Entrar"
textLoading="Aguarde..."
type="submit"
loading={loading}
/>
<div className="after:border-border relative my-4 text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
<span className="bg-card text-muted-foreground relative z-10 px-2">
Ou entre em contato
</span>
</div>
<div className="grid grid-cols-2 gap-4">
<Button variant="outline" type="button" className="w-full">
Chamar no Whatsapp
</Button>
<Button variant="outline" type="button" className="w-full">
Chamar no Local
</Button>
</div>
</form>
</Form>
{/* Lado direito (imagem) */}
<div className="bg-brand relative hidden items-center justify-center md:flex">
<Image
src="/images/logo-login.svg"
@ -111,11 +99,12 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
</div>
</CardContent>
</Card>
{/* Rodapé */}
<div className="text-muted-foreground text-center text-xs">
Ao clicar você concorda com <a href="#">Nossos termos de serviços</a> e{' '}
<a href="#">
Políticas de Privacidade
</a>.
Ao clicar você concorda com{' '}
<a href="#">Nossos termos de serviços</a> e{' '}
<a href="#">Políticas de Privacidade</a>.
</div>
</div>
);

View file

@ -19,32 +19,10 @@ import {
} from '@/components/ui/table';
import { EllipsisIcon, PencilIcon, Trash2Icon } from 'lucide-react';
import { UserInterface } from '../../interfaces/User/UserInterface'; // Ajuste o caminho conforme necessário
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
import { UserTableInterface } from '../../interfaces/User/UserTableInterface';
import { StatusBadge } from '@/components/StatusBadge';
interface UserTableProps {
data: UserInterface[];
onEdit: (user: UserInterface) => void;
onDelete: (user: UserInterface) => void;
}
/**
* Renderiza o badge de situação (Ativo/Inativo)
*/
function StatusBadge({ status }: { status: SituacoesEnum }) {
const isActive = status === '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 (
<span className={`${baseClasses} ${isActive ? activeClasses : inactiveClasses}`}>
{isActive ? 'Ativo' : 'Inativo'}
</span>
);
}
export default function UserTable({ data, onEdit, onDelete }: UserTableProps) {
export default function UserTable({ data, onEdit, onDelete }: UserTableInterface) {
return (
<Table>
<TableHeader>

View file

@ -0,0 +1,32 @@
'use server'
// Define que este módulo será executado no lado do servidor (Server Action do Next.js)
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
// Importa o enumerador que contém os métodos HTTP disponíveis (GET, POST, PUT, DELETE)
import API from '@/shared/services/api/Api';
// Importa a classe responsável por centralizar e padronizar as chamadas HTTP na aplicação
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa o decorador que adiciona tratamento global de erros às funções assíncronas
// Função principal responsável por enviar a requisição de exclusão de um cliente
async function executeClientDeleteData(usuarioId: number) {
// Cria uma nova instância da classe API, que gerencia headers, baseURL e envio das requisições
const api = new API();
// Envia a requisição DELETE para o endpoint correspondente ao cliente informado
const response = await api.send({
'method': Methods.DELETE, // Define o método HTTP como DELETE
'endpoint': `administrativo/client/${usuarioId}` // Define o endpoint incluindo o ID do cliente
});
// Retorna a resposta da API, contendo status, mensagem e possíveis dados adicionais
return response;
}
// Exporta a função encapsulada com o tratador global de erros
export const ClientDeleteData = withClientErrorHandler(executeClientDeleteData);
// `withClientErrorHandler` garante que qualquer exceção durante a execução da função seja capturada
// Isso permite um tratamento padronizado de erros e evita quebra no fluxo de execução do app

View file

@ -0,0 +1,39 @@
'use server';
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
// Importa o enum com os tipos de métodos HTTP disponíveis (GET, POST, PUT, DELETE...)
import API from '@/shared/services/api/Api';
// Importa a classe responsável por centralizar chamadas à API (wrapper de fetch ou axios)
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa um decorador/função HOC que trata erros de forma padronizada nas requisições
/**
* Função principal responsável por buscar a lista de clientes na API.
* Executa uma requisição HTTP GET para o endpoint administrativo/client.
*/
async function executeClientIndexData() {
// Instancia o serviço de API para uso nesta função
const api = new API();
// Executa uma requisição GET para o endpoint administrativo/client/
// - Usa o método 'send' da classe API
// - Passa o método HTTP e o endpoint como parâmetros
const response = await api.send({
method: Methods.GET, // Método HTTP GET
endpoint: `administrativo/client/`, // Rota da API que retorna a lista de clientes
});
// Retorna a resposta obtida da API
return response;
}
/**
* Exporta a função encapsulada com o handler de erro.
* Caso ocorra falha na requisição, o withClientErrorHandler
* intercepta o erro e o trata de forma uniforme (ex: logging, toast, etc.)
*/
export const ClientIndexData = withClientErrorHandler(executeClientIndexData);

View file

@ -0,0 +1,42 @@
'use server'
// Define que este módulo será executado no lado do servidor (Server Action do Next.js)
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
// Importa o enumerador que define os métodos HTTP disponíveis (GET, POST, PUT, DELETE)
import API from '@/shared/services/api/Api';
// Importa a classe responsável por realizar as chamadas HTTP centralizadas da aplicação
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa o decorador que adiciona tratamento global de erros à função principal
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
// Importa a tipagem do objeto de cliente, garantindo consistência nos dados enviados
// Função principal responsável por salvar (criar ou atualizar) os dados de um cliente
async function executeClientSaveData(form: ClientInterface) {
// Verifica se existe um `client_id`; se sim, trata-se de uma atualização (PUT), caso contrário, é um novo cadastro (POST)
const isUpdate = Boolean(form.client_id);
// Cria uma nova instância da classe API para enviar a requisição
const api = new API();
// Envia a requisição para o endpoint responsável por salvar os dados do cliente
const response = await api.send({
// Define o método HTTP dinamicamente com base no tipo de operação (POST ou PUT)
'method': isUpdate ? Methods.PUT : Methods.POST,
// Define o endpoint, incluindo o `client_id` se for atualização
'endpoint': `administrativo/client/${form.client_id || ''}`,
// Corpo da requisição contendo os dados do formulário
'body': form
});
// Retorna a resposta da API (pode conter status, dados ou mensagens)
return response;
}
// Exporta a função encapsulada com o tratador global de erros
export const ClientSaveData = withClientErrorHandler(executeClientSaveData);
// `withClientErrorHandler` assegura que qualquer erro durante a execução será capturado e tratado de forma padronizada

View file

@ -0,0 +1,31 @@
'use server'
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
// Importa o enumerador que contém os métodos HTTP padronizados (GET, POST, PUT, DELETE)
import API from '@/shared/services/api/Api';
// Importa a classe responsável por realizar requisições HTTP à API backend
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa o wrapper que padroniza o tratamento de erros e respostas para o cliente
// Função principal responsável por buscar um usuário específico pelo seu ID
async function executeLogIndexByIDData(client_id: number) {
// Cria uma nova instância da classe de comunicação com a API
const api = new API();
// Envia uma requisição GET ao endpoint que retorna os dados de um usuário específico
const response = await api.send({
'method': Methods.GET, // Define o método HTTP da requisição
'endpoint': `administrativo/log/${client_id}` // Monta dinamicamente o endpoint com o ID do usuário
});
// Retorna a resposta recebida da API (dados do usuário ou erro)
return response;
}
// Exporta a função encapsulada com o handler de erro
// Isso garante que exceções sejam tratadas de forma padronizada na camada superior
export const LogIndexByClientIDData = withClientErrorHandler(executeLogIndexByIDData);

View file

@ -0,0 +1,39 @@
'use server';
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
// Importa o enum com os tipos de métodos HTTP disponíveis (GET, POST, PUT, DELETE...)
import API from '@/shared/services/api/Api';
// Importa a classe responsável por centralizar chamadas à API (wrapper de fetch ou axios)
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa um decorador/função HOC que trata erros de forma padronizada nas requisições
/**
* Função principal responsável por buscar a lista de loges na API.
* Executa uma requisição HTTP GET para o endpoint administrativo/log.
*/
async function executeLogIndexData() {
// Instancia o serviço de API para uso nesta função
const api = new API();
// Executa uma requisição GET para o endpoint administrativo/log/
// - Usa o método 'send' da classe API
// - Passa o método HTTP e o endpoint como parâmetros
const response = await api.send({
method: Methods.GET, // Método HTTP GET
endpoint: `administrativo/log/`, // Rota da API que retorna a lista de loges
});
// Retorna a resposta obtida da API
return response;
}
/**
* Exporta a função encapsulada com o handler de erro.
* Caso ocorra falha na requisição, o withlogErrorHandler
* intercepta o erro e o trata de forma uniforme (ex: logging, toast, etc.)
*/
export const LogIndexData = withClientErrorHandler(executeLogIndexData);

View file

@ -0,0 +1,31 @@
'use server'
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
// Importa o enumerador que contém os métodos HTTP padronizados (GET, POST, PUT, DELETE)
import API from '@/shared/services/api/Api';
// Importa a classe responsável por realizar requisições HTTP à API backend
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa o wrapper que padroniza o tratamento de erros e respostas para o cliente
// Função principal responsável por buscar um usuário específico pelo seu ID
async function executeLogServerData(client_id: number) {
// Cria uma nova instância da classe de comunicação com a API
const api = new API();
// Envia uma requisição GET ao endpoint que retorna os dados de um usuário específico
const response = await api.send({
'method': Methods.GET, // Define o método HTTP da requisição
'endpoint': `administrativo/log/server/${client_id}` // Monta dinamicamente o endpoint com o ID do usuário
});
// Retorna a resposta recebida da API (dados do usuário ou erro)
return response;
}
// Exporta a função encapsulada com o handler de erro
// Isso garante que exceções sejam tratadas de forma padronizada na camada superior
export const LogServerData = withClientErrorHandler(executeLogServerData);

View file

@ -1,18 +0,0 @@
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 TImovelInterface from "../../interfaces/TImovel/TImovelInterface";
async function executeTImovelDeleteData(data: TImovelInterface): Promise<ApiResponseInterface> {
const api = new API();
return await api.send({
method: Methods.DELETE,
endpoint: `administrativo/t_imovel/${data.imovel_id}`
});
}
export const TImovelDeleteData = withClientErrorHandler(executeTImovelDeleteData);

View file

@ -1,17 +0,0 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import API from "@/shared/services/api/Api";
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
async function executeTImovelIndexData(): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.GET,
endpoint: `administrativo/t_imovel/classe/1`
});
}
export const TImovelIndexData = withClientErrorHandler(executeTImovelIndexData);

View file

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

View file

@ -1,19 +1,31 @@
'use server'
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
import API from '@/shared/services/api/Api';
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
// Importa o enumerador que contém os métodos HTTP padronizados (GET, POST, PUT, DELETE)
import API from '@/shared/services/api/Api';
// Importa a classe responsável por realizar requisições HTTP à API backend
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa o wrapper que padroniza o tratamento de erros e respostas para o cliente
// Função principal responsável por buscar um usuário específico pelo seu ID
async function executeUserIndexByIDData(user_id: number) {
// Cria uma nova instância da classe de comunicação com a API
const api = new API();
// Envia uma requisição GET ao endpoint que retorna os dados de um usuário específico
const response = await api.send({
'method': Methods.GET,
'endpoint': `administrativo/user/${user_id}`
'method': Methods.GET, // Define o método HTTP da requisição
'endpoint': `administrativo/user/${user_id}` // Monta dinamicamente o endpoint com o ID do usuário
});
// Retorna a resposta recebida da API (dados do usuário ou erro)
return response;
}
export const UserIndexByIDData = withClientErrorHandler(executeUserIndexByIDData)
// Exporta a função encapsulada com o handler de erro
// Isso garante que exceções sejam tratadas de forma padronizada na camada superior
export const UserIndexByIDData = withClientErrorHandler(executeUserIndexByIDData);

View file

@ -0,0 +1,37 @@
'use client';
// Define que este módulo será executado no lado do cliente (Client Component do Next.js)
import { useState } from 'react';
// Importa o hook `useState` do React (embora não esteja sendo usado aqui, pode ser útil em versões futuras)
import { useResponse } from '@/shared/components/response/ResponseContext';
// Importa o hook de contexto responsável por exibir feedbacks globais (como toasts, alerts ou modais)
import { ClientDeleteService } from '../../services/Client/ClientDeleteService';
// Importa o serviço responsável por realizar a exclusão do cliente via API
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
// Importa a tipagem do objeto `ClientInterface` para garantir segurança de tipo e padronização dos dados
// Hook personalizado responsável por encapsular a lógica de exclusão de clientes
export const useClientDeleteHook = () => {
// Obtém a função `setResponse` do contexto global, usada para exibir feedbacks ao usuário
const { setResponse } = useResponse();
// Função assíncrona que executa a exclusão de um cliente específico
const removeClient = async (client: ClientInterface) => {
try {
// Chama o serviço de exclusão, enviando o ID do cliente como parâmetro
const response = await ClientDeleteService(client.client_id);
// Define a resposta no contexto global, permitindo exibir mensagem de sucesso/erro na interface
setResponse(response);
} catch (error) {
// Captura e exibe o erro no console (embora o handler global já trate exceções)
console.error('Erro ao remover usuário:', error);
}
};
// Retorna a função principal de exclusão, permitindo que o componente que usa este hook a invoque
return { removeClient };
};

View file

@ -0,0 +1,43 @@
'use client';
// Define que este arquivo será executado no lado do cliente (Next.js Client Component)
import { useState } from 'react';
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
import { ClientIndexService } from '../../services/Client/ClientIndexService';
import { useResponse } from '@/shared/components/response/ResponseContext';
// Hook personalizado responsável por gerenciar a listagem de clientes
export const useClientIndexHook = () => {
// Obtém a função para definir mensagens globais de resposta (toast, modal, etc.)
const { setResponse } = useResponse();
// Estado local que armazena a lista de clientes retornados pela API
const [clients, setClients] = useState<ClientInterface[]>([]);
// Função responsável por buscar os clientes da API
const fetchClients = async () => {
try {
// Chama o serviço que faz a requisição HTTP
const response = await ClientIndexService();
// Atualiza o estado local com os dados retornados
setClients(response.data);
// Define a resposta global (útil para exibir mensagens de sucesso/erro)
setResponse(response);
} catch (error) {
// Caso ocorra erro na requisição, registra no console
console.error('Erro ao buscar clientes:', error);
// Atualiza o contexto de resposta com erro
setResponse({
status: 'error',
message: 'Falha ao carregar a lista de clientes.',
data: [],
});
}
};
// Retorna as variáveis e funções que o componente poderá utilizar
return { clients, fetchClients };
};

View file

@ -0,0 +1,43 @@
'use client';
// Indica que este arquivo será executado no lado do cliente (Client Component do Next.js)
import { useState } from 'react';
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
import { ClientSaveService } from '../../services/Client/ClientSaveService';
import { useResponse } from '@/shared/components/response/ResponseContext'; // 🔧 corrigido nome de import (clientesponse → clientResponse)
// Hook personalizado responsável por salvar (criar ou atualizar) clientes
export const useClientSaveHook = () => {
// Obtém a função global para definir mensagens de resposta (toast, modal, etc.)
const { setResponse } = useResponse();
// Estado local para armazenar o cliente salvo/retornado pela API
const [cliente, setCliente] = useState<ClientInterface>();
// Função responsável por enviar os dados do cliente para a API
const saveClient = async (clientData: ClientInterface) => {
try {
// Faz a chamada ao serviço que salva os dados do cliente
const response = await ClientSaveService(clientData);
// Atualiza o estado com o cliente retornado (pode conter ID ou dados processados)
setCliente(response.data);
// Define a resposta global (útil para feedback de sucesso)
setResponse(response);
} catch (error) {
// Em caso de erro, exibe no console para depuração
console.error('Erro ao salvar cliente:', error);
// Define resposta de erro global
setResponse({
status: 'error',
message: 'Falha ao salvar o cliente.',
data: null,
});
}
};
// Retorna o cliente salvo e a função responsável por salvar
return { cliente, saveClient };
};

View file

@ -0,0 +1,30 @@
'use client';
import { useState } from 'react';
import { LogInterface } from '../../interfaces/Log/LogInterface';
import { LogServerService } from '../../services/Log/LogServerService';
import { useResponse } from '@/shared/components/response/ResponseContext';
export const useLogServerHook = () => {
const { setResponse } = useResponse();
const [log, setLog] = useState<LogInterface | null>(null);
const fetchLogServer = async (client_id: number) => {
try {
const response = await LogServerService(client_id);
console.log(response)
setLog(response.data);
setResponse(response);
} catch (error) {
// O withClientErrorHandler já deve tratar o erro e formatar a 'response',
// mas um catch local pode ser útil para lógicas adicionais se necessário.
console.error("Erro ao buscar informação do servidor por ID:", error);
}
};
return { log, fetchLogServer };
};

View file

@ -1,20 +0,0 @@
import { useResponse } from '@/shared/components/response/ResponseContext';
import { useState } from 'react';
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
import { TImovelDeleteService } from '../../services/TImovel/TImovelDeleteService';
export const useTImovelDeleteHook = () => {
const { setResponse } = useResponse();
const [tImovel, setTImovel] = useState<TImovelInterface>();
const deleteTImovel = async (data: TImovelInterface) => {
const response = await TImovelDeleteService(data);
setTImovel(data);
setResponse(response);
};
return { tImovel, deleteTImovel };
};

View file

@ -1,12 +0,0 @@
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { TImovelFormValues, TImovelSchema } from "../../schemas/TImovel/TImovelSchema";
export function useTImovelFormHook(defaults?: Partial<TImovelFormValues>) {
return useForm<TImovelFormValues>({
resolver: zodResolver(TImovelSchema),
defaultValues: {
...defaults,
},
});
}

View file

@ -1,27 +0,0 @@
'use client';
import { useResponse } from '@/shared/components/response/ResponseContext';
import { useState } from 'react';
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
import { TImovelIndexData } from '../../data/TImovel/TImovelIndexData';
export const useTImovelIndexHook = () => {
const { setResponse } = useResponse();
const [tImovel, setTImovel] = useState<TImovelInterface[]>([]);
const indexTImovel = async () => {
const response = await TImovelIndexData();
// Armazena os dados consultados
setTImovel(response.data);
// Define os dados do componente de resposta (toast, modal, etc)
setResponse(response);
};
return {
tImovel,
indexTImovel
};
};

View file

@ -1,33 +0,0 @@
'use client';
import { useResponse } from '@/shared/components/response/ResponseContext';
import { useState } from 'react';
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
import { TImovelSaveService } from '../../services/TImovel/TImovelSaveService';
export const useTImovelSaveHook = () => {
const { setResponse } = useResponse();
const [tImovel, setTImovel] = useState<TImovelInterface>();
// controla se o formulário está aberto ou fechado
const [isOpen, setIsOpen] = useState(false);
const saveTImovel = async (data: TImovelInterface) => {
const response = await TImovelSaveService(data);
// Armazena os dados da repsota
setTImovel(response.data);
// Define os dados da respota(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 { tImovel, saveTImovel };
};

View file

@ -0,0 +1,68 @@
'use client';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
// Importa o schema de validação (Zod)
import { UserSchema } from '../../schemas/User/UserSchema';
// Importa o enum com o status
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
// Tipagem do formulário (interface compartilhada)
import { FormValues } from '../../interfaces/User/UserFormInterface';
/**
* Hook responsável por inicializar e gerenciar o estado do formulário de usuários.
* Centraliza a lógica de criação, reset e carregamento de dados.
*/
export function useUserFormHook(data: FormValues | null) {
// Inicializa o React Hook Form com validação baseada no Zod
const form = useForm<FormValues>({
resolver: zodResolver(UserSchema), // Aplica o schema para validação automática
defaultValues: {
user_id: 0,
name: '',
email: '',
password: '',
team: '',
cargo: '',
status: SituacoesEnum.ATIVO,
user_id_create: null,
user_id_update: null,
date_register: new Date().toISOString(),
date_update: null,
} as FormValues,
});
/**
* Efeito responsável por atualizar o formulário
* sempre que houver dados (modo edição) ou resetar (modo criação).
*/
useEffect(() => {
if (data) {
// Modo edição → carrega dados do usuário
form.reset(data);
console.log('Form carregado com dados:', data);
} else {
// Modo criação → limpa o formulário
form.reset({
user_id: 0,
name: '',
email: '',
password: '',
team: '',
cargo: '',
status: SituacoesEnum.ATIVO,
user_id_create: null,
user_id_update: null,
date_register: new Date().toISOString(),
date_update: null,
});
}
}, [data]); // Atualiza sempre que "data" mudar
// Retorna o objeto form para uso no componente
return form;
}

View file

@ -0,0 +1,65 @@
'use client';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { UserLoginSchema } from '../../schemas/User/UserLoginSchema';
import UserLoginService from '../../services/User/UserLoginService';
import z from 'zod';
/**
* Tipagem dos valores do formulário.
* Inclui o campo "rememberMe" que não está no schema Zod original.
*/
export type LoginFormValues = z.infer<typeof UserLoginSchema> & {
rememberMe: boolean;
};
/**
* Hook responsável por gerenciar o formulário de login e o comportamento
* de lembrar acesso (localStorage + API).
*/
export function useUserFormLoginHook() {
const [loading, setLoading] = useState(false);
// Inicializa o formulário com validação Zod
const form = useForm<LoginFormValues>({
resolver: zodResolver(UserLoginSchema),
defaultValues: {
email: '',
password: '',
rememberMe: false,
},
});
// Carrega o e-mail salvo no localStorage, se existir
useEffect(() => {
const savedEmail = localStorage.getItem('remembered_email');
if (savedEmail) {
form.setValue('email', savedEmail);
form.setValue('rememberMe', true);
}
}, [form]);
/**
* Função de envio do formulário autentica o usuário e
* salva o e-mail no localStorage se o "Lembrar acesso" estiver marcado.
*/
const onSubmit = async (values: LoginFormValues) => {
try {
setLoading(true);
await UserLoginService(values);
if (values.rememberMe) {
localStorage.setItem('remembered_email', values.email);
} else {
localStorage.removeItem('remembered_email');
}
} finally {
setLoading(false);
}
};
// Retorna o formulário e os estados necessários para o componente
return { form, onSubmit, loading };
}

View file

@ -1,11 +1,26 @@
'use client';
'use client'; // Indica que este código roda no lado do cliente (Next.js)
import { UserLogoutService } from '../../services/User/UserLogoutService';
import { useRouter } from 'next/navigation'; // Hook do Next.js para redirecionamentos no cliente
import { UserLogoutService } from '../../services/User/UserLogoutService'; // Importa o serviço de logout
// Hook customizado responsável por encapsular a lógica de logout
export const useUserLogoutHook = () => {
const router = useRouter(); // Inicializa o roteador do Next.js
// Função assíncrona responsável por executar o logout
const logoutUsuario = async () => {
await UserLogoutService('access_token');
try {
// Chama o serviço no servidor para apagar o cookie do token
await UserLogoutService('access_token');
// Redireciona o usuário para a tela de login após o logout
router.push('/login');
} catch (error) {
// Captura e exibe eventuais erros no processo de logout
console.error('Erro ao fazer logout:', error);
}
};
// Retorna a função de logout para ser usada em qualquer componente
return { logoutUsuario };
};

View file

@ -0,0 +1,16 @@
/**
* Interface que representa a tabela `client` do banco de dados `monitoring`.
* Cada campo reflete a tipagem e as restrições da DDL original.
*/
export interface ClientInterface {
client_id?: number; // ID único do cliente (chave primária, gerada automaticamente)
cns?: string | null; // Código CNS (campo opcional)
name: string; // Nome do cliente (campo obrigatório)
date_register?: string | null; // Data/hora do registro (timestamp gerado automaticamente)
state?: string | null; // Sigla do estado (ex: 'SP', 'RJ', etc.)
city?: string | null; // Nome da cidade
responsible?: string | null; // Responsável principal pelo cliente
consultant?: string | null; // Nome do consultor associado
type_contract?: string | null; // Tipo de contrato (ex: 'A' = anual, 'M' = mensal, etc.)
status?: string | null; // Tipo de contrato (ex: 'A' = Ativo, 'I' = Inativo, etc.)
}

View file

@ -0,0 +1,21 @@
import { ClientInterface } from './ClientInterface';
/**
* Interface que define as propriedades esperadas pelo componente ClientTable.
*/
export interface ClientTableInterface {
data: ClientInterface[]; // Lista de clientes a exibir
// Objeto opcional de paginação retornado pela API
pagination?: {
total_records: number; // Total de registros encontrados
total_pages: number; // Total de páginas disponíveis
current_page: number; // Página atual
next_page?: number | null; // Próxima página (opcional)
first?: number; // Quantidade padrão por página
skip?: number; // Offset inicial
};
onEdit: (client: ClientInterface) => void; // Ação ao clicar em editar
onDelete: (client: ClientInterface) => void; // Ação ao clicar em remover
}

View file

@ -0,0 +1,10 @@
/**
* Interface que representa a tabela `log` do banco de dados `monitoring`.
* Cada campo reflete a tipagem e as restrições definidas na DDL original.
*/
export interface LogInterface {
log_id?: number; // ID único do log (chave primária, gerada automaticamente pelo banco)
client_id: number; // ID do cliente relacionado (chave estrangeira obrigatória)
date_post?: string; // Data e hora do registro (gerada automaticamente pelo banco)
file: object; // Dados em formato JSON (ex: informações do arquivo ou operação)
}

View file

@ -0,0 +1,18 @@
// Define o tipo base dos valores do formulário (importa do schema do usuário)
import { z } from 'zod';
import { UserSchema } from '../../schemas/User/UserSchema';
// Cria o tipo inferido a partir do schema do usuário
export type FormValues = z.infer<typeof UserSchema>;
/**
* Interface com as propriedades aceitas pelo componente UserForm.
* Isso facilita a reutilização e deixa o código mais limpo.
*/
export interface UserFormInterface {
isOpen: boolean; // Controla se o diálogo está aberto
data: FormValues | null; // Dados do usuário para edição (ou null no modo de criação)
onClose: (item: null, isFormStatus: boolean) => void; // Função executada ao fechar o diálogo
onSave: (data: FormValues) => void; // Função executada ao salvar o formulário
buttonIsLoading: boolean; // Define se o botão de envio está em modo de carregamento
}

View file

@ -0,0 +1,8 @@
import React from 'react';
/**
* Interface de tipagem para o componente LoginForm.
*/
export interface UserFormLoginInterface extends React.ComponentProps<'div'> {
className?: string; // Classe CSS opcional
}

View file

@ -13,3 +13,4 @@ export interface UserInterface {
user_id_create: number | null,
user_id_update: number | null
}

View file

@ -0,0 +1,10 @@
import { UserInterface } from './UserInterface';
/**
* Interface que define as propriedades esperadas pelo componente UserTable.
*/
export interface UserTableInterface {
data: UserInterface[]; // Lista de usuários a exibir
onEdit: (user: UserInterface) => void; // Ação ao clicar em editar
onDelete: (user: UserInterface) => void; // Ação ao clicar em remover
}

View file

@ -0,0 +1,67 @@
import { z } from "zod";
/**
* Schema de validação para a tabela `client`
* Baseado na DDL do banco de dados `monitoring.client`
*/
export const ClientSchema = z.object({
client_id: z.number().optional(), // ID gerado automaticamente (AUTO_INCREMENT)
cns: z
.string()
.max(10, { message: "O campo CNS deve ter no máximo 10 caracteres." })
.nullable()
.optional(), // Campo opcional e pode ser nulo
name: z
.string()
.min(3, { message: "O nome deve ter no mínimo 3 caracteres." })
.max(550, { message: "O nome deve ter no máximo 550 caracteres." }), // Campo obrigatório
date_register: z
.string()
.nullable()
.optional(), // Timestamp gerado automaticamente pelo banco
state: z
.string()
.length(2, { message: "O estado deve conter exatamente 2 caracteres (ex: 'SP')." })
.nullable()
.optional(), // Campo opcional
city: z
.string()
.max(160, { message: "O nome da cidade deve ter no máximo 160 caracteres." })
.nullable()
.optional(),
responsible: z
.string()
.max(160, { message: "O nome do responsável deve ter no máximo 160 caracteres." })
.nullable()
.optional(),
consultant: z
.string()
.max(160, { message: "O nome do consultor deve ter no máximo 160 caracteres." })
.nullable()
.optional(),
type_contract: z
.string()
.length(1, { message: "O tipo de contrato deve conter apenas 1 caractere." })
.nullable()
.optional(), // Pode representar tipo de contrato (ex: 'A' = anual, 'M' = mensal)
status: z
.string()
.length(1, { message: "O status de deve conter apenas 1 caractere." })
.nullable()
.optional(), // Pode representar tipo de status (ex: 'A' = ativo, 'I' = inativo)
});
/**
* Tipo TypeScript inferido automaticamente a partir do schema.
* Permite utilizar a tipagem do Zod em qualquer lugar do código.
*/
export type ClientSchemaType = z.infer<typeof ClientSchema>;

View file

@ -0,0 +1,43 @@
import { z } from "zod";
/**
* Schema de validação para a tabela `log`
* Baseado na DDL do banco de dados `monitoring.log`
*/
export const LogSchema = z.object({
// ID do log, gerado automaticamente pelo banco (AUTO_INCREMENT)
log_id: z.number().optional(),
// ID do cliente relacionado — campo obrigatório
client_id: z
.number({
message: "O campo client_id deve ser um número.",
})
.int()
.positive({ message: "O client_id deve ser um número positivo." }),
// Data e hora da inserção — gerada automaticamente pelo banco (CURRENT_TIMESTAMP)
date_post: z
.string()
.datetime({ message: "O campo date_post deve ser uma data/hora válida (ISO 8601)." })
.optional()
.nullable(),
// Campo JSON que armazena informações sobre o arquivo
// Pode ser um objeto com qualquer estrutura válida em JSON
file: z
.any()
.refine((val) => val !== undefined, {
message: "O campo file é obrigatório e deve conter um JSON válido.",
})
.refine(
(val) => typeof val === "object" && val !== null,
{ message: "O campo file deve ser um objeto JSON válido." }
),
});
/**
* Tipo TypeScript inferido automaticamente a partir do schema.
* Permite utilizar a tipagem do Zod em qualquer parte do código.
*/
export type LogSchemaType = z.infer<typeof LogSchema>;

View file

@ -1,6 +1,12 @@
import { z } from 'zod';
/**
* Schema de validação do login do usuário
* - Garante que email e senha sejam obrigatórios
* - `rememberMe` é opcional e booleano
*/
export const UserLoginSchema = z.object({
email: z.string().min(1, 'O campo deve ser preenchido'),
password: z.string().min(1, 'O campo deve ser preenchido'),
rememberMe: z.boolean().optional(),
});

View file

@ -6,7 +6,7 @@ export const UserSchema = z.object({
name: z.string().min(3, { message: "O nome deve ter no mínimo 3 caracteres." }),
email: z.email({ message: "Por favor, insira um email válido." }),
password: z.string().min(6, { message: "A senha deve ter pelo menos 6 caracteres." }),
position: z.string().nullable().optional(),
cargo: z.string().nullable().optional(),
team: z.string().min(1, { message: "A equipe é obrigatória." }),
status: z.enum(SituacoesEnum), // 'A' ou 'I'
date_register: z.string().optional(),

View file

@ -0,0 +1,31 @@
'use server'
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler"
// Importa o decorador responsável por tratar erros de forma padronizada em funções assíncronas
import { ClientDeleteData } from "../../data/Client/ClientDeleteData"
// Importa a função responsável por realizar a operação real de exclusão do Cliente (camada de acesso a dados)
// Função principal que executa o processo de exclusão de um Cliente
async function executeClientDeleteService(usuarioId: number) {
// Validação básica: impede que IDs inválidos ou menores que 1 sejam processados
if (usuarioId <= 0) {
return {
'code': 400, // Código HTTP indicando requisição inválida
'message': 'Cliente informado inválido', // Mensagem de erro clara e descritiva
}
}
// Chama a função que realiza a exclusão no banco ou API, passando o ID do Cliente
const response = await ClientDeleteData(usuarioId)
// Retorna a resposta vinda da camada de dados, que pode conter status e mensagem
return response
}
// Exporta a função encapsulada com o tratador global de erros
export const ClientDeleteService = withClientErrorHandler(executeClientDeleteService)
// `withClientErrorHandler` garante que exceções não tratadas sejam capturadas e formatadas de forma uniforme
// Dessa forma, evita que erros internos quebrem o fluxo da aplicação

View file

@ -0,0 +1,24 @@
'use server';
// Indica que este código será executado no lado do servidor (Server Component ou ação server-side do Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
import { ClientIndexData } from "../../data/Client/ClientIndexData";
// Função principal que executa a chamada para buscar a lista de clientes
async function executeClientIndexService() {
try {
// Chama o método responsável por buscar os dados da API (camada "data")
const response = await ClientIndexData();
console.log('Kenão')
// Retorna a resposta recebida da API (geralmente um objeto com status, mensagem e dados)
return response;
} catch (error) {
// Caso ocorra algum erro inesperado, lança o erro para ser tratado pelo wrapper
throw error;
}
}
// Exporta o serviço com tratamento global de erros usando o wrapper `withClientErrorHandler`
export const ClientIndexService = withClientErrorHandler(executeClientIndexService);

View file

@ -0,0 +1,20 @@
'use server'
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
// Importa o utilitário responsável por envolver a função com tratamento global de erros do cliente
import { ClientSaveData } from "../../data/Client/ClientSaveData";
// Importa a função que realiza a operação de salvamento de dados do cliente (chamada à API ou banco de dados)
// Função principal que executa o salvamento de dados de um cliente
async function executeClientSave(form: any) {
// Chama a função que realmente salva os dados, repassando o formulário recebido
return await ClientSaveData(form);
}
// Exporta o serviço, agora protegido pelo tratador de erros
export const ClientSaveService = withClientErrorHandler(executeClientSave);
// `withClientErrorHandler` adiciona camadas de controle de exceção e formatação de erro à função principal
// Assim, qualquer falha em `executeClientSave` será capturada e tratada de forma padronizada

View file

@ -0,0 +1,22 @@
'use server'
// Indica que este arquivo é um "Server Action", executado no lado do servidor pelo Next.js
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
// Importa o wrapper responsável por padronizar o tratamento de erros nas requisições do cliente
import { LogIndexByClientIDData } from "../../data/Log/LogIndexByClientIDData";
// Importa a função que acessa a camada de dados e retorna as informações do usuário a partir do ID
// Função assíncrona principal responsável por buscar um usuário pelo seu ID
async function executeLogIndexByIDService(client_id: number) {
// Executa a função de busca de usuário, passando o ID recebido como parâmetro
const response = await LogIndexByClientIDData(client_id);
// Retorna a resposta vinda da camada de dados (usuário encontrado ou erro)
return response;
}
// Exporta o serviço com o tratamento de erros encapsulado
// O wrapper "withClientErrorHandler" assegura respostas consistentes em caso de falhas
export const LogIndexByIDService = withClientErrorHandler(executeLogIndexByIDService);

View file

@ -0,0 +1,22 @@
'use server'
// Indica que este arquivo é um "Server Action", executado no lado do servidor pelo Next.js
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
// Importa o wrapper responsável por padronizar o tratamento de erros nas requisições do Loge
import { LogServerData } from "../../data/Log/LogServerData";
// Importa a função que acessa a camada de dados e retorna as informações do usuário a partir do ID
// Função assíncrona principal responsável por buscar um usuário pelo seu ID
async function executeLogServerService(client_id: number) {
// Executa a função de busca de usuário, passando o ID recebido como parâmetro
const response = await LogServerData(client_id);
// Retorna a resposta vinda da camada de dados (usuário encontrado ou erro)
return response;
}
// Exporta o serviço com o tratamento de erros encapsulado
// O wrapper "withClientErrorHandler" assegura respostas consistentes em caso de falhas
export const LogServerService = withClientErrorHandler(executeLogServerService);

View file

@ -1,11 +0,0 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { TImovelDeleteData } from '../../data/TImovel/TImovelDeleteData';
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
async function executeTImovelDeleteService(data: TImovelInterface) {
const response = await TImovelDeleteData(data);
return response;
}
export const TImovelDeleteService = withClientErrorHandler(executeTImovelDeleteService);

View file

@ -1,10 +0,0 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { TImovelIndexData } from '../../data/TImovel/TImovelIndexData';
export default async function executeTImovelIndexService() {
const response = await TImovelIndexData();
return response;
}
export const TImovelIndexService = withClientErrorHandler(executeTImovelIndexService);

View file

@ -1,11 +0,0 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { TImovelSaveData } from '../../data/TImovel/TImovelSaveData';
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
async function executeTImovelSaveService(data: TImovelInterface) {
const response = await TImovelSaveData(data);
return response;
}
export const TImovelSaveService = withClientErrorHandler(executeTImovelSaveService);

View file

@ -1,20 +1,30 @@
'use server'
'use server'
// Indica que este módulo será executado no lado do servidor (Server Actions do Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler"
import { UserDeleteData } from "../../data/User/UserDeleteData"
// Importa um wrapper responsável por tratar erros de forma padronizada no cliente
import { UserDeleteData } from "../../data/User/UserDeleteData"
// Importa a função responsável por realizar a exclusão do usuário no nível de dados (requisição à API ou banco)
// Função assíncrona principal que executa o serviço de exclusão de usuário
async function executeUserDeleteService(usuarioId: number) {
// Verifica se o ID do usuário informado é inválido (zero, negativo ou inexistente)
if (usuarioId <= 0) {
return {
'code': 400,
'message': 'Usuário informado inválido',
'code': 400, // Código HTTP 400 - requisição inválida
'message': 'Usuário informado inválido', // Mensagem de erro clara
}
}
// Chama a função de exclusão no módulo de dados, passando o ID do usuário
const response = await UserDeleteData(usuarioId)
return response
// Retorna a resposta obtida da camada de dados (sucesso ou erro)
return response
}
export const UserDeleteService = withClientErrorHandler(executeUserDeleteService)
// Exporta o serviço já encapsulado com o handler de erros
// Isso garante que qualquer exceção seja tratada de forma uniforme no frontend
export const UserDeleteService = withClientErrorHandler(executeUserDeleteService)

View file

@ -1,14 +1,22 @@
'use server'
'use server'
// Define que este módulo será executado no lado do servidor (Server Actions do Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
import { UserIndexByEmailData } from "../../data/User/UserIndexByEmailData";
// Importa um wrapper para padronizar o tratamento de erros em chamadas do cliente
import { UserIndexByEmailData } from "../../data/User/UserIndexByEmailData";
// Importa a função responsável por buscar informações do usuário no banco de dados (ou API) com base no e-mail
// Função assíncrona principal que executa a busca de um usuário pelo e-mail
async function executeUserIndexByEmailService(email: string) {
// Chama a função de acesso a dados passando o e-mail como parâmetro
const response = await UserIndexByEmailData(email);
// Retorna a resposta obtida da camada de dados (usuário encontrado ou erro)
return response;
}
export const UserIndexByEmailService = withClientErrorHandler(executeUserIndexByEmailService)
// Exporta o serviço encapsulado com o handler de erros
// Isso garante tratamento uniforme de erros e mensagens entre cliente e servidor
export const UserIndexByEmailService = withClientErrorHandler(executeUserIndexByEmailService);

View file

@ -1,14 +1,22 @@
'use server'
'use server'
// Indica que este arquivo é um "Server Action", executado no lado do servidor pelo Next.js
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
import { UserIndexByIDData } from "../../data/User/UserIndexByIDData";
// Importa o wrapper responsável por padronizar o tratamento de erros nas requisições do cliente
import { UserIndexByIDData } from "../../data/User/UserIndexByIDData";
// Importa a função que acessa a camada de dados e retorna as informações do usuário a partir do ID
// Função assíncrona principal responsável por buscar um usuário pelo seu ID
async function executeUserIndexByIDService(user_id: number) {
// Executa a função de busca de usuário, passando o ID recebido como parâmetro
const response = await UserIndexByIDData(user_id);
// Retorna a resposta vinda da camada de dados (usuário encontrado ou erro)
return response;
}
export const UserIndexByIDService = withClientErrorHandler(executeUserIndexByIDService)
// Exporta o serviço com o tratamento de erros encapsulado
// O wrapper "withClientErrorHandler" assegura respostas consistentes em caso de falhas
export const UserIndexByIDService = withClientErrorHandler(executeUserIndexByIDService);

View file

@ -1,14 +1,22 @@
'use server'
'use server'
// Define que este módulo será executado no lado do servidor (Server Action do Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
import { UserIndexData } from "../../data/User/UserIndexData";
// Importa o wrapper responsável por tratar erros de forma padronizada entre cliente e servidor
import { UserIndexData } from "../../data/User/UserIndexData";
// Importa a função responsável por buscar a lista de usuários na camada de dados (API ou banco de dados)
// Função assíncrona principal que executa a listagem de todos os usuários
async function executeUserIndexService() {
// Chama a função da camada de dados que retorna todos os usuários
const response = await UserIndexData();
// Retorna o resultado obtido da função de dados (lista de usuários ou erro)
return response;
}
export const UserIndexService = withClientErrorHandler(executeUserIndexService)
// Exporta o serviço encapsulado com o handler de erros
// Isso garante que qualquer erro seja capturado e tratado de forma consistente no frontend
export const UserIndexService = withClientErrorHandler(executeUserIndexService);

View file

@ -1,36 +1,47 @@
'use server';
'use server';
// Define que este módulo será executado no lado do servidor (Server Action do Next.js)
import { cookies } from 'next/headers';
import { cookies } from 'next/headers';
// Importa o utilitário do Next.js responsável por manipular cookies no lado do servidor
import UserLoginData from '../../data/User/UserLoginData';
import { redirect } from 'next/navigation';
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
import UserLoginData from '../../data/User/UserLoginData';
// Importa a função da camada de dados responsável por validar o login do usuário e retornar o token
import { redirect } from 'next/navigation';
// Importa a função de redirecionamento do Next.js para navegação após o login
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
// Importa o wrapper genérico que trata erros de forma padronizada (não utilizado diretamente aqui)
// Função principal responsável por autenticar o usuário
export default async function UserLoginService(form: any) {
// Obtem a resposta da requisição
// Envia os dados do formulário para a camada de dados e aguarda a resposta
const response = await UserLoginData(form);
console.log("service",response)
// Verifica se localizou o usuário
// Exibe a resposta no console (útil para debug no ambiente de desenvolvimento)
console.log("service", response);
// Verifica se o usuário foi encontrado (caso contrário, retorna erro 404)
if (response.data.user_id <= 0) {
return {
code: 404,
message: 'Não foi localizado o usuário',
code: 404, // Código de erro HTTP simulando "não encontrado"
message: 'Não foi localizado o usuário', // Mensagem informativa ao cliente
};
}
// Importação do manipulador de cookies
// Obtém o manipulador de cookies do contexto do servidor
const cookieStore = await cookies();
// Cria um novo cookie
// Define um cookie com o token de autenticação
cookieStore.set('access_token', response.data.token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
path: '/',
maxAge: 60 * 60 * 24,
httpOnly: true, // Impede acesso via JavaScript (segurança contra XSS)
secure: process.env.NODE_ENV === 'production', // Garante HTTPS em produção
sameSite: 'strict', // Restringe envio de cookies entre domínios
path: '/', // Torna o cookie acessível em toda a aplicação
maxAge: 60 * 60 * 24, // Define validade de 24 horas (em segundos)
});
// Redireciona para a págian desejada
// Redireciona o usuário autenticado para a página administrativa de usuários
redirect('/administrativo/usuarios');
}

View file

@ -1,18 +1,20 @@
'use server';
'use server'; // Indica que este arquivo será executado apenas no servidor (Server Action)
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler'; // Middleware para capturar erros controladamente
import { cookies } from 'next/headers'; // API do Next.js para manipular cookies no servidor
// Função principal que apaga o cookie de autenticação
async function executeUserLogoutService(token: string) {
// Obtém o gerenciador de cookies do contexto do servidor
const cookieStore = await cookies();
cookieStore.set(token, '', {
expires: new Date(0),
path: '/',
});
redirect('/login');
// Remove o cookie do token, definindo expiração retroativa (logout efetivo)
cookieStore.set(token, '', {
expires: new Date(0), // Define data de expiração no passado
path: '/', // Garante que o cookie seja removido em toda a aplicação
});
}
export const UserLogoutService = withClientErrorHandler(executeUserLogoutService)
// Exporta a função com tratamento de erro aplicado
// Assim, qualquer exceção dentro de executeUserLogoutService é tratada de forma controlada
export const UserLogoutService = withClientErrorHandler(executeUserLogoutService);

View file

@ -1,22 +1,30 @@
'use server'
// Define que este módulo é executado no lado do servidor (Server Actions do Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
import { UserReadData } from "../../data/User/UserReadData";
// Importa o wrapper responsável por tratar erros de forma padronizada entre cliente e servidor
import { UserReadData } from "../../data/User/UserReadData";
// Importa a função que acessa o banco de dados (ou API) para obter os dados completos de um usuário específico
// Função principal do serviço que busca detalhes de um usuário com base no ID informado
async function executeUserReadService(usuarioId: number) {
// Verifica se o id informado é válido
// Validação simples do ID, evitando requisições desnecessárias ou inválidas
if (usuarioId <= 0) {
return {
'code': 400,
'message': 'Usuário informado inválido',
'code': 400, // Código de erro para requisição inválida
'message': 'Usuário informado inválido', // Mensagem clara ao cliente
}
}
// Chama a função da camada de dados para buscar as informações do usuário
const response = await UserReadData(usuarioId);
// Retorna o resultado vindo da camada de dados (dados do usuário ou erro)
return response
}
export const UserReadService = withClientErrorHandler(executeUserReadService)
// Exporta o serviço encapsulado com o tratamento automático de erros
// Garantindo uma resposta consistente para o frontend em caso de falhas
export const UserReadService = withClientErrorHandler(executeUserReadService);

View file

@ -1,12 +1,20 @@
'use server'
'use server'
// Define que este módulo é uma Server Action, ou seja, será executado no lado do servidor (Next.js)
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
import { UserSaveData } from "../../data/User/UserSaveData";
// Importa o wrapper responsável por capturar e padronizar erros em chamadas do cliente
import { UserSaveData } from "../../data/User/UserSaveData";
// Importa a função da camada de dados responsável por salvar (inserir ou atualizar) informações de um usuário no banco
// Função assíncrona principal que executa o salvamento dos dados de um usuário
async function executeUserSave(form: any) {
// Chama diretamente a função de persistência passando o formulário recebido
// O "await" garante que a função aguarde o resultado antes de retornar
return await UserSaveData(form);
}
export const UserSaveService = withClientErrorHandler(executeUserSave)
// Exporta o serviço encapsulado com o handler de erros
// Isso garante que qualquer exceção seja tratada de forma uniforme no frontend
export const UserSaveService = withClientErrorHandler(executeUserSave);