diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index c3c119e13a2af0415947ca70782bd4ddc8e163ff..cb7b57a95ee5242ad309bf754cebf5cdea4da685 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -1,10 +1,9 @@ -import React, { ReactElement, useState } from "react"; +import React, { ReactElement, useState, useContext } from "react"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import Landing from "./pages/Landing"; import ExternalPageNavBar from "./components/global/ExternalPageNavBar"; import GeantFooter from "./components/global/GeantFooter"; import DataAnalysis from "./pages/DataAnalysis"; -import AnnualReport from "./pages/AnnualReport"; import CompendiumData from "./pages/CompendiumData"; import FundingSourcePage from "./pages/FundingSource"; import ChargingStructurePage from "./pages/ChargingStructure"; @@ -13,32 +12,34 @@ import { FilterSelection } from "./Schema"; import SubOrganisation from "./pages/SubOrganisation"; import ParentOrganisation from "./pages/ParentOrganisation"; import ECProjects from "./pages/ECProjects"; +import SidebarProvider from "./helpers/SidebarProvider"; function App(): ReactElement { const [filterSelection, setFilterSelection] = useState<FilterSelection>({ selectedYears: [], selectedNrens: [] }); + + return ( <div className="app"> <Router> <ExternalPageNavBar /> - <Routes> - <Route path="/data" element={<CompendiumData />} /> - <Route path="/analysis" element={ - <DataAnalysis filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} - /> - <Route path="/report" element={<AnnualReport />} /> - <Route path="/funding" element={<FundingSourcePage 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="/charging" element={<ChargingStructurePage filterSelection={filterSelection} setFilterSelection={setFilterSelection}/>} /> - <Route path="/suborganisations" element={<SubOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> - <Route path="/parentorganisation" element={<ParentOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> - <Route path="/ec-projects" element={<ECProjects filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> - <Route path="*" element={<Landing />} /> - </Routes> + <SidebarProvider> + <Routes> + <Route path="/analysis" element={<DataAnalysis filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> + <Route path="/funding" element={<FundingSourcePage 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="/charging" element={<ChargingStructurePage filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> + <Route path="/suborganisations" element={<SubOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> + <Route path="/parentorganisation" element={<ParentOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> + <Route path="/ec-projects" element={<ECProjects filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} /> + <Route path="/data" element={<CompendiumData />} /> + <Route path="*" element={<Landing />} /> + </Routes> + </SidebarProvider> + <GeantFooter /> </Router> diff --git a/webapp/src/components/OrganizationSidebar.tsx b/webapp/src/components/OrganizationSidebar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3fac81c788720e8662fb2dbb3dd04ef90e559dad --- /dev/null +++ b/webapp/src/components/OrganizationSidebar.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Row } from 'react-bootstrap'; +import Sidebar from './SideBar'; + +const OrganizationSidebar = () => { + return ( + <Sidebar> + <Row> + <Link to="/analysis" className="link-text-underline"> + <span>Budget of NRENs per Year</span> + </Link> + </Row> + + <Row> + <Link to="/funding" className="link-text-underline"> + <span>Income Source of NRENs per Year</span> + </Link> + </Row> + + <Row> + <Link to="/charging" className="link-text-underline"> + <span>Charging Mechanism of NRENs per Year</span> + </Link> + </Row> + + <Row> + <Link to="/data/roles" className="link-text-underline"> + <span>Roles of NREN employees</span> + </Link> + </Row> + <Row> + <Link to="/data/employment" className="link-text-underline"> + <span>Types of employment for NRENs</span> + </Link> + </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> + <Row> + <Link to="/ec-projects" className="link-text-underline"> + <span>NREN Involvement in European Commission Projects</span> + </Link> + </Row> + </Sidebar> + ) +} + +export default OrganizationSidebar \ No newline at end of file diff --git a/webapp/src/components/SideBar.tsx b/webapp/src/components/SideBar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ddcab7d498061b8d973fb06b05c6e00f2755fe22 --- /dev/null +++ b/webapp/src/components/SideBar.tsx @@ -0,0 +1,25 @@ +import React, { useContext } from 'react'; +import { sidebarContext } from "../helpers/SidebarProvider"; + + +interface Props { + children: React.ReactNode; +} + +const Sidebar: React.FC<Props> = ({ children }) => { + + const { show, toggle } = useContext(sidebarContext); + + return ( + <div className="sidebar-wrapper"> + <nav className={show ? '' : 'no-sidebar'} id="sidebar"> + <div className={`menu-items`}>{children}</div> + </nav> + <button className="toggle-btn" onClick={toggle}> + {show ? 'Hide Sidebar' : 'Show Sidebar'} + </button> + </div> + ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/webapp/src/helpers/SidebarProvider.tsx b/webapp/src/helpers/SidebarProvider.tsx new file mode 100644 index 0000000000000000000000000000000000000000..fdf631d109931be26d3083a274ef4a33928aa6d0 --- /dev/null +++ b/webapp/src/helpers/SidebarProvider.tsx @@ -0,0 +1,31 @@ +import React, { createContext, useState } from 'react'; + +interface Props { + children: React.ReactNode; +} + +const sidebarContext = createContext<{ + show: boolean; + toggle: React.Dispatch<any>; +}>({ + show: false, + toggle: () => { } +}); + + +const SidebarProvider: React.FC<Props> = ({ children }) => { + const [show, setShow] = useState<boolean>(false); + + const toggle = () => { + setShow(!show); + }; + + return ( + <sidebarContext.Provider value={{ show, toggle }}> + {children} + </sidebarContext.Provider> + ); +}; + +export { sidebarContext }; +export default SidebarProvider; \ No newline at end of file diff --git a/webapp/src/pages/ChargingStructure.tsx b/webapp/src/pages/ChargingStructure.tsx index 800e10f438474b033a28c3eb0a4a3ff757258f85..660499a1dd832d1b58de6d271e6e9768419fdde4 100644 --- a/webapp/src/pages/ChargingStructure.tsx +++ b/webapp/src/pages/ChargingStructure.tsx @@ -1,8 +1,6 @@ -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { Container, Row, Col, Table } from "react-bootstrap"; -import Chart, { ChartOptions, Plugin, ChartTypeRegistry, ScatterDataPoint } from 'chart.js'; -import { Bubble, Scatter } from "react-chartjs-2"; -import { ChargingStructure, ChargingStructureDataset, FilterSelection } from "../Schema"; +import { ChargingStructure, FilterSelection } from "../Schema"; import { Chart as ChartJS, @@ -11,12 +9,13 @@ import { BarElement, Title, Tooltip, - Legend, - scales, + Legend } from 'chart.js'; import Filter from "../components/graphing/Filter"; import { createChargingStructureDataLookup } from "../helpers/dataconversion"; import ColorPill from "../components/ColorPill"; +import OrganizationSidebar from "../components/OrganizationSidebar"; + ChartJS.register( CategoryScale, LinearScale, @@ -26,10 +25,6 @@ ChartJS.register( Legend ); -const EMPTY_DATASET = { datasets: [], labels: [] }; - - - async function getData(): Promise<ChargingStructure[]> { try { const response = await fetch('/api/charging/'); @@ -61,89 +56,90 @@ function ChargingStructurePage({ filterSelection, setFilterSelection }: inputPro return ( <> - <Container fluid className="p-0"> - <Row> - <h1>Charging Structure</h1> - </Row> - <Row> - <Col xs={9}> - <Table className="charging-struct-table" striped bordered responsive > - <colgroup> - <col span={1} style={{ width: "10%" }} /> - <col span={1} style={{ width: "18%" }} /> - <col span={1} style={{ width: "18%" }} /> - <col span={1} style={{ width: "18%" }} /> - <col span={1} style={{ width: "18%" }} /> - <col span={1} style={{ width: "18%" }} /> - </colgroup> - <thead> - <tr > - <th></th> - <th>Flat fee based on bandwidth</th> - <th>Usage based fee</th> - <th>Combination flat fee & usage basedfee </th> - <th>No Direct Charge</th> - <th>Other</th> - </tr> - </thead> - <tbody> - {filterSelection.selectedNrens - .sort((a, b) => a>b? 1 : -1) - .map((nren: string) => ( - <tr key={nren}> - <td>{nren}</td> - <td > - {filterSelection.selectedYears - .sort((a, b) => a>b? 1 : -1) - .map((year: number, index) => ( - <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"flat_fee"}/> - ))} - </td> - <td> - {filterSelection.selectedYears - .sort((a, b) => a>b? 1 : -1) - .map((year: number, index) => ( - <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"usage_based_fee"}/> - )) - } - </td> - <td> - {filterSelection.selectedYears - .sort((a, b) => a>b? 1 : -1) - .map((year: number, index) => ( - <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"combination"}/> - ))} - </td> - <td> - {filterSelection.selectedYears - .sort((a, b) => a>b? 1 : -1) - .map((year: number, index) => ( - <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"no_charge"}/> - ))} - </td> - <td> - {filterSelection.selectedYears - .sort((a, b) => a>b? 1 : -1) - .map((year: number, index) => ( - <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"other"}/> - ))} - </td> - </tr> + <OrganizationSidebar /> + <Container fluid className="p-0"> + <Row> + <h1>Charging Structure</h1> + </Row> + <Row> + <Col xs={9}> + <Table className="charging-struct-table" striped bordered responsive > + <colgroup> + <col span={1} style={{ width: "10%" }} /> + <col span={1} style={{ width: "18%" }} /> + <col span={1} style={{ width: "18%" }} /> + <col span={1} style={{ width: "18%" }} /> + <col span={1} style={{ width: "18%" }} /> + <col span={1} style={{ width: "18%" }} /> + </colgroup> + <thead> + <tr > + <th></th> + <th>Flat fee based on bandwidth</th> + <th>Usage based fee</th> + <th>Combination flat fee & usage basedfee </th> + <th>No Direct Charge</th> + <th>Other</th> + </tr> + </thead> + <tbody> + {filterSelection.selectedNrens + .sort((a, b) => a > b ? 1 : -1) + .map((nren: string) => ( + <tr key={nren}> + <td>{nren}</td> + <td > + {filterSelection.selectedYears + .sort((a, b) => a > b ? 1 : -1) + .map((year: number, index) => ( + <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"flat_fee"} /> + ))} + </td> + <td> + {filterSelection.selectedYears + .sort((a, b) => a > b ? 1 : -1) + .map((year: number, index) => ( + <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"usage_based_fee"} /> + )) + } + </td> + <td> + {filterSelection.selectedYears + .sort((a, b) => a > b ? 1 : -1) + .map((year: number, index) => ( + <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"combination"} /> + ))} + </td> + <td> + {filterSelection.selectedYears + .sort((a, b) => a > b ? 1 : -1) + .map((year: number, index) => ( + <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"no_charge"} /> + ))} + </td> + <td> + {filterSelection.selectedYears + .sort((a, b) => a > b ? 1 : -1) + .map((year: number, index) => ( + <ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"other"} /> + ))} + </td> + </tr> - ))} - </tbody> + ))} + </tbody> - </Table> - </Col> - <Col xs={3}> - <Filter - filterOptions={{availableYears: [...labelsYear], availableNrens: [...labelsNREN]}} - filterSelection={filterSelection} - setFilterSelection={setFilterSelection} - /> - </Col> - </Row> - </Container> + </Table> + </Col> + <Col xs={3}> + <Filter + filterOptions={{ availableYears: [...labelsYear], availableNrens: [...labelsNREN] }} + filterSelection={filterSelection} + setFilterSelection={setFilterSelection} + /> + </Col> + </Row> + </Container> </> ); }; diff --git a/webapp/src/pages/DataAnalysis.tsx b/webapp/src/pages/DataAnalysis.tsx index 29cb56db110080ed94a0a398a98ae8e1f51c5782..53eb3a59a1aa4ccd817cc23f213d28e51619cd52 100644 --- a/webapp/src/pages/DataAnalysis.tsx +++ b/webapp/src/pages/DataAnalysis.tsx @@ -1,10 +1,11 @@ -import React, {ReactElement, useEffect, useState} from 'react'; +import React, { ReactElement, useEffect, useState } from 'react'; import { Col, Container, Row } from "react-bootstrap"; import LineGraph from "../components/graphing/LineGraph"; - -import {Budget, FilterSelection} from "../Schema"; import Filter from "../components/graphing/Filter"; -import {createBudgetDataset} from "../helpers/dataconversion"; +import OrganizationSidebar from "../components/OrganizationSidebar"; + +import { createBudgetDataset } from "../helpers/dataconversion"; +import { Budget, FilterSelection } from "../Schema"; function api<T>(url: string, options: RequestInit | undefined = undefined): Promise<T> { return fetch(url, options) @@ -35,7 +36,6 @@ function DataAnalysis({ filterSelection, setFilterSelection }: inputProps): Reac budgetData.datasets.forEach(dataset => { dataset.hidden = !filterSelection.selectedNrens.includes(dataset.label); }); - useEffect(() => { const loadData = async () => { @@ -61,6 +61,8 @@ function DataAnalysis({ filterSelection, setFilterSelection }: inputProps): Reac return ( <div> <h1>Data Analysis</h1> + <OrganizationSidebar /> + <Container> <Row> <Col> @@ -68,15 +70,15 @@ function DataAnalysis({ filterSelection, setFilterSelection }: inputProps): Reac <LineGraph data={budgetData} /> </Row> <Row> -The numbers are based on 30 NRENs that reported their budgets continuously throughout this -period. This means that some larger NRENs are not included and therefore the actual total budgets will -have been higher. (For comparison, the total budget according to the 2021 survey results based on the data -for all responding NRENs that year is €555 M). The percentage change is based on the previous year's budget. + The numbers are based on 30 NRENs that reported their budgets continuously throughout this + period. This means that some larger NRENs are not included and therefore the actual total budgets will + have been higher. (For comparison, the total budget according to the 2021 survey results based on the data + for all responding NRENs that year is €555 M). The percentage change is based on the previous year's budget. </Row> </Col> <Col xs={3}> <Filter - filterOptions={{availableYears: [], availableNrens: [...nrens]}} + filterOptions={{ availableYears: [], availableNrens: [...nrens] }} filterSelection={filterSelection} setFilterSelection={setFilterSelection} /> diff --git a/webapp/src/pages/ECProjects.tsx b/webapp/src/pages/ECProjects.tsx index 36ebd132724f55df28441af65cd9507e53ef4205..16748224d34b2d02caa21b1a74efeeafdc36cf0f 100644 --- a/webapp/src/pages/ECProjects.tsx +++ b/webapp/src/pages/ECProjects.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Col, Container, Row, Table } from "react-bootstrap"; import Filter from "../components/graphing/Filter" +import OrganizationSidebar from '../components/OrganizationSidebar'; import { ECProject, @@ -123,41 +124,45 @@ function ECProjects({ filterSelection, setFilterSelection }: inputProps) { }, [setFilterSelection]); return ( - <Container> - <Row> - <Col xs={9}> - <Row> - <h3>NREN Involvement in European Commission Projects</h3> - </Row> - <Row> - <Table> - <thead> - <tr> - <th>NREN</th> - <th>Year</th> - <th>EC Project Membership</th> - </tr> - </thead> - <tbody className='table-bg-highlighted'> - {getJSXFromMap(projectDataByYear)} - </tbody> - </Table> - </Row> - </Col> - <Col xs={3}> - <div className="sticky-top" style={{ top: '1rem' }}> - {/* the sticky-top class makes the filter follow the user as they scroll down the page */} - <Filter - filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }} - filterSelection={filterSelection} - setFilterSelection={setFilterSelection} - /> - </div> - - - </Col> - </Row> - </Container> + <> + <OrganizationSidebar /> + <Container> + <Row> + <Col xs={9}> + <Row> + <h3>NREN Involvement in European Commission Projects</h3> + </Row> + <Row> + <Table> + <thead> + <tr> + <th>NREN</th> + <th>Year</th> + <th>EC Project Membership</th> + </tr> + </thead> + <tbody className='table-bg-highlighted'> + {getJSXFromMap(projectDataByYear)} + </tbody> + </Table> + </Row> + </Col> + <Col xs={3}> + <div className="sticky-top" style={{ top: '1rem' }}> + {/* the sticky-top class makes the filter follow the user as they scroll down the page */} + <Filter + filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }} + filterSelection={filterSelection} + setFilterSelection={setFilterSelection} + /> + </div> + + + </Col> + </Row> + </Container> + </> + ); } export default ECProjects; diff --git a/webapp/src/pages/FundingSource.tsx b/webapp/src/pages/FundingSource.tsx index c0de41047e5775bb7826c12e35aa2ebdc4be32b5..a677223288bc66ddbd25250946b38a83c39ffdc5 100644 --- a/webapp/src/pages/FundingSource.tsx +++ b/webapp/src/pages/FundingSource.tsx @@ -1,8 +1,10 @@ -import React, {useEffect, useMemo, useState} from 'react'; -import {Col, Container, Row} from "react-bootstrap"; +import React, { useEffect, useMemo, useState } from 'react'; +import { Col, Container, Row } from "react-bootstrap"; import { Bar } from 'react-chartjs-2'; import { createFundingSourceDataset } from "../helpers/dataconversion"; import Filter from "../components/graphing/Filter" +import OrganizationSidebar from "../components/OrganizationSidebar"; + import { Chart as ChartJS, @@ -19,6 +21,7 @@ import { } from "../Schema"; + ChartJS.register( CategoryScale, LinearScale, @@ -82,7 +85,7 @@ function getYearsAndNrens(sourceData: FundingSource[]) { years.add(datapoint.YEAR); nrens.add(datapoint.NREN); }); - return {years: years, nrens: nrens}; + return { years: years, nrens: nrens }; } interface inputProps { @@ -132,30 +135,34 @@ function FundingSourcePage({ filterSelection, setFilterSelection }: inputProps) }, [setFilterSelection]); return ( - <Container> - <Row> - <Col xs={9}> - <Row> - <h1>Funding Source</h1> - </Row> - <Row> - <div className="chart-container" style={{ 'minHeight': '100vh', 'width': '60vw', }}> - <Bar - data={fundingSourceDataset} - options={chartOptions} - /> - </div> - </Row> - </Col> - <Col xs={3}> - <Filter - filterOptions={{availableYears: [...years], availableNrens: [...nrens]}} - filterSelection={filterSelection} - setFilterSelection={setFilterSelection} - /> - </Col> - </Row> - </Container> + <> + <OrganizationSidebar /> + <Container> + <Row> + <Col xs={9}> + <Row> + <h1>Funding Source</h1> + </Row> + <Row> + <div className="chart-container" style={{ 'minHeight': '100vh', 'width': '60vw', }}> + <Bar + data={fundingSourceDataset} + options={chartOptions} + /> + </div> + </Row> + </Col> + <Col xs={3}> + <Filter + filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }} + filterSelection={filterSelection} + setFilterSelection={setFilterSelection} + /> + </Col> + </Row> + </Container> + </> + ); } export default FundingSourcePage; diff --git a/webapp/src/pages/ParentOrganisation.tsx b/webapp/src/pages/ParentOrganisation.tsx index 725428e51a8f6b06c0b7182e7ddd1ba072af5b73..32e3cbd56d13552f669fc42f7b69cbd0b18c9fb5 100644 --- a/webapp/src/pages/ParentOrganisation.tsx +++ b/webapp/src/pages/ParentOrganisation.tsx @@ -2,6 +2,7 @@ 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 OrganizationSidebar from '../components/OrganizationSidebar'; import { Organisation, @@ -69,48 +70,52 @@ function ParentOrganisation({ filterSelection, setFilterSelection }: inputProps) } 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> - )) - ) - ) + <> + <OrganizationSidebar /> + <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> + </> - ))} - </tbody> - </Table> - </Row> - </Col> - <Col xs={3}> - <Filter - filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }} - filterSelection={filterSelection} - setFilterSelection={setFilterSelection} - /> - </Col> - </Row> - </Container> ); } export default ParentOrganisation; diff --git a/webapp/src/pages/StaffGraph.tsx b/webapp/src/pages/StaffGraph.tsx index acfc30b8abb2fd43d43fa1dea78b19ca4a5faa04..8a082c7550a2b3a4ebf393d9c1f879afc42f5bfb 100644 --- a/webapp/src/pages/StaffGraph.tsx +++ b/webapp/src/pages/StaffGraph.tsx @@ -3,6 +3,8 @@ import { Col, Container, Row } from "react-bootstrap"; import { Bar } from 'react-chartjs-2'; import { createNRENStaffDataset } from "../helpers/dataconversion"; import Filter from "../components/graphing/Filter" +import OrganizationSidebar from "../components/OrganizationSidebar"; + import { Chart as ChartJS, @@ -141,31 +143,35 @@ function StaffGraph({ filterSelection, setFilterSelection, roles = false }: inpu const text = roles ? "Roles" : "Employment"; return ( - <Container> - <Row> - <Col xs={9}> - <Row> - <h3>NREN Staff {text}</h3> - </Row> - <Row> - <div className="chart-container" style={{ 'minHeight': '60vh', 'width': '60vw', }}> - <Bar - data={nrenStaffDataset} - options={chartOptions} - /> - </div> - </Row> - </Col> - <Col xs={3}> - <Filter - max1year - filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }} - filterSelection={filterSelection} - setFilterSelection={setFilterSelection} - /> - </Col> - </Row> - </Container> + <> + <OrganizationSidebar /> + <Container> + <Row> + <Col xs={9}> + <Row> + <h3>NREN Staff {text}</h3> + </Row> + <Row> + <div className="chart-container" style={{ 'minHeight': '60vh', 'width': '60vw', }}> + <Bar + data={nrenStaffDataset} + options={chartOptions} + /> + </div> + </Row> + </Col> + <Col xs={3}> + <Filter + max1year + filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }} + filterSelection={filterSelection} + setFilterSelection={setFilterSelection} + /> + </Col> + </Row> + </Container> + </> + ); } export default StaffGraph; diff --git a/webapp/src/pages/SubOrganisation.tsx b/webapp/src/pages/SubOrganisation.tsx index 6bc2602bf9fc5f1204d485a4812755d4a63b15a4..2d78a2a231076db7694d71dce08f7820865c7649 100644 --- a/webapp/src/pages/SubOrganisation.tsx +++ b/webapp/src/pages/SubOrganisation.tsx @@ -2,6 +2,7 @@ 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 OrganizationSidebar from '../components/OrganizationSidebar'; import { Organisation, @@ -69,50 +70,54 @@ function SubOrganisation({ filterSelection, setFilterSelection }: inputProps) { } 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> - )) - ) - ) + <> + <OrganizationSidebar /> + <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> + </> - ))} - </tbody> - </Table> - </Row> - </Col> - <Col xs={3}> - <Filter - filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }} - filterSelection={filterSelection} - setFilterSelection={setFilterSelection} - /> - </Col> - </Row> - </Container> ); } export default SubOrganisation; diff --git a/webapp/src/scss/layout/Sidebar.scss b/webapp/src/scss/layout/Sidebar.scss new file mode 100644 index 0000000000000000000000000000000000000000..f98618c409282ac51d0dc58ad3c77dc6fde279e6 --- /dev/null +++ b/webapp/src/scss/layout/Sidebar.scss @@ -0,0 +1,31 @@ +.sidebar-wrapper { + display: flex; + position: fixed; + top: calc(50vh - 10%); + + .menu-items { + padding: 10px; + } +} + +.sidebar-wrapper>nav { + transition: 0.25s; + // transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55); + transition-property: margin-left; + margin-left: 0; + background-color: #fff; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25); + + a { + text-decoration: none; + } + + a:hover { + background-color: $yellow-orange; + text-decoration: none; + } +} + +nav.no-sidebar { + margin-left: -79%; +} \ No newline at end of file diff --git a/webapp/src/scss/layout/_components.scss b/webapp/src/scss/layout/_components.scss index 04c4ae2394284b77b3f1f45b5c27cd05529a7f6a..3239df40f1a9113b9fca48d32d928d75cf906d9b 100644 --- a/webapp/src/scss/layout/_components.scss +++ b/webapp/src/scss/layout/_components.scss @@ -1,4 +1,5 @@ @import '../abstracts/variables'; +@import '../layout/Sidebar'; .rounded-border { border-radius: 25px;