data-dashboard
Version:
A data-driven dashboard console for report widgets
185 lines (177 loc) • 6.87 kB
text/typescript
// DataDashboard ~~ MIT License
// Widget controller
// Imports
import { Chart, ChartConfiguration } from 'chart.js';
import { fetchJson } from 'fetch-json';
// Modules
import { webAppUtil } from '../../modules/util';
// {
// MRData: {
// xmlns: "http://ergast.com/mrd/1.4",
// series: "f1",
// url: "http://ergast.com/api/f1/2018/1/results.json",
// limit: "30",
// offset: "0",
// total: "20",
// RaceTable: {
// season: "2018",
// round: "1",
// Races: [
// {
// season: "2018",
// round: "1",
// url: "https://en.wikipedia.org/wiki/2018_Australian_Grand_Prix",
// raceName: "Australian Grand Prix",
// Circuit: {
// circuitId: "albert_park",
// url: "http://en.wikipedia.org/wiki/Melbourne_Grand_Prix_Circuit",
// circuitName: "Albert Park Grand Prix Circuit",
// Location: {
// lat: "-37.8497",
// long: "144.968",
// locality: "Melbourne",
// country: "Australia"
// }
// },
// date: "2018-03-26",
// time: "05:00:00Z",
// Results: [
// {
// number: "5",
// position: "1",
// positionText: "1",
// points: "25",
// Driver: {
// driverId: "vettel",
// permanentNumber: "5",
// code: "VET",
// url: "http://en.wikipedia.org/wiki/Sebastian_Vettel",
// givenName: "Sebastian",
// familyName: "Vettel",
// dateOfBirth: "1987-07-03",
// nationality: "German"
// },
// Constructor: {
// constructorId: "ferrari",
// url: "http://en.wikipedia.org/wiki/Scuderia_Ferrari",
// name: "Ferrari",
// nationality: "Italian"
// },
// grid: "2",
// laps: "57",
// status: "Finished",
// Time: {
// millis: "5051672",
// time: "1:24:11.672"
// },
// FastestLap: {
// rank: "3",
// lap: "53",
// Time: {
// time: "1:26.638"
// },
// AverageSpeed: {
// units: "kph",
// speed: "220.351"
// }
// }
// },
// ...
// ...
// const results = [
// { Driver: { nationality: 'German' }, Constructor: { nationality: 'Italian' } },
// { Driver: { nationality: 'German' }, Constructor: { nationality: 'French' } },
// ];
// Types
type Race = {
season: string,
raceName: string,
round: string,
Results: {
Driver: { nationality: string },
Constructor: { nationality: string },
}[],
};
type RawData = {
MRData: { RaceTable: { Races: Race[] } },
};
type TotalsMap = {
[nationality: string]: {
nationality: string,
numDrivers: number,
numConstructors: number,
},
};
const webAppWidgetTransF1TopCountries = {
displayDataChart(widgetElem: Element, race: Race): void {
// <web-app-widget-body class=trans-f1-top-countries>
// <figure>
// <canvas></canvas>
// </figure>
// <figure>
// <canvas></canvas>
// </figure>
// <figure>
// <canvas></canvas>
// </figure>
// </web-app-widget-body>
const topFinishes = 10;
const title = 'Nationalities of Top F1 Drivers and Constructors';
const subtitle = `${race.season} ${race.raceName} top ${topFinishes} finishes`;
const round = Number(race.round);
const addResult = (totalsMap: TotalsMap, result: Race['Results'][0]) => {
const setupNationality = (nationality: string) => {
if (!totalsMap[nationality])
totalsMap[nationality] = {
nationality: nationality,
numDrivers: 0,
numConstructors: 0,
};
};
[result.Driver.nationality, result.Constructor.nationality].forEach(setupNationality);
totalsMap[result.Driver.nationality]!.numDrivers++;
totalsMap[result.Constructor.nationality]!.numConstructors++;
return totalsMap;
};
const totals = race.Results.slice(0, topFinishes).reduce(addResult, {});
const data = Object.keys(totals).map(nationality => totals[nationality]!);
data.sort((a, b) => a.numDrivers + a.numConstructors - b.numDrivers - b.numConstructors ||
a.nationality.localeCompare(b.nationality));
const datasets = [
{ label: 'Driver', data: data.map(item => item.numDrivers) },
{ label: 'Constructor', data: data.map(item => item.numConstructors) },
];
const chartInfo = <ChartConfiguration>{
type: 'bar',
data: {
labels: data.map(item => item.nationality),
datasets: webAppUtil.addChartColors(datasets),
},
options: {
maintainAspectRatio: false,
scales: { x: { stacked: true }, y: { stacked: true } },
plugins: {
title: { display: true, text: [title, subtitle] },
},
},
};
const canvas = widgetElem.querySelectorAll('canvas')[round - 1]!;
dna.dom.state(widgetElem).chart = new Chart(canvas, chartInfo);
},
show(widgetElem: Element): void {
const raceYear = new Date().getFullYear() - 1;
const handleData = (data: RawData) => {
webAppUtil.spinnerStop(widgetElem);
const race = data.MRData.RaceTable.Races[0]!;
webAppWidgetTransF1TopCountries.displayDataChart(widgetElem, race);
};
webAppUtil.spinnerStart(widgetElem);
const display = (canvas: Element, index: number) => {
const round = index + 1;
const url = `https://ergast.com/api/f1/${raceYear}/${round}/results.json`;
fetchJson.get(url).then(handleData);
};
widgetElem.querySelectorAll('web-app-widget-body >figure >canvas').forEach(display);
},
};
export { webAppWidgetTransF1TopCountries };