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
JavaScript
// ** 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