Stashed / app / src / pages / dashboard / dashboard.jsx
dashboard.jsx
Raw
import React, { useState, useEffect, useLayoutEffect, useRef } from "react";
import clsx from "clsx";
import { connect } from "react-redux";
import ROUTES from "Constants/routes";
import { history } from "Redux/store/store";
import {
  writeConfigRequest,
  useConfigInMainRequest,
} from "secure-electron-store";
import { makeStyles } from "@material-ui/core/styles";
import {
  Button,
  Divider,
  Grid,
  Typography,
  Menu,
  MenuItem,
  Box,
} from "@material-ui/core";
import ArrowDownwardIcon from "@material-ui/icons/ArrowDownward";
import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward";

import * as am4core from "@amcharts/amcharts4/core";
import * as am4charts from "@amcharts/amcharts4/charts";
import am4themes_animated from "@amcharts/amcharts4/themes/animated";
import moment from "moment";
moment().format();

am4core.useTheme(am4themes_animated);
am4core.addLicense("ch-custom-attribution");

const useStyles = makeStyles((theme) => ({
  divider: { marginBottom: 24 },
  section: { paddingLeft: 4, paddingRight: 4, marginBottom: 18 },
  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 initialState = {
  mouseX: null,
  mouseY: null,
};

const Dashboard = (props) => {
  const styles = useStyles();

  const [graph, setGraph] = useState({});

  const [startInterval, setStartInterval] = useState("ytd");
  const [endInterval, setEndInterval] = useState("all");

  const parseInterval = (interval) => {
    if (interval == "1w") {
      return moment().subtract(1, "weeks");
    } else if (interval == "ytd") {
      return moment().startOf("year");
    }
  };

  const getInitialValues = () => {
    let initialChart = [];
    let startDate = moment(
      parseInterval(startInterval).format("YYYY-MM-DD")
    )._i;
    let endDate = "all";
    props.inventory.items.map((item) => {
      var itemId = item.siteLinks.stockX.id;
      window.api.stockxData("getChartData", { itemId, startDate, endDate });
    });

    if (startInterval == "1w") {
      var date = parseInterval(startInterval)
        .startOf("hour")
        .format("MM/DD/YY hh:mm A");
      for (var i = 0; i < 169; i++) {
        initialChart.push({ date: date, value: 0 });
        date = moment(date).add(1, "hour").format("MM/DD/YY hh:mm A");
      }
      return initialChart;
    } else if (startInterval == "ytd") {
      var date = parseInterval(startInterval).format("MM/DD/YY");
      var diff = moment().diff(date, "days");
      for (var i = 0; i < diff; i++) {
        initialChart.push({ date: date, value: 0 });
        date = moment(date).add(1, "day").format("MM/DD/YY");
      }
    }
    return initialChart;
  };

  //builds initial state
  useEffect(() => {
    let initialTimeframe = {};
    //uses startInterval(state) to receiveChartData and initializes chartBuilder, setting date (not yet value)
    let chartBuilder = getInitialValues();
    let count = 0;

    //called for every item in inventory
    window.api.stockxData("receiveChartData", (data) => {
      let initialValueFound = false;
      for (var i = 0; i < data.series[0].data.length; i++) {
        //if value is found on startInterval day then pushes {itemId, value} to initialTimeFrame
        if (
          data.series[0].data[i][1] != null &&
          data.series[0].data[i][1] != 0
        ) {
          //console.log(data.series[0].data[i][1]);
          initialTimeframe[data.itemId] = data.series[0].data[i][1];
          initialValueFound = true;
          if (
            Object.keys(initialTimeframe).length == props.inventory.items.length
          ) {
            console.log(initialTimeframe);
            getPriceUpdates(initialTimeframe);
          }
          break;
        }
      }
      //if no value is found, then call getProductData
      if (!initialValueFound) {
        window.api.stockxData("getProductData", data.itemId);
      }
    });

    //called if no value was found in receiveChartData, gets current price of item for initial value
    window.api.stockxData("receiveProductData", (data) => {
      //console.log(data, "receiveproductdata");
      initialTimeframe[data.Product.id] =
        (data.Product.market.lowestAskFloat +
          data.Product.market.highestBidFloat) /
        2;
      if (
        Object.keys(initialTimeframe).length == props.inventory.items.length
      ) {
        console.log(initialTimeframe);
        getPriceUpdates(initialTimeframe);
      }
    });

    const getPriceUpdates = (initialData) => {
      // var sum = (r, a) => r.map((b, i) => a[1] + b);
      // let totalInitialValue = initialTimeframe.reduce(sum)[1];

      chartBuilder.forEach((arr) => {
        let initialValue = 0;
        props.inventory.items.map((item) => {
          if (moment(item.dateObtained).isSameOrBefore(moment(arr.date))) {
            initialValue += initialTimeframe[item.siteLinks.stockX.id];
          }
        });
        arr.value = initialValue;
      });

      Object.keys(initialData).map((arr) => {
        window.api.stockxData("getSaleData", arr);
      });
    };

    window.api.stockxData("receiveSaleData", (data) => {
      const prodData = JSON.parse(data);
      let dateFound, prevValue, initialValue;
      prevValue = initialValue = 0;
      let itemUpdates = {};

      initialValue = initialTimeframe[prodData[0].skuUuid];

      for (var i = 0; i < prodData.length; i++) {
        if (
          moment(prodData[i].createdAt).isAfter(parseInterval(startInterval))
        ) {
          if (startInterval == "1w") {
            dateFound = moment(prodData[i].createdAt)
              .add(1, "hour")
              .startOf("hour")
              .format("MM/DD/YY hh:mm A");
          } else if (startInterval == "ytd") {
            dateFound = moment(prodData[i].createdAt).format("MM/DD/YY");
          }
          //prodData[i].amount
          if (itemUpdates[dateFound]) {
            itemUpdates[dateFound] = [
              ...itemUpdates[dateFound],
              prodData[i].amount,
            ];
          } else {
            itemUpdates[dateFound] = [prodData[i].amount];
          }
        }
      }

      chartBuilder.forEach((dataset, idx) => {
        const average = (arr) => arr.reduce((a, b) => a + b) / arr.length;

        if (itemUpdates[dataset.date]) {
          var newValue =
            average(itemUpdates[dataset.date]) +
            chartBuilder[idx].value -
            initialValue;

          chartBuilder[idx].value = prevValue = newValue;
        } else {
          if (prevValue != 0) {
            chartBuilder[idx].value = prevValue;
          }
        }
      });
      count++;
      if (count == props.inventory.items.length) {
        setGraph(chartBuilder);
        console.log(chartBuilder, "Final");
      }
    });
  }, [startInterval]);

  const [priceHeader, setPriceHeader] = useState(0);
  const [subPriceHeader, setSubPriceHeader] = useState(0);
  const resetPriceHeader = () => {
    setPriceHeader(graph[0].value.toFixed(2));
  };

  const chart = useRef(null);
  useEffect(() => {
    if (graph.length > 0) {
      resetPriceHeader();
      setSubPriceHeader(graph[0].value.toFixed(2));
    }
    let x = am4core.create("chartdiv", am4charts.XYChart);

    x.data = graph;

    let dateAxis = x.xAxes.push(new am4charts.DateAxis());
    dateAxis.dateFormats.setKey("hour", "MM/dd/yy hh:mm a");
    dateAxis.tooltipDateFormat = "MM/dd/yy, hh:mm a";
    dateAxis.renderer.grid.template.strokeWidth = 0;
    dateAxis.renderer.labels.template.disabled = true;
    dateAxis.tooltip.events.on("hidden", (e) => {
      resetPriceHeader();
    });

    let valueAxis = x.yAxes.push(new am4charts.ValueAxis());
    valueAxis.tooltip.disabled = true;
    valueAxis.renderer.grid.template.strokeWidth = 0;
    valueAxis.renderer.labels.template.disabled = true;

    let series = x.series.push(new am4charts.LineSeries());
    series.dataFields.dateX = "date";
    series.dataFields.valueY = "value";
    series.tooltip.disabled = true;

    series.segments.template.interactionsEnabled = true;
    series.events.on("tooltipshownat", (e) => {
      setPriceHeader(e.dataItem.dataContext.value.toFixed(2));
    });
    series.stroke = am4core.color("#1976d2");
    x.cursor = new am4charts.XYCursor();
    x.cursor.lineY.disabled = true;
    chart.current = x;

    return () => {
      x.dispose();
    };
  }, [graph]);

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

  return (
    <React.Fragment>
      <div className={styles.root}>
        <Grid container alignItems="center" direction="row">
          <Grid item xs={6}>
            <Box fontSize="h5.fontSize">Inventory value</Box>
            <Box fontSize="h4.fontSize" letterSpacing={1} fontWeight={500}>
              ${priceHeader}
            </Box>
            <Box
              fontSize="subtitle1.fontSize"
              letterSpacing={1}
              style={{
                display: "flex",
                alignItems: "center",
                color:
                  priceHeader - subPriceHeader >= 0 ? "#00c853" : "#d50000",
              }}
            >
              <span style={{ marginTop: 7 }}>
                {priceHeader - subPriceHeader >= 0 ? (
                  <ArrowUpwardIcon fontSize="small" />
                ) : (
                  <ArrowDownwardIcon fontSize="small" />
                )}
              </span>
              ${(priceHeader - subPriceHeader).toFixed(2)} (
              {(((priceHeader - subPriceHeader) / priceHeader) * 100).toFixed(
                2
              )}
              %)
            </Box>
          </Grid>
          <Grid item container xs justify="flex-end">
            <Button
              variant="contained"
              color="primary"
              onClick={() => {
                console.log("change graph");
              }}
            >
              Change Graph
            </Button>
          </Grid>
          <Grid item xs={12} style={{ paddingBottom: "24px" }}>
            <div id="chartdiv" style={{ width: "100%", height: "250px" }}></div>
          </Grid>
          <Grid item xs={12}>
            <Button
              variant="contained"
              color="primary"
              onClick={() => {
                setStartInterval("1w");
              }}
            >
              1W
            </Button>

            <Button
              variant="contained"
              color="primary"
              onClick={() => {
                setStartInterval("ytd");
              }}
            >
              YTD
            </Button>
          </Grid>
        </Grid>
      </div>
    </React.Fragment>
  );
};

const mapStateToProps = (state, props) => ({
  inventory: state.inventory,
  sales: state.sales,
});

export default connect(mapStateToProps)(Dashboard);