Stashed / app / src / pages / inventory / inventory.jsx
inventory.jsx
Raw
import React, { useState, useEffect, useCallback } from "react";
import clsx from "clsx";
import { connect } from "react-redux";
import { history } from "Redux/store/store";
import ROUTES from "Constants/routes";
import {
  writeConfigRequest,
  useConfigInMainRequest,
} from "secure-electron-store";
import axios from "axios";
import { makeStyles, withStyles } from "@material-ui/core/styles";
import {
  Button,
  Divider,
  Grid,
  Typography,
  Menu,
  MenuItem,
  Snackbar,
  Paper,
  TextField,
  InputAdornment,
} from "@material-ui/core";
import {
  DataGrid,
  GridToolbarContainer,
  GridToolbarExport,
} from "@material-ui/data-grid";
import CloseIcon from "@material-ui/icons/Close";
import SearchIcon from "@material-ui/icons/Search";
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";

import { openModal } from "Redux/modals/addItemModalSlice";
import { openEditModal } from "Redux/modals/editItemModalSlice";
import { openSaleModal } from "Redux/modals/addSaleModalSlice";
import { addItem, replaceItems, deleteItem } from "Redux/data/inventorySlice";
import { addSale } from "Redux/data/salesSlice";
import AddItemModal from "Components/modals/addItemModal";
import EditItemModal from "Components/modals/editItemModal";
import AddSaleModal from "Components/modals/addSaleModal";

const useStyles = makeStyles((theme) => ({
  divider: { marginBottom: 24 },
  section: { paddingLeft: 4, paddingRight: 4, marginBottom: 18 },
  data: {
    height: 650,
    width: "100%",
    "& .datagrid.negative": { color: "#d50000" },
    "& .datagrid.positive": {
      color: "#00c853",
    },
  },
  root: {
    width: "100%",
    "& > * + *": {
      marginTop: theme.spacing(2),
    },
  },
  paper: {
    padding: theme.spacing(2),
    color: theme.palette.text.secondary,
    display: "flex",
    alignItems: "center",
    marginBottom: theme.spacing(1),
  },
  buttonSpacing: {
    marginRight: 12,
  },
}));
const StyledMenu = withStyles({
  paper: {
    border: "1px solid #d3d4d5",
  },
})((props) => (
  <Menu
    elevation={0}
    getContentAnchorEl={null}
    anchorOrigin={{
      vertical: "bottom",
      horizontal: "left",
    }}
    transformOrigin={{
      vertical: "top",
      horizontal: "left",
    }}
    {...props}
  />
));

const initContextMenuState = {
  mouseX: null,
  mouseY: null,
};

const headers = {
  "Access-Control-Allow-Origin": "http://localhost:40992",
  "Content-Type": "application/json",
  "Access-Control-Allow-Methods": "GET",
  "Access-Control-Allow-Headers": "Content-Type, Authorization",
  "X-Requested-With": "XMLHttpRequest",
};
const Inventory = (props) => {
  useEffect(() => {
    window.api.store.send(useConfigInMainRequest);

    window.api.inventoryData("receiveInventoryData", (data) => {
      const imports = JSON.parse(data);
      let currIds = [];

      for (let i = 0; i < props.inventory.items.length; i++) {
        currIds.push(props.inventory.items[i].id);
      }
      let importsToAdd = imports.filter((im) => {
        return !currIds.includes(im.id);
      });
      importsToAdd.map((im) => {
        props.addItem(im);
      });
      window.api.store.send(writeConfigRequest, "inventoryItems", [
        ...importsToAdd,
        ...props.inventory.items,
      ]);
    });
    console.log("INVENTORYPAGE Props:", props);
    setInventoryRows(...inventoryRows, props.inventory.items);
  }, []);
  const Navigate = (url, item) => {
    history.push({
      pathname: url,
      search: selectedItems[0],
      state: "inventory",
    });
  };

  const columns = [
    { field: "name", headerName: "Product Name", width: 300 },
    { field: "size", headerName: "Size", filterable: false, width: 120 },
    {
      field: "sku",
      headerName: "SKU",
      width: 130,
      sortable: false,
      description: "Style ID",
    },
    { field: "status", headerName: "Status", width: 125 },
    {
      field: "unrealizedProfit",
      headerName: "Unrealized Profit",
      filterable: false,
      width: 165,
      valueGetter: (params) =>
        `${getUnrealizedProfit(params) < 0 ? "-" : ""}` +
        `$` +
        `${Math.abs(getUnrealizedProfit(params)).toFixed(2)}`,
      cellClassName: (params) =>
        clsx("datagrid", {
          negative: getUnrealizedProfit(params) < 0,
          positive: getUnrealizedProfit(params) > 0,
        }),
    },
    {
      field: "totalCost",
      headerName: "Total Cost",
      filterable: false,
      description: "Cost + Tax + Shipping",
      width: 150,
      valueGetter: (params) => `$${getTotalCost(params).toFixed(2)} `,
    },
    {
      field: "colorway",
      headerName: "Colorway",
      filterable: false,
      sortable: false,
      width: 160,
    },
    {
      field: "store",
      headerName: "Place of purchase",
      sortable: false,
      filterable: false,
      width: 200,
    },
    {
      field: "dateObtained",
      headerName: "Date Obtained",
      filterable: false,
      width: 170,
    },
    {
      field: "dateAdded",
      headerName: "Date Added",
      filterable: false,
      width: 170,
    },
  ];
  const getTotalCost = (params) => {
    return (
      parseFloat(params.row.cost) +
      parseFloat(params.row.costShipping) +
      parseFloat(params.row.costTax)
    );
  };
  const getUnrealizedProfit = (params) => {
    return (
      parseFloat(
        togglePriceType == "ask"
          ? params.row.marketPrices.stockX.lowestAsk
          : params.row.marketPrices.stockX.highestBid
      ) - parseFloat(getTotalCost(params))
    );
  };

  const [tableLoading, setTableLoading] = useState(true);
  const [inventoryRows, setInventoryRows] = useState([]);
  useEffect(() => {
    if (inventoryRows.length > 0) {
      setTableLoading(false);
    }
  }, [inventoryRows]);

  const [selectedItems, setSelectedItems] = useState([]);
  const [selectedItemsTotalCost, setSelectedItemsTotalCost] = useState(0);
  useEffect(() => {
    if (selectedItems.length > 0) {
      let totalCost = parseFloat(0);
      selectedItems.map((itemId) => {
        props.inventory.items.map((item) => {
          if (itemId == item.id) {
            totalCost +=
              parseFloat(item.cost) +
              parseFloat(item.costTax) +
              parseFloat(item.costShipping);
          }
        });
      });
      setSelectedItemsTotalCost(totalCost.toFixed(2));
    } else {
      setSelectedItemsTotalCost(0);
    }
  }, [selectedItems]);

  const deleteSelected = () => {
    const newItems = props.inventory.items.filter(
      (item) => !selectedItems.includes(item.id)
    );
    props.replaceItems(newItems);
    window.api.store.send(writeConfigRequest, "inventoryItems", newItems);
    setSelectedItems([]);
    closeContextMenu();
  };

  const [filterProductInput, setFilterProductInput] = useState("");
  const loadInventoryRows = (filter) => {
    const inventory = props.inventory.items;

    return new Promise((resolve) => {
      setTimeout(() => {
        if (!filter) {
          resolve(inventory);
          return;
        }
        resolve(
          inventory.filter(
            (row) => row.name.toLowerCase().indexOf(filter.toLowerCase()) > -1
          )
        );
      }, Math.random() * 500 + 100); // simulate network latency
    });
  };
  useEffect(() => {
    let active = true;

    (async () => {
      setTableLoading(true);
      const newRows = await loadInventoryRows(filterProductInput);

      if (!active) {
        return;
      }

      setInventoryRows(newRows);
      setTableLoading(false);
    })();

    return () => {
      active = false;
    };
  }, [filterProductInput, props.inventory.items]);

  const [actionsAnchorEl, setActionsAnchorEl] = useState(null);
  const handleActionsMenuClick = (e) => {
    setActionsAnchorEl(e.currentTarget);
  };
  const handleActionsMenuClose = () => {
    setActionsAnchorEl(null);
  };

  const [filterStatusInput, setFilterStatusInput] = useState("all");
  const [statusAnchorEl, setStatusAnchorEl] = useState(null);
  const handleStatusMenuClick = (e) => {
    setStatusAnchorEl(e.currentTarget);
  };
  const handleStatusMenuClose = () => {
    setStatusAnchorEl(null);
  };
  const loadStatusFilterRows = (filter) => {
    const inventory = props.inventory.items;

    return new Promise((resolve) => {
      setTimeout(() => {
        if (!filter) {
          resolve(inventory);
          return;
        }
        resolve(
          inventory.filter(
            (row) => row.status.toLowerCase() == filter.toLowerCase()
          )
        );
      }, Math.random() * 500 + 100); // simulate network latency
    });
  };
  useEffect(() => {
    let active = true;
    if (filterStatusInput == "all") {
      setInventoryRows(props.inventory.items);
    } else {
      (async () => {
        setTableLoading(true);
        const newRows = await loadStatusFilterRows(filterStatusInput);

        if (!active) {
          return;
        }
        setInventoryRows(newRows);
        setTableLoading(false);
      })();
    }
    return () => {
      active = false;
    };
  }, [filterStatusInput]);

  const [togglePriceType, setTogglePriceType] = useState("ask");
  const [priceTypeAnchorEl, setPriceTypeAnchorEl] = useState(null);
  const handlePriceTypeMenuClick = (e) => {
    setPriceTypeAnchorEl(e.currentTarget);
  };
  const handlePriceTypeMenuClose = () => {
    setPriceTypeAnchorEl(null);
  };

  const [contextMenuVis, setContextMenuVis] = useState(initContextMenuState);
  const openContextMenu = (e) => {
    e.preventDefault();
    setContextMenuVis({
      mouseX: e.clientX - 2,
      mouseY: e.clientY - 4,
    });
  };
  const closeContextMenu = () => {
    setContextMenuVis(initContextMenuState);
  };

  const styles = useStyles();
  return (
    <React.Fragment>
      <div className={styles.root}>
        <Grid container alignItems="center" direction="row">
          <Grid item xs={6}>
            <Typography variant="h5">Inventory</Typography>
            <Typography variant="subtitle1">
              Manage your inventory of sneakers, apparel, and collectibles.
            </Typography>
          </Grid>
          <Grid
            item
            container
            xs
            direction="row"
            justify="flex-end"
            alignItems="center"
          >
            <Button
              variant="contained"
              size="small"
              className={styles.buttonSpacing}
              onClick={() => {
                window.api.inventoryData("importInventoryData", (data) => {
                  console.log(data);
                });
              }}
            >
              Import
            </Button>
            <Button
              variant="contained"
              size="small"
              className={styles.buttonSpacing}
              onClick={() => {
                window.api.inventoryData(
                  "exportInventoryData",
                  props.inventory.items
                );
              }}
            >
              Export
            </Button>
            <Button
              variant="contained"
              color="primary"
              className={styles.buttonSpacing}
              endIcon={<ArrowDropDownIcon />}
              onClick={handleActionsMenuClick}
            >
              Actions
            </Button>
            <StyledMenu
              anchorEl={actionsAnchorEl}
              keepMounted
              open={Boolean(actionsAnchorEl)}
              onClose={handleActionsMenuClose}
            >
              <MenuItem
                onClick={() => {
                  Navigate(ROUTES.INFO);
                  closeContextMenu();
                }}
                disabled={selectedItems.length == 0}
              >
                Info
              </MenuItem>
              <MenuItem
                onClick={() => {
                  closeContextMenu();
                }}
                disabled={selectedItems.length == 0}
              >
                Mark Sold
              </MenuItem>
              <MenuItem
                onClick={() => {
                  props.openEditModal();
                  closeContextMenu();
                }}
                disabled={selectedItems.length == 0}
              >
                Edit
              </MenuItem>
              <MenuItem
                onClick={closeContextMenu}
                disabled={selectedItems.length == 0}
              >
                Duplicate
              </MenuItem>
              <MenuItem
                onClick={() => {
                  deleteSelected();
                  closeContextMenu();
                }}
                disabled={selectedItems.length == 0}
              >
                Delete
              </MenuItem>
            </StyledMenu>
            <Button
              variant="contained"
              color="primary"
              onClick={() => {
                props.openModal();
              }}
            >
              Add Item
            </Button>
          </Grid>
        </Grid>
        <Divider className={styles.divider} />

        <div className={styles.section}>
          <Grid container spacing={1} alignItems="center">
            <Grid item xs={3} xl={2}>
              <TextField
                fullWidth
                placeholder="Filter by Product Name"
                value={filterProductInput}
                onChange={(e) => {
                  setFilterProductInput(e.target.value);
                }}
                InputProps={{
                  startAdornment: (
                    <InputAdornment position="start">
                      <SearchIcon />
                    </InputAdornment>
                  ),
                  endAdornment: (
                    <InputAdornment
                      position="end"
                      style={{ cursor: "pointer" }}
                      onClick={() => {
                        setFilterProductInput("");
                      }}
                    >
                      <CloseIcon />
                    </InputAdornment>
                  ),
                }}
              />
            </Grid>
            <Grid item>
              <Button
                onClick={handleStatusMenuClick}
                variant="contained"
                color="primary"
                size="small"
                startIcon={<ArrowDropDownIcon />}
              >
                Status: {filterStatusInput}
              </Button>
              <StyledMenu
                anchorEl={statusAnchorEl}
                keepMounted
                open={Boolean(statusAnchorEl)}
                onClose={handleStatusMenuClose}
              >
                <MenuItem
                  onClick={() => {
                    setFilterStatusInput("all");
                  }}
                >
                  All
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setFilterStatusInput("sitting");
                  }}
                >
                  Sitting
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setFilterStatusInput("listed");
                  }}
                >
                  Listed
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setFilterStatusInput("personal");
                  }}
                >
                  Personal
                </MenuItem>
              </StyledMenu>
            </Grid>
            <Grid item>
              <Button
                onClick={handlePriceTypeMenuClick}
                variant="contained"
                color="primary"
                size="small"
                startIcon={<ArrowDropDownIcon />}
              >
                Price type: {togglePriceType}
              </Button>
              <StyledMenu
                anchorEl={priceTypeAnchorEl}
                keepMounted
                open={Boolean(priceTypeAnchorEl)}
                onClose={handlePriceTypeMenuClose}
              >
                <MenuItem
                  onClick={() => {
                    setTogglePriceType("ask");
                    handlePriceTypeMenuClose();
                  }}
                >
                  Ask
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setTogglePriceType("bid");
                    handlePriceTypeMenuClose();
                  }}
                >
                  Bid
                </MenuItem>
              </StyledMenu>
            </Grid>
          </Grid>
        </div>

        <div
          className={styles.data}
          onContextMenu={openContextMenu}
          style={{ cursor: "context-menu" }}
        >
          <DataGrid
            rows={inventoryRows}
            columns={columns}
            pageSize={10}
            checkboxSelection
            disableColumnMenu
            loading={tableLoading}
            onSelectionModelChange={(newSelection) => {
              setSelectedItems(newSelection.selectionModel);
            }}
            selectionModel={selectedItems}
          />

          <Menu
            keepMounted
            open={contextMenuVis.mouseY !== null}
            onClose={closeContextMenu}
            anchorReference="anchorPosition"
            anchorPosition={
              contextMenuVis.mouseY !== null && contextMenuVis.mouseX !== null
                ? { top: contextMenuVis.mouseY, left: contextMenuVis.mouseX }
                : undefined
            }
          >
            <MenuItem
              onClick={() => {
                Navigate(ROUTES.INFO);
                closeContextMenu();
              }}
              disabled={selectedItems.length == 0}
            >
              Info
            </MenuItem>
            <MenuItem
              onClick={() => {
                props.openSaleModal();
                closeContextMenu();
              }}
              disabled={selectedItems.length == 0}
            >
              Mark as Sold
            </MenuItem>
            <MenuItem
              onClick={() => {
                props.openEditModal();
                closeContextMenu();
              }}
              disabled={selectedItems.length == 0}
            >
              Edit
            </MenuItem>
            <MenuItem
              onClick={closeContextMenu}
              disabled={selectedItems.length == 0}
            >
              Duplicate
            </MenuItem>
            <MenuItem
              onClick={() => {
                deleteSelected();
                closeContextMenu();
              }}
              disabled={selectedItems.length == 0}
            >
              Delete Selected
            </MenuItem>
          </Menu>
        </div>

        <Snackbar open={selectedItems.length > 0}>
          <Paper className={styles.paper}>
            <Grid container spacing={1} alignItems="center">
              <Grid xs={4} item>
                <Typography variant="body2">
                  {selectedItems.length == 1
                    ? selectedItems.length + " item"
                    : selectedItems.length + " items"}{" "}
                  selected
                </Typography>
              </Grid>
              <Grid xs={4} item>
                <Typography variant="body2">
                  Total cost: ${selectedItemsTotalCost}
                </Typography>
              </Grid>
              <Grid xs={4} item>
                <Typography variant="body2">
                  {selectedItems.length == 1
                    ? selectedItems.length + " item"
                    : selectedItems.length + " items"}{" "}
                  selected
                </Typography>
              </Grid>
            </Grid>
          </Paper>
        </Snackbar>
        <AddItemModal />
        <EditItemModal editItems={selectedItems} />
        <AddSaleModal saleItems={selectedItems} />
      </div>
    </React.Fragment>
  );
};

const mapStateToProps = (state, props) => ({
  addItemModal: state.addItemModal,
  editItemModal: state.editItemModal,

  inventory: state.inventory,
});
const mapDispatch = {
  openModal,
  addItem,
  replaceItems,
  openEditModal,
  addSale,
  openSaleModal,
  deleteItem,
};

export default connect(mapStateToProps, mapDispatch)(Inventory);