diff --git a/src/components/AllocationManager/AllocationManager.jsx b/src/components/AllocationManager/AllocationManager.jsx index ec2b320..c506b6b 100755 --- a/src/components/AllocationManager/AllocationManager.jsx +++ b/src/components/AllocationManager/AllocationManager.jsx @@ -6,13 +6,13 @@ import TransferList from 'components/MDTransferList'; import './AllocationManager.component.scss'; const AllocationManager = props => { - const {boxStyleOverride, component, gridStyleOverride, initlist, list, matchProp, md, onChange, title, variant, xs} = props; + const {allDisabled, boxStyleOverride, component, gridStyleOverride, allocatedList, 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(',')); + onChange && onChange(state.assigned); }; return @@ -29,16 +29,17 @@ const AllocationManager = props => { {title} - + ; }; AllocationManager.propTypes = { + allDisabled: PropTypes.bool, boxStyleOverride: PropTypes.object, component: PropTypes.string, gridStyleOverride: PropTypes.object, - initlist: PropTypes.oneOfType([ + allocatedList: PropTypes.oneOfType([ PropTypes.string, PropTypes.array ]), diff --git a/src/components/MDTransferList/index.js b/src/components/MDTransferList/index.js index 3767b7d..025d05c 100644 --- a/src/components/MDTransferList/index.js +++ b/src/components/MDTransferList/index.js @@ -43,7 +43,7 @@ const useStyles = makeStyles({ } }); -export default function TransferList({list, initlist, matchProp, onChange}) { +export default function TransferList({allDisabled, list, allocatedList, matchProp, onChange}) { const classes = useStyles(); const [checked, setChecked] = useState([]); const [left, setLeft] = useState(list || []); @@ -53,14 +53,14 @@ export default function TransferList({list, initlist, matchProp, onChange}) { 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); + if (typeof allocatedList === 'string') { + const allocatedListClone = typeof allocatedList === 'object' ? allocatedList : allocatedList.split(','); + const left = notBy(matchProp, list, allocatedListClone); + const right = intersectionBy(matchProp, list, allocatedListClone); setLeft(left); setRight(right); } - }, []); + }, [allocatedList]); const handleToggle = (value) => () => { const currentIndex = checked.indexOf(value); @@ -116,7 +116,7 @@ export default function TransferList({list, initlist, matchProp, onChange}) { const labelId = `transfer-list-item-${value}-label`; return ( - + { - const {backgroundColor, classes, color, headCells, id, index, navUrl, records, table, value} = props; + const { backgroundColor, classes, color, headCells, id, loader, index, navUrl, records, table, value } = props; const navigate = useNavigate(); const StyledTableRow = styled(TableRow)(({ theme }) => ({ @@ -18,7 +18,7 @@ const PwTablePanel = props => { } })); - const rowRenders = ({records, headers, navUrl, table}) => { + const rowRenders = ({ records, headers, navUrl, table }) => { return records && records.map((record, keyouter) => { return {headers.map((columnConfig, key) => { @@ -26,15 +26,15 @@ const PwTablePanel = props => { const isAfter = columnConfig.placement && columnConfig.placement === 'after'; const limitWidth = columnConfig.limitWidth; return canEdit && navigate(navUrl, {state: {[table]: record}})}> + onClick={() => canEdit && navigate(navUrl, { state: { [table]: record } })}> {canEdit ? isAfter ? {columnConfig.value(record)} - + : - + {columnConfig.value(record)} : {columnConfig.value(record)}} @@ -52,13 +52,13 @@ const PwTablePanel = props => { backgroundColor={backgroundColor || '#007AFF'} color={color || '#fff'} > - - {records && records.length > 0 && rowRenders({records, headers: headCells, navUrl, table})} - + {records && records.length > 0 && + {rowRenders({ records, headers: headCells, navUrl, table })} + } {(!records || records.length === 0) - &&

No Records to Display

} - + &&

No Records to Display

} + ; }; PwTablePanel.propTypes = { @@ -68,10 +68,11 @@ PwTablePanel.propTypes = { headCells: PropTypes.array, id: PropTypes.string, index: PropTypes.number, + loader: PropTypes.bool, navUrl: PropTypes.string, records: PropTypes.array, table: PropTypes.string, value: PropTypes.number }; -export default PwTablePanel; \ No newline at end of file +export default PwTablePanel; diff --git a/src/components/Switch/index.js b/src/components/Switch/index.js index 3b8e6cd..4eea27e 100644 --- a/src/components/Switch/index.js +++ b/src/components/Switch/index.js @@ -56,12 +56,13 @@ const IOSSwitch = styled((props) => ( } })); -export default function Switch({ checked, onChange, name }) { - return ; +export default function Switch({ disabled, checked, onChange, name }) { + return ; } Switch.propTypes = { checked: PropTypes.any, + disabled: PropTypes.bool, name: PropTypes.any, onChange: PropTypes.any }; diff --git a/src/components/Toggles/Toggles.jsx b/src/components/Toggles/Toggles.jsx index 26436cd..8dec58d 100755 --- a/src/components/Toggles/Toggles.jsx +++ b/src/components/Toggles/Toggles.jsx @@ -25,22 +25,22 @@ const useStyles = makeStyles(() => ({ })); const Toggles = props => { - const {boxSx, gridStyleOverride, inittoggles, md, onChange, title, toggles, typoComponent, typoSx, typoVariant, xs} = props; + const {allDisabled, boxSx, gridStyleOverride, selectedToggles, 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); + if (typeof selectedToggles === 'string') { + const selectedToggleState = {}; + selectedToggles.split(',').forEach(iToggle => selectedToggleState[iToggle] = true); + updateToggleState(selectedToggleState); } - }, []); + }, [selectedToggles]); const handleToggle = (e, toggle) => { const toggleStateClone = {...toggleState, [toggle]: e.target.checked}; updateToggleState(toggleStateClone); - onChange && onChange(Object.keys(toggleStateClone).join(',')); + onChange && onChange(toggleStateClone); }; const switchRenders = toggles => toggles && toggles.map((toggle, key) => { @@ -49,7 +49,7 @@ const Toggles = props => { return {toggle} - handleToggle(e, toggle)} /> + handleToggle(e, toggle)} /> ; }); @@ -65,9 +65,10 @@ const Toggles = props => { }; Toggles.propTypes = { + allDisabled: PropTypes.bool, boxSx: PropTypes.object, gridStyleOverride: PropTypes.object, - inittoggles: PropTypes.string, + selectedToggles: PropTypes.string, md: PropTypes.number, onChange: PropTypes.func, title: PropTypes.string, diff --git a/src/index.js b/src/index.js index ff5fab3..1c0fb01 100644 --- a/src/index.js +++ b/src/index.js @@ -17,7 +17,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import 'bootstrap/dist/css/bootstrap.min.css'; -import './main.scss'; +import './main.css'; import App from 'App'; // Soft UI Context Provider diff --git a/src/main.css b/src/main.css new file mode 100644 index 0000000..1e20463 --- /dev/null +++ b/src/main.css @@ -0,0 +1,67 @@ +fieldset legend { + width: 0; +} + +.loader { + position: relative; +} + +.loader:before { + display: block; + content: ""; + position: absolute; + background-color: transparent; + background-image: url("./assets/images/favicon.png"); + background-size: 3rem; + background-repeat: no-repeat; + background-position: center; + height: 100%; + width: 100%; + -webkit-animation: rotating 2s linear infinite; + -moz-animation: rotating 2s linear infinite; + -ms-animation: rotating 2s linear infinite; + -o-animation: rotating 2s linear infinite; + animation: rotating 2s linear infinite; + z-index: 1050; +} + +.loader:after { + content: ""; + top: 0; + left: 0; + height: 100%; + width: 100%; + background: rgba(255, 255, 255, 0.7); + position: absolute; + z-index: 1040; +} + +@-webkit-keyframes rotating { + from { + -webkit-transform: rotate(0deg); + -o-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotateY(360deg); + -o-transform: rotateY(360deg); + transform: rotateY(360deg); + } +} + +@keyframes rotating { + from { + -ms-transform: rotate(0deg); + -moz-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + -o-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -ms-transform: rotateY(360deg); + -moz-transform: rotateY(360deg); + -webkit-transform: rotateY(360deg); + -o-transform: rotateY(360deg); + transform: rotateY(360deg); + } +} \ No newline at end of file diff --git a/src/main.scss b/src/main.scss deleted file mode 100644 index d1b797e..0000000 --- a/src/main.scss +++ /dev/null @@ -1,5 +0,0 @@ -fieldset { - legend { - width: 0; - } -} diff --git a/src/pages/createEditUser/index.js b/src/pages/createEditUser/index.js index bee2327..73ea169 100644 --- a/src/pages/createEditUser/index.js +++ b/src/pages/createEditUser/index.js @@ -14,7 +14,7 @@ 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 { AuthSelectors } from 'redux/AuthRedux'; import UsersActions from 'redux/UsersRedux'; import schema from 'services/ValidationServices'; @@ -73,12 +73,13 @@ function CreateEditUser(props) { 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 visibilities = useSelector(PermissionsSelectors.getPermissionsDetail); + // const currentUser = useSelector(AuthSelectors.getUser); const location = useLocation(); const [editedUser, setEditedUser] = useState(location?.state?.user); const [selectedRoles, setSelectedRoles] = useState([]); const [uploadedImg, setUploadedImg] = useState(); + // const [selectedPermissions, setSelectedPermissions] = useState({}); useEffect(() => { if (context === 'edit') { @@ -118,7 +119,7 @@ function CreateEditUser(props) { method: 'get' }) ); - (!permissions || permissions.length === 0) && + (!visibilities || visibilities.length === 0) && dispatch( PermissionsActions.getPermissionsAction({ loader: 'loading-request', @@ -149,10 +150,10 @@ function CreateEditUser(props) { visibilities: '', isActive: true, image: '', - createdBy: currentUser ? currentUser.fullName : '', - createdAt: new Date(), - updatedBy: currentUser ? currentUser.fullName : '', - updatedAt: new Date() + createdBy: '', + createdAt: '', + updatedBy: '', + updatedAt: '' } : { fullName: editedUser ? editedUser.fullName : '', phoneNumber: editedUser ? editedUser.phoneNumber : '', @@ -233,9 +234,31 @@ function CreateEditUser(props) { } }); formik.handleChange('roles')(uniqueRoles.map((role) => role.name).join()); + aggregatePermissions(uniqueRoles); setSelectedRoles(uniqueRoles); }; + const aggregatePermissions = roles => { + const actions = [], visibilities = [], warehouses = [], inventories = []; + roles.forEach(role => { + if (role.permissions) { + const currActions = role.permissions.actions; + const currVisibilities = role.permissions.allowedUIModules; + const currWarehouseScopes = role.permissions.warehouseScopes; + const currInventoryScopes = role.permissions.inventoryScopes; + currActions.forEach(ac => actions.indexOf(ac) === -1 && actions.push(ac)); + currVisibilities.forEach(vi => visibilities.indexOf(vi) === -1 && visibilities.push(vi)); + currWarehouseScopes.forEach(currWh => warehouses.findIndex(wh => wh.id === currWh.id) === -1 && warehouses.push(currWh)); + currInventoryScopes.forEach(currInv => inventories.findIndex(inv => inv.id === currInv.id) === -1 && inventories.push(currInv)); + // setSelectedPermissions({...selectedPermissions, actions}); + } + }); + formik.handleChange('actions')(actions.join(',')); + formik.handleChange('visibilities')(visibilities.join(',')); + formik.handleChange('warehouses')(warehouses.map(wh => wh.id).join(',')); + formik.handleChange('inventories')(inventories.map(inv => inv.id).join(',')); + }; + const handleFileChange = e => { const [file] = e.target.files; if (file) { @@ -456,40 +479,60 @@ function CreateEditUser(props) { 0} name="warehouses" gridStyleOverride={{ paddingLeft: '4rem !important' }} - initlist={formik.values.warehouses} + allocatedList={formik.values.warehouses} list={warehouses} matchProp={{ a: '_id' }} title="Warehouse" - onChange={(val) => formik.handleChange('warehouses')(val)} + onChange={val => { + val = val?.map(obj => obj._id).join(','); + // setSelectedPermissions({...selectedPermissions, warehouses: val}); + formik.handleChange('warehouses')(val); + }} /> 0} name="inventories" gridStyleOverride={{ paddingRight: '2rem' }} - initlist={formik.values.inventories} + allocatedList={formik.values.inventories} list={inventories} matchProp={{ a: '_id' }} title="Inventory" - onChange={(val) => formik.handleChange('inventories')(val)} + onChange={val => { + val = val?.map(obj => obj._id).join(','); + // setSelectedPermissions({...selectedPermissions, inventories: val}); + formik.handleChange('inventories')(val); + }} /> 0} name="actions" gridStyleOverride={{ paddingLeft: '4rem !important' }} title="Actions" toggles={actions} - inittoggles={formik.values.actions} - onChange={(val) => formik.handleChange('actions')(val)} + selectedToggles={formik.values.actions} + onChange={val => { + val = Object.keys(val).join(','); + // setSelectedPermissions({...selectedPermissions, actions: val}); + formik.handleChange('actions')(val); + }} /> 0} name="visibilities" gridStyleOverride={{ paddingRight: '2rem' }} title="Application" - toggles={permissions} - inittoggles={formik.values.visibilities} - onChange={(val) => formik.handleChange('visibilities')(val)} + toggles={visibilities} + selectedToggles={formik.values.visibilities} + onChange={val => { + val = Object.keys(val).join(','); + // setSelectedPermissions({...selectedPermissions, visibilities: val}); + formik.handleChange('visibilities')(val); + }} /> record.name }, - { id: 'warehouse', label: 'Warehouse', limitWidth: true, value: record => warehouses && record.permissions?.warehouseScopes && record.permissions?.warehouseScopes?.length === warehouses?.length ? 'All' : record.permissions?.warehouseScopes ? record.permissions?.warehouseScopes.map(sc => sc.name).join(', ') : ''}, - { id: 'inventory', label: 'Inventories', limitWidth: true, value: record => inventories && record.permissions?.inventoryScopes && record.permissions?.inventoryScopes?.length === inventories?.length ? 'All' : record.permissions?.inventoryScopes ? record.permissions?.inventoryScopes.map(sc => sc.name).join(', ') : ''}, - { id: 'actions', label: 'Actions', limitWidth: true, value: record => actions && record.permissions?.actions && record.permissions?.actions?.length === actions?.length ? 'All' : record.permissions?.actions ? record.permissions?.actions.join(', ') : ''}, - { id: 'visibilities', label: 'App Modules', limitWidth: true, value: record => permissions && record.permissions?.allowedUIModules && record.permissions?.allowedUIModules?.length === permissions?.length ? 'All' : record.permissions?.allowedUIModules ? record.permissions?.allowedUIModules.join(', ') : ''}, + { id: 'warehouse', label: 'Warehouse', limitWidth: true, value: record => { + const roleWh = record.permissions?.warehouseScopes; + return warehouses && roleWh && roleWh.length === warehouses.length ? 'All' : roleWh + ? warehouses.filter(wh => roleWh.findIndex(whCurr => whCurr.id === wh._id) > -1).map(wh => wh.name).join(', ') : ''; + } + }, + { id: 'inventory', label: 'Inventories', limitWidth: true, value: record => { + const roleIn = record.permissions?.inventoryScopes; + return inventories && roleIn && roleIn.length === inventories.length ? 'All' : roleIn + ? inventories.filter(inv => roleIn.findIndex(inCurr => inCurr.id === inv._id) > -1).map(inv => inv.name).join(', ') : ''; + } + }, + { id: 'actions', label: 'Actions', limitWidth: true, value: record => actions && record.permissions?.actions + && record.permissions?.actions?.length === actions?.length ? 'All' : record.permissions?.actions + ? record.permissions?.actions.join(', ') : ''}, + { id: 'visibilities', label: 'App Modules', limitWidth: true, value: record => permissions && record.permissions?.allowedUIModules + && record.permissions?.allowedUIModules?.length === permissions?.length ? 'All' : record.permissions?.allowedUIModules + ? record.permissions?.allowedUIModules.join(', ') : ''}, { id: 'status', label: 'Status', value: record => record.status === 'ACTIVE' ? Active : Inactive } ]; const usersHandler = () => { + setUserLoader(true); dispatch( UsersActions.getUsersAction({ loader: 'loading-request', slug: API.GET_USERS_DATA, - method: 'get' + method: 'get', + callback: setUserLoader }) ); }; const rolesHandler = () => { + setRoleLoader(true); dispatch( RolesActions.getRolesAction({ loader: 'loading-request', slug: API.GET_ROLES_DATA, - method: 'get' + method: 'get', + callback: setRoleLoader }) ); }; useMemo(() => rolesHandler(), []); + useMemo(() => usersHandler(), []); useEffect(() => { if (usersData.length) { @@ -188,7 +209,7 @@ function UserAccessScreen() { let roles = JSON.parse(JSON.stringify(rolesData)); roles = roles.map((item) => { item.name = item.name.split('-').join(' ').toUpperCase(); - item.permissions = item.permissions?.allowedUIModules?.join(','); + // item.permissions = item.permissions?.allowedUIModules?.join(','); if (!item.permissions) { item.permissions = 'NA'; } @@ -209,7 +230,7 @@ function UserAccessScreen() { let records = currentTab === 0 ? originalUserRecords : originalRolesRecords; records = JSON.parse(JSON.stringify(records)); records.forEach(record => record.status = record.status ? 'ACTIVE' : 'INACTIVE'); - let searchList = currentTab === 0 ? ['fullName', 'phoneNumber', 'role_name', 'status'] : ['name', 'permissions', 'status'] + let searchList = currentTab === 0 ? ['fullName', 'phoneNumber', 'role_name', 'status'] : ['name', 'permissions', 'status']; const setter = currentTab === 0 ? setUserRecords : setRoleRecords; searchList = searchList.concat(['createdBy.fullName', 'createdAt', 'updatedBy.fullName', 'updatedAt']); const filteredRecords = records.filter(record => searchList.some(field => { @@ -220,7 +241,7 @@ function UserAccessScreen() { field = field[1]; } return recordInner && recordInner[field] !== undefined && typeof recordInner[field] === 'string' - && recordInner[field].toLowerCase().indexOf(value?.toLowerCase()) > -1 + && recordInner[field].toLowerCase().indexOf(value?.toLowerCase()) > -1; })); records.forEach(record => record.status = record.status === 'ACTIVE'); setter(filteredRecords); @@ -238,7 +259,7 @@ function UserAccessScreen() { ]} /> - + usersHandler()} /> @@ -275,10 +296,10 @@ function UserAccessScreen() { - - + + diff --git a/src/routes/index.js b/src/routes/index.js index afa9251..8850780 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -187,11 +187,18 @@ const protectedRoutes = [ component: }, { - name: 'Edot User', + name: 'Edit User', key: 'edit-user', route: '/setup/users-access/edit-user', hide: true, component: + }, + { + name: 'Edit Role', + key: 'edit-role', + route: '/setup/users-access/edit-role', + hide: true, + component: } ] } diff --git a/src/sagas/Roles.js b/src/sagas/Roles.js index b2527cd..e18d162 100644 --- a/src/sagas/Roles.js +++ b/src/sagas/Roles.js @@ -1,5 +1,6 @@ import { AuthorizedAPI } from 'config'; import { takeLatest, call, put } from 'redux-saga/effects'; +import { toast } from 'react-toastify'; import RolesActions, { RolesTypes } from '../redux/RolesRedux'; import ApiServices from 'services/API/ApiServices'; @@ -10,6 +11,7 @@ export function* onRequestRolesData({ payload }) { payload?.slug, payload?.data ); + payload?.callback && payload?.callback(false); if (response?.status === 200) { yield put( RolesActions.getRolesSuccess({ @@ -18,6 +20,7 @@ export function* onRequestRolesData({ payload }) { }) ); } else { + toast('Failed to fetch roles'); payload.onFailedRolesData(response.data.error); yield put( RolesActions.getRolesFailure({ diff --git a/src/sagas/Users.js b/src/sagas/Users.js index d7eb7b4..533cc86 100644 --- a/src/sagas/Users.js +++ b/src/sagas/Users.js @@ -11,6 +11,7 @@ export function* onRequestUsersData({ payload }) { payload?.slug, payload?.data ); + payload?.callback && payload?.callback(false); if (response?.status === 200) { yield put( UsersActions.getUsersSuccess({ @@ -19,6 +20,7 @@ export function* onRequestUsersData({ payload }) { }) ); } else { + toast('Failed to fetch user list'); payload.onFailedUsersData(response.data.error); yield put( UsersActions.getUsersFailure({ @@ -40,6 +42,7 @@ export function* onCreateUserData({ payload }) { contentType: false } ); + payload?.callback && payload?.callback(false); if (response?.status === 200) { const data = response.data?.data; const msg = payload.toastMessage.replace('__placeholder__', data && data.fullName ? '"' + data.fullName + '" ' : '');