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);