data-dashboard
Version:
A data-driven dashboard console for report widgets
124 lines (117 loc) • 4.47 kB
text/typescript
// DataDashboard ~~ MIT License
// Widget controller
// Imports
import { Chart, ChartConfiguration, ChartDataset, ChartTypeRegistry, Point, TooltipItem } from 'chart.js';
import { fetchJson } from 'fetch-json';
import { DataTable } from 'simple-datatables';
// Modules
import { webAppLookup } from '../../modules/lookup.js';
import { webAppUtil } from '../../modules/util.js';
class DT extends DataTable {}
declare namespace simpleDatatables { class DataTable extends DT {} } //eslint-disable-line @typescript-eslint/no-namespace
// {
// root: {
// stations: {
// station: [
// {
// name: '12th St. Oakland City Center',
// abbr: '12TH',
// gtfs_latitude: '37.803768',
// gtfs_longitude: '-122.271450',
// address: '1245 Broadway',
// city: 'Oakland',
// county: 'alameda',
// state: 'CA',
// zipcode: '94612'
// },
// ...
// Types
type Station = {
name: string,
abbr: string,
gtfs_latitude: string,
gtfs_longitude: string,
city: string,
county: string,
};
type RawData = {
root: { stations: { station: Station[] } },
};
type DataPoint = {
x: number,
y: number,
label: string,
};
type Point$ = Point & { label: string }; //patch library type
const webAppWidgetTransBartStations = {
displayDataChart(widgetElem: Element, stations: Station[]): void {
const dataset: ChartDataset = {
label: 'Geolocation',
backgroundColor: webAppLookup.chartColor.green!.value,
data: stations.map((item: Station): DataPoint => ({
x: parseFloat(item.gtfs_longitude),
y: parseFloat(item.gtfs_latitude),
label: item.abbr + ' (' + item.name + ')',
})),
};
const latLong = (item: TooltipItem<keyof ChartTypeRegistry>): string => {
// Returns a string formatted like: "37.77°N 122.42°W" (San Francisco)
const lat = item.parsed.y ?? 0;
const long = item.parsed.x ?? 0;
const fixed = (degrees: number) => Math.abs(degrees).toFixed(2);
return `${fixed(lat)}°${lat > 0 ? 'N' : 'S'} ${fixed(long)}°${long > 0 ? 'E' : 'W'}`;
};
const makeTooltip = (item: TooltipItem<keyof ChartTypeRegistry>): string =>
`${(<Point$>item.dataset.data[item.dataIndex]).label} ${latLong(item)}`;
const chartInfo: ChartConfiguration = {
type: 'scatter',
data: {
datasets: [dataset],
},
options: {
maintainAspectRatio: false,
plugins: {
title: { display: true, text: ['BART Stations', 'San Francisco Bay Area'] },
tooltip: { callbacks: { label: makeTooltip } },
},
},
};
const canvas = widgetElem.querySelector('canvas')!;
dna.dom.state(widgetElem).chart = new Chart(canvas, chartInfo);
},
displayDataTable(widgetElem: Element, stations: Station[]): void {
const tableElem = <HTMLTableElement>widgetElem.querySelector('figure table');
const dataTable = new simpleDatatables.DataTable(tableElem);
const headers = [
'Name',
'Code',
'Latitude',
'Longitude',
'City',
'County',
];
const rows = stations.map(station => [
station.name,
station.abbr,
station.gtfs_latitude,
station.gtfs_longitude,
station.city,
station.county,
]);
dataTable.insert({ headings: headers, data: rows });
dna.dom.state(widgetElem).table = dataTable;
},
show(widgetElem: Element): void {
const handleData = (data: RawData) => {
webAppUtil.spinnerStop(widgetElem);
const stations = data.root.stations.station;
webAppWidgetTransBartStations.displayDataChart(widgetElem, stations);
webAppWidgetTransBartStations.displayDataTable(widgetElem, stations);
};
const url = 'https://api.bart.gov/api/stn.aspx';
const params = { cmd: 'stns', key: 'MW9S-E7SL-26DU-VV8V', json: 'y' };
webAppUtil.spinnerStart(widgetElem);
fetchJson.get(url, params).then(handleData);
},
};
export { webAppWidgetTransBartStations };