3 Commits

17 changed files with 656 additions and 77 deletions
Unified View
  1. +153
    -7
      first-app/package-lock.json
  2. +4
    -1
      first-app/package.json
  3. +7
    -0
      first-app/src/App.tsx
  4. +80
    -57
      first-app/src/components/ClienteForm.tsx
  5. +2
    -3
      first-app/src/components/sideBar.tsx
  6. +13
    -0
      first-app/src/context/NotificationContext.tsx
  7. +3
    -0
      first-app/src/environments.ts
  8. +8
    -0
      first-app/src/hooks/useToastfy.ts
  9. +10
    -0
      first-app/src/interfaces/Cliente.ts
  10. +7
    -0
      first-app/src/interfaces/ParamsType.ts
  11. +5
    -0
      first-app/src/main.tsx
  12. +17
    -0
      first-app/src/pages/Cliente/Cliente.module.css
  13. +141
    -0
      first-app/src/pages/Cliente/Clientes.tsx
  14. +11
    -9
      first-app/src/pages/Cliente/NewCliente.tsx
  15. +71
    -0
      first-app/src/services/ClienteService.ts
  16. +114
    -0
      first-app/src/slices/ClienteSlice.ts
  17. +10
    -0
      first-app/src/store.ts

+ 153
- 7
first-app/package-lock.json View File

@ -8,10 +8,13 @@
"name": "first-app", "name": "first-app",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^1.9.5",
"antd": "^5.6.2", "antd": "^5.6.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.13.0"
"react-redux": "^8.1.1",
"react-router-dom": "^6.13.0",
"react-toastify": "^9.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.37", "@types/react": "^18.0.37",
@ -1138,6 +1141,29 @@
"react-dom": ">=16.9.0" "react-dom": ">=16.9.0"
} }
}, },
"node_modules/@reduxjs/toolkit": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
"integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
"dependencies": {
"immer": "^9.0.21",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.8"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.0.2"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.6.3", "version": "1.6.3",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.3.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.3.tgz",
@ -1146,6 +1172,15 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.12", "version": "7.0.12",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
@ -1155,14 +1190,12 @@
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "18.2.13", "version": "18.2.13",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.13.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.13.tgz",
"integrity": "sha512-vJ+zElvi/Zn9cVXB5slX2xL8PZodPCwPRDpittQdw43JR2AJ5k3vKdgJJyneV/cYgIbLQUwXa9JVDvUZXGba+Q==", "integrity": "sha512-vJ+zElvi/Zn9cVXB5slX2xL8PZodPCwPRDpittQdw43JR2AJ5k3vKdgJJyneV/cYgIbLQUwXa9JVDvUZXGba+Q==",
"dev": true,
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
"@types/scheduler": "*", "@types/scheduler": "*",
@ -1173,7 +1206,7 @@
"version": "18.2.6", "version": "18.2.6",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.6.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.6.tgz",
"integrity": "sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==", "integrity": "sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==",
"dev": true,
"devOptional": true,
"dependencies": { "dependencies": {
"@types/react": "*" "@types/react": "*"
} }
@ -1181,8 +1214,7 @@
"node_modules/@types/scheduler": { "node_modules/@types/scheduler": {
"version": "0.16.3", "version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
}, },
"node_modules/@types/semver": { "node_modules/@types/semver": {
"version": "7.5.0", "version": "7.5.0",
@ -1190,6 +1222,11 @@
"integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
"dev": true "dev": true
}, },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.60.0", "version": "5.60.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz",
@ -1650,6 +1687,14 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" "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": { "node_modules/color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -2353,6 +2398,14 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.2.4", "version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -2362,6 +2415,15 @@
"node": ">= 4" "node": ">= 4"
} }
}, },
"node_modules/immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -3447,6 +3509,49 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"node_modules/react-redux": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz",
"integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==",
"dependencies": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/use-sync-external-store": "^0.0.3",
"hoist-non-react-statics": "^3.3.2",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^16.8 || ^17.0 || ^18.0",
"@types/react-dom": "^16.8 || ^17.0 || ^18.0",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0",
"react-native": ">=0.59",
"redux": "^4 || ^5.0.0-beta.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-redux/node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@ -3486,11 +3591,44 @@
"react-dom": ">=16.8" "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",
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/redux-thunk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
"integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"peerDependencies": {
"redux": "^4"
}
},
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.13.11", "version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
}, },
"node_modules/reselect": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
},
"node_modules/resize-observer-polyfill": { "node_modules/resize-observer-polyfill": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
@ -3840,6 +3978,14 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "4.3.9", "version": "4.3.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",


+ 4
- 1
first-app/package.json View File

@ -10,10 +10,13 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^1.9.5",
"antd": "^5.6.2", "antd": "^5.6.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.13.0"
"react-redux": "^8.1.1",
"react-router-dom": "^6.13.0",
"react-toastify": "^9.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.37", "@types/react": "^18.0.37",


+ 7
- 0
first-app/src/App.tsx View File

@ -3,10 +3,12 @@ import {
RouterProvider, RouterProvider,
} from "react-router-dom"; } from "react-router-dom";
import './App.css' import './App.css'
import { ToastContainer } from "./hooks/useToastfy";
//pages //pages
import PageBase from "./pages/PageBase/PageBase"; import PageBase from "./pages/PageBase/PageBase";
import NewCliente from "./pages/Cliente/NewCliente"; import NewCliente from "./pages/Cliente/NewCliente";
import Clientes from "./pages/Cliente/Clientes";
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
@ -16,6 +18,10 @@ const router = createBrowserRouter([
{ {
path:"/cliente/new", path:"/cliente/new",
element: <NewCliente/> element: <NewCliente/>
},
{
path:"/cliente/list",
element: <Clientes/>
} }
] ]
}, },
@ -26,6 +32,7 @@ function App() {
return ( return (
<div className='App'> <div className='App'>
<ToastContainer/>
<RouterProvider router={router}/> <RouterProvider router={router}/>
</div> </div>
) )


+ 80
- 57
first-app/src/components/ClienteForm.tsx View File

@ -1,17 +1,33 @@
import { Button, Form, Input, Select } from 'antd'; import { Button, Form, Input, Select } from 'antd';
import styles from '../pages/Cliente/Cliente.module.css'; import styles from '../pages/Cliente/Cliente.module.css';
import { useEffect } from 'react'; import { useEffect } from 'react';
import {
getAllPaises
} from '../slices/ClienteSlice';
import { useDispatch, useSelector } from 'react-redux';
import { Cliente } from '../interfaces/Cliente';
export interface FormClienteProps { export interface FormClienteProps {
values?: any,
values?: Cliente,
btnTitle: string, btnTitle: string,
eventEmiter: any eventEmiter: any
} }
function ClienteForm(props: FormClienteProps) { function ClienteForm(props: FormClienteProps) {
const { paises, loading} = useSelector((state: any) => state.cliente);
const dispatch = useDispatch<any>();
const onFinish = (values: any) => { const onFinish = (values: any) => {
props.eventEmiter(values);
const cliente: Cliente = {
nome: values.nome,
email: values.email,
telefone: values.telefone,
pais: {
uid: values.pais,
nome: ''
}
}
props.eventEmiter(cliente);
}; };
const onFinishFailed = (errorInfo: any) => { const onFinishFailed = (errorInfo: any) => {
@ -19,68 +35,75 @@ function ClienteForm(props: FormClienteProps) {
}; };
useEffect(() => { useEffect(() => {
//TO DO
//GET ALL COUNTRIES FOR SHOW IN SELECT ELEMENT
}, [])
dispatch(getAllPaises())
}, []);
return ( return (
<Form
className={styles.form_cliente}
name="basic"
layout="vertical"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<Form.Item
label="Nome"
name="nome"
initialValue={props.values ? props.values.nome : ''}
rules={[{ required: true, message: 'Nome é obrigatorio!' }]}
<>
<Form
className={styles.form_cliente}
name="basic"
layout="vertical"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
> >
<Input />
</Form.Item>
<Form.Item
label="Nome"
name="nome"
initialValue={props.values ? props.values.nome : ''}
rules={[{ required: true, message: 'Nome é obrigatorio!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="E-mail"
name="email"
initialValue={props.values ? props.values.email : ''}
rules={[{ required: true, message: 'E-mail é obrigatorio!' }]}
>
<Input type='email' />
</Form.Item>
<Form.Item
label="E-mail"
name="email"
initialValue={props.values ? props.values.email : ''}
rules={[{ required: true, message: 'E-mail é obrigatorio!' }]}
>
<Input type='email' />
</Form.Item>
<Form.Item
label="Telefone"
name="telefone"
initialValue={props.values ? props.values.telefone : ''}
rules={[{ required: true, message: 'Numero de telefone é obrigatorio!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Telefone"
name="telefone"
initialValue={props.values ? props.values.telefone : ''}
rules={[{ required: true, message: 'Numero de telefone é obrigatorio!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Pais"
name="pais"
rules={[{ required: true, message: 'É obrigatorio informar o pais!' }]}
>
<Select
options={[
{ value: 'jack', label: 'Jack' },
{ value: 'lucy', label: 'Lucy' },
{ value: 'Yiminghe', label: 'yiminghe' },
]}
/>
</Form.Item>
<Form.Item
label="Pais"
name="pais"
rules={[{ required: true, message: 'É obrigatorio informar o pais!' }]}
>
<Select
options={
paises.map((cliente: any) => (
{ value: cliente.uid, label: cliente.nome }
))
}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
{props.btnTitle}
</Button>
</Form.Item>
</Form>
<Form.Item>
{(loading === false) && (
<Button type="primary" htmlType="submit">
{props.btnTitle}
</Button>
)}
{(loading === true) && (
<Button type="primary" loading>
Aguarde...
</Button>
)}
</Form.Item>
</Form>
</>
) )
} }

+ 2
- 3
first-app/src/components/sideBar.tsx View File

@ -33,8 +33,7 @@ function SideBar () {
const items: MenuItem[] = [ const items: MenuItem[] = [
getItem('Clientes', 'sub1', <UserOutlined />, [ getItem('Clientes', 'sub1', <UserOutlined />, [
getItem((<Link to="/cliente/new">Novo Cliente</Link>), '1'), getItem((<Link to="/cliente/new">Novo Cliente</Link>), '1'),
getItem('Listar todos', '3'),
getItem('Alex', '4'),
getItem((<Link to="/cliente/list">Ver todos</Link>), '3'),
]), ]),
getItem('Produtos', 'sub2', <InboxOutlined />, [ getItem('Produtos', 'sub2', <InboxOutlined />, [
getItem('Tom', '5'), getItem('Tom', '5'),
@ -53,7 +52,7 @@ function SideBar () {
return ( return (
<Sider collapsible collapsed={collapsed} onCollapse={(value) => setCollapsed(value)}> <Sider collapsible collapsed={collapsed} onCollapse={(value) => setCollapsed(value)}>
<div className="demo-logo-vertical" /> <div className="demo-logo-vertical" />
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline" items={items} />
<Menu theme="dark" mode="inline" items={items} />
</Sider> </Sider>
) )
} }


+ 13
- 0
first-app/src/context/NotificationContext.tsx View File

@ -0,0 +1,13 @@
import { useContext, createContext } from "react";
const NotificationContext = createContext<any>({});
export function NotificationProvider({children, value}:any){
return <NotificationContext.Provider value={value}>
{children}
</NotificationContext.Provider>
}
export function useNotificationValue(){
return useContext(NotificationContext);
}

+ 3
- 0
first-app/src/environments.ts View File

@ -0,0 +1,3 @@
export const Environments = {
URLApi: "http://jailton.bootcamp.dev.netuno.org:20190/services/netunovendas"
}

+ 8
- 0
first-app/src/hooks/useToastfy.ts View File

@ -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};

+ 10
- 0
first-app/src/interfaces/Cliente.ts View File

@ -0,0 +1,10 @@
export interface Cliente {
uid?:string,
nome:string,
email:string,
telefone:string,
pais?:{
uid:string,
nome:string,
}
}

+ 7
- 0
first-app/src/interfaces/ParamsType.ts View File

@ -0,0 +1,7 @@
export interface ParamsType {
pagination?:{
pageSize:number,
current:number
},
filter?:any
}

+ 5
- 0
first-app/src/main.tsx View File

@ -4,9 +4,14 @@ import 'antd/dist/reset.css'
import './index.css' import './index.css'
import App from './App.tsx' import App from './App.tsx'
import store from './store.ts'
import { Provider } from 'react-redux'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<Provider store={store}>
<App/> <App/>
</Provider>
</React.StrictMode>, </React.StrictMode>,
) )

+ 17
- 0
first-app/src/pages/Cliente/Cliente.module.css View File

@ -1,3 +1,20 @@
.form_cliente{ .form_cliente{
width: 600px; 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;
} }

+ 141
- 0
first-app/src/pages/Cliente/Clientes.tsx View File

@ -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<any>();
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 (
<div className={styles.container_table}>
<div className={styles.container_table__filter}>
<Search
placeholder="Buscar por nome..."
enterButton
className={styles.container_table__search}
onSearch={handleSearch}
/>
</div>
<Table
dataSource={
clientes.map((cliente: Cliente) => (
{ 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}
>
<Column title="Nome" dataIndex="name" key="name" />
<Column title="Email" dataIndex="email" key="email" />
<Column title="Pais" dataIndex="pais" key="pais" />
<Column
title="Actions"
key="action"
render={(_: any, record: any) => (
<Space size="middle">
<Popconfirm
title="Atenção:"
description="Confirme a ação de delete!"
onConfirm={() => { handleDelete(record.key) }}
okText="Sim"
cancelText="Não"
>
<Button value="small"
type="primary"
danger icon={<DeleteOutlined />}
size="large"
title="Deletar"
/>
</Popconfirm>
<Button
value="small"
type="primary"
icon={<EditOutlined />} size="large"
title="Atualizar"
onClick={() => { navigate(`/cliente/new`) }}
/>
</Space>
)}
/>
</Table>
</div>
)
}
export default Clientes;

+ 11
- 9
first-app/src/pages/Cliente/NewCliente.tsx View File

@ -1,15 +1,17 @@
//components //components
import { useDispatch } from 'react-redux'
import ClienteForm from '../../components/ClienteForm' import ClienteForm from '../../components/ClienteForm'
import {
registerCliente
} from '../../slices/ClienteSlice';
import { Cliente as clieInte} from '../../interfaces/Cliente';
function Cliente() {
const handleSubmit = (values:any) => {
console.log('Componente Cliente:',values)
function NewCliente() {
//TO DO
//CREATED SERVICE REGISTER CUSTOMER
const dispatch = useDispatch<any>();
const handleSubmit = (cliente:clieInte) => {
dispatch(registerCliente(cliente));
} }
return ( return (
@ -22,4 +24,4 @@ function Cliente() {
) )
} }
export default Cliente
export default NewCliente

+ 71
- 0
first-app/src/services/ClienteService.ts View File

@ -0,0 +1,71 @@
import { Environments } from "../environments"
import { Cliente } from "../interfaces/Cliente";
import { ParamsType } from "../interfaces/ParamsType";
const { URLApi } = Environments;
const getAllPaises = async () => {
const data = await fetch(`${URLApi}/pais/list`)
.then((res)=>res.json())
.catch((err)=>err);
return data;
}
const registerCliente = async (cliente:Cliente) => {
console.log(cliente)
const config = {
method:"POST",
body:JSON.stringify(cliente),
headers: {
"Content-type":"application/json",
}
}
try {
const data = await fetch(`${URLApi}/cliente`,config)
.then((res)=>res.json())
.catch((err)=>err);
return data;
} catch (error) {
console.info(error);
return error;
}
}
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,
getAllClientes,
deleteCliente
}

+ 114
- 0
first-app/src/slices/ClienteSlice.ts View File

@ -0,0 +1,114 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import {
ClienteServices
} from "../services/ClienteService";
import { Cliente } from "../interfaces/Cliente";
import { notify } from "../hooks/useToastfy";
import { ParamsType } from "../interfaces/ParamsType";
const initialState = {
paises:[] as any,
clientes:[] as any,
total:0,
loading:false,
error:false,
success:true,
message:''
}
export const getAllPaises = createAsyncThunk(
"paise/getAll",
async () => {
const data = await ClienteServices.getAllPaises();
return data;
}
)
export const registerCliente = createAsyncThunk(
"cliente/register",
async (cliente:Cliente, thunkApi) => {
const data = await ClienteServices.registerCliente(cliente);
if (data.error) {
return thunkApi.rejectWithValue (data);
}
return data;
}
)
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.loading = false;
state.error = false;
state.message = '';
},
},
extraReducers(builder) {
builder
.addCase(getAllPaises.pending,(state)=>{
state.loading = true;
}).addCase(getAllPaises.fulfilled,(state,action)=>{
state.paises = action.payload.result;
state.loading = false;
})
.addCase(registerCliente.pending,(state)=>{
state.loading = true;
}).addCase(registerCliente.fulfilled, (state)=>{
state.loading = false;
state.success = true;
notify('Novo cliente cadastrado!','success');
}).addCase(registerCliente.rejected,(state,action:any)=>{
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');
})
},
});
export const { resetState } = clienteSlice.actions;
export default clienteSlice.reducer;

+ 10
- 0
first-app/src/store.ts View File

@ -0,0 +1,10 @@
import { configureStore } from "@reduxjs/toolkit";
//reduces
import ClienteSlice from "./slices/ClienteSlice";
export default configureStore({
reducer:{
cliente:ClienteSlice
}
})

Loading…
Cancel
Save