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

first version of organisation pages

parent 953d689f
No related branches found
No related tags found
1 merge request!9first version of organisation pages
...@@ -10,6 +10,8 @@ import FundingSourcePage from "./pages/FundingSource"; ...@@ -10,6 +10,8 @@ import FundingSourcePage from "./pages/FundingSource";
import ChargingStructurePage from "./pages/ChargingStructure"; import ChargingStructurePage from "./pages/ChargingStructure";
import StaffGraph from "./pages/StaffGraph"; import StaffGraph from "./pages/StaffGraph";
import { FilterSelection } from "./Schema"; import { FilterSelection } from "./Schema";
import SubOrganisation from "./pages/SubOrganisation";
import ParentOrganisation from "./pages/ParentOrganisation";
function App(): ReactElement { function App(): ReactElement {
...@@ -31,6 +33,8 @@ function App(): ReactElement { ...@@ -31,6 +33,8 @@ function App(): ReactElement {
<Route path="/data/employment" element={<StaffGraph filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> <Route path="/data/employment" element={<StaffGraph filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/data/roles" element={<StaffGraph roles filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> <Route path="/data/roles" element={<StaffGraph roles filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/charging" element={<ChargingStructurePage />} /> <Route path="/charging" element={<ChargingStructurePage />} />
<Route path="/suborganisations" element={<SubOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/parentorganisation" element={<ParentOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="*" element={<Landing />} /> <Route path="*" element={<Landing />} />
</Routes> </Routes>
<GeantFooter /> <GeantFooter />
......
...@@ -122,4 +122,11 @@ export interface NrenStaffDataset { ...@@ -122,4 +122,11 @@ export interface NrenStaffDataset {
stack: string stack: string
hidden: boolean hidden: boolean
}[] }[]
} }
\ No newline at end of file
export interface Organisation {
nren: string,
year: number,
name: string,
role?: string
}
...@@ -3,7 +3,7 @@ import { ...@@ -3,7 +3,7 @@ import {
FundingSource, FundingSource,
FundingSourceDataset, FundingSourceDataset,
ChargingStructure, ChargingStructure,
ChargingStructureDataset, Budget, BasicDataset, NrenStaff, NrenStaffDataset ChargingStructureDataset, Budget, BasicDataset, NrenStaff, NrenStaffDataset, Organisation
} from "../Schema"; } from "../Schema";
const DEFAULT_CHARGING_STRUCTURE_DATA = [ const DEFAULT_CHARGING_STRUCTURE_DATA = [
...@@ -244,6 +244,26 @@ export function createChargingStructureDataset(chargingStructureData: ChargingSt ...@@ -244,6 +244,26 @@ export function createChargingStructureDataset(chargingStructureData: ChargingSt
} }
export function createOrganisationDataLookup(organisationEntries: Organisation[]) {
const nrenMap = new Map<string, Map<number, Organisation[]>>();
organisationEntries.forEach(entry => {
let nrenEntry = nrenMap.get(entry.nren);
if (!nrenEntry) {
nrenEntry = new Map<number, Organisation[]>();
}
let suborgList = nrenEntry.get(entry.year);
if (!suborgList) {
suborgList = [];
}
suborgList.push(entry);
nrenEntry.set(entry.year, suborgList);
nrenMap.set(entry.nren, nrenEntry);
});
return nrenMap;
}
export const createNRENStaffDataset = (data: NrenStaff[], roles: boolean) => { export const createNRENStaffDataset = (data: NrenStaff[], roles: boolean) => {
function CreateDataLookup(data: NrenStaff[]) { function CreateDataLookup(data: NrenStaff[]) {
const dataLookup = new Map<string, Map<string, number>>(); const dataLookup = new Map<string, Map<string, number>>();
......
...@@ -56,8 +56,16 @@ function CompendiumData(): ReactElement { ...@@ -56,8 +56,16 @@ function CompendiumData(): ReactElement {
<span>Types of employment for NRENs</span> <span>Types of employment for NRENs</span>
</Link> </Link>
</Row> </Row>
<Row>
<Link to="/suborganisations" className="link-text-underline">
<span>NREN Suborganisations</span>
</Link>
</Row>
<Row>
<Link to="/parentorganisation" className="link-text-underline">
<span>NREN Parent Organisations</span>
</Link>
</Row>
</div> </div>
</CollapsibleBox> </CollapsibleBox>
</div> </div>
......
import React, { useEffect, useMemo, useState } from 'react';
import { Col, Container, Row, Table } from "react-bootstrap";
import { createOrganisationDataLookup } from "../helpers/dataconversion";
import Filter from "../components/graphing/Filter"
import {
Organisation,
FilterSelection
} from "../Schema";
async function getData(): Promise<Organisation[]> {
try {
const response = await fetch('/api/organization/parent');
return response.json();
} catch (error) {
console.error(`Failed to load data: ${error}`);
throw error;
}
}
function getYearsAndNrens(sourceData: Organisation[]) {
const years = new Set<number>();
const nrens = new Set<string>();
sourceData.forEach(datapoint => {
years.add(datapoint.year);
nrens.add(datapoint.nren);
});
return { years: years, nrens: nrens };
}
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function ParentOrganisation({ filterSelection, setFilterSelection }: inputProps) {
const [organisationData, setOrganisationData] = useState<Organisation[]>();
const { years, nrens } = useMemo(
() => getYearsAndNrens(organisationData || []),
[organisationData]
);
const selectedData = (organisationData || []).filter(data =>
filterSelection.selectedYears.includes(data.year) && filterSelection.selectedNrens.includes(data.nren)
);
const organisationDataset = createOrganisationDataLookup(selectedData);
useEffect(() => {
const loadData = async () => {
const data = await getData();
setOrganisationData(data);
// filter fallback for when nothing is selected (only last year for all nrens)
const { years, nrens } = getYearsAndNrens(data);
setFilterSelection(previous => {
const visibleYears = previous.selectedYears.filter(year => years.has(year));
const newSelectedYears = visibleYears.length ? previous.selectedYears : [Math.max(...years)];
const visibleNrens = previous.selectedNrens.filter(nren => nrens.has(nren));
const newSelectedNrens = visibleNrens.length ? previous.selectedNrens : [...nrens];
return { selectedYears: newSelectedYears, selectedNrens: newSelectedNrens };
});
}
loadData()
}, [setFilterSelection]);
function sortOrganisation(a: Organisation, b: Organisation) {
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
}
return (
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Parent Organisations</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>Parent Organisation</th>
</tr>
</thead>
<tbody>
{Array.from(organisationDataset).sort().map(([_, nrenEntry]) => (
Array.from(nrenEntry).sort().map(([_, nrenDataList], yearIndex) => (
nrenDataList.sort(sortOrganisation).map((nrenData, nrenDataIndex) => (
<tr key={nrenData.nren + nrenData.year + nrenData.name}>
<td>{yearIndex == 0 && nrenDataIndex == 0 && nrenData.nren}</td>
<td>{nrenDataIndex == 0 && nrenData.year}</td>
<td>{nrenData.name}</td>
</tr>
))
)
)
))}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
);
}
export default ParentOrganisation;
import React, { useEffect, useMemo, useState } from 'react';
import { Col, Container, Row, Table } from "react-bootstrap";
import { createOrganisationDataLookup } from "../helpers/dataconversion";
import Filter from "../components/graphing/Filter"
import {
Organisation,
FilterSelection
} from "../Schema";
async function getData(): Promise<Organisation[]> {
try {
const response = await fetch('/api/organization/sub');
return response.json();
} catch (error) {
console.error(`Failed to load data: ${error}`);
throw error;
}
}
function getYearsAndNrens(sourceData: Organisation[]) {
const years = new Set<number>();
const nrens = new Set<string>();
sourceData.forEach(datapoint => {
years.add(datapoint.year);
nrens.add(datapoint.nren);
});
return { years: years, nrens: nrens };
}
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function SubOrganisation({ filterSelection, setFilterSelection }: inputProps) {
const [organisationData, setOrganisationData] = useState<Organisation[]>();
const { years, nrens } = useMemo(
() => getYearsAndNrens(organisationData || []),
[organisationData]
);
const selectedData = (organisationData || []).filter(data =>
filterSelection.selectedYears.includes(data.year) && filterSelection.selectedNrens.includes(data.nren)
);
const organisationDataset = createOrganisationDataLookup(selectedData);
useEffect(() => {
const loadData = async () => {
const data = await getData();
setOrganisationData(data);
// filter fallback for when nothing is selected (only last year for all nrens)
const { years, nrens } = getYearsAndNrens(data);
setFilterSelection(previous => {
const visibleYears = previous.selectedYears.filter(year => years.has(year));
const newSelectedYears = visibleYears.length ? previous.selectedYears : [Math.max(...years)];
const visibleNrens = previous.selectedNrens.filter(nren => nrens.has(nren));
const newSelectedNrens = visibleNrens.length ? previous.selectedNrens : [...nrens];
return { selectedYears: newSelectedYears, selectedNrens: newSelectedNrens };
});
}
loadData()
}, [setFilterSelection]);
function sortOrganisation(a: Organisation, b: Organisation) {
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
}
return (
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Suborganisations</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>Suborganisation</th>
<th>Role</th>
</tr>
</thead>
<tbody>
{Array.from(organisationDataset).sort().map(([_, nrenEntry]) => (
Array.from(nrenEntry).sort().map(([_, nrenDataList], yearIndex) => (
nrenDataList.sort(sortOrganisation).map((nrenData, nrenDataIndex) => (
<tr key={nrenData.nren + nrenData.year + nrenData.name}>
<td>{yearIndex == 0 && nrenDataIndex == 0 && nrenData.nren}</td>
<td>{nrenDataIndex == 0 && nrenData.year}</td>
<td>{nrenData.name}</td>
<td>{nrenData.role}</td>
</tr>
))
)
)
))}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
);
}
export default SubOrganisation;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment