diff --git a/first-app/package-lock.json b/first-app/package-lock.json index d85e9f9..d54636a 100644 --- a/first-app/package-lock.json +++ b/first-app/package-lock.json @@ -13,7 +13,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.1.1", - "react-router-dom": "^6.13.0" + "react-router-dom": "^6.13.0", + "react-toastify": "^9.1.3" }, "devDependencies": { "@types/react": "^18.0.37", @@ -1686,6 +1687,14 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3582,6 +3591,18 @@ "react-dom": ">=16.8" } }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", diff --git a/first-app/package.json b/first-app/package.json index 75fe6b2..d815ce5 100644 --- a/first-app/package.json +++ b/first-app/package.json @@ -15,7 +15,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.1.1", - "react-router-dom": "^6.13.0" + "react-router-dom": "^6.13.0", + "react-toastify": "^9.1.3" }, "devDependencies": { "@types/react": "^18.0.37", diff --git a/first-app/src/App.tsx b/first-app/src/App.tsx index 509f3d7..a40b341 100644 --- a/first-app/src/App.tsx +++ b/first-app/src/App.tsx @@ -3,13 +3,12 @@ import { RouterProvider, } from "react-router-dom"; import './App.css' -import { NotificationProvider } from "./context/NotificationContext"; +import { ToastContainer } from "./hooks/useToastfy"; //pages import PageBase from "./pages/PageBase/PageBase"; import NewCliente from "./pages/Cliente/NewCliente"; -import { notification } from "antd"; -import { IconType } from "antd/es/notification/interface"; +import Clientes from "./pages/Cliente/Clientes"; const router = createBrowserRouter([ { @@ -19,6 +18,10 @@ const router = createBrowserRouter([ { path:"/cliente/new", element: + }, + { + path:"/cliente/list", + element: } ] }, @@ -27,22 +30,10 @@ const router = createBrowserRouter([ function App() { - const [api, contextHolder] = notification.useNotification(); - - const openNotification = (message:string, description:string, type:IconType) => { - api.open({ - type:type, - message: message, - description: description, - }); - }; - return (
- - {contextHolder} + -
) } diff --git a/first-app/src/components/ClienteForm.tsx b/first-app/src/components/ClienteForm.tsx index 02628b6..b25c6ee 100644 --- a/first-app/src/components/ClienteForm.tsx +++ b/first-app/src/components/ClienteForm.tsx @@ -1,9 +1,9 @@ import { Button, Form, Input, Select } from 'antd'; import styles from '../pages/Cliente/Cliente.module.css'; import { useEffect } from 'react'; -import { +import { getAllPaises - } from '../slices/ClienteSlice'; +} from '../slices/ClienteSlice'; import { useDispatch, useSelector } from 'react-redux'; import { Cliente } from '../interfaces/Cliente'; @@ -14,88 +14,95 @@ export interface FormClienteProps { } function ClienteForm(props: FormClienteProps) { - const {paises} = useSelector((state:any)=>state.cliente); + const { paises, loading} = useSelector((state: any) => state.cliente); const dispatch = useDispatch(); const onFinish = (values: any) => { - const cliente:Cliente = { - nome:values.nome, - email:values.email, - telefone:values.telefone, - pais:{ - uid:values.pais, - nome:'' + const cliente: Cliente = { + nome: values.nome, + email: values.email, + telefone: values.telefone, + pais: { + uid: values.pais, + nome: '' } } props.eventEmiter(cliente); }; const onFinishFailed = (errorInfo: any) => { - console.log('Failed:', errorInfo); + console.log('Failed:', errorInfo); }; useEffect(() => { - dispatch(getAllPaises()) + dispatch(getAllPaises()) }, []); return ( <> -
- - - + + + - - - + + + - - - + + + - - ( + { value: cliente.uid, label: cliente.nome } + )) + } + /> + - - - -
+ + {(loading === false) && ( + + )} + {(loading === true) && ( + + )} + + ) } diff --git a/first-app/src/components/sideBar.tsx b/first-app/src/components/sideBar.tsx index cfb5bc8..2b0ff7f 100644 --- a/first-app/src/components/sideBar.tsx +++ b/first-app/src/components/sideBar.tsx @@ -33,8 +33,7 @@ function SideBar () { const items: MenuItem[] = [ getItem('Clientes', 'sub1', , [ getItem((Novo Cliente), '1'), - getItem('Listar todos', '3'), - getItem('Alex', '4'), + getItem((Ver todos), '3'), ]), getItem('Produtos', 'sub2', , [ getItem('Tom', '5'), @@ -53,7 +52,7 @@ function SideBar () { return ( setCollapsed(value)}>
- + ) } diff --git a/first-app/src/hooks/useNotification.ts b/first-app/src/hooks/useNotification.ts deleted file mode 100644 index ca59487..0000000 --- a/first-app/src/hooks/useNotification.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { notification } from "antd"; -import { IconType, NotificationInstance } from "antd/es/notification/interface"; - - const [api, contextHolder] = notification.useNotification(); - - const openNotification = (message:string, description:string, type:IconType) => { - api.open({ - type:type, - message: message, - description: description, - }); - }; - -export { - openNotification -} diff --git a/first-app/src/hooks/useToastfy.ts b/first-app/src/hooks/useToastfy.ts index e69de29..ddac853 100644 --- a/first-app/src/hooks/useToastfy.ts +++ b/first-app/src/hooks/useToastfy.ts @@ -0,0 +1,8 @@ +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; + + +const notify = (message:String,type:any) => toast(message,{type:type,autoClose:2000,theme:'dark'}); + + +export {notify, ToastContainer}; \ No newline at end of file diff --git a/first-app/src/interfaces/ParamsType.ts b/first-app/src/interfaces/ParamsType.ts new file mode 100644 index 0000000..abaae4c --- /dev/null +++ b/first-app/src/interfaces/ParamsType.ts @@ -0,0 +1,7 @@ +export interface ParamsType { + pagination?:{ + pageSize:number, + current:number + }, + filter?:any +} \ No newline at end of file diff --git a/first-app/src/pages/Cliente/Cliente.module.css b/first-app/src/pages/Cliente/Cliente.module.css index 0dcac9a..2fc6aa6 100644 --- a/first-app/src/pages/Cliente/Cliente.module.css +++ b/first-app/src/pages/Cliente/Cliente.module.css @@ -1,3 +1,20 @@ .form_cliente{ width: 600px; +} + +.container_table { + width: 800px; + display: flex; + flex-direction: column; +} + +.container_table__filter{ + width: 100%; + display: flex; + justify-content: end; + margin-bottom: 1em; +} + +.container_table__search{ + width: 300px; } \ No newline at end of file diff --git a/first-app/src/pages/Cliente/Clientes.tsx b/first-app/src/pages/Cliente/Clientes.tsx new file mode 100644 index 0000000..4dedc50 --- /dev/null +++ b/first-app/src/pages/Cliente/Clientes.tsx @@ -0,0 +1,141 @@ +import Table from "antd/es/table"; +import { + getAllClientes, + deleteCliente +} from '../../slices/ClienteSlice'; +import { useDispatch, useSelector } from "react-redux"; +import { useEffect, useState } from "react"; +import { Cliente } from "../../interfaces/Cliente"; +import Column from "antd/es/table/Column"; +import { Button, Input, Popconfirm, Space } from "antd"; +import { + DeleteOutlined, + EditOutlined +} from '@ant-design/icons' +import { useNavigate } from "react-router-dom"; +import { ParamsType } from "../../interfaces/ParamsType"; +import styles from './Cliente.module.css'; +const { Search } = Input; + +function Clientes() { + + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { clientes, total, loading } = useSelector((state: any) => state.cliente); + const [nomeSearch, setNomeSearch] = useState(''); + + const onChangePage = (page: any, size: any) => { + + const params: ParamsType = { + pagination: { + current: page, + pageSize: size + }, + filter: { + nome: nomeSearch != '' ? nomeSearch : '' + } + } + dispatch(getAllClientes(params)) + } + + const handleDelete = (uid: string) => { + dispatch(deleteCliente(uid)); + } + + const handleSearch = (nome: string) => { + + setNomeSearch(nome); + const params: ParamsType = { + pagination: { + current: 1, + pageSize: 5 + }, + filter: { + nome: nome + } + } + dispatch(getAllClientes(params)) + } + + useEffect(() => { + dispatch(getAllClientes( + { pagination: { current: 1, pageSize: 5 } } + )); + }, []) + + return ( +
+
+ +
+ ( + { key: cliente.uid, name: cliente.nome, email: cliente.email, pais: cliente.pais?.nome } + )) + } + pagination={{ + position: ["bottomCenter"], + total: total, + defaultPageSize: 5, + onChange(page, pageSize) { + onChangePage(page, pageSize) + }, + pageSizeOptions: [5, 10, 15], + showSizeChanger: true, + onShowSizeChange(current, size) { + onChangePage(current, size) + }, + }} + loading={loading} + bordered={true} + > + + + + + ( + + + { handleDelete(record.key) }} + okText="Sim" + cancelText="Não" + > +
+ + +
+ ) +} + +export default Clientes; \ No newline at end of file diff --git a/first-app/src/services/ClienteService.ts b/first-app/src/services/ClienteService.ts index 9309ef4..7e43398 100644 --- a/first-app/src/services/ClienteService.ts +++ b/first-app/src/services/ClienteService.ts @@ -1,5 +1,6 @@ import { Environments } from "../environments" import { Cliente } from "../interfaces/Cliente"; +import { ParamsType } from "../interfaces/ParamsType"; const { URLApi } = Environments; @@ -29,7 +30,6 @@ const registerCliente = async (cliente:Cliente) => { const data = await fetch(`${URLApi}/cliente`,config) .then((res)=>res.json()) .catch((err)=>err); - console.log(data); return data; } catch (error) { console.info(error); @@ -38,7 +38,34 @@ const registerCliente = async (cliente:Cliente) => { } +const getAllClientes = async (params:ParamsType) => { + const config = { + method:"POST", + body:JSON.stringify(params), + headers: { + "Content-type":"application/json", + } + } + + const data = await fetch(`${URLApi}/cliente/list`,config) + .then((res)=>res.json()) + .catch((err)=>err); + + return data; +} + +const deleteCliente = async (uid:string) => { + + const data = await fetch(`${URLApi}/cliente?uid=${uid}`,{method:"DELETE"}) + .then ((res)=>res.json()) + .catch((err)=>err); + + return data; +} + export const ClienteServices = { getAllPaises, - registerCliente + registerCliente, + getAllClientes, + deleteCliente } \ No newline at end of file diff --git a/first-app/src/slices/ClienteSlice.ts b/first-app/src/slices/ClienteSlice.ts index 1f4d95a..f4abe7e 100644 --- a/first-app/src/slices/ClienteSlice.ts +++ b/first-app/src/slices/ClienteSlice.ts @@ -3,12 +3,14 @@ import { ClienteServices } from "../services/ClienteService"; import { Cliente } from "../interfaces/Cliente"; +import { notify } from "../hooks/useToastfy"; +import { ParamsType } from "../interfaces/ParamsType"; const initialState = { - cliente:{}, paises:[] as any, clientes:[] as any, - loadind:false, + total:0, + loading:false, error:false, success:true, message:'' @@ -33,13 +35,29 @@ export const registerCliente = createAsyncThunk( } ) +export const getAllClientes = createAsyncThunk( + "cliente/getAll", + async (params:ParamsType) => { + const data = await ClienteServices.getAllClientes(params); + return data; + } +) + +export const deleteCliente = createAsyncThunk( + "cliente/delete", + async (uid:string) => { + const data = await ClienteServices.deleteCliente(uid); + return data; + } +) + export const clienteSlice = createSlice({ name:"cliente", initialState, reducers:{ resetState:(state)=>{ - state.loadind = false; + state.loading = false; state.error = false; state.message = ''; }, @@ -48,22 +66,46 @@ export const clienteSlice = createSlice({ extraReducers(builder) { builder .addCase(getAllPaises.pending,(state)=>{ - state.loadind = true; + state.loading = true; }).addCase(getAllPaises.fulfilled,(state,action)=>{ state.paises = action.payload.result; - state.loadind = false; + state.loading = false; }) .addCase(registerCliente.pending,(state)=>{ - state.loadind = true; + state.loading = true; }).addCase(registerCliente.fulfilled, (state)=>{ - state.loadind = false; + state.loading = false; state.success = true; + notify('Novo cliente cadastrado!','success'); }).addCase(registerCliente.rejected,(state,action:any)=>{ - state.loadind = false; + state.loading = false; state.error = true; state.success = false; state.message = action.payload ? action.payload.message : 'Error ao cadastrar cliente, revise os dados e tente novamente!'; + notify(state.message,'error'); + }) + + .addCase(getAllClientes.pending,(state)=>{ + state.loading = true; + }).addCase(getAllClientes.fulfilled,(state,action)=>{ + state.clientes = action.payload.result; + state.total = action.payload.total; + state.loading = false; + }) + + .addCase(deleteCliente.pending,(state)=>{ + state.loading = true; + }).addCase(deleteCliente.fulfilled,(state,action)=>{ + state.clientes = state.clientes.filter((cliente:Cliente)=>(cliente.uid !== action.payload.uid)); + state.total = state.total - 1; + state.loading = false; + notify('Cliente deletedo com sucesso!','success'); + }).addCase(deleteCliente.rejected,(state,action:any)=>{ + state.loading = true; + state.error = true; + state.message = action.payload.message ? action.payload.message : 'Erro ao deletar cliente, tente novamente!'; + notify(state.message,'error'); }) }, });