Merge branch 'develop' of https://github.com/kfnawaz/plaidware-wms-web into feature/wms-55_develop

This commit is contained in:
m0n02hz
2022-03-03 04:18:17 +05:30
20 changed files with 1039 additions and 360 deletions

View File

@@ -44,6 +44,7 @@ const buildBreadcrumbs = (route, light) => {
fontWeight="regular"
textTransform="capitalize"
color={light ? 'white' : 'dark'}
key={el}
sx={{ lineHeight: 0 }}
>
{el.name}

View File

@@ -1,12 +1,7 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import PropTypes from 'prop-types';
import SearchBar from 'components/SearchBar';
import MDButton from 'components/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Fade from '@mui/material/Fade';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import { styled } from '@mui/material/styles';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
@@ -15,10 +10,8 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
// import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
// import IconButton from '@mui/material/IconButton';
// import Collapse from '@mui/material/Collapse';
import TablePagination from 'components/TablePagination';
import { Dialog, DialogActions, MenuItem, Select } from '@mui/material';
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
@@ -47,16 +40,16 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({
Row.propTypes = {
rowData: PropTypes.array,
tHeads: PropTypes.array
tHeads: PropTypes.array,
editHandler: PropTypes.any
};
function Row({ tHeads, rowData }) {
function Row({ tHeads, rowData, editHandler }) {
return (
<React.Fragment>
<StyledTableRow sx={{ '&odd > *': { borderBottom: 'unset' } }}>
<StyledTableCell sx={{ width: '10%', display: 'flex', alignItems: 'center' }}>
<MDButton
disabled
size="small"
variant="contained"
color="primary"
@@ -69,6 +62,9 @@ function Row({ tHeads, rowData }) {
fontWeight: '500',
padding: '0'
}}
onClick={() => {
editHandler(rowData._id);
}}
>
EDIT
</MDButton>
@@ -82,15 +78,24 @@ function Row({ tHeads, rowData }) {
);
}
function EnhancedTable({ data, tHeads }) {
// const [anchorEl, setAnchorEl] = React.useState(false);
// const open = Boolean(anchorEl);
// const handleClick = (event) => {
// setAnchorEl(event.currentTarget);
// };
// const handleClose = () => {
// setAnchorEl(null);
// };
function EnhancedTable({
count,
page,
setPage,
perPage,
setPerPage,
data,
tHeads,
editHandler,
filtersControl,
resetFilters
}) {
const [filtersOpen, setFiltersOpen] = React.useState(false);
const handleFiltersClose = () => {
setFiltersOpen(false);
};
return (
<>
<Box
@@ -109,9 +114,7 @@ function EnhancedTable({ data, tHeads }) {
padding: '16px'
}}
>
<Box>
<SearchBar />
</Box>
<Box>{/* <SearchBar /> */}</Box>
<Box sx={{ display: 'flex', columnGap: '15px' }}>
<MDButton
size="small"
@@ -123,10 +126,31 @@ function EnhancedTable({ data, tHeads }) {
minHeight: '44px',
fontWeight: '500'
}}
onClick={() => {
setFiltersOpen(true);
}}
>
Sorting
Filter
</MDButton>
<MDButton
{filtersControl ? (
<Dialog open={filtersOpen} onClose={handleFiltersClose}>
{filtersControl}
<DialogActions>
<MDButton onClick={resetFilters}>Reset Filters</MDButton>
<MDButton
size="small"
variant="outlined"
color="primary"
onClick={() => {
setFiltersOpen(false);
}}
>
Close
</MDButton>
</DialogActions>
</Dialog>
) : null}
{/* <MDButton
id="fade-button"
size="small"
variant="outlined"
@@ -144,8 +168,8 @@ function EnhancedTable({ data, tHeads }) {
onClick={() => {}}
>
Dashboard
</MDButton>
<Menu
</MDButton> */}
{/* <Menu
id="fade-menu"
MenuListProps={{
'aria-labelledby': 'fade-button'
@@ -158,7 +182,7 @@ function EnhancedTable({ data, tHeads }) {
<MenuItem>Profile</MenuItem>
<MenuItem>My account</MenuItem>
<MenuItem>Logout</MenuItem>
</Menu>
</Menu> */}
</Box>
</Box>
{/* Table-row- */}
@@ -174,7 +198,14 @@ function EnhancedTable({ data, tHeads }) {
</TableHead>
<TableBody>
{data &&
data.map((rowData) => <Row key={rowData._id} rowData={rowData} tHeads={tHeads} />)}
data.map((rowData) => (
<Row
key={rowData._id}
editHandler={editHandler}
rowData={rowData}
tHeads={tHeads}
/>
))}
</TableBody>
</Table>
</TableContainer>
@@ -265,10 +296,29 @@ function EnhancedTable({ data, tHeads }) {
</Box>
</Box> */}
{/*---- pagination- */}
<Box>
<TablePagination />
<Box sx={{ fontSize: '14px', color: '#000' }}>
Per page:{' '}
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={perPage}
label="Age"
onChange={(e) => {
setPerPage(e.target.value);
}}
>
<MenuItem value={5}>5</MenuItem>
<MenuItem value={10}>10</MenuItem>
<MenuItem value={20}>20</MenuItem>
</Select>
</Box>
<Box>
<TablePagination count={Math.ceil(count / perPage)} page={page} setPage={setPage} />
</Box>
<Box sx={{ fontSize: '14px', color: '#000' }}>
[{(page - 1) * perPage + 1} to {perPage * page > count ? count : perPage * page} of{' '}
{count}]
</Box>
<Box sx={{ fontSize: '14px', color: '#000' }}>[1 to 10 of 92]</Box>
</Box>
</Box>
</>
@@ -276,8 +326,16 @@ function EnhancedTable({ data, tHeads }) {
}
EnhancedTable.propTypes = {
count: PropTypes.number,
page: PropTypes.number,
setPage: PropTypes.any,
perPage: PropTypes.number,
setPerPage: PropTypes.any,
data: PropTypes.array,
tHeads: PropTypes.array
tHeads: PropTypes.array,
editHandler: PropTypes.any,
filtersControl: PropTypes.any,
resetFilters: PropTypes.any
};
export default EnhancedTable;

View File

@@ -39,6 +39,7 @@ function ImageUpload({ heading, accept, multiple, images, setImages, type, pageI
const addNewImageToImages = (image) => {
setImages([...images, image]);
};
const addImage = (e) => {
addNewImage(type, pageId, e.target.files[0], addNewImageToImages);
setImages([
@@ -46,8 +47,9 @@ function ImageUpload({ heading, accept, multiple, images, setImages, type, pageI
{ src: URL.createObjectURL(e.target.files[0]), file: e.target.files[0] }
]);
};
const removeImage = (index) => {
deleteImage(type, pageId, images._id);
multiple && deleteImage(type, pageId, images._id);
setImages(images.filter((_val, idx) => idx !== index));
};

View File

@@ -0,0 +1,128 @@
/* eslint-disable no-case-declarations */
import MDBox from 'components/MDBox';
import PropTypes from 'prop-types';
import UploadIcon from 'assets/images/UploadIcon';
import MDTypography from 'components/MDTypography';
import pxToRem from 'assets/theme-dark/functions/pxToRem';
import { Button } from '@mui/material';
import Close from 'assets/images/Close';
function ImageUpload({ heading, accept, multiple, images, setImages }) {
const addImage = (e) => {
setImages([
...images,
{ src: URL.createObjectURL(e.target.files[0]), file: e.target.files[0] }
]);
};
const removeImage = (index) => {
setImages(images.filter((_val, idx) => idx !== index));
};
return (
<>
<MDBox
sx={{
border: '1px solid #c4c4c4',
borderRadius: pxToRem(8),
padding: pxToRem(16)
}}
>
<MDBox
sx={{
border: '1px dashed #C4C4C4',
borderRadius: pxToRem(6),
cursor: 'pointer',
position: 'relative',
textAlign: 'center',
minHeight: pxToRem(200),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: '16px'
}}
>
<MDBox
component="input"
name="file"
disabled={!multiple && images.length}
accept={accept}
type="file"
sx={{
width: '100%',
opacity: '0',
cursor: 'pointer',
position: 'absolute',
top: '0',
left: '0',
right: '0',
bottom: '0'
}}
onChange={addImage}
/>
<MDBox component="span">
{!multiple && images.length ? null : <UploadIcon />}
<MDTypography
component="span"
sx={{ color: '#000', letterSpacing: '0.01em', display: 'block' }}
>
{!multiple && images.length ? 'Cannot add more images' : heading}
</MDTypography>
</MDBox>
</MDBox>
{/* -----------img-preview----------- */}
<MDBox sx={{ marginBottom: '-10px' }}>
{images &&
images.map((item, idx) => {
return (
<MDBox
key={idx}
component="span"
sx={{
width: '80px',
height: '63px',
marginRight: '16px',
display: 'inline-block',
borderRadius: '4px',
position: 'relative'
}}
>
<img src={item.src} alt="placeholder" width="100%" />
<Button
sx={{
backgroundColor: '#fff !important',
boxShadow: '0px 1px 1px rgb(0 0 0 / 25%)',
padding: '0',
minWidth: '20px',
minHeight: '20px',
borderRadius: '100%',
position: 'absolute',
right: '4px',
top: '4px',
'&:hover': {
backgroundColor: 'red !important'
}
}}
onClick={() => {
removeImage(idx);
}}
>
<Close />
</Button>
</MDBox>
);
})}
</MDBox>
</MDBox>
</>
);
}
export default ImageUpload;
ImageUpload.propTypes = {
images: PropTypes.array,
heading: PropTypes.string,
multiple: PropTypes.bool,
accept: PropTypes.string,
setImages: PropTypes.func
};

View File

@@ -0,0 +1,119 @@
/* eslint-disable no-case-declarations */
import MDBox from 'components/MDBox';
import PropTypes from 'prop-types';
import UploadIcon from 'assets/images/UploadIcon';
import MDTypography from 'components/MDTypography';
import pxToRem from 'assets/theme-dark/functions/pxToRem';
import { Button } from '@mui/material';
import Close from 'assets/images/Close';
function ImageUploadSingle({ heading, accept, multiple, images, setImages }) {
return (
<>
<MDBox
sx={{
border: '1px solid #c4c4c4',
borderRadius: pxToRem(8),
padding: pxToRem(16)
}}
>
{!images.length ? (
<MDBox
sx={{
border: '1px dashed #C4C4C4',
borderRadius: pxToRem(6),
cursor: 'pointer',
position: 'relative',
textAlign: 'center',
minHeight: pxToRem(200),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: '16px'
}}
>
<MDBox
component="input"
name="file"
disabled={!multiple && images.length}
accept={accept}
type="file"
sx={{
width: '100%',
opacity: '0',
cursor: 'pointer',
position: 'absolute',
top: '0',
left: '0',
right: '0',
bottom: '0'
}}
onChange={(e) => {
setImages([
{ src: URL.createObjectURL(e.target.files[0]), file: e.target.files[0] }
]);
}}
/>
<MDBox component="span">
{!multiple && images.length ? null : <UploadIcon />}
<MDTypography
component="span"
sx={{ color: '#000', letterSpacing: '0.01em', display: 'block' }}
>
{!multiple && images.length ? 'Cannot add more images' : heading}
</MDTypography>
</MDBox>
</MDBox>
) : (
<MDBox
sx={{
border: '1px dashed #C4C4C4',
borderRadius: pxToRem(6),
cursor: 'pointer',
position: 'relative',
textAlign: 'center',
minHeight: pxToRem(200),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: '16px'
}}
>
<img src={images[0].src} alt="" width="100%" height="100%" />
<Button
sx={{
backgroundColor: '#fff !important',
boxShadow: '0px 1px 1px rgb(0 0 0 / 25%)',
padding: '0',
minWidth: '20px',
minHeight: '20px',
borderRadius: '100%',
position: 'absolute',
right: '4px',
top: '4px',
'&:hover': {
backgroundColor: 'red !important'
}
}}
onClick={() => {
setImages([]);
}}
>
<Close />
</Button>
</MDBox>
)}
</MDBox>
</>
);
}
ImageUploadSingle.propTypes = {
images: PropTypes.array,
heading: PropTypes.string,
multiple: PropTypes.bool,
accept: PropTypes.string,
setImages: PropTypes.func
};
export default ImageUploadSingle;

View File

@@ -1,33 +1,43 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import Pagination from '@mui/material/Pagination';
import Stack from '@mui/material/Stack';
import { PaginationItem } from '@mui/material';
import NextIcon from 'assets/images/NextIcon';
import PreviousIcon from 'assets/images/PreviousIcon';
function TablePagination() {
function TablePagination({ count, page, setPage }) {
return (
<Stack spacing={2}>
<Pagination
count={10}
shape="rounded"
color="primary"
size="large"
<Pagination
count={count}
page={page}
shape="rounded"
color="primary"
size="large"
renderItem={(item) => (
<PaginationItem
components={{ previous: PreviousIcon, next: NextIcon }}
{...item}
sx={{
minWidth:'20px',
fontWeight:'500',
minWidth: '20px',
fontWeight: '500',
height: '30px'
}}
/>
)}
onChange={(event, value) => {
setPage(value);
}}
/>
</Stack>
);
}
TablePagination.propTypes = {
page: PropTypes.number,
count: PropTypes.number,
setPage: PropTypes.any
};
export default TablePagination;

View File

@@ -67,13 +67,30 @@ export default function TileBasic({ tiles }) {
<Grid container spacing={2}>
{tiles &&
tiles.map((item) => (
<>
<Grid item xs={12} sm={6} md={tiles.length > 4 ? 4 : 6}>
{item.disabled ? (
<Grid item key={item.name} xs={12} sm={6} md={tiles.length > 4 ? 4 : 6}>
{item.disabled ? (
<MDBox
key={item.name + item.path}
data={{ name: item.name, path: item.path }}
className={classes.disabledTileContent}
sx={{
height: 230,
backgroundColor: ({ palette: { white } }) => white.main,
padding: '32px 40px'
}}
>
{item.icon}
{item.name}
</MDBox>
) : (
<Link
to={item.path}
state={{ name: item.name, address: item.address, id: item._id }}
>
<MDBox
key={item.name + item.path}
data={{ name: item.name, path: item.path }}
className={classes.disabledTileContent}
className={classes.tileContent}
sx={{
height: 230,
backgroundColor: ({ palette: { white } }) => white.main,
@@ -83,28 +100,9 @@ export default function TileBasic({ tiles }) {
{item.icon}
{item.name}
</MDBox>
) : (
<Link
to={item.path}
state={{ name: item.name, address: item.address, id: item._id }}
>
<MDBox
key={item.name + item.path}
data={{ name: item.name, path: item.path }}
className={classes.tileContent}
sx={{
height: 230,
backgroundColor: ({ palette: { white } }) => white.main,
padding: '32px 40px'
}}
>
{item.icon}
{item.name}
</MDBox>
</Link>
)}
</Grid>
</>
</Link>
)}
</Grid>
))}
</Grid>
</MDBox>

View File

@@ -16,7 +16,8 @@ export default {
ADD_NEW_BAY: '/bay',
ADD_NEW_LEVEL: '/level',
ADD_NEW_SUBLEVEL: '/sublevel',
ADD_PRODUCT: '/item/',
ADD_ITEM: '/item/',
EDIT_ITEM: '/item/',
ADD_INVENTORY: '/inventory',
GET_INVENTORY: '/inventory/all?page=0&perPage=50',
GET_INVENTORY_TYPES: '/inventory/types',

View File

@@ -1,3 +1,4 @@
/* eslint-disable indent */
/* eslint-disable complexity */
import * as React from 'react';
import { Grid, TextField, Box, FormHelperText, TextareaAutosize } from '@mui/material';
@@ -7,7 +8,7 @@ import DashboardLayout from 'layouts/DashboardLayout';
import Select from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import ImageUpload from 'components/ImageUpload';
import ImageUploadMultiple from 'components/ImageUploadMultiple';
import MDButton from 'components/Button';
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
@@ -16,13 +17,15 @@ import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
import MDInput from 'components/MDInput';
import { useDispatch, useSelector } from 'react-redux';
import ProductActions from 'redux/ProductsRedux';
import { API } from 'constant';
import LOGGER from 'services/Logger';
import Breadcrumbs from 'components/Breadcrumbs';
import { useParams } from 'react-router-dom';
import { WidgetSelectors } from 'redux/WidgetRedux';
import WidgetActions from 'redux/WidgetRedux';
import ItemActions from 'redux/ItemRedux';
import { useNavigate } from 'react-router-dom';
import { ItemSelectors } from 'redux/ItemRedux';
const useStyles = makeStyles({
labelSize: {
@@ -48,11 +51,68 @@ const useStyles = makeStyles({
function AddNewItem() {
const classes = useStyles();
const { widgetName, inventoryId } = useParams();
const { widgetName, inventoryId, itemId } = useParams();
const getInitialFormValues = (data) => {
return data && data._id === itemId
? {
commonName: data.commonName,
formalName: data.formalName,
description: data.description,
manufacturer: data.manufacturer,
size: data.size,
color: data.color,
type: data.type,
unitOfMaterial: data.unitOfMaterial,
unitCost: data.unitCost,
packageCount: data.packageCount,
countPerPallet: data.countPerPallet,
countPerPalletPackage: data.countPerPalletPackage,
primaryWidgetFamilyId: data.widgetFamily.parent
? data.widgetFamily.parent
: data.widgetFamily._id,
secondaryWidgetFamilyId: data.widgetFamily.parent ? data.widgetFamily._id : '',
policiesMetadata: {
underStockLevelCount: data.policiesMetadata.underStockLevelCount,
overStockLevelCount: data.policiesMetadata.overStockLevelCount,
alertStockLevelCount: data.policiesMetadata.alertStockLevelCount,
reorderStockLevelCount: data.policiesMetadata.reorderStockLevelCount
},
images: data.images.map((img) => ({ ...img, src: img.url }))
}
: {
commonName: '',
formalName: '',
description: '',
manufacturer: '',
size: '',
color: '',
type: '',
unitOfMaterial: '',
unitCost: 0,
packageCount: 0,
countPerPallet: 0,
countPerPalletPackage: 0,
primaryWidgetFamilyId: '',
secondaryWidgetFamilyId: '',
policiesMetadata: {
underStockLevelCount: 0,
overStockLevelCount: 0,
alertStockLevelCount: 0,
reorderStockLevelCount: 0
},
images: []
};
};
const itemData = getInitialFormValues(useSelector(ItemSelectors.getFormItem(itemId)));
const dispatch = useDispatch();
const [Manufacturer, setManufacturer] = React.useState('');
const [open, setOpen] = React.useState(false);
const navigate = useNavigate();
const navigateTo = (path) => {
navigate(path);
};
React.useEffect(() => {
dispatch(
WidgetActions.widgetRequest({
@@ -61,74 +121,99 @@ function AddNewItem() {
method: 'get'
})
);
itemId &&
dispatch(
ItemActions.oneItemRequest({
loader: 'location-request',
slug: API.EDIT_ITEM,
method: 'get',
widgetName,
inventoryId,
itemId
})
);
}, []);
const [pFam, setPFam] = React.useState(null);
const primaryFamily = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
const secondaryFamily = useSelector(WidgetSelectors.getWidgetsByParentId(pFam));
LOGGER.log({ primaryFamily, secondaryFamily });
const formik = useFormik({
initialValues: {
commonName: '',
formalName: '',
description: '',
manufacturer: '',
size: '',
color: '',
type: '',
unitOfMaterial: '',
unitCost: 0,
packageCount: 0,
countPerPallet: 0,
countPerPalletPackage: 0,
primaryWidgetFamilyId: '',
secondaryWidgetFamilyId: '',
policiesMetadata: {
underStockLevelCount: 0,
overStockLevelCount: 0,
alertStockLevelCount: 0,
reorderStockLevelCount: 0
},
images: []
},
enableReinitialize: true,
initialValues: itemData,
validationSchema: schema.addNewItem,
onSubmit: (values, onSubmitProps) => {
onSubmit: (values) => {
LOGGER.log('values', values);
dispatch(
ProductActions.addProductAction({
loader: 'loading-request',
slug: API.ADD_PRODUCT,
method: 'post',
data: {
commonName: values.commonName,
formalName: values.formalName,
description: values.description,
manufacturer: values.manufacturer,
size: values.size,
color: values.color,
type: values.type,
unitOfMaterial: values.unitOfMaterial,
unitCost: values.unitCost,
packageCount: values.packageCount,
countPerPallet: values.countPerPallet,
countPerPalletPackage: values.countPerPalletPackage,
customAttributes: [],
policiesMetadata: {
underStockLevelCount: values.policiesMetadata.underStockLevelCount,
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();
itemId
? dispatch(
ItemActions.editItemRequest({
loader: 'loading-request',
slug: `${API.EDIT_ITEM}${itemId}`,
method: 'patch',
navigateTo,
data: {
commonName: values.commonName,
formalName: values.formalName,
description: values.description,
manufacturer: values.manufacturer,
size: values.size,
color: values.color,
type: values.type,
unitOfMaterial: values.unitOfMaterial,
unitCost: values.unitCost,
packageCount: values.packageCount,
countPerPallet: values.countPerPallet,
countPerPalletPackage: values.countPerPalletPackage,
customAttributes: [], // TBD
policiesMetadata: {
underStockLevelCount: values.policiesMetadata.underStockLevelCount,
overStockLevelCount: values.policiesMetadata.overStockLevelCount,
alertStockLevelCount: values.policiesMetadata.alertStockLevelCount,
reorderStockLevelCount: values.policiesMetadata.reorderStockLevelCount
},
widgetFamilyId:
values.secondaryWidgetFamilyId === ''
? values.primaryWidgetFamilyId
: values.secondaryWidgetFamilyId,
images: values.images
}
})
)
: dispatch(
ItemActions.addItemRequest({
loader: 'loading-request',
slug: API.ADD_ITEM,
method: 'post',
navigateTo,
data: {
commonName: values.commonName,
formalName: values.formalName,
description: values.description,
manufacturer: values.manufacturer,
size: values.size,
color: values.color,
type: values.type,
unitOfMaterial: values.unitOfMaterial,
unitCost: values.unitCost,
packageCount: values.packageCount,
countPerPallet: values.countPerPallet,
countPerPalletPackage: values.countPerPalletPackage,
customAttributes: [], // TBD
policiesMetadata: {
underStockLevelCount: values.policiesMetadata.underStockLevelCount,
overStockLevelCount: values.policiesMetadata.overStockLevelCount,
alertStockLevelCount: values.policiesMetadata.alertStockLevelCount,
reorderStockLevelCount: values.policiesMetadata.reorderStockLevelCount
},
widgetFamilyId:
values.secondaryWidgetFamilyId === ''
? values.primaryWidgetFamilyId
: values.secondaryWidgetFamilyId,
images: values.images
}
})
);
}
});
@@ -409,14 +494,27 @@ function AddNewItem() {
</Grid>
</Grid>
<Box>
<ImageUpload
heading="Upload Product Image"
accept="image/*"
images={formik.values.images}
setImages={(images) => {
formik.setFieldValue('images', images);
}}
/>
{itemId ? (
<ImageUploadMultiple
multiple
heading="Upload Product Image"
accept="image/*"
images={formik.values.images}
setImages={(images) => {
formik.setFieldValue('images', images);
}}
/>
) : (
<ImageUploadMultiple
multiple
heading="Upload Product Image"
accept="image/*"
images={formik.values.images}
setImages={(images) => {
formik.setFieldValue('images', images);
}}
/>
)}
</Box>
<Box
sx={{

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import {
@@ -16,13 +16,12 @@ import {
} from '@mui/material';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import ImageUpload from 'components/ImageUpload';
import ImageUploadSingle from 'components/ImageUploadSingle';
import MDButton from 'components/Button';
import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
// import schema from 'services/ValidationServices';
import MDInput from 'components/MDInput';
import WarehouseActions from 'redux/WarehouseRedux';
import SnackBar from 'components/SnackBar';
import { getChildLocationType } from 'utils/nestedTableTools';
import { getPropertiesOfLocationType } from 'utils/nestedTableTools';
import { getInitialvaluesFromParentData } from 'utils/nestedTableTools';
@@ -36,6 +35,8 @@ import { API } from 'constant';
import NestedDataTable from 'components/NestedTable';
import Breadcrumbs from 'components/Breadcrumbs';
import { WarehouseSelectors } from 'redux/WarehouseRedux';
import { InventorySelectors } from 'redux/InventoryRedux';
import InventoryActions from 'redux/InventoryRedux';
const bottomButtonStyling = {
width: '100%',
@@ -314,13 +315,22 @@ const WarehouseNestedDetails = () => {
);
};
const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet'];
function EditWarehouseDetails() {
const { warehouseId } = useParams();
const warehouseData = useSelector(WarehouseSelectors.getWarehouseDetailById(warehouseId));
const [open, setOpen] = useState(false);
const inventoryTypes = useSelector(InventorySelectors.getInventoryDetail);
React.useEffect(() => {
dispatch(
InventoryActions.getInventoryAction({
loader: 'loading-request',
slug: API.GET_INVENTORY,
method: 'get'
})
);
}, []);
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
@@ -336,13 +346,13 @@ function EditWarehouseDetails() {
const formik = useFormik({
initialValues: {
warehousename: warehouseData.name,
address: warehouseData.address,
inventorytype: [],
attributes: '',
images: warehouseData.images
address: warehouseData.address || '',
preferredInventories: warehouseData.preferredInventories || [],
specs: warehouseData.specs || '',
image: warehouseData.image_url ? [{ src: warehouseData.image_url }] : []
},
validationSchema: schema.warehouseForm,
onSubmit: (values, onSubmitProps) => {
// validationSchema: schema.warehouseForm,
onSubmit: (values) => {
dispatch(
WarehouseActions.editWarehouseAction({
loader: 'loading-request',
@@ -351,21 +361,14 @@ function EditWarehouseDetails() {
data: {
name: values.warehousename,
address: values.address,
specs: '',
company_id: ''
specs: values.specs,
preferredInventories: values.preferredInventories,
image: values.image
}
})
);
onSubmitProps.resetForm();
setOpen(true);
}
});
const handleClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setOpen(false);
};
return (
<>
@@ -456,40 +459,45 @@ function EditWarehouseDetails() {
<Select
multiple
select
disabled
fullWidth
variant="outlined"
name="inventorytype"
name="preferredInventories"
input={<OutlinedInput />}
value={formik.values.inventorytype}
error={formik.touched.inventorytype && Boolean(formik.errors.inventorytype)}
helperText={formik.touched.inventorytype && formik.errors.inventorytype}
value={formik.values.preferredInventories}
error={
formik.touched.preferredInventories &&
Boolean(formik.errors.preferredInventories)
}
helperText={
formik.touched.preferredInventories && formik.errors.preferredInventories
}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
<Chip
key={value}
label={inventoryTypes.find((x) => x._id === value)?.name || 'unknown'}
/>
))}
</Box>
)}
MenuProps={MenuProps}
onChange={(event) => {
const {
target: { value }
} = event;
onChange={(e) => {
const value = e.target.value;
formik.setFieldValue(
'inventorytype',
'preferredInventories',
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
);
}}
>
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
{inventoryTypes &&
inventoryTypes.map((inventory) => (
<MenuItem key={inventory._id} value={inventory._id}>
{inventory.name}
</MenuItem>
))}
</Select>
</Box>
<Box component="div" sx={{ marginBottom: '15px' }}>
@@ -508,23 +516,22 @@ function EditWarehouseDetails() {
fullWidth
type="text"
variant="outlined"
name="attributes"
value={formik.values.attributes}
error={formik.touched.attributes && Boolean(formik.errors.attributes)}
helperText={formik.touched.attributes && formik.errors.attributes}
name="specs"
value={formik.values.specs}
error={formik.touched.specs && Boolean(formik.errors.specs)}
helperText={formik.touched.specs && formik.errors.specs}
onChange={formik.handleChange}
/>
</Box>
</Grid>
<Grid item xs={12} sm={6} md={6}>
<Box sx={{ marginTop: '30px' }}>
<ImageUpload
multiple
<ImageUploadSingle
heading="Upload Warehouse Image"
accept="image/*"
images={formik.values.images}
images={formik.values.image}
setImages={(images) => {
formik.setFieldValue('images', images);
formik.setFieldValue('image', images);
}}
/>
</Box>
@@ -558,7 +565,6 @@ function EditWarehouseDetails() {
<WarehouseNestedDetails />
</Box>
</DashboardLayout>
<SnackBar open={open} message="warehouse edit successful" handleClose={handleClose} />
</>
);
}

View File

@@ -3,7 +3,6 @@ import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import { Box, Grid, MenuItem, Select } from '@mui/material';
import MDInput from 'components/MDInput';
import ImageUpload from 'components/ImageUpload';
import Switch from 'components/Switch';
import MDTypography from 'components/MDTypography';
import MDBox from 'components/MDBox';
@@ -76,6 +75,10 @@ function InventoryScreen() {
const navigate = useNavigate();
const { inventoryId } = useParams();
const navigateTo = () => {
navigate('/setup/inventory');
};
const currentInventoryData = useSelector(InventorySelectors.getInventoryDetailById(inventoryId));
LOGGER.log({ currentInventoryData });
// const [inventoryAllData, setInventoryAllData] = useState([]);
@@ -102,8 +105,7 @@ function InventoryScreen() {
replenishment: currentInventoryData.policies.replenishment,
preferredLocations: false, // TODO: change later when implemented on BE
inventory_process: currentInventoryData.policies.inventory_process
},
image: [{ src: currentInventoryData.image_url }]
}
}
: {
name: '',
@@ -114,8 +116,7 @@ function InventoryScreen() {
replenishment: false,
preferredLocations: false,
inventory_process: 'CCR'
},
image: []
}
},
validationSchema: schema.addInventory,
onSubmit: (values) => {
@@ -126,9 +127,10 @@ function InventoryScreen() {
loader: 'loading-request',
slug: `${API.ADD_INVENTORY}/${inventoryId}`,
method: 'patch',
navigateTo,
data: {
...values,
image: values.image[0]?.file || null
icon_slug: 'testslug'
}
})
)
@@ -137,14 +139,13 @@ function InventoryScreen() {
loader: 'loading-request',
slug: API.ADD_INVENTORY,
method: 'post',
navigateTo,
data: {
...values,
image: values.image[0]?.file || null
icon_slug: 'testslug'
}
})
);
// navigate to edit inventory page
// onSubmitProps.resetForm();
}
});
@@ -257,14 +258,7 @@ function InventoryScreen() {
</MDBox>
</Grid>
<Grid item xs={12} sm={6} md={6}>
<ImageUpload
heading="Upload Inventory Image"
accept="image/*"
images={formik.values.image}
setImages={(images) => {
formik.setFieldValue('image', images);
}}
/>
icon slugs selector
</Grid>
<MDBox sx={{ ml: 'auto', mr: 'auto', mt: 3 }}>
<MDButton

View File

@@ -1,3 +1,4 @@
/* eslint-disable indent */
import React from 'react';
import MDBox from 'components/MDBox';
import DashboardNavbar from 'components/DashboardNavbar';
@@ -10,41 +11,70 @@ import { useParams } from 'react-router-dom';
import LOGGER from 'services/Logger';
import { ItemSelectors } from 'redux/ItemRedux';
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';
const tHeads = [
{ key: 'name', name: '' },
{ key: 'commonName', name: 'Common Name' },
{ key: 'formalName', name: 'Formal Name' },
{ key: 'description', name: 'Description' },
{ key: 'manufacturer', name: 'Manufacturer' }
{ key: 'manufacturer', name: 'Manufacturer' },
{ key: 'size', name: 'Size' },
{ key: 'color', name: 'Color' },
{ key: 'type', name: 'Type' },
{ key: 'unitOfMaterial', name: 'Unit of Material' },
{ key: 'unitCost', name: 'Unit Cost' },
{ key: 'packageCount', name: 'Package Count' },
{ key: 'countPerPallet', name: 'Count Per Pallet' },
{ key: 'countPerPalletPackage', name: 'Count Per Pallet Package' }
];
function ItemListing() {
const dispatch = useDispatch();
const { widgetName, inventoryId } = useParams();
const [page /*, setPage*/] = React.useState(0);
const [perPage /*, setPerPage*/] = React.useState(10);
const [page, setPage] = React.useState(1);
const [perPage, setPerPage] = React.useState(10);
LOGGER.log({ widgetName, inventoryId });
const data = useSelector(ItemSelectors.getItemsByInventoryId(inventoryId));
const navigate = useNavigate();
const navigateTo = (path) => {
navigate(path);
};
React.useEffect(
() => {
dispatch(
ItemActions.itemRequest({
loader: 'loading-request',
slug: API.GET_ITEMS_BY_INVENTORY,
method: 'get',
page,
perPage,
inventoryId
})
);
},
[
/* page, perPage */
]
);
const data = useSelector(ItemSelectors.getItems);
const count = useSelector(ItemSelectors.getItemsCount);
const [pFam, setPFam] = React.useState('');
const [sFam, setSFam] = React.useState('');
const primaryFamilies = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
const secondaryFamilies = useSelector(WidgetSelectors.getWidgetsByParentId(pFam));
React.useEffect(() => {
dispatch(
ItemActions.itemRequest({
loader: 'loading-request',
slug: API.GET_ITEMS_BY_INVENTORY,
method: 'get',
page: page - 1,
perPage,
inventoryId,
family: sFam || pFam || null
})
);
}, [page, perPage, pFam, sFam]);
React.useEffect(() => {
dispatch(
WidgetActions.widgetRequest({
loader: 'location-request',
slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${inventoryId}`,
method: 'get'
})
);
}, []);
return (
<DashboardLayout>
@@ -60,7 +90,89 @@ function ItemListing() {
<MDBox px={2} py={3}>
List of {widgetName}s{/* <pre>{JSON.stringify(data, null, 4)}</pre> */}
<EnhancedTable data={data} tHeads={tHeads} />
<EnhancedTable
count={count}
page={page}
setPage={setPage}
perPage={perPage}
setPerPage={setPerPage}
editHandler={(id) => {
navigateTo(`/setup/inventory/browse/${widgetName}/${inventoryId}/edit/${id}`);
}}
resetFilters={() => {
setPFam('');
setSFam('');
}}
filtersControl={
<>
<DialogTitle>Add filters</DialogTitle>
<DialogContent>
<DialogContentText>Filter by family</DialogContentText>
<Select
select
fullWidth
variant="outlined"
value={pFam}
onChange={(e) => {
setSFam('');
setPFam(e.target.value);
}}
>
<MenuItem key={'none'} value={''}>
None Selected
</MenuItem>
{primaryFamilies &&
primaryFamilies.map((fam) => (
<MenuItem key={fam._id} value={fam._id}>
{fam.name}
</MenuItem>
))}
</Select>
<Select
select
fullWidth
variant="outlined"
value={sFam}
onChange={(e) => {
setSFam(e.target.value);
}}
>
<MenuItem key={'none'} value={''}>
None Selected
</MenuItem>
{secondaryFamilies &&
secondaryFamilies.map((fam) => (
<MenuItem key={fam._id} value={fam._id}>
{fam.name}
</MenuItem>
))}
</Select>
</DialogContent>
</>
}
data={
data
? data.map((item) => {
return {
name: item._id,
commonName: item.commonName,
formalName: item.formalName,
description: item.description,
manufacturer: item.manufacturer,
size: item.size,
color: item.color,
type: item.type,
unitOfMaterial: item.unitOfMaterial,
unitCost: item.unitCost,
packageCount: item.packageCount,
countPerPallet: item.countPerPallet,
countPerPalletPackage: item.countPerPalletPackage
};
})
: []
}
tHeads={tHeads}
/>
</MDBox>
</DashboardLayout>
);

View File

@@ -1,23 +1,40 @@
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Box, Grid, MenuItem, OutlinedInput, Chip, Select } from '@mui/material';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
import ImageUpload from 'components/ImageUpload';
import ImageUploadSingle from 'components/ImageUploadSingle';
import MDButton from 'components/Button';
import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
// import schema from 'services/ValidationServices';
import MDInput from 'components/MDInput';
import WarehouseActions from 'redux/WarehouseRedux';
import { API } from 'constant';
import SnackBar from 'components/SnackBar';
import Breadcrumbs from 'components/Breadcrumbs';
const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet'];
import { useNavigate } from 'react-router-dom';
import { InventorySelectors } from 'redux/InventoryRedux';
import InventoryActions from 'redux/InventoryRedux';
function NewWarehouseDetails() {
const dispatch = useDispatch();
const [open, setOpen] = useState(false);
const inventoryTypes = useSelector(InventorySelectors.getInventoryDetail);
React.useEffect(() => {
dispatch(
InventoryActions.getInventoryAction({
loader: 'loading-request',
slug: API.GET_INVENTORY,
method: 'get'
})
);
}, []);
const navigate = useNavigate();
const navigateTo = (id) => {
navigate(`/setup/warehouse/edit-warehouse/${id}`);
};
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
@@ -33,37 +50,31 @@ function NewWarehouseDetails() {
initialValues: {
warehousename: '',
address: '',
inventorytype: [],
attributes: '',
images: []
preferredInventories: [],
specs: '',
image: []
},
validationSchema: schema.warehouseForm,
onSubmit: (values, onSubmitProps) => {
// validationSchema: schema.warehouseForm,
onSubmit: (values) => {
dispatch(
WarehouseActions.createWarehouseAction({
loader: 'loading-request',
slug: API.CREATE_WAREHOUSE,
method: 'post',
navigateTo: navigateTo,
data: {
name: values.warehousename,
address: values.address,
specs: '',
preferredInventories: values.preferredInventories,
specs: values.specs,
image: values.image,
company_id: '61cea5fd028432700a7f8601'
}
})
);
onSubmitProps.resetForm();
setOpen(true);
}
});
const handleClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setOpen(false);
};
return (
<>
<DashboardLayout>
@@ -155,25 +166,31 @@ function NewWarehouseDetails() {
select
fullWidth
variant="outlined"
name="inventorytype"
name="preferredInventories"
input={<OutlinedInput />}
value={formik.values.inventorytype}
error={formik.touched.inventorytype && Boolean(formik.errors.inventorytype)}
helperText={formik.touched.inventorytype && formik.errors.inventorytype}
value={formik.values.preferredInventories}
error={
formik.touched.preferredInventories &&
Boolean(formik.errors.preferredInventories)
}
helperText={
formik.touched.preferredInventories && formik.errors.preferredInventories
}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
<Chip
key={value}
label={inventoryTypes.find((x) => x._id === value)?.name || 'unknown'}
/>
))}
</Box>
)}
MenuProps={MenuProps}
onChange={(event) => {
const {
target: { value }
} = event;
onChange={(e) => {
const value = e.target.value;
formik.setFieldValue(
'inventorytype',
'preferredInventories',
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
);
@@ -182,11 +199,12 @@ function NewWarehouseDetails() {
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
{inventoryTypes &&
inventoryTypes.map((inventory) => (
<MenuItem key={inventory._id} value={inventory._id}>
{inventory.name}
</MenuItem>
))}
</Select>
</Box>
<Box component="div" sx={{ marginBottom: '15px' }}>
@@ -205,23 +223,23 @@ function NewWarehouseDetails() {
fullWidth
type="text"
variant="outlined"
name="attributes"
value={formik.values.attributes}
error={formik.touched.attributes && Boolean(formik.errors.attributes)}
helperText={formik.touched.attributes && formik.errors.attributes}
name="specs"
value={formik.values.specs}
error={formik.touched.specs && Boolean(formik.errors.specs)}
helperText={formik.touched.specs && formik.errors.specs}
onChange={formik.handleChange}
/>
</Box>
</Grid>
<Grid item xs={12} sm={6} md={6}>
<Box sx={{ marginTop: '30px' }}>
<ImageUpload
<ImageUploadSingle
multiple
heading="Upload Warehouse Image"
accept="image/*"
images={formik.values.images}
setImages={(images) => {
formik.setFieldValue('images', images);
images={formik.values.image}
setImages={(image) => {
formik.setFieldValue('image', image);
}}
/>
</Box>
@@ -235,7 +253,14 @@ function NewWarehouseDetails() {
columnGap: '20px'
}}
>
<MDButton size="medium" color="error" variant="outlined">
<MDButton
size="medium"
color="error"
variant="outlined"
onClick={() => {
navigate('/setup/warehouse');
}}
>
CANCEL
</MDButton>
<MDButton size="medium" color="primary" variant="outlined" type="submit">
@@ -249,11 +274,6 @@ function NewWarehouseDetails() {
</form>
</Box>
</DashboardLayout>
<SnackBar
open={open}
message="new warehouse created successfully"
handleClose={handleClose}
/>
</>
);
}

View File

@@ -7,10 +7,13 @@ import { getFetchingValue, getErrorValue } from '../services/Utils';
const { Types, Creators } = createActions({
itemRequest: ['payload'],
itemSuccess: ['data'],
itemFailure: ['error'],
addItemRequest: ['payload'],
addItemSuccess: ['data'],
editItemRequest: ['payload'],
editItemSuccess: ['data'],
itemFailure: ['error'],
logout: null
oneItemRequest: ['payload'],
oneItemSuccess: ['data']
});
export const ItemTypes = Types;
@@ -20,6 +23,8 @@ export default ItemActions;
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
list: [],
item: null,
count: 0,
fetching: [],
error: {}
});
@@ -27,9 +32,9 @@ export const INITIAL_STATE = Immutable({
/* ------------- Selectors ------------- */
export const ItemSelectors = {
getItems: (state) => state.items.list,
getItemById: (id) => (state) => state.items.list.find((x) => x._id === id),
getItemsByInventoryId: (id) => (state) =>
state.items.list.filter((x) => x.widgetFamily?.inventory === id)
getFormItem: (itemId) => (state) => state.items.item?._id === itemId ? state.items.item : null,
getItemsCount: (state) => state.items.count,
getItemById: (id) => (state) => state.items.list.find((x) => x._id === id)
};
/* ------------- Reducers ------------- */
@@ -39,49 +44,51 @@ export const onItemRequest = (state, { payload }) =>
error: getErrorValue(state?.error, payload?.loader)
});
export const onItemSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
list: data.items,
count: data.count
});
export const onOneItemRequest = (state, { payload }) =>
state.merge({
fetching: _.uniq([...state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onOneItemSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
item: data.item
});
export const onAddItemRequest = (state, { payload }) =>
state.merge({
fetching: _.uniq([...state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onAddItemSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
item: null
});
export const onEditItemRequest = (state, { payload }) =>
state.merge({
fetching: _.uniq([...state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
const mergeItemStates = (stateData, items) => {
if (!items) return stateData; // undefined check
const idsInNewItems = items.map((x) => x._id);
const newState = stateData.filter((x) => !idsInNewItems.includes(x._id));
return [...newState, ...items];
};
export const onItemSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
list: mergeItemStates(state.list, data.items)
});
const mergeEditItemStates = (stateList, item, type) => {
if (!item) return stateList; // undefined check
if (type === 'add') {
return [...stateList, item];
} else if (type === 'edit') {
const newState = stateList.filter((x) => x._id !== item._id);
return [...newState, item];
// } else if (type === 'delete') {
// return stateList.filter((x) => x._id !== item._id);
} else {
return stateList;
}
};
export const onEditItemSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
list: mergeEditItemStates(state.list, data.item, data.type)
item: null
});
export const onItemFailure = (state, { error }) =>
@@ -93,8 +100,12 @@ export const onItemFailure = (state, { error }) =>
/* ------------- Hookup Reducers To Types ------------- */
export const itemReducer = createReducer(INITIAL_STATE, {
[Types.ITEM_REQUEST]: onItemRequest,
[Types.EDIT_ITEM_REQUEST]: onEditItemRequest,
[Types.ITEM_SUCCESS]: onItemSuccess,
[Types.ITEM_FAILURE]: onItemFailure,
[Types.ADD_ITEM_REQUEST]: onAddItemRequest,
[Types.ADD_ITEM_SUCCESS]: onAddItemSuccess,
[Types.EDIT_ITEM_REQUEST]: onEditItemRequest,
[Types.EDIT_ITEM_SUCCESS]: onEditItemSuccess,
[Types.ITEM_FAILURE]: onItemFailure
[Types.ONE_ITEM_REQUEST]: onOneItemRequest,
[Types.ONE_ITEM_SUCCESS]: onOneItemSuccess
});

View File

@@ -26,12 +26,8 @@ export default WarehouseActions;
export const INITIAL_STATE = Immutable({
warehouseDetail: [],
error: {},
createWarehouse: [],
createWarehouseLoading: false,
createWarehouseError: {},
editWarehouse: [],
editWarehouseLoading: false,
editWarehouseError: {}
});
@@ -75,7 +71,7 @@ export const onCreateWarehouseSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
createWarehouse: data.createWarehouse
warehouseDetail: [...state.warehouseDetail, data.createdWarehouse]
});
export const onCreateWarehouseFailure = (state, { error }) =>
@@ -94,7 +90,10 @@ export const onEditWarehouseSuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
editWarehouse: data.editWarehouse
warehouseDetail: [
...state.warehouseDetail.filter((x) => x._id !== data?.editedWarehouse?._id),
data.editedWarehouse
]
});
export const onEditWarehouseFailure = (state, { error }) =>

View File

@@ -117,6 +117,14 @@ const protectedRoutes = [
route: '/setup/inventory/new-item/:widgetName/:inventoryId',
component: <AddNewItem />
},
{
name: 'Update Item',
key: 'udpate-item',
hide: true,
route: '/setup/inventory/browse/:widgetName/:inventoryId/edit/:itemId',
component: <AddNewItem />
},
// /setup/inventory/browse/${payload?.widgetName}/${payload?.inventoryId}/edit/${payload?.id}
{
name: 'View Items',
key: 'view-items',

View File

@@ -55,35 +55,24 @@ export function* onRequestGetInventoryTypesData({ payload }) {
}
}
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)
payload?.data
);
if (response?.status === 200) {
toast('New inventory added');
yield put(
InventoryActions.addInventorySuccess({
loader: payload?.loader,
newInventory: response?.data?.data?.inventoryData
})
);
payload.navigateTo();
} else {
toast('Failed to add inventory');
payload.onFailedAddInventoryData(response.data.error);
yield put(
InventoryActions.addInventoryFailure({
@@ -109,8 +98,9 @@ export function* onRequestUpdateInventoryData({ payload }) {
updateInventory: response?.data?.data
})
);
payload.navigateTo();
} else {
payload.onFailedUpdateInventoryData(response.data.error);
toast('Failed to update inventory');
yield put(
InventoryActions.updateInventoryFailure({
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 ItemActions, { ItemTypes } from '../redux/ItemRedux';
@@ -7,14 +8,17 @@ export function* onRequestItem({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
`${payload?.slug}${payload?.inventoryId}&page=${payload?.page}&perPage=${payload?.perPage}`,
`${payload?.slug}${payload?.inventoryId}&page=${payload?.page}&perPage=${payload?.perPage}${
payload?.family ? '&family=' + payload?.family : ''
}`,
payload?.data
);
if (response?.status === 200) {
yield put(
ItemActions.itemSuccess({
loader: payload?.loader,
items: response?.data?.data,
items: response?.data?.data.result,
count: response?.data?.data.count,
page: payload?.page,
reset: !payload.page
})
@@ -29,22 +33,24 @@ export function* onRequestItem({ payload }) {
}
}
export function* onEditRequestItem({ payload }) {
export function* onRequestOneItem({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
`${payload.slug}${payload?.itemId}`
);
if (response?.status === 200) {
yield put(
ItemActions.editItemSuccess({
ItemActions.oneItemSuccess({
loader: payload?.loader,
item: response?.data?.data,
type: payload?.type
item: response?.data?.data
})
);
// payload.navigateTo(
// `/setup/inventory/browse/${payload?.widgetName}/${payload?.inventoryId}/edit/${payload?.itemId}`
// );
} else {
toast('Failed to get item details');
yield put(
ItemActions.itemFailure({
loader: payload?.loader,
@@ -54,7 +60,98 @@ export function* onEditRequestItem({ payload }) {
}
}
function addImagesToFormData(formData, images) {
let imgIdx = 0;
let preImgIdx = 0;
images.forEach((image) => {
if (image.file) formData.append(`images[${imgIdx++}]`, image.file);
else formData.append(`imageIds[${preImgIdx++}]`, image._id);
});
}
function buildFormData(formData, data, parentKey) {
if (data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File)) {
Object.keys(data).forEach((key) => {
if (key !== 'images')
buildFormData(formData, data[key], parentKey ? `${parentKey}[${key}]` : key);
else addImagesToFormData(formData, data['images']);
});
} else {
// eslint-disable-next-line eqeqeq
const value = data == null ? '' : data;
formData.append(parentKey, value);
}
}
const createFormData = (data) => {
const formData = new FormData();
buildFormData(formData, data);
return formData;
};
export function* onAddRequestItem({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
createFormData(payload?.data)
);
if (response?.status === 200) {
toast(`Added item: ${payload.data.commonName}`);
// payload.navigateTo(
// `/setup/inventory/browse/${payload?.widgetName}/${payload?.inventoryId}/edit/${response?.data?.data?._id}`
// );
payload.navigateTo('/setup/inventory');
yield put(
ItemActions.addItemSuccess({
loader: payload?.loader,
item: response?.data?.data
})
);
} else {
toast('Failed to add item');
yield put(
ItemActions.itemFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export function* onEditRequestItem({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
createFormData(payload?.data)
);
if (response?.status === 200) {
toast(`Successfully edited item: ${payload.data.commonName}`);
payload.navigateTo('/setup/inventory');
yield put(
ItemActions.addItemSuccess({
loader: payload?.loader,
item: response?.data?.data
})
);
} else {
toast('Failed to edit item');
yield put(
ItemActions.itemFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
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)
];

View File

@@ -2,6 +2,7 @@ import { AuthorizedAPI } from 'config';
import { takeLatest, call, put } from 'redux-saga/effects';
import WarehouseActions, { WarehouseTypes } from '../redux/WarehouseRedux';
import ApiServices from 'services/API/ApiServices';
import { toast } from 'react-toastify';
export function* onRequestWarehouseData({ payload }) {
const response = yield call(
@@ -28,22 +29,42 @@ export function* onRequestWarehouseData({ payload }) {
}
}
const makeFormData = (data) => {
const formData = new FormData();
if (data.name) formData.append('name', data.name);
if (data.address) formData.append('address', data.address);
if (data.specs) formData.append('specs', data.specs);
if (data.company_id) formData.append('company_id', data.company_id);
if (data.preferredInventories)
data.preferredInventories.forEach((prefInv, idx) => {
formData.append(`preferredInventories[${idx}]`, prefInv);
});
if (data.image[0].file) formData.append('image', data.image[0].file);
return formData;
};
export function* onRequestCreateWarehouse({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
makeFormData(payload?.data)
);
if (response?.status === 200) {
if (response?.status === 200 && response?.data?.message) {
const warehouse = response?.data?.message;
toast('Warehouse created successfully');
yield put(
WarehouseActions.createWarehouseSuccess({
loader: payload?.loader,
createWarehouse: response?.data?.data
createdWarehouse: {
...warehouse,
preferredInventories: warehouse.preferredInventories.map((z) => z._id)
}
})
);
payload.navigateTo(response?.data?.message?._id);
} else {
payload.onFailedCreateWarehouse(response.data.error);
toast('Failed to create warehouse');
yield put(
WarehouseActions.createWarehouseFailure({
loader: payload?.loader,
@@ -58,17 +79,22 @@ export function* onRequestEditWarehouse({ payload }) {
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
makeFormData(payload?.data)
);
if (response?.status === 200) {
if (response?.status === 200 && response?.data?.data) {
toast('Warehouse edited successfully');
const warehouse = response?.data?.data;
yield put(
WarehouseActions.editWarehouseSuccess({
loader: payload?.loader,
createWarehouse: response?.data?.data
editedWarehouse: {
...warehouse,
preferredInventories: warehouse.preferredInventories.map((z) => z._id)
}
})
);
} else {
payload.onFailedEditWarehouse(response.data.error);
toast('Failed to edit warehouse');
yield put(
WarehouseActions.editWarehouseFailure({
loader: payload?.loader,

View File

@@ -22,10 +22,11 @@ const schema = {
warehouseForm: Yup.object({
warehousename: Yup.string('Enter warehouse name').required('warehouse name is required'),
address: Yup.string('Enter address').required('address is required'),
inventorytype: Yup.array('Enter inventory Type')
preferredInventories: Yup.array('Enter inventory Type')
.of(Yup.string())
.required('inventory Type is required'),
attributes: Yup.string('Enter other attributes')
specs: Yup.string('Enter other attributes'),
image: Yup.array()
}),
addNewItem: Yup.object({