[WMS-55] User Create and Edit Final
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Grid, Typography } from '@mui/material';
|
||||
import MDBox from 'components/MDBox';
|
||||
@@ -6,8 +6,16 @@ import TransferList from 'components/MDTransferList';
|
||||
import './AllocationManager.component.scss';
|
||||
|
||||
const AllocationManager = props => {
|
||||
const {boxStyleOverride, component, gridStyleOverride, list, md, title, variant, xs} = props;
|
||||
return <Grid item className="c-AllocationManager" xs={xs || 12} md={md || 6} sx={gridStyleOverride}>
|
||||
const {boxStyleOverride, component, gridStyleOverride, initlist, list, matchProp, md, onChange, title, variant, xs} = props;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [allocationStatus, setAllocationStatus] = useState();
|
||||
|
||||
const handleAllocationChange = state => {
|
||||
setAllocationStatus(state);
|
||||
onChange && onChange(state.assigned?.map(obj => obj._id).join(','));
|
||||
};
|
||||
|
||||
return <Grid item className='c-AllocationManager' xs={xs || 12} md={md || 6} sx={gridStyleOverride}>
|
||||
<MDBox
|
||||
sx={boxStyleOverride || ({
|
||||
backgroundColor: '#fff',
|
||||
@@ -20,7 +28,7 @@ const AllocationManager = props => {
|
||||
<Typography gutterBottom variant={variant || 'h6'} component={component || 'div'}>
|
||||
{title}
|
||||
</Typography>
|
||||
<TransferList list={list || []} />
|
||||
<TransferList list={list || []} initlist={initlist} matchProp={matchProp} onChange={handleAllocationChange} />
|
||||
</MDBox>
|
||||
</Grid>;
|
||||
};
|
||||
@@ -29,8 +37,14 @@ AllocationManager.propTypes = {
|
||||
boxStyleOverride: PropTypes.object,
|
||||
component: PropTypes.string,
|
||||
gridStyleOverride: PropTypes.object,
|
||||
initlist: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.array
|
||||
]),
|
||||
list: PropTypes.array,
|
||||
matchProp: PropTypes.object,
|
||||
md: PropTypes.number,
|
||||
onChange: PropTypes.func,
|
||||
styleOverride: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
variant: PropTypes.string,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import List from '@mui/material/List';
|
||||
@@ -11,6 +11,7 @@ import Typography from '@mui/material/Typography';
|
||||
import Divider from '@mui/material/Divider';
|
||||
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import {intersection, not, notBy, intersectionBy} from 'services/Utils';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
boxStyling: {
|
||||
@@ -38,23 +39,25 @@ const useStyles = makeStyles({
|
||||
}
|
||||
});
|
||||
|
||||
function not(a, b) {
|
||||
return a.filter((value) => b.indexOf(value) === -1);
|
||||
}
|
||||
|
||||
function intersection(a, b) {
|
||||
return a.filter((value) => b.indexOf(value) !== -1);
|
||||
}
|
||||
|
||||
export default function TransferList({list}) {
|
||||
export default function TransferList({list, initlist, matchProp, onChange}) {
|
||||
const classes = useStyles();
|
||||
const [checked, setChecked] = React.useState([]);
|
||||
const [left, setLeft] = React.useState(list || []);
|
||||
const [right, setRight] = React.useState([]);
|
||||
const [checked, setChecked] = useState([]);
|
||||
const [left, setLeft] = useState(list || []);
|
||||
const [right, setRight] = useState([]);
|
||||
|
||||
const leftChecked = intersection(checked, left);
|
||||
const rightChecked = intersection(checked, right);
|
||||
|
||||
useEffect(() => {
|
||||
if (initlist) {
|
||||
const initlistClone = typeof initlist === 'object' ? initlist : initlist.split(',');
|
||||
const left = notBy(matchProp, list, initlistClone);
|
||||
const right = intersectionBy(matchProp, list, initlistClone);
|
||||
setLeft(left);
|
||||
setRight(right);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleToggle = (value) => () => {
|
||||
const currentIndex = checked.indexOf(value);
|
||||
const newChecked = [...checked];
|
||||
@@ -69,25 +72,37 @@ export default function TransferList({list}) {
|
||||
};
|
||||
|
||||
const handleAllRight = () => {
|
||||
setRight(right.concat(left));
|
||||
setLeft([]);
|
||||
const rightNew = right.concat(left);
|
||||
const leftNew = [];
|
||||
setRight(rightNew);
|
||||
setLeft(leftNew);
|
||||
onChange({unassigned: leftNew, assigned: rightNew});
|
||||
};
|
||||
|
||||
const handleCheckedRight = () => {
|
||||
setRight(right.concat(leftChecked));
|
||||
setLeft(not(left, leftChecked));
|
||||
const rightNew = right.concat(leftChecked);
|
||||
const leftNew = not(left, leftChecked);
|
||||
setRight(rightNew);
|
||||
setLeft(leftNew);
|
||||
setChecked(not(checked, leftChecked));
|
||||
onChange({unassigned: leftNew, assigned: rightNew});
|
||||
};
|
||||
|
||||
const handleCheckedLeft = () => {
|
||||
setLeft(left.concat(rightChecked));
|
||||
setRight(not(right, rightChecked));
|
||||
const rightNew = not(right, rightChecked);
|
||||
const leftNew = left.concat(rightChecked);
|
||||
setLeft(leftNew);
|
||||
setRight(rightNew);
|
||||
setChecked(not(checked, rightChecked));
|
||||
onChange({unassigned: leftNew, assigned: rightNew});
|
||||
};
|
||||
|
||||
const handleAllLeft = () => {
|
||||
setLeft(left.concat(right));
|
||||
setRight([]);
|
||||
const rightNew = [];
|
||||
const leftNew = left.concat(right);
|
||||
setLeft(leftNew);
|
||||
setRight(rightNew);
|
||||
onChange({unassigned: leftNew, assigned: rightNew});
|
||||
};
|
||||
|
||||
const customList = items => (
|
||||
@@ -186,5 +201,11 @@ export default function TransferList({list}) {
|
||||
}
|
||||
|
||||
TransferList.propTypes = {
|
||||
list: PropTypes.array
|
||||
initlist: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.array
|
||||
]),
|
||||
list: PropTypes.array,
|
||||
matchProp: PropTypes.object,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { Box, Grid } from '@mui/material';
|
||||
@@ -21,14 +21,31 @@ const useStyles = makeStyles(() => ({
|
||||
}));
|
||||
|
||||
const Toggles = props => {
|
||||
const {boxSx, md, title, toggles, typoComponent, typoSx, typoVariant, xs} = props;
|
||||
const {boxSx, inittoggles, md, onChange, title, toggles, typoComponent, typoSx, typoVariant, xs} = props;
|
||||
const [toggleState, updateToggleState] = useState({});
|
||||
const classes = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
if (inittoggles && typeof inittoggles === 'string') {
|
||||
const initToggleState = {};
|
||||
inittoggles.split(',').forEach(iToggle => initToggleState[iToggle] = true);
|
||||
updateToggleState(initToggleState);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleToggle = (e, toggle) => {
|
||||
const toggleStateClone = {...toggleState, [toggle]: e.target.checked};
|
||||
updateToggleState(toggleStateClone);
|
||||
onChange && onChange(Object.keys(toggleStateClone).join(','));
|
||||
};
|
||||
|
||||
const switchRenders = toggles => toggles && toggles.map((toggle, key) => {
|
||||
toggle = typeof toggle === 'string' ? toggle : toggle.name;
|
||||
const id = toggle + '-' + key;
|
||||
return <MDBox key={key} display='flex' justifyContent='space-between' alignItems='center' lineHeight={1} className={classes.switchSpacer}
|
||||
sx={{ marginBottom: '20px !important' }}>
|
||||
<MDTypography variant='body2'>{toggle}</MDTypography>
|
||||
<Switch checked />
|
||||
<Switch id={id} checked={toggleState[toggle] === undefined ? false : toggleState[toggle]} onChange={e => handleToggle(e, toggle)} />
|
||||
</MDBox>;
|
||||
});
|
||||
|
||||
@@ -36,7 +53,7 @@ const Toggles = props => {
|
||||
<MDBox sx={boxSx || {backgroundColor: '#fff', border: '1px solid #c2c2c2', borderTop: '7px solid #007aff', borderRadius: '4px'}}>
|
||||
<Typography gutterBottom variant={typoVariant || 'h6'} component={typoComponent || 'div'}
|
||||
sx={typoSx || {borderBottom: '1px solid #c2c2c2', padding: '10px 20px', marginBottom: '20px'}}>
|
||||
{title || "Title"}
|
||||
{title || 'Title'}
|
||||
</Typography>
|
||||
<Box sx={{ padding: ' 0px 20px' }}>{toggles && switchRenders(toggles)}</Box>
|
||||
</MDBox>
|
||||
@@ -45,7 +62,9 @@ const Toggles = props => {
|
||||
|
||||
Toggles.propTypes = {
|
||||
boxSx: PropTypes.object,
|
||||
inittoggles: PropTypes.string,
|
||||
md: PropTypes.number,
|
||||
onChange: PropTypes.func,
|
||||
title: PropTypes.string,
|
||||
toggles: PropTypes.array,
|
||||
typoComponent: PropTypes.string,
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
export default {
|
||||
LOGIN_USER: '/user/login',
|
||||
CREATE_USER: '/user/create',
|
||||
UPDATE_USER: '/user/:id',
|
||||
GET_USERS_DATA: '/user/all?page=0&perPage=20',
|
||||
GET_ROLES_DATA: '/user-role/all?page=0&perPage=10',
|
||||
GET_PERMISSIONS_DATA: '/user-permission/all?page=0&perPage=10',
|
||||
GET_ACTIONS_DATA: '/user-permission/actions/all?page=0&perPage=10',
|
||||
CREATE_WAREHOUSE: '/warehouse/',
|
||||
GET_WAREHOUSE_DATA: '/warehouse/all?page=0&perPage=50',
|
||||
LOGIN_USER: '/user/login',
|
||||
GET_CHILDREN_FROM_PARENT: '/dashboard/get-children-from-parent',
|
||||
LOCATION_DELETE: '/dashboard/delete-location',
|
||||
ADD_NEW_ZONE: '/zone',
|
||||
@@ -17,7 +20,6 @@ export default {
|
||||
ADD_INVENTORY: '/inventory',
|
||||
GET_INVENTORY: '/inventory/all?page=0&perPage=50',
|
||||
GET_INVENTORY_TYPES: '/inventory/types',
|
||||
LOCATION_DELETE: '/dashboard/delete-location',
|
||||
GET_WIDGET_FAMILY_BY_INVENTORY: '/widget-family/search-by-inventory?inventory=',
|
||||
ADD_WIDGET_FAMILY: '/widget-family',
|
||||
EDIT_WIDGET_FAMILY: '/widget-family/',
|
||||
|
||||
@@ -13,6 +13,7 @@ import Select from '@mui/material/Select';
|
||||
import WarehouseActions, { WarehouseSelectors } from 'redux/WarehouseRedux';
|
||||
import InventoryActions, { InventorySelectors } from 'redux/InventoryRedux';
|
||||
import RolesActions, { RolesSelectors } from 'redux/RolesRedux';
|
||||
import PermissionsActions, { PermissionsSelectors } from 'redux/PermissionsRedux';
|
||||
import { AuthSelectors } from 'redux/AuthRedux';
|
||||
import UsersActions from 'redux/UsersRedux';
|
||||
|
||||
@@ -70,49 +71,12 @@ function CreateEditUser(props) {
|
||||
const roles = useSelector(RolesSelectors.getRolesDetail);
|
||||
const warehouses = useSelector(WarehouseSelectors.getWarehouseDetail);
|
||||
const inventories = useSelector(InventorySelectors.getInventoryDetail);
|
||||
const actions = useSelector(PermissionsSelectors.getActionsDetail);
|
||||
const permissions = useSelector(PermissionsSelectors.getPermissionsDetail);
|
||||
const currentUser = useSelector(AuthSelectors.getUser);
|
||||
const location = useLocation();
|
||||
const [editedUser, setEditedUser] = useState();
|
||||
const [userRoles, setUserRoles] = useState([]);
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
fullName: editedUser ? editedUser.fullName : '',
|
||||
phoneNumber: editedUser ? editedUser.phoneNumber : '',
|
||||
roles: userRoles,
|
||||
permissions: {},
|
||||
isActive: editedUser && editedUser.isActive !== undefined ? editedUser.isActive : true,
|
||||
createdBy: context === 'new' ? currentUser ? currentUser.fullName : '' : editedUser ? editedUser.createdBy?.fullName : '',
|
||||
createdOn: new Date(),
|
||||
updatedBy: context === 'new' ? currentUser ? currentUser.fullName : '' : editedUser ? editedUser.updatedBy?.fullName : '',
|
||||
updatedOn: new Date()
|
||||
},
|
||||
validationSchema: schema.createUser,
|
||||
onSubmit: (values, { setSubmitting }) =>
|
||||
{
|
||||
const onValidationFailed = () => {
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
// const onSuccessfulSubmission = data => {
|
||||
const onSuccessfulSubmission = () => {
|
||||
navigate('/setup/users-access');
|
||||
// TODO
|
||||
// dispatchAlert
|
||||
};
|
||||
values.roles = values.roles && values.roles.length > 0 ? values.roles.map(role => role._id) : [];
|
||||
dispatch(
|
||||
UsersActions.createUserAction({
|
||||
loader: 'loading-request',
|
||||
slug: API.CREATE_USER,
|
||||
method: 'post',
|
||||
data: values,
|
||||
onValidationFailed,
|
||||
onSuccessfulSubmission
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
const [editedUser, setEditedUser] = useState(location?.state?.user);
|
||||
const [selectedRoles, setSelectedRoles] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (context === 'edit') {
|
||||
@@ -121,26 +85,100 @@ function CreateEditUser(props) {
|
||||
navigate('/setup/users-access');
|
||||
} else {
|
||||
setEditedUser(editedUser);
|
||||
setUserRoles(editedUser.role_name ? editedUser.role_name.split(',') : []);
|
||||
setSelectedRoles(editedUser.roles);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(WarehouseActions.warehouseDataAction({loader: 'loading-request', slug: API.GET_WAREHOUSE_DATA,method: 'get'}));
|
||||
dispatch(InventoryActions.getInventoryAction({loader: 'loading-request', slug: API.GET_INVENTORY,method: 'get'}));
|
||||
!roles || roles.length === 0 && dispatch(RolesActions.getRolesAction({loader: 'loading-request', slug: API.GET_ROLES_DATA, method: 'get'}));
|
||||
dispatch(RolesActions.getRolesAction({loader: 'loading-request', slug: API.GET_ROLES_DATA, method: 'get'}));
|
||||
dispatch(PermissionActions.getRolesAction({loader: 'loading-request', slug: API.GET_ROLES_DATA, method: 'get'}));
|
||||
fetchPermissions();
|
||||
fetchActions();
|
||||
(!warehouses || warehouses.length === 0) && dispatch(WarehouseActions.warehouseDataAction({loader: 'loading-request', slug: API.GET_WAREHOUSE_DATA,method: 'get'}));
|
||||
(!inventories || inventories.length === 0) && dispatch(InventoryActions.getInventoryAction({loader: 'loading-request', slug: API.GET_INVENTORY,method: 'get'}));
|
||||
(!roles || roles.length === 0) && dispatch(RolesActions.getRolesAction({loader: 'loading-request', slug: API.GET_ROLES_DATA, method: 'get'}));
|
||||
(!permissions || permissions.length === 0) && dispatch(PermissionsActions.getPermissionsAction({loader: 'loading-request', slug: API.GET_PERMISSIONS_DATA, method: 'get'}));
|
||||
(!actions || actions.length === 0) && dispatch(PermissionsActions.getActionsAction({loader: 'loading-request', slug: API.GET_ACTIONS_DATA, method: 'get'}));
|
||||
}, []);
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: context === 'new' ? {
|
||||
fullName: '',
|
||||
phoneNumber: '',
|
||||
email: '',
|
||||
password: '',
|
||||
roles: '',
|
||||
warehouses: '',
|
||||
inventories: '',
|
||||
actions: '',
|
||||
visibilities: '',
|
||||
isActive: true,
|
||||
createdBy: currentUser ? currentUser.fullName : '',
|
||||
createdAt: new Date(),
|
||||
updatedBy: currentUser ? currentUser.fullName : '',
|
||||
updatedAt: new Date()
|
||||
} : {
|
||||
fullName: editedUser ? editedUser.fullName : '',
|
||||
phoneNumber: editedUser ? editedUser.phoneNumber : '',
|
||||
email: editedUser ? editedUser.email : '',
|
||||
password: '',
|
||||
roles: editedUser ? editedUser.roles.map(role => role.name).join(', ') : '',
|
||||
warehouses: editedUser?.permissions?.warehouseScopes ? editedUser.permissions.warehouseScopes.map(sc => sc.id).join(',') : '',
|
||||
inventories: editedUser?.permissions?.inventoryScopes ? editedUser.permissions.inventoryScopes.map(sc => sc.id).join(',') : '',
|
||||
actions: editedUser?.permissions?.actions ? editedUser.permissions.actions.join(',') : '',
|
||||
visibilities: editedUser?.permissions?.allowedUIModules ? editedUser.permissions.allowedUIModules.join(',') : '',
|
||||
isActive: editedUser && editedUser.isActive !== undefined ? editedUser.isActive : true,
|
||||
createdBy: editedUser ? editedUser.createdBy?.fullName : '',
|
||||
createdAt: editedUser ? editedUser.createdAt : '',
|
||||
updatedBy: editedUser ? editedUser.updatedBy?.fullName : '',
|
||||
updatedAt: editedUser ? editedUser.updatedAt : ''
|
||||
},
|
||||
validationSchema: schema.createUser,
|
||||
onSubmit: (values, { setSubmitting }) =>
|
||||
{
|
||||
const onValidationFailed = () => {
|
||||
setSubmitting(false);
|
||||
};
|
||||
const onSuccessfulSubmission = () => {
|
||||
navigate('/setup/users-access');
|
||||
};
|
||||
const adaptPayload = values => {
|
||||
const valuesClone = {...values};
|
||||
valuesClone.permissions = {};
|
||||
valuesClone.permissions.inventoryScopes = values.inventories ? values.inventories.split(',').map(inv => ({id: inv, type: 'Inventory'})) : [];
|
||||
valuesClone.permissions.warehouseScopes = values.warehouses ? values.warehouses.split(',').map(wh => ({id: wh, type: 'Warehouse'})) : [];
|
||||
valuesClone.permissions.actions = values.actions ? values.actions.split(',') : [];
|
||||
valuesClone.permissions.allowedUIModules = values.visibilities ? values.visibilities.split(',') : [];
|
||||
delete valuesClone.inventories;
|
||||
delete valuesClone.warehouses;
|
||||
delete valuesClone.actions;
|
||||
delete valuesClone.visibilities;
|
||||
return valuesClone;
|
||||
};
|
||||
values.roles = selectedRoles && selectedRoles.length > 0 ? selectedRoles.map(role => role._id) : [];
|
||||
dispatch(
|
||||
UsersActions.createUserAction({
|
||||
loader: 'loading-request',
|
||||
slug: context === 'edit' ? API.UPDATE_USER.replace(':id', editedUser._id): API.CREATE_USER,
|
||||
method: 'post',
|
||||
data: adaptPayload(values),
|
||||
onValidationFailed,
|
||||
onSuccessfulSubmission,
|
||||
toastMessage: context === 'edit' ? 'Updated user __placeholder__successfully' : 'Added user __placeholder__successfully'
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const handleChange = event => {
|
||||
const value = typeof event.target.value === 'string' ? event.target.value?.split(',') : event.target.value;
|
||||
setUserRoles(value);
|
||||
formik.handleChange(event);
|
||||
const handleMultiSelectChange = e => {
|
||||
const uniqueRoles = [];
|
||||
e.target.value.forEach(role => {
|
||||
const roleIndex = uniqueRoles.findIndex(uRole => uRole._id === role._id);
|
||||
if (roleIndex > -1) {
|
||||
uniqueRoles.splice(roleIndex, 1);
|
||||
} else {
|
||||
uniqueRoles.push(role);
|
||||
}
|
||||
});
|
||||
formik.handleChange('roles')(uniqueRoles.map(role => role.name).join());
|
||||
setSelectedRoles(uniqueRoles);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -150,29 +188,45 @@ function CreateEditUser(props) {
|
||||
<MDBox mx={4} sx={{ border: '1px solid #C4C4C4', borderRadius: '4px', padding: '30px' }}>
|
||||
<MDBox sx={{ width: '50%', margin: 'auto' }}>
|
||||
<MDBox sx={{ width: '120px', margin: 'auto', position: 'relative' }}>
|
||||
<img src={UserIcon} alt="img" />
|
||||
<img src={UserIcon} alt='img' />
|
||||
<MDBox sx={{ position: 'absolute', bottom: '0', right: '0', cursor: 'pointer' }}>
|
||||
<img src={EditIcon} alt="img" />
|
||||
<img src={EditIcon} alt='img' />
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
<MDBox sx={{ marginBottom: '24px' }}>
|
||||
<Box component="div" sx={{}} className={classes.labelSize}>
|
||||
<Box component='div' sx={{}} className={classes.labelSize}>
|
||||
User Name
|
||||
</Box>
|
||||
<MDInput fullWidth disabled={context === 'edit'} value={formik.values.fullName} name="fullName" type="text"
|
||||
variant="outlined" error={formik.touched.fullName && Boolean(formik.errors.fullName)}
|
||||
<MDInput fullWidth value={formik.values.fullName} name='fullName' type='text'
|
||||
variant='outlined' error={formik.touched.fullName && Boolean(formik.errors.fullName)}
|
||||
helperText={formik.touched.fullName && formik.errors.fullName} onChange={formik.handleChange} />
|
||||
</MDBox>
|
||||
<MDBox sx={{ marginBottom: '24px' }}>
|
||||
<Box component="div" sx={{}} className={classes.labelSize}>
|
||||
<Box component='div' sx={{}} className={classes.labelSize}>
|
||||
Email
|
||||
</Box>
|
||||
<MDInput fullWidth disabled={context === 'edit'} value={formik.values.email} name='email' type='email'
|
||||
variant='outlined' error={formik.touched.email && Boolean(formik.errors.email)}
|
||||
helperText={formik.touched.email && formik.errors.email} onChange={formik.handleChange} />
|
||||
</MDBox>
|
||||
<MDBox sx={{ marginBottom: '24px' }}>
|
||||
<Box component='div' sx={{}} className={classes.labelSize}>
|
||||
Password
|
||||
</Box>
|
||||
<MDInput fullWidth value={formik.values.password} name='password' type='password'
|
||||
variant='outlined' error={formik.touched.password && Boolean(formik.errors.password)}
|
||||
helperText={formik.touched.password && formik.errors.password} onChange={formik.handleChange} />
|
||||
</MDBox>
|
||||
<MDBox sx={{ marginBottom: '24px' }}>
|
||||
<Box component='div' sx={{}} className={classes.labelSize}>
|
||||
Phone Number
|
||||
</Box>
|
||||
<MDInput fullWidth value={formik.values.phoneNumber} name="phoneNumber" type="text"
|
||||
variant="outlined" error={formik.touched.phoneNumber && Boolean(formik.errors.phoneNumber)}
|
||||
<MDInput fullWidth value={formik.values.phoneNumber} name='phoneNumber' type='text'
|
||||
variant='outlined' error={formik.touched.phoneNumber && Boolean(formik.errors.phoneNumber)}
|
||||
helperText={formik.touched.phoneNumber && formik.errors.phoneNumber} onChange={formik.handleChange} />
|
||||
</MDBox>
|
||||
<MDBox sx={{ marginBottom: '24px' }}>
|
||||
<Box component="div" sx={{}} className={classes.labelSize}>
|
||||
<Box component='div' sx={{}} className={classes.labelSize}>
|
||||
Role
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', columnGap: '20px' }}>
|
||||
@@ -181,21 +235,13 @@ function CreateEditUser(props) {
|
||||
multiple
|
||||
displayEmpty
|
||||
name='roles'
|
||||
value={userRoles}
|
||||
value={selectedRoles}
|
||||
input={<OutlinedInput />}
|
||||
error={formik.touched.roles && Boolean(formik.errors.roles)}
|
||||
renderValue={() => {
|
||||
if (userRoles.length === 0) {
|
||||
return 'Please select a role';
|
||||
}
|
||||
|
||||
return userRoles.map(role => role.name).join(',');
|
||||
}}
|
||||
renderValue={() => selectedRoles?.length === 0 ? 'Please select a role' : selectedRoles?.map(role => role.name).join(', ')}
|
||||
inputProps={{ 'aria-label': 'Without label' }}
|
||||
sx={{
|
||||
width: '100%'
|
||||
}}
|
||||
onChange={handleChange}
|
||||
sx={{width: '100%'}}
|
||||
onChange={handleMultiSelectChange}
|
||||
>
|
||||
{roles && roles.map((role, key) => <MenuItem key={key} value={role}>{role.name}</MenuItem>)}
|
||||
</Select>
|
||||
@@ -206,12 +252,13 @@ function CreateEditUser(props) {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '30%',
|
||||
paddingRight: '1rem',
|
||||
border: '1px solid #C4C4C4',
|
||||
borderRadius: '4px'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="div"
|
||||
component='div'
|
||||
sx={{
|
||||
fontSize: '16px',
|
||||
lineHeight: '20px',
|
||||
@@ -231,7 +278,7 @@ function CreateEditUser(props) {
|
||||
left: '20px'
|
||||
}}
|
||||
>
|
||||
<Switch checked={formik.values.isActive} />
|
||||
<Switch name='isActive' checked={formik.values.isActive} onChange={formik.handleChange} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -240,28 +287,28 @@ function CreateEditUser(props) {
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2} className={classes.margin}>
|
||||
<Grid item xs={6}>
|
||||
<Box component="div" className={classes.labelSize}>
|
||||
<Box component='div' className={classes.labelSize}>
|
||||
Created By
|
||||
</Box>
|
||||
<MDInput fullWidth disabled name="warehousename" type="text" value={currentUser ? currentUser.fullName : ''} variant="outlined" />
|
||||
<MDInput fullWidth disabled name='createdBy' type='text' value={formik.values.createdBy} variant='outlined' />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Box component="div" className={classes.labelSize}>
|
||||
<Box component='div' className={classes.labelSize}>
|
||||
Date & Time
|
||||
</Box>
|
||||
<DateTimeInput disabled />
|
||||
<DateTimeInput disabled name='createdAt' value={formik.values.createdAt} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Box component="div" className={classes.labelSize}>
|
||||
<Box component='div' className={classes.labelSize}>
|
||||
Last Updated by
|
||||
</Box>
|
||||
<MDInput fullWidth disabled name="warehousename" type="text" value={currentUser ? currentUser.fullName : ''} variant="outlined" />
|
||||
<MDInput fullWidth disabled name='updatedBy' type='text' value={formik.values.updatedBy} variant='outlined' />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Box component="div" className={classes.labelSize}>
|
||||
<Box component='div' className={classes.labelSize}>
|
||||
Date & Time
|
||||
</Box>
|
||||
<DateTimeInput disabled />
|
||||
<DateTimeInput disabled name='updatedAt' value={formik.values.updatedAt} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -269,30 +316,33 @@ function CreateEditUser(props) {
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
<Grid container spacing={4} sx={{ marginTop: '-6px' }}>
|
||||
<AllocationManager gridStyleOverride={{ paddingLeft: '4rem !important' }} list={warehouses} title='Warehouse' />
|
||||
<AllocationManager gridStyleOverride={{ paddingRight: '2rem' }} list={inventories} title='Inventory' />
|
||||
<AllocationManager name='warehouses' gridStyleOverride={{ paddingLeft: '4rem !important' }} initlist={formik.values.warehouses}
|
||||
list={warehouses} matchProp={{a: '_id'}} title='Warehouse' onChange={val => formik.handleChange('warehouses')(val)} />
|
||||
<AllocationManager name='inventories' gridStyleOverride={{ paddingRight: '2rem' }} initlist={formik.values.inventories}
|
||||
list={inventories} matchProp={{a: '_id'}} title='Inventory' onChange={val => formik.handleChange('inventories')(val)} />
|
||||
</Grid>
|
||||
<Grid container spacing={2} sx={{ marginTop: '12px', paddingLeft: '2rem' }}>
|
||||
<Toggles title='Actions' />
|
||||
<Toggles title='Application' />
|
||||
<Toggles name='actions' title='Actions' toggles={actions} inittoggles={formik.values.actions} onChange={val => formik.handleChange('actions')(val)} />
|
||||
<Toggles name='visibilities' title='Application' toggles={permissions} inittoggles={formik.values.visibilities} onChange={val => formik.handleChange('visibilities')(val)} />
|
||||
</Grid>
|
||||
<MDBox
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
display='flex'
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
lineHeight={1}
|
||||
sx={{ marginBottom: '15px', marginTop: '45px', paddingBottom: '30px' }}
|
||||
>
|
||||
<MDButton
|
||||
size="medium"
|
||||
color="error"
|
||||
variant="outlined"
|
||||
type="button"
|
||||
size='medium'
|
||||
color='error'
|
||||
variant='outlined'
|
||||
type='button'
|
||||
sx={{ marginRight: '15px' }}
|
||||
onClick={() => navigate('/setup/users-access')}
|
||||
>
|
||||
Cancel
|
||||
</MDButton>
|
||||
<MDButton size="medium" color="primary" variant="contained" type="submit">
|
||||
<MDButton size='medium' color='primary' variant='contained' type='submit'>
|
||||
{context === 'new' ? 'Create' : 'Save'}
|
||||
</MDButton>
|
||||
</MDBox>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Tabs, Tab } from '@mui/material';
|
||||
import TabPanel from 'components/Tabs';
|
||||
import UsersActions, { UsersSelectors } from 'redux/UsersRedux';
|
||||
import RolesActions, { RolesSelectors } from 'redux/RolesRedux';
|
||||
import { AuthSelectors } from 'redux/AuthRedux';
|
||||
import { API } from 'constant';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import moment from 'moment';
|
||||
@@ -22,9 +23,8 @@ import Breadcrumbs from 'components/Breadcrumbs';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
iconSize: {
|
||||
width: '2%',
|
||||
height: '2%',
|
||||
marginBottom: '10px',
|
||||
width: '1.5rem',
|
||||
height: '1.5rem',
|
||||
color: theme.palette.primary.light,
|
||||
marginRight: '8px'
|
||||
},
|
||||
@@ -83,6 +83,7 @@ function UserAccessScreen() {
|
||||
const [value, setValue] = useState(0);
|
||||
const usersData = useSelector(UsersSelectors.getUsersDetail);
|
||||
const rolesData = useSelector(RolesSelectors.getRolesDetail);
|
||||
const currentUser = useSelector(AuthSelectors.getUser);
|
||||
const [userRecords, setUserRecords] = useState([]);
|
||||
const [rolesRecords, setRoleRecords] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
@@ -91,12 +92,12 @@ function UserAccessScreen() {
|
||||
{ id: 'full_name', label: 'User Name', isEditAnchor: true, value: record => record.fullName },
|
||||
{ id: 'phone_number', label: 'Phone Number', value: record => record.phoneNumber },
|
||||
{ id: 'role_name', label: 'Roles', value: record => record.role_name },
|
||||
{ id: 'updated_by_at', label: 'Last Updated By & Date', value: record => `${record.updatedBy?.fullName} | ${moment(record.updatedAt).format('D/M/YYYY h:m:s A')}` },
|
||||
{ id: 'created_by_at', label: 'Created By & Date', value: record => `${record.createdBy?.fullName} | ${moment(record.createdAt).format('D/M/YYYY h:m:s A')}` },
|
||||
{ id: 'updated_by_at', label: 'Last Updated By & Date', value: record => `${record.updatedBy?.fullName ? record.updatedBy.fullName + ' | ': ''}${moment(record.updatedAt).format('D/M/YYYY h:m:s A')}` },
|
||||
{ id: 'created_by_at', label: 'Created By & Date', value: record => `${record.createdBy?.fullName ? record.createdBy.fullName + ' | ': ''}${moment(record.createdAt).format('D/M/YYYY h:m:s A')}` },
|
||||
{ id: 'last_login', label: 'Last Login', value: record => record.lastLogin },
|
||||
{
|
||||
id: 'is_active', label: 'Access', value: record => record.isActive ? <span className={classes.statusActive}>Active</span>
|
||||
: <span className={status.Inactive}>Inactive</span>
|
||||
: <span className={classes.statusInactive}>Inactive</span>
|
||||
}
|
||||
];
|
||||
|
||||
@@ -164,13 +165,14 @@ function UserAccessScreen() {
|
||||
}
|
||||
}));
|
||||
|
||||
const columnRenders = userRecords && userRecords.map(record => {
|
||||
const rowRenders = userRecords && userRecords.map(record => {
|
||||
const canEdit = columnConfig => columnConfig.isEditAnchor && currentUser.email !== record.email;
|
||||
return <StyledTableRow key={record.id}>
|
||||
{userHeadCells.map((columnConfig, key) => <TableCell key={key} onClick={() => columnConfig.isEditAnchor && navigate('/setup/users-access/edit-user', {state: {user: record}})}>
|
||||
{columnConfig.isEditAnchor && <span className={classes.iconwrap}>
|
||||
{userHeadCells.map((columnConfig, key) => <TableCell key={key} onClick={() => canEdit(columnConfig) && navigate('/setup/users-access/edit-user', {state: {user: record}})}>
|
||||
{canEdit(columnConfig) ? <span className={classes.iconwrap}>
|
||||
<EditIcon className={classes.iconSize}/>
|
||||
</span>}
|
||||
{columnConfig.value(record)}
|
||||
{columnConfig.value(record)}
|
||||
</span> : <span>{columnConfig.value(record)}</span>}
|
||||
</TableCell>)}
|
||||
</StyledTableRow>;
|
||||
});
|
||||
@@ -241,7 +243,7 @@ function UserAccessScreen() {
|
||||
>
|
||||
{userRecords && userRecords.length > 0
|
||||
? <TableBody>
|
||||
{columnRenders}
|
||||
{rowRenders}
|
||||
</TableBody> : 'No Records to Display'}
|
||||
</BasicTable>
|
||||
</TabPanel>
|
||||
|
||||
84
src/redux/PermissionsRedux.js
Normal file
84
src/redux/PermissionsRedux.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import { createActions, createReducer } from 'reduxsauce';
|
||||
import Immutable from 'seamless-immutable';
|
||||
import _ from 'underscore';
|
||||
import { getFetchingValue, getErrorValue } from '../services/Utils';
|
||||
|
||||
/* ------------- Types and Action Creators ------------- */
|
||||
const { Types, Creators } = createActions({
|
||||
getPermissionsAction: ['payload'],
|
||||
getPermissionsSuccess: ['data'],
|
||||
getPermissionsFailure: ['error'],
|
||||
|
||||
getActionsAction: ['payload'],
|
||||
getActionsSuccess: ['data'],
|
||||
getActionsFailure: ['error']
|
||||
});
|
||||
|
||||
export const PermissionsTypes = Types;
|
||||
const PermissionsActions = Creators;
|
||||
export default PermissionsActions;
|
||||
|
||||
/* ------------- Initial State ------------- */
|
||||
export const INITIAL_STATE = Immutable({
|
||||
permissionsDetail: [],
|
||||
permissionsLoading: false,
|
||||
permissionserror: {},
|
||||
actionsDetail: [],
|
||||
actionsLoading: false,
|
||||
actionsError: {}
|
||||
});
|
||||
|
||||
/* ------------- Selectors ------------- */
|
||||
export const PermissionsSelectors = {
|
||||
getPermissionsDetail: (state) => state.permissions.permissionsDetail,
|
||||
getActionsDetail: (state) => state.permissions.actionsDetail
|
||||
};
|
||||
|
||||
/* ------------- Reducers ------------- */
|
||||
export const onGetPermissionsAction = (state, { payload }) =>
|
||||
state.merge({
|
||||
fetching: _.uniq([state.fetching, payload?.loader]),
|
||||
error: getErrorValue(state?.error, payload?.loader)
|
||||
});
|
||||
|
||||
export const onGetPermissionsSuccess = (state, { data }) =>
|
||||
state.merge({
|
||||
fetching: getFetchingValue(state.fetching, data?.loader),
|
||||
error: getErrorValue(state?.error, data?.loader),
|
||||
permissionsDetail: data.permissionsDetail
|
||||
});
|
||||
|
||||
export const onGetPermissionsFailure = (state, { error }) =>
|
||||
state.merge({
|
||||
fetching: _.without(state.fetching, error?.loader),
|
||||
error: { ...state.error, [error?.loader]: error?.error }
|
||||
});
|
||||
|
||||
export const onGetActionsFailure = (state, { error }) =>
|
||||
state.merge({
|
||||
fetching: _.without(state.fetching, error?.loader),
|
||||
error: { ...state.error, [error?.loader]: error?.error }
|
||||
});
|
||||
|
||||
export const onGetActionsAction = (state, { payload }) =>
|
||||
state.merge({
|
||||
fetching: _.uniq([state.fetching, payload?.loader]),
|
||||
error: getErrorValue(state?.error, payload?.loader)
|
||||
});
|
||||
|
||||
export const onGetActionsSuccess = (state, { data }) =>
|
||||
state.merge({
|
||||
fetching: getFetchingValue(state.fetching, data?.loader),
|
||||
error: getErrorValue(state?.error, data?.loader),
|
||||
actionsDetail: data.actionsDetail
|
||||
});
|
||||
|
||||
/* ------------- Hookup Reducers To Types ------------- */
|
||||
export const permissionsReducer = createReducer(INITIAL_STATE, {
|
||||
[Types.GET_PERMISSIONS_ACTION]: onGetPermissionsAction,
|
||||
[Types.GET_PERMISSIONS_SUCCESS]: onGetPermissionsSuccess,
|
||||
[Types.GET_PERMISSIONS_FAILURE]: onGetPermissionsFailure,
|
||||
[Types.GET_ACTIONS_ACTION]: onGetActionsAction,
|
||||
[Types.GET_ACTIONS_SUCCESS]: onGetActionsSuccess,
|
||||
[Types.GET_ACTIONS_FAILURE]: onGetActionsFailure
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import { usersReducer } from './UsersRedux';
|
||||
import { productReducer } from './ProductsRedux';
|
||||
import { inventoryReducer } from './InventoryRedux';
|
||||
import { rolesReducer } from './RolesRedux';
|
||||
import { permissionsReducer } from './PermissionsRedux';
|
||||
import { WarehouseLocationsReducer } from './WarehouseLocationsRedux';
|
||||
import { widgetReducer } from './WidgetRedux';
|
||||
import { itemReducer } from './ItemRedux';
|
||||
@@ -15,6 +16,7 @@ const appReducer = combineReducers({
|
||||
warehouse: warehouseReducer,
|
||||
users: usersReducer,
|
||||
roles: rolesReducer,
|
||||
permissions: permissionsReducer,
|
||||
warehouseLocations: WarehouseLocationsReducer,
|
||||
product: productReducer,
|
||||
inventory: inventoryReducer,
|
||||
|
||||
58
src/sagas/Permissions.js
Normal file
58
src/sagas/Permissions.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import { AuthorizedAPI } from 'config';
|
||||
import { takeLatest, call, put } from 'redux-saga/effects';
|
||||
import PermissionsActions, { PermissionsTypes } from '../redux/PermissionsRedux';
|
||||
import ApiServices from 'services/API/ApiServices';
|
||||
|
||||
export function* onRequestPermissionsData({ payload }) {
|
||||
const response = yield call(
|
||||
ApiServices[payload?.method],
|
||||
AuthorizedAPI,
|
||||
payload?.slug,
|
||||
payload?.data
|
||||
);
|
||||
if (response?.status === 200) {
|
||||
yield put(
|
||||
PermissionsActions.getPermissionsSuccess({
|
||||
loader: payload?.loader,
|
||||
permissionsDetail: response?.data?.data
|
||||
})
|
||||
);
|
||||
} else {
|
||||
payload.onFailedPermissionsData(response.data.error);
|
||||
yield put(
|
||||
PermissionsActions.getPermissionsFailure({
|
||||
loader: payload?.loader,
|
||||
error: response?.data
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function* onRequestActionsData({ payload }) {
|
||||
const response = yield call(
|
||||
ApiServices[payload?.method],
|
||||
AuthorizedAPI,
|
||||
payload?.slug,
|
||||
payload?.data
|
||||
);
|
||||
if (response?.status === 200) {
|
||||
yield put(
|
||||
PermissionsActions.getActionsSuccess({
|
||||
loader: payload?.loader,
|
||||
actionsDetail: response?.data?.data
|
||||
})
|
||||
);
|
||||
} else {
|
||||
payload.onFailedActionsData(response.data.error);
|
||||
yield put(
|
||||
PermissionsActions.getActionsFailure({
|
||||
loader: payload?.loader,
|
||||
error: response?.data
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
export default [
|
||||
takeLatest(PermissionsTypes.GET_PERMISSIONS_ACTION, onRequestPermissionsData),
|
||||
takeLatest(PermissionsTypes.GET_ACTIONS_ACTION, onRequestActionsData)
|
||||
];
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AuthorizedAPI } from 'config';
|
||||
import { takeLatest, call, put } from 'redux-saga/effects';
|
||||
import { toast } from 'react-toastify';
|
||||
import UsersActions, { UsersTypes } from '../redux/UsersRedux';
|
||||
import ApiServices from 'services/API/ApiServices';
|
||||
|
||||
@@ -36,7 +37,10 @@ export function* onCreateUserData({ payload }) {
|
||||
payload?.data
|
||||
);
|
||||
if (response?.status === 200) {
|
||||
payload.onSuccessfulSubmission(response.data?.data);
|
||||
const data = response.data?.data;
|
||||
const msg = payload.toastMessage.replace('__placeholder__', data && data.fullName ? '"' + data.fullName + '" ' : '');
|
||||
toast(msg);
|
||||
payload.onSuccessfulSubmission(data);
|
||||
yield put(
|
||||
UsersActions.createUserSuccess({
|
||||
loader: payload?.loader,
|
||||
@@ -44,6 +48,7 @@ export function* onCreateUserData({ payload }) {
|
||||
})
|
||||
);
|
||||
} else {
|
||||
toast('Something went wrong!');
|
||||
payload.onValidationFailed(response.data?.error);
|
||||
yield put(
|
||||
UsersActions.createUserFailure({
|
||||
|
||||
@@ -5,6 +5,7 @@ import UsersSaga from './Users';
|
||||
import ProductSaga from './Product';
|
||||
import InventorySaga from './Inventory';
|
||||
import RolesSaga from './Roles';
|
||||
import PermissionsSaga from './Permissions';
|
||||
import WarehouseLocationsSaga from './WarehouseLocations';
|
||||
import WidgetSaga from './Widget';
|
||||
import ItemSaga from './Item';
|
||||
@@ -16,6 +17,7 @@ export default function* rootSaga() {
|
||||
yield all([...ProductSaga]);
|
||||
yield all([...InventorySaga]);
|
||||
yield all([...RolesSaga]);
|
||||
yield all([...PermissionsSaga]);
|
||||
yield all([...WarehouseLocationsSaga]);
|
||||
yield all([...WidgetSaga]);
|
||||
yield all([...ItemSaga]);
|
||||
|
||||
@@ -15,3 +15,29 @@ export const getFetchingValue = (data, type) => {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
export const not = (a, b) => {
|
||||
return a.filter((value) => b.indexOf(value) === -1);
|
||||
};
|
||||
|
||||
export const notBy = (matchProp, a, b) => {
|
||||
if (matchProp && typeof matchProp === 'object') {
|
||||
return a.filter((aItem) => b.findIndex(bItem => (matchProp.a ? aItem[matchProp.a] : aItem) === (matchProp.b ? bItem[matchProp.b] : bItem)) === -1);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Incorrect match prop received');
|
||||
return [];
|
||||
};
|
||||
|
||||
export const intersection = (a, b) => {
|
||||
return a.filter((value) => b.indexOf(value) !== -1);
|
||||
};
|
||||
|
||||
export const intersectionBy = (matchProp, a, b) => {
|
||||
if (matchProp && typeof matchProp === 'object') {
|
||||
return a.filter((aItem) => b.findIndex(bItem => (matchProp.a ? aItem[matchProp.a] : aItem) === (matchProp.b ? bItem[matchProp.b] : bItem)) !== -1);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Incorrect match prop received');
|
||||
return [];
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ const schema = {
|
||||
createUser: Yup.object({
|
||||
fullName: Yup.string('Enter Full Name').required('User Name is required'),
|
||||
phoneNumber: Yup.string('Enter Phone Numbe').required('Phone Number is required'),
|
||||
roles: Yup.string('Select a role').required('At least one role is required'),
|
||||
roles: Yup.string('Please select at least one role').required('At least one role is required')
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user