This commit is contained in:
Ubuntu 2023-10-26 21:20:16 +00:00
commit 42735388b7
9 changed files with 624 additions and 409 deletions

19
package-lock.json generated
View File

@ -1590,21 +1590,6 @@
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
"license": "MIT" "license": "MIT"
}, },
"node_modules/caniuse-lite": {
"version": "1.0.30001335",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
}
],
"license": "CC-BY-4.0"
},
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@ -5706,10 +5691,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz" "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
}, },
"caniuse-lite": {
"version": "1.0.30001335",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz"
},
"chalk": { "chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",

View File

@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev --port=3444",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint"

View File

@ -1,34 +1,43 @@
import Box from '@mui/material/Box'; import Box from '@mui/material/Box'
import Checkbox from '@mui/material/Checkbox'; import Checkbox from '@mui/material/Checkbox'
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper'
import Table from '@mui/material/Table'; import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'; import TableBody from '@mui/material/TableBody'
import TableCell from '@mui/material/TableCell'; import TableCell from '@mui/material/TableCell'
import TableContainer from '@mui/material/TableContainer'; import TableContainer from '@mui/material/TableContainer'
import TableHead from '@mui/material/TableHead'; import TableHead from '@mui/material/TableHead'
import TablePagination from '@mui/material/TablePagination'; import TablePagination from '@mui/material/TablePagination'
import TableRow from '@mui/material/TableRow'; import TableRow from '@mui/material/TableRow'
import TableSortLabel from '@mui/material/TableSortLabel'; import TableSortLabel from '@mui/material/TableSortLabel'
import { visuallyHidden } from '@mui/utils'; import { visuallyHidden } from '@mui/utils'
import { forwardRef, useEffect, useState } from 'react'; import { forwardRef, useDeferredValue, useEffect, useState } from 'react'
import Image from 'next/image'; import Image from 'next/image'
import MuiAlert, { AlertProps } from '@mui/material/Alert'; import MuiAlert, { AlertProps } from '@mui/material/Alert'
import Snackbar from '@mui/material/Snackbar'; import Snackbar from '@mui/material/Snackbar'
import Modal from '@mui/material/Modal'; import Modal from '@mui/material/Modal'
import { FormControl, InputLabel, MenuItem, Select, TextField, Typography } from '@mui/material'; import {
import FormData from 'form-data'; FormControl,
import { InputUploadView } from '../inputUploadImg/inputUploadView'; InputLabel,
MenuItem,
Select,
TextField,
Typography
} from '@mui/material'
import FormData from 'form-data'
import { InputUploadView } from '../inputUploadImg/inputUploadView'
import { api } from '../../services/api'; import { api } from '../../services/api'
import FaqButton1 from '../buttons/faqButton/FaqButton1'; import FaqButton1 from '../buttons/faqButton/FaqButton1'
import FaqButton2 from '../buttons/faqButton/FaqButton2'; import FaqButton2 from '../buttons/faqButton/FaqButton2'
import { StyledStatus, TableView } from './TableView'; import { StyledStatus, TableView } from './TableView'
import ReactLoading from 'react-loading'; import ReactLoading from 'react-loading'
import { sanitizeStringSearch } from '../../utils/stringHelper'
import { stableSort } from '../../utils/stableSort'
const style = { const style = {
position: 'absolute' as const, position: 'absolute' as const,
@ -42,62 +51,50 @@ const style = {
boxShadow: 24, boxShadow: 24,
p: 4, p: 4,
overflowY: 'scroll' overflowY: 'scroll'
}; }
const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert( const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
props, props,
ref, ref
) { ) {
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />; return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
}); })
interface Data { interface Data {
clientCode: number, clientCode: number
name: string, name: string
unity: string, unity: string
status: string, status: string
} }
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) { function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
if (b[orderBy] < a[orderBy]) { if (b[orderBy] < a[orderBy]) {
return -1; return -1
} }
if (b[orderBy] > a[orderBy]) { if (b[orderBy] > a[orderBy]) {
return 1; return 1
} }
return 0; return 0
} }
type Order = 'asc' | 'desc'; type Order = 'asc' | 'desc'
function getComparator<Key extends keyof any>( function getComparator<Key extends keyof any>(
order: Order, order: Order,
orderBy: any, orderBy: any
): ( ): (
a: { [key in Key]: number | string }, a: { [key in Key]: number | string },
b: { [key in Key]: number | string }, b: { [key in Key]: number | string }
) => number { ) => number {
return order === 'desc' return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy) ? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy); : (a, b) => -descendingComparator(a, b, orderBy)
}
function stableSort<T>(array: any, comparator: (a: T, b: T) => number) {
const stabilizedThis = array.map((el, index) => [el, index] as [T, number]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) {
return order;
}
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
} }
interface HeadCell { interface HeadCell {
disablePadding: boolean; disablePadding: boolean
id: keyof Data | string; id: keyof Data | string
label: string; label: string
numeric: boolean; numeric: boolean
} }
const headCells: readonly HeadCell[] = [ const headCells: readonly HeadCell[] = [
@ -105,44 +102,70 @@ const headCells: readonly HeadCell[] = [
id: 'clientCode', id: 'clientCode',
numeric: false, numeric: false,
disablePadding: true, disablePadding: true,
label: 'código do cliente', label: 'código do cliente'
}, },
{ {
id: 'name', id: 'name',
numeric: false, numeric: false,
disablePadding: false, disablePadding: false,
label: 'name', label: 'name'
}, },
{ {
id: 'unity', id: 'unity',
numeric: false, numeric: false,
disablePadding: false, disablePadding: false,
label: 'unity', label: 'unity'
}, },
{ {
id: 'status', id: 'status',
numeric: false, numeric: false,
disablePadding: false, disablePadding: false,
label: 'status', label: 'status'
}, }
]; ]
interface EnhancedTableProps { interface EnhancedTableProps {
numSelected: number; numSelected: number
onRequestSort: (event: React.MouseEvent<unknown>, property: keyof Data) => void; onRequestSort: (
onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void; event: React.MouseEvent<unknown>,
order: Order; property: keyof Data
orderBy: string; ) => void
rowCount: number; onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void
order: Order
orderBy: string
rowCount: number
}
interface ClientsTableInterface {
clients: any
onChange: any
}
function sortedClients(client, search: string) {
search = sanitizeStringSearch(search)
return client
.map(client => ({
...client,
name: sanitizeStringSearch(client.name),
client_id: sanitizeStringSearch(String(client.client_id))
}))
.filter((client) => client.name.includes(search) || client.client_id.includes(search))
} }
function EnhancedTableHead(props: EnhancedTableProps) { function EnhancedTableHead(props: EnhancedTableProps) {
const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = const {
props; onSelectAllClick,
order,
orderBy,
numSelected,
rowCount,
onRequestSort
} = props
const createSortHandler = const createSortHandler =
(property: any) => (event: React.MouseEvent<unknown>) => { (property: any) => (event: React.MouseEvent<unknown>) => {
onRequestSort(event, property); onRequestSort(event, property)
}; }
return ( return (
<TableHead> <TableHead>
@ -154,14 +177,14 @@ function EnhancedTableHead(props: EnhancedTableProps) {
checked={rowCount > 0 && numSelected === rowCount} checked={rowCount > 0 && numSelected === rowCount}
onChange={onSelectAllClick} onChange={onSelectAllClick}
inputProps={{ inputProps={{
'aria-label': 'select all desserts', 'aria-label': 'select all desserts'
}} }}
/> />
</TableCell> </TableCell>
{headCells.map((headCell) => ( {headCells.map((headCell) => (
<TableCell <TableCell
key={headCell.id} key={headCell.id}
align='left' align="left"
padding={headCell.disablePadding ? 'none' : 'normal'} padding={headCell.disablePadding ? 'none' : 'normal'}
sortDirection={orderBy === headCell.id ? order : false} sortDirection={orderBy === headCell.id ? order : false}
> >
@ -181,137 +204,136 @@ function EnhancedTableHead(props: EnhancedTableProps) {
))} ))}
</TableRow> </TableRow>
</TableHead> </TableHead>
); )
} }
interface ClientsTableInterface { export default function ClientTable({
clients: any, clients,
onChange: any onChange
} }: ClientsTableInterface) {
const [order, setOrder] = useState<Order>('asc')
const [orderBy, setOrderBy] = useState<keyof Data | string>('asc')
const [selected, setSelected] = useState<readonly string[]>([])
const [page, setPage] = useState<number>(0)
const [dense, setDense] = useState<boolean>(false)
const [rowsPerPage, setRowsPerPage] = useState<number>(5)
export default function ClientTable({ clients, onChange }: ClientsTableInterface) { const [openSnackError, setOpenSnackError] = useState<boolean>(false)
const [order, setOrder] = useState<Order>('asc');
const [orderBy, setOrderBy] = useState<keyof Data | string>('asc');
const [selected, setSelected] = useState<readonly string[]>([]);
const [page, setPage] = useState<number>(0);
const [dense, setDense] = useState<boolean>(false);
const [rowsPerPage, setRowsPerPage] = useState<number>(5);
const [openSnackError, setOpenSnackError] = useState<boolean>(false); const [open, setOpen] = useState(false)
const [open, setOpen] = useState(false);
const [openModalInativar, setOpenModalInativar] = useState(false) const [openModalInativar, setOpenModalInativar] = useState(false)
const handleOpen = () => setOpen(true); const [clientEdit, setClientEdit] = useState<any>()
const handleClose = () => setOpen(false); const [logo, setLogo] = useState(false)
const [imageURLS, setImageURLs] = useState([])
const [images, setImages] = useState([] as any)
const [nivelAcess, setnivelAcess] = useState<any>(2)
const [openEditUserModal, setOpenEditUserModal] = useState<any>(false)
const [openSnackSuccess, setOpenSnackSuccess] = useState<boolean>(false)
const [loading, setLoading] = useState<boolean>(false)
const [selectedClient, setSelectedClient] = useState<any>(2)
const [search, setSearch] = useState('')
const [units, setUnits] = useState([]) const [units, setUnits] = useState([])
// Avoid a layout jump when reaching the last page with empty rows.
const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - clients.length) : 0
const formData = new FormData()
const listClients = useDeferredValue(
sortedClients(stableSort(clients, getComparator(order, orderBy)), search)
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
)
// const handleOpen = () => setOpen(true)
const handleClose = () => setOpen(false)
function getClientUnits(client_id: number) { function getClientUnits(client_id: number) {
api.post('/units', { api
"filters": [ .post('/units', {
{ "type": "=", "field": "dados_cadastrais.cod_smart_cliente", "value": client_id } filters: [
{
type: '=',
field: 'dados_cadastrais.cod_smart_cliente',
value: client_id
}
], ],
"fields": ["unidade"], fields: ['unidade'],
"distinct": true distinct: true
}).then(res => setUnits(res.data.data)) })
.then((res) => setUnits(res.data.data))
.catch(() => setOpenSnackError(true)) .catch(() => setOpenSnackError(true))
return units return units
} }
const handleCloseSnack = (event?: React.SyntheticEvent | Event, reason?: string) => { const handleCloseSnack = (
event?: React.SyntheticEvent | Event,
reason?: string
) => {
if (reason === 'clickaway') { if (reason === 'clickaway') {
return; return
}
setOpenSnackError(false)
} }
setOpenSnackError(false);
};
const handleRequestSort = ( const handleRequestSort = (
event: React.MouseEvent<unknown>, event: React.MouseEvent<unknown>,
property: keyof Data, property: keyof Data
) => { ) => {
const isAsc = orderBy === property && order === 'asc'; const isAsc = orderBy === property && order === 'asc'
setOrder(isAsc ? 'desc' : 'asc'); setOrder(isAsc ? 'desc' : 'asc')
setOrderBy(property); setOrderBy(property)
}; }
const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => { const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.checked) { if (event.target.checked) {
const newSelecteds = clients.map((n) => n.name); const newSelecteds = clients.map((n) => n.name)
setSelected(newSelecteds); setSelected(newSelecteds)
return; return
}
setSelected([])
} }
setSelected([]);
};
const handleClick = (event: React.MouseEvent<unknown>, code: string) => { const handleClick = (event: React.MouseEvent<unknown>, code: string) => {
const selectedIndex = selected.indexOf(code); const selectedIndex = selected.indexOf(code)
let newSelected: readonly string[] = []; let newSelected: readonly string[] = []
if (selectedIndex === -1) { if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, code); newSelected = newSelected.concat(selected, code)
} else if (selectedIndex === 0) { } else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1)); newSelected = newSelected.concat(selected.slice(1))
} else if (selectedIndex === selected.length - 1) { } else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1)); newSelected = newSelected.concat(selected.slice(0, -1))
} else if (selectedIndex > 0) { } else if (selectedIndex > 0) {
newSelected = newSelected.concat( newSelected = newSelected.concat(
selected.slice(0, selectedIndex), selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1), selected.slice(selectedIndex + 1)
); )
} }
setSelected(newSelected); setSelected(newSelected)
}; }
const handleChangePage = (event: unknown, newPage: number) => { const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage); setPage(newPage)
}; }
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => { const handleChangeRowsPerPage = (
setRowsPerPage(parseInt(event.target.value, 10)); event: React.ChangeEvent<HTMLInputElement>
setPage(0); ) => {
}; setRowsPerPage(parseInt(event.target.value, 10))
setPage(0)
}
const isSelected = (code: any) => selected.indexOf(code.toString()) !== -1; const isSelected = (code: any) => selected.indexOf(code.toString()) !== -1
useEffect(() => {
onChange(selected)
}, [selected])
// Avoid a layout jump when reaching the last page with empty rows.
const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - clients.length) : 0;
const formData = new FormData()
const [clientEdit, setClientEdit] = useState<any>()
const [logo, setLogo] = useState(false)
const [imageURLS, setImageURLs] = useState([])
const [images, setImages] = useState([] as any)
const [nivelAcess, setnivelAcess] = useState<any>(2);
const [openEditUserModal, setOpenEditUserModal] = useState<any>(false);
const [selectedClient, setSelectedClient] = useState<any>(2);
const [search, setSearch] = useState('')
useEffect(() => {
if (images.length < 1) return
const newImageUrls: any = []
images.forEach((image: any) =>
newImageUrls.push(URL.createObjectURL(image))
)
setImageURLs(newImageUrls)
}, [images])
function onImageChange(e: any) { function onImageChange(e: any) {
setImages([...e.target.files]) setImages([...e.target.files])
setLogo(e.target.files[0]) setLogo(e.target.files[0])
} }
const [openSnackSuccess, setOpenSnackSuccess] = useState<boolean>(false)
const [loading, setLoading] = useState<boolean>(false)
async function handleUpdateClient(props, id) { async function handleUpdateClient(props, id) {
logo && formData.append('file', logo) logo && formData.append('file', logo)
let new_profile_picture let new_profile_picture
@ -337,14 +359,40 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
} }
} }
useEffect(() => {
onChange(selected)
}, [selected])
useEffect(() => {
if (images.length < 1) return
const newImageUrls: any = []
images.forEach((image: any) =>
newImageUrls.push(URL.createObjectURL(image))
)
setImageURLs(newImageUrls)
}, [images])
return ( return (
<TableView> <TableView>
<Snackbar open={openSnackError} autoHideDuration={4000} onClose={handleCloseSnack}> <Snackbar
<Alert onClose={handleCloseSnack} severity="error" sx={{ width: '100%' }}> open={openSnackError}
autoHideDuration={4000}
onClose={handleCloseSnack}
>
<Alert
onClose={handleCloseSnack}
severity="error"
sx={{ width: '100%' }}
>
Não foi possivel encontrar unidades do client! Não foi possivel encontrar unidades do client!
</Alert> </Alert>
</Snackbar> </Snackbar>
<TextField onChange={(e) => setSearch(e.target.value)} placeholder='persquisar por nome:' />
<TextField
onChange={(e) => setSearch(e.target.value)}
placeholder="Pesquisar por nome:"
/>
<Paper sx={{ width: '100%', mb: 2 }}> <Paper sx={{ width: '100%', mb: 2 }}>
<TableContainer> <TableContainer>
<Table <Table
@ -361,12 +409,9 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
rowCount={clients.length} rowCount={clients.length}
/> />
<TableBody> <TableBody>
{stableSort(clients, getComparator(order, orderBy)) {listClients.map((row, index) => {
.filter(client => client.name.toLowerCase().includes(search.toLowerCase())) const isItemSelected = isSelected(row.id)
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) const labelId = `enhanced-table-checkbox-${index}`
.map((row, index) => {
const isItemSelected = isSelected(row.id);
const labelId = `enhanced-table-checkbox-${index}`;
return ( return (
<TableRow <TableRow
@ -382,11 +427,10 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
<Checkbox <Checkbox
color="primary" color="primary"
checked={isItemSelected} checked={isItemSelected}
inputProps={{ inputProps={{ 'aria-labelledby': labelId }}
'aria-labelledby': labelId,
}}
/> />
</TableCell> </TableCell>
<TableCell <TableCell
component="th" component="th"
id={labelId} id={labelId}
@ -395,24 +439,51 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
> >
Client - {row.client_id} Client - {row.client_id}
</TableCell> </TableCell>
<TableCell align="left" style={{ cursor: 'pointer' }} onClick={() => {
<TableCell
align="left"
style={{ cursor: 'pointer' }}
onClick={() => {
setOpenEditUserModal(true) setOpenEditUserModal(true)
setSelectedClient(row) setSelectedClient(row)
setClientEdit({ email: row.email, name: row.name, client_id: row.client_id, profile_picture: row.profile_picture }) setClientEdit({
}}>{row.name}</TableCell> email: row.email,
<TableCell align="left" style={{ cursor: 'pointer' }} onClick={() => { name: row.name,
client_id: row.client_id,
profile_picture: row.profile_picture
})
}}
>
{row.name}
</TableCell>
<TableCell
align="left"
style={{ cursor: 'pointer' }}
onClick={() => {
setOpen(true) setOpen(true)
getClientUnits(row.client_id) getClientUnits(row.client_id)
setSelectedClient(row) setSelectedClient(row)
}}>clique aqui para ver as unidades</TableCell> }}
<TableCell align="left"><StyledStatus status={row.deleted_at ? 'inativo' : 'ativo'}> {row.deleted_at ? 'inativo' : 'ativo'}</StyledStatus></TableCell> >
clique aqui para ver as unidades
</TableCell>
<TableCell align="left">
<StyledStatus
status={row.deleted_at ? 'inativo' : 'ativo'}
>
{' '}
{row.deleted_at ? 'inativo' : 'ativo'}
</StyledStatus>
</TableCell>
</TableRow> </TableRow>
); )
})} })}
{emptyRows > 0 && ( {emptyRows > 0 && (
<TableRow <TableRow
style={{ style={{
height: (dense ? 33 : 53) * emptyRows, height: (dense ? 33 : 53) * emptyRows
}} }}
> >
<TableCell colSpan={6} /> <TableCell colSpan={6} />
@ -421,6 +492,7 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
<TablePagination <TablePagination
rowsPerPageOptions={[5, 10, 25]} rowsPerPageOptions={[5, 10, 25]}
component="div" component="div"
@ -431,6 +503,7 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
onRowsPerPageChange={handleChangeRowsPerPage} onRowsPerPageChange={handleChangeRowsPerPage}
/> />
</Paper> </Paper>
<Modal <Modal
open={openEditUserModal} open={openEditUserModal}
onClose={() => setOpenEditUserModal(false)} onClose={() => setOpenEditUserModal(false)}
@ -515,13 +588,15 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
<div className="imgContainer"> <div className="imgContainer">
<article> <article>
{imageURLS.map((imageSrc, index) => { {imageURLS.map((imageSrc, index) => {
return <Image return (
<Image
src={imageSrc} src={imageSrc}
key={index} key={index}
width={30} width={30}
height={30} height={30}
className="image" className="image"
/> />
)
})} })}
</article> </article>
</div> </div>
@ -543,7 +618,7 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
</div> </div>
</InputUploadView> </InputUploadView>
<div className='select'> <div className="select">
<FormControl sx={{ width: 350, ml: 5, mt: 2 }}> <FormControl sx={{ width: 350, ml: 5, mt: 2 }}>
<InputLabel id="demo-select-small">Nivel de acesso</InputLabel> <InputLabel id="demo-select-small">Nivel de acesso</InputLabel>
<Select <Select
@ -551,24 +626,29 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
id="demo-select-small" id="demo-select-small"
value={nivelAcess} value={nivelAcess}
label="Unidade" label="Unidade"
onChange={value => setnivelAcess(value.target.value)} onChange={(value) => setnivelAcess(value.target.value)}
fullWidth fullWidth
> >
<MenuItem value={1}>Administrador</MenuItem> <MenuItem value={1}>Administrador</MenuItem>
<MenuItem value={2}>Cliente</MenuItem> <MenuItem value={2}>Cliente</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
</div> </div>
{!loading && <FaqButton1 title="Cancelar" onClick={() => setOpenEditUserModal(false)} />} {!loading && (
{!loading <FaqButton1
? <FaqButton2 title="Cancelar"
onClick={() => setOpenEditUserModal(false)}
/>
)}
{!loading ? (
<FaqButton2
title="Salvar" title="Salvar"
onClick={() => handleUpdateClient(clientEdit, selectedClient.id)} onClick={() => handleUpdateClient(clientEdit, selectedClient.id)}
/> />
: <ReactLoading type='spin' color='#254f7f' height='5%' width='5%' /> ) : (
} <ReactLoading type="spin" color="#254f7f" height="5%" width="5%" />
)}
</Box> </Box>
</Modal> </Modal>
@ -579,18 +659,23 @@ export default function ClientTable({ clients, onChange }: ClientsTableInterface
aria-describedby="modal-modal-description" aria-describedby="modal-modal-description"
> >
<Box sx={style}> <Box sx={style}>
{ {units.map((units, index) => {
units.map((units, index) => { return (
return <> <>
<li style={{ <li
style={{
listStyle: 'none' listStyle: 'none'
}} key={index}>{units.unidade}</li> }}
key={index}
>
{units.unidade}
</li>
<hr /> <hr />
</> </>
}) )
} })}
</Box> </Box>
</Modal> </Modal>
</TableView> </TableView>
); )
} }

View File

@ -26,7 +26,7 @@ export function MyApp({ Component, pageProps, notificationsCount }: AppProps | a
const rota = router.pathname const rota = router.pathname
useEffect(() => { useEffect(() => {
const handleStart = (url) => { const handleStart = () => {
NProgress.start() NProgress.start()
} }
const handleStop = () => { const handleStop = () => {
@ -79,7 +79,7 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => {
let notificationsCount let notificationsCount
await apiClient.post('/download').then(console.log) await apiClient.post('/download')
if (!token) { if (!token) {
return { return {

View File

@ -1,88 +1,111 @@
import Box from '@mui/material/Box'; import Box from '@mui/material/Box'
import FormControl from '@mui/material/FormControl'; import FormControl from '@mui/material/FormControl'
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@mui/material/InputLabel'
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem'
import Select, { SelectChangeEvent } from '@mui/material/Select'; import Select, { SelectChangeEvent } from '@mui/material/Select'
import { GetServerSideProps } from 'next'; import { GetServerSideProps } from 'next'
import Head from 'next/head'; import Head from 'next/head'
import { parseCookies } from 'nookies'; import { parseCookies } from 'nookies'
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react'
import BasicButton from '../../components/buttons/basicButton/BasicButton'; import BasicButton from '../../components/buttons/basicButton/BasicButton'
import Header from '../../components/header/Header'; import Header from '../../components/header/Header'
import PageTitle from '../../components/pageTitle/PageTitle'; import PageTitle from '../../components/pageTitle/PageTitle'
import { api } from '../../services/api'; import { api } from '../../services/api'
import getAPIClient from '../../services/ssrApi'; import getAPIClient from '../../services/ssrApi'
import { TableBodyView, TableHeader, TableView } from '../../styles/layouts/ResumoOperacao/ResumoOperacaoView'; import {
TableBodyView,
TableHeader,
TableView
} from '../../styles/layouts/ResumoOperacao/ResumoOperacaoView'
import Fab from '@mui/material/Fab'; import Fab from '@mui/material/Fab'
import NavigationIcon from '@mui/icons-material/Navigation'; import NavigationIcon from '@mui/icons-material/Navigation'
export default function ResumoOperacao({tableData, clients, userName, clientMonth}: any) { export default function ResumoOperacao({
const csvData = tableData; tableData,
clients,
const [month, setMonth] = useState(''); userName,
const [unidade, setUnidade] = useState(clients[0].cod_smart_unidade); clientMonth
const [tableDataState, setTableDataState] = useState<any>([]); }: any) {
const [month, setMonth] = useState('')
const [unidade, setUnidade] = useState(clients[0].cod_smart_unidade)
const [tableDataState, setTableDataState] = useState<any>([])
const { ['user-id']: id } = parseCookies() const { ['user-id']: id } = parseCookies()
const handleChangeMonth = (event: SelectChangeEvent) => { const handleChangeMonth = (event: SelectChangeEvent) => {
setMonth(event.target.value); setMonth(event.target.value)
}; }
const handleChangeUnidade = (event: SelectChangeEvent) => { const handleChangeUnidade = (event: SelectChangeEvent) => {
setUnidade(event.target.value); setUnidade(event.target.value)
}; }
const [ pageYPosition, setPageYPosition ] = useState(0); const [pageYPosition, setPageYPosition] = useState(0)
function getPageYAfterScroll(){ function getPageYAfterScroll() {
setPageYPosition(window.scrollY); setPageYPosition(window.scrollY)
} }
function downloadCSVFile(csv, filename) { function downloadCSVFile(csv, filename) {
const csv_file = new Blob(["\ufeff",csv], {type: "text/csv"}); const csv_file = new Blob(['\ufeff', csv], { type: 'text/csv' })
const download_link = document.createElement("a"); const download_link = document.createElement('a')
download_link.download = filename; download_link.download = filename
download_link.href = window.URL.createObjectURL(csv_file); download_link.href = window.URL.createObjectURL(csv_file)
download_link.style.display = "none"; download_link.style.display = 'none'
document.body.appendChild(download_link); document.body.appendChild(download_link)
download_link.click(); download_link.click()
} }
function htmlToCSV(html, filename) { function htmlToCSV(html, filename) {
const data = []; const data = []
const rows = document.querySelectorAll("table tr"); const rows = document.querySelectorAll('table tr')
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
const row = [], cols: any = rows[i].querySelectorAll("td, th"); const row = [],
cols: any = rows[i].querySelectorAll('td, th')
for (let j = 0; j < cols.length; j++) { for (let j = 0; j < cols.length; j++) {
row.push(cols[j].innerText); row.push(cols[j].innerText)
} }
data.push(row.join(";")); data.push(row.join(';'))
} }
downloadCSVFile(data.join("\n"), filename); downloadCSVFile(data.join('\n'), filename)
} }
useEffect(() => { useEffect(() => {
if (unidade!=='' || month!==''){ if (unidade !== '' || month !== '') {
api.post('/operation/summary', month && !unidade? { api
"filters": [ .post(
{"type" : "=", "field": "mes", "value": month} '/operation/summary',
month && !unidade
? {
filters: [{ type: '=', field: 'mes', value: month }]
}
: !month && unidade
? {
filters: [
{
type: '=',
field: 'dados_te.cod_smart_unidade',
value: unidade
}
] ]
} : }
!month && unidade? { : month && unidade
"filters": [ ? {
{"type" : "=", "field": "dados_te.cod_smart_unidade", "value": unidade} filters: [
{ type: '=', field: 'mes', value: month },
{
type: '=',
field: 'dados_te.cod_smart_unidade',
value: unidade
}
] ]
} : }
month && unidade? { : {}
"filters": [ )
{"type" : "=", "field": "mes", "value": month}, .then((res) => {
{"type" : "=", "field": "dados_te.cod_smart_unidade", "value": unidade}
]
} : {}
).then(res => {
setTableDataState(res.data.data) setTableDataState(res.data.data)
}) })
} else { } else {
@ -91,7 +114,7 @@ export default function ResumoOperacao({tableData, clients, userName, clientMont
}, [month, unidade]) }, [month, unidade])
useEffect(() => { useEffect(() => {
window?.addEventListener('scroll', getPageYAfterScroll); window?.addEventListener('scroll', getPageYAfterScroll)
}, []) }, [])
return ( return (
@ -99,15 +122,21 @@ export default function ResumoOperacao({tableData, clients, userName, clientMont
<Head> <Head>
<title>Smart Energia - Resumo de Operação</title> <title>Smart Energia - Resumo de Operação</title>
</Head> </Head>
<Header name={userName}> <Header name={userName}>
<PageTitle title='Resumo de Operações' subtitle='Operações de compra e venda - Mensal' /> <PageTitle
title="Resumo de Operações"
subtitle="Operações de compra e venda - Mensal"
/>
</Header> </Header>
<TableHeader> <TableHeader>
<article id='select'> <article id="select">
<div className='select'> <div className="select">
<div> <div>
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel id="demo-simple-select-labels">Unidades</InputLabel> <InputLabel id="demo-simple-select-labels">Unidades</InputLabel>
<Select <Select
labelId="demo-simple-select-label" labelId="demo-simple-select-label"
id="demo-simple-select" id="demo-simple-select"
@ -116,12 +145,18 @@ export default function ResumoOperacao({tableData, clients, userName, clientMont
onChange={handleChangeUnidade} onChange={handleChangeUnidade}
fullWidth fullWidth
> >
<MenuItem key={1} value={''}>Todas</MenuItem> <MenuItem key={1} value={''}>
{ Todas
clients.map((value) => { </MenuItem>
return <MenuItem key={value.cod_smart_unidade} value={value.cod_smart_unidade}>{value.unidade}</MenuItem>
}) {clients.map((value) => (
} <MenuItem
key={value.cod_smart_unidade}
value={value.cod_smart_unidade}
>
{value.unidade}
</MenuItem>
))}
</Select> </Select>
</FormControl> </FormControl>
</div> </div>
@ -138,68 +173,122 @@ export default function ResumoOperacao({tableData, clients, userName, clientMont
fullWidth fullWidth
> >
<MenuItem value={''}>Todos</MenuItem> <MenuItem value={''}>Todos</MenuItem>
{ {clientMonth
clientMonth.sort((a, b) => { .sort((a, b) => {
if (parseFloat(a.mes.slice(0, 2)) < parseFloat(b.mes.slice(0, 2))) if (
if (parseFloat(a.mes.slice(3, 7)) > parseFloat(b.mes.slice(3, 7))) return -1 parseFloat(a.mes.slice(0, 2)) <
parseFloat(b.mes.slice(0, 2))
)
if (
parseFloat(a.mes.slice(3, 7)) >
parseFloat(b.mes.slice(3, 7))
)
return -1
else return 1 else return 1
if (parseFloat(a.mes.slice(0, 2)) > parseFloat(b.mes.slice(0, 2))) if (
if (parseFloat(a.mes.slice(3, 7)) < parseFloat(b.mes.slice(3, 7))) return 1 parseFloat(a.mes.slice(0, 2)) >
parseFloat(b.mes.slice(0, 2))
)
if (
parseFloat(a.mes.slice(3, 7)) <
parseFloat(b.mes.slice(3, 7))
)
return 1
else return -1 else return -1
return 0 return 0
}).map((value) => {
return <MenuItem key={value.mes} value={value.mes}>{value.mes}</MenuItem>
}) })
} .map((value) => {
return (
<MenuItem key={value.mes} value={value.mes}>
{value.mes}
</MenuItem>
)
})}
</Select> </Select>
</FormControl> </FormControl>
</div> </div>
</div> </div>
</article> </article>
<article> <article>
<BasicButton title='Download' onClick={() => { <BasicButton
const html = document.querySelector("table").outerHTML; title="Download"
htmlToCSV(html, "resumo_operacao.csv"); onClick={() => {
}}/> const html = document.querySelector('table').outerHTML
htmlToCSV(html, 'resumo_operacao.csv')
}}
/>
</article> </article>
</TableHeader> </TableHeader>
<TableBodyView> <TableBodyView>
<table className="tg"> <table className="tg">
<thead> <thead>
<tr> <tr>
<th className='tg-8oo6'>Mês </th> <th className="tg-8oo6">Mês </th>
<th className='tg-8oo6'>Unidade </th> <th className="tg-8oo6">Unidade </th>
<th className='tg-8oo6'>Operação</th> <th className="tg-8oo6">Operação</th>
<th className='tg-8oo6'>Contraparte</th> <th className="tg-8oo6">Contraparte</th>
<th className='tg-8oo6'>Montante (MWh)</th> <th className="tg-8oo6">Montante (MWh)</th>
<th className='tg-8oo6'>Preço(R$/MWh)</th> <th className="tg-8oo6">Preço(R$/MWh)</th>
<th className='tg-8oo6'>Nota Fiscal (R$)</th> <th className="tg-8oo6">Nota Fiscal (R$)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{ {tableDataState?.map((value, index) => {
tableDataState?.map((value, index) => { if (value.mes.slice(4, 7) != '2020')
if (value.mes.slice(4,7) != '2020') return (
return <tr> <tr>
<td key={value.mes} className='tg-gceh'>{value.mes}</td> <td key={value.mes} className="tg-gceh">
<td key={value.cod_smart_unidade} className='tg-gceh'>{value.unidade}</td> {value.mes}
<td key={value.operacao} className='tg-gceh'>{value.nf_c_icms > 0 ? 'Compra' : 'Cessão'}</td> </td>
<td key={value.contraparte} className='tg-gceh'>{value.contraparte}</td>
<td key={value.montante_nf} className='tg-gceh'>{parseFloat(value.montante_nf).toLocaleString('pt-br')}</td> <td key={value.cod_smart_unidade} className="tg-gceh">
<td key={value.preco_nf} className='tg-gceh'>{parseFloat(value.preco_nf).toLocaleString('pt-br',{style: 'currency', currency: 'BRL', minimumFractionDigits: 2})}</td> {value.unidade}
<td key={value.nf_c_icms} className='tg-gceh'>{parseFloat(value.nf_c_icms).toLocaleString('pt-br',{style: 'currency', currency: 'BRL', minimumFractionDigits: 2})}</td> </td>
<td key={value.operacao} className="tg-gceh">
{value.nf_c_icms > 0 ? 'Compra' : 'Cessão'}
</td>
<td key={value.contraparte} className="tg-gceh">
{value.contraparte}
</td>
<td key={value.montante_nf} className="tg-gceh">
{parseFloat(value.montante_nf).toLocaleString('pt-br')}
</td>
<td key={value.preco_nf} className="tg-gceh">
{parseFloat(value.preco_nf).toLocaleString('pt-br', {
style: 'currency',
currency: 'BRL',
minimumFractionDigits: 2
})}
</td>
<td key={value.nf_c_icms} className="tg-gceh">
{parseFloat(value.nf_c_icms).toLocaleString('pt-br', {
style: 'currency',
currency: 'BRL',
minimumFractionDigits: 2
})}
</td>
</tr> </tr>
}) )
} })}
</tbody> </tbody>
</table> </table>
</TableBodyView> </TableBodyView>
{pageYPosition > 300 && <a href="#select" style={{position: 'fixed', right: '50px', bottom: '100px'}}> {pageYPosition > 300 && (
<a
href="#select"
style={{ position: 'fixed', right: '50px', bottom: '100px' }}
>
<Fab aria-label="add"> <Fab aria-label="add">
<NavigationIcon /> <NavigationIcon />
</Fab> </Fab>
</a>} </a>
)}
</TableView> </TableView>
) )
} }
@ -210,38 +299,46 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { ['user-client_id']: client_id } = parseCookies(ctx) const { ['user-client_id']: client_id } = parseCookies(ctx)
const { ['user-name']: userName } = parseCookies(ctx) const { ['user-name']: userName } = parseCookies(ctx)
let tableData = []; let tableData = []
let clientMonth = []; let clientMonth = []
await apiClient.post('/operation/summary', { await apiClient
"filters": [] .post('/operation/summary', {
}).then(res => { filters: []
})
.then((res) => {
tableData = res.data.data tableData = res.data.data
}) })
let clients = []; let clients = []
await apiClient.post('/operation', { await apiClient
"filters": [ .post('/operation', {
{"type" : ">=", "field":"dados_te.mes", "value":1, "interval": "year"} filters: [
{ type: '>=', field: 'dados_te.mes', value: 1, interval: 'year' }
], ],
"fields": ["mes"], fields: ['mes'],
"distinct": true distinct: true
}).then(res => { })
.then((res) => {
clientMonth = res.data.data clientMonth = res.data.data
}) })
await apiClient.post('/units', { await apiClient
"filters": [ .post('/units', {
{"type" : "=", "field": "dados_cadastrais.cod_smart_cliente", "value": client_id}, filters: [
{"type" : "not_in", "field": "dados_cadastrais.codigo_scde", "value":["0P"]} {
type: '=',
field: 'dados_cadastrais.cod_smart_cliente',
value: client_id
},
{ type: 'not_in', field: 'dados_cadastrais.codigo_scde', value: ['0P'] },
{ type: 'order', field: 'dados_cadastrais.cliente', value: ['asc'] },
], ],
"fields": [ fields: ['unidade', 'cod_smart_unidade', 'codigo_scde'],
"unidade", distinct: true
"cod_smart_unidade", })
"codigo_scde"], .then((res) => {
"distinct": true
}).then(res => {
clients = res.data.data clients = res.data.data
}) })

View File

@ -1,25 +1,31 @@
import axios from "axios"; import axios from 'axios'
import * as express from 'express'; import * as express from 'express'
import * as next from 'next'; import * as next from 'next'
import { parseCookies } from "nookies"; import { parseCookies } from 'nookies'
export default function getAPIClient(ctx?: Pick<next.NextPageContext, 'req'> | {
req: next.NextApiRequest;
} | {
req: express.Request;
} | null | undefined) {
export default function getAPIClient(
ctx?:
| Pick<next.NextPageContext, 'req'>
| {
req: next.NextApiRequest
}
| {
req: express.Request
}
| null
| undefined
) {
const { '@smartAuth-token': token } = parseCookies(ctx) const { '@smartAuth-token': token } = parseCookies(ctx)
const api = axios.create({ const api = axios.create({
baseURL: 'https://api.energiasmart.com.br/api' // baseURL: 'https://api.energiasmart.com.br/api'
// baseURL: 'https://api.energiasmart.klupp.com.br/api' // baseURL: 'https://api.energiasmart.klupp.com.br/api'
baseURL: 'http://api-smart.test/api'
}) })
api.interceptors.request.use(config => { api.interceptors.request.use((config) => {
return config; return config
}, })
);
if (token) { if (token) {
api.defaults.headers['Authorization'] = `Bearer ${token}` api.defaults.headers['Authorization'] = `Bearer ${token}`

3
src/utils/formatPrice.ts Normal file
View File

@ -0,0 +1,3 @@
export function formatPrice(value: string | number): string {
return parseFloat(`${value}`).toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
}

18
src/utils/stableSort.ts Normal file
View File

@ -0,0 +1,18 @@
export function stableSort<T = any>(
array: any,
comparator: (a: T, b: T) => number
) {
const stabilizedThis = array.map((el, index) => [el, index] as [T, number])
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0])
if (order !== 0) {
return order
}
return a[1] - b[1]
})
return stabilizedThis.map((el) => el[0])
}

25
src/utils/stringHelper.ts Normal file
View File

@ -0,0 +1,25 @@
export function sanitizeStringSearch(value: string): string {
return value
.trim()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase();
}
export function sortedString<T = any>(field: string = "", optionsSorts: Intl.CollatorOptions = {}) {
const locale = "pt-BR";
optionsSorts = {
...optionsSorts,
sensitivity: optionsSorts?.sensitivity ?? "variant",
};
if (!field) return Intl.Collator(locale, optionsSorts).compare;
return (a: T, b: T): number =>
(a[field as keyof T] as string)?.localeCompare(
(b[field as keyof T] as string) ?? "",
locale,
optionsSorts
);
}