44 Commits

Author SHA1 Message Date
m0n02hz
55d8526808 Merge branch 'develop' of https://github.com/kfnawaz/plaidware-wms-web into feature/wms-55_develop 2022-03-11 02:35:52 +05:30
m0n02hz
c4d3190d5e [WMS-55] Implemented review comments
Implemented review comments, added loader on submission
2022-03-11 02:35:40 +05:30
Dikshajain39511
ceb9256f1b Feature/wms 70 (#93)
* Warehouse edit - Inventory hosted selector fix
* Update: Unique key fix

Co-authored-by: evdigitech <evdigitech@gmail.com>
Co-authored-by: Llewellyn Dsouza <lledsouza2209@gmail.com>
2022-03-10 22:31:51 +05:30
bluestreamlds
8abe5ba900 Merge pull request #92 from kfnawaz/fix/WMS-83
Fix/wms 83
2022-03-10 10:58:39 +05:30
Llewellyn Dsouza
4d3b6ec9df Fixed: labels on widget dropdown select 2022-03-10 10:57:54 +05:30
Llewellyn Dsouza
b2c0c3f958 Update: item listing breadcrumbs 2022-03-10 10:40:36 +05:30
Llewellyn Dsouza
7561006ce0 Fixed: warehouse listing key 2022-03-09 12:53:42 +05:30
Dikshajain39511
7131e86792 Feature/wms 72 (#89)
* remove duplicates name validation
* popop made for inventory icon slug
* add item page:breadcrumb changes
* remove new
* Update: naming and redirections
* Removed: validation code

Co-authored-by: evdigitech <evdigitech@gmail.com>
2022-03-09 12:48:58 +05:30
bluestreamlds
cb71fe1a42 Merge pull request #86 from kfnawaz/fix/WMS-66
Fix/wms 66 - labelling functionality
2022-03-09 12:06:40 +05:30
Llewellyn Dsouza
787c91fe7a Merge branch 'develop' into fix/WMS-66 2022-03-09 12:06:08 +05:30
Llewellyn Dsouza
c62453e318 Added: filter button to location labelling 2022-03-09 12:04:55 +05:30
e17f4fb0b6 Merge pull request #87 from kfnawaz/feature/wms-55_develop
[WMS-55] Review Comments, more
2022-03-09 03:24:41 +05:30
m0n02hz
728406b452 [WMS-55] Review Comments, more
Permissions selected disabled and now dependent on role selection
added loader and alert message when table load fails
refactored
Removed default created, update by/at field values
Loaded data in roles table
Added 'edit-role' route
2022-03-09 03:23:58 +05:30
Llewellyn D'souza
30b4b5d7c2 fixed: unupdated state id for row
removed: unecessary dispatch action
Updated: label saga to listen to every dispatch
2022-03-08 17:47:36 +05:30
Llewellyn D'souza
51dc7c33c3 Update: increase gradient distance 2022-03-08 17:18:25 +05:30
Llewellyn D'souza
e75e270cdf Added: vertical and horizontal overflows 2022-03-08 16:51:31 +05:30
Llewellyn D'souza
913c1dbcf2 Removed: reset inventory after filter click 2022-03-08 16:45:07 +05:30
Llewellyn D'souza
940c1e704e Linted: dropdown 2022-03-08 16:11:55 +05:30
Llewellyn D'souza
0fb3f3bf55 Linted: search bar 2022-03-08 16:10:59 +05:30
Llewellyn D'souza
70cda3d3f2 Fixed: widget labelling 2022-03-08 16:10:35 +05:30
Llewellyn D'souza
75b6f2a88b Fixed: location labelling 2022-03-08 16:08:16 +05:30
Llewellyn D'souza
be258aea34 Fixed: dropdown component 2022-03-08 16:08:08 +05:30
Llewellyn Dsouza
ef4e3a2d9f Fix: eslint config 2022-03-08 08:03:20 +05:30
bluestreamlds
320ec7061c Merge pull request #84 from kfnawaz/feat/WMS-67
Feat/wms 67 - delete-confirmations-toasts
2022-03-07 18:17:55 +05:30
Llewellyn Dsouza
ae7e8547c3 Added: Toast messages for add, edit, delete 2022-03-07 18:14:50 +05:30
Llewellyn Dsouza
74199f6752 Added: Delete confirmations for widget families 2022-03-07 18:05:03 +05:30
Llewellyn Dsouza
54369ae9b7 Update: Delete confirmation for warehouse locations 2022-03-07 18:04:48 +05:30
87a890cd23 Merge pull request #83 from kfnawaz/feature/wms-55_develop
[WMS-55] Review comments
2022-03-06 06:45:15 +05:30
m0n02hz
95e05cb51c [WMS-55] Review comments 2022-03-06 06:44:33 +05:30
e87dbdeee6 Merge pull request #82 from kfnawaz/feature/wms-55_develop
[WMS-55] Review comments
2022-03-06 06:07:02 +05:30
m0n02hz
eb0e34fef5 [WMS-55] Review comments 2022-03-06 06:06:07 +05:30
61bfd87029 Merge pull request #80 from kfnawaz/feature/wms-55_develop
Feature/wms 55 develop
2022-03-06 01:18:20 +05:30
m0n02hz
1469e4ff5a [WMS-39] Role Page Added, date field bug fixed 2022-03-06 01:15:51 +05:30
m0n02hz
2f8d60049c Merge branch 'develop' of https://github.com/kfnawaz/plaidware-wms-web into feature/wms-55_develop 2022-03-06 01:12:28 +05:30
bluestreamlds
85947c18c2 Merge pull request #79 from kfnawaz/feat/delete-inventory
Feat/delete inventory
2022-03-04 18:39:00 +05:30
Llewellyn Dsouza
a22c3fcf42 Added: delete inventory 2022-03-04 18:28:41 +05:30
bluestreamlds
8cccf1f034 feat/delete-item-warehouse (#78)
Delete item
Delete warehouse
Delete confirmation messages
Bonus: Full cover image

Co-authored-by: Llewellyn Dsouza <lledsouza2209@gmail.com>
2022-03-04 16:27:53 +05:30
m0n02hz
5d2e29fc41 [WMS-55]
Image upload, better error handling, removed warnings,
fixed date field not rendering correct date during edit
2022-03-04 02:03:10 +05:30
bluestreamlds
5381f7d727 Fixes/minor (#77)
Breadcrumbs, images, icon slugs, notifications

Co-authored-by: Llewellyn Dsouza <lledsouza2209@gmail.com>
2022-03-04 01:59:51 +05:30
Llewellyn Dsouza
e2d3ccfb7e Fix: Merge (#71) 2022-03-03 22:34:30 +05:30
Dikshajain39511
fd8d0b1167 Feature/wms 52 (#71)
Labeling module

Co-authored-by: Llewellyn D'souza <lledsouza2209@gmail.com>
Co-authored-by: evdigitech <evdigitech@gmail.com>
2022-03-03 22:29:08 +05:30
Llewellyn Dsouza
a883212126 Fix: Build issues 2022-03-03 21:07:53 +05:30
bluestreamlds
7c9bc373d4 Merge pull request #76 from kfnawaz/feature/wms-55_develop
Added rewire lib to update webpack config
2022-03-03 19:27:28 +05:30
m0n02hz
f1a2210e16 Added rewire lib to update webpack config 2022-03-03 18:58:21 +05:30
70 changed files with 4408 additions and 6318 deletions

View File

@@ -8,6 +8,10 @@ module.exports = {
extends: ['eslint:recommended', 'plugin:react/recommended'],
parser: '@babel/eslint-parser',
parserOptions: {
requireConfigFile: false,
babelOptions: {
presets: ['@babel/preset-react']
},
ecmaFeatures: {
jsx: true,
modules: true,
@@ -23,7 +27,7 @@ module.exports = {
'linebreak-style': ['error', 'unix'],
quotes: ['warn', 'single'],
semi: ['warn', 'always'],
'no-unused-vars' : 'warn',
'no-unused-vars': 'warn',
'comma-dangle': [
'warn',
{

2
.gitignore vendored
View File

@@ -1,7 +1,7 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
node_modules
.DS_Store
# testing

7144
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
"dependencies": {
"@asseinfo/react-kanban": "2.2.0",
"@emotion/cache": "11.4.0",
"@emotion/react": "11.4.1",
"@emotion/react": "^11.4.1",
"@emotion/styled": "11.3.0",
"@fullcalendar/daygrid": "5.9.0",
"@fullcalendar/interaction": "5.9.0",
@@ -30,7 +30,6 @@
"@zxing/library": "^0.18.5",
"apisauce": "^2.1.5",
"bootstrap": "^5.1.3",
"buffer": "^6.0.3",
"chart.js": "3.4.1",
"chroma-js": "2.1.2",
"date-fns": "^2.28.0",
@@ -41,7 +40,6 @@
"jsbarcode": "^3.11.5",
"jsonwebtoken": "^8.5.1",
"moment": "^2.29.1",
"process": "^0.11.10",
"prop-types": "15.7.2",
"ramda": "^0.27.2",
"react": "17.0.2",
@@ -59,11 +57,9 @@
"redux-saga": "^1.1.3",
"reduxsauce": "^1.2.1",
"seamless-immutable": "^7.1.4",
"stream": "^0.0.2",
"stylis": "4.0.10",
"stylis-plugin-rtl": "2.1.0",
"underscore": "^1.13.2",
"util": "^0.12.4",
"uuid": "8.3.2",
"web-vitals": "1.0.1",
"yup": "^0.32.11"
@@ -104,7 +100,9 @@
]
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/eslint-parser": "^7.17.0",
"@babel/preset-react": "^7.16.7",
"eslint": "^8.2.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "8.3.0",

View File

@@ -52,7 +52,7 @@ import PublicRoutes from 'routes/PublicRoutes';
import reduxStore from './redux/Store';
import { protectedRoutes as routes } from './routes/index';
import PrivateRoute from './routes/PrivateRoute';
import MDAlert from 'components/MDAlert';
// import MDAlert from 'components/MDAlert';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
@@ -171,7 +171,7 @@ export default function App() {
</Routes>
{/* <MDAlert dismissible><span>Submitted Successfully!</span></MDAlert> */}
</ThemeProvider>
<ToastContainer />
<ToastContainer position="bottom-right" />
</PersistGate>
</Provider>
);

View File

@@ -1,12 +1,38 @@
import PropTypes from 'prop-types';
const InventoryIcon = ({ width = 25, height = 24, ...props }) => (
<svg width={width} height={height} {...props} viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M37.24 58.7998L43.24 64.7998L59.24 48.7998" stroke="#007AFF" strokeWidth="5" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M40 24H56C64 24 64 20 64 16C64 8 60 8 56 8H40C36 8 32 8 32 16C32 24 36 24 40 24Z" stroke="#007AFF" strokeWidth="5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M64 16.0801C77.32 16.8001 84 21.7201 84 40.0001V64.0001C84 80.0001 80 88.0001 60 88.0001H36C16 88.0001 12 80.0001 12 64.0001V40.0001C12 21.7601 18.68 16.8001 32 16.0801" stroke="#007AFF" strokeWidth="5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round"/>
const InventoryIcon = ({ width = '25', height = '25', ...props }) => (
<svg
width={width}
height={height}
{...props}
viewBox="0 0 96 96"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M37.24 58.7998L43.24 64.7998L59.24 48.7998"
stroke="#007AFF"
strokeWidth="5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M40 24H56C64 24 64 20 64 16C64 8 60 8 56 8H40C36 8 32 8 32 16C32 24 36 24 40 24Z"
stroke="#007AFF"
strokeWidth="5"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M64 16.0801C77.32 16.8001 84 21.7201 84 40.0001V64.0001C84 80.0001 80 88.0001 60 88.0001H36C16 88.0001 12 80.0001 12 64.0001V40.0001C12 21.7601 18.68 16.8001 32 16.0801"
stroke="#007AFF"
strokeWidth="5"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
export default InventoryIcon;

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -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 <Grid item className='c-AllocationManager' xs={xs || 12} md={md || 6} sx={gridStyleOverride}>
@@ -24,20 +24,22 @@ const AllocationManager = props => {
padding: '12px',
borderRadius: '4px'
})}
className="h-100 d-flex flex-column"
>
<Typography gutterBottom variant={variant || 'h6'} component={component || 'div'}>
{title}
</Typography>
<TransferList list={list || []} initlist={initlist} matchProp={matchProp} onChange={handleAllocationChange} />
<TransferList allDisabled={allDisabled} list={list || []} allocatedList={allocatedList} matchProp={matchProp} onChange={handleAllocationChange} />
</MDBox>
</Grid>;
};
AllocationManager.propTypes = {
allDisabled: PropTypes.bool,
boxStyleOverride: PropTypes.object,
component: PropTypes.string,
gridStyleOverride: PropTypes.object,
initlist: PropTypes.oneOfType([
allocatedList: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array
]),

View File

@@ -16,7 +16,7 @@ import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
// @mui material components
import { Breadcrumbs as MuiBreadcrumbs, Grid, Toolbar } from '@mui/material';
import { Box, Breadcrumbs as MuiBreadcrumbs, Grid, Toolbar } from '@mui/material';
import ArrowRight from 'assets/images/CarretArrowRightIcon';
// Material Dashboard 2 PRO React components
@@ -53,7 +53,8 @@ const buildBreadcrumbs = (route, light) => {
});
};
function Breadcrumbs({ route, light, children }) {
function Breadcrumbs({ title, route, children }) {
const light = false;
return (
<Toolbar variant="dense">
<MDBox
@@ -63,6 +64,19 @@ function Breadcrumbs({ route, light, children }) {
// backgroundColor: '#fff'
}}
>
{title && (
<Box
component="div"
sx={{
fontSize: '22px',
letterSpacing: '0.01em',
color: '#000',
marginBottom: '15px'
}}
>
{title}
</Box>
)}
<Grid container spacing={2} alignItems="center">
<Grid item>
<MuiBreadcrumbs
@@ -86,15 +100,15 @@ function Breadcrumbs({ route, light, children }) {
);
}
// Setting default values for the props of Breadcrumbs
Breadcrumbs.defaultProps = {
light: false
};
// // Setting default values for the props of Breadcrumbs
// Breadcrumbs.defaultProps = {
// light: false
// };
// Typechecking props for the Breadcrumbs
Breadcrumbs.propTypes = {
route: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired,
light: PropTypes.bool,
title: PropTypes.string,
children: PropTypes.node
};

View File

@@ -1,4 +1,4 @@
import * as React from 'react';
import React, {useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import Stack from '@mui/material/Stack';
import TextField from '@mui/material/TextField';
@@ -6,11 +6,15 @@ import AdapterDateFns from '@mui/lab/AdapterDateFns';
import LocalizationProvider from '@mui/lab/LocalizationProvider';
import DateTimePicker from '@mui/lab/DateTimePicker';
export default function DateTimeInput({ disabled }) {
const [value, setValue] = React.useState(new Date());
export default function DateTimeInput({ disabled, value }) {
const [date, setDate] = useState(value || new Date());
useEffect(() => {
setDate(value);
}, [value]);
const handleChange = (newValue) => {
setValue(newValue);
setDate(newValue);
};
return (
@@ -19,7 +23,7 @@ export default function DateTimeInput({ disabled }) {
<DateTimePicker
disabled={disabled}
label=""
value={value}
value={date}
renderInput={(params) => <TextField {...params} />}
onChange={handleChange}
/>
@@ -29,5 +33,6 @@ export default function DateTimeInput({ disabled }) {
}
DateTimeInput.propTypes = {
disabled: PropTypes.bool
disabled: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object])
};

View File

@@ -1,8 +1,6 @@
import React, { useState, useEffect } from 'react';
import React from 'react';
import Box from '@mui/material/Box';
// import InputLabel from '@mui/material/InputLabel'
import PropTypes from 'prop-types';
import OutlinedInput from '@mui/material/OutlinedInput';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
@@ -15,53 +13,32 @@ const useStyles = makeStyles({
color: 'black'
}
});
export default function Dropdown({ items, dropdownData }) {
export default function Dropdown({ dropdownData, label, onChange, value }) {
const classes = useStyles();
const [age, setAge] = useState('');
const [dropDownValue, setDropDownValue] = useState([]);
useEffect(() => {
setDropDownValue(items);
}, [items]);
const handleChange = (event) => {
const {
target: { value }
} = event;
setAge(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
);
};
return (
<>
<Box sx={{ width: '100%' }}>
<InputLabel className={classes.font} id="demo-simple-select-label" sx={{ pb: 2, pt: 3 }}>
{dropDownValue?.label}
{label}
</InputLabel>
<FormControl sx={{ width: '100%' }}>
<Select
displayEmpty
input={<OutlinedInput />}
value={age}
renderValue={(selected) => {
if (selected.length === 0) {
return <span>{dropDownValue?.placeholder}</span>;
}
return selected;
}}
value={value}
inputProps={{ 'aria-label': 'Without label' }}
onChange={handleChange}
onChange={onChange}
>
<MenuItem disabled value="">
<span>{dropDownValue?.label}</span>
None selected
</MenuItem>
{dropdownData && dropdownData.map((data) => (
<MenuItem value={data.displayname} key={data.ID}>{data.displayname}</MenuItem>
))}
{dropdownData &&
dropdownData.map((data) => (
<MenuItem value={data._id} key={data._id}>
{data.name}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
@@ -69,7 +46,8 @@ export default function Dropdown({ items, dropdownData }) {
);
}
Dropdown.propTypes = {
items: PropTypes.object.isRequired,
dropdownData: PropTypes.object.isRequired
dropdownData: PropTypes.array,
onChange: PropTypes.any,
label: PropTypes.string,
value: PropTypes.any
};

View File

@@ -11,7 +11,7 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import TablePagination from 'components/TablePagination';
import { Dialog, DialogActions, MenuItem, Select } from '@mui/material';
import { Grid, MenuItem, Select } from '@mui/material';
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
@@ -41,10 +41,11 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({
Row.propTypes = {
rowData: PropTypes.array,
tHeads: PropTypes.array,
editHandler: PropTypes.any
editHandler: PropTypes.any,
deleteHandler: PropTypes.any
};
function Row({ tHeads, rowData, editHandler }) {
function Row({ tHeads, rowData, editHandler, deleteHandler }) {
return (
<React.Fragment>
<StyledTableRow sx={{ '&odd > *': { borderBottom: 'unset' } }}>
@@ -68,6 +69,25 @@ function Row({ tHeads, rowData, editHandler }) {
>
EDIT
</MDButton>
<MDButton
size="small"
variant="contained"
color="error"
sx={{
textTransform: 'capitalize',
minWidth: '45px',
minHeight: '28px',
marginLeft: '10px',
boxShadow: 'none',
fontWeight: '500',
padding: '0'
}}
onClick={() => {
deleteHandler(rowData._id);
}}
>
DELETE
</MDButton>
</StyledTableCell>
{tHeads &&
tHeads
@@ -87,15 +107,10 @@ function EnhancedTable({
data,
tHeads,
editHandler,
deleteHandler,
filtersControl,
resetFilters
}) {
const [filtersOpen, setFiltersOpen] = React.useState(false);
const handleFiltersClose = () => {
setFiltersOpen(false);
};
return (
<>
<Box
@@ -106,7 +121,9 @@ function EnhancedTable({
overflow: 'hidden'
}}
>
<Box
<Grid
container
fullWidth
sx={{
display: 'flex',
alignItems: 'center',
@@ -114,41 +131,15 @@ function EnhancedTable({
padding: '16px'
}}
>
<Box>{/* <SearchBar /> */}</Box>
<Box sx={{ display: 'flex', columnGap: '15px' }}>
<MDButton
size="small"
variant="outlined"
color="primary"
sx={{
textTransform: 'capitalize',
minWidth: '60px',
minHeight: '44px',
fontWeight: '500'
}}
onClick={() => {
setFiltersOpen(true);
}}
>
Filter
</MDButton>
{/* <Box><SearchBar /></Box> */}
<Grid container item xs={12} spacing={5}>
{filtersControl ? (
<Dialog open={filtersOpen} onClose={handleFiltersClose}>
<>
{filtersControl}
<DialogActions>
<Grid item sx={4}>
<MDButton onClick={resetFilters}>Reset Filters</MDButton>
<MDButton
size="small"
variant="outlined"
color="primary"
onClick={() => {
setFiltersOpen(false);
}}
>
Close
</MDButton>
</DialogActions>
</Dialog>
</Grid>
</>
) : null}
{/* <MDButton
id="fade-button"
@@ -183,8 +174,8 @@ function EnhancedTable({
<MenuItem>My account</MenuItem>
<MenuItem>Logout</MenuItem>
</Menu> */}
</Box>
</Box>
</Grid>
</Grid>
{/* Table-row- */}
<TableContainer component={Paper} sx={{ borderRadius: '0 !important', boxShadow: 'none' }}>
<Table aria-label="collapsible table" sx={{ minWidth: 700 }}>
@@ -202,6 +193,7 @@ function EnhancedTable({
<Row
key={rowData._id}
editHandler={editHandler}
deleteHandler={deleteHandler}
rowData={rowData}
tHeads={tHeads}
/>
@@ -334,6 +326,7 @@ EnhancedTable.propTypes = {
data: PropTypes.array,
tHeads: PropTypes.array,
editHandler: PropTypes.any,
deleteHandler: PropTypes.any,
filtersControl: PropTypes.any,
resetFilters: PropTypes.any
};

View File

@@ -25,7 +25,7 @@ function ImageUploadSingle({ heading, accept, multiple, images, setImages }) {
cursor: 'pointer',
position: 'relative',
textAlign: 'center',
minHeight: pxToRem(200),
height: pxToRem(250),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
@@ -72,14 +72,14 @@ function ImageUploadSingle({ heading, accept, multiple, images, setImages }) {
cursor: 'pointer',
position: 'relative',
textAlign: 'center',
minHeight: pxToRem(200),
height: pxToRem(250),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: '16px'
}}
>
<img src={images[0].src} alt="" width="100%" height="100%" />
<img src={images[0].src} alt="" height="100%" />
<Button
sx={{
backgroundColor: '#fff !important',

View File

@@ -36,10 +36,14 @@ const useStyles = makeStyles({
'& .MuiCheckbox-root': {
paddingLeft: '0px'
}
},
maxHeight: {
maxHeight: '20rem',
overflow: 'auto'
}
});
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 || []);
@@ -49,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);
@@ -106,13 +110,13 @@ export default function TransferList({list, initlist, matchProp, onChange}) {
};
const customList = items => (
<List component="div" role="list">
<List component="div" role="list" className={classes.maxHeight}>
{items.map((item, key) => {
const value = item.name;
const labelId = `transfer-list-item-${value}-label`;
return (
<ListItem button key={value + '-' + key} role="listitem" onClick={handleToggle(item)}>
<ListItem button disabled={allDisabled} key={value + '-' + key} role="listitem" onClick={handleToggle(item)}>
<ListItemIcon className={classes.unsetwidth}>
<Checkbox
disableRipple
@@ -137,7 +141,7 @@ export default function TransferList({list, initlist, matchProp, onChange}) {
);
return (
<Grid container>
<Grid container className="flex-fill">
<Grid item md={5} className={classes.boxStyling}>
<Typography gutterBottom variant="caption" component="div">
Unassigned
@@ -201,7 +205,8 @@ export default function TransferList({list, initlist, matchProp, onChange}) {
}
TransferList.propTypes = {
initlist: PropTypes.oneOfType([
allDisabled: PropTypes.bool,
allocatedList: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array
]),

View File

@@ -1,10 +1,12 @@
/* eslint-disable indent */
import {
Box,
Button,
// Chip,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Grid,
MenuItem,
@@ -64,6 +66,14 @@ function NestedDataTable({ data, selected, setSelected, populateChildren }) {
populateChildren(data.id, data.location);
}, []);
const [deleteAlertOpen, setDeleteAlertOpen] = React.useState(null);
const handleDeleteAlertClose = () => {
setDeleteAlertOpen(false);
};
const handleDeleteAlertOpen = () => {
setDeleteAlertOpen(true);
};
return (
<>
<Box
@@ -80,7 +90,7 @@ function NestedDataTable({ data, selected, setSelected, populateChildren }) {
selected?.id === data.id
? 'linear-gradient(135deg, ' +
getColorOfLocationType(data.location) +
' 0%, #f9f9f9 20%)'
' 0%, #f9f9f9 100%)'
: '#f9f9f9'
}}
>
@@ -154,18 +164,44 @@ function NestedDataTable({ data, selected, setSelected, populateChildren }) {
padding: '0 6'
}}
onClick={() => {
dispatch(
WarehouseLocationsActions.deleteLocationRequest({
loader: 'location-request',
slug: API.LOCATION_DELETE,
method: 'post',
data: { type: data.location, id: data.id }
})
);
handleDeleteAlertOpen();
}}
>
DELETE
</MDButton>
<Dialog
open={deleteAlertOpen}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
onClose={handleDeleteAlertClose}
>
<DialogTitle id="alert-dialog-title">Confirm Delete</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleDeleteAlertClose}>
No
</Button>
<Button
onClick={() => {
dispatch(
WarehouseLocationsActions.deleteLocationRequest({
loader: 'location-request',
slug: API.LOCATION_DELETE,
method: 'post',
data: { type: data.location, id: data.id }
})
);
handleDeleteAlertClose();
}}
>
Yes
</Button>
</DialogActions>
</Dialog>
</Grid>
</Grid>
</Grid>

View File

@@ -0,0 +1,3 @@
.c-PwTablePanel {
}

View File

@@ -0,0 +1,79 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useNavigate } from 'react-router-dom';
import { styled } from '@mui/material/styles';
import { TableBody, TableCell, TableRow } from '@mui/material';
import BasicTable from 'components/BasicTable';
import TabPanel from 'components/Tabs';
import EditIcon from '@mui/icons-material/Edit';
import './PwTablePanel.component.scss';
const PwTablePanel = props => {
const { backgroundColor, classes, color, dataFetched, headCells, id, loader, index, navUrl, records, table, value } = props;
const navigate = useNavigate();
const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(even)': {
backgroundColor: theme.palette.action.hover
}
}));
const rowRenders = ({ records, headers, navUrl, table }) => {
return records && records.map((record, keyouter) => {
return <StyledTableRow key={record.id + '-' + keyouter}>
{headers.map((columnConfig, key) => {
const canEdit = columnConfig.isEditAnchor;
const isAfter = columnConfig.placement && columnConfig.placement === 'after';
const limitWidth = columnConfig.limitWidth;
return <TableCell key={key} className={`${isAfter ? 'position-relative pe-5' : ''}${limitWidth ? ' overflow-auto ' + classes.limitWidth : ''}`}
onClick={() => canEdit && navigate(navUrl, { state: { [table]: record } })}>
{canEdit
? isAfter
? <span className={classes.iconwrap}>
{columnConfig.value(record)}
<EditIcon className={classes.iconSize + ' ' + classes.rightPlaced} />
</span>
: <span className={classes.iconwrap}>
<EditIcon className={classes.iconSize} />
{columnConfig.value(record)}
</span>
: <span>{columnConfig.value(record)}</span>}
</TableCell>;
}
)}
</StyledTableRow>;
});
};
return <TabPanel id={`c-PwTablePanel-${index}`} value={value} index={index} className={classes.radialBorder}>
<BasicTable
id={id}
headCells={headCells}
backgroundColor={backgroundColor || '#007AFF'}
color={color || '#fff'}
>
{records && records.length > 0 && <TableBody className={loader ? 'loader' : ''}>
{rowRenders({ records, headers: headCells, navUrl, table })}
</TableBody>}
</BasicTable>
{(dataFetched && (!records || records.length === 0))
&& <p className='mx-3 my-5 d-flex justify-content-center align-items-center h4'>No Records to Display</p>}
</TabPanel>;
};
PwTablePanel.propTypes = {
backgroundColor: PropTypes.string,
classes: PropTypes.string,
color: PropTypes.string,
dataFetched: PropTypes.bool,
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;

View File

@@ -0,0 +1,8 @@
import React from "react";
import PwTablePanel from "./PwTablePanel";
describe("PwTablePanel", () => {
it("renders without error", () => {
});
});

View File

@@ -0,0 +1,3 @@
import PwTablePanel from "./PwTablePanel.jsx";
export default PwTablePanel;

View File

@@ -1,8 +1,10 @@
import { TextField, InputAdornment, SvgIcon } from '@mui/material';
import PropTypes from 'prop-types';
import { makeStyles } from '@mui/styles';
import Search from 'assets/images/SearchIcon';
function SearchBar() {
function SearchBar(props) {
const { onChange } = props;
const useStyles = makeStyles(() => ({
textField: {
@@ -30,8 +32,13 @@ function SearchBar() {
}}
placeholder="Search"
variant="outlined"
onChange={onChange}
/>
</>
);
}
SearchBar.propTypes = {
onChange: PropTypes.func
};
export default SearchBar;

View File

@@ -56,12 +56,13 @@ const IOSSwitch = styled((props) => (
}
}));
export default function Switch({ checked, onChange, name }) {
return <IOSSwitch checked={checked} sx={{ m: 1 }} name={name} onChange={onChange} />;
export default function Switch({ disabled, checked, onChange, name }) {
return <IOSSwitch disabled={disabled} checked={checked} sx={{ m: 1 }} name={name} onChange={onChange} />;
}
Switch.propTypes = {
checked: PropTypes.any,
disabled: PropTypes.bool,
name: PropTypes.any,
onChange: PropTypes.any
};

View File

@@ -67,7 +67,7 @@ export default function TileBasic({ tiles }) {
<Grid container spacing={2}>
{tiles &&
tiles.map((item) => (
<Grid item key={item.name} xs={12} sm={6} md={tiles.length > 4 ? 4 : 6}>
<Grid item key={item._id} xs={12} sm={6} md={tiles.length > 4 ? 4 : 6}>
{item.disabled ? (
<MDBox
key={item.name + item.path}

View File

@@ -39,7 +39,7 @@ export default function Tile({ data, children }) {
Update {data.name} <ArrowRightIcon />
</Box>
</Link>
<Link to={`/setup/inventory/new-item/${data.widgetname}/${data.id}`}>
<Link to={`/setup/inventory/new-item/${data.name}/${data.widgetname}/${data.id}`}>
<Box className={`${classes.box} ${classes.boxEven}`}>
Add New {data.widgetname} <ArrowRightIcon />
</Box>
@@ -49,7 +49,7 @@ export default function Tile({ data, children }) {
Cycle Count <ArrowRightIcon />
</Box>
</Link>
<Link to={`/setup/inventory/browse/${data.widgetname}/${data.id}`}>
<Link to={`/setup/inventory/browse/${data.name}/${data.widgetname}/${data.id}`}>
<Box className={`${classes.box} ${classes.boxEven}`}>
{data.widgetname} List <ArrowRightIcon />
</Box>

View File

@@ -17,26 +17,30 @@ const useStyles = makeStyles(() => ({
'& .MuiSwitch-root': {
margin: '0'
}
},
maxHeight: {
maxHeight: '20rem',
overflow: 'auto'
}
}));
const Toggles = props => {
const {boxSx, 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) => {
@@ -45,24 +49,26 @@ const Toggles = props => {
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 id={id} checked={toggleState[toggle] === undefined ? false : toggleState[toggle]} onChange={e => handleToggle(e, toggle)} />
<Switch disabled={allDisabled} id={id} checked={toggleState[toggle] === undefined ? false : toggleState[toggle]} onChange={e => handleToggle(e, toggle)} />
</MDBox>;
});
return <Grid item id='c-Toggles' xs={xs || 12} md={md || 3} >
return <Grid item className='c-Toggles' xs={xs || 12} md={md || 6} sx={gridStyleOverride}>
<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'}
</Typography>
<Box sx={{ padding: ' 0px 20px' }}>{toggles && switchRenders(toggles)}</Box>
<Box sx={{ padding: ' 0px 20px' }} className={classes.maxHeight}>{toggles && switchRenders(toggles)}</Box>
</MDBox>
</Grid>;
};
Toggles.propTypes = {
allDisabled: PropTypes.bool,
boxSx: PropTypes.object,
inittoggles: PropTypes.string,
gridStyleOverride: PropTypes.object,
selectedToggles: PropTypes.string,
md: PropTypes.number,
onChange: PropTypes.func,
title: PropTypes.string,

View File

@@ -1,9 +1,11 @@
/* eslint-disable indent */
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Grid,
TextField
@@ -116,6 +118,14 @@ function WidgetNestedDataTable({
const dispatch = useDispatch();
const widgetChildren = useSelector(WidgetSelectors.getWidgetsByParentId(data._id));
const [deleteAlertOpen, setDeleteAlertOpen] = React.useState(null);
const handleDeleteAlertClose = () => {
setDeleteAlertOpen(false);
};
const handleDeleteAlertOpen = () => {
setDeleteAlertOpen(true);
};
return (
<>
<Box
@@ -193,19 +203,45 @@ function WidgetNestedDataTable({
}}
onClick={() => {
setSelected(null);
dispatch(
WidgetActions.editWidgetRequest({
loader: 'location-request',
slug: `${API.EDIT_WIDGET_FAMILY}${data._id}`,
deletedId: data._id,
method: 'delete',
type: 'delete'
})
);
handleDeleteAlertOpen();
}}
>
DELETE
</MDButton>
<Dialog
open={deleteAlertOpen}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
onClose={handleDeleteAlertClose}
>
<DialogTitle id="alert-dialog-title">Confirm Delete</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleDeleteAlertClose}>
No
</Button>
<Button
onClick={() => {
dispatch(
WidgetActions.editWidgetRequest({
loader: 'location-request',
slug: `${API.EDIT_WIDGET_FAMILY}${data._id}`,
deletedId: data._id,
method: 'delete',
type: 'delete'
})
);
handleDeleteAlertClose();
}}
>
Yes
</Button>
</DialogActions>
</Dialog>
</Grid>
</Grid>
{open && widgetChildren ? (

View File

@@ -1,9 +1,11 @@
/* PLOP_INJECT_IMPORT */
import PwTablePanel from './PwTablePanel';
import Toggles from './Toggles';
import AllocationManager from './AllocationManager';
export {
/* PLOP_INJECT_EXPORT */
PwTablePanel,
Toggles,
AllocationManager
};

View File

@@ -24,5 +24,7 @@ export default {
GET_WIDGET_FAMILY_BY_INVENTORY: '/widget-family/search-by-inventory?inventory=',
ADD_WIDGET_FAMILY: '/widget-family',
EDIT_WIDGET_FAMILY: '/widget-family/',
GET_LABEL: '/sublevel/filter',
GET_PRODUCT_BY_ID: '/item/filter?inventory=',
GET_ITEMS_BY_INVENTORY: '/item/filter?inventory='
};

View File

@@ -1,17 +1,32 @@
import { useSelector } from 'react-redux';
import { AuthSelectors } from 'redux/AuthRedux';
import { decode } from 'jsonwebtoken';
// import { decode } from 'jsonwebtoken';
import LOGGER from 'services/Logger';
import { useDispatch } from 'react-redux';
import AuthActions from 'redux/AuthRedux';
function decode(token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
return JSON.parse(jsonPayload);
}
const useAuthentication = () => {
const dispatch = useDispatch();
const user = useSelector(AuthSelectors.getUser);
const token = localStorage.getItem('token');
try {
const { exp } = decode(token);
if (exp < (new Date().getTime() + 1) / 1000) {
const decodedToken = token && decode(token);
if (!token || decodedToken.exp < (new Date().getTime() + 1) / 1000) {
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
dispatch(AuthActions.logout());
@@ -22,7 +37,6 @@ const useAuthentication = () => {
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
dispatch(AuthActions.logout());
return { isAuthenticated: false };
}
return { isAuthenticated: !!user };
};

View File

@@ -17,6 +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.css';
import App from 'App';
// Soft UI Context Provider

View File

@@ -13,6 +13,8 @@ import PageLayout from 'layouts/PageLayout';
// Material Dashboard 2 PRO React context
import { useMaterialUIController } from 'context';
import companyImage from 'assets/images/fsr-logo.png';
function AuthLayout({ header, title, description, illustration, children }) {
const [controller] = useMaterialUIController();
const { darkMode } = controller;
@@ -34,12 +36,30 @@ function AuthLayout({ header, title, description, illustration, children }) {
borderRadius="lg"
ml={2}
mt={2}
sx={{ backgroundImage: `url(${illustration})` }}
sx={{
backgroundImage: `url(${illustration})`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center'
}}
/>
</Grid>
<Grid item xs={11} sm={8} md={6} lg={4} xl={3} sx={{ mx: 'auto' }}>
<MDBox display="flex" flexDirection="column" justifyContent="center" height="100vh">
<MDBox py={3} px={3} textAlign="center">
<MDBox
display={{ xs: 'none', lg: 'flex' }}
width="350px"
height="117px"
borderRadius="lg"
ml={2}
my={5}
sx={{
backgroundImage: `url(${companyImage})`,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center'
}}
/>
{!header ? (
<>
<MDBox mb={1} textAlign="center">

67
src/main.css Normal file
View File

@@ -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);
}
}

View File

@@ -51,7 +51,7 @@ const useStyles = makeStyles({
function AddNewItem() {
const classes = useStyles();
const { widgetName, inventoryId, itemId } = useParams();
const { widgetName, inventoryName, inventoryId, itemId } = useParams();
const getInitialFormValues = (data) => {
return data && data._id === itemId
? {
@@ -135,10 +135,6 @@ function AddNewItem() {
);
}, []);
const [pFam, setPFam] = React.useState(null);
const primaryFamily = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
const secondaryFamily = useSelector(WidgetSelectors.getWidgetsByParentId(pFam));
const formik = useFormik({
enableReinitialize: true,
initialValues: itemData,
@@ -217,17 +213,23 @@ function AddNewItem() {
}
});
const primaryFamily = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
const secondaryFamily = useSelector(
WidgetSelectors.getWidgetsByParentId(formik.values.primaryWidgetFamilyId)
);
return (
<>
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title={`${widgetName} Details`}
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
{ name: 'Inventory', path: '/setup/inventory' },
{ name: `${widgetName || 'Item'}` },
{ name: `Add New ${widgetName || 'Item'}` }
{ name: `${inventoryName || 'Inventory'}` },
{ name: `${itemId ? 'Edit' : 'Add'} ${widgetName || 'Item'}` }
]}
/>
<Box mx={3} my={3}>
@@ -246,7 +248,12 @@ function AddNewItem() {
variant="outlined"
value={formik.values.commonName}
error={formik.touched.commonName && Boolean(formik.errors.commonName)}
helperText={formik.touched.commonName && formik.errors.commonName}
helperText={
formik.touched.commonName &&
formik.errors.commonName && (
<div style={{ color: 'red' }}>{formik.errors.commonName}</div>
)
}
onChange={formik.handleChange}
/>
</Box>
@@ -341,7 +348,12 @@ function AddNewItem() {
variant="outlined"
value={formik.values.formalName}
error={formik.touched.formalName && Boolean(formik.errors.formalName)}
helperText={formik.touched.formalName && formik.errors.formalName}
helperText={
formik.touched.formalName &&
formik.errors.formalName && (
<div style={{ color: 'red' }}>{formik.errors.formalName}</div>
)
}
onChange={formik.handleChange}
/>
</Box>
@@ -440,10 +452,7 @@ function AddNewItem() {
formik.touched.primaryWidgetFamilyId &&
Boolean(formik.errors.primaryWidgetFamilyId)
}
onChange={(e, ...rest) => {
setPFam(e.target.value);
formik.handleChange(e, ...rest);
}}
onChange={formik.handleChange}
>
<MenuItem key={'none'} value={''}>
None Selected
@@ -497,7 +506,7 @@ function AddNewItem() {
{itemId ? (
<ImageUploadMultiple
multiple
heading="Upload Product Image"
heading={`Upload ${widgetName} Image`}
accept="image/*"
images={formik.values.images}
setImages={(images) => {
@@ -507,7 +516,7 @@ function AddNewItem() {
) : (
<ImageUploadMultiple
multiple
heading="Upload Product Image"
heading={`Upload ${widgetName} Image`}
accept="image/*"
images={formik.values.images}
setImages={(images) => {
@@ -516,7 +525,7 @@ function AddNewItem() {
/>
)}
</Box>
<Box
{/* <Box
sx={{
display: 'flex',
justifyContent: 'center',
@@ -539,7 +548,7 @@ function AddNewItem() {
<MDButton disabled size="large" color="primary" variant="outlined">
import
</MDButton>
</Box>
</Box> */}
<Box
component="div"
sx={{
@@ -614,11 +623,18 @@ function AddNewItem() {
marginBottom: '30px'
}}
>
<MDButton size="medium" color="error" variant="outlined">
<MDButton
size="medium"
color="error"
variant="outlined"
onClick={() => {
navigate('/setup/inventory');
}}
>
Cancel
</MDButton>
<MDButton size="medium" color="primary" variant="contained" type="submit">
Add {widgetName}
Save
</MDButton>
</Box>
</Box>

View File

@@ -18,7 +18,7 @@ import AuthActions from 'redux/AuthRedux';
import { API } from 'constant';
// Image
import bgImage from 'assets/images/illustrations/illustration-reset.jpg';
import bgImage from 'assets/images/roofing-company-houston.jpg';
import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
@@ -35,8 +35,7 @@ function LoginScreen() {
rememberMe: true
},
validationSchema: schema.login,
onSubmit: (values, { resetForm, setSubmitting }) =>
{
onSubmit: (values, { resetForm, setSubmitting }) => {
const onFailedLogin = (errorMessage) => {
resetForm();
setSubmitting(false);

View File

@@ -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';
@@ -29,8 +29,9 @@ import DateTimeInput from 'components/DateTimePicker';
import MDInput from 'components/MDInput';
import { API } from 'constant';
import UserIcon from 'assets/images/userIcon.png';
import BlankImage from 'assets/images/blank-profile-picture.webp';
import EditIcon from 'assets/images/edit-icon.png';
import Breadcrumbs from 'components/Breadcrumbs';
const useStyles = makeStyles(() => ({
labelSize: {
@@ -72,11 +73,14 @@ 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 [loader, setLoader] = useState();
// const [selectedPermissions, setSelectedPermissions] = useState({});
useEffect(() => {
if (context === 'edit') {
@@ -86,16 +90,52 @@ function CreateEditUser(props) {
} else {
setEditedUser(editedUser);
setSelectedRoles(editedUser.roles);
editedUser.image_url && setUploadedImg(editedUser.image_url);
}
}
}, []);
useEffect(() => {
(!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'}));
(!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'
})
);
(!visibilities || visibilities.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({
@@ -110,10 +150,11 @@ function CreateEditUser(props) {
actions: '',
visibilities: '',
isActive: true,
createdBy: currentUser ? currentUser.fullName : '',
createdAt: new Date(),
updatedBy: currentUser ? currentUser.fullName : '',
updatedAt: new Date()
image: '',
createdBy: '',
createdAt: '',
updatedBy: '',
updatedAt: ''
} : {
fullName: editedUser ? editedUser.fullName : '',
phoneNumber: editedUser ? editedUser.phoneNumber : '',
@@ -125,108 +166,207 @@ function CreateEditUser(props) {
actions: editedUser?.permissions?.actions ? editedUser.permissions.actions.join(',') : '',
visibilities: editedUser?.permissions?.allowedUIModules ? editedUser.permissions.allowedUIModules.join(',') : '',
isActive: editedUser && editedUser.isActive !== undefined ? editedUser.isActive : true,
image: editedUser ? editedUser.image_url : EditIcon,
createdBy: editedUser ? editedUser.createdBy?.fullName : '',
createdAt: editedUser ? editedUser.createdAt : '',
updatedBy: editedUser ? editedUser.updatedBy?.fullName : '',
updatedAt: editedUser ? editedUser.updatedAt : ''
},
validationSchema: schema.createUser,
onSubmit: (values, { setSubmitting }) =>
{
onSubmit: (values, { setSubmitting }) => {
const onValidationFailed = () => {
setLoader(false);
setSubmitting(false);
};
const onSuccessfulSubmission = () => {
setLoader(false);
navigate('/setup/users-access');
};
const adaptPayload = values => {
const valuesClone = {...values};
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.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(',') : [];
valuesClone.permissions.allowedUIModules = values.visibilities
? values.visibilities.split(',')
: [];
delete valuesClone.inventories;
delete valuesClone.warehouses;
delete valuesClone.actions;
delete valuesClone.visibilities;
return valuesClone;
valuesClone.permissions = JSON.stringify(valuesClone.permissions);
valuesClone.roles = selectedRoles && selectedRoles.length > 0 ? selectedRoles.map(role => role._id) : [];
const formData = new FormData();
Object.keys(valuesClone).forEach(key => formData.append(key, valuesClone[key]));
uploadedImg && formData.append('image', uploadedImg);
setLoader(true);
return formData;
};
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,
slug:
context === 'edit' ? API.UPDATE_USER.replace(':id', editedUser._id) : API.CREATE_USER,
method: 'post',
contentType: false,
processData: false,
data: adaptPayload(values),
onValidationFailed,
onSuccessfulSubmission,
toastMessage: context === 'edit' ? 'Updated user __placeholder__successfully' : 'Added user __placeholder__successfully'
toastMessage:
context === 'edit'
? 'Updated user __placeholder__successfully'
: 'Added user __placeholder__successfully'
})
);
}
});
const handleMultiSelectChange = e => {
const handleMultiSelectChange = (e) => {
const uniqueRoles = [];
e.target.value.forEach(role => {
const roleIndex = uniqueRoles.findIndex(uRole => uRole._id === role._id);
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());
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) {
setUploadedImg(file);
}
};
return (
<DashboardLayout className={classes.createEditUserGlobal}>
<DashboardNavbar />
<MDBox component='form' role='form' px={2} sx={{ backgroundColor: '#fff' }} onSubmit={formik.handleSubmit}>
<MDBox mx={4} sx={{ border: '1px solid #C4C4C4', borderRadius: '4px', padding: '30px' }}>
<Breadcrumbs
title="User Details"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
{ name: 'User Access', path: '/setup/users-access' },
{ name: 'User Details' }
]}
/>
<MDBox component="form" role="form" px={2} className={loader ? " loader" : ""} onSubmit={formik.handleSubmit}>
<MDBox
mx={4}
sx={{
border: '1px solid #C4C4C4',
borderRadius: '4px',
padding: '30px',
backgroundColor: '#fff'
}}
>
<MDBox sx={{ width: '50%', margin: 'auto' }}>
<MDBox sx={{ width: '120px', margin: 'auto', position: 'relative' }}>
<img src={UserIcon} alt='img' />
<MDBox sx={{ position: 'absolute', bottom: '0', right: '0', cursor: 'pointer' }}>
<img src={EditIcon} alt='img' />
<img src={uploadedImg ? typeof uploadedImg === 'string' ? uploadedImg : URL.createObjectURL(uploadedImg) : BlankImage}
alt='img' width='120' height='120' style={{borderRadius: '50%'}} onError={() => setUploadedImg(BlankImage)} />
<MDBox sx={{ position: 'absolute', bottom: '0', right: '0' }}>
<label htmlFor="image" style={{ cursor: 'pointer' }}>
<img src={EditIcon} />
</label>
<input id='image' name='image' type="file" className='d-none' accept="image/png, image/gif, image/jpeg"
onChange={handleFileChange} />
</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 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} />
<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} />
<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}>
<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} />
<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}>
<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)}
helperText={formik.touched.phoneNumber && formik.errors.phoneNumber} onChange={formik.handleChange} />
<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' }}>
@@ -234,16 +374,25 @@ function CreateEditUser(props) {
<Select
multiple
displayEmpty
name='roles'
name="roles"
value={selectedRoles}
input={<OutlinedInput />}
error={formik.touched.roles && Boolean(formik.errors.roles)}
renderValue={() => selectedRoles?.length === 0 ? 'Please select a role' : selectedRoles?.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%'}}
sx={{ width: '100%' }}
onChange={handleMultiSelectChange}
>
{roles && roles.map((role, key) => <MenuItem key={key} value={role}>{role.name}</MenuItem>)}
{roles &&
roles.map((role, key) => (
<MenuItem key={key} value={role}>
{role.name}
</MenuItem>
))}
</Select>
</Box>
<Box
@@ -258,7 +407,7 @@ function CreateEditUser(props) {
}}
>
<Box
component='div'
component="div"
sx={{
fontSize: '16px',
lineHeight: '20px',
@@ -278,7 +427,11 @@ function CreateEditUser(props) {
left: '20px'
}}
>
<Switch name='isActive' checked={formik.values.isActive} onChange={formik.handleChange} />
<Switch
name="isActive"
checked={formik.values.isActive}
onChange={formik.handleChange}
/>
</Box>
</Box>
</Box>
@@ -287,28 +440,42 @@ 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='createdBy' type='text' value={formik.values.createdBy} 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 &amp; Time
</Box>
<DateTimeInput disabled name='createdAt' value={formik.values.createdAt} />
<DateTimeInput disabled name='createdAt' value={new Date(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='updatedBy' type='text' value={formik.values.updatedBy} 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 &amp; Time
</Box>
<DateTimeInput disabled name='updatedAt' value={formik.values.updatedAt} />
<DateTimeInput disabled name='updatedAt' value={new Date(formik.values.updatedAt)} />
</Grid>
</Grid>
</Grid>
@@ -316,33 +483,81 @@ function CreateEditUser(props) {
</MDBox>
</MDBox>
<Grid container spacing={4} sx={{ marginTop: '-6px' }}>
<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)} />
<AllocationManager
allDisabled={selectedRoles?.length > 0}
name="warehouses"
gridStyleOverride={{ paddingLeft: '4rem !important' }}
allocatedList={formik.values.warehouses}
list={warehouses}
matchProp={{ a: '_id' }}
title="Warehouse"
onChange={val => {
val = val?.map(obj => obj._id).join(',');
// setSelectedPermissions({...selectedPermissions, warehouses: val});
formik.handleChange('warehouses')(val);
}}
/>
<AllocationManager
allDisabled={selectedRoles?.length > 0}
name="inventories"
gridStyleOverride={{ paddingRight: '2rem' }}
allocatedList={formik.values.inventories}
list={inventories}
matchProp={{ a: '_id' }}
title="Inventory"
onChange={val => {
val = val?.map(obj => obj._id).join(',');
// setSelectedPermissions({...selectedPermissions, inventories: val});
formik.handleChange('inventories')(val);
}}
/>
</Grid>
<Grid container spacing={2} sx={{ marginTop: '12px', paddingLeft: '2rem' }}>
<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 container spacing={4} sx={{ marginTop: '12px'}}>
<Toggles
allDisabled={selectedRoles?.length > 0}
name="actions"
gridStyleOverride={{ paddingLeft: '4rem !important' }}
title="Actions"
toggles={actions}
selectedToggles={formik.values.actions}
onChange={val => {
val = Object.keys(val).join(',');
// setSelectedPermissions({...selectedPermissions, actions: val});
formik.handleChange('actions')(val);
}}
/>
<Toggles
allDisabled={selectedRoles?.length > 0}
name="visibilities"
gridStyleOverride={{ paddingRight: '2rem' }}
title="Application"
toggles={visibilities}
selectedToggles={formik.values.visibilities}
onChange={val => {
val = Object.keys(val).join(',');
// setSelectedPermissions({...selectedPermissions, visibilities: 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>

View File

@@ -0,0 +1,218 @@
import React from 'react';
import MDBox from 'components/MDBox';
import DashboardNavbar from 'components/DashboardNavbar';
import Footer from 'components/Footer';
import DashboardLayout from 'layouts/DashboardLayout';
import { makeStyles } from '@mui/styles';
import { Box, Grid } from '@mui/material';
import MDButton from 'components/Button';
import TransferList from 'components/MDTransferList';
import DateTimeInput from 'components/DateTimePicker';
import Switch from 'components/Switch';
import MDTypography from 'components/MDTypography';
import MDInput from 'components/MDInput';
import TextareaAutosize from '@mui/material/TextareaAutosize';
import Typography from '@mui/material/Typography';
const useStyles = makeStyles(() => ({
labelSize: {
fontSize: '16px',
letterSpacing: '0.01em',
color: '#000',
marginBottom: '4px'
},
boxWrap: {
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '3px solid #007aff',
display: 'inline-block',
padding: '12px',
borderRadius: '4px'
},
noLegend: {
display: 'none'
},
fullWidth: {
width: '100%',
borderColor: '#d2d6da',
borderRadius: '0.375rem'
},
switchSpacer: {
margin: '0',
'& .MuiFormControlLabel-root': {
margin: '0'
},
'& .MuiSwitch-root': {
margin: '0'
}
}
}));
function CreateEditUserRole() {
const classes = useStyles();
return (
<DashboardLayout>
<DashboardNavbar />
<MDBox px={2} py={3}>
<Grid container spacing={2} className={classes.margin}>
<Grid item xs={8} md={8}>
<Grid container spacing={2} className={classes.margin}>
<Grid item xs={12}>
<Box component="div" sx={{}} className={classes.labelSize}>
Warehouse name
</Box>
<MDInput fullWidth name="warehousename" type="text" variant="outlined" />
</Grid>
<Grid item xs={12}>
<Box component="div" className={classes.labelSize}>
Description
</Box>
<TextareaAutosize className={classes.fullWidth} minRows={5} />
</Grid>
<Grid item xs={12}>
<Grid container spacing={2} className={classes.margin}>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Created By
</Box>
<MDInput fullWidth name="warehousename" type="text" variant="outlined" />
</Grid>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Date &amp; Time
</Box>
<DateTimeInput />
</Grid>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Last Updated by
</Box>
<MDInput fullWidth name="warehousename" type="text" variant="outlined" />
</Grid>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Date &amp; Time
</Box>
<DateTimeInput />
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={4}>
<Box component="div" className={classes.labelSize}>
Choose avatar component here
</Box>
</Grid>
</Grid>
<Grid container spacing={4} sx={{ marginTop: '-6px' }}>
<Grid item xs={12} md={4}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
padding: '12px',
borderRadius: '4px'
}}
>
<Typography gutterBottom variant="h6" component="div">
Warehouse
</Typography>
<TransferList />
</MDBox>
</Grid>
<Grid item xs={12} md={4}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
padding: '12px',
borderRadius: '4px'
}}
>
<Typography gutterBottom variant="h6" component="div">
Warehouse
</Typography>
<TransferList />
</MDBox>
</Grid>
</Grid>
<Grid container spacing={2} sx={{ marginTop: '12px' }}>
<Grid item xs={12} md={3}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
padding: '12px',
borderRadius: '4px'
}}
>
<Typography gutterBottom variant="h6" component="div">
Process
</Typography>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
>
<MDTypography variant="body2">Sidenav Mini</MDTypography>
<Switch checked />
</MDBox>
</MDBox>
</Grid>
<Grid item xs={12} md={3}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
padding: '12px',
borderRadius: '4px'
}}
>
<Typography gutterBottom variant="h6" component="div">
Application
</Typography>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
>
<MDTypography variant="body2">Sidenav Mini</MDTypography>
<Switch checked />
</MDBox>
</MDBox>
</Grid>
</Grid>
<MDBox
display="flex"
justifyContent="center"
alignItems="center"
lineHeight={1}
sx={{ marginBottom: '15px', marginTop: '24px' }}
>
<MDButton
size="medium"
color="primary"
variant="outlined"
type="submit"
sx={{ marginRight: '15px' }}
>
Cancel
</MDButton>
<MDButton size="medium" color="primary" variant="contained">
Save
</MDButton>
</MDBox>
</MDBox>
<Footer />
</DashboardLayout>
);
}
export default CreateEditUserRole;

View File

@@ -1,365 +0,0 @@
import React from 'react';
import MDBox from 'components/MDBox';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import { makeStyles } from '@mui/styles';
import { Box, Grid } from '@mui/material';
import MDButton from 'components/Button';
import TransferList from 'components/MDTransferList';
import DateTimeInput from 'components/DateTimePicker';
import Switch from 'components/Switch';
import MDTypography from 'components/MDTypography';
import MDInput from 'components/MDInput';
import Typography from '@mui/material/Typography';
import UserIcon from 'assets/images/userIcon.png';
import EditIcon from 'assets/images/edit-icon.png';
import OutlinedInput from '@mui/material/OutlinedInput';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
const useStyles = makeStyles(() => ({
labelSize: {
fontSize: '16px',
letterSpacing: '0.01em',
color: '#000',
marginBottom: '4px'
},
boxWrap: {
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '3px solid #007aff',
display: 'inline-block',
padding: '12px',
borderRadius: '4px'
},
noLegend: {
display: 'none'
},
fullWidth: {
width: '100%',
borderColor: '#d2d6da',
borderRadius: '0.375rem'
},
switchSpacer: {
margin: '0',
'& .MuiFormControlLabel-root': {
margin: '0'
},
'& .MuiSwitch-root': {
margin: '0'
}
}
}));
function CreateUserRole() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value }
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
);
};
return (
<DashboardLayout>
<DashboardNavbar />
<MDBox px={2} sx={{ backgroundColor: '#fff' }}>
<MDBox mx={2} 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" />
<MDBox sx={{ position: 'absolute', bottom: '0', right: '0', cursor: 'pointer' }}>
<img src={EditIcon} alt="img" />
</MDBox>
</MDBox>
<MDBox sx={{ marginBottom: '24px' }}>
<Box component="div" sx={{}} className={classes.labelSize}>
User Name
</Box>
<MDInput fullWidth name="warehousename" type="text" variant="outlined" />
</MDBox>
<MDBox sx={{ marginBottom: '24px' }}>
<Box component="div" sx={{}} className={classes.labelSize}>
Phone Number
</Box>
<MDInput fullWidth name="warehousename" type="text" variant="outlined" />
</MDBox>
<MDBox sx={{ marginBottom: '24px' }}>
<Box component="div" sx={{}} className={classes.labelSize}>
Role
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', columnGap: '20px' }}>
<Box sx={{ width: '70%' }}>
<Select
multiple
displayEmpty
value={personName}
input={<OutlinedInput />}
renderValue={(selected) => {
if (selected.length === 0) {
return 'Placeholder';
}
return selected.join(', ');
}}
inputProps={{ 'aria-label': 'Without label' }}
sx={{
width: '100%'
}}
onChange={handleChange}
>
<MenuItem disabled value="">
Placeholder
</MenuItem>
<MenuItem>Lorem Ipsum</MenuItem>
</Select>
</Box>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '30%',
border: '1px solid #C4C4C4',
borderRadius: '4px'
}}
>
<Box
component="div"
sx={{
fontSize: '16px',
lineHeight: '20px',
letterSpacing: '0.01em',
textTransform: 'capitalize',
color: '#000',
marginLeft: '10px'
}}
>
Access
</Box>
<Box
sx={{
display: 'flex',
alignItems: 'center',
position: 'relative',
left: '20px'
}}
>
<Switch checked />
</Box>
</Box>
</Box>
</MDBox>
<Grid container spacing={2} className={classes.margin}>
<Grid item xs={12}>
<Grid container spacing={2} className={classes.margin}>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Created By
</Box>
<MDInput fullWidth name="warehousename" type="text" variant="outlined" />
</Grid>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Date &amp; Time
</Box>
<DateTimeInput />
</Grid>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Last Updated by
</Box>
<MDInput fullWidth name="warehousename" type="text" variant="outlined" />
</Grid>
<Grid item xs={6}>
<Box component="div" className={classes.labelSize}>
Date &amp; Time
</Box>
<DateTimeInput />
</Grid>
</Grid>
</Grid>
</Grid>
</MDBox>
</MDBox>
<Grid container spacing={4} sx={{ marginTop: '-6px' }}>
<Grid item xs={12} md={6}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
padding: '12px',
borderRadius: '4px'
}}
>
<Typography gutterBottom variant="h6" component="div">
Warehouse
</Typography>
<TransferList />
</MDBox>
</Grid>
<Grid item xs={12} md={6}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
padding: '12px',
borderRadius: '4px'
}}
>
<Typography gutterBottom variant="h6" component="div">
Warehouse
</Typography>
<TransferList />
</MDBox>
</Grid>
</Grid>
<Grid container spacing={2} sx={{ marginTop: '12px' }}>
<Grid item xs={12} md={3}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
borderRadius: '4px'
}}
>
<Typography
gutterBottom
variant="h6"
component="div"
sx={{
borderBottom: '1px solid #c2c2c2',
padding: '10px 20px',
marginBottom: '20px'
}}
>
Process
</Typography>
<Box sx={{ padding: ' 0px 20px' }}>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
sx={{ marginBottom: '20px !important' }}
>
<MDTypography variant="body2">Stock Tracking</MDTypography>
<Switch checked />
</MDBox>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
sx={{ marginBottom: '20px !important' }}
>
<MDTypography variant="body2">Replenishment</MDTypography>
<Switch checked />
</MDBox>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
sx={{ marginBottom: '20px !important' }}
>
<MDTypography variant="body2">Alerting</MDTypography>
<Switch checked />
</MDBox>
</Box>
</MDBox>
</Grid>
<Grid item xs={12} md={3}>
<MDBox
sx={{
backgroundColor: '#fff',
border: '1px solid #c2c2c2',
borderTop: '7px solid #007aff',
borderRadius: '4px'
}}
>
<Typography
gutterBottom
variant="h6"
component="div"
sx={{
borderBottom: '1px solid #c2c2c2',
padding: '10px 20px',
marginBottom: '20px'
}}
>
Application
</Typography>
<Box sx={{ padding: ' 0px 20px' }}>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
sx={{ marginBottom: '20px !important' }}
>
<MDTypography variant="body2">Home</MDTypography>
<Switch checked />
</MDBox>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
sx={{ marginBottom: '20px !important' }}
>
<MDTypography variant="body2">Setup</MDTypography>
<Switch checked />
</MDBox>
<MDBox
display="flex"
justifyContent="space-between"
alignItems="center"
lineHeight={1}
className={classes.switchSpacer}
sx={{ marginBottom: '20px !important' }}
>
<MDTypography variant="body2">Reports</MDTypography>
<Switch checked />
</MDBox>
</Box>
</MDBox>
</Grid>
</Grid>
<MDBox
display="flex"
justifyContent="center"
alignItems="center"
lineHeight={1}
sx={{ marginBottom: '15px', marginTop: '45px', paddingBottom: '30px' }}
>
<MDButton
size="medium"
color="error"
variant="outlined"
type="submit"
sx={{ marginRight: '15px' }}
>
Cancel
</MDButton>
<MDButton size="medium" color="primary" variant="contained">
Save
</MDButton>
</MDBox>
</MDBox>
</DashboardLayout>
);
}
export default CreateUserRole;

View File

@@ -12,7 +12,9 @@ import {
DialogTitle,
DialogContent,
TextField,
DialogActions
DialogActions,
Button,
DialogContentText
} from '@mui/material';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
@@ -37,6 +39,8 @@ import Breadcrumbs from 'components/Breadcrumbs';
import { WarehouseSelectors } from 'redux/WarehouseRedux';
import { InventorySelectors } from 'redux/InventoryRedux';
import InventoryActions from 'redux/InventoryRedux';
import { useNavigate } from 'react-router-dom';
import schema from 'services/ValidationServices';
const bottomButtonStyling = {
width: '100%',
@@ -317,6 +321,10 @@ const WarehouseNestedDetails = () => {
function EditWarehouseDetails() {
const { warehouseId } = useParams();
const navigate = useNavigate();
const navigateTo = (to) => {
navigate(to);
};
const warehouseData = useSelector(WarehouseSelectors.getWarehouseDetailById(warehouseId));
const inventoryTypes = useSelector(InventorySelectors.getInventoryDetail);
@@ -347,11 +355,11 @@ function EditWarehouseDetails() {
initialValues: {
warehousename: warehouseData.name,
address: warehouseData.address || '',
preferredInventories: warehouseData.preferredInventories || [],
preferredInventories: [...warehouseData.preferredInventories] || [],
specs: warehouseData.specs || '',
image: warehouseData.image_url ? [{ src: warehouseData.image_url }] : []
},
// validationSchema: schema.warehouseForm,
validationSchema: schema.warehouseForm,
onSubmit: (values) => {
dispatch(
WarehouseActions.editWarehouseAction({
@@ -370,11 +378,20 @@ function EditWarehouseDetails() {
}
});
const [deleteAlertOpen, setDeleteAlertOpen] = React.useState(null);
const handleDeleteAlertClose = () => {
setDeleteAlertOpen(false);
};
const handleDeleteAlertOpen = () => {
setDeleteAlertOpen(true);
};
return (
<>
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="Edit Warehouse Details"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
@@ -385,17 +402,6 @@ function EditWarehouseDetails() {
<Box mx={3} my={3}>
<form onSubmit={formik.handleSubmit}>
<Box sx={{ backgroundColor: '#fff', padding: '30px' }}>
<Box
component="div"
sx={{
fontSize: '22px',
letterSpacing: '0.01em',
color: '#000',
marginBottom: '30px'
}}
>
Form to Input
</Box>
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={6}>
<Box component="div" sx={{ marginBottom: '15px' }}>
@@ -417,7 +423,12 @@ function EditWarehouseDetails() {
variant="outlined"
value={formik.values.warehousename}
error={formik.touched.warehousename && Boolean(formik.errors.warehousename)}
helperText={formik.touched.warehousename && formik.errors.warehousename}
helperText={
formik.touched.warehousename &&
formik.errors.warehousename && (
<div style={{ color: 'red' }}>{formik.errors.warehousename}</div>
)
}
onChange={formik.handleChange}
/>
</Box>
@@ -440,7 +451,12 @@ function EditWarehouseDetails() {
variant="outlined"
value={formik.values.address}
error={formik.touched.address && Boolean(formik.errors.address)}
helperText={formik.touched.address && formik.errors.address}
helperText={
formik.touched.address &&
formik.errors.address && (
<div style={{ color: 'red' }}>{formik.errors.address}</div>
)
}
onChange={formik.handleChange}
/>
</Box>
@@ -459,7 +475,6 @@ function EditWarehouseDetails() {
<Select
multiple
select
disabled
fullWidth
variant="outlined"
name="preferredInventories"
@@ -492,6 +507,9 @@ function EditWarehouseDetails() {
);
}}
>
<MenuItem disabled key={'none'} value={''}>
None Selected
</MenuItem>
{inventoryTypes &&
inventoryTypes.map((inventory) => (
<MenuItem key={inventory._id} value={inventory._id}>
@@ -524,7 +542,49 @@ function EditWarehouseDetails() {
/>
</Box>
</Grid>
<Grid item xs={12} sm={6} md={6}>
<Grid item sx={{ textAlign: 'end' }} xs={12} sm={6} md={6}>
<MDButton
size="large"
color="error"
variant="outlined"
onClick={handleDeleteAlertOpen}
>
Delete Warehouse
</MDButton>
<Dialog
open={deleteAlertOpen}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
onClose={handleDeleteAlertClose}
>
<DialogTitle id="alert-dialog-title">Confirm Warehouse Delete</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this warehouse?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleDeleteAlertClose}>
No
</Button>
<Button
onClick={() => {
dispatch(
WarehouseActions.deleteWarehouseAction({
loader: 'loading-request',
slug: '/warehouse/',
method: 'delete',
warehouseId: warehouseData._id,
navigateTo
})
);
handleDeleteAlertClose();
}}
>
Yes
</Button>
</DialogActions>
</Dialog>
<Box sx={{ marginTop: '30px' }}>
<ImageUploadSingle
heading="Upload Warehouse Image"
@@ -545,6 +605,16 @@ function EditWarehouseDetails() {
columnGap: '20px'
}}
>
<MDButton
size="medium"
color="error"
variant="outlined"
onClick={() => {
navigate('/setup/warehouse');
}}
>
CANCEL
</MDButton>
{/* ---edit-- */}
<MDButton size="large" color="primary" variant="outlined" type="submit">
SAVE

View File

@@ -37,7 +37,7 @@ function HomepageScreen() {
return (
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs route={[{ name: 'Home', path: '/home' }]} />
<Breadcrumbs title="Warehouse Management System" route={[{ name: 'Home', path: '/home' }]} />
<TileBasic tiles={data} />
</DashboardLayout>
);

View File

@@ -1,7 +1,24 @@
import React from 'react';
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import { Box, Grid, MenuItem, Select } from '@mui/material';
import {
Box,
FormControlLabel,
Grid,
MenuItem,
Radio,
RadioGroup,
useRadioGroup,
Select,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Button
} from '@mui/material';
import { styled } from '@mui/material/styles';
import MDInput from 'components/MDInput';
import Switch from 'components/Switch';
import MDTypography from 'components/MDTypography';
@@ -18,6 +35,9 @@ import { useParams } from 'react-router-dom';
import { InventorySelectors } from 'redux/InventoryRedux';
import { useNavigate } from 'react-router-dom';
import WidgetNestedDataTable from 'components/WidgetNestedDataTable';
import { GetIconFromSlug } from 'utils/inventorySlugTools';
import { iconSlugs } from 'utils/inventorySlugTools';
import AddCircleIcon from '@mui/icons-material/AddCircle';
const customStyles = {
labelSize: {
@@ -63,13 +83,34 @@ const definedPolicies = [
{
text: 'Alerting',
key: 'alerting'
},
{
text: 'Location',
key: 'preferredLocations'
}
// {
// text: 'Location',
// key: 'preferredLocations'
// }
];
const StyledFormControlLabel = styled((props) => <FormControlLabel {...props} />)(
({ theme, checked }) => ({
'.MuiFormControlLabel-label': checked && {
border: `1px solid ${theme.palette.primary.light}`,
borderRadius: '10px'
}
})
);
function MyFormControlLabel(props) {
const radioGroup = useRadioGroup();
let checked = false;
if (radioGroup) {
checked = radioGroup.value === props.value;
}
return <StyledFormControlLabel checked={checked} {...props} />;
}
MyFormControlLabel.propTypes = {
value: PropTypes.any
};
function InventoryScreen() {
const dispatch = useDispatch();
const navigate = useNavigate();
@@ -80,18 +121,16 @@ function InventoryScreen() {
};
const currentInventoryData = useSelector(InventorySelectors.getInventoryDetailById(inventoryId));
LOGGER.log({ currentInventoryData });
// const [inventoryAllData, setInventoryAllData] = useState([]);
// const initialInventoryName='';
const [iconSelect, setIconSelect] = React.useState('');
// useEffect(() => {
// const filterData = inventoryData.filter((item) => item._id === inventoryId);
// console.log('filterData', filterData);
// setInitialInventoryName(filterData[0].name)
// setInventoryAllData(filterData[0].widgetName);
// }, []);
// LOGGER.log('initialInventoryName', initialInventoryName);
useEffect(() => {
if (inventoryId) {
const iconFilter = iconSlugs.filter(
(iconSlug) => iconSlug === currentInventoryData.icon_slug
);
setIconSelect(GetIconFromSlug(iconFilter[0]));
}
}, [inventoryId]);
/* eslint-disable indent */
const formik = useFormik({
@@ -99,6 +138,7 @@ function InventoryScreen() {
? {
name: currentInventoryData.name,
widgetName: currentInventoryData.widgetName,
icon_slug: currentInventoryData.icon_slug,
policies: {
orderTracking: currentInventoryData.policies.orderTracking,
alerting: currentInventoryData.policies.alerting,
@@ -110,11 +150,12 @@ function InventoryScreen() {
: {
name: '',
widgetName: '',
icon_slug: '',
policies: {
orderTracking: false,
alerting: false,
replenishment: false,
preferredLocations: false,
preferredLocations: false, // TODO: change later
inventory_process: 'CCR'
}
},
@@ -130,7 +171,7 @@ function InventoryScreen() {
navigateTo,
data: {
...values,
icon_slug: 'testslug'
icon_slug: values.icon_slug
}
})
)
@@ -142,18 +183,39 @@ function InventoryScreen() {
navigateTo,
data: {
...values,
icon_slug: 'testslug'
icon_slug: values.icon_slug
}
})
);
}
});
LOGGER.log('Form values', formik.values);
const [deleteAlertOpen, setDeleteAlertOpen] = React.useState(null);
const handleDeleteAlertClose = () => {
setDeleteAlertOpen(false);
};
const handleDeleteAlertOpen = () => {
setDeleteAlertOpen(true);
};
const [iconSlugOpen, setIconSlugOpen] = React.useState(null);
const handleIconSlugClose = () => {
setIconSlugOpen(false);
};
const handleIconSlugOpen = () => {
setIconSlugOpen(true);
};
const handleIconSlug = (value) => {
const iconFilter = iconSlugs.filter((iconSlug) => iconSlug === value);
setIconSelect(GetIconFromSlug(iconFilter[0]));
setIconSlugOpen(false);
};
return (
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="Inventory Details"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
@@ -186,7 +248,10 @@ function InventoryScreen() {
variant="outlined"
value={formik.values.name}
error={formik.touched.name && Boolean(formik.errors.name)}
helpertText={formik.touched.name && formik.errors.name}
helperText={
formik.touched.name &&
formik.errors.name && <div style={{ color: 'red' }}>{formik.errors.name}</div>
}
onChange={formik.handleChange}
/>
</Box>
@@ -202,7 +267,12 @@ function InventoryScreen() {
variant="outlined"
value={formik.values.widgetName}
error={formik.touched.widgetName && Boolean(formik.errors.widgetName)}
helpertext={formik.touched.widgetName && formik.errors.widgetName}
helperText={
formik.touched.widgetName &&
formik.errors.widgetName && (
<div style={{ color: 'red' }}>{formik.errors.widgetName}</div>
)
}
onChange={formik.handleChange}
/>
</Grid>
@@ -257,8 +327,107 @@ function InventoryScreen() {
</div>
</MDBox>
</Grid>
<Grid item xs={12} sm={6} md={6}>
icon slugs selector
<Grid item sx={{ textAlign: 'right' }} xs={12} sm={6} md={6}>
<MDButton
size="large"
color="error"
variant="outlined"
sx={{
marginTop: '20px'
}}
onClick={handleDeleteAlertOpen}
>
Delete Inventory
</MDButton>
<Dialog
open={deleteAlertOpen}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
onClose={handleDeleteAlertClose}
>
<DialogTitle id="alert-dialog-title">Confirm Inventory Delete</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this inventory?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleDeleteAlertClose}>
No
</Button>
<Button
onClick={() => {
dispatch(
InventoryActions.deleteInventoryAction({
loader: 'loading-request',
slug: '/inventory/' + inventoryId,
method: 'delete',
inventoryId,
navigateTo
})
);
handleDeleteAlertClose();
}}
>
Yes
</Button>
</DialogActions>
</Dialog>
<Box
sx={{
height: 300,
padding: '20px',
marginTop: '80px',
textAlign: 'left'
}}
>
<MDBox sx={{ my: 4 }}>
<MDTypography variant="h5">Inventory Icon</MDTypography>
<MDTypography sx={customStyles.textSize}>
Choose the icon to represent the inventory
</MDTypography>
</MDBox>
<AddCircleIcon fontSize="large" onClick={handleIconSlugOpen} />
{iconSelect}
<Dialog
open={iconSlugOpen}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
onClose={handleIconSlugClose}
>
<DialogTitle id="alert-dialog-title">Select Icon</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
<RadioGroup
row
sx={{
marginTop: '15px'
}}
aria-labelledby="demo-error-radios"
name="icon_slug"
value={formik.values.icon_slug}
onChange={formik.handleChange}
>
{iconSlugs.map((iconSlug) => (
<MyFormControlLabel
key={iconSlug}
value={iconSlug}
control={<Radio style={{ display: 'none' }} />}
label={GetIconFromSlug(iconSlug)}
/>
))}
</RadioGroup>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleIconSlugClose}>
No
</Button>
<Button onClick={() => handleIconSlug(formik.values.icon_slug)}>Yes</Button>
</DialogActions>
</Dialog>
</Box>
</Grid>
<MDBox sx={{ ml: 'auto', mr: 'auto', mt: 3 }}>
<MDButton
@@ -269,12 +438,12 @@ function InventoryScreen() {
navigate('/setup/inventory');
}}
>
{'CANCEL'}
{'CLOSE'}
</MDButton>
<MDButton sx={{ ml: 3 }} color="primary" variant="outlined" type="submit">
{'SAVE'}
</MDButton>
<MDButton
{/* <MDButton
sx={{ ml: 3 }}
color="primary"
onClick={() => {
@@ -284,7 +453,7 @@ function InventoryScreen() {
}}
>
{'ADD ITEMS'}
</MDButton>
</MDButton> */}
</MDBox>
</Grid>
</MDBox>

View File

@@ -14,7 +14,17 @@ import EnhancedTable from 'components/EnhancedTable';
import { useNavigate } from 'react-router-dom';
import WidgetActions from 'redux/WidgetRedux';
import { WidgetSelectors } from 'redux/WidgetRedux';
import { DialogContent, DialogContentText, DialogTitle, MenuItem, Select } from '@mui/material';
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Grid,
MenuItem,
Select
} from '@mui/material';
const tHeads = [
{ key: 'name', name: '' },
@@ -34,7 +44,7 @@ const tHeads = [
function ItemListing() {
const dispatch = useDispatch();
const { widgetName, inventoryId } = useParams();
const { inventoryName, widgetName, inventoryId } = useParams();
const [page, setPage] = React.useState(1);
const [perPage, setPerPage] = React.useState(10);
LOGGER.log({ widgetName, inventoryId });
@@ -76,20 +86,76 @@ function ItemListing() {
);
}, []);
const [deleteAlertOpen, setDeleteAlertOpen] = React.useState(null);
const handleDeleteAlertClose = () => {
setDeleteAlertOpen(null);
};
const handleDeleteAlertOpen = (id) => {
setDeleteAlertOpen(id);
};
return (
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title={`List of ${widgetName}s`}
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
{ name: 'Inventory', path: '/setup/inventory' },
{ name: inventoryName || 'Inventory' },
{ name: `${widgetName}s List` }
]}
/>
<MDBox px={2} py={3}>
List of {widgetName}s{/* <pre>{JSON.stringify(data, null, 4)}</pre> */}
<MDBox px={5} py={3}>
<Dialog
open={deleteAlertOpen}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
onClose={handleDeleteAlertClose}
>
<DialogTitle id="alert-dialog-title">Confirm Delete</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this item?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleDeleteAlertClose}>
No
</Button>
<Button
onClick={() => {
const refreshDispatch = () => {
dispatch(
ItemActions.itemRequest({
loader: 'loading-request',
slug: API.GET_ITEMS_BY_INVENTORY,
method: 'get',
page: page - 1,
perPage,
inventoryId,
family: sFam || pFam || null
})
);
};
dispatch(
ItemActions.deleteItemRequest({
loader: 'loading-request',
slug: '/item/',
method: 'delete',
itemId: deleteAlertOpen,
refreshDispatch
})
);
handleDeleteAlertClose();
}}
>
Yes
</Button>
</DialogActions>
</Dialog>
<EnhancedTable
count={count}
page={page}
@@ -97,7 +163,12 @@ function ItemListing() {
perPage={perPage}
setPerPage={setPerPage}
editHandler={(id) => {
navigateTo(`/setup/inventory/browse/${widgetName}/${inventoryId}/edit/${id}`);
navigateTo(
`/setup/inventory/browse/${inventoryName}/${widgetName}/${inventoryId}/edit/${id}`
);
}}
deleteHandler={(id) => {
handleDeleteAlertOpen(id);
}}
resetFilters={() => {
setPFam('');
@@ -105,21 +176,21 @@ function ItemListing() {
}}
filtersControl={
<>
<DialogTitle>Add filters</DialogTitle>
<DialogContent>
<DialogContentText>Filter by family</DialogContentText>
<Grid item sx={4}>
<Select
select
fullWidth
variant="outlined"
displayEmpty
value={pFam}
inputProps={{ 'aria-label': 'Without label' }}
sx={{
width: 200
}}
onChange={(e) => {
setSFam('');
setPFam(e.target.value);
}}
>
<MenuItem key={'none'} value={''}>
None Selected
<MenuItem disabled key={'none'} value={''}>
Widget family L1
</MenuItem>
{primaryFamilies &&
primaryFamilies.map((fam) => (
@@ -128,17 +199,22 @@ function ItemListing() {
</MenuItem>
))}
</Select>
</Grid>
<Grid item sx={4}>
<Select
select
fullWidth
displayEmpty
variant="outlined"
value={sFam}
inputProps={{ 'aria-label': 'Without label' }}
sx={{
width: 200
}}
onChange={(e) => {
setSFam(e.target.value);
}}
>
<MenuItem key={'none'} value={''}>
None Selected
<MenuItem disabled key={'none'} value={''}>
Widget family L2
</MenuItem>
{secondaryFamilies &&
secondaryFamilies.map((fam) => (
@@ -147,14 +223,14 @@ function ItemListing() {
</MenuItem>
))}
</Select>
</DialogContent>
</Grid>
</>
}
data={
data
? data.map((item) => {
return {
name: item._id,
_id: item._id,
commonName: item.commonName,
formalName: item.formalName,
description: item.description,

View File

@@ -3,12 +3,29 @@ import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import { makeStyles } from '@mui/styles';
import Dropdown from 'components/Dropdown';
import { Grid, TableBody, TableCell, TableRow } from '@mui/material';
import BasicTable from 'components/BasicTable';
import { Box, Checkbox, Grid, TableBody, TableCell, TableRow } from '@mui/material';
import MDButton from 'components/Button';
import Breadcrumbs from 'components/Breadcrumbs';
import { useDispatch, useSelector } from 'react-redux';
import React, { useEffect, useState } from 'react';
import { LabellingSelectors } from 'redux/LabellingRedux';
import { WarehouseSelectors } from 'redux/WarehouseRedux';
import { WarehouseLocationsSelectors } from 'redux/WarehouseLocationsRedux';
import WarehouseActions from 'redux/WarehouseRedux';
import { API } from 'constant';
import WarehouseLocationsActions from 'redux/WarehouseLocationsRedux';
import LabellingActions from 'redux/LabellingRedux';
import BasicTable from 'components/BasicTable';
const label = { inputProps: { 'aria-label': 'Checkbox demo' } };
const useStyles = makeStyles({
nodataStyle: {
height: '200px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '300%'
},
iconSize: {
width: '50%',
height: '50%',
@@ -25,37 +42,10 @@ const useStyles = makeStyles({
}
});
const records = [
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
row: 'Nulla',
label: 'Mauris',
bay: ''
},
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
row: 'Nulla',
label: 'Mauris',
bay: ''
},
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
row: 'Nulla',
label: 'Mauris',
bay: ''
}
];
const headCells = [
{
id: 'warehouse',
label: 'warehouse'
label: 'Warehouse'
},
{
id: 'zone',
@@ -70,65 +60,116 @@ const headCells = [
label: 'Row'
},
{
id: 'Label',
label: 'label'
},
{
id: 'Bay',
label: 'bay'
id: 'bay',
label: 'Bay'
}
];
function LabelingScreen() {
const classes = useStyles();
const dispatch = useDispatch();
const dropdownData = [
{
ID: '1',
displayname: 'Regular, full time'
},
{
ID: '2',
displayname: 'Regular, part time'
},
{
ID: '3',
displayname: 'Contractor- Arise Max'
const [warehouseId, setWarehouseId] = useState('');
const [zoneId, setZoneId] = useState('');
const [areaId, setAreaId] = useState('');
const [rowId, setRowId] = useState('');
const warehouseData = useSelector(WarehouseSelectors.getWarehouseDetail);
const zonedata = useSelector(WarehouseLocationsSelectors.getChildrenOfParent(warehouseId));
const areadata = useSelector(WarehouseLocationsSelectors.getChildrenOfParent(zoneId));
const rowdata = useSelector(WarehouseLocationsSelectors.getChildrenOfParent(areaId));
const [allLabelData, setAllLabelData] = useState([]);
const [totemLabelData, setTotemLabelData] = useState([]);
const [locationLabelData, setLocationLabelData] = useState([]);
const labelData = useSelector(LabellingSelectors.getLabelDetail);
React.useEffect(() => {
if (labelData && warehouseId && zoneId && areaId && rowId) {
setAllLabelData(labelData);
}
];
const data = [
{
placeholder: 'Lorem Ipsum',
label: 'Select Warehouse'
},
{
placeholder: 'Lorem Ipsum',
label: 'Select Zone'
},
{
placeholder: 'Lorem Ipsum',
label: 'Select Area'
},
{
placeholder: 'Warehouse 1',
label: 'Select Row'
}, [labelData, warehouseId, zoneId, areaId]);
useEffect(() => {
dispatch(
WarehouseActions.warehouseDataAction({
loader: 'loading-request',
slug: API.GET_WAREHOUSE_DATA,
method: 'get'
})
);
}, []);
const warehouseChange = (event) => {
const id = event.target.value;
const type = 'warehouse';
setWarehouseId(id);
setZoneId('');
setAreaId('');
setRowId('');
dispatch(
WarehouseLocationsActions.locationRequest({
loader: 'loading-request',
slug: API.GET_CHILDREN_FROM_PARENT,
method: 'post',
data: { id, type }
})
);
};
const zoneChange = (event) => {
const id = event.target.value;
const type = 'zone';
setZoneId(id);
setAreaId('');
setRowId('');
dispatch(
WarehouseLocationsActions.locationRequest({
loader: 'loading-request',
slug: API.GET_CHILDREN_FROM_PARENT,
method: 'post',
data: { id, type }
})
);
};
const areaChange = (event) => {
const id = event.target.value;
const type = 'area';
setAreaId(id);
setRowId('');
dispatch(
WarehouseLocationsActions.locationRequest({
loader: 'loading-request',
slug: API.GET_CHILDREN_FROM_PARENT,
method: 'post',
data: { id, type }
})
);
};
const rowChange = (event) => {
const id = event.target.value;
setRowId(id);
};
const getTableItem = (e, item) => {
if (e.target.checked) {
setTotemLabelData((prev) => [...prev, item.totem_label]);
setLocationLabelData((prev) => [...prev, item.location_data]);
} else {
const filterData = allLabelData.filter((item2) => item2.bay._id !== item.bay._id);
setTotemLabelData(filterData);
setLocationLabelData(filterData);
}
];
const data2 = [
{
placeholder: 'Z01-A02-R001-B001',
label: 'Bay TOTEM Labels'
},
{
placeholder: 'Z01-A02-R001-B001',
label: 'BIN Location Labels'
}
];
};
return (
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="Search And Print Location Labels"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
@@ -137,42 +178,106 @@ function LabelingScreen() {
]}
/>
<MDBox px={5} py={5}>
<Grid container spacing={2}>
{data &&
data.map((item, index) => (
<Grid item xs={12} sm={6} md={3} key={index}>
<Dropdown items={item} dropdownData={dropdownData} />
</Grid>
))}
<Grid container spacing={2} alignItems="end">
<Grid item xs={12} md={2.5}>
<Dropdown
dropdownData={warehouseData}
value={warehouseId}
label="Select warehouse"
onChange={warehouseChange}
/>
</Grid>
<Grid item xs={12} md={2.5}>
<Dropdown dropdownData={zonedata} label="Select Zone" onChange={zoneChange} />
</Grid>
<Grid item xs={12} md={2.5}>
<Dropdown dropdownData={areadata} label="Select Area" onChange={areaChange} />
</Grid>
<Grid item xs={12} md={2.5}>
<Dropdown dropdownData={rowdata} label="Select Row" onChange={rowChange} />
</Grid>
<Grid item xs={12} md={2}>
<MDButton
fullWidth
color="primary"
sx={{
height: '45px'
}}
onClick={() => {
warehouseId &&
zoneId &&
areaId &&
rowId &&
dispatch(
LabellingActions.getLabelAction({
loader: 'labelling-request',
slug: API.GET_LABEL,
method: 'post',
data: {
warehouse: warehouseId,
zone: zoneId,
area: areaId,
row: rowId
}
})
);
}}
>
Filter
</MDButton>
</Grid>
</Grid>
<br />
<BasicTable
headCells={headCells}
records={records}
backgroundColor="#F4F4F4"
records={allLabelData}
backgroundColor="#E5E5E5"
color="#8D8D8D"
>
<TableBody>
{records &&
records.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.warehouse}</TableCell>
<TableCell>{item.zone}</TableCell>
<TableCell>{item.area}</TableCell>
<TableCell>{item.row}</TableCell>
<TableCell>{item.label}</TableCell>
<TableCell>{item.Bay}</TableCell>
{rowId ? (
allLabelData &&
allLabelData.map((item) => (
<TableRow key={item._id}>
<TableCell>
<Checkbox
{...label}
sx={{ marginRight: '2px' }}
onChange={(e) => getTableItem(e, item)}
/>
{item?.warehouse?.name}
</TableCell>
<TableCell>{item?.zone?.name}</TableCell>
<TableCell>{item?.row?.name}</TableCell>
<TableCell>{item?.area?.name}</TableCell>
<TableCell>{item?.bay?.name}</TableCell>
</TableRow>
))}
))
) : (
<TableRow className={classes.nodataStyle}>No Data</TableRow>
)}
</TableBody>
</BasicTable>
<Grid container spacing={2}>
{data2 &&
data2.map((item, index) => (
<Grid item xs={12} sm={6} md={3} key={index}>
<Dropdown items={item} dropdownData={dropdownData} />
</Grid>
))}
<Grid container spacing={2} py={5}>
<Grid item xs={12} md={3}>
<Box sx={{ backgroundColor: '#FFBC26', padding: '3px 12px' }}>Bay Totem Labels</Box>
<Box sx={{ border: '1px solid black', padding: '3px 12px', height: '300px' }}>
{totemLabelData && totemLabelData.map((item, index) => <div key={index}>{item}</div>)}
</Box>
</Grid>
<Grid item xs={12} md={3}>
<Box sx={{ backgroundColor: '#FFBC26', padding: '3px 12px' }}>Bin Location Labels</Box>
<Box sx={{ border: '1px solid black', padding: '3px 12px', height: '300px' }}>
{locationLabelData &&
locationLabelData.map((item, index) => (
<div key={index}>
{item.map((data, index) => (
<div key={index}>{data.label}</div>
))}
</div>
))}
</Box>
</Grid>
</Grid>
<div className={classes.buttondiv}>
<MDButton color="primary">{'Print Labels'}</MDButton>

View File

@@ -23,6 +23,7 @@ function LabelingHome() {
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="Labeling"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },

View File

@@ -15,6 +15,7 @@ import Breadcrumbs from 'components/Breadcrumbs';
import { useNavigate } from 'react-router-dom';
import { InventorySelectors } from 'redux/InventoryRedux';
import InventoryActions from 'redux/InventoryRedux';
import schema from 'services/ValidationServices';
function NewWarehouseDetails() {
const dispatch = useDispatch();
@@ -54,7 +55,7 @@ function NewWarehouseDetails() {
specs: '',
image: []
},
// validationSchema: schema.warehouseForm,
validationSchema: schema.warehouseForm,
onSubmit: (values) => {
dispatch(
WarehouseActions.createWarehouseAction({
@@ -80,6 +81,7 @@ function NewWarehouseDetails() {
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="Create A Warehouse"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
@@ -90,17 +92,6 @@ function NewWarehouseDetails() {
<Box mx={3} my={3}>
<form onSubmit={formik.handleSubmit}>
<Box sx={{ backgroundColor: '#fff', padding: '30px' }}>
<Box
component="div"
sx={{
fontSize: '22px',
letterSpacing: '0.01em',
color: '#000',
marginBottom: '30px'
}}
>
Form to Input
</Box>
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={6}>
<Box component="div" sx={{ marginBottom: '15px' }}>
@@ -122,7 +113,12 @@ function NewWarehouseDetails() {
name="warehousename"
value={formik.values.warehousename}
error={formik.touched.warehousename && Boolean(formik.errors.warehousename)}
helperText={formik.touched.warehousename && formik.errors.warehousename}
helperText={
formik.touched.warehousename &&
formik.errors.warehousename && (
<div style={{ color: 'red' }}>{formik.errors.warehousename}</div>
)
}
onChange={formik.handleChange}
/>
</Box>
@@ -145,7 +141,12 @@ function NewWarehouseDetails() {
name="address"
value={formik.values.address}
error={formik.touched.address && Boolean(formik.errors.address)}
helperText={formik.touched.address && formik.errors.address}
helperText={
formik.touched.address &&
formik.errors.address && (
<div style={{ color: 'red' }}>{formik.errors.address}</div>
)
}
onChange={formik.handleChange}
/>
</Box>
@@ -196,7 +197,7 @@ function NewWarehouseDetails() {
);
}}
>
<MenuItem key={''} value={''}>
<MenuItem disabled key={'none'} value={''}>
None Selected
</MenuItem>
{inventoryTypes &&

View File

@@ -20,7 +20,7 @@ function SetupHome() {
icon: <InventoryIcon width={96} height={96} color="#007AFF" />
},
{
name: 'User & Access',
name: 'User Access',
path: '/setup/users-access',
icon: <ProfileCircleIcon width={96} height={96} color="#007AFF" />
},
@@ -33,7 +33,7 @@ function SetupHome() {
return (
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs route={[{ name: 'Home', path: '/home' }, { name: 'Setup' }]} />
<Breadcrumbs title="Warehouse Management System Setup" route={[{ name: 'Home', path: '/home' }, { name: 'Setup' }]} />
<TileBasic tiles={data} />
</DashboardLayout>
);

View File

@@ -56,6 +56,7 @@ function SetupInventory() {
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="List Of Inventories"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },

View File

@@ -1,25 +1,25 @@
import React, { useEffect, useMemo } from 'react';
import moment from 'moment';
import React, { useEffect, useState, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { Grid, Tabs, Tab } from '@mui/material';
import { makeStyles } from '@mui/styles';
import MDBox from 'components/MDBox';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import { styled } from '@mui/material/styles';
import { makeStyles } from '@mui/styles';
import BasicTable from 'components/BasicTable';
import { Grid, TableBody, TableCell, TableRow } from '@mui/material';
import SearchBar from 'components/SearchBar';
import EditIcon from '@mui/icons-material/Edit';
import MDButton from 'components/Button';
import { useState } from 'react';
import { Tabs, Tab } from '@mui/material';
import TabPanel from 'components/Tabs';
import Breadcrumbs from 'components/Breadcrumbs';
import { PwTablePanel } from 'components';
import WarehouseActions, { WarehouseSelectors } from 'redux/WarehouseRedux';
import InventoryActions, { InventorySelectors } from 'redux/InventoryRedux';
import PermissionsActions, { PermissionsSelectors } from 'redux/PermissionsRedux';
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';
import { useNavigate } from 'react-router-dom';
import Breadcrumbs from 'components/Breadcrumbs';
const useStyles = makeStyles((theme) => ({
iconSize: {
@@ -34,6 +34,9 @@ const useStyles = makeStyles((theme) => ({
statusInactive: {
color: 'red'
},
limitWidth: {
maxWidth: '20rem'
},
margin: {
marginBottom: '20px',
borderTop: '1px solid #ddd',
@@ -53,6 +56,10 @@ const useStyles = makeStyles((theme) => ({
overflow: 'hidden',
borderRadius: '0.5rem'
},
rightPlaced: {
position: 'absolute',
right: 0
},
tabs: {
borderRadius: 0,
'& .MuiButtonBase-root.MuiTab-root': {
@@ -83,52 +90,117 @@ function UserAccessScreen() {
const [value, setValue] = useState(0);
const usersData = useSelector(UsersSelectors.getUsersDetail);
const rolesData = useSelector(RolesSelectors.getRolesDetail);
const currentUser = useSelector(AuthSelectors.getUser);
const warehouses = useSelector(WarehouseSelectors.getWarehouseDetail);
const inventories = useSelector(InventorySelectors.getInventoryDetail);
const actions = useSelector(PermissionsSelectors.getActionsDetail);
const permissions = useSelector(PermissionsSelectors.getPermissionsDetail);
const [userRecords, setUserRecords] = useState([]);
const [rolesRecords, setRoleRecords] = useState([]);
const [userLoader, setUserLoader] = useState(false);
const [roleLoader, setRoleLoader] = useState(false);
const [roleApiCompleted, setRoleApiCompleted] = useState(false);
const [userApiCompleted, setUserApiCompleted] = useState(false);
const [originalUserRecords, setOriginalUserRecords] = useState([]);
const [originalRolesRecords, setOriginalRoleRecords] = useState([]);
const navigate = useNavigate();
useEffect(() => {
(!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'}));
(!rolesData || rolesData.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 userHeadCells = [
{ 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 ? 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: '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: 'is_active', label: 'Access', value: record => record.isActive ? <span className={classes.statusActive}>Active</span>
: <span className={classes.statusInactive}>Inactive</span>
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={classes.statusInactive}>Inactive</span>
)
}
];
const rolesHeadCells = [
{ id: 'role', label: 'Role' },
{ id: 'permissions', label: 'Permissions' },
{ id: 'status', label: 'Status' }
{ id: 'role', label: 'Role', isEditAnchor: true, value: record => record.name },
{ 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' ? <span className={classes.statusActive}>Active</span>
: <span className={classes.statusInactive}>Inactive</span> }
];
const usersHandler = () => {
setUserLoader(true);
dispatch(
UsersActions.getUsersAction({
loader: 'loading-request',
slug: API.GET_USERS_DATA,
method: 'get'
method: 'get',
callback: () => {
setUserLoader(false);
setUserApiCompleted(true);
}
})
);
};
const rolesHandler = () => {
setRoleLoader(true);
dispatch(
RolesActions.getRolesAction({
loader: 'loading-request',
slug: API.GET_ROLES_DATA,
method: 'get'
method: 'get',
callback: () => {
setRoleLoader(false);
setRoleApiCompleted(true);
}
})
);
};
useMemo(() => rolesHandler(), []);
useMemo(() => usersHandler(), []);
useEffect(() => {
if (usersData.length) {
@@ -137,6 +209,7 @@ function UserAccessScreen() {
item.role_name = item.roles.map((role) => role.name).join(',');
return item;
});
setOriginalUserRecords(users);
setUserRecords(users);
}
@@ -144,13 +217,14 @@ 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';
}
item.status = item.status ? 'ACTIVE' : 'INACTIVE';
return item;
});
setOriginalRoleRecords(roles);
setRoleRecords(roles);
}
}, [rolesData, usersData]);
@@ -159,94 +233,81 @@ function UserAccessScreen() {
setValue(val);
};
const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(even)': {
backgroundColor: theme.palette.action.hover
}
}));
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={() => canEdit(columnConfig) && navigate('/setup/users-access/edit-user', {state: {user: record}})}>
{canEdit(columnConfig) ? <span className={classes.iconwrap}>
<EditIcon className={classes.iconSize}/>
{columnConfig.value(record)}
</span> : <span>{columnConfig.value(record)}</span>}
</TableCell>)}
</StyledTableRow>;
});
const handleSearch = (e, currentTab) => {
const value = e.target.value;
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'];
const setter = currentTab === 0 ? setUserRecords : setRoleRecords;
searchList = searchList.concat(['createdBy.fullName', 'createdAt', 'updatedBy.fullName', 'updatedAt']);
const filteredRecords = records.filter(record => searchList.some(field => {
let recordInner = {...record};
if (field.indexOf('.') > -1) {
field = field.split('.');
recordInner = recordInner[field[0]];
field = field[1];
}
return recordInner && recordInner[field] !== undefined && typeof recordInner[field] === 'string'
&& recordInner[field].toLowerCase().indexOf(value?.toLowerCase()) > -1;
}));
records.forEach(record => record.status = record.status === 'ACTIVE');
setter(filteredRecords);
};
return (
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="Access Details"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
{ name: 'Users and Access', path: '/setup/users-access' }
{ name: 'User Access' }
]}
/>
<MDBox px={0} py={3}>
<Grid container spacing={1} className={classes.margin}>
<Grid item xs={12} sm={4} md={4} className='ps-2 pt-0'>
<MDBox px={5} py={3}>
<Grid container spacing={1} className={classes.margin + ' w-100 ms-0'}>
<Grid item xs={12} sm={4} md={4} className="ps-0 pt-0">
<Tabs value={value} className={`p-0 h-100 ${classes.tabs}`} onChange={handleTabs}>
<Tab label="Roles" onClick={() => rolesHandler()} />
<Tab label="Users" onClick={() => usersHandler()} />
<Tab label="Roles" onClick={() => rolesHandler()} />
</Tabs>
</Grid>
<Grid item xs={12} sm={4} md={6} className='py-2' style={{ display: 'flex', alignItems: 'center' }}>
<SearchBar />
<Grid
item
xs={12}
sm={4}
md={6}
className="py-2"
style={{ display: 'flex', alignItems: 'center', justifyContent: 'end' }}
>
<SearchBar onChange={e => handleSearch(e, value)} />
</Grid>
<Grid item xs={12} sm={4} md={2} className='py-2' style={{ display: 'flex', alignItems: 'center' }}>
<Grid
item
xs={12}
sm={4}
md={2}
className="py-2"
style={{ display: 'flex', alignItems: 'center', justifyContent: 'end' }}
>
<MDButton
color="primary"
size="medium"
onClick={() => navigate(`/setup/users-access/${value === 0 ? 'create-role' : 'create-user'}`)}
onClick={() =>
navigate(`/setup/users-access/${value === 0 ? 'create-user' : 'create-role'}`)
}
>
{value === 0 ? '+ CREATE ROLE' : '+ CREATE USER'}
{value === 0 ? '+ CREATE USER' : '+ CREATE ROLE'}
</MDButton>
</Grid>
</Grid>
<Grid px={2}>
<TabPanel value={value} index={0} className={classes.radialBorder}>
<BasicTable
headCells={rolesHeadCells}
records={rolesRecords}
backgroundColor="#007AFF"
color="#fff"
>
<TableBody>
{rolesRecords &&
rolesRecords.map((item) => (
<StyledTableRow key={item.id}>
<TableCell>
<div className={classes.iconwrap}>
<EditIcon className={classes.iconSize} />
{item.name}
</div>
</TableCell>
<TableCell>{item.permissions}</TableCell>
<TableCell>{item.status}</TableCell>
</StyledTableRow>
))}
</TableBody>
</BasicTable>
</TabPanel>
<TabPanel value={value} index={1} className={classes.radialBorder}>
<BasicTable
id="user-list"
headCells={userHeadCells}
records={userRecords}
backgroundColor="#007AFF"
color="#fff"
>
{userRecords && userRecords.length > 0
? <TableBody>
{rowRenders}
</TableBody> : 'No Records to Display'}
</BasicTable>
</TabPanel>
<Grid>
<PwTablePanel classes={classes} headCells={userHeadCells} id="user-list" index={0} loader={userLoader} dataFetched={userApiCompleted}
records={userRecords} navUrl='/setup/users-access/edit-user' table="user" value={value} />
<PwTablePanel classes={classes} headCells={rolesHeadCells} id="role-list" index={1} loader={roleLoader} dataFetched={roleApiCompleted}
records={rolesRecords} navUrl='/setup/users-access/edit-role' table="role" value={value} />
</Grid>
</MDBox>
</DashboardLayout>

View File

@@ -29,6 +29,7 @@ function WarehouseScreen() {
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="List of Warehouses"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },

View File

@@ -1,21 +1,33 @@
import { Box, TableBody, TableCell, TableRow } from '@mui/material';
import { Box, Grid, TableBody, TableCell, TableRow } from '@mui/material';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import { makeStyles } from '@mui/styles';
import OutlinedInput from '@mui/material/OutlinedInput';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import React from 'react';
import SearchBar from 'components/SearchBar';
import React, { useState } from 'react';
import BasicTable from 'components/BasicTable';
import Barcodeimage from 'assets/images/barcode-number.png';
import MDButton from 'components/Button';
import Checkbox from '@mui/material/Checkbox';
const label = { inputProps: { 'aria-label': 'Checkbox demo' } };
import Breadcrumbs from 'components/Breadcrumbs';
import Dropdown from 'components/Dropdown';
import { useDispatch, useSelector } from 'react-redux';
import { InventorySelectors } from 'redux/InventoryRedux';
import WidgetActions from 'redux/WidgetRedux';
import { API } from 'constant';
import MDBox from 'components/MDBox';
import { WidgetSelectors } from 'redux/WidgetRedux';
import ProductActions from 'redux/ProductsRedux';
import { ProductSelectors } from 'redux/ProductsRedux';
import InventoryActions from 'redux/InventoryRedux';
import QRcode from 'components/QRcode';
const useStyles = makeStyles({
nodataStyle: {
height: '200px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '600%'
},
labelSize: {
fontSize: '16px',
letterSpacing: '0.01em',
@@ -32,69 +44,6 @@ const useStyles = makeStyles({
margin: '52px 0px'
}
});
const records = [
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
row: 'Nulla',
label: 'Mauris',
bay: 'Senectus',
barcodenumber: '2085550112',
barcodeimage: Barcodeimage
},
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
row: 'Nulla',
label: 'Mauris',
bay: 'Senectus',
barcodenumber: '2085550112',
barcodeimage: Barcodeimage
},
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
row: 'Nulla',
label: 'Mauris',
bay: 'Senectus',
barcodenumber: '2085550112',
barcodeimage: Barcodeimage
}
];
const recordsNew = [
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
barcodenumber: '2085550112',
barcodeimage: Barcodeimage
},
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
barcodenumber: '2085550112',
barcodeimage: Barcodeimage
},
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
barcodenumber: '2085550112',
barcodeimage: Barcodeimage
},
{
warehouse: 'Ipsum',
zone: 'Vivera',
area: 'Nisi',
barcodenumber: '2085550112',
barcodeimage: Barcodeimage
}
];
const headCells = [
{
@@ -110,15 +59,15 @@ const headCells = [
label: 'Subfamily'
},
{
id: 'Name',
id: 'formalName',
label: 'Name'
},
{
id: 'Manufacture',
id: 'manufacturer',
label: 'Manufacture'
},
{
id: 'Size',
id: 'size',
label: 'Size'
},
{
@@ -136,11 +85,11 @@ const headCellsNew = [
label: 'Inventory Name'
},
{
id: 'Item Name',
id: 'formalName',
label: 'Item Name'
},
{
id: 'Item Description',
id: 'description',
label: 'Item Description'
},
{
@@ -152,24 +101,103 @@ const headCellsNew = [
label: 'Barcode'
}
];
function WidgetLabel() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const dispatch = useDispatch();
const handleChange = (event) => {
const {
target: { value }
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
const [labelData, setLabelData] = useState([]);
const [inventoryId, setInventoryId] = useState('');
const [familyId, setFamilyId] = useState('');
const [subFamilyId, setSubFamilyId] = useState('');
const [allProductData, setAllProductData] = useState([]);
const [filterClick, setFilterClick] = useState(false);
const inventoryData = useSelector(InventorySelectors.getInventoryDetail);
const familyData = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
const subFamilyData = useSelector(WidgetSelectors.getWidgetsByParentId(familyId));
const productData = useSelector(ProductSelectors.getProductDetail);
React.useEffect(() => {
dispatch(
InventoryActions.getInventoryAction({
loader: 'loading-request',
slug: API.GET_INVENTORY,
method: 'get'
})
);
}, []);
React.useEffect(() => {
if (productData.result && filterClick) {
setAllProductData(productData?.result);
}
}, [productData, filterClick]);
const inventoryChange = (event) => {
const id = event.target.value;
setInventoryId(id);
setFamilyId('');
setSubFamilyId('');
dispatch(
WidgetActions.widgetRequest({
loader: 'loading-request',
slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${id}`,
method: 'get'
})
);
};
const familyChange = (event) => {
const id = event.target.value;
setFamilyId(id);
setSubFamilyId('');
dispatch(
WidgetActions.widgetRequest({
loader: 'loading-request',
slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${id}`,
method: 'get'
})
);
};
const subFamilyChange = (event) => {
const id = event.target.value;
setSubFamilyId(id);
dispatch(
WidgetActions.widgetRequest({
loader: 'loading-request',
slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${id}`,
method: 'get'
})
);
};
const filterHandler = () => {
setFilterClick(true);
dispatch(
ProductActions.getProductByIdAction({
loader: 'loading-request',
slug: `${API.GET_PRODUCT_BY_ID}${inventoryId}`,
method: 'get'
})
);
};
const getTableItem = (e, item) => {
if (e.target.checked) {
setLabelData((prev) => [...prev, item]);
} else {
const filterData = labelData.filter((item2) => item2._id !== item._id);
setLabelData(filterData);
}
};
return (
<>
<DashboardLayout>
<DashboardNavbar />
<Breadcrumbs
title="Search And Print Location Labels"
route={[
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
@@ -177,159 +205,104 @@ function WidgetLabel() {
{ name: 'Widget Label' }
]}
/>
<Box mx={3} my={3}>
<MDBox px={5} py={5}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={3}>
<Dropdown
label="Select Inventory"
dropdownData={inventoryData}
value={inventoryId}
onChange={inventoryChange}
/>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<Dropdown
label="Select Family"
dropdownData={familyData}
value={familyId}
onChange={familyChange}
/>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<Dropdown
label="Select Sub Family"
value={subFamilyId}
dropdownData={subFamilyData}
onChange={subFamilyChange}
/>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<MDButton
color="primary"
sx={{ minWidth: '100%', marginTop: '50px', padding: '13px 40px' }}
onClick={() => filterHandler()}
>
{'Filter'}
</MDButton>
</Grid>
</Grid>
<Box
sx ={{
display: 'grid',
gridTemplateColumns: ' repeat(5, 1fr)',
gridColumnGap :'20px'
sx={{
marginTop: '24px',
backgroundColor: '#FFFFFF',
overflowX: 'auto',
overflowY: 'auto',
height: '60vh'
}}
>
<Box>
<Box component="div" className={classes.labelSize}>
Select Inventory
</Box>
<Box className={classes.customLabel}>
<Select
multiple
displayEmpty
value={personName}
input={<OutlinedInput />}
renderValue={(selected) => {
if (selected.length === 0) {
return 'Placeholder';
}
return selected.join(', ');
}}
inputProps={{ 'aria-label': 'Without label' }}
sx={{
width: '100%'
}}
onChange={handleChange}
>
<MenuItem disabled value="">
Placeholder
</MenuItem>
<MenuItem>Lorem Ipsum</MenuItem>
</Select>
</Box>
</Box>
<Box>
<Box component="div" className={classes.labelSize}>
Select Family
</Box>
<Box className={classes.customLabel}>
<Select
multiple
displayEmpty
value={personName}
input={<OutlinedInput />}
renderValue={(selected) => {
if (selected.length === 0) {
return 'Placeholder';
}
return selected.join(', ');
}}
inputProps={{ 'aria-label': 'Without label' }}
sx={{
width: '100%'
}}
onChange={handleChange}
>
<MenuItem disabled value="">
Placeholder
</MenuItem>
<MenuItem>Lorem Ipsum</MenuItem>
</Select>
</Box>
</Box>
<Box>
<Box component="div" className={classes.labelSize}>
Select Sub Family
</Box>
<Box className={classes.customLabel}>
<Select
multiple
displayEmpty
value={personName}
input={<OutlinedInput />}
renderValue={(selected) => {
if (selected.length === 0) {
return 'Placeholder';
}
return selected.join(', ');
}}
inputProps={{ 'aria-label': 'Without label' }}
sx={{
width: '100%'
}}
onChange={handleChange}
>
<MenuItem disabled value="">
Placeholder
</MenuItem>
<MenuItem>Lorem Ipsum</MenuItem>
</Select>
</Box>
</Box>
<Box>
<Box component="div" className={classes.labelSize}>
Search Keyword
</Box>
<Box className={classes.customLabel}>
<SearchBar />
</Box>
</Box>
<Box>
<MDButton color="primary" sx={{ minWidth:'100%', marginTop:'30px', padding:'13px 40px' }} >{'Filter'}</MDButton>
</Box>
</Box>
<Box sx={{ marginTop: '24px', backgroundColor: '#FFFFFF' }}>
<BasicTable
headCells={headCells}
records={records}
records={allProductData}
backgroundColor="#E5E5E5"
color="#8D8D8D"
>
<TableBody>
{records &&
records.map((item) => (
{filterClick && allProductData.length ? (
allProductData &&
allProductData.map((item) => (
<TableRow key={item.id}>
<TableCell><Checkbox {...label} sx={{ marginRight:'2px' }} /> {item.warehouse}</TableCell>
<TableCell>{item.zone}</TableCell>
<TableCell>{item.area}</TableCell>
<TableCell>{item.row}</TableCell>
<TableCell>{item.label}</TableCell>
<TableCell>{item.bay}</TableCell>
<TableCell>{item.barcodenumber}</TableCell>
<TableCell>
<img src={item.barcodeimage} alt="img" width="200px" />
<Checkbox
{...label}
sx={{ marginRight: '2px' }}
onChange={(e) => getTableItem(e, item)}
/>
{item?.inventory?.name}
</TableCell>
<TableCell>{item?.widgetfamily?.name}</TableCell>
<TableCell>{item?.location?.sub_levels}</TableCell>
<TableCell>{item?.formalName}</TableCell>
<TableCell>{item?.manufacturer}</TableCell>
<TableCell>{item?.size}</TableCell>
<TableCell>{item?._id}</TableCell>
<TableCell>
<QRcode payload={item._id} width={100} height={100} />
</TableCell>
</TableRow>
))}
))
) : (
<TableRow className={classes.nodataStyle}>No Data</TableRow>
)}
</TableBody>
</BasicTable>
</Box>
<Box sx={{ marginTop: '24px', backgroundColor: '#FFFFFF' }}>
<BasicTable
headCells={headCellsNew}
records={records}
records={labelData}
backgroundColor="#E5E5E5"
color="#8D8D8D"
>
<TableBody>
{recordsNew &&
recordsNew.map((item) => (
{labelData &&
labelData.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.warehouse}</TableCell>
<TableCell>{item.zone}</TableCell>
<TableCell>{item.area}</TableCell>
<TableCell>{item.barcodenumber}</TableCell>
<TableCell>{item?.inventory?.name}</TableCell>
<TableCell>{item.formalName}</TableCell>
<TableCell>{item.description}</TableCell>
<TableCell>{item._id}</TableCell>
<TableCell>
<img src={item.barcodeimage} alt="img" width="200px" />
<QRcode payload={item._id} width={100} height={100} />
</TableCell>
</TableRow>
))}
@@ -339,7 +312,7 @@ function WidgetLabel() {
<div className={classes.buttondiv}>
<MDButton color="primary">{'Print Labels'}</MDButton>
</div>
</Box>
</MDBox>
</DashboardLayout>
</>
);

View File

@@ -13,6 +13,8 @@ const { Types, Creators } = createActions({
addInventoryFailure: ['error'],
updateInventoryAction: ['payload'],
updateInventorySuccess: ['data'],
deleteInventoryAction: ['payload'],
deleteInventorySuccess: ['data'],
updateInventoryFailure: ['error'],
getInventoryTypesAction: ['payload'],
getInventoryTypesSuccess: ['data'],
@@ -98,6 +100,21 @@ export const onUpdateInventorySuccess = (state, { data }) =>
]
});
export const onDeleteInventoryAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onDeleteInventorySuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
getInventoryDetail: data.deletedInventoryID
? [...state.getInventoryDetail.filter((x) => x._id !== data.deletedInventoryID)]
: state.getInventoryDetail
});
export const onUpdateInventoryFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
@@ -133,6 +150,8 @@ export const inventoryReducer = createReducer(INITIAL_STATE, {
[Types.ADD_INVENTORY_FAILURE]: onAddInventoryFailure,
[Types.UPDATE_INVENTORY_ACTION]: onUpdateInventoryAction,
[Types.UPDATE_INVENTORY_SUCCESS]: onUpdateInventorySuccess,
[Types.DELETE_INVENTORY_ACTION]: onDeleteInventoryAction,
[Types.DELETE_INVENTORY_SUCCESS]: onDeleteInventorySuccess,
[Types.UPDATE_INVENTORY_FAILURE]: onUpdateInventoryFailure,
[Types.GET_INVENTORY_TYPES_ACTION]: onGetInventoryTypesAction,
[Types.GET_INVENTORY_TYPES_SUCCESS]: onGetInventoryTypesSuccess,

View File

@@ -12,6 +12,8 @@ const { Types, Creators } = createActions({
addItemSuccess: ['data'],
editItemRequest: ['payload'],
editItemSuccess: ['data'],
deleteItemRequest: ['payload'],
deleteItemSuccess: ['data'],
oneItemRequest: ['payload'],
oneItemSuccess: ['data']
});
@@ -91,6 +93,18 @@ export const onEditItemSuccess = (state, { data }) =>
item: null
});
export const onDeleteItemRequest = (state, { payload }) =>
state.merge({
fetching: _.uniq([...state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onDeleteItemSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader)
});
export const onItemFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
@@ -106,6 +120,8 @@ export const itemReducer = createReducer(INITIAL_STATE, {
[Types.ADD_ITEM_SUCCESS]: onAddItemSuccess,
[Types.EDIT_ITEM_REQUEST]: onEditItemRequest,
[Types.EDIT_ITEM_SUCCESS]: onEditItemSuccess,
[Types.DELETE_ITEM_REQUEST]: onDeleteItemRequest,
[Types.DELETE_ITEM_SUCCESS]: onDeleteItemSuccess,
[Types.ONE_ITEM_REQUEST]: onOneItemRequest,
[Types.ONE_ITEM_SUCCESS]: onOneItemSuccess
});

View File

@@ -0,0 +1,54 @@
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({
getLabelAction: ['payload'],
getLabelSuccess: ['data'],
getLabelFailure: ['error']
});
export const LabellingTypes = Types;
const LabellingActions = Creators;
export default LabellingActions;
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
getLabelDetail: [],
getLabelLoading: false,
getLabelerror: {}
});
/* ------------- Selectors ------------- */
export const LabellingSelectors = {
getLabelDetail: (state) => state.labelling.getLabelDetail
};
/* ------------- Reducers ------------- */
export const onGetLabelAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onGetLabelSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
getLabelDetail: data.getLabelDetail
});
export const onGetLabelFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
error: { ...state.error, [error?.loader]: error?.error }
});
/* ------------- Hookup Reducers To Types ------------- */
export const labellingReducer = createReducer(INITIAL_STATE, {
[Types.GET_LABEL_ACTION]: onGetLabelAction,
[Types.GET_LABEL_SUCCESS]: onGetLabelSuccess,
[Types.GET_LABEL_FAILURE]: onGetLabelFailure
});

View File

@@ -5,6 +5,9 @@ import { getFetchingValue, getErrorValue } from '../services/Utils';
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
getProductByIdAction: ['payload'],
getProductByIdSuccess: ['data'],
getProductByIdFailure: ['error'],
addProductAction: ['payload'],
addProductSuccess: ['data'],
addProductFailure: ['error']
@@ -18,15 +21,35 @@ export default ProductActions;
export const INITIAL_STATE = Immutable({
addProductDetail: [],
addProductLoading: false,
addProducterror: {}
addProducterror: {},
getProductByIdDetail: []
});
/* ------------- Selectors ------------- */
export const ProductSelectors = {
addProductDetail: (state) => state.product.productDetail
addProductDetail: (state) => state.product.productDetail,
getProductDetail: (state) => state.product.getProductByIdDetail
};
/* ------------- Reducers ------------- */
export const onGetProductByIdAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onGetProductByIdSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
getProductByIdDetail: data.getProductByIdDetail
});
export const onGetProductByIdFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
error: { ...state.error, [error?.loader]: error?.error }
});
export const onAddProductAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
@@ -48,6 +71,10 @@ export const onAddProductFailure = (state, { error }) =>
/* ------------- Hookup Reducers To Types ------------- */
export const productReducer = createReducer(INITIAL_STATE, {
[Types.GET_PRODUCT_BY_ID_ACTION]: onGetProductByIdAction,
[Types.GET_PRODUCT_BY_ID_SUCCESS]: onGetProductByIdSuccess,
[Types.GET_PRODUCT_BY_ID_FAILURE]: onGetProductByIdFailure,
[Types.ADD_PRODUCT_FAILURE]: onAddProductFailure,
[Types.ADD_PRODUCT_ACTION]: onAddProductAction,
[Types.ADD_PRODUCT_SUCCESS]: onAddProductSuccess,
[Types.ADD_PRODUCT_FAILURE]: onAddProductFailure

View File

@@ -15,7 +15,10 @@ const { Types, Creators } = createActions({
editWarehouseAction: ['payload'],
editWarehouseSuccess: ['data'],
editWarehouseFailure: ['error']
editWarehouseFailure: ['error'],
deleteWarehouseAction: ['payload'],
deleteWarehouseSuccess: ['data']
});
export const WarehouseTypes = Types;
@@ -96,6 +99,21 @@ export const onEditWarehouseSuccess = (state, { data }) =>
]
});
export const onDeleteWarehouseAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onDeleteWarehouseSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
warehouseDetail: data?.deletedWarehouseID
? [...state.warehouseDetail.filter((x) => x._id !== data?.deletedWarehouseID)]
: state.warehouseDetail
});
export const onEditWarehouseFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
@@ -113,5 +131,7 @@ export const warehouseReducer = createReducer(INITIAL_STATE, {
[Types.EDIT_WAREHOUSE_ACTION]: onEditWarehouseAction,
[Types.EDIT_WAREHOUSE_SUCCESS]: onEditWarehouseSuccess,
[Types.EDIT_WAREHOUSE_FAILURE]: onEditWarehouseFailure
[Types.EDIT_WAREHOUSE_FAILURE]: onEditWarehouseFailure,
[Types.DELETE_WAREHOUSE_ACTION]: onDeleteWarehouseAction,
[Types.DELETE_WAREHOUSE_SUCCESS]: onDeleteWarehouseSuccess
});

View File

@@ -8,6 +8,7 @@ import { rolesReducer } from './RolesRedux';
import { permissionsReducer } from './PermissionsRedux';
import { WarehouseLocationsReducer } from './WarehouseLocationsRedux';
import { widgetReducer } from './WidgetRedux';
import { labellingReducer } from './LabellingRedux';
import { itemReducer } from './ItemRedux';
// Combine all reducers.
@@ -21,6 +22,7 @@ const appReducer = combineReducers({
product: productReducer,
inventory: inventoryReducer,
widgets: widgetReducer,
labelling: labellingReducer,
items: itemReducer
});

View File

@@ -12,7 +12,7 @@ import NewWarehouseDetails from 'pages/newWarehouseDetails';
import EditWarehouseDetails from 'pages/editWarehouseDetails';
import LabelingHome from 'pages/labellingHome';
import SetupInventory from 'pages/setupInventory';
import CreateUserRole from 'pages/createUserRole';
import CreateEditUserRole from 'pages/createEditUserRole';
import CreateEditUser from 'pages/createEditUser';
import WidgetLabel from 'pages/widgetLabel';
import ItemListing from 'pages/itemListing';
@@ -114,22 +114,21 @@ const protectedRoutes = [
name: 'Add New Item',
key: 'add-new-item',
hide: true,
route: '/setup/inventory/new-item/:widgetName/:inventoryId',
route: '/setup/inventory/new-item/:inventoryName/:widgetName/:inventoryId',
component: <AddNewItem />
},
{
name: 'Update Item',
key: 'udpate-item',
hide: true,
route: '/setup/inventory/browse/:widgetName/:inventoryId/edit/:itemId',
route: '/setup/inventory/browse/:inventoryName/:widgetName/:inventoryId/edit/:itemId',
component: <AddNewItem />
},
// /setup/inventory/browse/${payload?.widgetName}/${payload?.inventoryId}/edit/${payload?.id}
{
name: 'View Items',
key: 'view-items',
hide: true,
route: '/setup/inventory/browse/:widgetName/:inventoryId',
route: '/setup/inventory/browse/:inventoryName/:widgetName/:inventoryId',
component: <ItemListing />
},
{
@@ -177,21 +176,28 @@ const protectedRoutes = [
key: 'create-role',
route: '/setup/users-access/create-role',
hide: true,
component: <CreateUserRole />
component: <CreateEditUserRole />
},
{
name: 'Create User',
key: 'create-user',
route: '/setup/users-access/create-user',
hide: true,
component: <CreateEditUser context='new' />
component: <CreateEditUser context="new" />
},
{
name: 'Edot User',
name: 'Edit User',
key: 'edit-user',
route: '/setup/users-access/edit-user',
hide: true,
component: <CreateEditUser context='edit' />
},
{
name: 'Edit Role',
key: 'edit-role',
route: '/setup/users-access/edit-role',
hide: true,
component: <CreateEditUserRole context='edit' />
}
]
}

View File

@@ -63,7 +63,9 @@ export function* onRequestAddInventoryData({ payload }) {
payload?.data
);
if (response?.status === 200) {
toast('New inventory added');
toast.success('New inventory added', {
theme: 'colored'
});
yield put(
InventoryActions.addInventorySuccess({
loader: payload?.loader,
@@ -72,7 +74,9 @@ export function* onRequestAddInventoryData({ payload }) {
);
payload.navigateTo();
} else {
toast('Failed to add inventory');
toast.error('Failed to add inventory', {
theme: 'colored'
});
payload.onFailedAddInventoryData(response.data.error);
yield put(
InventoryActions.addInventoryFailure({
@@ -91,16 +95,20 @@ export function* onRequestUpdateInventoryData({ payload }) {
payload?.data
);
if (response?.status === 200) {
toast('Updated inventory successfully');
toast.success('Updated inventory successfully', {
theme: 'colored'
});
yield put(
InventoryActions.updateInventorySuccess({
loader: payload?.loader,
updateInventory: response?.data?.data
})
);
payload.navigateTo();
// payload.navigateTo();
} else {
toast('Failed to update inventory');
toast.error('Failed to update inventory', {
theme: 'colored'
});
yield put(
InventoryActions.updateInventoryFailure({
loader: payload?.loader,
@@ -109,9 +117,37 @@ export function* onRequestUpdateInventoryData({ payload }) {
);
}
}
export function* onRequestDeleteInventoryData({ payload }) {
const response = yield call(ApiServices[payload?.method], AuthorizedAPI, payload?.slug);
if (response?.status === 200) {
toast.success('Deleted inventory successfully', {
theme: 'colored'
});
payload.navigateTo();
yield put(
InventoryActions.deleteInventorySuccess({
loader: payload?.loader,
deletedInventoryID: payload?.inventoryId
})
);
} else {
toast.error('Failed to delete inventory', {
theme: 'colored'
});
yield put(
InventoryActions.updateInventoryFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export default [
takeEvery(InventoryTypes.GET_INVENTORY_ACTION, onRequestGetInventoryData),
takeEvery(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData),
takeEvery(InventoryTypes.DELETE_INVENTORY_ACTION, onRequestDeleteInventoryData),
takeEvery(InventoryTypes.UPDATE_INVENTORY_ACTION, onRequestUpdateInventoryData),
takeEvery(InventoryTypes.GET_INVENTORY_TYPES_ACTION, onRequestGetInventoryTypesData)
];

View File

@@ -50,7 +50,9 @@ export function* onRequestOneItem({ payload }) {
// `/setup/inventory/browse/${payload?.widgetName}/${payload?.inventoryId}/edit/${payload?.itemId}`
// );
} else {
toast('Failed to get item details');
toast.error('Failed to get item details', {
theme: 'colored'
});
yield put(
ItemActions.itemFailure({
loader: payload?.loader,
@@ -100,7 +102,9 @@ export function* onAddRequestItem({ payload }) {
createFormData(payload?.data)
);
if (response?.status === 200) {
toast(`Added item: ${payload.data.commonName}`);
toast.success(`Added item: ${payload.data.commonName}`, {
theme: 'colored'
});
// payload.navigateTo(
// `/setup/inventory/browse/${payload?.widgetName}/${payload?.inventoryId}/edit/${response?.data?.data?._id}`
// );
@@ -112,7 +116,9 @@ export function* onAddRequestItem({ payload }) {
})
);
} else {
toast('Failed to add item');
toast.error('Failed to add item', {
theme: 'colored'
});
yield put(
ItemActions.itemFailure({
loader: payload?.loader,
@@ -130,7 +136,9 @@ export function* onEditRequestItem({ payload }) {
createFormData(payload?.data)
);
if (response?.status === 200) {
toast(`Successfully edited item: ${payload.data.commonName}`);
toast.success(`Successfully edited item: ${payload.data.commonName}`, {
theme: 'colored'
});
payload.navigateTo('/setup/inventory');
yield put(
ItemActions.addItemSuccess({
@@ -139,7 +147,39 @@ export function* onEditRequestItem({ payload }) {
})
);
} else {
toast('Failed to edit item');
toast.error('Failed to edit item', {
theme: 'colored'
});
yield put(
ItemActions.itemFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export function* onDeleteRequestItem({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug + payload?.itemId
);
if (response?.status === 200) {
toast.success('Successfully deleted item', {
theme: 'colored'
});
payload.refreshDispatch();
yield put(
ItemActions.deleteItemSuccess({
loader: payload?.loader,
item: response?.data?.data
})
);
} else {
toast.error('Failed to delete item', {
theme: 'colored'
});
yield put(
ItemActions.itemFailure({
loader: payload?.loader,
@@ -153,5 +193,6 @@ export default [
takeEvery(ItemTypes.ITEM_REQUEST, onRequestItem),
takeEvery(ItemTypes.ONE_ITEM_REQUEST, onRequestOneItem),
takeEvery(ItemTypes.ADD_ITEM_REQUEST, onAddRequestItem),
takeEvery(ItemTypes.EDIT_ITEM_REQUEST, onEditRequestItem)
takeEvery(ItemTypes.EDIT_ITEM_REQUEST, onEditRequestItem),
takeEvery(ItemTypes.DELETE_ITEM_REQUEST, onDeleteRequestItem)
];

31
src/sagas/Labelling.js Normal file
View File

@@ -0,0 +1,31 @@
import { AuthorizedAPI } from 'config';
import { call, put, takeEvery } from 'redux-saga/effects';
import LabellingActions from 'redux/LabellingRedux';
import { LabellingTypes } from 'redux/LabellingRedux';
import ApiServices from 'services/API/ApiServices';
export function* onRequestGetLabelData({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
if (response?.status === 200) {
yield put(
LabellingActions.getLabelSuccess({
loader: payload?.loader,
getLabelDetail: response?.data?.data
})
);
} else {
payload.onFailedGetLabelData(response.data.error);
yield put(
LabellingActions.getLabelFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export default [takeEvery(LabellingTypes.GET_LABEL_ACTION, onRequestGetLabelData)];

View File

@@ -28,4 +28,32 @@ export function* onRequestAddProductData({ payload }) {
);
}
}
export default [takeLatest(ProductTypes.ADD_PRODUCT_ACTION, onRequestAddProductData)];
export function* onRequestGetProductById({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
if (response?.status === 200) {
yield put(
ProductActions.getProductByIdSuccess({
loader: payload?.loader,
getProductByIdDetail: response?.data?.data
})
);
} else {
payload.onFailedGetProductById(response.data.error);
yield put(
ProductActions.getProductByIdFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export default [
takeLatest(ProductTypes.ADD_PRODUCT_ACTION, onRequestAddProductData),
takeLatest(ProductTypes.GET_PRODUCT_BY_ID_ACTION, onRequestGetProductById)
];

View File

@@ -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();
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({

View File

@@ -11,6 +11,7 @@ export function* onRequestUsersData({ payload }) {
payload?.slug,
payload?.data
);
payload?.callback && payload?.callback();
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({
@@ -34,8 +36,13 @@ export function* onCreateUserData({ payload }) {
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
payload?.data,
{
processData: false,
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 + '" ' : '');
@@ -48,8 +55,9 @@ export function* onCreateUserData({ payload }) {
})
);
} else {
toast('Something went wrong!');
payload.onValidationFailed(response.data?.error);
const error = response?.originalError?.response?.data?.error;
toast(error && error.indexOf('E11000') > -1 ? 'Email already exists!' : 'Something went wrong!');
payload.onValidationFailed();
yield put(
UsersActions.createUserFailure({
loader: payload?.loader,

View File

@@ -39,7 +39,7 @@ const makeFormData = (data) => {
data.preferredInventories.forEach((prefInv, idx) => {
formData.append(`preferredInventories[${idx}]`, prefInv);
});
if (data.image[0].file) formData.append('image', data.image[0].file);
if (data.image[0]?.file) formData.append('image', data.image[0].file);
return formData;
};
@@ -52,7 +52,9 @@ export function* onRequestCreateWarehouse({ payload }) {
);
if (response?.status === 200 && response?.data?.message) {
const warehouse = response?.data?.message;
toast('Warehouse created successfully');
toast.success('Warehouse created successfully', {
theme: 'colored'
});
yield put(
WarehouseActions.createWarehouseSuccess({
loader: payload?.loader,
@@ -64,7 +66,9 @@ export function* onRequestCreateWarehouse({ payload }) {
);
payload.navigateTo(response?.data?.message?._id);
} else {
toast('Failed to create warehouse');
toast.error('Failed to create warehouse', {
theme: 'colored'
});
yield put(
WarehouseActions.createWarehouseFailure({
loader: payload?.loader,
@@ -82,7 +86,9 @@ export function* onRequestEditWarehouse({ payload }) {
makeFormData(payload?.data)
);
if (response?.status === 200 && response?.data?.data) {
toast('Warehouse edited successfully');
toast.success('Warehouse edited successfully', {
theme: 'colored'
});
const warehouse = response?.data?.data;
yield put(
WarehouseActions.editWarehouseSuccess({
@@ -94,7 +100,39 @@ export function* onRequestEditWarehouse({ payload }) {
})
);
} else {
toast('Failed to edit warehouse');
toast.error('Failed to edit warehouse', {
theme: 'colored'
});
yield put(
WarehouseActions.editWarehouseFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export function* onRequestDeleteWarehouse({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug + payload?.warehouseId
);
if (response?.status === 200) {
toast.success('Warehouse deleted successfully', {
theme: 'colored'
});
payload.navigateTo('/setup/warehouse');
yield put(
WarehouseActions.deleteWarehouseSuccess({
loader: payload?.loader,
deletedWarehouseID: payload?.warehouseId
})
);
} else {
toast.error('Failed to delete warehouse', {
theme: 'colored'
});
yield put(
WarehouseActions.editWarehouseFailure({
loader: payload?.loader,
@@ -107,5 +145,6 @@ export function* onRequestEditWarehouse({ payload }) {
export default [
takeLatest(WarehouseTypes.WAREHOUSE_DATA_ACTION, onRequestWarehouseData),
takeLatest(WarehouseTypes.CREATE_WAREHOUSE_ACTION, onRequestCreateWarehouse),
takeLatest(WarehouseTypes.EDIT_WAREHOUSE_ACTION, onRequestEditWarehouse)
takeLatest(WarehouseTypes.EDIT_WAREHOUSE_ACTION, onRequestEditWarehouse),
takeLatest(WarehouseTypes.DELETE_WAREHOUSE_ACTION, onRequestDeleteWarehouse)
];

View File

@@ -1,4 +1,5 @@
import { AuthorizedAPI } from 'config';
import { toast } from 'react-toastify';
import { call, put, takeEvery } from 'redux-saga/effects';
import WarehouseLocationsActions from 'redux/WarehouseLocationsRedux';
import { WarehouseLocationsTypes } from 'redux/WarehouseLocationsRedux';
@@ -42,6 +43,9 @@ export function* onAddRequestLocation({ payload }) {
);
LOGGER.log('add response', response.data);
if (response?.status === 200) {
toast.success('Location created successfully', {
theme: 'colored'
});
yield put(
WarehouseLocationsActions.locationSuccess({
loader: payload?.loader,
@@ -52,7 +56,9 @@ export function* onAddRequestLocation({ payload }) {
})
);
} else {
payload.onFailedLocation(response.data.error);
toast.error('Failed to create warehouse location', {
theme: 'colored'
});
yield put(
WarehouseLocationsActions.locationFailure({
loader: payload?.loader,
@@ -71,6 +77,9 @@ export function* onDeleteRequestLocation({ payload }) {
);
LOGGER.log('delete response', response.data);
if (response?.status === 200) {
toast.success('Location deleted successfully', {
theme: 'colored'
});
yield put(
WarehouseLocationsActions.locationSuccess({
loader: payload?.loader,
@@ -78,7 +87,9 @@ export function* onDeleteRequestLocation({ payload }) {
})
);
} else {
payload.onFailedLocation(response.data.error);
toast.error('Failed to delete warehouse location', {
theme: 'colored'
});
yield put(
WarehouseLocationsActions.locationFailure({
loader: payload?.loader,
@@ -97,6 +108,9 @@ export function* onEditRequestLocation({ payload }) {
);
LOGGER.log('edit response', response.data);
if (response?.status === 200) {
toast.success('Location edited successfully', {
theme: 'colored'
});
yield put(
WarehouseLocationsActions.locationSuccess({
loader: payload?.loader,
@@ -108,7 +122,9 @@ export function* onEditRequestLocation({ payload }) {
})
);
} else {
payload.onFailedLocation(response.data.error);
toast.error('Failed to edit warehouse location', {
theme: 'colored'
});
yield put(
WarehouseLocationsActions.locationFailure({
loader: payload?.loader,

View File

@@ -1,4 +1,5 @@
import { AuthorizedAPI } from 'config';
import { toast } from 'react-toastify';
import { call, put, takeEvery } from 'redux-saga/effects';
import ApiServices from 'services/API/ApiServices';
import WidgetActions, { WidgetTypes } from '../redux/WidgetRedux';
@@ -35,6 +36,12 @@ export function* onEditRequestWidget({ payload }) {
payload?.data
);
if (response?.status === 200) {
toast.success(
`Successfully ${payload?.type !== 'delete' ? payload?.type : 'delet'}ed widget family`,
{
theme: 'colored'
}
);
yield put(
WidgetActions.editWidgetSuccess({
loader: payload?.loader,
@@ -44,6 +51,12 @@ export function* onEditRequestWidget({ payload }) {
})
);
} else {
toast.error(
payload?.type ? `Failed to ${payload?.type} widget family` : 'Failed to fulfill request',
{
theme: 'colored'
}
);
yield put(
WidgetActions.widgetFailure({
loader: payload?.loader,

View File

@@ -8,6 +8,7 @@ import RolesSaga from './Roles';
import PermissionsSaga from './Permissions';
import WarehouseLocationsSaga from './WarehouseLocations';
import WidgetSaga from './Widget';
import LabellingSaga from './Labelling';
import ItemSaga from './Item';
export default function* rootSaga() {
@@ -20,5 +21,6 @@ export default function* rootSaga() {
yield all([...PermissionsSaga]);
yield all([...WarehouseLocationsSaga]);
yield all([...WidgetSaga]);
yield all([...LabellingSaga]);
yield all([...ItemSaga]);
}

View File

@@ -1,6 +1,6 @@
// REST API SERVICES
export default {
post: (API, slug, payload) => API.post(slug, payload),
post: (API, slug, payload, headers) => headers ? API.post(slug, payload, {headers}) : API.post(slug, payload),
get: (API, slug) => API.get(slug),
delete: (API, slug) => {
return API.delete(slug);

View File

@@ -32,17 +32,17 @@ const schema = {
addNewItem: Yup.object({
commonName: Yup.string('Enter details').required(),
formalName: Yup.string('Enter details').required(),
description: Yup.string('Enter details').required(),
manufacturer: Yup.string('Enter details').required(),
size: Yup.string('Enter details').required(),
color: Yup.string('Enter details').required(),
type: Yup.string('Enter details').required(),
unitOfMaterial: Yup.string('Enter details').required(),
description: Yup.string('Enter details'),
manufacturer: Yup.string('Enter details'),
size: Yup.string('Enter details'),
color: Yup.string('Enter details'),
type: Yup.string('Enter details'),
unitOfMaterial: Yup.string('Enter details'),
unitCost: Yup.number().test((val) => val >= 0),
packageCount: Yup.number().test((val) => val >= 0),
countPerPallet: Yup.number().test((val) => val >= 0),
countPerPalletPackage: Yup.number().test((val) => val >= 0),
primaryWidgetFamilyId: Yup.string('Enter details').required(),
primaryWidgetFamilyId: Yup.string('Enter details'),
secondaryWidgetFamilyId: Yup.string('Enter details'),
policiesMetadata: Yup.object({
underStockLevelCount: Yup.number().test((val) => val >= 0),
@@ -54,8 +54,8 @@ const schema = {
}),
addInventory: Yup.object({
name: Yup.string('Enter Widget Name').required('Widget Name is required'),
widgetName: Yup.string('Enter Inventory name').required('Inventory name is required'),
name: Yup.string('Enter Inventory Name').required('Inventory Name is required'),
widgetName: Yup.string('Enter Widget name').required('Widget Name is required'),
policies: Yup.object({
orderTracking: Yup.boolean(),
alerting: Yup.boolean(),
@@ -63,11 +63,13 @@ const schema = {
preferredLocations: Yup.boolean(),
inventory_process: Yup.string()
}),
image: Yup.array()
icons: Yup.string()
}),
createUser: Yup.object({
fullName: Yup.string('Enter Full Name').required('User Name is required'),
email: Yup.string('Enter Email').required('Email is required'),
password: Yup.string('Enter Password').required('Password is required'),
phoneNumber: Yup.string('Enter Phone Numbe').required('Phone Number is required'),
roles: Yup.string('Please select at least one role').required('At least one role is required')
})

View File

@@ -0,0 +1,18 @@
import EquipmentIcon from 'assets/images/EquimpmentIcon';
import FleetIcon from 'assets/images/FleetIcon';
import RawMaterialIcon from 'assets/images/RawMaterialIcon';
export const GetIconFromSlug = (slug) => {
switch (slug) {
case 'equipment':
return <EquipmentIcon />;
case 'fleet':
return <FleetIcon />;
case 'rawmaterial':
return <RawMaterialIcon />;
default:
return <RawMaterialIcon />;
}
};
export const iconSlugs = ['equipment', 'fleet', 'rawmaterial'];