project become whole blank during import graph. Linegraph problem in Reactjs - javascript

//* This is Linegraph.js file /
import React, { useEffect, useState } from 'react'
import { Line } from 'react-chartjs-2'
import numeral from 'numeral'
const options = {
legend: {
display: false,
},
elements: {
point: {
radius: 0,
},
},
maintainAspectRatio: false,
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return numeral(tooltipItem.value).format("+0,0");
},
},
},
scales: {
xAxes: [
{
type: "time",
time: {
format: "MM/DD/YY",
tooltipFormat: "ll",
},
},
],
yAxes: [
{
gridLines: {
display: false,
},
ticks: {
callback: function (value, index, values) {
return numeral(value).format("0a");
},
},
},
],
},
};
const LineGraph = ({ casesType="cases" }) => {
const [data, setData] = useState({})
const buildChartData = (data, caseType) => {
const chartData = []
let lastPoint;
for(let date in data.cases){
if(lastPoint) {
const newPoint = {
x: date,
y: data[caseType][date] - lastPoint
}
chartData.push(newPoint)
}
lastPoint = data[caseType][date]
}
return chartData
}
useEffect(() => {
const fetchData = async () => {
fetch('https://disease.sh/v3/covid-19/historical/all?lastdays=120')
.then(response => response.json())
.then(data => {
console.log(data)
const chartData = buildChartData(data, casesType)
setData(chartData)
})
}
fetchData()
}, [casesType])
return (
<div>
{data?.length > 0 && (
<Line
options={options}
data={{
datasets: [{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data
}]
}}
/>
)}
</div>
)
}
export default LineGraph

Related

Error when trying to update chart data: 'this undefined'

I am working in Vue. My goal is create dynamic stepsize for timeline chart. Here my
TimelineChart.vue:
<template>
<div class="chart chart-timeline">
<ChartLegend
:items="legend"
:allowFilterCreation="allowFilterCreation"
#highlight="highlighted = $event"
#toggle="toggleDatasetVisibility($event)"
#apply-filter="$emit('apply-filter', $event)"
v-if="legend"/>
<div class="chart-container" v-show="hasData">
<canvas ref="chart"></canvas>
<hr> <hr> <hr> Stepsize Y
<input type="number" id ="ySteps">
<hr> Stepsize X
<input type="number" id ="xSteps">
</div>
<h2 class="subtitle is-5" v-if="!hasData">
No data
</h2>
</div>
</template>
<script>
import _ from "lodash";
import Chart from "chart.js";
import Moment from "moment";
import ChartLegend from "#/components/charts/ChartLegend";
import ChartMixin from "#/components/charts/ChartMixin";
import Deferred from "#/Deferred";
export default {
props: ["data", "multiMode", "labelConverter", "valueConverter", "allowFilterCreation"],
components: { ChartLegend },
mixins: [ChartMixin],
data() {
return {
chart: Deferred.create(),
highlighted: null
};
},
computed: {
timeline() {
return this.data ? _.map(this.data.parameter, it => new Moment(it)) : [];
},
keys() {
return this.data ? _.map(this.data.datasets, it => it.name) : [];
},
labels() {
const converter = this.labelConverter || _.identity;
return _.map(this.keys, converter);
},
datasets() {
return this.data ? _.map(this.data.datasets, it => it.data) : [];
},
datasetCount() {
return this.datasets.length;
},
legend() {
if (!this.multiMode) {
return null;
}
return _.zipWith(this.keys, this.labels, this.palette, (key, text, color) => ({
id: key,
text,
color: color.normal,
hidden: _.includes(this.hiddenDatasets, key)
}));
},
chartColors() {
const highlightIndex = this.highlighted !== null && !_.includes(this.hiddenDatasets, this.highlighted)
? _.indexOf(this.keys, this.highlighted)
: null;
const colors = [];
for (const index of _.keys(this.datasets)) {
const color = this.palette[index];
if (highlightIndex !== null) {
if (index == highlightIndex) {
colors.push({ backgroundColor: color.strongMuted, borderColor: color.strong });
} else {
colors.push({ backgroundColor: color.weakMuted, borderColor: color.weak });
}
} else {
colors.push({ backgroundColor: color.normalMuted, borderColor: color.normal });
}
}
return colors;
}
},
mounted() {
this.chart.resolve(new Chart(this.$refs.chart, {
type: "line",
data: {
datasets: [],
labels: []
},
options: {
maintainAspectRatio: false,
legend: { display: false },
scales: {
xAxes: [{
type: "time",
time: {
isoWeekday: true,
displayFormats: {
hour: "HH:mm",
day: "DD-MM",
week: "DD-MM",
month: "MM-YYYY"
}
},
tooltips: {
},
ticks: { source: "labels",
// stepSize: 2
}
}],
yAxes: [{
ticks: { min: 0, precision: 0,
stepSize: 1
}
}]
},
spanGaps: false,
tooltips: {
callbacks: {
label: item => {
const converter = this.valueConverter || _.identity;
const label = this.multiMode ? (this.labels[item.datasetIndex] + ": ") : "";
return label + converter(this.datasets[item.datasetIndex][item.index]);
}
}
}
}
}));
// method that change stepsize
const ySteps = document.getElementById('ySteps');
ySteps.addEventListener('input', (e) => {
// eslint-disable-next-line no-undef
stepSize(this.$refs.chart, e)
});
function stepSize(chart) {
this.chart.resolve.scales.yAxes.stepSize = ySteps.value;
this.$refs.chart.update()
}
watch: {
data() {
this.resetDatasetVisibility();
}
}
}
</script>
When I alter stepsize nothing happen. In debug mode I have noticed the following pattern:
When I create chart 'this.chart' is not undefined. But when I use function stepSize I have an error, that this.chart is undefined. I have read a documentation about it, but I still do not understand how to connect to instance and change the property in the chart.
this.chart inside stepSize function will not be referring to the chart object inside your data node, because stepSize is not a vue compoenent method, its a javascript function. So this wont be refering to your vue scope.
Since you are already passing your chart object as a parameter to stepSize function as stepSize(this.$refs.chart, e), you can make use of the same inside your stepSize function like
function stepSize(chart) {
chart.resolve.scales.yAxes.stepSize = ySteps.value;
chart.update();
}

How to hide the legend in chart.js in a react project?

I am trying to hide the legend of my chart created with Chart.js.
According to the official documentation (https://www.chartjs.org/docs/latest/configuration/legend.html), to hide the legend, the display property of the options.display object must be set to false.
I have tried to do it in the following way:
const options = {
legend: {
display: false,
}
};
But it doesn't work, my legend is still there. I even tried this other way, but unfortunately, without success.
const options = {
legend: {
display: false,
labels: {
display: false
}
}
}
};
This is my full code.
import React, { useEffect, useState } from 'react';
import { Line } from "react-chartjs-2";
import numeral from 'numeral';
const options = {
legend: {
display: false,
},
elements: {
point: {
radius: 1,
},
},
maintainAspectRatio: false,
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return numeral(tooltipItem.value).format("+0,000");
},
},
},
scales: {
xAxes: [
{
type: "time",
time: {
format: "DD/MM/YY",
tooltipFormat: "ll",
},
},
],
yAxes: [
{
gridLines: {
display: false,
},
ticks: {
callback: function(value, index, values) {
return numeral(value).format("0a");
},
},
},
],
},
};
const buildChartData = (data, casesType = "cases") => {
let chartData = [];
let lastDataPoint;
for(let date in data.cases) {
if (lastDataPoint) {
let newDataPoint = {
x: date,
y: data[casesType][date] - lastDataPoint
}
chartData.push(newDataPoint);
}
lastDataPoint = data[casesType][date];
}
return chartData;
};
function LineGraph({ casesType }) {
const [data, setData] = useState({});
useEffect(() => {
const fetchData = async() => {
await fetch("https://disease.sh/v3/covid-19/historical/all?lastdays=120")
.then ((response) => {
return response.json();
})
.then((data) => {
let chartData = buildChartData(data, casesType);
setData(chartData);
});
};
fetchData();
}, [casesType]);
return (
<div>
{data?.length > 0 && (
<Line
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data
},
],
}}
options={options}
/>
)}
</div>
);
}
export default LineGraph;
Could someone help me? Thank you in advance!
PD: Maybe is useful to try to find a solution, but I get 'undefined' in the text of my legend and when I try to change the text like this, the text legend still appearing as 'Undefindex'.
const options = {
legend: {
display: true,
text: 'Hello!'
}
};
As described in the documentation you linked the namespace where the legend is configured is: options.plugins.legend, if you put it there it will work:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
}
]
},
options: {
plugins: {
legend: {
display: false
}
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.0/chart.js"></script>
</body>
On another note, a big part of your options object is wrong, its in V2 syntax while you are using v3, please take a look at the migration guide
Reason why you get undefined as text in your legend is, is because you dont supply any label argument in your dataset.
in the newest versions this code works fine
const options = {
plugins: {
legend: {
display: false,
},
},
};
return <Doughnut data={data} options={options} />;
Import your options value inside the charts component like so:
const options = {
legend: {
display: false
}
};
<Line data={data} options={options} />

Can't change the time of the xAxes in the Chart.js

So I have ChartData Component. I am taking the data from an API, the data is being displayed through Chart. I have determine Format Logic, which it's the main thing is to determine the time of the data, I have 3 buttons, the thing that I am struggling to achieve is when let's say I wanna see the data of 7days when I press the button. I can see data is changing in the yAxes but I can't see time being changed on thexAxes, it's still set to hours, it should display the days.
ChartData
import React, { useRef, useEffect, useState } from "react";
import 'chartjs-adapter-moment';
import annotationPlugin from 'chartjs-plugin-annotation';
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
Chart.register(annotationPlugin);
const determineTimeFormat = (
timeFormat: string,
day: any,
week: any,
year: any
) => {
switch (timeFormat) {
case "24h":
return day;
case "7d":
return week;
case "1y":
return year;
default:
return day;
}
};
interface Props {
data: any
}
const ChartData: React.FC<Props> = ({ data }) => {
const chartCanvasRef = useRef<HTMLCanvasElement | null>(null);
const { day, week, year, detail } = data;
const [timeFormat, setTimeFormat] = useState("24h");
const [isRebuildingCanvas, setIsRebuildingCanvas] = useState(false);
useEffect(() => {
setIsRebuildingCanvas(true);
}, [timeFormat]);
useEffect(() => {
if (isRebuildingCanvas) {
setIsRebuildingCanvas(false);
}
}, [isRebuildingCanvas]);
useEffect(() => {
if (chartCanvasRef && chartCanvasRef.current && detail) {
const chartCanvas = chartCanvasRef.current
if (isRebuildingCanvas || !chartCanvas) {
return;
}
const chartInstance = new Chart(chartCanvasRef.current, {
type: "line",
data: {
datasets: [
{
label: `${detail.name} price`,
data: determineTimeFormat(timeFormat, day, week, year),
backgroundColor: "rgba(134,159,152, 1)",
borderColor: "rgba(174, 305, 194, 0.4",
},
],
},
Options
options: {
plugins: {
annotation: {
annotations: {
}
}
},
animations: {
tension: {
duration: 1000,
easing: 'linear',
from: 1,
to: 0,
loop: true
}
},
maintainAspectRatio: false,
responsive: true,
scales: {
x:
{
type: 'time',
},
},
}
});
return () => {
chartInstance.destroy();
}
}}, [day, isRebuildingCanvas,timeFormat, week, year, detail]);
Rest of the Component
return (
<div className='chart__container'>
{renderPrice()}
{isRebuildingCanvas ? undefined : (
<canvas ref={chartCanvasRef} width={250} height={250} id='myChart'></canvas>
)}
<button className='time__format' onClick={() => setTimeFormat("24h")}>24h</button>
<button className='time__format' onClick={() => setTimeFormat("7d")}>7d</button>
<button className='time__format' onClick={() => setTimeFormat("1y")}>1y</button>
</div>
);
};
export default ChartData;
I have created a similar working example without reactjs. This can be useful for you to understand. You can refer the fiddle as well. A limitation that am currently facing is if there is data more than 100 then am getting errors while updating the data, performed slicing of data upto 100 to make it working.
var coinData = {};
var fetchData = async() => {
api = "https://api.coingecko.com/api/v3";
id = "bitcoin";
var [day1, week1, year1, detail1] = await Promise.all([
fetch(api + `/coins/${id}/market_chart/?vs_currency=usd&days=1`).then(data => {
return data.json()
}),
fetch(api + `/coins/${id}/market_chart/?vs_currency=usd&days=7`).then(data => {
return data.json()
}),
fetch(api + `/coins/${id}/market_chart/?vs_currency=usd&days=365`).then(data => {
return data.json()
}),
fetch(api + `/coins/markets/?vs_currency=usd&ids=${id}`).then(data => {
return data.json()
})
]);
coinData = {
day: formatData(day1.prices).slice(0, 100),
week: formatData(week1.prices).slice(0, 100),
year: formatData(year1.prices).slice(0, 100),
detail: detail1[0]
}
}
const formatData = (data) => {
return data.map((el) => {
return {
t: el[0],
y: el[1].toFixed(2)
};
});
};
const determineTimeFormat = (timeFormat) => {
switch (timeFormat) {
case "24h":
return coinData.day;
case "7d":
return coinData.week;
case "1y":
return coinData.year;
default:
return coinData.day;
}
};
var chartInstance;
fetchData().then(() => {
/* console.log(coinData); */
var ctx = document.getElementById('chartJSContainer');
chartInstance = new Chart(ctx, {
type: "line",
labels: false,
data: {
datasets: [{
label: `${coinData.detail.name} price`,
data: determineTimeFormat('1d'),
parsing: {
yAxisKey: 'y',
xAxisKey: 't',
},
backgroundColor: "rgba(134,159,152, 1)",
borderColor: "rgba(174, 305, 194, 0.4)"
}],
},
options: {
scales: {
x: {
ticks: {
source: "data"
},
type: 'time',
time: {
unit: "day"
}
}
},
}
});
});
function setTimeFormat(format) {
Chart.instances[0].data.datasets[0].data = determineTimeFormat(format);
Chart.instances[0].update();
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.3.1/dist/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment#1.0.0/dist/chartjs-adapter-moment.min.js"></script>
<body>
<button className='time__format' onclick="setTimeFormat( '24h')">24h</button>
<button className='time__format' onclick="setTimeFormat( '7d')">7d</button>
<button className='time__format' onclick="setTimeFormat( '1y')">1y</button><br><br>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>

Display API data in line charts dynamically

I was having trouble getting parseInt(res.data[0]['97'].value to render accurate data from my API call. [0] being the index, and ['97'] being the ID from my API Response. I've updated my code to accept props of {jobId} from my App.js sending the ID to LineChart.js. This allows me to access this ID when it is in a Cycle along with the Title, so that I can say "whatever Title is displaying, get the ID and in LineCharts, match that ID to the data needed for the Line chart."
Old LineChart.js
import React, { useState, useEffect } from "react";
import { Scatter } from "react-chartjs-2";
const TotalLineChart = () => {
const [chartData, setChartData] = useState({});
// const [designHours, setDesignHours] = useState([]);
// const [designAmount, setDesignAmount] = useState([]);
// const [subRoughHours, setSubRoughHours] = useState([]);
// const [subRoughAmount, setSubRoughAmount] = useState([]);
// const [roughHours, setRoughHours] = useState([]);
// const [roughAmount, setRoughAmount] = useState([]);
// const [finishHours, setFinishHours] = useState([]);
// const [finishAmount, setFinishAmount] = useState([]);
// const [closeHours, setCloseHours] = useState([]);
// const [closeAmount, setCloseAmount] = useState([]);
// const [actualHours, setActualHours] = useState([]);
// const [actualAmount, setActualAmount] = useState([]);
const chart = () => {
let designHours = [];
let designAmount = [];
let subRoughHours = [];
let subRoughAmount = [];
let roughHours = [];
let roughAmount = [];
let finishHours = [];
let finishAmount = [];
let closeHours = [];
let closeAmount = [];
let actualHours = [];
let actualAmount = [];
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXX_XXX_XXXXXXXXXX',
'Content-Type': 'application/json'
};
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"where": "{40.CT. 'In Progress'}","sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => response.json())
.then(res => {
console.log(res);
const designHours = parseInt(res.data[0]['89'].value, 10);
const designAmount = parseInt(res.data[0]['91'].value, 10);
const subRoughHours = parseInt(res.data[0]['93'].value, 10);
const subRoughAmount = parseInt(res.data[0]['95'].value, 10);
const roughHours = parseInt(res.data[0]['97'].value, 10);
const roughAmount = parseInt(res.data[0]['99'].value, 10);
const finishHours = parseInt(res.data[0]['105'].value, 10);
const finishAmount = parseInt(res.data[0]['107'].value, 10);
const closeHours = parseInt(res.data[0]['477'].value, 10);
const closeAmount = parseInt(res.data[0]['480'].value, 10);
const actualHours = parseInt(res.data[0]['479'].value, 10);
const actualAmount = parseInt(res.data[0]['224'].value, 10);
}
setChartData({
type: 'scatter',
data: {
datasets: [
{
data: [
{x: designHours, y: designAmount },
{x: subRoughHours, y: subRoughAmount },
{x: roughHours, y: roughAmount },
{x: finishHours, y: finishAmount },
{x: closeHours, y: closeAmount }
],
backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
}
});
})
.catch(err => {
console.log(err);
});
console.log(designHours, designAmount, subRoughHours, subRoughAmount, finishHours, finishAmount, closeHours, closeAmount, actualHours, actualAmount);
};
useEffect(() => {
chart();
}, []);
return (
<div className="App">
<div>
<Scatter
data={chartData}
options={{
responsive: true,
title: { text: "", display: true },
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
maxTicksLimit: 20,
beginAtZero: true
},
gridLines: {
display: true
}
}
],
xAxes: [
{
gridLines: {
display: true
}
}
]
}
}}
/>
</div>
</div>
);
};
export default TotalLineChart;
Fixed/Updated LineChart.js
import React, { useState, useEffect } from "react";
import { Scatter } from "react-chartjs-2";
// import jobId from '../TitleCycle';
// import Title from '../header/Title';
const TotalLineChart = (props) => {
const {jobId} = props;
console.log(jobId)
const [chartData, setChartData] = useState({});
const chart = () => {
let designHours = [];
let designAmount = [];
let subRoughHours = [];
let subRoughAmount = [];
let roughHours = [];
let roughAmount = [];
let finishHours = [];
let finishAmount = [];
let closeHours = [];
let closeAmount = [];
let actualHours = [];
let actualAmount = [];
let headers = {
"QB-Realm-Hostname": "XXXXXXXXX.quickbase.com",
"User-Agent": "FileService_Integration_V2.1",
"Authorization": "QB-USER-TOKEN XXXXXXXXXXXX",
"Content-Type": "application/json"
};
const body = {
from: "bpz99ram7",
select: [
3,
6,
80,
81,
82,
83,
86,
84,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
101,
103,
104,
105,
106,
107,
109,
111,
113,
115,
120,
123,
224,
225,
226,
227,
228,
229,
230,
231,
477,
479,
480,
481
],
where: `{3.EX. ${jobId}}`,
sortBy: [{ fieldId: 6, order: "ASC" }],
groupBy: [{ fieldId: 40, grouping: "equal-values" }],
options: { skip: 0, compareWithAppLocalTime: false }
};
fetch("https://api.quickbase.com/v1/records/query", {
method: "POST",
headers: headers,
body: JSON.stringify(body)
})
.then((response) => response.json())
.then((res) => {
// console.log(res);
Object.keys(res.data).map(jobId => {
const designHours = parseInt(res.data[jobId]['88'].value, 10);
const designAmount = parseInt(res.data[jobId]['91'].value, 10);
const subRoughHours = parseInt(res.data[jobId]['92'].value, 10);
const subRoughAmount = parseInt(res.data[jobId]['95'].value, 10);
const roughHours = parseInt(res.data[jobId]['96'].value, 10);
const roughAmount = parseInt(res.data[jobId]['98'].value, 10);
const finishHours = parseInt(res.data[jobId]['104'].value, 10);
const finishAmount = parseInt(res.data[jobId]['107'].value, 10);
const closeHours = parseInt(res.data[jobId]['477'].value, 10);
const closeAmount = parseInt(res.data[jobId]['480'].value, 10);
const actualHours = parseInt(res.data[jobId]['479'].value, 10);
const actualAmount = parseInt(res.data[jobId]['224'].value, 10);
setChartData({
type: 'scatter',
datasets: [
{
label: 'TOTAL',
data: [
{ x: designHours, y: designAmount },
{ x: subRoughHours, y: subRoughAmount },
{ x: roughHours, y: roughAmount },
{ x: finishHours, y: finishAmount },
{ x: closeHours, y: closeAmount }
],
borderWidth: 2,
borderColor: '#4183c4',
backgroundColor: '#4183c4',
tension: 0.8,
spanGaps: true,
lineTension: 0.5,
showLine: true,
fill: false,
showTooltip: false,
pointBorderWidth: 1
},
{
label: 'ACTUALS',
data: [{ x: actualHours, y: actualAmount }],
fill: false,
borderColor: '#e34747',
backgroundColor: '#e34747',
borderWidth: 3,
showTooltip: false
}
],
options: {
showAllTooltips: true,
enabled: true,
maintainAspectRatio: false,
legend: {
display: true
}
}
})
setChartData.addData(chart, label, data); {
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
}
setChartData.removeData(chart); {
chart.data.labels.pop();
chart.data.datasets.forEach((dataset) => {
dataset.data.pop();
});
chart.update();
}
})
})
.catch((err) => {
console.log(err);
});
console.log(
designHours,
designAmount,
subRoughHours,
subRoughAmount,
roughHours,
roughAmount,
finishHours,
finishAmount,
closeHours,
closeAmount,
actualHours,
actualAmount
);
};
useEffect(() => {
chart();
}, []);
return (
<div className="App">
<div>
<Scatter
data={chartData}
options={{
responsive: true,
title: { text: "Total Project", display: true },
scales: {
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Amount'
},
ticks: {
autoSkip: true,
maxTicksLimit: 10,
beginAtZero: true
},
gridLines: {
display: true
}
}
],
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Hours'
},
gridLines: {
display: true
}
}
],
},
}}
/>
</div>
</div>
);
};
export default TotalLineChart;
NOTE how I've added ${jobId} to the API Body, as well as replacing the manually inputted Index in the parseInt()
App.js
import React, { useEffect, useState } from "react";
import './App.css'
import Title from './components/header/Title'
import TotalLineChart from './components/charts/TotalLineChart'
import RadiantLineChart from './components/charts/RadiantLineChart'
import PlumbingLineChart from './components/charts/PlumbingLineChart'
import SnowmeltLineChart from './components/charts/SnowmeltLineChart'
import HVACLineChart from './components/charts/HVACLineChart'
import GasPipeLineChart from './components/charts/GasPipeLineChart'
import FixturesLineChart from './components/charts/FixturesLineChart'
// import TitleCycle from './components/TitleCycle'
// import Logo from './components/Logo';
let headers = {
"QB-Realm-Hostname": "XXXXXXXX.quickbase.com",
"User-Agent": "FileService_Integration_V2.1",
"Authorization": "QB-USER-TOKEN XXXXXXXX",
"Content-Type": "application/json"
};
function App() {
const [allData, setAllData] = useState([]);
const [index, setIndex] = useState(0);
// Fetch all data, all jobs
useEffect(() => {
function fetchData() {
let body = {
from: "bpz99ram7",
select: [3, 6, 40],
where: "{40.CT. 'In Progress'}",
sortBy: [{ fieldId: 6, order: "ASC" }],
groupBy: [{ fieldId: 40, grouping: "equal-values" }],
options: { skip: 0, top: 0, compareWithAppLocalTime: false },
};
fetch("https://api.quickbase.com/v1/records/query", {
method: "POST",
headers: headers,
body: JSON.stringify(body),
})
.then((response) => response.json())
.then(({ data }) => setAllData(data));
}
fetchData();
}, []);
// Cycle through the jobIds and indexes
useEffect(() => {
const timerId = setInterval(
() => setIndex((i) => (i + 1) % allData.length),
5000 // 5 seconds.
);
return () => clearInterval(timerId);
}, [allData]);
console.log(allData)
console.log(index)
// Calculate info based on index
const jobId = allData[index]?.['3']?.value || 291; // Default 291
const title = allData[index]?.['6']?.value || 'Default Title';
console.log(jobId)
return (
<div>
{/* <div className="flexbox-container">
<div className="Logo">
{/* <Logo /> */}
{/* </div> */}
<div className="App">
<Title title = {title}/>
</div>
<div className="TopChart">
<TotalLineChart jobId = {jobId}/>
</div>
<div className="FirstRowContainer">
<RadiantLineChart jobId = {jobId}/>
<PlumbingLineChart jobId = {jobId}/>
<FixturesLineChart jobId = {jobId}/>
</div>
<div className="SecondRowContainer">
<SnowmeltLineChart jobId = {jobId}/>
<HVACLineChart jobId = {jobId}/>
<GasPipeLineChart jobId = {jobId}/>
</div>
</div>
);
}
export default App;
App.js takes the field ID and cycles it along with the Title in a duration of 5 seconds each, then sends that data to LineChart.js to be used at the same time with corresponding data.
UPDATE: Able to display dynamically by using props with IDs needed, and using those in each api call to get specific data based on the Title Displaying:
App.js:
import React, { useEffect, useState } from "react";
import './App.css'
import Title from './components/header/Title'
import TotalLineChart from './components/charts/TotalLineChart'
import RadiantLineChart from './components/charts/RadiantLineChart'
import PlumbingLineChart from './components/charts/PlumbingLineChart'
import SnowmeltLineChart from './components/charts/SnowmeltLineChart'
import HVACLineChart from './components/charts/HVACLineChart'
import GasPipeLineChart from './components/charts/GasPipeLineChart'
import FixturesLineChart from './components/charts/FixturesLineChart'
// import TitleCycle from './components/TitleCycle'
// import Logo from './components/Logo';
let headers = {
"QB-Realm-Hostname": "XXXXXXXXX.quickbase.com",
"User-Agent": "FileService_Integration_V2.1",
"Authorization": "QB-USER-TOKEN XXXXXXXXX",
"Content-Type": "application/json",
"Retry-After": 120000
};
function App() {
const [allData, setAllData] = useState([]);
const [index, setIndex] = useState(0);
// Fetch all data, all jobs
useEffect(() => {
function fetchData() {
let body = {
from: "bpz99ram7",
select: [3, 6, 40],
where: "{40.CT. 'In Progress'}",
sortBy: [{ fieldId: 6, order: "ASC" }],
groupBy: [{ fieldId: 40, grouping: "equal-values" }],
options: { skip: 0, top: 0, compareWithAppLocalTime: false },
};
fetch("https://api.quickbase.com/v1/records/query", {
method: "POST",
headers: headers,
body: JSON.stringify(body),
})
.then((response) => response.json())
.then(({ data }) => setAllData(data));
}
fetchData();
}, []);
// Cycle through the jobIds and indexes
useEffect(() => {
const timerId = setInterval(
() => setIndex((i) => (i + 1) % allData.length),
5000 // 5 seconds.
);
return () => clearInterval(timerId);
}, [allData]);
// console.log(allData)
// console.log(index)
// Calculate info based on index
const jobId = allData[index]?.['3']?.value || '291'; // Default 291
const title = allData[index]?.['6']?.value || 'Default Title';
// console.log(jobId)
return (
<div>
{/* <div className="flexbox-container">
<div className="Logo">
{/* <Logo /> */}
{/* </div> */}
<div className="App">
<Title title = {title} />
</div>
<div className="TopChart">
<TotalLineChart jobId = {jobId} />
</div>
<div className="FirstRowContainer">
{/* <RadiantLineChart jobId = {jobId} /> */}
<PlumbingLineChart jobId = {jobId} />
<FixturesLineChart jobId = {jobId} />
</div>
<div className="SecondRowContainer">
<SnowmeltLineChart jobId = {jobId} />
<HVACLineChart jobId = {jobId} />
<GasPipeLineChart jobId = {jobId} />
</div>
</div>
);
}
export default App;
LineChart.js
import React, { useState, useEffect } from "react";
import { Scatter } from "react-chartjs-2";
// import jobId from '../TitleCycle';
// import Title from '../header/Title';
function TotalLineChart(props) {
const { jobId } = props;
// console.log(`${jobId}`)
const [chartData, setChartData] = useState({});
const chart = () => {
let designHours = [];
let designAmount = [];
let subRoughHours = [];
let subRoughAmount = [];
let roughHours = [];
let roughAmount = [];
let finishHours = [];
let finishAmount = [];
let closeHours = [];
let closeAmount = [];
let actualHours = [];
let actualAmount = [];
let headers = {
"QB-Realm-Hostname": "XXXXXXXX.quickbase.com",
"User-Agent": "FileService_Integration_V2.1",
"Authorization": "QB-USER-TOKEN XXXXXXXX",
"Content-Type": "application/json",
"x-ratelimit-reset": 10000,
"Retry-After": 30000
};
// useEffect(() => {
// function fetchData() {
const body = {
from: "bpz99ram7",
select: [
3,
88,
91,
92,
95,
96,
98,
104,
107,
224,
477,
479,
480,
],
where: `{3.EX. ${ jobId }}`,
sortBy: [{ fieldId: 6, order: "ASC" }],
groupBy: [{ fieldId: 40, grouping: "equal-values" }],
options: { skip: 0, compareWithAppLocalTime: false }
};
fetch("https://api.quickbase.com/v1/records/query", {
method: "POST",
headers: headers,
body: JSON.stringify(body)
})
// }
// fetchData();
// }, [])
.then((response) => response.json())
.then((res) => {
// console.log(res);
Object.keys(res.data).map(jobId => {
designHours = parseInt(res.data[jobId]['88'].value, 10);
designAmount = parseInt(res.data[jobId]['91'].value, 10);
subRoughHours = parseInt(res.data[jobId]['92'].value, 10);
subRoughAmount = parseInt(res.data[jobId]['95'].value, 10);
roughHours = parseInt(res.data[jobId]['96'].value, 10);
roughAmount = parseInt(res.data[jobId]['98'].value, 10);
finishHours = parseInt(res.data[jobId]['104'].value, 10);
finishAmount = parseInt(res.data[jobId]['107'].value, 10);
closeHours = parseInt(res.data[jobId]['477'].value, 10);
closeAmount = parseInt(res.data[jobId]['480'].value, 10);
actualHours = parseInt(res.data[jobId]['479'].value, 10);
actualAmount = parseInt(res.data[jobId]['224'].value, 10);
setChartData({
type: 'scatter',
redraw: true,
datasets: [
{
label: 'TOTAL',
data: [
{ x: designHours, y: designAmount },
{ x: subRoughHours, y: subRoughAmount },
{ x: roughHours, y: roughAmount },
{ x: finishHours, y: finishAmount },
{ x: closeHours, y: closeAmount }
],
borderWidth: 2,
borderColor: '#4183c4',
backgroundColor: '#4183c4',
tension: 0.8,
spanGaps: true,
lineTension: 0.5,
showLine: true,
fill: false,
showTooltip: false,
pointBorderWidth: 1
},
{
label: 'ACTUALS',
data: [{ x: actualHours, y: actualAmount }],
fill: false,
borderColor: '#e34747',
backgroundColor: '#e34747',
borderWidth: 3,
showTooltip: false
}
],
options: {
showAllTooltips: true,
enabled: true,
maintainAspectRatio: false,
legend: {
display: true
}
}
})
})
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
chart();
}, []);
return (
<div className="App">
<div>
<Scatter
// ref={(reference) => this.chartReference = reference }
data={chartData}
options={{
title: { text: "Total Project", display: false },
scales: {
yAxes: [
{
scaleLabel: {
display: true,
labelString: '$ AMOUNT'
},
ticks: {
autoSkip: true,
maxTicksLimit: 10,
beginAtZero: true
},
gridLines: {
display: true
}
}
],
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'HOURS'
},
gridLines: {
display: true
}
}
],
},
}}
/>
</div>
</div>
);
};
export default TotalLineChart;
You are specifying all kind of weird radixes for your parseInt method, I assume all of your values are just base 10, even if not with a quick test the radix argument only goes up till 36, so if you remove that it should work.
Also you are pushing the same value to a lot of different arrays, seems like you need to specify the value in the dataObject you want to push and not just value, otherwise 1 array would be enough.

how to resolve the problem that hovering mouse on chart will appear old data/charts?

I used react hooks useEffect for rendering a chart from chart.js to canvas. However, I found problem of old chart showing when I hover my mouse on the chart. From the sources I found online I realized the problem might be able to solve by chart.destroy(), but I just do not know where and how to use it in my case of code. I have attached a snip of my code here, hope anybody can help me out.
import React, { useEffect } from 'react';
import Chart from 'chart.js';
import { Card } from 'components/Card';
import { localDateTime, dateFilter } from 'helpers';
const red = '#644DFF';
const purple = '#F73F64';
const DailyTrafficCard = (props) => {
const { store, capacity, data, setVar} = props;
const lastSevenDays = Array(7)
.fill()
.map((_, i) => {
const localdate = localDateTime(store);
return localdate()
.subtract(i, 'day')
.format('YYYY-MM-DD[T]07:00:00[Z]');
});
useEffect(() => {
const ctx = document && document.querySelector('#daily-traffic-chart');
if (!ctx) {
return;
}
const bar = new Chart(ctx, {
type: 'bar',
data: {
labels: [],
datasets: [{
data: data[data.length-1],
barThickness: 13,
backgroundColor: (ctx) => {
const idx = ctx && ctx.dataIndex;
const val = ctx && ctx.dataset && ctx.dataset.data && ctx.dataset.data[idx];
return val < 40 ? purple : red;
}
}]
},
options: {
intersect: false,
legend: {
display: false,
},
scales: {
xAxes: [{
type: 'time',
offset: true,
time: {
unit: 'hour',
displayFormats: {
hour: 'HH',
},
},
ticks: {},
gridLines: {
display: false,
},
}],
yAxes: [{
gridLines: {
display: true,
},
ticks: {
beginAtZero: true,
min: 0,
max: capacity,
stepSize: 5
},
}],
},
}
});
}, [data,capacity]);
const handleOnChange = (event) => {
setVar(event.target.value);
}
return (
<Card
classname="DailyTrafficCard"
icon={<span><i className="icon-user"/></span>}
title={<h3>Daily Traffic Analytics</h3>}>
<div className="daily">
<div className="daily-head p-4 text-center">
<select className="py-2 px-3" onChange={handleOnChange}>
{lastSevenDays.map(date => (
<option key={date} value={date}>{dateFilter(date, 'dddd')}</option>
))}
</select>
</div>
<div className="px-8">
{data && data.length > 0 && (
<canvas width="250" id="daily-traffic-chart"></canvas>
)}
</div>
</div>
</Card>
)
}
export {
DailyTrafficCard
}

Categories

Resources