[fe-02] feat(componente): Cria o componente que manipula as respostas vindas do servidor
This commit is contained in:
parent
1407f0df90
commit
7ec1259ba0
21 changed files with 326 additions and 64 deletions
|
|
@ -1,39 +1,31 @@
|
|||
'use client'
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
import {
|
||||
toast
|
||||
} from "sonner";
|
||||
import {
|
||||
Card,
|
||||
CardContent
|
||||
} from "@/components/ui/card";
|
||||
|
||||
import GUsuarioRead from "@/app/(protected)/(administrativo)/_services/g_usuario/GUsuarioRead";
|
||||
import { useGUsuarioReadHooks } from "@/app/(protected)/(administrativo)/_hooks/g_usuario/useGUsuarioReadHooks";
|
||||
import Usuario from "@/app/(protected)/(administrativo)/_interfaces/IGUsuario";
|
||||
import Loading from "@/app/_components/loading/loading";
|
||||
|
||||
export default function UsuarioDetalhes() {
|
||||
|
||||
const params = useParams();
|
||||
|
||||
const [usuario, setUsuario] = useState<any>(null);
|
||||
const { usuario, fetchUsuario } = useGUsuarioReadHooks();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchUsuarios() {
|
||||
const data = await GUsuarioRead(Number(params.id));
|
||||
|
||||
if (data.code == 400) {
|
||||
|
||||
toast(data.message);
|
||||
|
||||
}
|
||||
|
||||
setUsuario(data);
|
||||
if (params.id) {
|
||||
fetchUsuario({ usuario_id: Number(params.id) } as Usuario);
|
||||
}
|
||||
fetchUsuarios();
|
||||
}, []);
|
||||
|
||||
if (!usuario) return <Loading type={1} />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
|
|
@ -44,7 +36,7 @@ export default function UsuarioDetalhes() {
|
|||
Nome
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.data?.nome_completo}
|
||||
{usuario?.nome_completo}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -52,7 +44,7 @@ export default function UsuarioDetalhes() {
|
|||
CPF
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.data?.cpf}
|
||||
{usuario?.cpf}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -60,7 +52,7 @@ export default function UsuarioDetalhes() {
|
|||
Função
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.data?.funcao}
|
||||
{usuario?.funcao}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -68,7 +60,7 @@ export default function UsuarioDetalhes() {
|
|||
Email
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.data?.email}
|
||||
{usuario?.email}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { UsuarioFormSchema } from "../../../_schemas/GUsuarioSchema"
|
|||
import {
|
||||
Button
|
||||
} from "@/components/ui/button"
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent
|
||||
|
|
@ -23,13 +24,14 @@ import {
|
|||
FormMessage,
|
||||
} from "@/components/ui/form"
|
||||
|
||||
import GUsuarioSave from "../../../_services/g_usuario/GUsuarioSave"
|
||||
import { toast } from "sonner"
|
||||
import { useGUsuarioSaveHook } from "../../../_hooks/g_usuario/useGUsuarioSaveHook"
|
||||
|
||||
type FormValues = z.infer<typeof UsuarioFormSchema>
|
||||
|
||||
export default function UsuarioFormularioPage() {
|
||||
|
||||
const { usuario, saveUsuario } = useGUsuarioSaveHook();
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(UsuarioFormSchema),
|
||||
defaultValues: {
|
||||
|
|
@ -42,16 +44,7 @@ export default function UsuarioFormularioPage() {
|
|||
});
|
||||
|
||||
async function onSubmit(values: FormValues) {
|
||||
|
||||
const response = await GUsuarioSave(values);
|
||||
|
||||
if(response.message)
|
||||
{
|
||||
toast("Event has been created.");
|
||||
}
|
||||
|
||||
console.log(response);
|
||||
|
||||
saveUsuario(values);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
'use client'
|
||||
|
||||
import GUsuarioIndex from "@/app/(protected)/(administrativo)/_services/g_usuario/GUsuarioIndex";
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
|
@ -19,25 +17,19 @@ import {
|
|||
import Usuario from "../../_interfaces/IGUsuario";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useGUsuarioIndexHook } from "../../_hooks/g_usuario/useGUsuarioIndexHook";
|
||||
import { useEffect } from "react";
|
||||
import Loading from "@/app/_components/loading/loading";
|
||||
|
||||
export default function UsuarioPage() {
|
||||
|
||||
const [usuarios, setUsuarios] = useState<any>(null);
|
||||
|
||||
function RemoveUser(usuarioId: number) {
|
||||
console.log('asdasd')
|
||||
}
|
||||
const { usuarios, fetchUsuarios } = useGUsuarioIndexHook();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchUsuarios() {
|
||||
const data = await GUsuarioIndex();
|
||||
setUsuarios(data);
|
||||
}
|
||||
fetchUsuarios();
|
||||
}, []);
|
||||
|
||||
if (!usuarios) return <div>Carregando...</div>;
|
||||
if (!usuarios) return <Loading type={2} />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -78,7 +70,7 @@ export default function UsuarioPage() {
|
|||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{usuarios.data.map((usuario: Usuario) => (
|
||||
{usuarios.map((usuario: Usuario) => (
|
||||
<TableRow key={usuario.usuario_id} className="cursor-pointer">
|
||||
<TableCell className="text-center">
|
||||
{usuario.usuario_id}
|
||||
|
|
@ -109,11 +101,6 @@ export default function UsuarioPage() {
|
|||
</Link>
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button onClick={() => RemoveUser(usuario.usuario_id)}>
|
||||
Remover
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use server'
|
||||
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
import API from "@/services/api/Api";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from "react"
|
||||
import Usuario from "../../_interfaces/IGUsuario"
|
||||
import GUsuarioIndex from "../../_services/g_usuario/GUsuarioIndex";
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
|
||||
export const useGUsuarioIndexHook = () => {
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [usuarios, setUsuarios] = useState<Usuario[] | null>(null);
|
||||
|
||||
const fetchUsuarios = async () => {
|
||||
|
||||
const response = await GUsuarioIndex();
|
||||
|
||||
setUsuarios(response.data);
|
||||
|
||||
// Define os dados do componente de resposta (toast, modal, etc)
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { usuarios, fetchUsuarios }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from "react"
|
||||
import Usuario from "../../_interfaces/IGUsuario"
|
||||
import GUsuarioRead from "../../_services/g_usuario/GUsuarioRead";
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
|
||||
export const useGUsuarioReadHooks = () => {
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [usuario, setUsuario] = useState<Usuario>();
|
||||
|
||||
const fetchUsuario = async (Usuario: Usuario) => {
|
||||
|
||||
const response = await GUsuarioRead(Usuario.usuario_id);
|
||||
|
||||
setUsuario(response.data);
|
||||
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { usuario, fetchUsuario }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from "react"
|
||||
import Usuario from "../../_interfaces/IGUsuario"
|
||||
import GUsuarioSave from "../../_services/g_usuario/GUsuarioSave";
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
|
||||
export const useGUsuarioSaveHook = () => {
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [usuario, setUsuario] = useState<Usuario>();
|
||||
|
||||
const saveUsuario = async (Usuario: any) => {
|
||||
|
||||
const response = await GUsuarioSave(Usuario);
|
||||
|
||||
setUsuario(response.data);
|
||||
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { usuario, saveUsuario }
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ import GUsuarioIndexData from "../../_data/g_usuario/GUsuarioIndexData"
|
|||
|
||||
export default async function GUsuarioIndex() {
|
||||
|
||||
return await GUsuarioIndexData();
|
||||
const response = await GUsuarioIndexData();
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
import { ResponseProvider } from '../_response/ResponseContext';
|
||||
import { AppSidebar } from "@/components/app-sidebar"
|
||||
import {
|
||||
Breadcrumb,
|
||||
|
|
@ -18,6 +19,7 @@ import {
|
|||
SidebarTrigger,
|
||||
} from "@/components/ui/sidebar"
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import Response from "../_response/response";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
|
|
@ -69,10 +71,13 @@ export default function RootLayout({
|
|||
</Breadcrumb>
|
||||
</div>
|
||||
</header>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
{children}
|
||||
<Toaster position="top-center" />
|
||||
</div>
|
||||
<ResponseProvider>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
{children}
|
||||
<Toaster position="top-center" />
|
||||
<Response />
|
||||
</div>
|
||||
</ResponseProvider>
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "../../globals.css";
|
||||
import { Suspense } from "react";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
|
|
@ -24,9 +25,7 @@ export default function RootLayout({
|
|||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
18
src/app/_components/loading/loading.tsx
Normal file
18
src/app/_components/loading/loading.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React from "react";
|
||||
import SkeletonCard from "./skeletonCard";
|
||||
import SkeletonTable from "./skeletonTable";
|
||||
|
||||
export default function Loading({ type }: any) {
|
||||
|
||||
switch (type) {
|
||||
case 1:
|
||||
|
||||
return <SkeletonCard />;
|
||||
break;
|
||||
case 2:
|
||||
|
||||
return <SkeletonTable />
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
35
src/app/_components/loading/skeletonCard.tsx
Normal file
35
src/app/_components/loading/skeletonCard.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
export default function SkeletonCard() {
|
||||
|
||||
return (
|
||||
|
||||
<div className="space-y-6">
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-10 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-32 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-10 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-20 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-10 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="h-10 w-32 bg-gray-200 dark:bg-slate-700 rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
81
src/app/_components/loading/skeletonTable.tsx
Normal file
81
src/app/_components/loading/skeletonTable.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
export default function SkeletonTable() {
|
||||
|
||||
return (
|
||||
|
||||
<div>
|
||||
<div role="status" aria-busy="true" aria-label="Carregando tabela" className="p-4 w-full">
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-32 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
36
src/app/_response/ResponseContext.tsx
Normal file
36
src/app/_response/ResponseContext.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
'use client';
|
||||
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
|
||||
interface ResponseState {
|
||||
message: string;
|
||||
type: 'toast' | 'modal' | 'alert' | null;
|
||||
status: number;
|
||||
}
|
||||
|
||||
interface ResponseContextProps {
|
||||
response: ResponseState;
|
||||
setResponse: (value: ResponseState) => void;
|
||||
clearResponse: () => void;
|
||||
}
|
||||
|
||||
const ResponseContext = createContext<ResponseContextProps | undefined>(undefined);
|
||||
|
||||
export const ResponseProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||
const [response, setResponseState] = useState<ResponseState>({ message: '', type: null, status : 0});
|
||||
|
||||
const setResponse = (value: ResponseState) => setResponseState(value);
|
||||
const clearResponse = () => setResponseState({ message: '', type: null, status : 0});
|
||||
|
||||
return (
|
||||
<ResponseContext.Provider value={{ response, setResponse, clearResponse }}>
|
||||
{children}
|
||||
</ResponseContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useResponse = () => {
|
||||
const context = useContext(ResponseContext);
|
||||
if (!context) throw new Error('useResponse must be used within ResponseProvider');
|
||||
return context;
|
||||
};
|
||||
20
src/app/_response/response.tsx
Normal file
20
src/app/_response/response.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// app/src/app/_response/response.tsx
|
||||
"use client";
|
||||
|
||||
import { useResponse } from "./ResponseContext";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function Response() {
|
||||
const { response, clearResponse } = useResponse();
|
||||
|
||||
useEffect(() => {
|
||||
switch (Number(response?.status)) {
|
||||
case 200:
|
||||
toast(response.message);
|
||||
break;
|
||||
}
|
||||
}, [response, clearResponse]);
|
||||
|
||||
return <div></div>;
|
||||
}
|
||||
6
src/app/loading.tsx
Normal file
6
src/app/loading.tsx
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// components/Loading.tsx
|
||||
export default function Loading() {
|
||||
return (
|
||||
<span className="loading loading-spinner loading-xs"></span>
|
||||
)
|
||||
}
|
||||
|
|
@ -59,11 +59,10 @@ export default class API {
|
|||
// Converte a reposta para json
|
||||
const responseData = await response.json();
|
||||
|
||||
// Classe para manipular a resposta
|
||||
const ResponseHandler = new Response();
|
||||
// Obtem o status da requisição
|
||||
responseData.status = response.status;
|
||||
|
||||
// Manipula as respostas
|
||||
return ResponseHandler.handler(responseData);
|
||||
return responseData;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue