Fixes/ll inventory (#70)
* 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 * Added: Add items button * Added: Item list page * Added: saga * Added: Route handler * Added: item redux * Udpate: breadcrumbs * Fixed: edit widget * Fix: delete widget * Added: item list table * Update: Routing * Update: dynamic headers * Update: disabled edit * Added: toastify and toast * removed: nested table page * Fixewd: switch and inventory form * Update: data table * Fixed: image upload * Update: redux and widget * Update: warehouse edit with selector Co-authored-by: evdigitech <evdigitech@gmail.com> Co-authored-by: Llewellyn Dsouza <lledsouza2209@gmail.com>
This commit is contained in:
@@ -53,6 +53,9 @@ import reduxStore from './redux/Store';
|
||||
import { protectedRoutes as routes } from './routes/index';
|
||||
import PrivateRoute from './routes/PrivateRoute';
|
||||
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
export default function App() {
|
||||
const [controller, dispatch] = useMaterialUIController();
|
||||
const {
|
||||
@@ -166,6 +169,7 @@ export default function App() {
|
||||
<Route path="*" element={<Navigate to="/" />} />
|
||||
</Routes>
|
||||
</ThemeProvider>
|
||||
<ToastContainer />
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
283
src/components/EnhancedTable/index.js
Normal file
283
src/components/EnhancedTable/index.js
Normal file
@@ -0,0 +1,283 @@
|
||||
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';
|
||||
import TableCell, { tableCellClasses } from '@mui/material/TableCell';
|
||||
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';
|
||||
|
||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||
[`&.${tableCellClasses.head}`]: {
|
||||
backgroundColor: '#e5e7eb',
|
||||
color: theme.palette.common.black,
|
||||
fontWeight: 400
|
||||
},
|
||||
[`&.${tableCellClasses.body}`]: {
|
||||
fontSize: 14,
|
||||
fontWeight: 400
|
||||
}
|
||||
}));
|
||||
|
||||
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
||||
'&:nth-of-type(2n+1)': {
|
||||
backgroundColor: theme.palette.action.hover
|
||||
},
|
||||
'td, th': {
|
||||
padding: '0.75rem 0.5rem'
|
||||
},
|
||||
// hide last border
|
||||
'&:last-child td, &:last-child th': {
|
||||
border: 0
|
||||
}
|
||||
}));
|
||||
|
||||
Row.propTypes = {
|
||||
rowData: PropTypes.array,
|
||||
tHeads: PropTypes.array
|
||||
};
|
||||
|
||||
function Row({ tHeads, rowData }) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<StyledTableRow sx={{ '&odd > *': { borderBottom: 'unset' } }}>
|
||||
<StyledTableCell sx={{ width: '10%', display: 'flex', alignItems: 'center' }}>
|
||||
<MDButton
|
||||
disabled
|
||||
size="small"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
sx={{
|
||||
textTransform: 'capitalize',
|
||||
minWidth: '45px',
|
||||
minHeight: '28px',
|
||||
marginLeft: '10px',
|
||||
boxShadow: 'none',
|
||||
fontWeight: '500',
|
||||
padding: '0'
|
||||
}}
|
||||
>
|
||||
EDIT
|
||||
</MDButton>
|
||||
</StyledTableCell>
|
||||
{tHeads &&
|
||||
tHeads
|
||||
.slice(1)
|
||||
.map((head) => <StyledTableCell key={head.key}>{rowData[head.key]}</StyledTableCell>)}
|
||||
</StyledTableRow>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function EnhancedTable({ data, tHeads }) {
|
||||
// const [anchorEl, setAnchorEl] = React.useState(false);
|
||||
// const open = Boolean(anchorEl);
|
||||
// const handleClick = (event) => {
|
||||
// setAnchorEl(event.currentTarget);
|
||||
// };
|
||||
// const handleClose = () => {
|
||||
// setAnchorEl(null);
|
||||
// };
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
border: '1px solid #c4c4c4',
|
||||
borderTop: '6px solid #007aff',
|
||||
borderRadius: '4px',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '16px'
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<SearchBar />
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', columnGap: '15px' }}>
|
||||
<MDButton
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
sx={{
|
||||
textTransform: 'capitalize',
|
||||
minWidth: '60px',
|
||||
minHeight: '44px',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
Sorting
|
||||
</MDButton>
|
||||
<MDButton
|
||||
id="fade-button"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
sx={{
|
||||
textTransform: 'capitalize',
|
||||
minWidth: '60px',
|
||||
minHeight: '44px',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
aria-controls={open ? 'fade-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
endIcon={<KeyboardArrowDownIcon />}
|
||||
onClick={() => {}}
|
||||
>
|
||||
Dashboard
|
||||
</MDButton>
|
||||
<Menu
|
||||
id="fade-menu"
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'fade-button'
|
||||
}}
|
||||
anchorEl={null}
|
||||
open={false}
|
||||
TransitionComponent={Fade}
|
||||
onClose={() => {}}
|
||||
>
|
||||
<MenuItem>Profile</MenuItem>
|
||||
<MenuItem>My account</MenuItem>
|
||||
<MenuItem>Logout</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
</Box>
|
||||
{/* Table-row- */}
|
||||
<TableContainer component={Paper} sx={{ borderRadius: '0 !important', boxShadow: 'none' }}>
|
||||
<Table aria-label="collapsible table" sx={{ minWidth: 700 }}>
|
||||
<TableHead sx={{ display: 'table-header-group' }}>
|
||||
<TableRow>
|
||||
{tHeads &&
|
||||
tHeads.map((head) => (
|
||||
<StyledTableCell key={head.name}>{head.name}</StyledTableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{data &&
|
||||
data.map((rowData) => <Row key={rowData._id} rowData={rowData} tHeads={tHeads} />)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '15px 10px'
|
||||
}}
|
||||
>
|
||||
{/* <Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<MDButton
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
sx={{
|
||||
textTransform: 'inherit',
|
||||
minWidth: '45px',
|
||||
minHeight: '28px',
|
||||
boxShadow: 'none',
|
||||
fontWeight: '500',
|
||||
padding: '0'
|
||||
}}
|
||||
>
|
||||
Go to
|
||||
</MDButton>
|
||||
<MDButton
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
sx={{
|
||||
textTransform: 'inherit',
|
||||
minWidth: '45px',
|
||||
minHeight: '28px',
|
||||
marginLeft: '10px',
|
||||
boxShadow: 'none',
|
||||
fontWeight: '500',
|
||||
padding: '0',
|
||||
border: ' 1px solid #C2C2C2',
|
||||
color: '#000'
|
||||
}}
|
||||
>
|
||||
1
|
||||
</MDButton>
|
||||
<Box
|
||||
sx={{ display: 'flex', alignItems: 'center', fontSize: '12px', marginLeft: '15px' }}
|
||||
>
|
||||
View:
|
||||
<MDButton
|
||||
id="fade-button"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
aria-controls={open ? 'fade-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
endIcon={<KeyboardArrowDownIcon />}
|
||||
sx={{
|
||||
textTransform: 'inherit',
|
||||
minWidth: '45px',
|
||||
minHeight: '28px',
|
||||
marginLeft: '10px',
|
||||
boxShadow: 'none',
|
||||
fontWeight: '500',
|
||||
padding: '0',
|
||||
border: ' 1px solid #C2C2C2',
|
||||
color: '#000'
|
||||
}}
|
||||
onClick={handleClick}
|
||||
>
|
||||
12
|
||||
</MDButton>
|
||||
<Menu
|
||||
id="fade-menu"
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'fade-button'
|
||||
}}
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
TransitionComponent={Fade}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<MenuItem>Profile</MenuItem>
|
||||
<MenuItem>My account</MenuItem>
|
||||
<MenuItem>Logout</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
</Box> */}
|
||||
{/*---- pagination- */}
|
||||
<Box>
|
||||
<TablePagination />
|
||||
</Box>
|
||||
<Box sx={{ fontSize: '14px', color: '#000' }}>[1 to 10 of 92]</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
EnhancedTable.propTypes = {
|
||||
data: PropTypes.array,
|
||||
tHeads: PropTypes.array
|
||||
};
|
||||
|
||||
export default EnhancedTable;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-case-declarations */
|
||||
import MDBox from 'components/MDBox';
|
||||
import PropTypes from 'prop-types';
|
||||
import UploadIcon from 'assets/images/UploadIcon';
|
||||
@@ -5,15 +6,48 @@ import MDTypography from 'components/MDTypography';
|
||||
import pxToRem from 'assets/theme-dark/functions/pxToRem';
|
||||
import { Button } from '@mui/material';
|
||||
import Close from 'assets/images/Close';
|
||||
import { AuthorizedAPI } from 'config';
|
||||
import LOGGER from 'services/Logger';
|
||||
|
||||
function ImageUpload({ heading, accept, multiple, images, setImages }) {
|
||||
const deleteImage = async (type, id, imageId) => {
|
||||
LOGGER.log({ type, id, imageId });
|
||||
switch (type) {
|
||||
case 'warehouse':
|
||||
await AuthorizedAPI.delete(`/warehouse/${id}/image/${imageId}`);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const addNewImage = async (type, id, image, addNewImageToImages) => {
|
||||
LOGGER.log({ type, id, image });
|
||||
const formData = new FormData();
|
||||
switch (type) {
|
||||
case 'warehouse':
|
||||
formData.append('warehouse-image', image);
|
||||
const response = await AuthorizedAPI.post(`/warehouse/add-image/${id}`, formData);
|
||||
addNewImageToImages(response?.data?.data);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
function ImageUpload({ heading, accept, multiple, images, setImages, type, pageId }) {
|
||||
const addNewImageToImages = (image) => {
|
||||
setImages([...images, image]);
|
||||
};
|
||||
const addImage = (e) => {
|
||||
addNewImage(type, pageId, e.target.files[0], addNewImageToImages);
|
||||
setImages([
|
||||
...images,
|
||||
{ src: URL.createObjectURL(e.target.files[0]), file: e.target.files[0] }
|
||||
]);
|
||||
};
|
||||
const removeImage = (index) => {
|
||||
deleteImage(type, pageId, images._id);
|
||||
setImages(images.filter((_val, idx) => idx !== index));
|
||||
};
|
||||
|
||||
@@ -26,91 +60,133 @@ function ImageUpload({ heading, accept, multiple, images, setImages }) {
|
||||
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'
|
||||
}}
|
||||
>
|
||||
{multiple || !images.length ? (
|
||||
<MDBox
|
||||
component="input"
|
||||
name="file"
|
||||
disabled={!multiple && images.length}
|
||||
accept={accept}
|
||||
type="file"
|
||||
sx={{
|
||||
width: '100%',
|
||||
opacity: '0',
|
||||
border: '1px dashed #C4C4C4',
|
||||
borderRadius: pxToRem(6),
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
right: '0',
|
||||
bottom: '0'
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
minHeight: pxToRem(200),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '16px'
|
||||
}}
|
||||
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
|
||||
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>
|
||||
</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={() => {
|
||||
removeImage(0);
|
||||
}}
|
||||
>
|
||||
<Close />
|
||||
</Button>
|
||||
</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
|
||||
{multiple ? (
|
||||
<MDBox sx={{ marginBottom: '-10px' }}>
|
||||
{images &&
|
||||
images.map((item, idx) => {
|
||||
return (
|
||||
<MDBox
|
||||
key={idx}
|
||||
component="span"
|
||||
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);
|
||||
width: '80px',
|
||||
height: '63px',
|
||||
marginRight: '16px',
|
||||
display: 'inline-block',
|
||||
borderRadius: '4px',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Close />
|
||||
</Button>
|
||||
</MDBox>
|
||||
);
|
||||
})}
|
||||
</MDBox>
|
||||
<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>
|
||||
) : null}
|
||||
</MDBox>
|
||||
</>
|
||||
);
|
||||
@@ -122,5 +198,7 @@ ImageUpload.propTypes = {
|
||||
heading: PropTypes.string,
|
||||
multiple: PropTypes.bool,
|
||||
accept: PropTypes.string,
|
||||
setImages: PropTypes.func
|
||||
setImages: PropTypes.func,
|
||||
type: PropTypes.string,
|
||||
pageId: PropTypes.string
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
// import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import PropTypes from 'prop-types';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import FormGroup from '@mui/material/FormGroup';
|
||||
// import FormGroup from '@mui/material/FormGroup';
|
||||
import SwitchButton from '@mui/material/Switch';
|
||||
|
||||
const IOSSwitch = styled((props) => (
|
||||
@@ -56,14 +56,12 @@ const IOSSwitch = styled((props) => (
|
||||
}
|
||||
}));
|
||||
|
||||
export default function Switch({ checked }) {
|
||||
return (
|
||||
<FormGroup>
|
||||
<FormControlLabel control={<IOSSwitch defaultChecked={checked} sx={{ m: 1 }} />} label="" />
|
||||
</FormGroup>
|
||||
);
|
||||
export default function Switch({ checked, onChange, name }) {
|
||||
return <IOSSwitch checked={checked} sx={{ m: 1 }} name={name} onChange={onChange} />;
|
||||
}
|
||||
|
||||
Switch.propTypes = {
|
||||
checked: PropTypes.bool
|
||||
checked: PropTypes.any,
|
||||
name: PropTypes.any,
|
||||
onChange: PropTypes.any
|
||||
};
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function Tile({ data, children }) {
|
||||
Cycle Count <ArrowRightIcon />
|
||||
</Box>
|
||||
</Link>
|
||||
<Link to="/">
|
||||
<Link to={`/setup/inventory/browse/${data.widgetname}/${data.id}`}>
|
||||
<Box className={`${classes.box} ${classes.boxEven}`}>
|
||||
{data.widgetname} List <ArrowRightIcon />
|
||||
</Box>
|
||||
|
||||
@@ -46,7 +46,7 @@ function MaterialForm({ formType, setFormOpen, selected, inventoryId }) {
|
||||
: dispatch(
|
||||
WidgetActions.editWidgetRequest({
|
||||
loader: 'location-request',
|
||||
slug: `${API.EDIT_WIDGET_FAMILY}${inventoryId}`,
|
||||
slug: `${API.EDIT_WIDGET_FAMILY}${formType._id}`,
|
||||
method: 'patch',
|
||||
data: values,
|
||||
type: 'edit'
|
||||
@@ -113,6 +113,7 @@ function WidgetNestedDataTable({
|
||||
inventoryId
|
||||
}) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const widgetChildren = useSelector(WidgetSelectors.getWidgetsByParentId(data._id));
|
||||
|
||||
return (
|
||||
@@ -167,46 +168,44 @@ function WidgetNestedDataTable({
|
||||
</MDButton>
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={10}
|
||||
xs={8}
|
||||
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>
|
||||
{data.name}
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<MDButton
|
||||
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={() => {
|
||||
setSelected(null);
|
||||
dispatch(
|
||||
WidgetActions.editWidgetRequest({
|
||||
loader: 'location-request',
|
||||
slug: `${API.EDIT_WIDGET_FAMILY}${data._id}`,
|
||||
deletedId: data._id,
|
||||
method: 'delete',
|
||||
type: 'delete'
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
DELETE
|
||||
</MDButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{open && widgetChildren ? (
|
||||
|
||||
@@ -18,5 +18,6 @@ export default {
|
||||
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/'
|
||||
EDIT_WIDGET_FAMILY: '/widget-family/',
|
||||
GET_ITEMS_BY_INVENTORY: '/item/filter?inventory='
|
||||
};
|
||||
|
||||
@@ -21,7 +21,6 @@ import MDButton from 'components/Button';
|
||||
import { useFormik } from 'formik';
|
||||
import schema from 'services/ValidationServices';
|
||||
import MDInput from 'components/MDInput';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import WarehouseActions from 'redux/WarehouseRedux';
|
||||
import SnackBar from 'components/SnackBar';
|
||||
import { getChildLocationType } from 'utils/nestedTableTools';
|
||||
@@ -36,6 +35,7 @@ import { WarehouseLocationsSelectors } from 'redux/WarehouseLocationsRedux';
|
||||
import { API } from 'constant';
|
||||
import NestedDataTable from 'components/NestedTable';
|
||||
import Breadcrumbs from 'components/Breadcrumbs';
|
||||
import { WarehouseSelectors } from 'redux/WarehouseRedux';
|
||||
|
||||
const bottomButtonStyling = {
|
||||
width: '100%',
|
||||
@@ -317,7 +317,9 @@ const WarehouseNestedDetails = () => {
|
||||
const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet'];
|
||||
|
||||
function EditWarehouseDetails() {
|
||||
const location = useLocation();
|
||||
const { warehouseId } = useParams();
|
||||
const warehouseData = useSelector(WarehouseSelectors.getWarehouseDetailById(warehouseId));
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const ITEM_HEIGHT = 48;
|
||||
const ITEM_PADDING_TOP = 8;
|
||||
@@ -329,21 +331,22 @@ function EditWarehouseDetails() {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
warehousename: location.state.name,
|
||||
address: location.state.address,
|
||||
warehousename: warehouseData.name,
|
||||
address: warehouseData.address,
|
||||
inventorytype: [],
|
||||
attributes: '',
|
||||
images: []
|
||||
images: warehouseData.images
|
||||
},
|
||||
validationSchema: schema.warehouseForm,
|
||||
onSubmit: (values, onSubmitProps) => {
|
||||
dispatch(
|
||||
WarehouseActions.editWarehouseAction({
|
||||
loader: 'loading-request',
|
||||
slug: `/warehouse/${location.state.id}`,
|
||||
slug: `/warehouse/${warehouseData._id}`,
|
||||
method: 'patch',
|
||||
data: {
|
||||
name: values.warehousename,
|
||||
@@ -373,7 +376,7 @@ function EditWarehouseDetails() {
|
||||
{ name: 'Home', path: '/home' },
|
||||
{ name: 'Setup', path: '/setup' },
|
||||
{ name: 'Warehouse', path: '/setup/warehouse' },
|
||||
{ name: location.state.name || '' }
|
||||
{ name: warehouseData.name || '' }
|
||||
]}
|
||||
/>
|
||||
<Box mx={3} my={3}>
|
||||
|
||||
@@ -103,7 +103,7 @@ function InventoryScreen() {
|
||||
preferredLocations: false, // TODO: change later when implemented on BE
|
||||
inventory_process: currentInventoryData.policies.inventory_process
|
||||
},
|
||||
image: [{ src: currentInventoryData.image }]
|
||||
image: [{ src: currentInventoryData.image_url }]
|
||||
}
|
||||
: {
|
||||
name: '',
|
||||
@@ -118,7 +118,7 @@ function InventoryScreen() {
|
||||
image: []
|
||||
},
|
||||
validationSchema: schema.addInventory,
|
||||
onSubmit: (values, onSubmitProps) => {
|
||||
onSubmit: (values) => {
|
||||
LOGGER.log('values', values);
|
||||
inventoryId
|
||||
? dispatch(
|
||||
@@ -144,9 +144,11 @@ function InventoryScreen() {
|
||||
})
|
||||
);
|
||||
// navigate to edit inventory page
|
||||
onSubmitProps.resetForm();
|
||||
// onSubmitProps.resetForm();
|
||||
}
|
||||
});
|
||||
|
||||
LOGGER.log('Form values', formik.values);
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardNavbar />
|
||||
@@ -178,7 +180,6 @@ function InventoryScreen() {
|
||||
</Box>
|
||||
<MDInput
|
||||
fullWidth
|
||||
disabled={inventoryId}
|
||||
name="name"
|
||||
type="text"
|
||||
variant="outlined"
|
||||
@@ -195,7 +196,6 @@ function InventoryScreen() {
|
||||
</Box>
|
||||
<MDInput
|
||||
fullWidth
|
||||
disabled={inventoryId}
|
||||
name="widgetName"
|
||||
type="text"
|
||||
variant="outlined"
|
||||
@@ -227,7 +227,6 @@ function InventoryScreen() {
|
||||
<div sx={customStyles.gridWrap} key={item.key}>
|
||||
<MDTypography sx={customStyles.textWrap}>{item.text}</MDTypography>
|
||||
<Switch
|
||||
disabled={inventoryId}
|
||||
name={`policies.${item.key}`}
|
||||
checked={formik.values.policies[item.key]}
|
||||
onChange={formik.handleChange}
|
||||
@@ -241,7 +240,6 @@ function InventoryScreen() {
|
||||
<Select
|
||||
select
|
||||
fullWidth
|
||||
disabled={inventoryId}
|
||||
variant="outlined"
|
||||
name="policies.inventory_process"
|
||||
value={formik.values.policies.inventory_process}
|
||||
@@ -279,23 +277,35 @@ function InventoryScreen() {
|
||||
>
|
||||
{'CANCEL'}
|
||||
</MDButton>
|
||||
<MDButton sx={{ ml: 3 }} color="primary" variant="outlined" type="submit">
|
||||
{'SAVE'}
|
||||
</MDButton>
|
||||
<MDButton
|
||||
sx={{ ml: 3 }}
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
disabled={inventoryId}
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/setup/inventory/new-item/${currentInventoryData.widgetName}/${inventoryId}`
|
||||
);
|
||||
}}
|
||||
>
|
||||
{'SAVE'}
|
||||
</MDButton>
|
||||
<MDButton sx={{ ml: 3 }} color="primary">
|
||||
{'ADD ITEMS'}
|
||||
</MDButton>
|
||||
</MDBox>
|
||||
</Grid>
|
||||
</MDBox>
|
||||
</form>
|
||||
{inventoryId ? <WidgetNestedDataTable inventoryId={inventoryId} /> : null}
|
||||
{inventoryId ? (
|
||||
<>
|
||||
<MDBox sx={{ my: 4 }}>
|
||||
<MDTypography variant="h5">Widget family hierarchy</MDTypography>
|
||||
<MDTypography sx={customStyles.textSize}>
|
||||
Define widget family and sub-family
|
||||
</MDTypography>
|
||||
</MDBox>
|
||||
<WidgetNestedDataTable inventoryId={inventoryId} />
|
||||
</>
|
||||
) : null}
|
||||
</MDBox>
|
||||
</DashboardLayout>
|
||||
);
|
||||
|
||||
68
src/pages/itemListing/index.js
Normal file
68
src/pages/itemListing/index.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import MDBox from 'components/MDBox';
|
||||
import DashboardNavbar from 'components/DashboardNavbar';
|
||||
import DashboardLayout from 'layouts/DashboardLayout';
|
||||
import Breadcrumbs from 'components/Breadcrumbs';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import ItemActions from 'redux/ItemRedux';
|
||||
import { API } from 'constant';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import LOGGER from 'services/Logger';
|
||||
import { ItemSelectors } from 'redux/ItemRedux';
|
||||
import EnhancedTable from 'components/EnhancedTable';
|
||||
|
||||
const tHeads = [
|
||||
{ key: 'name', name: '' },
|
||||
{ key: 'commonName', name: 'Common Name' },
|
||||
{ key: 'formalName', name: 'Formal Name' },
|
||||
{ key: 'description', name: 'Description' },
|
||||
{ key: 'manufacturer', name: 'Manufacturer' }
|
||||
];
|
||||
|
||||
function ItemListing() {
|
||||
const dispatch = useDispatch();
|
||||
const { widgetName, inventoryId } = useParams();
|
||||
const [page /*, setPage*/] = React.useState(0);
|
||||
const [perPage /*, setPerPage*/] = React.useState(10);
|
||||
LOGGER.log({ widgetName, inventoryId });
|
||||
|
||||
const data = useSelector(ItemSelectors.getItemsByInventoryId(inventoryId));
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
dispatch(
|
||||
ItemActions.itemRequest({
|
||||
loader: 'loading-request',
|
||||
slug: API.GET_ITEMS_BY_INVENTORY,
|
||||
method: 'get',
|
||||
page,
|
||||
perPage,
|
||||
inventoryId
|
||||
})
|
||||
);
|
||||
},
|
||||
[
|
||||
/* page, perPage */
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardNavbar />
|
||||
<Breadcrumbs
|
||||
route={[
|
||||
{ name: 'Home', path: '/home' },
|
||||
{ name: 'Setup', path: '/setup' },
|
||||
{ name: 'Inventory', path: '/setup/inventory' },
|
||||
{ name: `${widgetName}s List` }
|
||||
]}
|
||||
/>
|
||||
|
||||
<MDBox px={2} py={3}>
|
||||
List of {widgetName}s{/* <pre>{JSON.stringify(data, null, 4)}</pre> */}
|
||||
<EnhancedTable data={data} tHeads={tHeads} />
|
||||
</MDBox>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
export default ItemListing;
|
||||
@@ -1,313 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import MDButton from 'components/Button';
|
||||
import DashboardNavbar from 'components/DashboardNavbar';
|
||||
import DashboardLayout from 'layouts/DashboardLayout';
|
||||
import {
|
||||
Box,
|
||||
Chip,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
MenuItem,
|
||||
OutlinedInput,
|
||||
Select,
|
||||
TextField
|
||||
} from '@mui/material';
|
||||
import NestedDataTable from 'components/NestedTable';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import WarehouseLocationsActions from 'redux/WarehouseLocationsRedux';
|
||||
import { API } from 'constant';
|
||||
import { WarehouseLocationsSelectors } from 'redux/WarehouseLocationsRedux';
|
||||
import LOGGER from 'services/Logger';
|
||||
import { getPropertiesOfLocationType } from 'utils/nestedTableTools';
|
||||
import { useFormik } from 'formik';
|
||||
import { getInitialvaluesFromParentData } from 'utils/nestedTableTools';
|
||||
import { toTitleCase } from 'utils/nestedTableTools';
|
||||
import { getChildLocationType } from 'utils/nestedTableTools';
|
||||
import { getAPIslugOfLocationType } from 'utils/nestedTableTools';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
const bottomButtonStyling = {
|
||||
width: '100%',
|
||||
textTransform: 'uppercase',
|
||||
borderRadius: '100px',
|
||||
padding: '13px 30px'
|
||||
};
|
||||
|
||||
const AddForm = ({ addFormOpen, setAddFormOpen, selected, warehouseId }) => {
|
||||
const dispatch = useDispatch();
|
||||
const data = addFormOpen !== 'zone' ? selected : { location: 'warehouse', id: warehouseId };
|
||||
|
||||
const childLocationType = getChildLocationType(data.location);
|
||||
const fields = getPropertiesOfLocationType(childLocationType);
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: getInitialvaluesFromParentData(data),
|
||||
onSubmit: (values) => {
|
||||
LOGGER.log('Form values and parent info', values, data);
|
||||
const formData = { ...values };
|
||||
formData[`${data.location}_id`] = data.id;
|
||||
dispatch(
|
||||
WarehouseLocationsActions.addLocationRequest({
|
||||
loader: 'location-request',
|
||||
slug: getAPIslugOfLocationType(childLocationType),
|
||||
method: 'post',
|
||||
data: formData,
|
||||
parent: {
|
||||
id: data.id,
|
||||
type: data.location
|
||||
}
|
||||
})
|
||||
);
|
||||
setAddFormOpen(false);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={addFormOpen}
|
||||
onClose={() => {
|
||||
setAddFormOpen(false);
|
||||
}}
|
||||
>
|
||||
<DialogTitle>Add new {childLocationType} details</DialogTitle>
|
||||
<DialogContent>
|
||||
{/* <DialogContentText>Some more text if needed</DialogContentText> */}
|
||||
{fields &&
|
||||
fields.map((fieldName) => (
|
||||
<TextField
|
||||
autoFocus
|
||||
fullWidth
|
||||
key={fieldName}
|
||||
margin="dense"
|
||||
label={toTitleCase(fieldName)}
|
||||
type={fieldName === 'number' ? 'number' : 'text'}
|
||||
name={fieldName}
|
||||
variant="standard"
|
||||
value={formik.values[fieldName]}
|
||||
error={formik.touched[fieldName] && Boolean(formik.errors[fieldName])}
|
||||
helperText={formik.touched[fieldName] && formik.errors[fieldName]}
|
||||
onChange={formik.handleChange}
|
||||
/>
|
||||
))}
|
||||
{childLocationType === 'sublevel' ? (
|
||||
<>
|
||||
Type:{' '}
|
||||
<Select
|
||||
label="Type"
|
||||
name="type"
|
||||
value={formik.values.type}
|
||||
onChange={formik.handleChange}
|
||||
>
|
||||
<MenuItem value="POSITION">Position</MenuItem>
|
||||
<MenuItem value="BIN">Bin</MenuItem>
|
||||
<MenuItem value="PALLET">Pallet</MenuItem>
|
||||
</Select>
|
||||
Positions:{' '}
|
||||
<Select
|
||||
multiple
|
||||
name="postitions"
|
||||
value={formik.values.positions}
|
||||
input={<OutlinedInput id="select-multiple-chip" label="Positions" />}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={value} />
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: 48 * 4.5 + 8,
|
||||
width: 250
|
||||
}
|
||||
}
|
||||
}}
|
||||
onChange={(event) => {
|
||||
const {
|
||||
target: { value }
|
||||
} = event;
|
||||
formik.setFieldValue(
|
||||
'positions',
|
||||
// On autofill we get a stringified value.
|
||||
typeof value === 'string' ? value.split(',') : value
|
||||
);
|
||||
}}
|
||||
>
|
||||
{['LDB', 'LDF', 'LUB', 'LUF', 'RDB', 'RDF', 'RUB', 'RUF'].map((position) => (
|
||||
<MenuItem
|
||||
key={position}
|
||||
value={position}
|
||||
// style={{
|
||||
// fontWeight: theme.typography.fontWeightMedium
|
||||
// }}
|
||||
>
|
||||
{position}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
) : null}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MDButton
|
||||
onClick={() => {
|
||||
setAddFormOpen(false);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</MDButton>
|
||||
<MDButton onClick={formik.handleSubmit}>Save</MDButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
AddForm.propTypes = {
|
||||
addFormOpen: PropTypes.any,
|
||||
setAddFormOpen: PropTypes.any,
|
||||
selected: PropTypes.any,
|
||||
warehouseId: PropTypes.any
|
||||
};
|
||||
|
||||
const WarehouseNestedDetails = () => {
|
||||
const [selected, setSelected] = React.useState(null);
|
||||
const [addFormOpen, setAddFormOpen] = React.useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { warehouseId } = useParams();
|
||||
LOGGER.log('warehouseID', warehouseId);
|
||||
const data = useSelector(WarehouseLocationsSelectors.getChildrenOfParent(warehouseId));
|
||||
|
||||
const populateChildren = (id, type) => {
|
||||
LOGGER.log('populating:', id, type);
|
||||
dispatch(
|
||||
WarehouseLocationsActions.locationRequest({
|
||||
loader: 'location-request',
|
||||
slug: API.GET_CHILDREN_FROM_PARENT,
|
||||
method: 'post',
|
||||
data: { id, type }
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
populateChildren(warehouseId, 'warehouse');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DashboardLayout>
|
||||
<DashboardNavbar />
|
||||
<Box px={3} py={3}>
|
||||
{data &&
|
||||
data.map((data) => (
|
||||
<NestedDataTable
|
||||
key={data.id}
|
||||
data={data}
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
populateChildren={populateChildren}
|
||||
/>
|
||||
))}
|
||||
{/* Debugging */}
|
||||
{/* <pre>{JSON.stringify(selected, null, 4)}</pre> */}
|
||||
{/* Bottom buttons */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
columnGap: '20px',
|
||||
margin: '20px'
|
||||
}}
|
||||
>
|
||||
<MDButton
|
||||
size="medium"
|
||||
sx={bottomButtonStyling}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setAddFormOpen('zone');
|
||||
}}
|
||||
>
|
||||
Add zone
|
||||
</MDButton>
|
||||
<MDButton
|
||||
size="medium"
|
||||
sx={bottomButtonStyling}
|
||||
disabled={selected?.location !== 'zone'}
|
||||
color={selected?.location === 'zone' ? 'primary' : 'secondary'}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setAddFormOpen(true);
|
||||
}}
|
||||
>
|
||||
Add area
|
||||
</MDButton>
|
||||
<MDButton
|
||||
size="medium"
|
||||
sx={bottomButtonStyling}
|
||||
disabled={selected?.location !== 'area'}
|
||||
color={selected?.location === 'area' ? 'primary' : 'secondary'}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setAddFormOpen(true);
|
||||
}}
|
||||
>
|
||||
Add row
|
||||
</MDButton>
|
||||
<MDButton
|
||||
size="medium"
|
||||
sx={bottomButtonStyling}
|
||||
disabled={selected?.location !== 'row'}
|
||||
color={selected?.location === 'row' ? 'primary' : 'secondary'}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setAddFormOpen(true);
|
||||
}}
|
||||
>
|
||||
Add bay
|
||||
</MDButton>
|
||||
<MDButton
|
||||
size="medium"
|
||||
sx={bottomButtonStyling}
|
||||
disabled={selected?.location !== 'bay'}
|
||||
color={selected?.location === 'bay' ? 'primary' : 'secondary'}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setAddFormOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Level
|
||||
</MDButton>
|
||||
<MDButton
|
||||
size="medium"
|
||||
sx={bottomButtonStyling}
|
||||
disabled={!['level', 'sublevel'].includes(selected?.location)}
|
||||
color={['level', 'sublevel'].includes(selected?.location) ? 'primary' : 'secondary'}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setAddFormOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Sublevel
|
||||
</MDButton>
|
||||
</Box>
|
||||
</Box>
|
||||
{addFormOpen && (
|
||||
<AddForm
|
||||
addFormOpen={addFormOpen}
|
||||
setAddFormOpen={setAddFormOpen}
|
||||
selected={selected}
|
||||
warehouseId={warehouseId}
|
||||
/>
|
||||
)}
|
||||
</DashboardLayout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default WarehouseNestedDetails;
|
||||
@@ -93,8 +93,8 @@ export const onUpdateInventorySuccess = (state, { data }) =>
|
||||
fetching: getFetchingValue(state.fetching, data?.loader),
|
||||
error: getErrorValue(state?.error, data?.loader),
|
||||
getInventoryDetail: [
|
||||
...state.getInventoryDetail.filter((x) => x._id !== data.newInventory._id),
|
||||
data.newInventory
|
||||
...state.getInventoryDetail.filter((x) => x._id !== data.updateInventory?.inventory?._id),
|
||||
data.updateInventory?.inventory
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
100
src/redux/ItemRedux.js
Normal file
100
src/redux/ItemRedux.js
Normal file
@@ -0,0 +1,100 @@
|
||||
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({
|
||||
itemRequest: ['payload'],
|
||||
itemSuccess: ['data'],
|
||||
editItemRequest: ['payload'],
|
||||
editItemSuccess: ['data'],
|
||||
itemFailure: ['error'],
|
||||
logout: null
|
||||
});
|
||||
|
||||
export const ItemTypes = Types;
|
||||
const ItemActions = Creators;
|
||||
export default ItemActions;
|
||||
|
||||
/* ------------- Initial State ------------- */
|
||||
export const INITIAL_STATE = Immutable({
|
||||
list: [],
|
||||
fetching: [],
|
||||
error: {}
|
||||
});
|
||||
|
||||
/* ------------- 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)
|
||||
};
|
||||
|
||||
/* ------------- Reducers ------------- */
|
||||
export const onItemRequest = (state, { payload }) =>
|
||||
state.merge({
|
||||
fetching: _.uniq([...state.fetching, payload?.loader]),
|
||||
error: getErrorValue(state?.error, payload?.loader)
|
||||
});
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
export const onItemFailure = (state, { error }) =>
|
||||
state.merge({
|
||||
fetching: _.without(state.fetching, error?.loader),
|
||||
error: { ...state.error, [error?.loader]: error?.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.EDIT_ITEM_SUCCESS]: onEditItemSuccess,
|
||||
[Types.ITEM_FAILURE]: onItemFailure
|
||||
});
|
||||
@@ -39,6 +39,8 @@ export const INITIAL_STATE = Immutable({
|
||||
/* ------------- Selectors ------------- */
|
||||
export const WarehouseSelectors = {
|
||||
getWarehouseDetail: (state) => state.warehouse.warehouseDetail,
|
||||
getWarehouseDetailById: (id) => (state) =>
|
||||
state.warehouse.warehouseDetail.find((x) => x._id === id),
|
||||
createWarehouseDetail: (state) => state.warehouse.createWarehouse,
|
||||
editWarehouseDetail: (state) => state.warehouse.editWarehouse
|
||||
};
|
||||
|
||||
@@ -65,14 +65,16 @@ export const onWidgetSuccess = (state, { data }) =>
|
||||
list: mergeWidgetStates(state.list, data.widgets)
|
||||
});
|
||||
|
||||
const mergeEditWidgetStates = (stateList, widget, type) => {
|
||||
if (!widget) return stateList; // undefined check
|
||||
const mergeEditWidgetStates = (stateList, widget, type, deletedId) => {
|
||||
if (!(widget || deletedId)) 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 if (type === 'delete') {
|
||||
return stateList.filter((x) => x._id !== deletedId);
|
||||
} else {
|
||||
return stateList;
|
||||
}
|
||||
@@ -82,7 +84,7 @@ 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)
|
||||
list: mergeEditWidgetStates(state.list, data.widget, data.type, data.deletedId)
|
||||
});
|
||||
|
||||
export const onWidgetFailure = (state, { error }) =>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { inventoryReducer } from './InventoryRedux';
|
||||
import { rolesReducer } from './RolesRedux';
|
||||
import { WarehouseLocationsReducer } from './WarehouseLocationsRedux';
|
||||
import { widgetReducer } from './WidgetRedux';
|
||||
import { itemReducer } from './ItemRedux';
|
||||
|
||||
// Combine all reducers.
|
||||
const appReducer = combineReducers({
|
||||
@@ -17,7 +18,8 @@ const appReducer = combineReducers({
|
||||
warehouseLocations: WarehouseLocationsReducer,
|
||||
product: productReducer,
|
||||
inventory: inventoryReducer,
|
||||
widgets: widgetReducer
|
||||
widgets: widgetReducer,
|
||||
items: itemReducer
|
||||
});
|
||||
|
||||
const rootReducer = (state, action) => {
|
||||
|
||||
@@ -51,7 +51,6 @@ import LocationLabelingScreen from 'pages/labeling';
|
||||
import UserAccessScreen from 'pages/useraccess';
|
||||
import NewWarehouseDetails from 'pages/newWarehouseDetails';
|
||||
import SetupHome from 'pages/setup';
|
||||
import WarehouseDetailsTables from 'pages/warehouseDetailsTables';
|
||||
import EditWarehouseDetails from 'pages/editWarehouseDetails';
|
||||
import LabelingHome from 'pages/labellingHome';
|
||||
import SetupInventory from 'pages/setupInventory';
|
||||
@@ -60,6 +59,7 @@ import SetupIcon from 'assets/images/SetupIcon';
|
||||
import AddNewItem from '../pages/addNewProduct';
|
||||
import CreateUserRole from 'pages/createUserRole';
|
||||
import WidgetLabel from 'pages/widgetLabel';
|
||||
import ItemListing from 'pages/itemListing';
|
||||
|
||||
// Images
|
||||
// import profilePicture from 'assets/images/team-3.jpg';
|
||||
@@ -158,6 +158,13 @@ const protectedRoutes = [
|
||||
route: '/setup/inventory/new-item/:widgetName/:inventoryId',
|
||||
component: <AddNewItem />
|
||||
},
|
||||
{
|
||||
name: 'View Items',
|
||||
key: 'view-items',
|
||||
hide: true,
|
||||
route: '/setup/inventory/browse/:widgetName/:inventoryId',
|
||||
component: <ItemListing />
|
||||
},
|
||||
{
|
||||
name: 'Location Labeling',
|
||||
key: 'location-labeling',
|
||||
@@ -177,13 +184,6 @@ const protectedRoutes = [
|
||||
route: '/setup/labeling',
|
||||
component: <LabelingHome />
|
||||
},
|
||||
{
|
||||
name: 'Warehouse Details',
|
||||
key: 'warehouse-details',
|
||||
hide: true,
|
||||
route: '/setup/warehouse/warehouse-details/:warehouseId',
|
||||
component: <WarehouseDetailsTables />
|
||||
},
|
||||
{
|
||||
name: 'Add Warehouse',
|
||||
key: 'add-warehouse',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AuthorizedAPI } from 'config';
|
||||
import { takeLatest, call, put, takeEvery } from 'redux-saga/effects';
|
||||
import { toast } from 'react-toastify';
|
||||
import { call, put, takeEvery } from 'redux-saga/effects';
|
||||
import InventoryActions from 'redux/InventoryRedux';
|
||||
import { InventoryTypes } from 'redux/InventoryRedux';
|
||||
import ApiServices from 'services/API/ApiServices';
|
||||
@@ -101,10 +102,11 @@ export function* onRequestUpdateInventoryData({ payload }) {
|
||||
payload?.data
|
||||
);
|
||||
if (response?.status === 200) {
|
||||
toast('Updated inventory successfully');
|
||||
yield put(
|
||||
InventoryActions.updateInventorySuccess({
|
||||
loader: payload?.loader,
|
||||
updateInventoryDetail: response?.data?.data
|
||||
updateInventory: response?.data?.data
|
||||
})
|
||||
);
|
||||
} else {
|
||||
@@ -118,8 +120,8 @@ export function* onRequestUpdateInventoryData({ payload }) {
|
||||
}
|
||||
}
|
||||
export default [
|
||||
takeLatest(InventoryTypes.GET_INVENTORY_ACTION, onRequestGetInventoryData),
|
||||
takeLatest(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData),
|
||||
takeLatest(InventoryTypes.UPDATE_INVENTORY_ACTION, onRequestUpdateInventoryData),
|
||||
takeEvery(InventoryTypes.GET_INVENTORY_ACTION, onRequestGetInventoryData),
|
||||
takeEvery(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData),
|
||||
takeEvery(InventoryTypes.UPDATE_INVENTORY_ACTION, onRequestUpdateInventoryData),
|
||||
takeEvery(InventoryTypes.GET_INVENTORY_TYPES_ACTION, onRequestGetInventoryTypesData)
|
||||
];
|
||||
|
||||
60
src/sagas/Item.js
Normal file
60
src/sagas/Item.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import { AuthorizedAPI } from 'config';
|
||||
import { call, put, takeEvery } from 'redux-saga/effects';
|
||||
import ApiServices from 'services/API/ApiServices';
|
||||
import ItemActions, { ItemTypes } from '../redux/ItemRedux';
|
||||
|
||||
export function* onRequestItem({ payload }) {
|
||||
const response = yield call(
|
||||
ApiServices[payload?.method],
|
||||
AuthorizedAPI,
|
||||
`${payload?.slug}${payload?.inventoryId}&page=${payload?.page}&perPage=${payload?.perPage}`,
|
||||
payload?.data
|
||||
);
|
||||
if (response?.status === 200) {
|
||||
yield put(
|
||||
ItemActions.itemSuccess({
|
||||
loader: payload?.loader,
|
||||
items: response?.data?.data,
|
||||
page: payload?.page,
|
||||
reset: !payload.page
|
||||
})
|
||||
);
|
||||
} else {
|
||||
yield put(
|
||||
ItemActions.itemFailure({
|
||||
loader: payload?.loader,
|
||||
error: response?.message
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function* onEditRequestItem({ payload }) {
|
||||
const response = yield call(
|
||||
ApiServices[payload?.method],
|
||||
AuthorizedAPI,
|
||||
payload?.slug,
|
||||
payload?.data
|
||||
);
|
||||
if (response?.status === 200) {
|
||||
yield put(
|
||||
ItemActions.editItemSuccess({
|
||||
loader: payload?.loader,
|
||||
item: response?.data?.data,
|
||||
type: payload?.type
|
||||
})
|
||||
);
|
||||
} else {
|
||||
yield put(
|
||||
ItemActions.itemFailure({
|
||||
loader: payload?.loader,
|
||||
error: response?.message
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default [
|
||||
takeEvery(ItemTypes.ITEM_REQUEST, onRequestItem),
|
||||
takeEvery(ItemTypes.EDIT_ITEM_REQUEST, onEditRequestItem)
|
||||
];
|
||||
@@ -39,7 +39,8 @@ export function* onEditRequestWidget({ payload }) {
|
||||
WidgetActions.editWidgetSuccess({
|
||||
loader: payload?.loader,
|
||||
widget: response?.data?.data,
|
||||
type: payload?.type
|
||||
type: payload?.type,
|
||||
deletedId: payload?.deletedId
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -7,6 +7,7 @@ import InventorySaga from './Inventory';
|
||||
import RolesSaga from './Roles';
|
||||
import WarehouseLocationsSaga from './WarehouseLocations';
|
||||
import WidgetSaga from './Widget';
|
||||
import ItemSaga from './Item';
|
||||
|
||||
export default function* rootSaga() {
|
||||
yield all([...AuthSaga]);
|
||||
@@ -17,4 +18,5 @@ export default function* rootSaga() {
|
||||
yield all([...RolesSaga]);
|
||||
yield all([...WarehouseLocationsSaga]);
|
||||
yield all([...WidgetSaga]);
|
||||
yield all([...ItemSaga]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user