diff --git a/src/components/TileComponent/index.js b/src/components/TileComponent/index.js
index 8bc10c8..e484a9f 100644
--- a/src/components/TileComponent/index.js
+++ b/src/components/TileComponent/index.js
@@ -34,24 +34,24 @@ export default function Tile({ data, children }) {
-
+
Update {data.name}
-
+
- Add New {data.name}
+ Add New {data.widgetname}
-
-
+
+
Cycle Count
-
+
- {data.name} List
+ {data.widgetname} List
diff --git a/src/components/TileComponent/styles.js b/src/components/TileComponent/styles.js
index 55d2b6b..e82720c 100644
--- a/src/components/TileComponent/styles.js
+++ b/src/components/TileComponent/styles.js
@@ -83,6 +83,23 @@ const useStyles = makeStyles({
},
remove: {
display: 'none'
+ },
+ boxDisabled: {
+ display: 'flex',
+ color: '#cccccc',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: '13px 0 13px 10px',
+ borderTop: '1px solid lightgray',
+ '& svg': {
+ opacity: '0'
+ },
+ '&:hover': {
+ cursor: 'auto',
+ '& svg': {
+ opacity: '0'
+ }
+ }
}
});
diff --git a/src/components/WidgetNestedDataTable/index.js b/src/components/WidgetNestedDataTable/index.js
new file mode 100644
index 0000000..3eafd98
--- /dev/null
+++ b/src/components/WidgetNestedDataTable/index.js
@@ -0,0 +1,329 @@
+/* eslint-disable indent */
+import {
+ Box,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ Grid,
+ TextField
+} from '@mui/material';
+import React, { useEffect } from 'react';
+import PropTypes from 'prop-types';
+
+import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
+import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
+import IconButton from '@mui/material/IconButton';
+import MDButton from 'components/Button';
+import { useDispatch, useSelector } from 'react-redux';
+import { WidgetSelectors } from 'redux/WidgetRedux';
+import WidgetActions from 'redux/WidgetRedux';
+import { API } from 'constant';
+import { useFormik } from 'formik';
+import LOGGER from 'services/Logger';
+
+function MaterialForm({ formType, setFormOpen, selected, inventoryId }) {
+ const dispatch = useDispatch();
+ const formik = useFormik({
+ initialValues:
+ formType === 'family'
+ ? { name: '', inventoryId }
+ : formType === 'subfamily'
+ ? { name: '', inventoryId, parentId: selected._id || '' }
+ : { name: formType.name, inventoryId, parentId: formType.parent?._id || undefined },
+ onSubmit: (values) => {
+ LOGGER.log('Form values and field info', values);
+ ['family', 'subfamily'].includes(formType)
+ ? dispatch(
+ WidgetActions.editWidgetRequest({
+ loader: 'location-request',
+ slug: API.ADD_WIDGET_FAMILY,
+ method: 'post',
+ data: values,
+ type: 'add'
+ })
+ )
+ : dispatch(
+ WidgetActions.editWidgetRequest({
+ loader: 'location-request',
+ slug: `${API.EDIT_WIDGET_FAMILY}${inventoryId}`,
+ method: 'patch',
+ data: values,
+ type: 'edit'
+ })
+ );
+ setFormOpen(false);
+ }
+ });
+
+ return (
+
+ );
+}
+
+MaterialForm.propTypes = {
+ formType: PropTypes.any,
+ setFormOpen: PropTypes.any,
+ selected: PropTypes.any,
+ inventoryId: PropTypes.any
+};
+
+function WidgetNestedDataTable({
+ data,
+ selected,
+ setSelected,
+ setFormOpen,
+ setFormType,
+ inventoryId
+}) {
+ const [open, setOpen] = React.useState(false);
+ const widgetChildren = useSelector(WidgetSelectors.getWidgetsByParentId(data._id));
+
+ return (
+ <>
+
+
+
+ setOpen(!open)}
+ >
+ {open ? : }
+
+ {
+ setFormType(data);
+ setFormOpen(true);
+ }}
+ >
+ EDIT
+
+
+ {
+ setSelected(data);
+ }}
+ >
+
+ {data.name}
+
+
+ {
+ // dispatch(
+ // WarehouseLocationsActions.deleteLocationRequest({
+ // loader: 'location-request',
+ // slug: API.LOCATION_DELETE,
+ // method: 'post',
+ // data: { type: data.location, id: data.id }
+ // })
+ // );
+ }}
+ >
+ DELETE
+
+
+
+
+ {open && widgetChildren ? (
+
+ {/* Add headers here */}
+ {widgetChildren.map((data) => (
+
+ ))}
+
+ ) : null}
+
+ >
+ );
+}
+
+WidgetNestedDataTable.propTypes = {
+ data: PropTypes.any,
+ selected: PropTypes.any,
+ setSelected: PropTypes.any,
+ setFormOpen: PropTypes.any,
+ setFormType: PropTypes.any,
+ inventoryId: PropTypes.any
+};
+
+const bottomButtonStyling = {
+ width: '200px',
+ marginTop: '25px',
+ textTransform: 'uppercase',
+ borderRadius: '100px',
+ padding: '13px 30px'
+};
+
+function WidgetNestedDataTableContainer({ inventoryId }) {
+ const dispatch = useDispatch();
+ const [selected, setSelected] = React.useState(null);
+ const [formType, setFormType] = React.useState('family');
+ const [formOpen, setFormOpen] = React.useState(false);
+ const widgetFamilyData = useSelector(WidgetSelectors.getWidgetFamiliesByInventoryId(inventoryId));
+
+ useEffect(() => {
+ dispatch(
+ WidgetActions.widgetRequest({
+ loader: 'location-request',
+ slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${inventoryId}`,
+ method: 'get'
+ })
+ );
+ }, []);
+
+ return (
+ <>
+ {widgetFamilyData &&
+ widgetFamilyData.map((p) => (
+
+ ))}
+
+
+ {
+ setFormType('family');
+ setFormOpen(true);
+ }}
+ >
+ Add Family
+
+
+
+ {
+ setFormType('subfamily');
+ setFormOpen(true);
+ }}
+ >
+ Add SubFamily
+
+
+
+ {/*
{JSON.stringify(selected, null, 4)} */}
+
+ {formOpen && (
+
+ )}
+ >
+ );
+}
+
+WidgetNestedDataTableContainer.propTypes = {
+ inventoryId: PropTypes.any
+};
+
+export default WidgetNestedDataTableContainer;
diff --git a/src/constant/Endpoints.js b/src/constant/Endpoints.js
index b904006..77ce016 100644
--- a/src/constant/Endpoints.js
+++ b/src/constant/Endpoints.js
@@ -13,5 +13,10 @@ export default {
ADD_NEW_SUBLEVEL: '/sublevel',
ADD_PRODUCT: '/item/',
ADD_INVENTORY: '/inventory',
- LOCATION_DELETE: '/dashboard/delete-location'
+ GET_INVENTORY: '/inventory/all?page=0&perPage=50',
+ GET_INVENTORY_TYPES: '/inventory/types',
+ LOCATION_DELETE: '/dashboard/delete-location',
+ GET_WIDGET_FAMILY_BY_INVENTORY: '/widget-family/search-by-inventory?inventory=',
+ ADD_WIDGET_FAMILY: '/widget-family',
+ EDIT_WIDGET_FAMILY: '/widget-family/'
};
diff --git a/src/pages/addNewProduct/index.js b/src/pages/addNewProduct/index.js
index f7ec80a..3256b78 100644
--- a/src/pages/addNewProduct/index.js
+++ b/src/pages/addNewProduct/index.js
@@ -15,11 +15,14 @@ import CrossIcon from 'assets/images/CrossIcon';
import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
import MDInput from 'components/MDInput';
-import { useDispatch } from 'react-redux';
+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';
const useStyles = makeStyles({
labelSize: {
@@ -43,47 +46,53 @@ const useStyles = makeStyles({
}
});
-const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet'];
-
-function AddNewProduct() {
+function AddNewItem() {
const classes = useStyles();
+ const { widgetName, inventoryId } = useParams();
const dispatch = useDispatch();
const [Manufacturer, setManufacturer] = React.useState('');
const [open, setOpen] = React.useState(false);
- const handleClickOpen = () => {
- setOpen(true);
- };
+ React.useEffect(() => {
+ dispatch(
+ WidgetActions.widgetRequest({
+ loader: 'location-request',
+ slug: `${API.GET_WIDGET_FAMILY_BY_INVENTORY}${inventoryId}`,
+ method: 'get'
+ })
+ );
+ }, []);
- const handleClose = () => {
- setOpen(false);
- };
-
- const handleChange = (event) => {
- setManufacturer(event.target.value);
- };
+ 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: {
- warehousename: '',
+ commonName: '',
+ formalName: '',
description: '',
manufacturer: '',
- type: '',
- unitofmaterial: '',
- packagecount: '',
- formalname: '',
size: '',
color: '',
- unitcost: '',
- countperpallet: '',
- countperpalletpackage: '',
- productfamilyassociation: '',
- under: '',
- over: '',
- alert: '',
+ type: '',
+ unitOfMaterial: '',
+ unitCost: 0,
+ packageCount: 0,
+ countPerPallet: 0,
+ countPerPalletPackage: 0,
+ primaryWidgetFamilyId: '',
+ secondaryWidgetFamilyId: '',
+ policiesMetadata: {
+ underStockLevelCount: 0,
+ overStockLevelCount: 0,
+ alertStockLevelCount: 0,
+ reorderStockLevelCount: 0
+ },
images: []
},
- validationSchema: schema.addNewProduct,
+ validationSchema: schema.addNewItem,
onSubmit: (values, onSubmitProps) => {
LOGGER.log('values', values);
dispatch(
@@ -92,25 +101,33 @@ function AddNewProduct() {
slug: API.ADD_PRODUCT,
method: 'post',
data: {
- commonName: values.warehousename,
- formalName: values.formalname,
+ 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: [
- { fieldName: 'someName', fieldType: 'String', fieldValue: 'someValue' }
- ],
- widgetFamilyId: '61dcdd10699e8f55b44c606d'
+ 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();
}
});
@@ -124,8 +141,8 @@ function AddNewProduct() {
{ name: 'Home', path: '/home' },
{ name: 'Setup', path: '/setup' },
{ name: 'Inventory', path: '/setup/inventory' },
- { name: 'Products' },
- { name: 'Add New Product' }
+ { name: `${widgetName || 'Item'}` },
+ { name: `Add New ${widgetName || 'Item'}` }
]}
/>
@@ -135,16 +152,16 @@ function AddNewProduct() {
- Warehouse name
+ {widgetName || 'Item'} name
@@ -170,91 +187,46 @@ function AddNewProduct() {
Manufacturer
-
-
-
- {formik.errors.manufacturer &&
- formik.touched.manufacturer &&
- formik.errors.manufacturer}
-
-
+
Type
-
-
-
- {formik.errors.type && formik.touched.type && formik.errors.type}
-
-
+
Unit of Material
-
-
-
- {formik.errors.unitofmaterial &&
- formik.touched.unitofmaterial &&
- formik.errors.unitofmaterial}
-
-
+
@@ -262,12 +234,12 @@ function AddNewProduct() {
@@ -279,12 +251,12 @@ function AddNewProduct() {
@@ -292,57 +264,31 @@ function AddNewProduct() {
Size
-
-
-
- {formik.errors.size && formik.touched.size && formik.errors.size}
-
-
+
Color
-
-
-
- {formik.errors.color && formik.touched.color && formik.errors.color}
-
-
+
@@ -350,12 +296,12 @@ function AddNewProduct() {
@@ -365,12 +311,12 @@ function AddNewProduct() {
@@ -380,16 +326,16 @@ function AddNewProduct() {
@@ -398,32 +344,36 @@ function AddNewProduct() {
Product Family Association
-
+
- {formik.errors.productfamilyassociation &&
- formik.touched.productfamilyassociation &&
- formik.errors.productfamilyassociation}
+ {formik.errors.primaryWidgetFamilyId &&
+ formik.touched.primaryWidgetFamilyId &&
+ formik.errors.primaryWidgetFamilyId}
@@ -431,27 +381,28 @@ function AddNewProduct() {
select
fullWidth
variant="outlined"
- name="productfamilyassociation"
- value={formik.values.productfamilyassociation}
+ name="secondaryWidgetFamilyId"
+ value={formik.values.secondaryWidgetFamilyId}
error={
- formik.touched.productfamilyassociation &&
- Boolean(formik.errors.productfamilyassociation)
+ formik.touched.secondaryWidgetFamilyId &&
+ Boolean(formik.errors.secondaryWidgetFamilyId)
}
onChange={formik.handleChange}
>
-
+
None Selected
- {inventoryTypes.map((name) => (
-
- {name}
-
- ))}
+ {secondaryFamily &&
+ secondaryFamily.map((fam) => (
+
+ {fam.name}
+
+ ))}
- {formik.errors.productfamilyassociation &&
- formik.touched.productfamilyassociation &&
- formik.errors.productfamilyassociation}
+ {formik.errors.secondaryWidgetFamilyId &&
+ formik.touched.secondaryWidgetFamilyId &&
+ formik.errors.secondaryWidgetFamilyId}
@@ -459,7 +410,6 @@ function AddNewProduct() {
{
+ setOpen(true);
+ }}
>
add custom fields
@@ -508,12 +460,10 @@ function AddNewProduct() {
@@ -523,12 +473,10 @@ function AddNewProduct() {
@@ -538,12 +486,23 @@ function AddNewProduct() {
+
+
+
+ Reorder
+
+
@@ -558,10 +517,10 @@ function AddNewProduct() {
}}
>
- cancel
+ Cancel
- add ITem
+ Add {widgetName}
@@ -584,7 +543,12 @@ function AddNewProduct() {
padding: '10px 20px'
}}
>
-
+ {
+ setOpen(false);
+ }}
+ >
@@ -628,7 +592,9 @@ function AddNewProduct() {
return Placeholder;
}
}}
- onChange={handleChange}
+ onChange={(event) => {
+ setManufacturer(event.target.value);
+ }}
>
Ten
Twenty
@@ -648,7 +614,9 @@ function AddNewProduct() {
return Placeholder;
}
}}
- onChange={handleChange}
+ onChange={(event) => {
+ setManufacturer(event.target.value);
+ }}
>
Ten
Twenty
@@ -783,4 +751,4 @@ function AddNewProduct() {
);
}
-export default AddNewProduct;
+export default AddNewItem;
diff --git a/src/pages/inventory/index.js b/src/pages/inventory/index.js
index 2214b44..6f8ec94 100644
--- a/src/pages/inventory/index.js
+++ b/src/pages/inventory/index.js
@@ -1,21 +1,24 @@
+import React from 'react';
import DashboardNavbar from 'components/DashboardNavbar';
import DashboardLayout from 'layouts/DashboardLayout';
-import { Box, Grid, MenuItem, Select, TableBody, TableCell, TableRow } from '@mui/material';
+import { Box, Grid, MenuItem, Select } from '@mui/material';
import MDInput from 'components/MDInput';
import ImageUpload from 'components/ImageUpload';
import Switch from 'components/Switch';
import MDTypography from 'components/MDTypography';
import MDBox from 'components/MDBox';
-import Dropdown from 'components/Dropdown';
import MDButton from 'components/Button';
-import BasicTable from 'components/BasicTable';
import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
import { API } from 'constant';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import InventoryActions from 'redux/InventoryRedux';
import LOGGER from 'services/Logger';
import Breadcrumbs from 'components/Breadcrumbs';
+import { useParams } from 'react-router-dom';
+import { InventorySelectors } from 'redux/InventoryRedux';
+import { useNavigate } from 'react-router-dom';
+import WidgetNestedDataTable from 'components/WidgetNestedDataTable';
const customStyles = {
labelSize: {
@@ -49,110 +52,98 @@ const customStyles = {
}
};
-const stockBox = [
+const definedPolicies = [
{
- text: 'Stock Tracking'
+ text: 'Order Tracking',
+ key: 'orderTracking'
},
{
- text: 'Replenishment'
+ text: 'Replenishment',
+ key: 'replenishment'
},
{
- text: 'Alerting'
+ text: 'Alerting',
+ key: 'alerting'
},
{
- text: 'Check In/Out'
- },
- {
- text: 'Maintenance'
- },
- {
- text: 'Location'
- }
-];
-
-const records = [
- {
- level1: 'Ipsum',
- level2: 'Vivera'
- },
- {
- level1: 'Ipsum',
- level2: 'Vivera'
- }
-];
-
-const headCells = [
- {
- id: 'level1',
- label: 'Level 1'
- },
- {
- id: 'level2',
- label: 'Level 2'
- }
-];
-
-const dropdownData = [
- {
- ID: '1',
- displayname: 'Regular, full time'
- },
- {
- ID: '2',
- displayname: 'Regular, part time'
- },
- {
- ID: '3',
- displayname: 'Contractor- Arise Max'
- }
-];
-
-const inventoryTypes = ['Perishable', 'Material', 'Product', 'Inventory', 'Fleet'];
-
-const dataLevel = [
- {
- placeholder: 'Lorem Ipsum',
- label: 'Level 1'
- },
- {
- placeholder: 'Lorem Ipsum',
- label: 'Level 2'
+ text: 'Location',
+ key: 'preferredLocations'
}
];
function InventoryScreen() {
const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const { inventoryId } = useParams();
+ const currentInventoryData = useSelector(InventorySelectors.getInventoryDetailById(inventoryId));
+ LOGGER.log({ currentInventoryData });
+ // const [inventoryAllData, setInventoryAllData] = useState([]);
+ // const initialInventoryName='';
+
+ // useEffect(() => {
+ // const filterData = inventoryData.filter((item) => item._id === inventoryId);
+ // console.log('filterData', filterData);
+ // setInitialInventoryName(filterData[0].name)
+ // setInventoryAllData(filterData[0].widgetName);
+ // }, []);
+
+ // LOGGER.log('initialInventoryName', initialInventoryName);
+
+ /* eslint-disable indent */
const formik = useFormik({
- initialValues: {
- inventoryname: '',
- inventorytype: '',
- widgetname: '',
- policies: false,
- images: []
- },
+ initialValues: inventoryId
+ ? {
+ name: currentInventoryData.name,
+ widgetName: currentInventoryData.widgetName,
+ policies: {
+ orderTracking: currentInventoryData.policies.orderTracking,
+ alerting: currentInventoryData.policies.alerting,
+ replenishment: currentInventoryData.policies.replenishment,
+ preferredLocations: false, // TODO: change later when implemented on BE
+ inventory_process: currentInventoryData.policies.inventory_process
+ },
+ image: [{ src: currentInventoryData.image }]
+ }
+ : {
+ name: '',
+ widgetName: '',
+ policies: {
+ orderTracking: false,
+ alerting: false,
+ replenishment: false,
+ preferredLocations: false,
+ inventory_process: 'CCR'
+ },
+ image: []
+ },
validationSchema: schema.addInventory,
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
+ inventoryId
+ ? dispatch(
+ InventoryActions.updateInventoryAction({
+ loader: 'loading-request',
+ slug: `${API.ADD_INVENTORY}/${inventoryId}`,
+ method: 'patch',
+ data: {
+ ...values,
+ image: values.image[0]?.file || null
}
- }
- }
- })
- );
+ })
+ )
+ : dispatch(
+ InventoryActions.addInventoryAction({
+ loader: 'loading-request',
+ slug: API.ADD_INVENTORY,
+ method: 'post',
+ data: {
+ ...values,
+ image: values.image[0]?.file || null
+ }
+ })
+ );
+ // navigate to edit inventory page
onSubmitProps.resetForm();
}
});
@@ -187,67 +178,43 @@ function InventoryScreen() {
-
-
- Inventory Type
-
-
-
-
+
Widget Name
Policies
- Egestas pulvinar ornare vulputate porttitor consectetur condimentum at tellus
- quis. Leo pellentesque ipsum, a purus dignissim aliquam, orci. Elementum
- ullamcorper a sit eleifend ante ullamcorper ornare mi pharetra.
+ Choose policies to be applied
- {stockBox.map((item) => (
- <>
-
- {item.text}
-
-
- >
+ {definedPolicies.map((item) => (
+
+ {item.text}
+
+
))}
+
+
+ Inventory Process
+
+
+
{
- formik.setFieldValue('images', images);
+ formik.setFieldValue('image', images);
}}
/>
-
-
- {dataLevel &&
- dataLevel.map((item, index) => (
-
-
-
- ))}
-
-
- {'add hierarchy level'}
-
-
-
-
-
-
+ {
+ navigate('/setup/inventory');
}}
>
-
- Widget hierarchy
-
-
-
- {records &&
- records.map((item) => (
-
- {item.level1}
- {item.level2}
-
- ))}
-
-
-
-
-
-
{'CANCEL'}
-
+
{'SAVE'}
@@ -346,6 +295,7 @@ function InventoryScreen() {
+ {inventoryId ? : null}
);
diff --git a/src/pages/setupInventory/index.js b/src/pages/setupInventory/index.js
index 0c29e29..09c0333 100644
--- a/src/pages/setupInventory/index.js
+++ b/src/pages/setupInventory/index.js
@@ -5,42 +5,53 @@ import EquipmentIcon from 'assets/images/EquimpmentIcon';
import ProductsIcon from 'assets/images/ProductsIcon';
import FleetIcon from 'assets/images/FleetIcon';
import RawMaterialIcon from 'assets/images/RawMaterialIcon';
+import InventoryActions from 'redux/InventoryRedux';
+import { InventorySelectors } from 'redux/InventoryRedux';
import { Grid } from '@mui/material';
import Tile from 'components/TileComponent';
import MDButton from 'components/Button';
import { useNavigate } from 'react-router-dom';
import Breadcrumbs from 'components/Breadcrumbs';
+import { useDispatch, useSelector } from 'react-redux';
+import { useEffect, useState } from 'react';
+import { API } from 'constant';
+
+function getIconFromSlug(slug) {
+ switch (slug) {
+ case 'equipment':
+ return ;
+ case 'product':
+ return ;
+ case 'fleet':
+ return ;
+ case 'rawmaterial':
+ default:
+ return ;
+ }
+}
function SetupInventory() {
const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const inventoryData = useSelector(InventorySelectors.getInventoryDetail);
+ const [inventoryAllData, setInventoryAllData] = useState([]);
- const tiles = [
- {
- name: 'Raw Material',
- path: { update: '/', addNew: '/', cycleCount: '/', list: '/' },
- icon:
- },
- {
- name: 'Products',
- path: {
- update: '/',
- addNew: '/setup/inventory/product/add-new-product',
- cycleCount: '/',
- list: '/'
- },
- icon:
- },
- {
- name: 'Equipment',
- path: { update: '/', addNew: '/', cycleCount: '/', list: '/' },
- icon:
- },
- {
- name: 'Fleet',
- path: { update: '/', addNew: '/', cycleCount: '/', list: '/' },
- icon:
+ useEffect(() => {
+ if (inventoryData?.length) {
+ setInventoryAllData(inventoryData);
}
- ];
+ }, [inventoryData]);
+
+ useEffect(() => {
+ dispatch(
+ InventoryActions.getInventoryAction({
+ loader: 'loading-request',
+ slug: API.GET_INVENTORY,
+ method: 'get'
+ })
+ );
+ }, []);
+
return (
@@ -51,20 +62,18 @@ function SetupInventory() {
{ name: 'Inventory' }
]}
>
- navigate('/setup/inventory/inventory-new')}
- >
+ navigate('/setup/inventory/new')}>
Create Inventory
- {tiles &&
- tiles.map((tile) => (
- 4 ? 4 : 6} key={tile.name}>
- {tile.icon}
+ {inventoryAllData &&
+ inventoryAllData.map((tile) => (
+ 4 ? 4 : 6} key={tile._id}>
+
+ {getIconFromSlug(tile.icon_slug)}
+
))}
diff --git a/src/redux/InventoryRedux.js b/src/redux/InventoryRedux.js
index 1dc19c8..f67e144 100644
--- a/src/redux/InventoryRedux.js
+++ b/src/redux/InventoryRedux.js
@@ -5,9 +5,18 @@ import { getFetchingValue, getErrorValue } from '../services/Utils';
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
+ getInventoryAction: ['payload'],
+ getInventorySuccess: ['data'],
+ getInventoryFailure: ['error'],
addInventoryAction: ['payload'],
addInventorySuccess: ['data'],
- addInventoryFailure: ['error']
+ addInventoryFailure: ['error'],
+ updateInventoryAction: ['payload'],
+ updateInventorySuccess: ['data'],
+ updateInventoryFailure: ['error'],
+ getInventoryTypesAction: ['payload'],
+ getInventoryTypesSuccess: ['data'],
+ getInventoryTypesFailure: ['error']
});
export const InventoryTypes = Types;
@@ -16,17 +25,44 @@ export default InventoryActions;
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
+ getInventoryDetail: [],
addInventoryDetail: [],
+ updateInventoryDetail: [],
addInventoryLoading: false,
- addInventoryerror: {}
+ addInventoryerror: {},
+ inventoryTypes: []
});
/* ------------- Selectors ------------- */
export const InventorySelectors = {
- addInventoryDetail: (state) => state.inventory.inventoryDetail
+ addInventoryDetail: (state) => state.inventory.inventoryDetail,
+ getInventoryDetail: (state) => state.inventory.getInventoryDetail,
+ getInventoryDetailById: (id) => (state) =>
+ state.inventory.getInventoryDetail.find((x) => x._id === id),
+ getInventoryTypes: (state) => state.inventory.inventoryTypes,
+ updateInventoryDetail: (state) => state.inventory.updateInventoryDetail
};
/* ------------- Reducers ------------- */
+export const onGetInventoryAction = (state, { payload }) =>
+ state.merge({
+ fetching: _.uniq([state.fetching, payload?.loader]),
+ error: getErrorValue(state?.error, payload?.loader)
+ });
+
+export const onGetInventorySuccess = (state, { data }) =>
+ state.merge({
+ fetching: getFetchingValue(state.fetching, data?.loader),
+ error: getErrorValue(state?.error, data?.loader),
+ getInventoryDetail: data.getInventoryDetail
+ });
+
+export const onGetInventoryFailure = (state, { error }) =>
+ state.merge({
+ fetching: _.without(state.fetching, error?.loader),
+ error: { ...state.error, [error?.loader]: error?.error }
+ });
+
export const onAddInventoryAction = (state, { payload }) =>
state.merge({
fetching: _.uniq([state.fetching, payload?.loader]),
@@ -37,7 +73,7 @@ export const onAddInventorySuccess = (state, { data }) =>
state.merge({
fetching: getFetchingValue(state.fetching, data?.loader),
error: getErrorValue(state?.error, data?.loader),
- addInventoryDetail: data.addInventoryDetail
+ getInventoryDetail: [...state.getInventoryDetail, data.newInventory]
});
export const onAddInventoryFailure = (state, { error }) =>
@@ -46,9 +82,59 @@ export const onAddInventoryFailure = (state, { error }) =>
error: { ...state.error, [error?.loader]: error?.error }
});
+export const onUpdateInventoryAction = (state, { payload }) =>
+ state.merge({
+ fetching: _.uniq([state.fetching, payload?.loader]),
+ error: getErrorValue(state?.error, payload?.loader)
+ });
+
+export const onUpdateInventorySuccess = (state, { data }) =>
+ state.merge({
+ fetching: getFetchingValue(state.fetching, data?.loader),
+ error: getErrorValue(state?.error, data?.loader),
+ getInventoryDetail: [
+ ...state.getInventoryDetail.filter((x) => x._id !== data.newInventory._id),
+ data.newInventory
+ ]
+ });
+
+export const onUpdateInventoryFailure = (state, { error }) =>
+ state.merge({
+ fetching: _.without(state.fetching, error?.loader),
+ error: { ...state.error, [error?.loader]: error?.error }
+ });
+
+export const onGetInventoryTypesAction = (state, { payload }) =>
+ state.merge({
+ fetching: _.uniq([state.fetching, payload?.loader]),
+ error: getErrorValue(state?.error, payload?.loader)
+ });
+
+export const onGetInventoryTypesSuccess = (state, { data }) =>
+ state.merge({
+ fetching: getFetchingValue(state.fetching, data?.loader),
+ error: getErrorValue(state?.error, data?.loader),
+ inventoryTypes: data.inventoryTypes
+ });
+
+export const onGetInventoryTypesFailure = (state, { error }) =>
+ state.merge({
+ fetching: _.without(state.fetching, error?.loader),
+ error: { ...state.error, [error?.loader]: error?.error }
+ });
+
/* ------------- Hookup Reducers To Types ------------- */
export const inventoryReducer = createReducer(INITIAL_STATE, {
+ [Types.GET_INVENTORY_ACTION]: onGetInventoryAction,
+ [Types.GET_INVENTORY_SUCCESS]: onGetInventorySuccess,
+ [Types.GET_INVENTORY_FAILURE]: onGetInventoryFailure,
[Types.ADD_INVENTORY_ACTION]: onAddInventoryAction,
[Types.ADD_INVENTORY_SUCCESS]: onAddInventorySuccess,
- [Types.ADD_INVENTORY_FAILURE]: onAddInventoryFailure
+ [Types.ADD_INVENTORY_FAILURE]: onAddInventoryFailure,
+ [Types.UPDATE_INVENTORY_ACTION]: onUpdateInventoryAction,
+ [Types.UPDATE_INVENTORY_SUCCESS]: onUpdateInventorySuccess,
+ [Types.UPDATE_INVENTORY_FAILURE]: onUpdateInventoryFailure,
+ [Types.GET_INVENTORY_TYPES_ACTION]: onGetInventoryTypesAction,
+ [Types.GET_INVENTORY_TYPES_SUCCESS]: onGetInventoryTypesSuccess,
+ [Types.GET_INVENTORY_TYPES_FAILURE]: onGetInventoryTypesFailure
});
diff --git a/src/redux/WidgetRedux.js b/src/redux/WidgetRedux.js
new file mode 100644
index 0000000..ab219da
--- /dev/null
+++ b/src/redux/WidgetRedux.js
@@ -0,0 +1,101 @@
+import { createActions, createReducer } from 'reduxsauce';
+import Immutable from 'seamless-immutable';
+import _ from 'underscore';
+import { getFetchingValue, getErrorValue } from '../services/Utils';
+
+/* ------------- Types and Action Creators ------------- */
+const { Types, Creators } = createActions({
+ widgetRequest: ['payload'],
+ widgetSuccess: ['data'],
+ editWidgetRequest: ['payload'],
+ editWidgetSuccess: ['data'],
+ widgetFailure: ['error'],
+ logout: null
+});
+
+export const WidgetTypes = Types;
+const WidgetActions = Creators;
+export default WidgetActions;
+
+/* ------------- Initial State ------------- */
+export const INITIAL_STATE = Immutable({
+ list: [],
+ fetching: [],
+ error: {}
+});
+
+/* ------------- Selectors ------------- */
+export const WidgetSelectors = {
+ getWidgets: (state) => state.widgets.list,
+ getWidgetById: (id) => (state) => state.widgets.list.find((x) => x._id === id),
+ getWidgetsByInventoryId: (id) => (state) =>
+ state.widgets.list.filter((x) => x.inventory._id === id),
+ getWidgetFamiliesByInventoryId: (id) => (state) =>
+ state.widgets.list.filter((x) => !x.parent && x.inventory._id === id),
+ getWidgetsByParentId: (id) => (state) => state.widgets.list.filter((x) => x.parent?._id === id)
+};
+
+/* ------------- Reducers ------------- */
+export const onWidgetRequest = (state, { payload }) =>
+ state.merge({
+ fetching: _.uniq([...state.fetching, payload?.loader]),
+ error: getErrorValue(state?.error, payload?.loader)
+ });
+
+export const onEditWidgetRequest = (state, { payload }) =>
+ state.merge({
+ fetching: _.uniq([...state.fetching, payload?.loader]),
+ error: getErrorValue(state?.error, payload?.loader)
+ });
+
+const mergeWidgetStates = (stateData, widgets) => {
+ if (!widgets) return stateData; // undefined check
+
+ const idsInNewWidgets = widgets.map((x) => x._id);
+
+ const newState = stateData.filter((x) => !idsInNewWidgets.includes(x._id));
+
+ return [...newState, ...widgets];
+};
+
+export const onWidgetSuccess = (state, { data }) =>
+ state.merge({
+ fetching: getFetchingValue(state.fetching, data?.loader),
+ error: getErrorValue(state?.error, data?.loader),
+ list: mergeWidgetStates(state.list, data.widgets)
+ });
+
+const mergeEditWidgetStates = (stateList, widget, type) => {
+ if (!widget) return stateList; // undefined check
+
+ if (type === 'add') {
+ return [...stateList, widget];
+ } else if (type === 'edit') {
+ const newState = stateList.filter((x) => x._id !== widget._id);
+ return [...newState, widget];
+ } else {
+ return stateList;
+ }
+};
+
+export const onEditWidgetSuccess = (state, { data }) =>
+ state.merge({
+ fetching: getFetchingValue(state.fetching, data?.loader),
+ error: getErrorValue(state?.error, data?.loader),
+ list: mergeEditWidgetStates(state.list, data.widget, data.type)
+ });
+
+export const onWidgetFailure = (state, { error }) =>
+ state.merge({
+ fetching: _.without(state.fetching, error?.loader),
+ error: { ...state.error, [error?.loader]: error?.error }
+ });
+
+/* ------------- Hookup Reducers To Types ------------- */
+export const widgetReducer = createReducer(INITIAL_STATE, {
+ [Types.WIDGET_REQUEST]: onWidgetRequest,
+ [Types.EDIT_WIDGET_REQUEST]: onEditWidgetRequest,
+ [Types.WIDGET_SUCCESS]: onWidgetSuccess,
+ [Types.EDIT_WIDGET_SUCCESS]: onEditWidgetSuccess,
+ [Types.WIDGET_FAILURE]: onWidgetFailure
+});
diff --git a/src/redux/index.js b/src/redux/index.js
index 025c49f..d23d878 100644
--- a/src/redux/index.js
+++ b/src/redux/index.js
@@ -6,6 +6,7 @@ import { productReducer } from './ProductsRedux';
import { inventoryReducer } from './InventoryRedux';
import { rolesReducer } from './RolesRedux';
import { WarehouseLocationsReducer } from './WarehouseLocationsRedux';
+import { widgetReducer } from './WidgetRedux';
// Combine all reducers.
const appReducer = combineReducers({
@@ -15,7 +16,8 @@ const appReducer = combineReducers({
roles: rolesReducer,
warehouseLocations: WarehouseLocationsReducer,
product: productReducer,
- inventory: inventoryReducer
+ inventory: inventoryReducer,
+ widgets: widgetReducer
});
const rootReducer = (state, action) => {
diff --git a/src/routes/index.js b/src/routes/index.js
index 0af896c..fd423fb 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -57,7 +57,7 @@ import LabelingHome from 'pages/labellingHome';
import SetupInventory from 'pages/setupInventory';
import HomeIcon from 'assets/images/HomeIcon';
import SetupIcon from 'assets/images/SetupIcon';
-import AddNewProduct from '../pages/addNewProduct';
+import AddNewItem from '../pages/addNewProduct';
import CreateUserRole from 'pages/createUserRole';
import WidgetLabel from 'pages/widgetLabel';
@@ -141,15 +141,22 @@ const protectedRoutes = [
name: 'Inventory Definition',
key: 'inventory-new',
hide: true,
- route: '/setup/inventory/inventory-new',
+ route: '/setup/inventory/new',
component:
},
{
- name: 'Add New Product',
- key: 'add-new-product',
+ name: 'Inventory Definition',
+ key: 'inventory-update',
hide: true,
- route: '/setup/inventory/product/add-new-product',
- component:
+ route: '/setup/inventory/update/:inventoryId',
+ component:
+ },
+ {
+ name: 'Add New Item',
+ key: 'add-new-item',
+ hide: true,
+ route: '/setup/inventory/new-item/:widgetName/:inventoryId',
+ component:
},
{
name: 'Location Labeling',
diff --git a/src/sagas/Inventory.js b/src/sagas/Inventory.js
index fe1e20d..ae3d2cc 100644
--- a/src/sagas/Inventory.js
+++ b/src/sagas/Inventory.js
@@ -1,21 +1,85 @@
import { AuthorizedAPI } from 'config';
-import { takeLatest, call, put } from 'redux-saga/effects';
+import { takeLatest, call, put, takeEvery } from 'redux-saga/effects';
import InventoryActions from 'redux/InventoryRedux';
import { InventoryTypes } from 'redux/InventoryRedux';
import ApiServices from 'services/API/ApiServices';
-export function* onRequestAddInventoryData({ payload }) {
+export function* onRequestGetInventoryData({ payload }) {
const response = yield call(
ApiServices[payload?.method],
AuthorizedAPI,
payload?.slug,
payload?.data
);
+ if (response?.status === 200) {
+ yield put(
+ InventoryActions.getInventorySuccess({
+ loader: payload?.loader,
+ getInventoryDetail: response?.data?.data
+ })
+ );
+ } else {
+ payload.onFailedGetInventoryData(response.data.error);
+ yield put(
+ InventoryActions.getInventoryFailure({
+ loader: payload?.loader,
+ error: response?.data
+ })
+ );
+ }
+}
+
+export function* onRequestGetInventoryTypesData({ payload }) {
+ const response = yield call(
+ ApiServices[payload?.method],
+ AuthorizedAPI,
+ payload?.slug,
+ payload?.data
+ );
+ if (response?.status === 200) {
+ yield put(
+ InventoryActions.getInventoryTypesSuccess({
+ loader: payload?.loader,
+ inventoryTypes: response?.data?.data
+ })
+ );
+ } else {
+ payload.onFailedGetInventoryData(response.data.error);
+ yield put(
+ InventoryActions.getInventoryTypesFailure({
+ loader: payload?.loader,
+ error: response?.data
+ })
+ );
+ }
+}
+
+const parseDataToFormData = (data) => {
+ var formData = new FormData();
+ formData.append('name', data.name);
+ formData.append('widgetName', data.widgetName);
+ formData.append('icon_slug', 'testslug');
+ formData.append('policies[orderTracking]', data.policies.orderTracking);
+ formData.append('policies[alerting]', data.policies.alerting);
+ formData.append('policies[replenishment]', data.policies.replenishment);
+ formData.append('policies[preferredLocations]', data.policies.preferredLocations);
+ formData.append('policies[inventory_process]', data.policies.inventory_process);
+ data.image && formData.append('image', data.image);
+ return formData;
+};
+
+export function* onRequestAddInventoryData({ payload }) {
+ const response = yield call(
+ ApiServices[payload?.method],
+ AuthorizedAPI,
+ payload?.slug,
+ parseDataToFormData(payload?.data)
+ );
if (response?.status === 200) {
yield put(
InventoryActions.addInventorySuccess({
loader: payload?.loader,
- addInventoryDetail: response?.data?.data
+ newInventory: response?.data?.data?.inventoryData
})
);
} else {
@@ -28,4 +92,34 @@ export function* onRequestAddInventoryData({ payload }) {
);
}
}
-export default [takeLatest(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData)];
+
+export function* onRequestUpdateInventoryData({ payload }) {
+ const response = yield call(
+ ApiServices[payload?.method],
+ AuthorizedAPI,
+ payload?.slug,
+ payload?.data
+ );
+ if (response?.status === 200) {
+ yield put(
+ InventoryActions.updateInventorySuccess({
+ loader: payload?.loader,
+ updateInventoryDetail: response?.data?.data
+ })
+ );
+ } else {
+ payload.onFailedUpdateInventoryData(response.data.error);
+ yield put(
+ InventoryActions.updateInventoryFailure({
+ loader: payload?.loader,
+ error: response?.data
+ })
+ );
+ }
+}
+export default [
+ takeLatest(InventoryTypes.GET_INVENTORY_ACTION, onRequestGetInventoryData),
+ takeLatest(InventoryTypes.ADD_INVENTORY_ACTION, onRequestAddInventoryData),
+ takeLatest(InventoryTypes.UPDATE_INVENTORY_ACTION, onRequestUpdateInventoryData),
+ takeEvery(InventoryTypes.GET_INVENTORY_TYPES_ACTION, onRequestGetInventoryTypesData)
+];
diff --git a/src/sagas/Widget.js b/src/sagas/Widget.js
new file mode 100644
index 0000000..30f9448
--- /dev/null
+++ b/src/sagas/Widget.js
@@ -0,0 +1,58 @@
+import { AuthorizedAPI } from 'config';
+import { call, put, takeEvery } from 'redux-saga/effects';
+import ApiServices from 'services/API/ApiServices';
+import WidgetActions, { WidgetTypes } from '../redux/WidgetRedux';
+
+export function* onRequestWidget({ payload }) {
+ const response = yield call(
+ ApiServices[payload?.method],
+ AuthorizedAPI,
+ payload?.slug,
+ payload?.data
+ );
+ if (response?.status === 200) {
+ yield put(
+ WidgetActions.widgetSuccess({
+ loader: payload?.loader,
+ widgets: response?.data?.data
+ })
+ );
+ } else {
+ yield put(
+ WidgetActions.widgetFailure({
+ loader: payload?.loader,
+ error: response?.message
+ })
+ );
+ }
+}
+
+export function* onEditRequestWidget({ payload }) {
+ const response = yield call(
+ ApiServices[payload?.method],
+ AuthorizedAPI,
+ payload?.slug,
+ payload?.data
+ );
+ if (response?.status === 200) {
+ yield put(
+ WidgetActions.editWidgetSuccess({
+ loader: payload?.loader,
+ widget: response?.data?.data,
+ type: payload?.type
+ })
+ );
+ } else {
+ yield put(
+ WidgetActions.widgetFailure({
+ loader: payload?.loader,
+ error: response?.message
+ })
+ );
+ }
+}
+
+export default [
+ takeEvery(WidgetTypes.WIDGET_REQUEST, onRequestWidget),
+ takeEvery(WidgetTypes.EDIT_WIDGET_REQUEST, onEditRequestWidget)
+];
diff --git a/src/sagas/index.js b/src/sagas/index.js
index 72a2c8c..f8ab1db 100644
--- a/src/sagas/index.js
+++ b/src/sagas/index.js
@@ -6,6 +6,7 @@ import ProductSaga from './Product';
import InventorySaga from './Inventory';
import RolesSaga from './Roles';
import WarehouseLocationsSaga from './WarehouseLocations';
+import WidgetSaga from './Widget';
export default function* rootSaga() {
yield all([...AuthSaga]);
@@ -15,4 +16,5 @@ export default function* rootSaga() {
yield all([...InventorySaga]);
yield all([...RolesSaga]);
yield all([...WarehouseLocationsSaga]);
+ yield all([...WidgetSaga]);
}
diff --git a/src/services/ValidationServices.js b/src/services/ValidationServices.js
index 58576bb..5f1832f 100644
--- a/src/services/ValidationServices.js
+++ b/src/services/ValidationServices.js
@@ -28,33 +28,41 @@ const schema = {
attributes: Yup.string('Enter other attributes')
}),
- addNewProduct: Yup.object({
- warehousename: Yup.string('Enter warehouse name').required('warehouse name is required'),
- description: Yup.string('Enter Description').required('description is required'),
- manufacturer: Yup.string('Enter manufacturer').required('manufacturer is required'),
- type: Yup.string('Enter type').required('type is required'),
- unitofmaterial: Yup.string('Enter unitofmaterial').required('Unit of material is required'),
- packagecount: Yup.number('Enter packagecount').required('Package Count is required'),
- formalname: Yup.string('Enter formal name').required('Formal Name is required'),
- size: Yup.string('Enter Size').required('Size is required'),
- color: Yup.string('Enter Color').required('Color is required'),
- unitcost: Yup.number('Enter UnitCost').required('Unit Cost is required'),
- countperpallet: Yup.number('Enter countperpallet').required('Count per pallet is required'),
- countperpalletpackage: Yup.number('Enter countperpalletpackage').required(
- 'count per pallet package is required'
- ),
- productfamilyassociation: Yup.string('Enter productfamilyassociation').required(
- 'product Family Association is required'
- ),
- under: Yup.number().required('required'),
- over: Yup.number().required('required'),
- alert: Yup.number().required('required')
+ addNewItem: Yup.object({
+ commonName: Yup.string('Enter details').required(),
+ formalName: Yup.string('Enter details').required(),
+ description: Yup.string('Enter details').required(),
+ manufacturer: Yup.string('Enter details').required(),
+ size: Yup.string('Enter details').required(),
+ color: Yup.string('Enter details').required(),
+ type: Yup.string('Enter details').required(),
+ unitOfMaterial: Yup.string('Enter details').required(),
+ unitCost: Yup.number().test((val) => val >= 0),
+ packageCount: Yup.number().test((val) => val >= 0),
+ countPerPallet: Yup.number().test((val) => val >= 0),
+ countPerPalletPackage: Yup.number().test((val) => val >= 0),
+ primaryWidgetFamilyId: Yup.string('Enter details').required(),
+ secondaryWidgetFamilyId: Yup.string('Enter details'),
+ policiesMetadata: Yup.object({
+ underStockLevelCount: Yup.number().test((val) => val >= 0),
+ overStockLevelCount: Yup.number().test((val) => val >= 0),
+ alertStockLevelCount: Yup.number().test((val) => val >= 0),
+ reorderStockLevelCount: Yup.number().test((val) => val >= 0)
+ }),
+ images: Yup.array()
}),
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')
+ name: Yup.string('Enter Widget Name').required('Widget Name is required'),
+ widgetName: Yup.string('Enter Inventory name').required('Inventory name is required'),
+ policies: Yup.object({
+ orderTracking: Yup.boolean(),
+ alerting: Yup.boolean(),
+ replenishment: Yup.boolean(),
+ preferredLocations: Yup.boolean(),
+ inventory_process: Yup.string()
+ }),
+ image: Yup.array()
})
};