| @ -1,3 +1,3 @@ | |||||
| export const Environments = { | export const Environments = { | ||||
| URLApi: "http://jailton.bootcamp.dev.netuno.org:20190/services/netunovendas" | |||||
| URLApi: "https://jailton.bootcamp.dev.netuno.org:20193/services/netunovendas" | |||||
| } | } | ||||
| @ -0,0 +1,5 @@ | |||||
| export interface Encomenda { | |||||
| cliente:string, | |||||
| itens:[], | |||||
| valor:number | |||||
| } | |||||
| @ -0,0 +1,246 @@ | |||||
| import styles from './encomenda.module.css'; | |||||
| import { Button, Input, InputRef, Select, Space, Table } from 'antd'; | |||||
| import Column from 'antd/es/table/Column'; | |||||
| import { SearchOutlined } from '@ant-design/icons'; | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| import { ColumnType } from 'antd/es/table'; | |||||
| import Highlighter from 'react-highlight-words'; | |||||
| import { useDispatch, useSelector } from 'react-redux'; | |||||
| import { | |||||
| getAllEncomenda | |||||
| } from '../../slices/EncomendaSlice'; | |||||
| function Encomendas() { | |||||
| const dispatch = useDispatch<any>(); | |||||
| const { encomendas, loading } = useSelector((state: any) => state.encomenda); | |||||
| const [searchText, setSearchText] = useState(''); | |||||
| const [estadoFilter, setEstadoFilter] = useState(''); | |||||
| const [clienteFilter, setClienteFilter] = useState(''); | |||||
| const [dataFilter, setDataFilter] = useState(''); | |||||
| const [searchedColumn, setSearchedColumn] = useState(''); | |||||
| const searchInput = useRef<InputRef>(null); | |||||
| const handleSearch = ( | |||||
| selectedKeys: string[], | |||||
| dataIndex: any, | |||||
| ) => { | |||||
| setSearchText(selectedKeys[0]); | |||||
| setSearchedColumn(dataIndex); | |||||
| if (dataIndex == 'data') { | |||||
| setDataFilter(selectedKeys[0]) | |||||
| console.log(selectedKeys[0]) | |||||
| } else if (dataIndex == 'cliente') { | |||||
| setClienteFilter(selectedKeys[0]) | |||||
| console.log(selectedKeys[0]) | |||||
| } else if (dataIndex == 'status') { | |||||
| setEstadoFilter(selectedKeys[0]); | |||||
| console.log(selectedKeys[0]); | |||||
| } | |||||
| }; | |||||
| const handleReset = (clearFilters: () => void) => { | |||||
| clearFilters(); | |||||
| setSearchText(''); | |||||
| }; | |||||
| useEffect(() => { | |||||
| dispatch(getAllEncomenda( | |||||
| { | |||||
| pagination: { | |||||
| current: 1, | |||||
| pageSize: 5 | |||||
| } | |||||
| } | |||||
| )) | |||||
| }, []); | |||||
| // **Columns Filter** | |||||
| const getColumnSearchProps = (dataIndex: any): ColumnType<any> => ({ | |||||
| filterDropdown: ({ setSelectedKeys, selectedKeys, clearFilters, close }) => ( | |||||
| <div style={{ padding: 8 }} onKeyDown={(e) => e.stopPropagation()}> | |||||
| {dataIndex == 'status' ? ( | |||||
| <Select | |||||
| options={[ | |||||
| { value: '1', label: 'CREATED' }, | |||||
| { value: '2', label: 'CANCELED' }, | |||||
| { value: '3', label: 'FINISHED' } | |||||
| ]} | |||||
| style={{ marginBottom: 8, display: 'block' }} | |||||
| value={selectedKeys[0]} | |||||
| /> | |||||
| ) : | |||||
| ( | |||||
| <Input | |||||
| ref={searchInput} | |||||
| placeholder={`Search ${dataIndex}`} | |||||
| type={(dataIndex == 'data_criacao' || dataIndex == 'data_envio') ? 'date' : 'text'} | |||||
| value={selectedKeys[0]} | |||||
| onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])} | |||||
| onPressEnter={() => handleSearch(selectedKeys as string[], dataIndex)} | |||||
| style={{ marginBottom: 8, display: 'block' }} | |||||
| /> | |||||
| )} | |||||
| <Space> | |||||
| <Button | |||||
| type="primary" | |||||
| onClick={() => handleSearch(selectedKeys as string[], dataIndex)} | |||||
| icon={<SearchOutlined />} | |||||
| size="small" | |||||
| style={{ width: 90 }} | |||||
| > | |||||
| Search | |||||
| </Button> | |||||
| <Button | |||||
| onClick={() => clearFilters && handleReset(clearFilters)} | |||||
| size="small" | |||||
| style={{ width: 90 }} | |||||
| > | |||||
| Reset | |||||
| </Button> | |||||
| <Button | |||||
| type="link" | |||||
| size="small" | |||||
| onClick={() => { | |||||
| close(); | |||||
| }} | |||||
| > | |||||
| close | |||||
| </Button> | |||||
| </Space> | |||||
| </div> | |||||
| ), | |||||
| filterIcon: (filtered: boolean) => ( | |||||
| <SearchOutlined style={{ color: filtered ? '#1677ff' : undefined,fontSize:15 }} /> | |||||
| ), | |||||
| onFilter: (value, record) => | |||||
| record[dataIndex] | |||||
| .toString() | |||||
| .toLowerCase() | |||||
| .includes((value as string).toLowerCase()), | |||||
| onFilterDropdownOpenChange: (visible) => { | |||||
| if (visible) { | |||||
| setTimeout(() => searchInput.current?.select(), 100); | |||||
| } | |||||
| }, | |||||
| render: (text) => | |||||
| searchedColumn === dataIndex ? ( | |||||
| <Highlighter | |||||
| highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }} | |||||
| searchWords={[searchText]} | |||||
| autoEscape | |||||
| textToHighlight={text ? text.toString() : ''} | |||||
| /> | |||||
| ) : ( | |||||
| text | |||||
| ), | |||||
| }); | |||||
| // **Columns Filter** | |||||
| return ( | |||||
| <div className={styles.container_table}> | |||||
| <Table | |||||
| dataSource={ | |||||
| encomendas.map((encomenda: any) => ( | |||||
| { | |||||
| key: encomenda.uid, | |||||
| data_criacao: encomenda.data_criacao, | |||||
| data_envio: encomenda.data_envio, | |||||
| status: encomenda.status.nome, | |||||
| valor: `R$ ${encomenda.valor}` | |||||
| } | |||||
| )) | |||||
| } | |||||
| // 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="Data Criação" | |||||
| dataIndex="data_criacao" | |||||
| key="data_criacao" | |||||
| {...getColumnSearchProps('data_criacao')} | |||||
| /> | |||||
| <Column | |||||
| title="Data Envio" | |||||
| dataIndex="data_envio" | |||||
| key="data_envio" | |||||
| {...getColumnSearchProps('data_envio')} | |||||
| /> | |||||
| <Column | |||||
| title="Cliente" | |||||
| dataIndex="cliente" | |||||
| key="cliente" | |||||
| {...getColumnSearchProps('cliente')} | |||||
| /> | |||||
| <Column | |||||
| title="Status" | |||||
| dataIndex="status" | |||||
| key="status" | |||||
| {...getColumnSearchProps('status')} | |||||
| /> | |||||
| <Column | |||||
| title="Valor" | |||||
| dataIndex="valor" | |||||
| key="valor" | |||||
| /> | |||||
| <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 Encomendas; | |||||
| @ -1,73 +1,139 @@ | |||||
| import { Button, Collapse, Popconfirm, Space, Table } from 'antd'; | |||||
| import { Button, Divider, Select, Space, Table } from 'antd'; | |||||
| import styles from './encomenda.module.css'; | import styles from './encomenda.module.css'; | ||||
| import Column from 'antd/es/table/Column'; | import Column from 'antd/es/table/Column'; | ||||
| import { useSelector } from 'react-redux'; | |||||
| import { useDispatch, useSelector } from 'react-redux'; | |||||
| import { ItemCart } from '../../interfaces/ItemCart'; | import { ItemCart } from '../../interfaces/ItemCart'; | ||||
| import { | import { | ||||
| DeleteOutlined | DeleteOutlined | ||||
| } from '@ant-design/icons'; | } from '@ant-design/icons'; | ||||
| import { removeItem } from '../../slices/CarrinhoSlice'; | |||||
| import { getAllClientes } from '../../slices/ClienteSlice'; | |||||
| import { useState } from 'react'; | |||||
| import { ParamsType } from '../../interfaces/ParamsType'; | |||||
| import { Cliente } from '../../interfaces/Cliente'; | |||||
| import { newEncomenda } from '../../slices/EncomendaSlice'; | |||||
| import { notify } from '../../hooks/useToastfy'; | |||||
| import Search from 'antd/es/input/Search'; | |||||
| import { Encomenda } from '../../interfaces/Encomenda'; | |||||
| function NewEncomenda() { | function NewEncomenda() { | ||||
| const { itens } = useSelector((state: any) => state.cart); | |||||
| const dispatch = useDispatch<any>(); | |||||
| const { itens, valorTotal } = useSelector((state: any) => state.cart); | |||||
| const { clientes, loading:loadingCliente } = useSelector((state:any) => state.cliente); | |||||
| const { loading:loadingEncomenda } = useSelector((state:any) => state.encomenda); | |||||
| const handleDelete = () => { | |||||
| const [clienteUid, setCliente] = useState(''); | |||||
| const handleDelete = (uid: string) => { | |||||
| dispatch(removeItem(uid)); | |||||
| } | |||||
| const searchCliente = (nome:string) => { | |||||
| const params: ParamsType = { | |||||
| pagination: { | |||||
| current: 1, | |||||
| pageSize: 5 | |||||
| }, | |||||
| filter: { | |||||
| nome: nome | |||||
| } | |||||
| } | |||||
| dispatch(getAllClientes(params)) | |||||
| } | |||||
| const handleFinalize = () => { | |||||
| if (clienteUid == '' || clienteUid == null) { | |||||
| notify('Selecione o cliente!','error') | |||||
| return; | |||||
| } else if (itens.length == 0) { | |||||
| notify('A lista está vazia!','error') | |||||
| return; | |||||
| } | |||||
| const encomenda:Encomenda = { | |||||
| itens:itens, | |||||
| cliente:clienteUid, | |||||
| valor:valorTotal | |||||
| } | |||||
| dispatch(newEncomenda(encomenda)); | |||||
| } | } | ||||
| return ( | return ( | ||||
| <div className={styles.container_table}> | |||||
| <Collapse | |||||
| size="large" | |||||
| items={[{ | |||||
| key: '1', label: 'Itens da encomenda.', children: ( | |||||
| <Table | |||||
| dataSource={ | |||||
| itens.map((item: ItemCart) => ( | |||||
| { key: item.produto.uid, name: item.produto.nome, quantidade: item.quantidade, preco: item.produto.preco } | |||||
| )) | |||||
| } | |||||
| pagination={false} | |||||
| bordered={true} | |||||
| scroll={{ x: 300, y: 300 }} | |||||
| > | |||||
| <Column title="Nome" dataIndex="name" key="name" /> | |||||
| <Column title="Quantidade" dataIndex="quantidade" key="quantidade" /> | |||||
| <Column title="Preço" dataIndex="preco" key="preco" /> | |||||
| <Column | |||||
| title="Actions" | |||||
| key="action" | |||||
| render={(_: any) => ( | |||||
| <Space size="middle"> | |||||
| <Popconfirm | |||||
| title="Atenção:" | |||||
| description="Confirme a ação de delete!" | |||||
| onConfirm={() => { handleDelete() }} | |||||
| okText="Sim" | |||||
| cancelText="Não" | |||||
| > | |||||
| <Button value="small" | |||||
| type="primary" | |||||
| danger icon={<DeleteOutlined />} | |||||
| size="large" | |||||
| title="Deletar" | |||||
| /> | |||||
| </Popconfirm> | |||||
| </Space> | |||||
| )} | |||||
| <div className={styles.container_encomenda}> | |||||
| <Table | |||||
| dataSource={ | |||||
| itens.map((item: ItemCart) => ( | |||||
| { key: item.produto.uid, name: item.produto.nome, quantidade: item.quantidade, preco: item.produto.preco } | |||||
| )) | |||||
| } | |||||
| pagination={false} | |||||
| bordered={true} | |||||
| scroll={{ x: 300, y: 300 }} | |||||
| style={{minHeight:300}} | |||||
| > | |||||
| <Column title="Nome" dataIndex="name" key="name" /> | |||||
| <Column title="Quantidade" dataIndex="quantidade" key="quantidade" /> | |||||
| <Column title="Preço" dataIndex="preco" key="preco" /> | |||||
| <Column | |||||
| title="Actions" | |||||
| key="action" | |||||
| render={(_: any, record: any) => ( | |||||
| <Space size="middle"> | |||||
| <Button value="small" | |||||
| type="primary" | |||||
| danger icon={<DeleteOutlined />} | |||||
| size="large" | |||||
| title="Deletar" | |||||
| onClick={() => { handleDelete(record.key) }} | |||||
| /> | /> | ||||
| </Space> | |||||
| )} | |||||
| /> | |||||
| </Table> | |||||
| <Divider style={{ backgroundColor: '#000' }} /> | |||||
| <div className={styles.container_cliente}> | |||||
| <Select | |||||
| style={{ width: 300 }} | |||||
| placeholder="Selecione o cliente" | |||||
| optionFilterProp="children" | |||||
| onChange={setCliente} | |||||
| loading={loadingCliente} | |||||
| options={ | |||||
| clientes.map((cliente:Cliente) => ( | |||||
| {value:cliente.uid, label:cliente.nome} | |||||
| )) | |||||
| } | |||||
| /> | |||||
| <Search | |||||
| placeholder="Buscar cliente por nome" | |||||
| enterButton | |||||
| style={{ width: 300 }} | |||||
| onSearch={searchCliente} | |||||
| loading={loadingCliente} | |||||
| /> | |||||
| </div> | |||||
| </Table> | |||||
| <Divider style={{ backgroundColor: '#000' }} /> | |||||
| ) | |||||
| }]} | |||||
| <div className={styles.container_finalize}> | |||||
| <div className={styles.container_finalize__total}> | |||||
| <span>Total: </span> <span>{`R$ ${valorTotal.toFixed(2)}`}</span> | |||||
| </div> | |||||
| <Button type="primary" | |||||
| onClick={handleFinalize} | |||||
| disabled={itens.length == 0} | |||||
| loading={loadingEncomenda} | |||||
| >Finalizar Encomenda | |||||
| </Button> | |||||
| </div> | |||||
| /> | |||||
| </div> | </div> | ||||
| ) | ) | ||||
| } | } | ||||
| @ -1,18 +1,33 @@ | |||||
| .container_table { | |||||
| .container_encomenda { | |||||
| width: 800px; | width: 800px; | ||||
| height: 100%; | height: 100%; | ||||
| margin-top: 3em; | margin-top: 3em; | ||||
| gap: 1em; | |||||
| display: flex; | display: flex; | ||||
| flex-direction: column; | flex-direction: column; | ||||
| } | } | ||||
| .container_table__filter{ | |||||
| width: 100%; | |||||
| .container_cliente { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: baseline; | |||||
| gap: 0.5em; | |||||
| } | |||||
| .container_finalize{ | |||||
| display: flex; | display: flex; | ||||
| justify-content: end; | |||||
| margin-bottom: 1em; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| width: 100%; | |||||
| } | } | ||||
| .container_table__search{ | |||||
| width: 300px; | |||||
| .container_finalize__total{ | |||||
| font-size: 1.3em; | |||||
| } | } | ||||
| .container_table { | |||||
| width: 800px; | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| } | |||||
| @ -0,0 +1,47 @@ | |||||
| import { Environments} from "../environments"; | |||||
| import { Encomenda } from "../interfaces/Encomenda"; | |||||
| import { ParamsType } from "../interfaces/ParamsType"; | |||||
| const { URLApi } = Environments; | |||||
| const newEncomenda = async (encomenda:Encomenda) => { | |||||
| const config = { | |||||
| method:"POST", | |||||
| body:JSON.stringify(encomenda), | |||||
| headers: { | |||||
| "Content-type":"application/json", | |||||
| } | |||||
| } | |||||
| try { | |||||
| const data = await fetch(`${URLApi}/encomenda`,config) | |||||
| .then((res)=>res.json()) | |||||
| .catch((err)=>err); | |||||
| return data; | |||||
| } catch (error) { | |||||
| console.log(error) | |||||
| } | |||||
| } | |||||
| const getAllEncomenda = async (parans:ParamsType) => { | |||||
| const config = { | |||||
| method:"POST", | |||||
| body:JSON.stringify(parans), | |||||
| headers: { | |||||
| "Content-type":"application/json", | |||||
| } | |||||
| } | |||||
| const data = await fetch(`${URLApi}/encomenda/list`,config) | |||||
| .then((res)=>res.json()) | |||||
| .catch((err)=>err); | |||||
| return data; | |||||
| } | |||||
| export const encomendaService = { | |||||
| newEncomenda, | |||||
| getAllEncomenda | |||||
| } | |||||
| @ -0,0 +1,72 @@ | |||||
| import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; | |||||
| import { notify } from "../hooks/useToastfy"; | |||||
| import { Encomenda } from "../interfaces/Encomenda"; | |||||
| import { encomendaService } from "../services/EncomendaService"; | |||||
| import { ParamsType } from "../interfaces/ParamsType"; | |||||
| const initialState = { | |||||
| encomendas:[] as any, | |||||
| loading:false, | |||||
| error:false, | |||||
| message:'' | |||||
| } | |||||
| export const newEncomenda = createAsyncThunk( | |||||
| "encomenda/new", | |||||
| async (encomenda:Encomenda, ThunkApi) => { | |||||
| const data = await encomendaService.newEncomenda(encomenda); | |||||
| if (data.error) { | |||||
| return ThunkApi.rejectWithValue(data); | |||||
| } | |||||
| return data; | |||||
| } | |||||
| ) | |||||
| export const getAllEncomenda = createAsyncThunk( | |||||
| "encomenda/getAll", | |||||
| async (parans:ParamsType) => { | |||||
| const data = await encomendaService.getAllEncomenda(parans); | |||||
| return data; | |||||
| } | |||||
| ) | |||||
| export const encomendaSlice = createSlice({ | |||||
| name:"cliente", | |||||
| initialState, | |||||
| reducers:{ | |||||
| resetState:(state)=>{ | |||||
| state.loading = false, | |||||
| state.error = false; | |||||
| state.message = '' | |||||
| }, | |||||
| }, | |||||
| extraReducers(builder) { | |||||
| builder | |||||
| .addCase(newEncomenda.pending,(state)=>{ | |||||
| state.loading = true; | |||||
| }).addCase(newEncomenda.fulfilled,(state)=>{ | |||||
| state.loading = false; | |||||
| state.error = false; | |||||
| notify('Encomenda feita com sucesso!','success') | |||||
| }).addCase(newEncomenda.rejected,(state,action:any)=>{ | |||||
| state.loading = false, | |||||
| state.error = true; | |||||
| state.message = action.payload.messege ? action.payload.messege : ''; | |||||
| notify(state.message,'error'); | |||||
| }) | |||||
| .addCase(getAllEncomenda.pending,(state)=>{ | |||||
| state.loading = true; | |||||
| }).addCase(getAllEncomenda.fulfilled,(state, action:any)=>{ | |||||
| state.loading = false; | |||||
| state.error = false; | |||||
| state.encomendas = action.payload.results; | |||||
| }) | |||||
| }, | |||||
| }); | |||||
| export const { resetState } = encomendaSlice.actions; | |||||
| export default encomendaSlice.reducer; | |||||