Feature/wms 56 (#59)

* create warehouse
* edit warehouse changes
* Update: linted and formatted
* add warehouse button
* user access changes
* basic table component changes
* Updated: eslint errors
* basic table component changes
* update: linted
* add New Product form api integrate
* add Inventory changes
* policies change
* add validation for widget
* Fixed: Image upload component
* Updated: Custom styling inputs

Co-authored-by: [Diksha] <[diksha39511@gmail.com]>
Co-authored-by: Llewellyn Dsouza <lledsouza2209@gmail.com>
This commit is contained in:
Dikshajain39511
2022-02-22 13:48:26 +05:30
committed by GitHub
parent 68dca0dc34
commit 302057a0a7
9 changed files with 331 additions and 133 deletions

View File

@@ -4,5 +4,6 @@ export default {
CREATE_WAREHOUSE: '/warehouse/', CREATE_WAREHOUSE: '/warehouse/',
GET_USERS_DATA: '/user/all?page=0&perPage=10', GET_USERS_DATA: '/user/all?page=0&perPage=10',
ADD_PRODUCT: '/item/', ADD_PRODUCT: '/item/',
ADD_INVENTORY: '/inventory',
GET_ROLES_DATA: '/user-role/all?page=0&perPage=10' GET_ROLES_DATA: '/user-role/all?page=0&perPage=10'
}; };

View File

@@ -1,17 +1,28 @@
import DashboardNavbar from 'components/DashboardNavbar'; import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout'; import DashboardLayout from 'layouts/DashboardLayout';
import { Grid, InputLabel, TableBody, TableCell, TableRow } from '@mui/material'; import { Box, Grid, MenuItem, Select, TableBody, TableCell, TableRow } from '@mui/material';
import MDInput from 'components/MDInput'; import MDInput from 'components/MDInput';
import ImageUpload from 'components/ImageUpload'; import ImageUpload from 'components/ImageUpload';
import Switch from 'components/Switch'; import Switch from 'components/Switch';
import MDTypography from 'components/MDTypography'; import MDTypography from 'components/MDTypography';
import { makeStyles } from '@mui/styles';
import MDBox from 'components/MDBox'; import MDBox from 'components/MDBox';
import Dropdown from 'components/Dropdown'; import Dropdown from 'components/Dropdown';
import MDButton from 'components/Button'; import MDButton from 'components/Button';
import BasicTable from 'components/BasicTable'; import BasicTable from 'components/BasicTable';
import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
import { API } from 'constant';
import { useDispatch } from 'react-redux';
import InventoryActions from 'redux/InventoryRedux';
import LOGGER from 'services/Logger';
const useStyles = makeStyles({ const customStyles = {
labelSize: {
fontSize: '16px',
letterSpacing: '0.01em',
color: '#000',
marginBottom: '4px'
},
textWrap: { textWrap: {
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
fontSize: '16px', fontSize: '16px',
@@ -35,7 +46,7 @@ const useStyles = makeStyles({
marginTop: { marginTop: {
marginTop: '54px' marginTop: '54px'
} }
}); };
const stockBox = [ const stockBox = [
{ {
@@ -95,12 +106,8 @@ const dropdownData = [
} }
]; ];
const dataInventory = [ const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet'];
{
placeholder: 'Lorem Ipsum',
label: 'Inventory Type'
}
];
const dataLevel = [ const dataLevel = [
{ {
placeholder: 'Lorem Ipsum', placeholder: 'Lorem Ipsum',
@@ -113,12 +120,46 @@ const dataLevel = [
]; ];
function InventoryScreen() { function InventoryScreen() {
const previewImg = [1, 2, 3]; const dispatch = useDispatch();
const classes = useStyles();
const formik = useFormik({
initialValues: {
inventoryname: '',
inventorytype: '',
widgetname: '',
policies: false,
images: []
},
validationSchema: schema.addInventory,
onSubmit: (values, onSubmitProps) => {
LOGGER.log('values', values);
dispatch(
InventoryActions.addInventoryAction({
loader: 'loading-request',
slug: API.ADD_INVENTORY,
method: 'post',
data: {
name: values.inventoryname,
type: values.inventorytype,
policies: {
alerting: {
lowestStockLevel: true,
highestStockLevel: true,
alertStockLevel: true,
reOrderLevel: true
}
}
}
})
);
onSubmitProps.resetForm();
}
});
return ( return (
<DashboardLayout> <DashboardLayout>
<DashboardNavbar /> <DashboardNavbar />
<MDBox px={5} py={5}> <MDBox px={5} py={5}>
<form onSubmit={formik.handleSubmit}>
<MDBox <MDBox
px={5} px={5}
py={5} py={5}
@@ -131,29 +172,64 @@ function InventoryScreen() {
> >
<Grid container spacing={5}> <Grid container spacing={5}>
<Grid item xs={12} sm={6} md={6}> <Grid item xs={12} sm={6} md={6}>
<Grid container spacing={2}> <Box component="div" sx={{ marginBottom: '20px' }}>
<Grid item xs={12} sm={12} md={12}> <Box component="div" sx={customStyles.labelSize}>
<InputLabel sx={{ pb: 2 }} id="demo-simple-select-label">
Inventory Name Inventory Name
</InputLabel> </Box>
<MDInput sx={{ width: '100%' }} /> <MDInput
</Grid> fullWidth
{dataInventory && name="inventoryname"
dataInventory.map((item, index) => ( type="text"
<Grid item xs={12} sm={12} md={12} key={index}> variant="outlined"
<Dropdown items={item} dropdownData={dropdownData} /> value={formik.values.inventoryname}
</Grid> error={formik.touched.inventoryname && Boolean(formik.errors.inventoryname)}
helpertText={formik.touched.inventoryname && formik.errors.inventoryname}
onChange={formik.handleChange}
/>
</Box>
<Box component="div" sx={{ marginBottom: '20px' }}>
<Box component="div" sx={customStyles.labelSize}>
Inventory Type
</Box>
<Select
select
fullWidth
variant="outlined"
name="inventorytype"
value={formik.values.inventorytype}
error={formik.touched.inventorytype && Boolean(formik.errors.inventorytype)}
helperText={formik.touched.inventorytype && formik.errors.inventorytype}
onChange={formik.handleChange}
>
<MenuItem key={''} value={''}>
None Selected
</MenuItem>
{inventoryTypes.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))} ))}
</Select>
</Box>
<Grid item xs={12} sm={12} md={12}> <Grid item xs={12} sm={12} md={12}>
<InputLabel sx={{ pb: 2 }} id="demo-simple-select-label"> <Box component="div" sx={customStyles.labelSize}>
Widget Name Widget Name
</InputLabel> </Box>
<MDInput sx={{ width: '100%' }} /> <MDInput
</Grid> fullWidth
name="widgetname"
type="text"
variant="outlined"
value={formik.values.widgetname}
error={formik.touched.widgetname && Boolean(formik.errors.widgetname)}
helpertext={formik.touched.widgetname && formik.errors.widgetname}
onChange={formik.handleChange}
/>
</Grid> </Grid>
<MDBox sx={{ my: 4 }}> <MDBox sx={{ my: 4 }}>
<MDTypography variant="h5">Policies</MDTypography> <MDTypography variant="h5">Policies</MDTypography>
<MDTypography className={classes.textSize}> <MDTypography sx={customStyles.textSize}>
Egestas pulvinar ornare vulputate porttitor consectetur condimentum at tellus Egestas pulvinar ornare vulputate porttitor consectetur condimentum at tellus
quis. Leo pellentesque ipsum, a purus dignissim aliquam, orci. Elementum quis. Leo pellentesque ipsum, a purus dignissim aliquam, orci. Elementum
ullamcorper a sit eleifend ante ullamcorper ornare mi pharetra. ullamcorper a sit eleifend ante ullamcorper ornare mi pharetra.
@@ -170,12 +246,16 @@ function InventoryScreen() {
my: 5 my: 5
}} }}
> >
<div className={classes.wrap}> <div sx={customStyles.wrap}>
{stockBox.map((item) => ( {stockBox.map((item) => (
<> <>
<div className={classes.gridWrap}> <div sx={customStyles.gridWrap}>
<MDTypography className={classes.textWrap}>{item.text}</MDTypography> <MDTypography sx={customStyles.textWrap}>{item.text}</MDTypography>
<Switch /> <Switch
name="policies"
checked={formik.values.policies}
onChange={formik.handleChange}
/>
</div> </div>
</> </>
))} ))}
@@ -183,7 +263,15 @@ function InventoryScreen() {
</MDBox> </MDBox>
</Grid> </Grid>
<Grid item xs={12} sm={6} md={6}> <Grid item xs={12} sm={6} md={6}>
<ImageUpload heading="Upload Inventory Images" previewImg={previewImg} /> <ImageUpload
multiple
heading="Upload Inventory Images"
accept="image/*"
images={formik.values.images}
setImages={(images) => {
formik.setFieldValue('images', images);
}}
/>
</Grid> </Grid>
<Grid item xs={12} sm={12} md={12}> <Grid item xs={12} sm={12} md={12}>
<Grid container spacing={1}> <Grid container spacing={1}>
@@ -194,7 +282,7 @@ function InventoryScreen() {
</Grid> </Grid>
))} ))}
<Grid item xs={12} sm={6} md={4}> <Grid item xs={12} sm={6} md={4}>
<MDButton color="primary" circular="true" className={classes.marginTop}> <MDButton color="primary" circular="true" sx={customStyles.marginTop}>
{'add hierarchy level'} {'add hierarchy level'}
</MDButton> </MDButton>
</Grid> </Grid>
@@ -239,7 +327,7 @@ function InventoryScreen() {
<MDButton sx={{ ml: 3 }} color="error" variant="outlined"> <MDButton sx={{ ml: 3 }} color="error" variant="outlined">
{'CANCEL'} {'CANCEL'}
</MDButton> </MDButton>
<MDButton sx={{ ml: 3 }} color="primary" variant="outlined"> <MDButton sx={{ ml: 3 }} color="primary" variant="outlined" type="submit">
{'SAVE'} {'SAVE'}
</MDButton> </MDButton>
<MDButton sx={{ ml: 3 }} color="primary"> <MDButton sx={{ ml: 3 }} color="primary">
@@ -248,6 +336,7 @@ function InventoryScreen() {
</MDBox> </MDBox>
</Grid> </Grid>
</MDBox> </MDBox>
</form>
</MDBox> </MDBox>
</DashboardLayout> </DashboardLayout>
); );

View File

@@ -7,8 +7,13 @@ import FleetIcon from 'assets/images/FleetIcon';
import RawMaterialIcon from 'assets/images/RawMaterialIcon'; import RawMaterialIcon from 'assets/images/RawMaterialIcon';
import { Grid } from '@mui/material'; import { Grid } from '@mui/material';
import Tile from 'components/TileComponent'; import Tile from 'components/TileComponent';
import MDButton from 'components/Button';
import { useNavigate } from 'react-router-dom';
function SetupInventory()
{
const navigate = useNavigate();
function SetupInventory() {
const tiles = [ const tiles = [
{ {
name: 'Raw Material', name: 'Raw Material',
@@ -38,7 +43,11 @@ function SetupInventory() {
]; ];
return ( return (
<DashboardLayout> <DashboardLayout>
<DashboardNavbar /> <DashboardNavbar>
<MDButton sx={{ ml: 3 }} color="primary" onClick={() => navigate('/setup/inventory/inventory-new')}>
+ Add new
</MDButton>
</DashboardNavbar>
<MDBox px={2} py={3}> <MDBox px={2} py={3}>
<Grid container spacing={2}> <Grid container spacing={2}>
{tiles && {tiles &&

View File

@@ -158,7 +158,11 @@ function UserAccessScreen() {
<SearchBar /> <SearchBar />
</Grid> </Grid>
<Grid item xs={12} sm={4} md={2}> <Grid item xs={12} sm={4} md={2}>
<MDButton color="primary" size="medium" onClick={() => navigate('/setup/users-access/create-role')}> <MDButton
color="primary"
size="medium"
onClick={() => navigate('/setup/users-access/create-role')}
>
{'+ CREATE USER'} {'+ CREATE USER'}
</MDButton> </MDButton>
</Grid> </Grid>

View File

@@ -0,0 +1,54 @@
import { createActions, createReducer } from 'reduxsauce';
import Immutable from 'seamless-immutable';
import _ from 'underscore';
import { getFetchingValue, getErrorValue } from '../services/Utils';
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
addInventoryAction: ['payload'],
addInventorySuccess: ['data'],
addInventoryFailure: ['error']
});
export const InventoryTypes = Types;
const InventoryActions = Creators;
export default InventoryActions;
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
addInventoryDetail: [],
addInventoryLoading: false,
addInventoryerror: {}
});
/* ------------- Selectors ------------- */
export const InventorySelectors = {
addInventoryDetail: (state) => state.inventory.inventoryDetail
};
/* ------------- Reducers ------------- */
export const onAddInventoryAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
error: getErrorValue(state?.error, payload?.loader)
});
export const onAddInventorySuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
addInventoryDetail: data.addInventoryDetail
});
export const onAddInventoryFailure = (state, { error }) =>
state.merge({
fetching: _.without(state.fetching, error?.loader),
error: { ...state.error, [error?.loader]: error?.error }
});
/* ------------- Hookup Reducers To Types ------------- */
export const inventoryReducer = createReducer(INITIAL_STATE, {
[Types.ADD_INVENTORY_ACTION]: onAddInventoryAction,
[Types.ADD_INVENTORY_SUCCESS]: onAddInventorySuccess,
[Types.ADD_INVENTORY_FAILURE]: onAddInventoryFailure
});

View File

@@ -3,6 +3,7 @@ import { authReducer } from './AuthRedux';
import { warehouseReducer } from './WarehouseRedux'; import { warehouseReducer } from './WarehouseRedux';
import { usersReducer } from './UsersRedux'; import { usersReducer } from './UsersRedux';
import { productReducer } from './ProductsRedux'; import { productReducer } from './ProductsRedux';
import { inventoryReducer } from './InventoryRedux';
import { rolesReducer } from './RolesRedux'; import { rolesReducer } from './RolesRedux';
// Combine all reducers. // Combine all reducers.
@@ -11,6 +12,7 @@ const appReducer = combineReducers({
warehouse: warehouseReducer, warehouse: warehouseReducer,
users: usersReducer, users: usersReducer,
product: productReducer, product: productReducer,
inventory: inventoryReducer,
roles: rolesReducer roles: rolesReducer
}); });

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

@@ -0,0 +1,31 @@
import { AuthorizedAPI } from 'config';
import { takeLatest, call, put } from 'redux-saga/effects';
import InventoryActions from 'redux/InventoryRedux';
import { InventoryTypes } from 'redux/InventoryRedux';
import ApiServices from 'services/API/ApiServices';
export function* onRequestAddInventoryData({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
if (response?.status === 200) {
yield put(
InventoryActions.addInventorySuccess({
loader: payload?.loader,
addInventoryDetail: response?.data?.data
})
);
} else {
payload.onFailedAddInventoryData(response.data.error);
yield put(
InventoryActions.addInventoryFailure({
loader: payload?.loader,
error: response?.data
})
);
}
}
export default [takeLatest(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData)];

View File

@@ -3,6 +3,7 @@ import AuthSaga from './Auth';
import WarehouseSaga from './Warehouse'; import WarehouseSaga from './Warehouse';
import UsersSaga from './Users'; import UsersSaga from './Users';
import ProductSaga from './Product'; import ProductSaga from './Product';
import InventorySaga from './Inventory';
import RolesSaga from './Roles'; import RolesSaga from './Roles';
export default function* rootSaga() { export default function* rootSaga() {
@@ -10,5 +11,6 @@ export default function* rootSaga() {
yield all([...WarehouseSaga]); yield all([...WarehouseSaga]);
yield all([...UsersSaga]); yield all([...UsersSaga]);
yield all([...ProductSaga]); yield all([...ProductSaga]);
yield all([...InventorySaga]);
yield all([...RolesSaga]); yield all([...RolesSaga]);
} }

View File

@@ -49,6 +49,12 @@ const schema = {
under: Yup.number().required('required'), under: Yup.number().required('required'),
over: Yup.number().required('required'), over: Yup.number().required('required'),
alert: Yup.number().required('required') alert: Yup.number().required('required')
}),
addInventory: Yup.object({
inventoryname: Yup.string('Enter Inventory name').required('Inventory name is required'),
inventorytype: Yup.string('Enter inventory Type').required('inventory Type is required'),
widgetname: Yup.string('Enter Widget Name').required('Widget Name is required')
}) })
}; };