[VDU-9] feat(implementação) ainda na fase de implementação, mas não está completo

This commit is contained in:
= 2025-10-14 15:26:47 -03:00
parent cb51e0ed19
commit a1d08b9be0
12 changed files with 458 additions and 15 deletions

View file

@ -0,0 +1,134 @@
'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 Usuário
import UserTable from '@/packages/administrativo/components/User/UserTable';
import UserForm from '@/packages/administrativo/components/User/UserForm';
// Hooks Específicos de Usuário
import { useUserIndexHook } from '@/packages/administrativo/hooks/User/useUserIndexHook';
import { useUserSaveHook } from '@/packages/administrativo/hooks/User/useUserSaveHook';
import { useUserDeleteHook } from '@/packages/administrativo/hooks/User/useUserDeleteHook';
// Interface
import { UserInterface } from '@/packages/administrativo/interfaces/User/UserInterface';
import { UserSchema } from '@/packages/administrativo/schemas/User/UserSchema';
type FormValues = z.infer<typeof UserSchema>;
export default function UsersPage() {
// 1. Hooks de dados para Usuário
const { usuarios, fetchUsuarios } = useUserIndexHook();
const { saveUser } = useUserSaveHook();
const { removeUser } = useUserDeleteHook(); // Presumindo que o hook existe e expõe `removeUser`
// 2. Estados da página
const [selectedUser, setSelectedUser] = useState<UserInterface | null>(null);
const [isFormOpen, setIsFormOpen] = useState(false);
const [userToDelete, setUserToDelete] = useState<UserInterface | 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((user: UserInterface | null) => {
setSelectedUser(user);
setIsFormOpen(true);
}, []);
const handleCloseForm = useCallback(() => {
setSelectedUser(null);
setIsFormOpen(false);
}, []);
// 4. Função para salvar (criar ou editar)
const handleSave = useCallback(
async (formData: FormValues) => {
await saveUser(formData);
handleCloseForm(); // Fecha o modal após salvar
await fetchUsuarios(); // Atualiza a lista de usuários
},
[saveUser, fetchUsuarios, handleCloseForm],
);
// 5. Funções para exclusão
const handleConfirmDelete = useCallback(
(user: UserInterface) => {
setUserToDelete(user);
openConfirmDialog();
},
[openConfirmDialog],
);
const handleDelete = useCallback(async () => {
if (!userToDelete) return;
await removeUser(userToDelete); // Chama o hook de remoção
await fetchUsuarios(); // Atualiza a lista
setUserToDelete(null); // Limpa o estado
handleCancel(); // Fecha o modal de confirmação
}, [userToDelete, removeUser, fetchUsuarios, handleCancel]);
// 6. Busca inicial dos dados
useEffect(() => {
fetchUsuarios();
}, []);
// 7. Renderização condicional de loading
if (!usuarios) {
return <Loading type={2} />;
}
// 8. Renderização da página
return (
<div>
<Header
title={'Usuários'}
description={'Gerenciamento de Usuários do Sistema'}
buttonText={'Novo Usuário'}
buttonAction={() => handleOpenForm(null)}
/>
<Card>
<CardContent>
<UserTable
data={usuarios}
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 usuário "${userToDelete?.name}"?`}
confirmText="Sim, excluir"
cancelText="Cancelar"
onConfirm={handleDelete}
onCancel={handleCancel}
/>
<UserForm
isOpen={isFormOpen}
data={selectedUser}
onClose={handleCloseForm}
onSave={handleSave}
/>
</div>
);
}

View file

@ -0,0 +1,164 @@
'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 { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
import { UserSchema } from '../../schemas/User/UserSchema';
type FormValues = z.infer<typeof UserSchema>;
interface Props {
isOpen: boolean;
data: FormValues | null;
onClose: () => void;
onSave: (data: FormValues) => void;
}
export default function UserForm({ isOpen, data, onClose, onSave }: Props) {
const form = useForm<FormValues>({
resolver: zodResolver(UserSchema),
defaultValues: {
name: '',
email: '',
password: '',
team: '',
status: SituacoesEnum.ATIVO,
},
});
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
if (data) {
form.reset(data);
} else {
form.reset({ // Garante que o form limpe ao abrir para criar novo
name: '',
email: '',
password: '',
team: '',
status: SituacoesEnum.ATIVO,
});
}
}, [data, form, isOpen]); // isOpen garante o reset ao reabrir
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>{data ? 'Editar Usuário' : 'Novo Usuário'}</DialogTitle>
<DialogDescription>Gerencie os dados do usuário aqui.</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="space-y-6">
{/* Nome */}
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Nome</FormLabel>
<FormControl>
<Input {...field} placeholder="Digite o nome completo" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* 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>
)}
/>
{/* 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>
)}
/>
{/* 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>
)}
/>
{/* Status */}
<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é */}
<DialogFooter className="mt-4">
<DialogClose asChild>
<Button variant="outline" type="button" onClick={onClose}>
Cancelar
</Button>
</DialogClose>
<Button type="submit">Salvar</Button>
</DialogFooter>
{/* Campo oculto para o ID */}
<input type="hidden" {...form.register('user_id')} />
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View file

@ -6,7 +6,7 @@ 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 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';
@ -29,6 +29,7 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
// 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);

View file

@ -0,0 +1,105 @@
'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 { UserInterface } from '../../interfaces/User/UserInterface'; // Ajuste o caminho conforme necessário
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
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) {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>#</TableHead>
<TableHead>Status</TableHead>
<TableHead>Nome</TableHead>
<TableHead>Email</TableHead>
<TableHead>Equipe</TableHead>
<TableHead className="text-right">Ações</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((user) => (
<TableRow key={user.user_id}>
<TableCell className="font-medium">{user.user_id}</TableCell>
<TableCell>
<StatusBadge status={user.status} />
</TableCell>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>{user.team}</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(user)}
>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => onDelete(user)}
>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}

View file

@ -5,7 +5,7 @@ import API from '@/shared/services/api/Api';
import { AuthenticateUserInterface } from '@/shared/interfaces/AuthenticateUserInterface';
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
async function executeUserLoginData(form: AuthenticateUserInterface) {
export default async function UserLoginData(form: any) {
const api = new API();
const response = await api.send({
@ -14,8 +14,8 @@ async function executeUserLoginData(form: AuthenticateUserInterface) {
body: form,
});
console.log("resposta da api",response)
return response;
}
export const UserLoginData = withClientErrorHandler(executeUserLoginData)
}

View file

@ -0,0 +1,28 @@
'use client';
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import { UserDeleteService } from '../../services/User/UserDeleteService'; // Ajuste o caminho conforme necessário
import { UserInterface } from '../../interfaces/User/UserInterface'; // Ajuste o caminho
export const useUserDeleteHook = () => {
// Hook de contexto para fornecer feedback (toast, modal, etc.)
const { setResponse } = useResponse();
const removeUser = async (user: UserInterface) => {
try {
// Chama o serviço de exclusão, passando apenas o ID do usuário
const response = await UserDeleteService(user.user_id);
// Define a resposta para que o ResponseContext possa exibir um feedback
setResponse(response);
} catch (error) {
// O withClientErrorHandler já trata o erro, mas um log pode ser útil
console.error('Erro ao remover usuário:', error);
}
};
// Retorna a função de remoção e o estado de carregamento
return { removeUser };
};

View file

@ -5,7 +5,7 @@ import { UserInterface } from '../../interfaces/User/UserInterface';
import { UserReadService } from '../../services/User/UserReadService';
import { useResponse } from '@/shared/components/response/ResponseContext';
export const useGUserReadHooks = () => {
export const useUserReadHooks = () => {
const { setResponse } = useResponse();
const [User, setUser] = useState<UserInterface>();

View file

@ -5,7 +5,7 @@ import { UserInterface } from '../../interfaces/User/UserInterface';
import { UserSaveService } from '../../services/User/UserSaveService';
import { useResponse } from '@/shared/components/response/ResponseContext';
export const useGUserSaveHook = () => {
export const useUserSaveHook = () => {
const { setResponse } = useResponse();
const [User, setUser] = useState<UserInterface>();

View file

@ -0,0 +1,11 @@
import z from 'zod';
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
export const UserSchema = z.object({
user_id: z.number().optional(),
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().optional(),
team: z.string().min(1, { message: 'A equipe é obrigatória.' }),
status: z.enum(SituacoesEnum),
});

View file

@ -2,13 +2,15 @@
import { cookies } from 'next/headers';
import { UserLoginData } from '../../data/User/UserLoginData';
import UserLoginData from '../../data/User/UserLoginData';
import { redirect } from 'next/navigation';
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
async function executeUserLoginService(form: any) {
export default async function UserLoginService(form: any) {
// Obtem a resposta da requisição
const response = await UserLoginData(form);
console.log("service",response)
// Verifica se localizou o usuário
if (response.data.user_id <= 0) {
return {
@ -30,7 +32,5 @@ async function executeUserLoginService(form: any) {
});
// Redireciona para a págian desejada
redirect('/user');
redirect('/administrativo/usuarios');
}
export const UserLoginService = withClientErrorHandler(executeUserLoginService)

View file

@ -1,4 +1,4 @@
export enum SituacoesEnum {
A = 'A',
I = 'I',
ATIVO = 'A',
INATIVO = 'I',
}

View file

@ -48,7 +48,7 @@ export default class API {
headers: {
Accept: `${this.ApiSchema.contentType}`,
'Content-Type': `${this.ApiSchema.contentType}`,
Authorization: `Bearer ${this.ApiSchema.token}`,
Authorization: `Bearear ${this.ApiSchema.token}`,
},
...(filteredBody && { body: JSON.stringify(filteredBody) }),
},