Fixes/ll inventory (#69)

* inventory data changes
* update Inventory changes
* Fixed: removed unnecessary imports
* Disabled: cycle count
* Added: Inventory types sagas
* Fix: null check
* Updated: policies
* Fixed: formik values
* update: allow single image
* Update: Policies control
* Updated: new inventory add form
* Update: new inventory conditional render
* Update: populate formik fields
* Added: Validation
* Added: edit functionality, disabled fields
* Update: housekeeping
* Fix: iconslug and key
* Update: route handling
* Added: endpoints
* Added: widget nested page
* Added: sagas
* Added: redux handling
* Update: new product page functionality
* Added: inventory page functionality
* Fixed: form validation
* Fix: route handling

Co-authored-by: evdigitech <evdigitech@gmail.com>
Co-authored-by: Llewellyn Dsouza <lledsouza2209@gmail.com>
This commit is contained in:
bluestreamlds
2022-03-01 03:18:16 +05:30
committed by GitHub
parent b3d7b0344a
commit af28c0b99c
15 changed files with 1151 additions and 515 deletions

View File

@@ -34,24 +34,24 @@ export default function Tile({ data, children }) {
</Box> </Box>
</AccordionSummary> </AccordionSummary>
<AccordionDetails className={`${classes.row2} ${expand ? null : classes.remove}`}> <AccordionDetails className={`${classes.row2} ${expand ? null : classes.remove}`}>
<Link to={data.path.update}> <Link to={`/setup/inventory/update/${data.id}`}>
<Box className={classes.box}> <Box className={classes.box}>
Update {data.name} <ArrowRightIcon /> Update {data.name} <ArrowRightIcon />
</Box> </Box>
</Link> </Link>
<Link to={data.path.addNew}> <Link to={`/setup/inventory/new-item/${data.widgetname}/${data.id}`}>
<Box className={`${classes.box} ${classes.boxEven}`}> <Box className={`${classes.box} ${classes.boxEven}`}>
Add New {data.name} <ArrowRightIcon /> Add New {data.widgetname} <ArrowRightIcon />
</Box> </Box>
</Link> </Link>
<Link to={data.path.cycleCount}> <Link to="#">
<Box className={classes.box}> <Box className={classes.boxDisabled}>
Cycle Count <ArrowRightIcon /> Cycle Count <ArrowRightIcon />
</Box> </Box>
</Link> </Link>
<Link to={data.path.list}> <Link to="/">
<Box className={`${classes.box} ${classes.boxEven}`}> <Box className={`${classes.box} ${classes.boxEven}`}>
{data.name} List <ArrowRightIcon /> {data.widgetname} List <ArrowRightIcon />
</Box> </Box>
</Link> </Link>
</AccordionDetails> </AccordionDetails>

View File

@@ -83,6 +83,23 @@ const useStyles = makeStyles({
}, },
remove: { remove: {
display: 'none' display: 'none'
},
boxDisabled: {
display: 'flex',
color: '#cccccc',
alignItems: 'center',
justifyContent: 'space-between',
padding: '13px 0 13px 10px',
borderTop: '1px solid lightgray',
'& svg': {
opacity: '0'
},
'&:hover': {
cursor: 'auto',
'& svg': {
opacity: '0'
}
}
} }
}); });

View File

@@ -0,0 +1,329 @@
/* eslint-disable indent */
import {
Box,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Grid,
TextField
} from '@mui/material';
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import IconButton from '@mui/material/IconButton';
import MDButton from 'components/Button';
import { useDispatch, useSelector } from 'react-redux';
import { WidgetSelectors } from 'redux/WidgetRedux';
import WidgetActions from 'redux/WidgetRedux';
import { API } from 'constant';
import { useFormik } from 'formik';
import LOGGER from 'services/Logger';
function MaterialForm({ formType, setFormOpen, selected, inventoryId }) {
const dispatch = useDispatch();
const formik = useFormik({
initialValues:
formType === 'family'
? { name: '', inventoryId }
: formType === 'subfamily'
? { name: '', inventoryId, parentId: selected._id || '' }
: { name: formType.name, inventoryId, parentId: formType.parent?._id || undefined },
onSubmit: (values) => {
LOGGER.log('Form values and field info', values);
['family', 'subfamily'].includes(formType)
? dispatch(
WidgetActions.editWidgetRequest({
loader: 'location-request',
slug: API.ADD_WIDGET_FAMILY,
method: 'post',
data: values,
type: 'add'
})
)
: dispatch(
WidgetActions.editWidgetRequest({
loader: 'location-request',
slug: `${API.EDIT_WIDGET_FAMILY}${inventoryId}`,
method: 'patch',
data: values,
type: 'edit'
})
);
setFormOpen(false);
}
});
return (
<Dialog
open
onClose={() => {
setFormOpen(false);
}}
>
<DialogTitle>
{['family', 'subfamily'].includes(formType) ? 'Create' : 'Edit'} material{' '}
{['family', 'subfamily'].includes(formType) ? formType : null}
</DialogTitle>
<DialogContent>
{/* <DialogContentText>Some more text if needed</DialogContentText> */}
<TextField
autoFocus
fullWidth
margin="dense"
label="Name"
type="text"
name="name"
variant="standard"
value={formik.values.name}
error={formik.touched.name && Boolean(formik.errors.name)}
helperText={formik.touched.name && formik.errors.name}
onChange={formik.handleChange}
/>
</DialogContent>
<DialogActions>
<MDButton
onClick={() => {
setFormOpen(false);
}}
>
Cancel
</MDButton>
<MDButton onClick={formik.handleSubmit}>Save</MDButton>
</DialogActions>
</Dialog>
);
}
MaterialForm.propTypes = {
formType: PropTypes.any,
setFormOpen: PropTypes.any,
selected: PropTypes.any,
inventoryId: PropTypes.any
};
function WidgetNestedDataTable({
data,
selected,
setSelected,
setFormOpen,
setFormType,
inventoryId
}) {
const [open, setOpen] = React.useState(false);
const widgetChildren = useSelector(WidgetSelectors.getWidgetsByParentId(data._id));
return (
<>
<Box
sx={{
borderLeftWidth: '2px',
borderLeftStyle: 'solid',
borderLeftColor: '#aedaed',
borderTopWidth: '1px',
borderTopStyle: 'solid',
borderTopColor: '#BDD0DB',
background:
selected?._id === data._id
? 'linear-gradient(135deg, ' + '#aedaed' + ' 0%, #f9f9f9 20%)'
: '#f9f9f9'
}}
>
<Grid container key={data._id}>
<Grid item xs={2}>
<IconButton
disabled={!widgetChildren?.length}
aria-label="expand row"
size="small"
sx={{ marginLeft: '12px' }}
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
<MDButton
size="small"
variant="contained"
color="primary"
sx={{
textTransform: 'capitalize',
minWidth: '45px',
minHeight: '28px',
marginLeft: '5px',
marginRight: '20px',
boxShadow: 'none',
fontWeight: '500',
padding: '0'
}}
onClick={() => {
setFormType(data);
setFormOpen(true);
}}
>
EDIT
</MDButton>
</Grid>
<Grid
container
item
xs={10}
onClick={() => {
setSelected(data);
}}
>
<Grid item xs={9}>
{data.name}
</Grid>
<Grid item xs={1}>
<MDButton
disabled
size="small"
variant="contained"
color="error"
sx={{
textTransform: 'capitalize',
minWidth: '45px',
minHeight: '28px',
marginLeft: '5px',
marginRight: '20px',
boxShadow: 'none',
fontWeight: '500',
padding: '0 6'
}}
onClick={() => {
// dispatch(
// WarehouseLocationsActions.deleteLocationRequest({
// loader: 'location-request',
// slug: API.LOCATION_DELETE,
// method: 'post',
// data: { type: data.location, id: data.id }
// })
// );
}}
>
DELETE
</MDButton>
</Grid>
</Grid>
</Grid>
{open && widgetChildren ? (
<Box Box sx={{ marginLeft: '25px', marginBottom: '15px' }}>
{/* Add headers here */}
{widgetChildren.map((data) => (
<WidgetNestedDataTable
key={data._id}
data={data}
selected={selected}
setSelected={setSelected}
setFormOpen={setFormOpen}
setFormType={setFormType}
inventoryId={inventoryId}
/>
))}
</Box>
) : null}
</Box>
</>
);
}
WidgetNestedDataTable.propTypes = {
data: PropTypes.any,
selected: PropTypes.any,
setSelected: PropTypes.any,
setFormOpen: PropTypes.any,
setFormType: PropTypes.any,
inventoryId: PropTypes.any
};
const bottomButtonStyling = {
width: '200px',
marginTop: '25px',
textTransform: 'uppercase',
borderRadius: '100px',
padding: '13px 30px'
};
function WidgetNestedDataTableContainer({ inventoryId }) {
const dispatch = useDispatch();
const [selected, setSelected] = React.useState(null);
const [formType, setFormType] = React.useState('family');
const [formOpen, setFormOpen] = React.useState(false);
const widgetFamilyData = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
useEffect(() => {
dispatch(
WidgetActions.widgetRequest({
loader: 'location-request',
slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${inventoryId}`,
method: 'get'
})
);
}, []);
return (
<>
{widgetFamilyData &&
widgetFamilyData.map((p) => (
<WidgetNestedDataTable
key={p._id}
data={p}
selected={selected}
setSelected={setSelected}
setFormOpen={setFormOpen}
setFormType={setFormType}
/>
))}
<Grid container spacing={5} justifyContent="center">
<Grid item>
<MDButton
size="medium"
sx={bottomButtonStyling}
color="primary"
variant="contained"
onClick={() => {
setFormType('family');
setFormOpen(true);
}}
>
Add Family
</MDButton>
</Grid>
<Grid item>
<MDButton
size="medium"
sx={bottomButtonStyling}
disabled={selected?.parent}
color={selected && !selected?.parent ? 'primary' : 'secondary'}
variant="contained"
onClick={() => {
setFormType('subfamily');
setFormOpen(true);
}}
>
Add SubFamily
</MDButton>
</Grid>
</Grid>
{/* <pre>{JSON.stringify(selected, null, 4)}</pre> */}
{formOpen && (
<MaterialForm
formType={formType}
setFormOpen={setFormOpen}
selected={selected}
inventoryId={inventoryId}
/>
)}
</>
);
}
WidgetNestedDataTableContainer.propTypes = {
inventoryId: PropTypes.any
};
export default WidgetNestedDataTableContainer;

View File

@@ -13,5 +13,10 @@ export default {
ADD_NEW_SUBLEVEL: '/sublevel', ADD_NEW_SUBLEVEL: '/sublevel',
ADD_PRODUCT: '/item/', ADD_PRODUCT: '/item/',
ADD_INVENTORY: '/inventory', ADD_INVENTORY: '/inventory',
LOCATION_DELETE: '/dashboard/delete-location' GET_INVENTORY: '/inventory/all?page=0&perPage=50',
GET_INVENTORY_TYPES: '/inventory/types',
LOCATION_DELETE: '/dashboard/delete-location',
GET_WIDGET_FAMILY_BY_INVENTORY: '/widget-family/search-by-inventory?inventory=',
ADD_WIDGET_FAMILY: '/widget-family',
EDIT_WIDGET_FAMILY: '/widget-family/'
}; };

View File

@@ -15,11 +15,14 @@ import CrossIcon from 'assets/images/CrossIcon';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import schema from 'services/ValidationServices'; import schema from 'services/ValidationServices';
import MDInput from 'components/MDInput'; import MDInput from 'components/MDInput';
import { useDispatch } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import ProductActions from 'redux/ProductsRedux'; import ProductActions from 'redux/ProductsRedux';
import { API } from 'constant'; import { API } from 'constant';
import LOGGER from 'services/Logger'; import LOGGER from 'services/Logger';
import Breadcrumbs from 'components/Breadcrumbs'; import Breadcrumbs from 'components/Breadcrumbs';
import { useParams } from 'react-router-dom';
import { WidgetSelectors } from 'redux/WidgetRedux';
import WidgetActions from 'redux/WidgetRedux';
const useStyles = makeStyles({ const useStyles = makeStyles({
labelSize: { labelSize: {
@@ -43,47 +46,53 @@ const useStyles = makeStyles({
} }
}); });
const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet']; function AddNewItem() {
function AddNewProduct() {
const classes = useStyles(); const classes = useStyles();
const { widgetName, inventoryId } = useParams();
const dispatch = useDispatch(); const dispatch = useDispatch();
const [Manufacturer, setManufacturer] = React.useState(''); const [Manufacturer, setManufacturer] = React.useState('');
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const handleClickOpen = () => { React.useEffect(() => {
setOpen(true); dispatch(
}; WidgetActions.widgetRequest({
loader: 'location-request',
slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${inventoryId}`,
method: 'get'
})
);
}, []);
const handleClose = () => { const [pFam, setPFam] = React.useState(null);
setOpen(false); const primaryFamily = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
}; const secondaryFamily = useSelector(WidgetSelectors.getWidgetsByParentId(pFam));
LOGGER.log({ primaryFamily, secondaryFamily });
const handleChange = (event) => {
setManufacturer(event.target.value);
};
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
warehousename: '', commonName: '',
formalName: '',
description: '', description: '',
manufacturer: '', manufacturer: '',
type: '',
unitofmaterial: '',
packagecount: '',
formalname: '',
size: '', size: '',
color: '', color: '',
unitcost: '', type: '',
countperpallet: '', unitOfMaterial: '',
countperpalletpackage: '', unitCost: 0,
productfamilyassociation: '', packageCount: 0,
under: '', countPerPallet: 0,
over: '', countPerPalletPackage: 0,
alert: '', primaryWidgetFamilyId: '',
secondaryWidgetFamilyId: '',
policiesMetadata: {
underStockLevelCount: 0,
overStockLevelCount: 0,
alertStockLevelCount: 0,
reorderStockLevelCount: 0
},
images: [] images: []
}, },
validationSchema: schema.addNewProduct, validationSchema: schema.addNewItem,
onSubmit: (values, onSubmitProps) => { onSubmit: (values, onSubmitProps) => {
LOGGER.log('values', values); LOGGER.log('values', values);
dispatch( dispatch(
@@ -92,25 +101,33 @@ function AddNewProduct() {
slug: API.ADD_PRODUCT, slug: API.ADD_PRODUCT,
method: 'post', method: 'post',
data: { data: {
commonName: values.warehousename, commonName: values.commonName,
formalName: values.formalname, formalName: values.formalName,
description: values.description, description: values.description,
manufacturer: values.manufacturer, manufacturer: values.manufacturer,
size: values.size, size: values.size,
color: values.color, color: values.color,
type: values.type, type: values.type,
unitOfMaterial: values.unitofmaterial, unitOfMaterial: values.unitOfMaterial,
unitCost: values.unitcost, unitCost: values.unitCost,
packageCount: values.packagecount, packageCount: values.packageCount,
countPerPallet: values.countperpallet, countPerPallet: values.countPerPallet,
countPerPalletPackage: values.countperpalletpackage, countPerPalletPackage: values.countPerPalletPackage,
customAttributes: [ customAttributes: [],
{ fieldName: 'someName', fieldType: 'String', fieldValue: 'someValue' } policiesMetadata: {
], underStockLevelCount: values.policiesMetadata.underStockLevelCount,
widgetFamilyId: '61dcdd10699e8f55b44c606d' overStockLevelCount: values.policiesMetadata.overStockLevelCount,
alertStockLevelCount: values.policiesMetadata.alertStockLevelCount,
reorderStockLevelCount: values.policiesMetadata.reorderStockLevelCount
},
widgetFamilyId:
values.secondaryWidgetFamilyId === ''
? values.primaryWidgetFamilyId
: values.secondaryWidgetFamilyId
} }
}) })
); );
// navigate to inventory page?
onSubmitProps.resetForm(); onSubmitProps.resetForm();
} }
}); });
@@ -124,8 +141,8 @@ function AddNewProduct() {
{ name: 'Home', path: '/home' }, { name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' }, { name: 'Setup', path: '/setup' },
{ name: 'Inventory', path: '/setup/inventory' }, { name: 'Inventory', path: '/setup/inventory' },
{ name: 'Products' }, { name: `${widgetName || 'Item'}` },
{ name: 'Add New Product' } { name: `Add New ${widgetName || 'Item'}` }
]} ]}
/> />
<Box mx={3} my={3}> <Box mx={3} my={3}>
@@ -135,16 +152,16 @@ function AddNewProduct() {
<Grid item xs={12} sm={6} md={6}> <Grid item xs={12} sm={6} md={6}>
<Box component="div" sx={{ marginBottom: '24px' }}> <Box component="div" sx={{ marginBottom: '24px' }}>
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
Warehouse name {widgetName || 'Item'} name
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="warehousename" name="commonName"
type="text" type="text"
variant="outlined" variant="outlined"
value={formik.values.warehousename} value={formik.values.commonName}
error={formik.touched.warehousename && Boolean(formik.errors.warehousename)} error={formik.touched.commonName && Boolean(formik.errors.commonName)}
helperText={formik.touched.warehousename && formik.errors.warehousename} helperText={formik.touched.commonName && formik.errors.commonName}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -170,91 +187,46 @@ function AddNewProduct() {
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
Manufacturer Manufacturer
</Box> </Box>
<FormControl fullWidth> <MDInput
<Select fullWidth
select name="manufacturer"
fullWidth type="text"
variant="outlined" variant="outlined"
name="manufacturer" value={formik.values.manufacturer}
value={formik.values.manufacturer} error={formik.touched.manufacturer && Boolean(formik.errors.manufacturer)}
error={formik.touched.manufacturer && Boolean(formik.errors.manufacturer)} helperText={formik.touched.manufacturer && formik.errors.manufacturer}
onChange={formik.handleChange} onChange={formik.handleChange}
> />
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
<FormHelperText>
{formik.errors.manufacturer &&
formik.touched.manufacturer &&
formik.errors.manufacturer}
</FormHelperText>
</FormControl>
</Box> </Box>
<Box component="div" sx={{ marginBottom: '24px' }}> <Box component="div" sx={{ marginBottom: '24px' }}>
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
Type Type
</Box> </Box>
<FormControl fullWidth> <MDInput
<Select fullWidth
select name="type"
fullWidth type="text"
variant="outlined" variant="outlined"
name="type" value={formik.values.type}
value={formik.values.type} error={formik.touched.type && Boolean(formik.errors.type)}
error={formik.touched.type && Boolean(formik.errors.type)} helperText={formik.touched.type && formik.errors.type}
onChange={formik.handleChange} onChange={formik.handleChange}
> />
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
<FormHelperText>
{formik.errors.type && formik.touched.type && formik.errors.type}
</FormHelperText>
</FormControl>
</Box> </Box>
<Box component="div" sx={{ marginBottom: '24px' }}> <Box component="div" sx={{ marginBottom: '24px' }}>
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
Unit of Material Unit of Material
</Box> </Box>
<FormControl fullWidth> <MDInput
<Select fullWidth
select name="unitOfMaterial"
fullWidth type="text"
variant="outlined" variant="outlined"
name="unitofmaterial" value={formik.values.unitOfMaterial}
value={formik.values.unitofmaterial} error={formik.touched.unitOfMaterial && Boolean(formik.errors.unitOfMaterial)}
error={ helperText={formik.touched.unitOfMaterial && formik.errors.unitOfMaterial}
formik.touched.unitofmaterial && Boolean(formik.errors.unitofmaterial) onChange={formik.handleChange}
} />
onChange={formik.handleChange}
>
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
<FormHelperText>
{formik.errors.unitofmaterial &&
formik.touched.unitofmaterial &&
formik.errors.unitofmaterial}
</FormHelperText>
</FormControl>
</Box> </Box>
<Box component="div" sx={{ marginBottom: '24px' }}> <Box component="div" sx={{ marginBottom: '24px' }}>
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
@@ -262,12 +234,12 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="packagecount" name="packageCount"
type="text" type="number"
variant="outlined" variant="outlined"
value={formik.values.packagecount} value={formik.values.packageCount}
error={formik.touched.packagecount && Boolean(formik.errors.packagecount)} error={formik.touched.packageCount && Boolean(formik.errors.packageCount)}
helperText={formik.touched.packagecount && formik.errors.packagecount} helperText={formik.touched.packageCount && formik.errors.packageCount}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -279,12 +251,12 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="formalname" name="formalName"
type="text" type="text"
variant="outlined" variant="outlined"
value={formik.values.formalname} value={formik.values.formalName}
error={formik.touched.formalname && Boolean(formik.errors.formalname)} error={formik.touched.formalName && Boolean(formik.errors.formalName)}
helperText={formik.touched.formalname && formik.errors.formalname} helperText={formik.touched.formalName && formik.errors.formalName}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -292,57 +264,31 @@ function AddNewProduct() {
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
Size Size
</Box> </Box>
<FormControl fullWidth> <MDInput
<Select fullWidth
select name="size"
fullWidth type="text"
variant="outlined" variant="outlined"
name="size" value={formik.values.size}
value={formik.values.size} error={formik.touched.size && Boolean(formik.errors.size)}
error={formik.touched.size && Boolean(formik.errors.size)} helperText={formik.touched.size && formik.errors.size}
onChange={formik.handleChange} onChange={formik.handleChange}
> />
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
<FormHelperText>
{formik.errors.size && formik.touched.size && formik.errors.size}
</FormHelperText>
</FormControl>
</Box> </Box>
<Box component="div" sx={{ marginBottom: '24px' }}> <Box component="div" sx={{ marginBottom: '24px' }}>
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
Color Color
</Box> </Box>
<FormControl fullWidth> <MDInput
<Select fullWidth
select name="color"
fullWidth type="text"
variant="outlined" variant="outlined"
name="color" value={formik.values.color}
value={formik.values.color} error={formik.touched.color && Boolean(formik.errors.color)}
error={formik.touched.color && Boolean(formik.errors.color)} helperText={formik.touched.color && formik.errors.color}
onChange={formik.handleChange} onChange={formik.handleChange}
> />
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
<FormHelperText>
{formik.errors.color && formik.touched.color && formik.errors.color}
</FormHelperText>
</FormControl>
</Box> </Box>
<Box component="div" sx={{ marginBottom: '24px' }}> <Box component="div" sx={{ marginBottom: '24px' }}>
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
@@ -350,12 +296,12 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="unitcost" name="unitCost"
type="text" type="number"
variant="outlined" variant="outlined"
value={formik.values.unitcost} value={formik.values.unitCost}
error={formik.touched.unitcost && Boolean(formik.errors.unitcost)} error={formik.touched.unitCost && Boolean(formik.errors.unitCost)}
helperText={formik.touched.unitcost && formik.errors.unitcost} helperText={formik.touched.unitCost && formik.errors.unitCost}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -365,12 +311,12 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="countperpallet" name="countPerPallet"
type="text" type="number"
variant="outlined" variant="outlined"
value={formik.values.countperpallet} value={formik.values.countPerPallet}
error={formik.touched.countperpallet && Boolean(formik.errors.countperpallet)} error={formik.touched.countPerPallet && Boolean(formik.errors.countPerPallet)}
helperText={formik.touched.countperpallet && formik.errors.countperpallet} helperText={formik.touched.countPerPallet && formik.errors.countPerPallet}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -380,16 +326,16 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="countperpalletpackage" name="countPerPalletPackage"
type="text" type="number"
variant="outlined" variant="outlined"
value={formik.values.countperpalletpackage} value={formik.values.countPerPalletPackage}
error={ error={
formik.touched.countperpalletpackage && formik.touched.countPerPalletPackage &&
Boolean(formik.errors.countperpalletpackage) Boolean(formik.errors.countPerPalletPackage)
} }
helperText={ helperText={
formik.touched.countperpalletpackage && formik.errors.countperpalletpackage formik.touched.countPerPalletPackage && formik.errors.countPerPalletPackage
} }
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
@@ -398,32 +344,36 @@ function AddNewProduct() {
<Box component="div" className={classes.labelSize}> <Box component="div" className={classes.labelSize}>
Product Family Association Product Family Association
</Box> </Box>
<FormControl fullWidth sx={{ marginBottom: '32px' }}> <FormControl fullWidth>
<Select <Select
select select
fullWidth fullWidth
variant="outlined" variant="outlined"
name="productfamilyassociation" name="primaryWidgetFamilyId"
value={formik.values.productfamilyassociation} value={formik.values.primaryWidgetFamilyId}
error={ error={
formik.touched.productfamilyassociation && formik.touched.primaryWidgetFamilyId &&
Boolean(formik.errors.productfamilyassociation) Boolean(formik.errors.primaryWidgetFamilyId)
} }
onChange={formik.handleChange} onChange={(e, ...rest) => {
setPFam(e.target.value);
formik.handleChange(e, ...rest);
}}
> >
<MenuItem key={''} value={''}> <MenuItem key={'none'} value={''}>
None Selected None Selected
</MenuItem> </MenuItem>
{inventoryTypes.map((name) => ( {primaryFamily &&
<MenuItem key={name} value={name}> primaryFamily.map((fam) => (
{name} <MenuItem key={fam._id} value={fam._id}>
</MenuItem> {fam.name}
))} </MenuItem>
))}
</Select> </Select>
<FormHelperText> <FormHelperText>
{formik.errors.productfamilyassociation && {formik.errors.primaryWidgetFamilyId &&
formik.touched.productfamilyassociation && formik.touched.primaryWidgetFamilyId &&
formik.errors.productfamilyassociation} formik.errors.primaryWidgetFamilyId}
</FormHelperText> </FormHelperText>
</FormControl> </FormControl>
<FormControl fullWidth> <FormControl fullWidth>
@@ -431,27 +381,28 @@ function AddNewProduct() {
select select
fullWidth fullWidth
variant="outlined" variant="outlined"
name="productfamilyassociation" name="secondaryWidgetFamilyId"
value={formik.values.productfamilyassociation} value={formik.values.secondaryWidgetFamilyId}
error={ error={
formik.touched.productfamilyassociation && formik.touched.secondaryWidgetFamilyId &&
Boolean(formik.errors.productfamilyassociation) Boolean(formik.errors.secondaryWidgetFamilyId)
} }
onChange={formik.handleChange} onChange={formik.handleChange}
> >
<MenuItem key={''} value={''}> <MenuItem key={'none'} value={''}>
None Selected None Selected
</MenuItem> </MenuItem>
{inventoryTypes.map((name) => ( {secondaryFamily &&
<MenuItem key={name} value={name}> secondaryFamily.map((fam) => (
{name} <MenuItem key={fam._id} value={fam._id}>
</MenuItem> {fam.name}
))} </MenuItem>
))}
</Select> </Select>
<FormHelperText> <FormHelperText>
{formik.errors.productfamilyassociation && {formik.errors.secondaryWidgetFamilyId &&
formik.touched.productfamilyassociation && formik.touched.secondaryWidgetFamilyId &&
formik.errors.productfamilyassociation} formik.errors.secondaryWidgetFamilyId}
</FormHelperText> </FormHelperText>
</FormControl> </FormControl>
</Box> </Box>
@@ -459,7 +410,6 @@ function AddNewProduct() {
</Grid> </Grid>
<Box> <Box>
<ImageUpload <ImageUpload
multiple
heading="Upload Product Image" heading="Upload Product Image"
accept="image/*" accept="image/*"
images={formik.values.images} images={formik.values.images}
@@ -482,7 +432,9 @@ function AddNewProduct() {
size="large" size="large"
color="primary" color="primary"
variant="outlined" variant="outlined"
onClick={handleClickOpen} onClick={() => {
setOpen(true);
}}
> >
add custom fields add custom fields
</MDButton> </MDButton>
@@ -508,12 +460,10 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="under" name="policiesMetadata.underStockLevelCount"
type="number" type="number"
variant="outlined" variant="outlined"
value={formik.values.under} value={formik.values.policiesMetadata.underStockLevelCount}
error={formik.touched.under && Boolean(formik.errors.under)}
helperText={formik.touched.under && formik.errors.under}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -523,12 +473,10 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="over" name="policiesMetadata.overStockLevelCount"
type="number" type="number"
variant="outlined" variant="outlined"
value={formik.values.over} value={formik.values.policiesMetadata.overStockLevelCount}
error={formik.touched.over && Boolean(formik.errors.over)}
helperText={formik.touched.over && formik.errors.over}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -538,12 +486,23 @@ function AddNewProduct() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="alert" name="policiesMetadata.alertStockLevelCount"
type="number" type="number"
variant="outlined" variant="outlined"
value={formik.values.alert} value={formik.values.policiesMetadata.alertStockLevelCount}
error={formik.touched.alert && Boolean(formik.errors.alert)} onChange={formik.handleChange}
helperText={formik.touched.alert && formik.errors.alert} />
</Box>
<Box>
<Box component="div" className={classes.labelSize}>
Reorder
</Box>
<MDInput
fullWidth
name="policiesMetadata.reorderStockLevelCount"
type="number"
variant="outlined"
value={formik.values.policiesMetadata.reorderStockLevelCount}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
@@ -558,10 +517,10 @@ function AddNewProduct() {
}} }}
> >
<MDButton size="medium" color="error" variant="outlined"> <MDButton size="medium" color="error" variant="outlined">
cancel Cancel
</MDButton> </MDButton>
<MDButton size="medium" color="primary" variant="contained" type="submit"> <MDButton size="medium" color="primary" variant="contained" type="submit">
add ITem Add {widgetName}
</MDButton> </MDButton>
</Box> </Box>
</Box> </Box>
@@ -584,7 +543,12 @@ function AddNewProduct() {
padding: '10px 20px' padding: '10px 20px'
}} }}
> >
<MDButton sx={{ padding: '0px', minWidth: '14px' }} onClick={handleClose}> <MDButton
sx={{ padding: '0px', minWidth: '14px' }}
onClick={() => {
setOpen(false);
}}
>
<CrossIcon className={classes.cursorPointer} /> <CrossIcon className={classes.cursorPointer} />
</MDButton> </MDButton>
</Box> </Box>
@@ -628,7 +592,9 @@ function AddNewProduct() {
return <em>Placeholder</em>; return <em>Placeholder</em>;
} }
}} }}
onChange={handleChange} onChange={(event) => {
setManufacturer(event.target.value);
}}
> >
<MenuItem value={10}>Ten</MenuItem> <MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem> <MenuItem value={20}>Twenty</MenuItem>
@@ -648,7 +614,9 @@ function AddNewProduct() {
return <em>Placeholder</em>; return <em>Placeholder</em>;
} }
}} }}
onChange={handleChange} onChange={(event) => {
setManufacturer(event.target.value);
}}
> >
<MenuItem value={10}>Ten</MenuItem> <MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem> <MenuItem value={20}>Twenty</MenuItem>
@@ -783,4 +751,4 @@ function AddNewProduct() {
); );
} }
export default AddNewProduct; export default AddNewItem;

View File

@@ -1,21 +1,24 @@
import React from 'react';
import DashboardNavbar from 'components/DashboardNavbar'; import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout'; import DashboardLayout from 'layouts/DashboardLayout';
import { Box, Grid, MenuItem, Select, TableBody, TableCell, TableRow } from '@mui/material'; import { Box, Grid, MenuItem, Select } from '@mui/material';
import MDInput from 'components/MDInput'; import MDInput from 'components/MDInput';
import ImageUpload from 'components/ImageUpload'; import ImageUpload from 'components/ImageUpload';
import Switch from 'components/Switch'; import Switch from 'components/Switch';
import MDTypography from 'components/MDTypography'; import MDTypography from 'components/MDTypography';
import MDBox from 'components/MDBox'; import MDBox from 'components/MDBox';
import Dropdown from 'components/Dropdown';
import MDButton from 'components/Button'; import MDButton from 'components/Button';
import BasicTable from 'components/BasicTable';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import schema from 'services/ValidationServices'; import schema from 'services/ValidationServices';
import { API } from 'constant'; import { API } from 'constant';
import { useDispatch } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import InventoryActions from 'redux/InventoryRedux'; import InventoryActions from 'redux/InventoryRedux';
import LOGGER from 'services/Logger'; import LOGGER from 'services/Logger';
import Breadcrumbs from 'components/Breadcrumbs'; import Breadcrumbs from 'components/Breadcrumbs';
import { useParams } from 'react-router-dom';
import { InventorySelectors } from 'redux/InventoryRedux';
import { useNavigate } from 'react-router-dom';
import WidgetNestedDataTable from 'components/WidgetNestedDataTable';
const customStyles = { const customStyles = {
labelSize: { labelSize: {
@@ -49,110 +52,98 @@ const customStyles = {
} }
}; };
const stockBox = [ const definedPolicies = [
{ {
text: 'Stock Tracking' text: 'Order Tracking',
key: 'orderTracking'
}, },
{ {
text: 'Replenishment' text: 'Replenishment',
key: 'replenishment'
}, },
{ {
text: 'Alerting' text: 'Alerting',
key: 'alerting'
}, },
{ {
text: 'Check In/Out' text: 'Location',
}, key: 'preferredLocations'
{
text: 'Maintenance'
},
{
text: 'Location'
}
];
const records = [
{
level1: 'Ipsum',
level2: 'Vivera'
},
{
level1: 'Ipsum',
level2: 'Vivera'
}
];
const headCells = [
{
id: 'level1',
label: 'Level 1'
},
{
id: 'level2',
label: 'Level 2'
}
];
const dropdownData = [
{
ID: '1',
displayname: 'Regular, full time'
},
{
ID: '2',
displayname: 'Regular, part time'
},
{
ID: '3',
displayname: 'Contractor- Arise Max'
}
];
const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet'];
const dataLevel = [
{
placeholder: 'Lorem Ipsum',
label: 'Level 1'
},
{
placeholder: 'Lorem Ipsum',
label: 'Level 2'
} }
]; ];
function InventoryScreen() { function InventoryScreen() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate();
const { inventoryId } = useParams();
const currentInventoryData = useSelector(InventorySelectors.getInventoryDetailById(inventoryId));
LOGGER.log({ currentInventoryData });
// const [inventoryAllData, setInventoryAllData] = useState([]);
// const initialInventoryName='';
// 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);
/* eslint-disable indent */
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: inventoryId
inventoryname: '', ? {
inventorytype: '', name: currentInventoryData.name,
widgetname: '', widgetName: currentInventoryData.widgetName,
policies: false, policies: {
images: [] orderTracking: currentInventoryData.policies.orderTracking,
}, alerting: currentInventoryData.policies.alerting,
replenishment: currentInventoryData.policies.replenishment,
preferredLocations: false, // TODO: change later when implemented on BE
inventory_process: currentInventoryData.policies.inventory_process
},
image: [{ src: currentInventoryData.image }]
}
: {
name: '',
widgetName: '',
policies: {
orderTracking: false,
alerting: false,
replenishment: false,
preferredLocations: false,
inventory_process: 'CCR'
},
image: []
},
validationSchema: schema.addInventory, validationSchema: schema.addInventory,
onSubmit: (values, onSubmitProps) => { onSubmit: (values, onSubmitProps) => {
LOGGER.log('values', values); LOGGER.log('values', values);
dispatch( inventoryId
InventoryActions.addInventoryAction({ ? dispatch(
loader: 'loading-request', InventoryActions.updateInventoryAction({
slug: API.ADD_INVENTORY, loader: 'loading-request',
method: 'post', slug: `${API.ADD_INVENTORY}/${inventoryId}`,
data: { method: 'patch',
name: values.inventoryname, data: {
type: values.inventorytype, ...values,
policies: { image: values.image[0]?.file || null
alerting: {
lowestStockLevel: true,
highestStockLevel: true,
alertStockLevel: true,
reOrderLevel: true
} }
} })
} )
}) : dispatch(
); InventoryActions.addInventoryAction({
loader: 'loading-request',
slug: API.ADD_INVENTORY,
method: 'post',
data: {
...values,
image: values.image[0]?.file || null
}
})
);
// navigate to edit inventory page
onSubmitProps.resetForm(); onSubmitProps.resetForm();
} }
}); });
@@ -187,67 +178,43 @@ function InventoryScreen() {
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="inventoryname" disabled={inventoryId}
name="name"
type="text" type="text"
variant="outlined" variant="outlined"
value={formik.values.inventoryname} value={formik.values.name}
error={formik.touched.inventoryname && Boolean(formik.errors.inventoryname)} error={formik.touched.name && Boolean(formik.errors.name)}
helpertText={formik.touched.inventoryname && formik.errors.inventoryname} helpertText={formik.touched.name && formik.errors.name}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Box> </Box>
<Box component="div" sx={{ marginBottom: '20px' }}>
<Box component="div" sx={customStyles.labelSize}>
Inventory Type
</Box>
<Select
select
fullWidth
variant="outlined"
name="inventorytype"
value={formik.values.inventorytype}
error={formik.touched.inventorytype && Boolean(formik.errors.inventorytype)}
helperText={formik.touched.inventorytype && formik.errors.inventorytype}
onChange={formik.handleChange}
>
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</Box>
<Grid item xs={12} sm={12} md={12}> <Grid item xs={12}>
<Box component="div" sx={customStyles.labelSize}> <Box component="div" sx={customStyles.labelSize}>
Widget Name Widget Name
</Box> </Box>
<MDInput <MDInput
fullWidth fullWidth
name="widgetname" disabled={inventoryId}
name="widgetName"
type="text" type="text"
variant="outlined" variant="outlined"
value={formik.values.widgetname} value={formik.values.widgetName}
error={formik.touched.widgetname && Boolean(formik.errors.widgetname)} error={formik.touched.widgetName && Boolean(formik.errors.widgetName)}
helpertext={formik.touched.widgetname && formik.errors.widgetname} helpertext={formik.touched.widgetName && formik.errors.widgetName}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</Grid> </Grid>
<MDBox sx={{ my: 4 }}> <MDBox sx={{ my: 4 }}>
<MDTypography variant="h5">Policies</MDTypography> <MDTypography variant="h5">Policies</MDTypography>
<MDTypography sx={customStyles.textSize}> <MDTypography sx={customStyles.textSize}>
Egestas pulvinar ornare vulputate porttitor consectetur condimentum at tellus Choose policies to be applied
quis. Leo pellentesque ipsum, a purus dignissim aliquam, orci. Elementum
ullamcorper a sit eleifend ante ullamcorper ornare mi pharetra.
</MDTypography> </MDTypography>
</MDBox> </MDBox>
<MDBox <MDBox
mr={{ xs: 0, xl: 8 }} mr={{ xs: 0, xl: 8 }}
sx={{ sx={{
width: '40%', width: '60%',
padding: '12.5px 10px', padding: '12.5px 10px',
backgroundColor: '#fff', backgroundColor: '#fff',
border: 'solid 0.5px #c4c4c4', border: 'solid 0.5px #c4c4c4',
@@ -256,87 +223,69 @@ function InventoryScreen() {
}} }}
> >
<div sx={customStyles.wrap}> <div sx={customStyles.wrap}>
{stockBox.map((item) => ( {definedPolicies.map((item) => (
<> <div sx={customStyles.gridWrap} key={item.key}>
<div sx={customStyles.gridWrap}> <MDTypography sx={customStyles.textWrap}>{item.text}</MDTypography>
<MDTypography sx={customStyles.textWrap}>{item.text}</MDTypography> <Switch
<Switch disabled={inventoryId}
name="policies" name={`policies.${item.key}`}
checked={formik.values.policies} checked={formik.values.policies[item.key]}
onChange={formik.handleChange} onChange={formik.handleChange}
/> />
</div> </div>
</>
))} ))}
<Box component="div" sx={{ marginBottom: '20px' }}>
<Box component="div" sx={customStyles.labelSize}>
Inventory Process
</Box>
<Select
select
fullWidth
disabled={inventoryId}
variant="outlined"
name="policies.inventory_process"
value={formik.values.policies.inventory_process}
onChange={formik.handleChange}
>
<MenuItem key="CCR" value="CCR">
CCR
</MenuItem>
<MenuItem key="PPR" value="PPR">
PPR
</MenuItem>
</Select>
</Box>
</div> </div>
</MDBox> </MDBox>
</Grid> </Grid>
<Grid item xs={12} sm={6} md={6}> <Grid item xs={12} sm={6} md={6}>
<ImageUpload <ImageUpload
multiple heading="Upload Inventory Image"
heading="Upload Inventory Images"
accept="image/*" accept="image/*"
images={formik.values.images} images={formik.values.image}
setImages={(images) => { setImages={(images) => {
formik.setFieldValue('images', images); formik.setFieldValue('image', images);
}} }}
/> />
</Grid> </Grid>
<Grid item xs={12} sm={12} md={12}> <MDBox sx={{ ml: 'auto', mr: 'auto', mt: 3 }}>
<Grid container spacing={1}> <MDButton
{dataLevel && sx={{ ml: 3 }}
dataLevel.map((item, index) => ( color="error"
<Grid item xs={12} sm={6} md={4} key={index}> variant="outlined"
<Dropdown items={item} dropdownData={dropdownData} /> onClick={() => {
</Grid> navigate('/setup/inventory');
))}
<Grid item xs={12} sm={6} md={4}>
<MDButton color="primary" circular="true" sx={customStyles.marginTop}>
{'add hierarchy level'}
</MDButton>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<MDBox
sx={{
// backgroundColor: '#E5E5E5',
width: '100%',
padding: '9px'
}} }}
> >
<MDTypography
sx={{
backgroundColor: '#E5E5E5',
width: '100%',
padding: '9px'
}}
>
Widget hierarchy
</MDTypography>
<BasicTable
headCells={headCells}
records={records}
backgroundColor="#E5E5E5"
color="#343434"
>
<TableBody>
{records &&
records.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.level1}</TableCell>
<TableCell>{item.level2}</TableCell>
</TableRow>
))}
</TableBody>
</BasicTable>
</MDBox>
</Grid>
<MDBox sx={{ ml: 'auto', mr: 'auto', mt: 3 }}>
<MDButton sx={{ ml: 3 }} color="error" variant="outlined">
{'CANCEL'} {'CANCEL'}
</MDButton> </MDButton>
<MDButton sx={{ ml: 3 }} color="primary" variant="outlined" type="submit"> <MDButton
sx={{ ml: 3 }}
color="primary"
variant="outlined"
disabled={inventoryId}
type="submit"
>
{'SAVE'} {'SAVE'}
</MDButton> </MDButton>
<MDButton sx={{ ml: 3 }} color="primary"> <MDButton sx={{ ml: 3 }} color="primary">
@@ -346,6 +295,7 @@ function InventoryScreen() {
</Grid> </Grid>
</MDBox> </MDBox>
</form> </form>
{inventoryId ? <WidgetNestedDataTable inventoryId={inventoryId} /> : null}
</MDBox> </MDBox>
</DashboardLayout> </DashboardLayout>
); );

View File

@@ -5,42 +5,53 @@ import EquipmentIcon from 'assets/images/EquimpmentIcon';
import ProductsIcon from 'assets/images/ProductsIcon'; import ProductsIcon from 'assets/images/ProductsIcon';
import FleetIcon from 'assets/images/FleetIcon'; import FleetIcon from 'assets/images/FleetIcon';
import RawMaterialIcon from 'assets/images/RawMaterialIcon'; import RawMaterialIcon from 'assets/images/RawMaterialIcon';
import InventoryActions from 'redux/InventoryRedux';
import { InventorySelectors } from 'redux/InventoryRedux';
import { Grid } from '@mui/material'; import { Grid } from '@mui/material';
import Tile from 'components/TileComponent'; import Tile from 'components/TileComponent';
import MDButton from 'components/Button'; import MDButton from 'components/Button';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Breadcrumbs from 'components/Breadcrumbs'; import Breadcrumbs from 'components/Breadcrumbs';
import { useDispatch, useSelector } from 'react-redux';
import { useEffect, useState } from 'react';
import { API } from 'constant';
function getIconFromSlug(slug) {
switch (slug) {
case 'equipment':
return <EquipmentIcon />;
case 'product':
return <ProductsIcon />;
case 'fleet':
return <FleetIcon />;
case 'rawmaterial':
default:
return <RawMaterialIcon />;
}
}
function SetupInventory() { function SetupInventory() {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch();
const inventoryData = useSelector(InventorySelectors.getInventoryDetail);
const [inventoryAllData, setInventoryAllData] = useState([]);
const tiles = [ useEffect(() => {
{ if (inventoryData?.length) {
name: 'Raw Material', setInventoryAllData(inventoryData);
path: { update: '/', addNew: '/', cycleCount: '/', list: '/' },
icon: <RawMaterialIcon />
},
{
name: 'Products',
path: {
update: '/',
addNew: '/setup/inventory/product/add-new-product',
cycleCount: '/',
list: '/'
},
icon: <ProductsIcon />
},
{
name: 'Equipment',
path: { update: '/', addNew: '/', cycleCount: '/', list: '/' },
icon: <EquipmentIcon />
},
{
name: 'Fleet',
path: { update: '/', addNew: '/', cycleCount: '/', list: '/' },
icon: <FleetIcon />
} }
]; }, [inventoryData]);
useEffect(() => {
dispatch(
InventoryActions.getInventoryAction({
loader: 'loading-request',
slug: API.GET_INVENTORY,
method: 'get'
})
);
}, []);
return ( return (
<DashboardLayout> <DashboardLayout>
<DashboardNavbar /> <DashboardNavbar />
@@ -51,20 +62,18 @@ function SetupInventory() {
{ name: 'Inventory' } { name: 'Inventory' }
]} ]}
> >
<MDButton <MDButton sx={{ ml: 3 }} color="primary" onClick={() => navigate('/setup/inventory/new')}>
sx={{ ml: 3 }}
color="primary"
onClick={() => navigate('/setup/inventory/inventory-new')}
>
Create Inventory Create Inventory
</MDButton> </MDButton>
</Breadcrumbs> </Breadcrumbs>
<MDBox px={2} py={3}> <MDBox px={2} py={3}>
<Grid container spacing={2}> <Grid container spacing={2}>
{tiles && {inventoryAllData &&
tiles.map((tile) => ( inventoryAllData.map((tile) => (
<Grid item xs={12} sm={6} md={tiles.length > 4 ? 4 : 6} key={tile.name}> <Grid item xs={12} sm={6} md={inventoryAllData?.length > 4 ? 4 : 6} key={tile._id}>
<Tile data={{ name: tile.name, path: tile.path }}>{tile.icon}</Tile> <Tile data={{ name: tile?.name, widgetname: tile?.widgetName, id: tile?._id }}>
{getIconFromSlug(tile.icon_slug)}
</Tile>
</Grid> </Grid>
))} ))}
</Grid> </Grid>

View File

@@ -5,9 +5,18 @@ import { getFetchingValue, getErrorValue } from '../services/Utils';
/* ------------- Types and Action Creators ------------- */ /* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({ const { Types, Creators } = createActions({
getInventoryAction: ['payload'],
getInventorySuccess: ['data'],
getInventoryFailure: ['error'],
addInventoryAction: ['payload'], addInventoryAction: ['payload'],
addInventorySuccess: ['data'], addInventorySuccess: ['data'],
addInventoryFailure: ['error'] addInventoryFailure: ['error'],
updateInventoryAction: ['payload'],
updateInventorySuccess: ['data'],
updateInventoryFailure: ['error'],
getInventoryTypesAction: ['payload'],
getInventoryTypesSuccess: ['data'],
getInventoryTypesFailure: ['error']
}); });
export const InventoryTypes = Types; export const InventoryTypes = Types;
@@ -16,17 +25,44 @@ export default InventoryActions;
/* ------------- Initial State ------------- */ /* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({ export const INITIAL_STATE = Immutable({
getInventoryDetail: [],
addInventoryDetail: [], addInventoryDetail: [],
updateInventoryDetail: [],
addInventoryLoading: false, addInventoryLoading: false,
addInventoryerror: {} addInventoryerror: {},
inventoryTypes: []
}); });
/* ------------- Selectors ------------- */ /* ------------- Selectors ------------- */
export const InventorySelectors = { export const InventorySelectors = {
addInventoryDetail: (state) => state.inventory.inventoryDetail addInventoryDetail: (state) => state.inventory.inventoryDetail,
getInventoryDetail: (state) => state.inventory.getInventoryDetail,
getInventoryDetailById: (id) => (state) =>
state.inventory.getInventoryDetail.find((x) => x._id === id),
getInventoryTypes: (state) => state.inventory.inventoryTypes,
updateInventoryDetail: (state) => state.inventory.updateInventoryDetail
}; };
/* ------------- Reducers ------------- */ /* ------------- Reducers ------------- */
export const onGetInventoryAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onGetInventorySuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
getInventoryDetail: data.getInventoryDetail
});
export const onGetInventoryFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
error: { ...state.error, [error?.loader]: error?.error }
});
export const onAddInventoryAction = (state, { payload }) => export const onAddInventoryAction = (state, { payload }) =>
state.merge({ state.merge({
fetching: _.uniq([state.fetching, payload?.loader]), fetching: _.uniq([state.fetching, payload?.loader]),
@@ -37,7 +73,7 @@ export const onAddInventorySuccess = (state, { data }) =>
state.merge({ state.merge({
fetching: getFetchingValue(state.fetching, data?.loader), fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader), error: getErrorValue(state?.error, data?.loader),
addInventoryDetail: data.addInventoryDetail getInventoryDetail: [...state.getInventoryDetail, data.newInventory]
}); });
export const onAddInventoryFailure = (state, { error }) => export const onAddInventoryFailure = (state, { error }) =>
@@ -46,9 +82,59 @@ export const onAddInventoryFailure = (state, { error }) =>
error: { ...state.error, [error?.loader]: error?.error } error: { ...state.error, [error?.loader]: error?.error }
}); });
export const onUpdateInventoryAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onUpdateInventorySuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
getInventoryDetail: [
...state.getInventoryDetail.filter((x) => x._id !== data.newInventory._id),
data.newInventory
]
});
export const onUpdateInventoryFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
error: { ...state.error, [error?.loader]: error?.error }
});
export const onGetInventoryTypesAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onGetInventoryTypesSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
inventoryTypes: data.inventoryTypes
});
export const onGetInventoryTypesFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
error: { ...state.error, [error?.loader]: error?.error }
});
/* ------------- Hookup Reducers To Types ------------- */ /* ------------- Hookup Reducers To Types ------------- */
export const inventoryReducer = createReducer(INITIAL_STATE, { export const inventoryReducer = createReducer(INITIAL_STATE, {
[Types.GET_INVENTORY_ACTION]: onGetInventoryAction,
[Types.GET_INVENTORY_SUCCESS]: onGetInventorySuccess,
[Types.GET_INVENTORY_FAILURE]: onGetInventoryFailure,
[Types.ADD_INVENTORY_ACTION]: onAddInventoryAction, [Types.ADD_INVENTORY_ACTION]: onAddInventoryAction,
[Types.ADD_INVENTORY_SUCCESS]: onAddInventorySuccess, [Types.ADD_INVENTORY_SUCCESS]: onAddInventorySuccess,
[Types.ADD_INVENTORY_FAILURE]: onAddInventoryFailure [Types.ADD_INVENTORY_FAILURE]: onAddInventoryFailure,
[Types.UPDATE_INVENTORY_ACTION]: onUpdateInventoryAction,
[Types.UPDATE_INVENTORY_SUCCESS]: onUpdateInventorySuccess,
[Types.UPDATE_INVENTORY_FAILURE]: onUpdateInventoryFailure,
[Types.GET_INVENTORY_TYPES_ACTION]: onGetInventoryTypesAction,
[Types.GET_INVENTORY_TYPES_SUCCESS]: onGetInventoryTypesSuccess,
[Types.GET_INVENTORY_TYPES_FAILURE]: onGetInventoryTypesFailure
}); });

101
src/redux/WidgetRedux.js Normal file
View File

@@ -0,0 +1,101 @@
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({
widgetRequest: ['payload'],
widgetSuccess: ['data'],
editWidgetRequest: ['payload'],
editWidgetSuccess: ['data'],
widgetFailure: ['error'],
logout: null
});
export const WidgetTypes = Types;
const WidgetActions = Creators;
export default WidgetActions;
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
list: [],
fetching: [],
error: {}
});
/* ------------- Selectors ------------- */
export const WidgetSelectors = {
getWidgets: (state) => state.widgets.list,
getWidgetById: (id) => (state) => state.widgets.list.find((x) => x._id === id),
getWidgetsByInventoryId: (id) => (state) =>
state.widgets.list.filter((x) => x.inventory._id === id),
getWidgetFamiliesByInventoryId: (id) => (state) =>
state.widgets.list.filter((x) => !x.parent && x.inventory._id === id),
getWidgetsByParentId: (id) => (state) => state.widgets.list.filter((x) => x.parent?._id === id)
};
/* ------------- Reducers ------------- */
export const onWidgetRequest = (state, { payload }) =>
state.merge({
fetching: _.uniq([...state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onEditWidgetRequest = (state, { payload }) =>
state.merge({
fetching: _.uniq([...state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
const mergeWidgetStates = (stateData, widgets) => {
if (!widgets) return stateData; // undefined check
const idsInNewWidgets = widgets.map((x) => x._id);
const newState = stateData.filter((x) => !idsInNewWidgets.includes(x._id));
return [...newState, ...widgets];
};
export const onWidgetSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
list: mergeWidgetStates(state.list, data.widgets)
});
const mergeEditWidgetStates = (stateList, widget, type) => {
if (!widget) return stateList; // undefined check
if (type === 'add') {
return [...stateList, widget];
} else if (type === 'edit') {
const newState = stateList.filter((x) => x._id !== widget._id);
return [...newState, widget];
} else {
return stateList;
}
};
export const onEditWidgetSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
list: mergeEditWidgetStates(state.list, data.widget, data.type)
});
export const onWidgetFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
error: { ...state.error, [error?.loader]: error?.error }
});
/* ------------- Hookup Reducers To Types ------------- */
export const widgetReducer = createReducer(INITIAL_STATE, {
[Types.WIDGET_REQUEST]: onWidgetRequest,
[Types.EDIT_WIDGET_REQUEST]: onEditWidgetRequest,
[Types.WIDGET_SUCCESS]: onWidgetSuccess,
[Types.EDIT_WIDGET_SUCCESS]: onEditWidgetSuccess,
[Types.WIDGET_FAILURE]: onWidgetFailure
});

View File

@@ -6,6 +6,7 @@ import { productReducer } from './ProductsRedux';
import { inventoryReducer } from './InventoryRedux'; import { inventoryReducer } from './InventoryRedux';
import { rolesReducer } from './RolesRedux'; import { rolesReducer } from './RolesRedux';
import { WarehouseLocationsReducer } from './WarehouseLocationsRedux'; import { WarehouseLocationsReducer } from './WarehouseLocationsRedux';
import { widgetReducer } from './WidgetRedux';
// Combine all reducers. // Combine all reducers.
const appReducer = combineReducers({ const appReducer = combineReducers({
@@ -15,7 +16,8 @@ const appReducer = combineReducers({
roles: rolesReducer, roles: rolesReducer,
warehouseLocations: WarehouseLocationsReducer, warehouseLocations: WarehouseLocationsReducer,
product: productReducer, product: productReducer,
inventory: inventoryReducer inventory: inventoryReducer,
widgets: widgetReducer
}); });
const rootReducer = (state, action) => { const rootReducer = (state, action) => {

View File

@@ -57,7 +57,7 @@ import LabelingHome from 'pages/labellingHome';
import SetupInventory from 'pages/setupInventory'; import SetupInventory from 'pages/setupInventory';
import HomeIcon from 'assets/images/HomeIcon'; import HomeIcon from 'assets/images/HomeIcon';
import SetupIcon from 'assets/images/SetupIcon'; import SetupIcon from 'assets/images/SetupIcon';
import AddNewProduct from '../pages/addNewProduct'; import AddNewItem from '../pages/addNewProduct';
import CreateUserRole from 'pages/createUserRole'; import CreateUserRole from 'pages/createUserRole';
import WidgetLabel from 'pages/widgetLabel'; import WidgetLabel from 'pages/widgetLabel';
@@ -141,15 +141,22 @@ const protectedRoutes = [
name: 'Inventory Definition', name: 'Inventory Definition',
key: 'inventory-new', key: 'inventory-new',
hide: true, hide: true,
route: '/setup/inventory/inventory-new', route: '/setup/inventory/new',
component: <InventoryScreen /> component: <InventoryScreen />
}, },
{ {
name: 'Add New Product', name: 'Inventory Definition',
key: 'add-new-product', key: 'inventory-update',
hide: true, hide: true,
route: '/setup/inventory/product/add-new-product', route: '/setup/inventory/update/:inventoryId',
component: <AddNewProduct /> component: <InventoryScreen />
},
{
name: 'Add New Item',
key: 'add-new-item',
hide: true,
route: '/setup/inventory/new-item/:widgetName/:inventoryId',
component: <AddNewItem />
}, },
{ {
name: 'Location Labeling', name: 'Location Labeling',

View File

@@ -1,21 +1,85 @@
import { AuthorizedAPI } from 'config'; import { AuthorizedAPI } from 'config';
import { takeLatest, call, put } from 'redux-saga/effects'; import { takeLatest, call, put, takeEvery } from 'redux-saga/effects';
import InventoryActions from 'redux/InventoryRedux'; import InventoryActions from 'redux/InventoryRedux';
import { InventoryTypes } from 'redux/InventoryRedux'; import { InventoryTypes } from 'redux/InventoryRedux';
import ApiServices from 'services/API/ApiServices'; import ApiServices from 'services/API/ApiServices';
export function* onRequestAddInventoryData({ payload }) { export function* onRequestGetInventoryData({ payload }) {
const response = yield call( const response = yield call(
ApiServices[payload?.method], ApiServices[payload?.method],
AuthorizedAPI, AuthorizedAPI,
payload?.slug, payload?.slug,
payload?.data payload?.data
); );
if (response?.status === 200) {
yield put(
InventoryActions.getInventorySuccess({
loader: payload?.loader,
getInventoryDetail: response?.data?.data
})
);
} else {
payload.onFailedGetInventoryData(response.data.error);
yield put(
InventoryActions.getInventoryFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export function* onRequestGetInventoryTypesData({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
if (response?.status === 200) {
yield put(
InventoryActions.getInventoryTypesSuccess({
loader: payload?.loader,
inventoryTypes: response?.data?.data
})
);
} else {
payload.onFailedGetInventoryData(response.data.error);
yield put(
InventoryActions.getInventoryTypesFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
const parseDataToFormData = (data) => {
var formData = new FormData();
formData.append('name', data.name);
formData.append('widgetName', data.widgetName);
formData.append('icon_slug', 'testslug');
formData.append('policies[orderTracking]', data.policies.orderTracking);
formData.append('policies[alerting]', data.policies.alerting);
formData.append('policies[replenishment]', data.policies.replenishment);
formData.append('policies[preferredLocations]', data.policies.preferredLocations);
formData.append('policies[inventory_process]', data.policies.inventory_process);
data.image && formData.append('image', data.image);
return formData;
};
export function* onRequestAddInventoryData({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
parseDataToFormData(payload?.data)
);
if (response?.status === 200) { if (response?.status === 200) {
yield put( yield put(
InventoryActions.addInventorySuccess({ InventoryActions.addInventorySuccess({
loader: payload?.loader, loader: payload?.loader,
addInventoryDetail: response?.data?.data newInventory: response?.data?.data?.inventoryData
}) })
); );
} else { } else {
@@ -28,4 +92,34 @@ export function* onRequestAddInventoryData({ payload }) {
); );
} }
} }
export default [takeLatest(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData)];
export function* onRequestUpdateInventoryData({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
if (response?.status === 200) {
yield put(
InventoryActions.updateInventorySuccess({
loader: payload?.loader,
updateInventoryDetail: response?.data?.data
})
);
} else {
payload.onFailedUpdateInventoryData(response.data.error);
yield put(
InventoryActions.updateInventoryFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export default [
takeLatest(InventoryTypes.GET_INVENTORY_ACTION, onRequestGetInventoryData),
takeLatest(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData),
takeLatest(InventoryTypes.UPDATE_INVENTORY_ACTION, onRequestUpdateInventoryData),
takeEvery(InventoryTypes.GET_INVENTORY_TYPES_ACTION, onRequestGetInventoryTypesData)
];

58
src/sagas/Widget.js Normal file
View File

@@ -0,0 +1,58 @@
import { AuthorizedAPI } from 'config';
import { call, put, takeEvery } from 'redux-saga/effects';
import ApiServices from 'services/API/ApiServices';
import WidgetActions, { WidgetTypes } from '../redux/WidgetRedux';
export function* onRequestWidget({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
if (response?.status === 200) {
yield put(
WidgetActions.widgetSuccess({
loader: payload?.loader,
widgets: response?.data?.data
})
);
} else {
yield put(
WidgetActions.widgetFailure({
loader: payload?.loader,
error: response?.message
})
);
}
}
export function* onEditRequestWidget({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
if (response?.status === 200) {
yield put(
WidgetActions.editWidgetSuccess({
loader: payload?.loader,
widget: response?.data?.data,
type: payload?.type
})
);
} else {
yield put(
WidgetActions.widgetFailure({
loader: payload?.loader,
error: response?.message
})
);
}
}
export default [
takeEvery(WidgetTypes.WIDGET_REQUEST, onRequestWidget),
takeEvery(WidgetTypes.EDIT_WIDGET_REQUEST, onEditRequestWidget)
];

View File

@@ -6,6 +6,7 @@ import ProductSaga from './Product';
import InventorySaga from './Inventory'; import InventorySaga from './Inventory';
import RolesSaga from './Roles'; import RolesSaga from './Roles';
import WarehouseLocationsSaga from './WarehouseLocations'; import WarehouseLocationsSaga from './WarehouseLocations';
import WidgetSaga from './Widget';
export default function* rootSaga() { export default function* rootSaga() {
yield all([...AuthSaga]); yield all([...AuthSaga]);
@@ -15,4 +16,5 @@ export default function* rootSaga() {
yield all([...InventorySaga]); yield all([...InventorySaga]);
yield all([...RolesSaga]); yield all([...RolesSaga]);
yield all([...WarehouseLocationsSaga]); yield all([...WarehouseLocationsSaga]);
yield all([...WidgetSaga]);
} }

View File

@@ -28,33 +28,41 @@ const schema = {
attributes: Yup.string('Enter other attributes') attributes: Yup.string('Enter other attributes')
}), }),
addNewProduct: Yup.object({ addNewItem: Yup.object({
warehousename: Yup.string('Enter warehouse name').required('warehouse name is required'), commonName: Yup.string('Enter details').required(),
description: Yup.string('Enter Description').required('description is required'), formalName: Yup.string('Enter details').required(),
manufacturer: Yup.string('Enter manufacturer').required('manufacturer is required'), description: Yup.string('Enter details').required(),
type: Yup.string('Enter type').required('type is required'), manufacturer: Yup.string('Enter details').required(),
unitofmaterial: Yup.string('Enter unitofmaterial').required('Unit of material is required'), size: Yup.string('Enter details').required(),
packagecount: Yup.number('Enter packagecount').required('Package Count is required'), color: Yup.string('Enter details').required(),
formalname: Yup.string('Enter formal name').required('Formal Name is required'), type: Yup.string('Enter details').required(),
size: Yup.string('Enter Size').required('Size is required'), unitOfMaterial: Yup.string('Enter details').required(),
color: Yup.string('Enter Color').required('Color is required'), unitCost: Yup.number().test((val) => val >= 0),
unitcost: Yup.number('Enter UnitCost').required('Unit Cost is required'), packageCount: Yup.number().test((val) => val >= 0),
countperpallet: Yup.number('Enter countperpallet').required('Count per pallet is required'), countPerPallet: Yup.number().test((val) => val >= 0),
countperpalletpackage: Yup.number('Enter countperpalletpackage').required( countPerPalletPackage: Yup.number().test((val) => val >= 0),
'count per pallet package is required' primaryWidgetFamilyId: Yup.string('Enter details').required(),
), secondaryWidgetFamilyId: Yup.string('Enter details'),
productfamilyassociation: Yup.string('Enter productfamilyassociation').required( policiesMetadata: Yup.object({
'product Family Association is required' underStockLevelCount: Yup.number().test((val) => val >= 0),
), overStockLevelCount: Yup.number().test((val) => val >= 0),
under: Yup.number().required('required'), alertStockLevelCount: Yup.number().test((val) => val >= 0),
over: Yup.number().required('required'), reorderStockLevelCount: Yup.number().test((val) => val >= 0)
alert: Yup.number().required('required') }),
images: Yup.array()
}), }),
addInventory: Yup.object({ addInventory: Yup.object({
inventoryname: Yup.string('Enter Inventory name').required('Inventory name is required'), name: Yup.string('Enter Widget Name').required('Widget Name is required'),
inventorytype: Yup.string('Enter inventory Type').required('inventory Type is required'), widgetName: 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(),
replenishment: Yup.boolean(),
preferredLocations: Yup.boolean(),
inventory_process: Yup.string()
}),
image: Yup.array()
}) })
}; };