diff --git a/first-app/src/App.tsx b/first-app/src/App.tsx index a40b341..86d377f 100644 --- a/first-app/src/App.tsx +++ b/first-app/src/App.tsx @@ -5,28 +5,15 @@ import { import './App.css' import { ToastContainer } from "./hooks/useToastfy"; -//pages -import PageBase from "./pages/PageBase/PageBase"; -import NewCliente from "./pages/Cliente/NewCliente"; -import Clientes from "./pages/Cliente/Clientes"; +// //pages +// import PageBase from "./pages/PageBase/PageBase"; +// import NewCliente from "./pages/Cliente/NewCliente"; +// import Clientes from "./pages/Cliente/Clientes"; +// import NewProduto from "./pages/Produto/NewProduto"; +// import Produtos from "./pages/Produto/Produtos"; +import {Routes} from './Routes'; -const router = createBrowserRouter([ - { - path: "/", - element: , - children:[ - { - path:"/cliente/new", - element: - }, - { - path:"/cliente/list", - element: - } - ] - }, - -]); +const router = createBrowserRouter(Routes); function App() { diff --git a/first-app/src/Routes.tsx b/first-app/src/Routes.tsx new file mode 100644 index 0000000..2205db5 --- /dev/null +++ b/first-app/src/Routes.tsx @@ -0,0 +1,38 @@ +import NewCliente from "./pages/Cliente/NewCliente"; +import PageBase from "./pages/PageBase/PageBase"; +import Clientes from "./pages/Cliente/Clientes"; +import NewProduto from "./pages/Produto/NewProduto"; +import Produtos from "./pages/Produto/Produtos"; +import NewEncomenda from "./pages/Encomenda/NewEncomenda"; + + +export const Routes = [ + { + path: "/", + element: , + children: [ + { + path: "/cliente/new", + element: + }, + { + path: "/cliente/list", + element: + }, + //produto routes + { + path: "/produto/new", + element: + }, + { + path: "/produto/list", + element: + }, + //encomeda routes + { + path: "/encomenda/new", + element: + } + ] + } +] diff --git a/first-app/src/components/ProdutoForm.tsx b/first-app/src/components/ProdutoForm.tsx new file mode 100644 index 0000000..075b9ed --- /dev/null +++ b/first-app/src/components/ProdutoForm.tsx @@ -0,0 +1,102 @@ +import { Input, Select, Button, Form } from "antd"; +import { Produto } from "../interfaces/Produto"; +import styles from '../pages/Produto/produto.module.css' +import { + getAllCategoria +} from "../slices/ProdutoSlice"; +import { useDispatch, useSelector } from "react-redux"; +import { useEffect } from "react"; + +interface FormProdutoProps { + produto?:Produto, + btnTitle:string, + eventEmitter:any +} + +function ProdutoForm( props:FormProdutoProps ) { + + const dispatch = useDispatch(); + const { categorias, loading } = useSelector((state:any) => state.produto); + + const onFinish = (values: any) => { + + const produto:Produto = { + nome:values.nome, + preco:values.preco, + categoria:{ + uid:values.categoria + } + } + + props.eventEmitter(produto); + }; + + const onFinishFailed = (errorInfo: any) => { + console.log('Failed:', errorInfo); + }; + + useEffect(() => { + dispatch(getAllCategoria()) + },[]) + + return ( + <> +
+ + + + + + + + + + ( + { value: categoria.uid, label: categoria.nome } + )).concat({ value: '', label: 'Todos' }) + } + onChange={filterByCategoria} + /> + setNomeSearch(value.target.value)} + /> + + ( + { key: produto.uid, name: produto.nome, preco: produto.preco, categoria: produto.categoria.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 Produtos; \ No newline at end of file diff --git a/first-app/src/pages/Produto/produto.module.css b/first-app/src/pages/Produto/produto.module.css new file mode 100644 index 0000000..34341f2 --- /dev/null +++ b/first-app/src/pages/Produto/produto.module.css @@ -0,0 +1,20 @@ +.form_produto{ + width: 600px; +} + +.container_table { + width: 800px; + display: flex; + flex-direction: column; +} + +.container_table__filter{ + width: 100%; + display: flex; + justify-content: space-between; + margin-bottom: 1em; +} + +.container_table__search{ + width: 300px; +} \ No newline at end of file diff --git a/first-app/src/services/ProdutoService.ts b/first-app/src/services/ProdutoService.ts new file mode 100644 index 0000000..4b1ca03 --- /dev/null +++ b/first-app/src/services/ProdutoService.ts @@ -0,0 +1,71 @@ +import { Environments } from "../environments"; +import { ParamsType } from "../interfaces/ParamsType"; +import { Produto } from "../interfaces/Produto"; + +const URLApi = Environments.URLApi; + +const getAllCategorias = async () => { + const data = await fetch(`${URLApi}/categoria`) + .then((res)=>res.json()) + .catch((err)=>err); + + return data; +} + +const registerProduto = async (produto:Produto) => { + + const config = { + method:"POST", + body:JSON.stringify(produto), + headers: { + "Content-type":"application/json", + } + } + + try { + const data = await fetch(`${URLApi}/produto`,config) + .then((res)=>res.json()) + .catch((err)=>err); + return data; + + } catch (error) { + console.info(error); + return error; + } +} + +const getAllProdutos = async (params:ParamsType) => { + + const config = { + method:"POST", + body:JSON.stringify(params), + headers: { + "Content-type":"application/json", + } + } + + const data = await fetch (`${URLApi}/produto/list`,config) + .then((res)=>res.json()) + .catch((err)=>err); + + return data; +} + +const deleteProduto = async (uid:string) => { + + try { + const data = await fetch(`${URLApi}/produto?uid=${uid}`,{method:'DELETE'}) + .then((res)=>res.json()) + .catch((err)=> err); + return data; + } catch (error) { + console.info(error); + } +} + +export const ProdutoService = { + getAllCategorias, + registerProduto, + getAllProdutos, + deleteProduto +} \ No newline at end of file diff --git a/first-app/src/slices/CarrinhoSlice.ts b/first-app/src/slices/CarrinhoSlice.ts new file mode 100644 index 0000000..af95262 --- /dev/null +++ b/first-app/src/slices/CarrinhoSlice.ts @@ -0,0 +1,51 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { ItemCart } from "../interfaces/ItemCart"; +import { notify } from "../hooks/useToastfy"; + +const initialState = { + itens:[], + valorTotal:0 +} + +const calclQuantidade = (itens:Array) =>{ + let valor = 0; + itens.map((item) => { + valor += Number(item.produto.preco); + }) + return valor; +} + +export const addItem = createAsyncThunk( + "cart/add", + async (item:ItemCart) => { + return item; + } +) + + + +export const cartSlice = createSlice({ + name:"cliente", + initialState, + + reducers:{ + resetState:(state)=>{ + state.itens = [], + state.valorTotal=0 + }, + }, + + extraReducers(builder) { + builder + .addCase(addItem.fulfilled,(state,action:any)=>{ + state.itens = state.itens.concat(action.payload); + state.valorTotal = calclQuantidade(state.itens); + notify('Produto adicionado com sucessso!','success') + }) + + + }, +}); + +export const { resetState } = cartSlice.actions; +export default cartSlice.reducer; \ No newline at end of file diff --git a/first-app/src/slices/ProdutoSlice.ts b/first-app/src/slices/ProdutoSlice.ts new file mode 100644 index 0000000..d1844e9 --- /dev/null +++ b/first-app/src/slices/ProdutoSlice.ts @@ -0,0 +1,117 @@ +import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; +import { ProdutoService } from "../services/ProdutoService"; +import { Produto } from "../interfaces/Produto"; +import { notify } from "../hooks/useToastfy"; +import { ParamsType } from "../interfaces/ParamsType"; + +const initialState = { + produtos:[] as any, + total: 0, + categorias:[] as any, + loading:false, + error:false, + message:'' +} + +export const getAllCategoria = createAsyncThunk( + "produto/categorias", + async () =>{ + const data = await ProdutoService.getAllCategorias(); + return data; + } +) + +export const registerProduto = createAsyncThunk( + "produto/new", + async (produto:Produto, thunkApi) =>{ + const data = await ProdutoService.registerProduto(produto); + + if (data.error) { + return thunkApi.rejectWithValue(data); + } + return data; + } +) + +export const getAllProdutos = createAsyncThunk( + "produto/getAll", + async (parans:ParamsType) => { + const data = await ProdutoService.getAllProdutos(parans); + return data; + } +) + +export const deleteProduto = createAsyncThunk( + "produto/delete", + async (uid:string, thunkApi) => { + + const data = await ProdutoService.deleteProduto(uid); + + if (data.error) { + return thunkApi.rejectWithValue(data); + } + return data; + } +) + +export const produtoSlice = createSlice({ + name:"produto", + initialState, + + reducers:{ + resetState:(state)=>{ + state.loading = false; + state.error = false; + state.message = ''; + }, + }, + + extraReducers(builder) { + builder + .addCase(getAllCategoria.pending,(state)=>{ + state.loading = true; + }).addCase(getAllCategoria.fulfilled,(state,action)=>{ + state.categorias = action.payload.result; + state.loading = false; + }) + + .addCase(registerProduto.pending,(state)=>{ + state.loading = true; + }).addCase(registerProduto.fulfilled,(state)=>{ + state.loading = false; + state.message = 'Novo produto cadastrado!'; + notify(state.message,'success'); + }).addCase(registerProduto.rejected,(state, action:any)=>{ + state.loading = false; + state.error = true; + state.message = action.payload.message ? action.payload.message : 'Error ao cadastrar produto, tente novamente mais tarde!'; + notify(state.message, 'error'); + }) + + .addCase(getAllProdutos.pending,(state)=>{ + state.loading = true; + }).addCase(getAllProdutos.fulfilled,(state,action)=>{ + state.produtos = action.payload.result; + state.loading = false; + state.total = action.payload.total; + }) + + .addCase(deleteProduto.pending,(state)=>{ + state.loading = true; + }).addCase(deleteProduto.fulfilled,(state,action:any)=>{ + state.loading = false; + state.message = 'Produo deletado com sucesso!'; + state.produtos = state.produtos.filter((produto:Produto) => (produto.uid != action.payload.uid)); + notify(state.message,'success'); + }).addCase(deleteProduto.rejected,(state, action:any)=>{ + state.loading = false; + state.error = true; + state.message = action.payload.message ? action.payload.message : 'Error ao deletar produto, tente novamente mais tarde!'; + notify(state.message, 'error'); + }) + + }, +}); + +export const { resetState } = produtoSlice.actions; +export default produtoSlice.reducer; \ No newline at end of file diff --git a/first-app/src/store.ts b/first-app/src/store.ts index 6cdbc6a..918c831 100644 --- a/first-app/src/store.ts +++ b/first-app/src/store.ts @@ -2,9 +2,13 @@ import { configureStore } from "@reduxjs/toolkit"; //reduces import ClienteSlice from "./slices/ClienteSlice"; +import ProdutoSlice from "./slices/ProdutoSlice"; +import CarrinhoSlice from "./slices/CarrinhoSlice"; export default configureStore({ reducer:{ - cliente:ClienteSlice + cliente:ClienteSlice, + produto:ProdutoSlice, + cart:CarrinhoSlice } }) \ No newline at end of file