Skip to content
Snippets Groups Projects
Commit f490d431 authored by Remco Tukker's avatar Remco Tukker
Browse files

factored out filter element and basic application to the budget page

parent 32d49616
No related branches found
No related tags found
1 merge request!2Feature/comp 116 initial filter mechanism
import React, { ReactElement } from "react"; import React, {ReactElement, useState} from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Landing from "./pages/Landing"; import Landing from "./pages/Landing";
import ExternalPageNavBar from "./components/global/ExternalPageNavBar"; import ExternalPageNavBar from "./components/global/ExternalPageNavBar";
...@@ -8,9 +8,13 @@ import AnnualReport from "./pages/AnnualReport"; ...@@ -8,9 +8,13 @@ import AnnualReport from "./pages/AnnualReport";
import CompendiumData from "./pages/CompendiumData"; import CompendiumData from "./pages/CompendiumData";
import FundingSourcePage from "./pages/FundingSource"; import FundingSourcePage from "./pages/FundingSource";
import ChargingStructurePage from "./pages/ChargingStructure"; import ChargingStructurePage from "./pages/ChargingStructure";
import {FilterSelection} from "./Schema";
function App(): ReactElement { function App(): ReactElement {
const [filterSelection, setFilterSelection] = useState<FilterSelection>({selectedYears: [], selectedNrens: []});
return ( return (
<div> <div>
<Router> <Router>
...@@ -18,9 +22,15 @@ function App(): ReactElement { ...@@ -18,9 +22,15 @@ function App(): ReactElement {
<Routes> <Routes>
<Route path="/data" element={<CompendiumData />} /> <Route path="/data" element={<CompendiumData />} />
<Route path="/about" element={<About />} /> <Route path="/about" element={<About />} />
<Route path="/analysis" element={<DataAnalysis />} /> <Route path="/analysis" element={<DataAnalysis
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}/>}
/>
<Route path="/report" element={<AnnualReport />} /> <Route path="/report" element={<AnnualReport />} />
<Route path="/funding" element={<FundingSourcePage />} /> <Route path="/funding" element={<FundingSourcePage
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}/>}
/>
<Route path="/charging" element={<ChargingStructurePage />} /> <Route path="/charging" element={<ChargingStructurePage />} />
<Route path="*" element={<Landing />} /> <Route path="*" element={<Landing />} />
</Routes> </Routes>
......
import React, {ReactElement} from 'react';
import {Row} from 'react-bootstrap';
import {FilterOptions, FilterSelection} from "../../Schema";
interface inputProps {
filterOptions: FilterOptions
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function Filter({ filterOptions, filterSelection, setFilterSelection }: inputProps): ReactElement {
const handleNrenClick = (nren: string) => {
if (filterSelection.selectedNrens.includes(nren)) {
setFilterSelection({
selectedYears: [...filterSelection.selectedYears],
selectedNrens: filterSelection.selectedNrens.filter((e) => e !== nren)
});
} else {
setFilterSelection({
selectedYears: [...filterSelection.selectedYears],
selectedNrens: [...filterSelection.selectedNrens, nren]
});
}
};
const handleYearClick = (year: number) => {
if (filterSelection.selectedYears.includes(year)) {
setFilterSelection({
selectedYears: filterSelection.selectedYears.filter((e) => e !== year),
selectedNrens: [...filterSelection.selectedNrens]
});
} else {
setFilterSelection({
selectedYears: [...filterSelection.selectedYears, year],
selectedNrens: [...filterSelection.selectedNrens]
});
}
};
return (
<>
<Row>
{filterOptions.availableYears.map((year) => (
<div key={year} onClick={() => (handleYearClick(year))}>
<input
type="checkbox"
checked={filterSelection.selectedYears.includes(year)}
readOnly
/>
{year}
</div>
))}
</Row>
<Row>
{filterOptions.availableNrens.map((nren) => (
<div key={nren} onClick={() => (handleNrenClick(nren))}>
<input
type="checkbox"
checked={filterSelection.selectedNrens.includes(nren)}
readOnly
/>
{nren}
</div>
))}
</Row>
</>
);
}
export default Filter;
...@@ -30,6 +30,7 @@ const options = { ...@@ -30,6 +30,7 @@ const options = {
plugins: { plugins: {
legend: { legend: {
position: 'top' as const, position: 'top' as const,
onClick: () => { /* intentionally empty */ },
}, },
title: { title: {
display: true, display: true,
......
import React, { ReactElement, useEffect, useState } from 'react'; import React, { ReactElement, useEffect, useState } from 'react';
import { Accordion, Col, Container, ListGroup, Row } from "react-bootstrap"; import { Col, Container, Row } from "react-bootstrap";
import LineGraph from "../components/graphing/LineGraph"; import LineGraph from "../components/graphing/LineGraph";
import { BudgetMatrix, DataEntrySection, Budget } from "../Schema"; import {BudgetMatrix, DataEntrySection, Budget, FilterSelection, FilterOptions} from "../Schema";
import Filter from "../components/graphing/Filter";
// create a color from a string, credits https://stackoverflow.com/a/16348977
const stringToColour = function(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
let colour = '#';
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 0xFF;
colour += ('00' + value.toString(16)).substr(-2);
}
return colour;
}
export const options = { interface inputProps {
// indexAxis: 'y' as const, filterSelection: FilterSelection
responsive: true, setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
plugins: { }
legend: {
position: 'top' as const, function DataAnalysis({ filterSelection, setFilterSelection }: inputProps): ReactElement {
},
title: {
display: true,
text: 'Loading...',
},
},
};
function DataAnalysis(): ReactElement {
function api<T>(url: string, options: RequestInit | undefined = undefined): Promise<T> { function api<T>(url: string, options: RequestInit | undefined = undefined): Promise<T> {
return fetch(url, options) return fetch(url, options)
...@@ -37,6 +44,7 @@ function DataAnalysis(): ReactElement { ...@@ -37,6 +44,7 @@ function DataAnalysis(): ReactElement {
const [budgetResponse, setBudget] = useState<Budget[]>(); const [budgetResponse, setBudget] = useState<Budget[]>();
const [dataEntrySection, setDataEntrySection] = useState<DataEntrySection>(); const [dataEntrySection, setDataEntrySection] = useState<DataEntrySection>();
const [selectedDataEntry, setSelectedDataEntry] = useState<number>(0); const [selectedDataEntry, setSelectedDataEntry] = useState<number>(0);
const [filterOptions, setFilterOptions] = useState<FilterOptions>({availableYears: [], availableNrens: []});
useEffect(() => { useEffect(() => {
// hardcode selected section for now // hardcode selected section for now
...@@ -70,8 +78,6 @@ function DataAnalysis(): ReactElement { ...@@ -70,8 +78,6 @@ function DataAnalysis(): ReactElement {
console.log('budget :', budget) console.log('budget :', budget)
const entry = dataEntrySection?.items.find(i => i.id == selectedDataEntry) const entry = dataEntrySection?.items.find(i => i.id == selectedDataEntry)
console.log(selectedDataEntry, dataEntrySection, entry) console.log(selectedDataEntry, dataEntrySection, entry)
if (entry)
options.plugins.title.text = entry.title;
setBudget(budget) setBudget(budget)
console.log("budgetResponse after api " + budgetResponse) console.log("budgetResponse after api " + budgetResponse)
convertToBudgetPerYearDataResponse(budget) convertToBudgetPerYearDataResponse(budget)
...@@ -85,7 +91,7 @@ function DataAnalysis(): ReactElement { ...@@ -85,7 +91,7 @@ function DataAnalysis(): ReactElement {
} }
loadData() loadData()
}, [dataEntrySection, selectedDataEntry]); }, [dataEntrySection, selectedDataEntry, filterSelection]);
const empty_bar_response = { const empty_bar_response = {
data: { data: {
...@@ -118,14 +124,7 @@ function DataAnalysis(): ReactElement { ...@@ -118,14 +124,7 @@ function DataAnalysis(): ReactElement {
const labelsYear = [...new Set(barResponse.map((item) => item.BUDGET_YEAR))]; const labelsYear = [...new Set(barResponse.map((item) => item.BUDGET_YEAR))];
const labelsNREN = [...new Set(barResponse.map((item) => item.NREN))]; const labelsNREN = [...new Set(barResponse.map((item) => item.NREN))];
setFilterOptions({ availableYears: [], availableNrens: labelsNREN });
function getRandomColor() {
const red = Math.floor(Math.random() * 256).toString(16).padStart(2, '0'); // generates a value between 00 and ff
const green = Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
const blue = Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
return `#${red}${green}${blue}`;
}
function dataForNRENForYear(year: number, nren: string) { function dataForNRENForYear(year: number, nren: string) {
const budget = barResponse.find(function (entry, index) { const budget = barResponse.find(function (entry, index) {
...@@ -137,7 +136,7 @@ function DataAnalysis(): ReactElement { ...@@ -137,7 +136,7 @@ function DataAnalysis(): ReactElement {
} }
const datasetPerYear = labelsYear.map(function (year) { const datasetPerYear = labelsYear.map(function (year) {
const randomColor = getRandomColor(); const randomColor = stringToColour(year);
return { return {
backgroundColor: randomColor, backgroundColor: randomColor,
borderColor: randomColor, borderColor: randomColor,
...@@ -147,12 +146,13 @@ function DataAnalysis(): ReactElement { ...@@ -147,12 +146,13 @@ function DataAnalysis(): ReactElement {
}) })
const datasetPerNREN = labelsNREN.map(function (nren) { const datasetPerNREN = labelsNREN.map(function (nren) {
const randomColor = getRandomColor(); const randomColor = stringToColour(nren);
return { return {
backgroundColor: randomColor, backgroundColor: randomColor,
borderColor: randomColor, borderColor: randomColor,
data: labelsYear.map(year => dataForNRENForYear(year, nren)), data: labelsYear.map(year => dataForNRENForYear(year, nren)),
label: nren label: nren,
hidden: !filterSelection.selectedNrens.includes(nren)
} }
}) })
...@@ -215,20 +215,25 @@ function DataAnalysis(): ReactElement { ...@@ -215,20 +215,25 @@ function DataAnalysis(): ReactElement {
</Col> </Col>
<Col xs={3}> <Col xs={3}>
<Accordion defaultActiveKey="0"> {/*<Accordion defaultActiveKey="0">*/}
<Accordion.Item eventKey="0"> {/* <Accordion.Item eventKey="0">*/}
<Accordion.Header>Items</Accordion.Header> {/* <Accordion.Header>Items</Accordion.Header>*/}
<Accordion.Body> {/* <Accordion.Body>*/}
<ListGroup> {/* <ListGroup>*/}
{ {/* {*/}
dataEntrySection?.items.map((item) => ( {/* dataEntrySection?.items.map((item) => (*/}
<ListGroup.Item key={item.id} action active={item.id == selectedDataEntry} onClick={() => setSelectedDataEntry(item.id)}>{item.title}</ListGroup.Item> {/* <ListGroup.Item key={item.id} action active={item.id == selectedDataEntry} onClick={() => setSelectedDataEntry(item.id)}>{item.title}</ListGroup.Item>*/}
)) {/* ))*/}
} {/* }*/}
</ListGroup> {/* </ListGroup>*/}
</Accordion.Body> {/* </Accordion.Body>*/}
</Accordion.Item> {/* </Accordion.Item>*/}
</Accordion> {/*</Accordion>*/}
<Filter
filterOptions={filterOptions}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col> </Col>
</Row> </Row>
......
...@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; ...@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import {Col, Container, Row} from "react-bootstrap"; import {Col, Container, Row} from "react-bootstrap";
import { Bar } from 'react-chartjs-2'; import { Bar } from 'react-chartjs-2';
import { createFundingSourceDataset } from "../helpers/dataconversion"; import { createFundingSourceDataset } from "../helpers/dataconversion";
import Filter from "../components/graphing/Filter"
import { import {
Chart as ChartJS, Chart as ChartJS,
...@@ -93,13 +94,14 @@ async function getData(): Promise<FundingSource[]> { ...@@ -93,13 +94,14 @@ async function getData(): Promise<FundingSource[]> {
} }
} }
function FundingSourcePage() { interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function FundingSourcePage({ filterSelection, setFilterSelection }: inputProps) {
const [fundingSourceDataset, setDataset] = useState<FundingSourceDataset>(EMPTY_DATASET); const [fundingSourceDataset, setDataset] = useState<FundingSourceDataset>(EMPTY_DATASET);
const [fundingSourceData, setFundingSourceData] = useState<FundingSource[]>(); const [fundingSourceData, setFundingSourceData] = useState<FundingSource[]>();
// this state should go up
const [filterSelection, setFilterSelection] = useState<FilterSelection>({selectedYears: [], selectedNrens: []});
// and this one is fine here
const [filterOptions, setFilterOptions] = useState<FilterOptions>({availableYears: [], availableNrens: []}); const [filterOptions, setFilterOptions] = useState<FilterOptions>({availableYears: [], availableNrens: []});
useEffect(() => { useEffect(() => {
...@@ -152,35 +154,6 @@ function FundingSourcePage() { ...@@ -152,35 +154,6 @@ function FundingSourcePage() {
setDataset(freshDataset); setDataset(freshDataset);
}, [filterSelection]) }, [filterSelection])
const handleNrenClick = (nren: string) => {
if (filterSelection.selectedNrens.includes(nren)) {
setFilterSelection({
selectedYears: [...filterSelection.selectedYears],
selectedNrens: filterSelection.selectedNrens.filter((e) => e !== nren)
});
} else {
setFilterSelection({
selectedYears: [...filterSelection.selectedYears],
selectedNrens: [...filterSelection.selectedNrens, nren]
});
}
};
const handleYearClick = (year: number) => {
if (filterSelection.selectedYears.includes(year)) {
setFilterSelection({
selectedYears: filterSelection.selectedYears.filter((e) => e !== year),
selectedNrens: [...filterSelection.selectedNrens]
});
} else {
setFilterSelection({
selectedYears: [...filterSelection.selectedYears, year],
selectedNrens: [...filterSelection.selectedNrens]
});
}
};
return ( return (
<Container> <Container>
<Row> <Row>
...@@ -198,30 +171,11 @@ function FundingSourcePage() { ...@@ -198,30 +171,11 @@ function FundingSourcePage() {
</Row> </Row>
</Col> </Col>
<Col xs={3}> <Col xs={3}>
<Row> <Filter
{filterOptions.availableYears.map((year) => ( filterOptions={filterOptions}
<div key={year} onClick={() => (handleYearClick(year))}> filterSelection={filterSelection}
<input setFilterSelection={setFilterSelection}
type="checkbox" />
checked={filterSelection.selectedYears.includes(year)}
readOnly
/>
{year}
</div>
))}
</Row>
<Row>
{filterOptions.availableNrens.map((nren) => (
<div key={nren} onClick={() => (handleNrenClick(nren))}>
<input
type="checkbox"
checked={filterSelection.selectedNrens.includes(nren)}
readOnly
/>
{nren}
</div>
))}
</Row>
</Col> </Col>
</Row> </Row>
</Container> </Container>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment