UNPKG

react-garden

Version:

React + TypeScript + ThreeJS app using Material UI on NextJS, Apollo Client, GraphQL + WordPress REST APIs, for ThreeD web development.. a part of the threed.ai code family.

408 lines (379 loc) 13.3 kB
// ** React Imports import { Fragment, useState, useEffect, forwardRef } from 'react' // ** Next Import import Link from 'next/link' // ** MUI Imports import Box from '@mui/material/Box' import Grid from '@mui/material/Grid' import Card from '@mui/material/Card' import Menu from '@mui/material/Menu' import Tooltip from '@mui/material/Tooltip' import { styled } from '@mui/material/styles' import MenuItem from '@mui/material/MenuItem' import TextField from '@mui/material/TextField' import CardHeader from '@mui/material/CardHeader' import IconButton from '@mui/material/IconButton' import InputLabel from '@mui/material/InputLabel' import Typography from '@mui/material/Typography' import FormControl from '@mui/material/FormControl' import CardContent from '@mui/material/CardContent' import { DataGrid } from '@mui/x-data-grid' import Select from '@mui/material/Select' // ** Icons Imports import Send from 'mdi-material-ui/Send' import Check from 'mdi-material-ui/Check' import ChartPie from 'mdi-material-ui/ChartPie' import Download from 'mdi-material-ui/Download' import ArrowDown from 'mdi-material-ui/ArrowDown' import EyeOutline from 'mdi-material-ui/EyeOutline' import TrendingUp from 'mdi-material-ui/TrendingUp' import ContentCopy from 'mdi-material-ui/ContentCopy' import DotsVertical from 'mdi-material-ui/DotsVertical' import PencilOutline from 'mdi-material-ui/PencilOutline' import DeleteOutline from 'mdi-material-ui/DeleteOutline' import InformationOutline from 'mdi-material-ui/InformationOutline' import ContentSaveOutline from 'mdi-material-ui/ContentSaveOutline' // ** Third Party Imports import format from 'date-fns/format' import DatePicker from 'react-datepicker' // ** Store & Actions Imports import { useDispatch, useSelector } from 'react-redux' import { fetchData, deleteInvoice } from '~/store/apps/invoice' // ** Utils Import import { getInitials } from '~/@core/utils/get-initials' // ** Custom Components Imports import CustomChip from '~/@core/components/mui/chip' import CustomAvatar from '~/@core/components/mui/avatar' import TableHeader from '~/views/apps/invoice/list/TableHeader' // ** Third Party Styles Imports // import 'react-datepicker/dist/react-datepicker.css' // ** Styled Components import DatePickerWrapper from '~/@core/styles/libs/react-datepicker' // ** Styled component for the link in the dataTable const StyledLink = styled('a')(({ theme }) => ({ textDecoration: 'none', color: theme.palette.primary.main })) const RowOptions = ({ id }) => { // ** State const [anchorEl, setAnchorEl] = useState(null) const rowOptionsOpen = Boolean(anchorEl) const handleRowOptionsClick = event => { setAnchorEl(event.currentTarget) } const handleRowOptionsClose = () => { setAnchorEl(null) } return ( <> <IconButton size='small' onClick={handleRowOptionsClick}> <DotsVertical fontSize='small' /> </IconButton> <Menu keepMounted anchorEl={anchorEl} open={rowOptionsOpen} onClose={handleRowOptionsClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} transformOrigin={{ vertical: 'top', horizontal: 'right' }} > <MenuItem> <Download fontSize='small' sx={{ mr: 2 }} /> Download </MenuItem> <Link href={`/apps/invoice/edit/${id}`} passHref> <MenuItem> <PencilOutline fontSize='small' sx={{ mr: 2 }} /> Edit </MenuItem> </Link> <MenuItem> <ContentCopy fontSize='small' sx={{ mr: 2 }} /> Duplicate </MenuItem> </Menu> </> ) } // ** Vars const invoiceStatusObj = { Sent: { color: 'secondary', icon: <Send sx={{ fontSize: '1rem' }} /> }, Paid: { color: 'success', icon: <Check sx={{ fontSize: '1rem' }} /> }, Draft: { color: 'primary', icon: <ContentSaveOutline sx={{ fontSize: '1rem' }} /> }, 'Partial Payment': { color: 'warning', icon: <ChartPie sx={{ fontSize: '1rem' }} /> }, 'Past Due': { color: 'error', icon: <InformationOutline sx={{ fontSize: '1rem' }} /> }, Downloaded: { color: 'info', icon: <ArrowDown sx={{ fontSize: '1rem' }} /> } } // ** renders client column const renderClient = row => { if (row.avatar.length) { return <CustomAvatar src={row.avatar} sx={{ mr: 3, width: '1.875rem', height: '1.875rem' }} /> } else { return ( <CustomAvatar skin='light' color={row.avatarColor || 'primary'} sx={{ mr: 3, fontSize: '.8rem', width: '1.875rem', height: '1.875rem' }} > {getInitials(row.name || 'Marty McGee')} </CustomAvatar> ) } } const defaultColumns = [ { flex: 0.1, field: 'id', minWidth: 80, headerName: '#', renderCell: ({ row }) => ( <Link href={`/apps/invoice/preview/${row.id}`} passHref> <StyledLink>{`#${row.id}`}</StyledLink> </Link> ) }, { flex: 0.1, minWidth: 80, field: 'invoiceStatus', renderHeader: () => <TrendingUp fontSize='small' />, renderCell: ({ row }) => { const { dueDate, balance, invoiceStatus } = row const color = invoiceStatusObj[invoiceStatus] ? invoiceStatusObj[invoiceStatus].color : 'primary' const Icon = invoiceStatusObj[invoiceStatus] ? invoiceStatusObj[invoiceStatus].icon : null return ( <Tooltip title={ <div> <Typography variant='caption' sx={{ color: 'common.white', fontWeight: 600 }}> {invoiceStatus} </Typography> <br /> <Typography variant='caption' sx={{ color: 'common.white', fontWeight: 600 }}> Balance: </Typography>{' '} {balance} <br /> <Typography variant='caption' sx={{ color: 'common.white', fontWeight: 600 }}> Due Date: </Typography>{' '} {dueDate} </div> } > <CustomAvatar skin='light' color={color} sx={{ width: '1.875rem', height: '1.875rem' }}> {Icon} </CustomAvatar> </Tooltip> ) } }, { flex: 0.25, field: 'name', minWidth: 300, headerName: 'Client', renderCell: ({ row }) => { const { name, companyEmail } = row return ( <Box sx={{ display: 'flex', alignItems: 'center' }}> {renderClient(row)} <Box sx={{ display: 'flex', flexDirection: 'column' }}> <Typography noWrap variant='body2' sx={{ color: 'text.primary', fontWeight: 600 }}> {name} </Typography> <Typography noWrap variant='caption'> {companyEmail} </Typography> </Box> </Box> ) } }, { flex: 0.1, minWidth: 90, field: 'total', headerName: 'Total', renderCell: ({ row }) => <Typography variant='body2'>{`$${row.total || 0}`}</Typography> }, { flex: 0.15, minWidth: 125, field: 'issuedDate', headerName: 'Issued Date', renderCell: ({ row }) => <Typography variant='body2'>{row.issuedDate}</Typography> }, { flex: 0.1, minWidth: 90, field: 'balance', headerName: 'Balance', renderCell: ({ row }) => { return row.balance !== 0 ? ( <Typography variant='body2' sx={{ color: 'text.primary' }}> {row.balance} </Typography> ) : ( <CustomChip size='small' skin='light' color='success' label='Paid' /> ) } } ] /* eslint-disable */ const CustomInput = forwardRef((props, ref) => { const startDate = props.start !== null ? format(props.start, 'MM/dd/yyyy') : '' const endDate = props.end !== null ? ` - ${format(props.end, 'MM/dd/yyyy')}` : null const value = `${startDate}${endDate !== null ? endDate : ''}` props.start === null && props.dates.length && props.setDates ? props.setDates([]) : null const updatedProps = { ...props } delete updatedProps.setDates return <TextField fullWidth inputRef={ref} {...updatedProps} label={props.label || ''} value={value} /> }) /* eslint-enable */ const InvoiceList = () => { // ** State const [dates, setDates] = useState([]) const [value, setValue] = useState('') const [pageSize, setPageSize] = useState(10) const [statusValue, setStatusValue] = useState('') const [endDateRange, setEndDateRange] = useState(null) const [selectedRows, setSelectedRows] = useState([]) const [startDateRange, setStartDateRange] = useState(new Date()) // ** Hooks const dispatch = useDispatch() const store = useSelector(state => state.invoice) useEffect(() => { dispatch( fetchData({ dates, q: value, status: statusValue }) ) }, [dispatch, statusValue, value, dates]) const handleFilter = val => { setValue(val) } const handleStatusValue = e => { setStatusValue(e.target.value) } const handleOnChangeRange = dates => { const [start, end] = dates if (start !== null && end !== null) { setDates(dates) } setStartDateRange(start) setEndDateRange(end) } const columns = [ ...defaultColumns, { flex: 0.1, minWidth: 130, sortable: false, field: 'actions', headerName: 'Actions', renderCell: ({ row }) => ( <Box sx={{ display: 'flex', alignItems: 'center' }}> <Tooltip title='Delete Invoice'> <IconButton size='small' onClick={() => dispatch(deleteInvoice(row.id))}> <DeleteOutline fontSize='small' /> </IconButton> </Tooltip> <Tooltip title='View'> <Box> <Link href={`/apps/invoice/preview/${row.id}`} passHref> <IconButton size='small' component='a' sx={{ textDecoration: 'none' }}> <EyeOutline fontSize='small' /> </IconButton> </Link> </Box> </Tooltip> <RowOptions id={row.id} /> </Box> ) } ] return ( <Grid container spacing={6}> <Grid item xs={12}> <Card> <CardHeader title='Filters' /> <CardContent> <Grid container spacing={6}> <Grid item xs={12} sm={6}> <FormControl fullWidth> <InputLabel id='invoice-status-select'>Invoice Status</InputLabel> <Select fullWidth value={statusValue} sx={{ mr: 4, mb: 2 }} label='Invoice Status' onChange={handleStatusValue} labelId='invoice-status-select' > <MenuItem value=''>none</MenuItem> <MenuItem value='downloaded'>Downloaded</MenuItem> <MenuItem value='draft'>Draft</MenuItem> <MenuItem value='paid'>Paid</MenuItem> <MenuItem value='past due'>Past Due</MenuItem> <MenuItem value='partial payment'>Partial Payment</MenuItem> </Select> </FormControl> </Grid> <Grid item xs={12} sm={6}> <DatePickerWrapper> <DatePicker isClearable selectsRange monthsShown={2} endDate={endDateRange} selected={startDateRange} startDate={startDateRange} shouldCloseOnSelect={false} id='date-range-picker-months' onChange={handleOnChangeRange} customInput={ <CustomInput dates={dates} setDates={setDates} label='Invoice Date' end={endDateRange} start={startDateRange} /> } /> </DatePickerWrapper> </Grid> </Grid> </CardContent> </Card> </Grid> <Grid item xs={12}> <Card> <TableHeader value={value} selectedRows={selectedRows} handleFilter={handleFilter} /> <DataGrid autoHeight pagination rows={store.data} columns={columns} checkboxSelection disableSelectionOnClick pageSize={Number(pageSize)} rowsPerPageOptions={[10, 25, 50]} onSelectionModelChange={rows => setSelectedRows(rows)} onPageSizeChange={newPageSize => setPageSize(newPageSize)} /> </Card> </Grid> </Grid> ) } export default InvoiceList