From e794421938d017eda71a0683dbbce9c47b206e89 Mon Sep 17 00:00:00 2001 From: bluestreamlds <85561356+bluestreamlds@users.noreply.github.com> Date: Wed, 2 Mar 2022 23:09:36 +0530 Subject: [PATCH] Fixes/items-and-images (#73) * Fixed: warehouse preferred inventories upload * Fix: item redux saga * Item listing * Added: add item * Added: edit handler * Update: add item * Update: multi images * Fix: multiple images * Fix: item api mapping * Update: added keys Co-authored-by: Llewellyn Dsouza --- src/components/Breadcrumbs/index.js | 1 + src/components/EnhancedTable/index.js | 68 ++++-- src/components/ImageUploadMultiple/index.js | 128 +++++++++++ src/components/TablePagination/index.js | 28 ++- src/constant/Endpoints.js | 3 +- src/pages/addNewProduct/index.js | 238 ++++++++++++++------ src/pages/editWarehouseDetails/index.js | 70 +++--- src/pages/itemListing/index.js | 68 ++++-- src/pages/newWarehouseDetails/index.js | 71 ++++-- src/redux/ItemRedux.js | 91 ++++---- src/routes/index.js | 8 + src/sagas/Item.js | 109 ++++++++- src/sagas/Warehouse.js | 18 +- src/services/ValidationServices.js | 5 +- 14 files changed, 688 insertions(+), 218 deletions(-) create mode 100644 src/components/ImageUploadMultiple/index.js diff --git a/src/components/Breadcrumbs/index.js b/src/components/Breadcrumbs/index.js index a424bc3..8975b24 100644 --- a/src/components/Breadcrumbs/index.js +++ b/src/components/Breadcrumbs/index.js @@ -44,6 +44,7 @@ const buildBreadcrumbs = (route, light) => { fontWeight="regular" textTransform="capitalize" color={light ? 'white' : 'dark'} + key={el} sx={{ lineHeight: 0 }} > {el.name} diff --git a/src/components/EnhancedTable/index.js b/src/components/EnhancedTable/index.js index 8842e2a..ccebae8 100644 --- a/src/components/EnhancedTable/index.js +++ b/src/components/EnhancedTable/index.js @@ -1,12 +1,12 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import PropTypes from 'prop-types'; -import SearchBar from 'components/SearchBar'; +// 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 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'; @@ -19,6 +19,7 @@ import Paper from '@mui/material/Paper'; // import IconButton from '@mui/material/IconButton'; // import Collapse from '@mui/material/Collapse'; import TablePagination from 'components/TablePagination'; +import { MenuItem, Select } from '@mui/material'; const StyledTableCell = styled(TableCell)(({ theme }) => ({ [`&.${tableCellClasses.head}`]: { @@ -47,16 +48,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 ( *': { borderBottom: 'unset' } }}> { + editHandler(rowData._id); + }} > EDIT @@ -82,7 +86,7 @@ function Row({ tHeads, rowData }) { ); } -function EnhancedTable({ data, tHeads }) { +function EnhancedTable({ count, page, setPage, perPage, setPerPage, data, tHeads, editHandler }) { // const [anchorEl, setAnchorEl] = React.useState(false); // const open = Boolean(anchorEl); // const handleClick = (event) => { @@ -101,7 +105,7 @@ function EnhancedTable({ data, tHeads }) { overflow: 'hidden' }} > - Logout - + */} {/* Table-row- */} @@ -174,7 +178,14 @@ function EnhancedTable({ data, tHeads }) { {data && - data.map((rowData) => )} + data.map((rowData) => ( + + ))}
@@ -265,10 +276,29 @@ function EnhancedTable({ data, tHeads }) { */} {/*---- pagination- */} - - + + Per page:{' '} + + + + + + + [{(page - 1) * perPage + 1} to {perPage * page > count ? count : perPage * page} of{' '} + {count}] - [1 to 10 of 92] @@ -276,8 +306,14 @@ 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 }; export default EnhancedTable; diff --git a/src/components/ImageUploadMultiple/index.js b/src/components/ImageUploadMultiple/index.js new file mode 100644 index 0000000..ee4a82f --- /dev/null +++ b/src/components/ImageUploadMultiple/index.js @@ -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 ( + <> + + + + + {!multiple && images.length ? null : } + + {!multiple && images.length ? 'Cannot add more images' : heading} + + + + {/* -----------img-preview----------- */} + + {images && + images.map((item, idx) => { + return ( + + placeholder + + + ); + })} + + + + ); +} + +export default ImageUpload; +ImageUpload.propTypes = { + images: PropTypes.array, + heading: PropTypes.string, + multiple: PropTypes.bool, + accept: PropTypes.string, + setImages: PropTypes.func +}; diff --git a/src/components/TablePagination/index.js b/src/components/TablePagination/index.js index 9981eaf..8669415 100644 --- a/src/components/TablePagination/index.js +++ b/src/components/TablePagination/index.js @@ -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 ( - ( )} + onChange={(event, value) => { + setPage(value); + }} /> - ); } +TablePagination.propTypes = { + page: PropTypes.number, + count: PropTypes.number, + setPage: PropTypes.any +}; + export default TablePagination; diff --git a/src/constant/Endpoints.js b/src/constant/Endpoints.js index b6ec053..d8fc8a9 100644 --- a/src/constant/Endpoints.js +++ b/src/constant/Endpoints.js @@ -11,7 +11,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', diff --git a/src/pages/addNewProduct/index.js b/src/pages/addNewProduct/index.js index 3256b78..34991d1 100644 --- a/src/pages/addNewProduct/index.js +++ b/src/pages/addNewProduct/index.js @@ -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() { - { - formik.setFieldValue('images', images); - }} - /> + {itemId ? ( + { + formik.setFieldValue('images', images); + }} + /> + ) : ( + { + formik.setFieldValue('images', images); + }} + /> + )} { ); }; -const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet']; - function EditWarehouseDetails() { const { warehouseId } = useParams(); const warehouseData = useSelector(WarehouseSelectors.getWarehouseDetailById(warehouseId)); + 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 = { @@ -334,12 +346,12 @@ function EditWarehouseDetails() { const formik = useFormik({ initialValues: { warehousename: warehouseData.name, - address: warehouseData.address, - inventorytype: [], - specs: warehouseData.specs, - image: [{ src: warehouseData.image_url }] + address: warehouseData.address || '', + preferredInventories: warehouseData.preferredInventories || [], + specs: warehouseData.specs || '', + image: warehouseData.image_url ? [{ src: warehouseData.image_url }] : [] }, - validationSchema: schema.warehouseForm, + // validationSchema: schema.warehouseForm, onSubmit: (values) => { dispatch( WarehouseActions.editWarehouseAction({ @@ -350,6 +362,7 @@ function EditWarehouseDetails() { name: values.warehousename, address: values.address, specs: values.specs, + preferredInventories: values.preferredInventories, image: values.image } }) @@ -446,40 +459,45 @@ function EditWarehouseDetails() { diff --git a/src/pages/itemListing/index.js b/src/pages/itemListing/index.js index 790ed49..db7002a 100644 --- a/src/pages/itemListing/index.js +++ b/src/pages/itemListing/index.js @@ -10,41 +10,54 @@ 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'; 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: 'unitOfMaterial' }, + { key: 'unitCost', name: 'unitCost' }, + { key: 'packageCount', name: 'packageCount' }, + { key: 'countPerPallet', name: 'countPerPallet' }, + { key: 'countPerPalletPackage', name: 'countPerPalletPackage' }, + { key: 'totalQuantity', name: 'totalQuantity' }, + { key: 'reservedQuantity', name: 'reservedQuantity' }, + { key: 'availableQuantity', name: 'availableQuantity' } ]; 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); + + React.useEffect(() => { + dispatch( + ItemActions.itemRequest({ + loader: 'loading-request', + slug: API.GET_ITEMS_BY_INVENTORY, + method: 'get', + page: page - 1, + perPage, + inventoryId + }) + ); + }, [page, perPage]); return ( @@ -60,7 +73,18 @@ function ItemListing() { List of {widgetName}s{/*
{JSON.stringify(data, null, 4)}
*/} - + { + navigateTo(`/setup/inventory/browse/${widgetName}/${inventoryId}/edit/${id}`); + }} + data={data} + tHeads={tHeads} + />
); diff --git a/src/pages/newWarehouseDetails/index.js b/src/pages/newWarehouseDetails/index.js index 53aa9fd..4692061 100644 --- a/src/pages/newWarehouseDetails/index.js +++ b/src/pages/newWarehouseDetails/index.js @@ -1,23 +1,35 @@ import React from 'react'; -import { useDispatch } from 'react-redux'; +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 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 Breadcrumbs from 'components/Breadcrumbs'; import { useNavigate } from 'react-router-dom'; - -const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet']; +import { InventorySelectors } from 'redux/InventoryRedux'; +import InventoryActions from 'redux/InventoryRedux'; function NewWarehouseDetails() { const dispatch = useDispatch(); + 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) => { @@ -38,11 +50,11 @@ function NewWarehouseDetails() { initialValues: { warehousename: '', address: '', - inventorytype: [], + preferredInventories: [], specs: '', image: [] }, - validationSchema: schema.warehouseForm, + // validationSchema: schema.warehouseForm, onSubmit: (values) => { dispatch( WarehouseActions.createWarehouseAction({ @@ -53,6 +65,7 @@ function NewWarehouseDetails() { data: { name: values.warehousename, address: values.address, + preferredInventories: values.preferredInventories, specs: values.specs, image: values.image, company_id: '61cea5fd028432700a7f8601' @@ -153,25 +166,31 @@ function NewWarehouseDetails() { select fullWidth variant="outlined" - name="inventorytype" + name="preferredInventories" input={} - 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) => ( {selected.map((value) => ( - + x._id === value)?.name || 'unknown'} + /> ))} )} 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 ); @@ -180,11 +199,12 @@ function NewWarehouseDetails() { None Selected - {inventoryTypes.map((name) => ( - - {name} - - ))} + {inventoryTypes && + inventoryTypes.map((inventory) => ( + + {inventory.name} + + ))}
@@ -233,7 +253,14 @@ function NewWarehouseDetails() { columnGap: '20px' }} > - + { + navigate('/setup/warehouse'); + }} + > CANCEL diff --git a/src/redux/ItemRedux.js b/src/redux/ItemRedux.js index 9d52a6a..f7fe178 100644 --- a/src/redux/ItemRedux.js +++ b/src/redux/ItemRedux.js @@ -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 }); diff --git a/src/routes/index.js b/src/routes/index.js index 5d51908..0bc4c09 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -158,6 +158,14 @@ const protectedRoutes = [ route: '/setup/inventory/new-item/:widgetName/:inventoryId', component: }, + { + name: 'Update Item', + key: 'udpate-item', + hide: true, + route: '/setup/inventory/browse/:widgetName/:inventoryId/edit/:itemId', + component: + }, + // /setup/inventory/browse/${payload?.widgetName}/${payload?.inventoryId}/edit/${payload?.id} { name: 'View Items', key: 'view-items', diff --git a/src/sagas/Item.js b/src/sagas/Item.js index e09426b..cb84a11 100644 --- a/src/sagas/Item.js +++ b/src/sagas/Item.js @@ -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'; @@ -14,7 +15,8 @@ export function* onRequestItem({ payload }) { 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 +31,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 +58,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) ]; diff --git a/src/sagas/Warehouse.js b/src/sagas/Warehouse.js index 2f24657..b01484c 100644 --- a/src/sagas/Warehouse.js +++ b/src/sagas/Warehouse.js @@ -35,6 +35,10 @@ const makeFormData = (data) => { 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; }; @@ -47,11 +51,15 @@ export function* onRequestCreateWarehouse({ payload }) { makeFormData(payload?.data) ); if (response?.status === 200 && response?.data?.message) { + const warehouse = response?.data?.message; toast('Warehouse created successfully'); yield put( WarehouseActions.createWarehouseSuccess({ loader: payload?.loader, - createdWarehouse: response?.data?.message + createdWarehouse: { + ...warehouse, + preferredInventories: warehouse.preferredInventories.map((z) => z._id) + } }) ); payload.navigateTo(response?.data?.message?._id); @@ -73,12 +81,16 @@ export function* onRequestEditWarehouse({ payload }) { payload?.slug, 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, - editedWarehouse: response?.data?.data + editedWarehouse: { + ...warehouse, + preferredInventories: warehouse.preferredInventories.map((z) => z._id) + } }) ); } else { diff --git a/src/services/ValidationServices.js b/src/services/ValidationServices.js index 5f1832f..e1d8d70 100644 --- a/src/services/ValidationServices.js +++ b/src/services/ValidationServices.js @@ -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({