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.
406 lines (374 loc) • 12.5 kB
JavaScript
// ** React Imports
import { useState, useEffect, useCallback } from 'react'
// ** Next Import
import Link from 'next/link'
// ** MUI Imports
import Box from '@mui/material/Box'
import Card from '@mui/material/Card'
import Menu from '@mui/material/Menu'
import Grid from '@mui/material/Grid'
import { DataGrid } from '@mui/x-data-grid'
import MenuItem from '@mui/material/MenuItem'
import { styled } from '@mui/material/styles'
import IconButton from '@mui/material/IconButton'
import Typography from '@mui/material/Typography'
import CardHeader from '@mui/material/CardHeader'
import InputLabel from '@mui/material/InputLabel'
import FormControl from '@mui/material/FormControl'
import CardContent from '@mui/material/CardContent'
import Select from '@mui/material/Select'
// ** Icons Imports
import Laptop from 'mdi-material-ui/Laptop'
import ChartDonut from 'mdi-material-ui/ChartDonut'
import CogOutline from 'mdi-material-ui/CogOutline'
import EyeOutline from 'mdi-material-ui/EyeOutline'
import DotsVertical from 'mdi-material-ui/DotsVertical'
import PencilOutline from 'mdi-material-ui/PencilOutline'
import DeleteOutline from 'mdi-material-ui/DeleteOutline'
import AccountOutline from 'mdi-material-ui/AccountOutline'
// ** Store Imports
import { useDispatch, useSelector } from 'react-redux'
// ** Custom Components Imports
import CustomChip from '~/@core/components/mui/chip'
import CustomAvatar from '~/@core/components/mui/avatar'
// ** Utils Import
import { getInitials } from '~/@core/utils/get-initials'
// ** Actions Imports
import { fetchData, deleteUser } from '~/store/apps/user'
// ** Custom Components Imports
import TableHeader from '~/views/apps/user/list/TableHeader'
import AddUserDrawer from '~/views/apps/user/list/AddUserDrawer'
// ** Vars
const userRoleObj = {
admin: <Laptop fontSize='small' sx={{ mr: 3, color: 'error.main' }} />,
author: <CogOutline fontSize='small' sx={{ mr: 3, color: 'warning.main' }} />,
editor: <PencilOutline fontSize='small' sx={{ mr: 3, color: 'info.main' }} />,
maintainer: <ChartDonut fontSize='small' sx={{ mr: 3, color: 'success.main' }} />,
subscriber: <AccountOutline fontSize='small' sx={{ mr: 3, color: 'primary.main' }} />
}
const userStatusObj = {
active: 'success',
pending: 'warning',
inactive: 'secondary'
}
// ** Styled component for the link for the avatar with image
const AvatarWithImageLink = styled(Link)(({ theme }) => ({
marginRight: theme.spacing(3)
}))
// ** Styled component for the link for the avatar without image
const AvatarWithoutImageLink = styled(Link)(({ theme }) => ({
textDecoration: 'none',
marginRight: theme.spacing(3)
}))
// ** renders client column
const renderClient = row => {
if (row.avatar.length) {
return (
<AvatarWithImageLink href={`/apps/user/view/${row.id}`}>
<CustomAvatar src={row.avatar} sx={{ mr: 3, width: 30, height: 30 }} />
</AvatarWithImageLink>
)
} else {
return (
<AvatarWithoutImageLink href={`/apps/user/view/${row.id}`}>
<CustomAvatar
skin='light'
color={row.avatarColor || 'primary'}
sx={{ mr: 3, width: 30, height: 30, fontSize: '.875rem' }}
>
{getInitials(row.fullName ? row.fullName : 'Marty McGee')}
</CustomAvatar>
</AvatarWithoutImageLink>
)
}
}
// ** Styled component for the link inside menu
const MenuItemLink = styled('a')(({ theme }) => ({
width: '100%',
display: 'flex',
alignItems: 'center',
textDecoration: 'none',
padding: theme.spacing(1.5, 4),
color: theme.palette.text.primary
}))
const RowOptions = ({ id }) => {
// ** Hooks
const dispatch = useDispatch()
// ** State
const [anchorEl, setAnchorEl] = useState(null)
const rowOptionsOpen = Boolean(anchorEl)
const handleRowOptionsClick = event => {
setAnchorEl(event.currentTarget)
}
const handleRowOptionsClose = () => {
setAnchorEl(null)
}
const handleDelete = () => {
dispatch(deleteUser(id))
handleRowOptionsClose()
}
return (
<>
<IconButton size='small' onClick={handleRowOptionsClick}>
<DotsVertical />
</IconButton>
<Menu
keepMounted
anchorEl={anchorEl}
open={rowOptionsOpen}
onClose={handleRowOptionsClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
PaperProps={{ style: { minWidth: '8rem' } }}
>
<MenuItem sx={{ p: 0 }}>
<Link href={`/apps/user/view/${id}`} passHref>
<MenuItemLink>
<EyeOutline fontSize='small' sx={{ mr: 2 }} />
View
</MenuItemLink>
</Link>
</MenuItem>
<MenuItem onClick={handleRowOptionsClose}>
<PencilOutline fontSize='small' sx={{ mr: 2 }} />
Edit
</MenuItem>
<MenuItem onClick={handleDelete}>
<DeleteOutline fontSize='small' sx={{ mr: 2 }} />
Delete
</MenuItem>
</Menu>
</>
)
}
const columns = [
{
flex: 0.2,
minWidth: 230,
field: 'fullName',
headerName: 'User',
renderCell: ({ row }) => {
const { id, fullName, username } = row
return (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{renderClient(row)}
<Box sx={{ display: 'flex', alignItems: 'flex-start', flexDirection: 'column' }}>
<Link href={`/apps/user/view/${id}`} passHref>
<Typography
noWrap
component='a'
variant='body2'
sx={{ fontWeight: 600, color: 'text.primary', textDecoration: 'none' }}
>
{fullName}
</Typography>
</Link>
<Link href={`/apps/user/view/${id}`} passHref>
<Typography noWrap component='a' variant='caption' sx={{ textDecoration: 'none' }}>
@{username}
</Typography>
</Link>
</Box>
</Box>
)
}
},
{
flex: 0.2,
minWidth: 250,
field: 'email',
headerName: 'Email',
renderCell: ({ row }) => {
return (
<Typography noWrap variant='body2'>
{row.email}
</Typography>
)
}
},
{
flex: 0.15,
field: 'role',
minWidth: 150,
headerName: 'Role',
renderCell: ({ row }) => {
return (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{userRoleObj[row.role]}
<Typography noWrap sx={{ color: 'text.secondary', textTransform: 'capitalize' }}>
{row.role}
</Typography>
</Box>
)
}
},
{
flex: 0.15,
minWidth: 120,
headerName: 'Plan',
field: 'currentPlan',
renderCell: ({ row }) => {
return (
<Typography noWrap sx={{ textTransform: 'capitalize' }}>
{row.currentPlan}
</Typography>
)
}
},
{
flex: 0.1,
minWidth: 110,
field: 'status',
headerName: 'Status',
renderCell: ({ row }) => {
return (
<CustomChip
skin='light'
size='small'
label={row.status}
color={userStatusObj[row.status]}
sx={{ textTransform: 'capitalize' }}
/>
)
}
},
{
flex: 0.1,
minWidth: 90,
sortable: false,
field: 'actions',
headerName: 'Actions',
renderCell: ({ row }) => <RowOptions id={row.id} />
}
]
const UserList = () => {
// ** State
const [role, setRole] = useState('')
const [plan, setPlan] = useState('')
const [value, setValue] = useState('')
const [status, setStatus] = useState('')
const [pageSize, setPageSize] = useState(10)
const [addUserOpen, setAddUserOpen] = useState(false)
// ** Hooks
const dispatch = useDispatch()
const store = useSelector(state => state.user)
useEffect(() => {
dispatch(
fetchData({
role,
status,
q: value,
currentPlan: plan
})
)
}, [dispatch, plan, role, status, value])
const handleFilter = useCallback(val => {
setValue(val)
}, [])
const handleRoleChange = useCallback(e => {
setRole(e.target.value)
}, [])
const handlePlanChange = useCallback(e => {
setPlan(e.target.value)
}, [])
const handleStatusChange = useCallback(e => {
setStatus(e.target.value)
}, [])
const toggleAddUserDrawer = () => setAddUserOpen(!addUserOpen)
return (
<Grid container spacing={6}>
<Grid item xs={12}>
<Card>
<CardHeader title='Search Filters' />
<CardContent>
<Grid container spacing={6}>
<Grid item sm={4} xs={12}>
<FormControl fullWidth>
<InputLabel id='role-select'>Select Role</InputLabel>
<Select
fullWidth
value={role}
id='select-role'
label='Select Role'
labelId='role-select'
onChange={handleRoleChange}
inputProps={{ placeholder: 'Select Role' }}
>
<MenuItem value=''>Select Role</MenuItem>
<MenuItem value='admin'>Admin</MenuItem>
<MenuItem value='author'>Author</MenuItem>
<MenuItem value='editor'>Editor</MenuItem>
<MenuItem value='maintainer'>Maintainer</MenuItem>
<MenuItem value='subscriber'>Subscriber</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item sm={4} xs={12}>
<FormControl fullWidth>
<InputLabel id='plan-select'>Select Plan</InputLabel>
<Select
fullWidth
value={plan}
id='select-plan'
label='Select Plan'
labelId='plan-select'
onChange={handlePlanChange}
inputProps={{ placeholder: 'Select Plan' }}
>
<MenuItem value=''>Select Plan</MenuItem>
<MenuItem value='basic'>Basic</MenuItem>
<MenuItem value='company'>Company</MenuItem>
<MenuItem value='enterprise'>Enterprise</MenuItem>
<MenuItem value='team'>Team</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item sm={4} xs={12}>
<FormControl fullWidth>
<InputLabel id='status-select'>Select Status</InputLabel>
<Select
fullWidth
value={status}
id='select-status'
label='Select Status'
labelId='status-select'
onChange={handleStatusChange}
inputProps={{ placeholder: 'Select Role' }}
>
<MenuItem value=''>Select Role</MenuItem>
<MenuItem value='pending'>Pending</MenuItem>
<MenuItem value='active'>Active</MenuItem>
<MenuItem value='inactive'>Inactive</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
<Grid item xs={12}>
<Card>
<TableHeader value={value} handleFilter={handleFilter} toggle={toggleAddUserDrawer} />
<DataGrid
autoHeight
rows={store.data}
columns={columns}
checkboxSelection
pageSize={pageSize}
disableSelectionOnClick
rowsPerPageOptions={[10, 25, 50]}
onPageSizeChange={newPageSize => setPageSize(newPageSize)}
/>
</Card>
</Grid>
<AddUserDrawer open={addUserOpen} toggle={toggleAddUserDrawer} />
</Grid>
)
}
export default UserList