diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..d42099c Binary files /dev/null and b/.DS_Store differ diff --git a/backend/dbwrapper.ts b/backend/dbwrapper.ts index 9c543bf..a6053ad 100644 --- a/backend/dbwrapper.ts +++ b/backend/dbwrapper.ts @@ -87,7 +87,7 @@ export function createProject(project: types.Project, owner: string): [string | ]) return [null, res] } catch (error: any) { - return [error?.code, null] + return [error?.code, null] } } @@ -266,14 +266,22 @@ export function updateProject(id: number, owner: string, project: types.Project, } } export function deleteProject(id: number, owner: string, isAdmin: boolean) { - let updateProjectStatusStmt + let deleteProjectStmt let res if (isAdmin) { - updateProjectStatusStmt = db.prepare("DELETE FROM Projects where id = ?") - res = updateProjectStatusStmt.run([id]) + deleteProjectStmt = db.prepare("DELETE FROM ProjectSteps where projectId = ?") + res = deleteProjectStmt.run([id]) + deleteProjectStmt = db.prepare("DELETE FROM ProjectSources where projectId = ?") + res = deleteProjectStmt.run([id]) + deleteProjectStmt = db.prepare("DELETE FROM Projects where id = ?") + res = deleteProjectStmt.run([id]) } else { - updateProjectStatusStmt = db.prepare("DELETE FROM Projects where id = ? and owner = ?") - res = updateProjectStatusStmt.run([id, owner]) + deleteProjectStmt = db.prepare("DELETE FROM ProjectSteps where projectId = ? and owner = ?") + res = deleteProjectStmt.run([id, owner]) + deleteProjectStmt = db.prepare("DELETE FROM ProjectSources where projectId = ? and owner = ?") + res = deleteProjectStmt.run([id, owner]) + deleteProjectStmt = db.prepare("DELETE FROM Projects where id = ? and owner = ?") + res = deleteProjectStmt.run([id, owner]) } return res } diff --git a/backend/index.ts b/backend/index.ts index 053e5ab..8cae8de 100644 --- a/backend/index.ts +++ b/backend/index.ts @@ -193,7 +193,7 @@ app.get('/api/project/:projectId/viz', keycloak.protect(), (req: Request, res: R }); }); -// emissions and energy computed for top down checking +// emissions and energy computed for top-down checking app.get('/api/project/:projectId/Inventory/0/emissions', keycloak.protect(), (req: Request, res: Response) => { const owner = (req as any).kauth.grant.access_token.content.email const isAdmin = (req as any).kauth.grant.access_token.content.realm_access.roles.indexOf("app-admin") !== -1 diff --git a/backend/keycloak-config.ts b/backend/keycloak-config.ts index bde3246..8a213e6 100644 --- a/backend/keycloak-config.ts +++ b/backend/keycloak-config.ts @@ -6,7 +6,7 @@ let keycloak = undefined const keycloakConfig = { "realm": "myc-convert", "bearer-only": true, - "auth-server-url": "http://0.0.0.0:8080/", + "auth-server-url": "https://idpmyc.fabmob.io:8443/", "ssl-required": "external", "resource": "node-microservice", "confidential-port": 0 diff --git a/data.db b/data.db new file mode 100644 index 0000000..b79efda Binary files /dev/null and b/data.db differ diff --git a/frontend/.DS_Store b/frontend/.DS_Store new file mode 100644 index 0000000..e4b8dbf Binary files /dev/null and b/frontend/.DS_Store differ diff --git a/frontend/public/.DS_Store b/frontend/public/.DS_Store new file mode 100644 index 0000000..4cbae86 Binary files /dev/null and b/frontend/public/.DS_Store differ diff --git a/frontend/public/GitHub-Mark-32px.png b/frontend/public/_bin/GitHub-Mark-32px.png similarity index 100% rename from frontend/public/GitHub-Mark-32px.png rename to frontend/public/_bin/GitHub-Mark-32px.png diff --git a/frontend/public/afd.png b/frontend/public/_bin/afd.png similarity index 100% rename from frontend/public/afd.png rename to frontend/public/_bin/afd.png diff --git a/frontend/public/duplicate.svg b/frontend/public/_bin/duplicate.svg similarity index 100% rename from frontend/public/duplicate.svg rename to frontend/public/_bin/duplicate.svg diff --git a/frontend/public/giz.svg b/frontend/public/_bin/giz.svg similarity index 100% rename from frontend/public/giz.svg rename to frontend/public/_bin/giz.svg diff --git a/frontend/public/icon_dl_csv.svg b/frontend/public/_bin/icon_dl_csv.svg similarity index 100% rename from frontend/public/icon_dl_csv.svg rename to frontend/public/_bin/icon_dl_csv.svg diff --git a/frontend/public/icon_dl_image.svg b/frontend/public/_bin/icon_dl_image.svg similarity index 100% rename from frontend/public/icon_dl_image.svg rename to frontend/public/_bin/icon_dl_image.svg diff --git a/frontend/public/icon_spinner.svg b/frontend/public/_bin/icon_spinner.svg similarity index 100% rename from frontend/public/icon_spinner.svg rename to frontend/public/_bin/icon_spinner.svg diff --git a/frontend/public/partners/ADB_logo.png b/frontend/public/_bin/partners/ADB_logo.png similarity index 100% rename from frontend/public/partners/ADB_logo.png rename to frontend/public/_bin/partners/ADB_logo.png diff --git a/frontend/public/partners/AFD_logo.png b/frontend/public/_bin/partners/AFD_logo.png similarity index 100% rename from frontend/public/partners/AFD_logo.png rename to frontend/public/_bin/partners/AFD_logo.png diff --git a/frontend/public/partners/Ademe2020_GB_RVB.jpg b/frontend/public/_bin/partners/Ademe2020_GB_RVB.jpg similarity index 100% rename from frontend/public/partners/Ademe2020_GB_RVB.jpg rename to frontend/public/_bin/partners/Ademe2020_GB_RVB.jpg diff --git a/frontend/public/partners/BMUV-Logo_en_2022.jpg b/frontend/public/_bin/partners/BMUV-Logo_en_2022.jpg similarity index 100% rename from frontend/public/partners/BMUV-Logo_en_2022.jpg rename to frontend/public/_bin/partners/BMUV-Logo_en_2022.jpg diff --git a/frontend/public/partners/BMZ-Logo_en.png b/frontend/public/_bin/partners/BMZ-Logo_en.png similarity index 100% rename from frontend/public/partners/BMZ-Logo_en.png rename to frontend/public/_bin/partners/BMZ-Logo_en.png diff --git a/frontend/public/partners/Cerema_horizontal_ENG_RVB.jpg b/frontend/public/_bin/partners/Cerema_horizontal_ENG_RVB.jpg similarity index 100% rename from frontend/public/partners/Cerema_horizontal_ENG_RVB.jpg rename to frontend/public/_bin/partners/Cerema_horizontal_ENG_RVB.jpg diff --git a/frontend/public/partners/Codatu_logo.png b/frontend/public/_bin/partners/Codatu_logo.png similarity index 100% rename from frontend/public/partners/Codatu_logo.png rename to frontend/public/_bin/partners/Codatu_logo.png diff --git a/frontend/public/partners/DT4A_logo.png b/frontend/public/_bin/partners/DT4A_logo.png similarity index 100% rename from frontend/public/partners/DT4A_logo.png rename to frontend/public/_bin/partners/DT4A_logo.png diff --git a/frontend/public/partners/ECF_logo.jpg b/frontend/public/_bin/partners/ECF_logo.jpg similarity index 100% rename from frontend/public/partners/ECF_logo.jpg rename to frontend/public/_bin/partners/ECF_logo.jpg diff --git a/frontend/public/partners/EUROMED 11.2017.png b/frontend/public/_bin/partners/EUROMED 11.2017.png similarity index 100% rename from frontend/public/partners/EUROMED 11.2017.png rename to frontend/public/_bin/partners/EUROMED 11.2017.png diff --git a/frontend/public/partners/Euroclima+_logo.jpg b/frontend/public/_bin/partners/Euroclima+_logo.jpg similarity index 100% rename from frontend/public/partners/Euroclima+_logo.jpg rename to frontend/public/_bin/partners/Euroclima+_logo.jpg diff --git a/frontend/public/partners/European-bank-for-reconstruction-and-development-ebrd-vector-logo-horizontal.png b/frontend/public/_bin/partners/European-bank-for-reconstruction-and-development-ebrd-vector-logo-horizontal.png similarity index 100% rename from frontend/public/partners/European-bank-for-reconstruction-and-development-ebrd-vector-logo-horizontal.png rename to frontend/public/_bin/partners/European-bank-for-reconstruction-and-development-ebrd-vector-logo-horizontal.png diff --git a/frontend/public/partners/EuropeanCommission_logo.jpg b/frontend/public/_bin/partners/EuropeanCommission_logo.jpg similarity index 100% rename from frontend/public/partners/EuropeanCommission_logo.jpg rename to frontend/public/_bin/partners/EuropeanCommission_logo.jpg diff --git a/frontend/public/partners/European_Cyclists_Federation_logo.svg.png b/frontend/public/_bin/partners/European_Cyclists_Federation_logo.svg.png similarity index 100% rename from frontend/public/partners/European_Cyclists_Federation_logo.svg.png rename to frontend/public/_bin/partners/European_Cyclists_Federation_logo.svg.png diff --git a/frontend/public/partners/FFEM_logo.jpg b/frontend/public/_bin/partners/FFEM_logo.jpg similarity index 100% rename from frontend/public/partners/FFEM_logo.jpg rename to frontend/public/_bin/partners/FFEM_logo.jpg diff --git a/frontend/public/partners/GIZ_logo.jpg b/frontend/public/_bin/partners/GIZ_logo.jpg similarity index 100% rename from frontend/public/partners/GIZ_logo.jpg rename to frontend/public/_bin/partners/GIZ_logo.jpg diff --git a/frontend/public/partners/GPIT_logo.png b/frontend/public/_bin/partners/GPIT_logo.png similarity index 100% rename from frontend/public/partners/GPIT_logo.png rename to frontend/public/_bin/partners/GPIT_logo.png diff --git a/frontend/public/partners/ITDP_logo.png b/frontend/public/_bin/partners/ITDP_logo.png similarity index 100% rename from frontend/public/partners/ITDP_logo.png rename to frontend/public/_bin/partners/ITDP_logo.png diff --git a/frontend/public/partners/KfW_logo.png b/frontend/public/_bin/partners/KfW_logo.png similarity index 100% rename from frontend/public/partners/KfW_logo.png rename to frontend/public/_bin/partners/KfW_logo.png diff --git a/frontend/public/partners/Marrakech_Partnership.png b/frontend/public/_bin/partners/Marrakech_Partnership.png similarity index 100% rename from frontend/public/partners/Marrakech_Partnership.png rename to frontend/public/_bin/partners/Marrakech_Partnership.png diff --git a/frontend/public/partners/Min_transition_ecologique_logo.jpg b/frontend/public/_bin/partners/Min_transition_ecologique_logo.jpg similarity index 100% rename from frontend/public/partners/Min_transition_ecologique_logo.jpg rename to frontend/public/_bin/partners/Min_transition_ecologique_logo.jpg diff --git a/frontend/public/partners/Platforma_logo.png b/frontend/public/_bin/partners/Platforma_logo.png similarity index 100% rename from frontend/public/partners/Platforma_logo.png rename to frontend/public/_bin/partners/Platforma_logo.png diff --git a/frontend/public/partners/Ssatp_logo.jpg b/frontend/public/_bin/partners/Ssatp_logo.jpg similarity index 100% rename from frontend/public/partners/Ssatp_logo.jpg rename to frontend/public/_bin/partners/Ssatp_logo.jpg diff --git a/frontend/public/partners/SuM4All_logo.png b/frontend/public/_bin/partners/SuM4All_logo.png similarity index 100% rename from frontend/public/partners/SuM4All_logo.png rename to frontend/public/_bin/partners/SuM4All_logo.png diff --git a/frontend/public/partners/TUMI_logo.jpg b/frontend/public/_bin/partners/TUMI_logo.jpg similarity index 100% rename from frontend/public/partners/TUMI_logo.jpg rename to frontend/public/_bin/partners/TUMI_logo.jpg diff --git a/frontend/public/partners/Trufi_association_logo.png b/frontend/public/_bin/partners/Trufi_association_logo.png similarity index 100% rename from frontend/public/partners/Trufi_association_logo.png rename to frontend/public/_bin/partners/Trufi_association_logo.png diff --git a/frontend/public/partners/UCLG_logo.jpg b/frontend/public/_bin/partners/UCLG_logo.jpg similarity index 100% rename from frontend/public/partners/UCLG_logo.jpg rename to frontend/public/_bin/partners/UCLG_logo.jpg diff --git a/frontend/public/partners/UNHABITAT_logo.jpg b/frontend/public/_bin/partners/UNHABITAT_logo.jpg similarity index 100% rename from frontend/public/partners/UNHABITAT_logo.jpg rename to frontend/public/_bin/partners/UNHABITAT_logo.jpg diff --git a/frontend/public/partners/WUPPERTAL_logo.png b/frontend/public/_bin/partners/WUPPERTAL_logo.png similarity index 100% rename from frontend/public/partners/WUPPERTAL_logo.png rename to frontend/public/_bin/partners/WUPPERTAL_logo.png diff --git a/frontend/public/asi-approach-diagram.png b/frontend/public/asi-approach-diagram.png deleted file mode 100644 index caeb22c..0000000 Binary files a/frontend/public/asi-approach-diagram.png and /dev/null differ diff --git a/frontend/public/asif-framework-diagram.png b/frontend/public/asif-framework-diagram.png deleted file mode 100644 index a32532f..0000000 Binary files a/frontend/public/asif-framework-diagram.png and /dev/null differ diff --git a/frontend/public/fonts/.DS_Store b/frontend/public/fonts/.DS_Store new file mode 100644 index 0000000..f3b797d Binary files /dev/null and b/frontend/public/fonts/.DS_Store differ diff --git a/frontend/public/fonts/GravurCondensed/GravurCondensed-Black.otf b/frontend/public/fonts/GravurCondensed/GravurCondensed-Black.otf new file mode 100644 index 0000000..a6cf677 Binary files /dev/null and b/frontend/public/fonts/GravurCondensed/GravurCondensed-Black.otf differ diff --git a/frontend/public/fonts/GravurCondensed/GravurCondensed-Bold.otf b/frontend/public/fonts/GravurCondensed/GravurCondensed-Bold.otf new file mode 100644 index 0000000..8613168 Binary files /dev/null and b/frontend/public/fonts/GravurCondensed/GravurCondensed-Bold.otf differ diff --git a/frontend/public/fonts/GravurCondensed/GravurCondensed-Light.otf b/frontend/public/fonts/GravurCondensed/GravurCondensed-Light.otf new file mode 100644 index 0000000..acdecf8 Binary files /dev/null and b/frontend/public/fonts/GravurCondensed/GravurCondensed-Light.otf differ diff --git a/frontend/public/fonts/GravurCondensed/GravurCondensed-Regular.otf b/frontend/public/fonts/GravurCondensed/GravurCondensed-Regular.otf new file mode 100644 index 0000000..306f084 Binary files /dev/null and b/frontend/public/fonts/GravurCondensed/GravurCondensed-Regular.otf differ diff --git a/frontend/public/fonts/GravurCondensed/GravurCondensed-Thin.otf b/frontend/public/fonts/GravurCondensed/GravurCondensed-Thin.otf new file mode 100644 index 0000000..10722f2 Binary files /dev/null and b/frontend/public/fonts/GravurCondensed/GravurCondensed-Thin.otf differ diff --git a/frontend/public/icons.svg b/frontend/public/icons.svg new file mode 100644 index 0000000..0d55716 --- /dev/null +++ b/frontend/public/icons.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/index.html b/frontend/public/index.html index 32411ab..13336a5 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -13,7 +13,7 @@ - Mobilise Your City GHG calculator + MYC GHG emissions calculator diff --git a/frontend/public/logos/logo-ADB.png b/frontend/public/logos/logo-ADB.png new file mode 100644 index 0000000..7274cad Binary files /dev/null and b/frontend/public/logos/logo-ADB.png differ diff --git a/frontend/public/logos/logo-ADEME.png b/frontend/public/logos/logo-ADEME.png new file mode 100644 index 0000000..deee140 Binary files /dev/null and b/frontend/public/logos/logo-ADEME.png differ diff --git a/frontend/public/logos/logo-AFD.png b/frontend/public/logos/logo-AFD.png new file mode 100644 index 0000000..27fbd83 Binary files /dev/null and b/frontend/public/logos/logo-AFD.png differ diff --git a/frontend/public/logos/logo-Cerema.png b/frontend/public/logos/logo-Cerema.png new file mode 100644 index 0000000..d2d113f Binary files /dev/null and b/frontend/public/logos/logo-Cerema.png differ diff --git a/frontend/public/logos/logo-Codatu.png b/frontend/public/logos/logo-Codatu.png new file mode 100644 index 0000000..c06fc57 Binary files /dev/null and b/frontend/public/logos/logo-Codatu.png differ diff --git a/frontend/public/logos/logo-DTFA.png b/frontend/public/logos/logo-DTFA.png new file mode 100644 index 0000000..b168354 Binary files /dev/null and b/frontend/public/logos/logo-DTFA.png differ diff --git a/frontend/public/logos/logo-EB.png b/frontend/public/logos/logo-EB.png new file mode 100644 index 0000000..6a5d836 Binary files /dev/null and b/frontend/public/logos/logo-EB.png differ diff --git a/frontend/public/logos/logo-ECF.png b/frontend/public/logos/logo-ECF.png new file mode 100644 index 0000000..40178de Binary files /dev/null and b/frontend/public/logos/logo-ECF.png differ diff --git a/frontend/public/logos/logo-EU.png b/frontend/public/logos/logo-EU.png new file mode 100644 index 0000000..f04ddc3 Binary files /dev/null and b/frontend/public/logos/logo-EU.png differ diff --git a/frontend/public/logos/logo-Euroclima.png b/frontend/public/logos/logo-Euroclima.png new file mode 100644 index 0000000..b9670ba Binary files /dev/null and b/frontend/public/logos/logo-Euroclima.png differ diff --git a/frontend/public/logos/logo-FFEM.png b/frontend/public/logos/logo-FFEM.png new file mode 100644 index 0000000..d8cb780 Binary files /dev/null and b/frontend/public/logos/logo-FFEM.png differ diff --git a/frontend/public/logos/logo-FMECD.png b/frontend/public/logos/logo-FMECD.png new file mode 100644 index 0000000..0a6de49 Binary files /dev/null and b/frontend/public/logos/logo-FMECD.png differ diff --git a/frontend/public/logos/logo-FMENCNSCP.png b/frontend/public/logos/logo-FMENCNSCP.png new file mode 100644 index 0000000..2fc1389 Binary files /dev/null and b/frontend/public/logos/logo-FMENCNSCP.png differ diff --git a/frontend/public/logos/logo-GIZ.png b/frontend/public/logos/logo-GIZ.png new file mode 100644 index 0000000..bc1ce10 Binary files /dev/null and b/frontend/public/logos/logo-GIZ.png differ diff --git a/frontend/public/logos/logo-GPIT.png b/frontend/public/logos/logo-GPIT.png new file mode 100644 index 0000000..a4d6244 Binary files /dev/null and b/frontend/public/logos/logo-GPIT.png differ diff --git a/frontend/public/ifeu.gif b/frontend/public/logos/logo-IFEU.gif similarity index 100% rename from frontend/public/ifeu.gif rename to frontend/public/logos/logo-IFEU.gif diff --git a/frontend/public/logos/logo-ITDP.png b/frontend/public/logos/logo-ITDP.png new file mode 100644 index 0000000..1f41c6c Binary files /dev/null and b/frontend/public/logos/logo-ITDP.png differ diff --git a/frontend/public/logos/logo-KFW.png b/frontend/public/logos/logo-KFW.png new file mode 100644 index 0000000..93f51e2 Binary files /dev/null and b/frontend/public/logos/logo-KFW.png differ diff --git a/frontend/public/logos/logo-MP.png b/frontend/public/logos/logo-MP.png new file mode 100644 index 0000000..e532198 Binary files /dev/null and b/frontend/public/logos/logo-MP.png differ diff --git a/frontend/public/logos/logo-MTE.png b/frontend/public/logos/logo-MTE.png new file mode 100644 index 0000000..5efe010 Binary files /dev/null and b/frontend/public/logos/logo-MTE.png differ diff --git a/frontend/public/mobiliseyourcity.png b/frontend/public/logos/logo-MYC.png similarity index 100% rename from frontend/public/mobiliseyourcity.png rename to frontend/public/logos/logo-MYC.png diff --git a/frontend/public/logos/logo-Platforma.png b/frontend/public/logos/logo-Platforma.png new file mode 100644 index 0000000..355cee0 Binary files /dev/null and b/frontend/public/logos/logo-Platforma.png differ diff --git a/frontend/public/logos/logo-SMFA.png b/frontend/public/logos/logo-SMFA.png new file mode 100644 index 0000000..ec492a3 Binary files /dev/null and b/frontend/public/logos/logo-SMFA.png differ diff --git a/frontend/public/logos/logo-SSATP.png b/frontend/public/logos/logo-SSATP.png new file mode 100644 index 0000000..eb3d539 Binary files /dev/null and b/frontend/public/logos/logo-SSATP.png differ diff --git a/frontend/public/logos/logo-TRUFI.png b/frontend/public/logos/logo-TRUFI.png new file mode 100644 index 0000000..5a9bf39 Binary files /dev/null and b/frontend/public/logos/logo-TRUFI.png differ diff --git a/frontend/public/logos/logo-TUMI.png b/frontend/public/logos/logo-TUMI.png new file mode 100644 index 0000000..feafe9d Binary files /dev/null and b/frontend/public/logos/logo-TUMI.png differ diff --git a/frontend/public/logos/logo-UCLG.png b/frontend/public/logos/logo-UCLG.png new file mode 100644 index 0000000..c60588d Binary files /dev/null and b/frontend/public/logos/logo-UCLG.png differ diff --git a/frontend/public/logos/logo-UNH.png b/frontend/public/logos/logo-UNH.png new file mode 100644 index 0000000..c2b5506 Binary files /dev/null and b/frontend/public/logos/logo-UNH.png differ diff --git a/frontend/public/logos/logo-WI.png b/frontend/public/logos/logo-WI.png new file mode 100644 index 0000000..6dcc3ac Binary files /dev/null and b/frontend/public/logos/logo-WI.png differ diff --git a/frontend/public/fabmob.png b/frontend/public/logos/logo-fabmob.png similarity index 100% rename from frontend/public/fabmob.png rename to frontend/public/logos/logo-fabmob.png diff --git a/frontend/public/logos/logo-github.svg b/frontend/public/logos/logo-github.svg new file mode 100644 index 0000000..4655756 --- /dev/null +++ b/frontend/public/logos/logo-github.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/public/navbar-toggler-icon.svg b/frontend/public/navbar-toggler-icon.svg new file mode 100644 index 0000000..9a08a4e --- /dev/null +++ b/frontend/public/navbar-toggler-icon.svg @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/frontend/public/pictures/.DS_Store b/frontend/public/pictures/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/frontend/public/pictures/.DS_Store differ diff --git a/frontend/public/pictures/asi-approach-diagram.png b/frontend/public/pictures/asi-approach-diagram.png new file mode 100644 index 0000000..5d0abcf Binary files /dev/null and b/frontend/public/pictures/asi-approach-diagram.png differ diff --git a/frontend/public/pictures/asif-framework-diagram.png b/frontend/public/pictures/asif-framework-diagram.png new file mode 100644 index 0000000..82cd598 Binary files /dev/null and b/frontend/public/pictures/asif-framework-diagram.png differ diff --git a/frontend/public/pictures/frame-364-1.png b/frontend/public/pictures/frame-364-1.png new file mode 100644 index 0000000..d559336 Binary files /dev/null and b/frontend/public/pictures/frame-364-1.png differ diff --git a/frontend/public/pictures/frame-364-2.png b/frontend/public/pictures/frame-364-2.png new file mode 100644 index 0000000..376453a Binary files /dev/null and b/frontend/public/pictures/frame-364-2.png differ diff --git a/frontend/public/pictures/frame-364-3.png b/frontend/public/pictures/frame-364-3.png new file mode 100644 index 0000000..2d58e32 Binary files /dev/null and b/frontend/public/pictures/frame-364-3.png differ diff --git a/frontend/public/pictures/homepage-hero-bg.svg b/frontend/public/pictures/homepage-hero-bg.svg new file mode 100644 index 0000000..0563ee2 --- /dev/null +++ b/frontend/public/pictures/homepage-hero-bg.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/pictures/homepage-hero-illu-1.png b/frontend/public/pictures/homepage-hero-illu-1.png new file mode 100644 index 0000000..2970be0 Binary files /dev/null and b/frontend/public/pictures/homepage-hero-illu-1.png differ diff --git a/frontend/public/pictures/homepage-hero-illu-2.png b/frontend/public/pictures/homepage-hero-illu-2.png new file mode 100644 index 0000000..795a788 Binary files /dev/null and b/frontend/public/pictures/homepage-hero-illu-2.png differ diff --git a/frontend/public/methodology.svg b/frontend/public/pictures/methodology.svg similarity index 100% rename from frontend/public/methodology.svg rename to frontend/public/pictures/methodology.svg diff --git a/frontend/public/myc_map.png b/frontend/public/pictures/myc_map.png similarity index 100% rename from frontend/public/myc_map.png rename to frontend/public/pictures/myc_map.png diff --git a/frontend/public/myc_map_transparent.png b/frontend/public/pictures/myc_map_transparent.png similarity index 100% rename from frontend/public/myc_map_transparent.png rename to frontend/public/pictures/myc_map_transparent.png diff --git a/frontend/public/myc_partners.pdf b/frontend/public/pictures/myc_partners.pdf similarity index 100% rename from frontend/public/myc_partners.pdf rename to frontend/public/pictures/myc_partners.pdf diff --git a/frontend/src/App.css b/frontend/src/App.css index efd1a34..0e2ea96 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,7 +1,3 @@ -/* .App { - text-align: center; -} */ - .App-logo { height: 40vmin; pointer-events: none; @@ -26,8 +22,4 @@ .App-link { color: #61dafb; -} - -.nav-link { - margin: 0 10px 0 10px } \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6dd895f..635b65c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,7 +1,6 @@ import React from 'react' import './App.css' import Projects from './pages/Projects' -import CreateProject from './pages/CreateProject' import WelcomePage from './pages/WelcomePage' import InventoryIntro from './pages/Inventory/InventoryIntro' import InventoryStep1 from './pages/Inventory/InventoryStep1' @@ -38,23 +37,28 @@ import Nav from "./components/Nav" import { BrowserRouter, Route, Routes } from "react-router-dom" import { ReactKeycloakProvider } from "@react-keycloak/web" import keycloak from "./Keycloak" -import ProjectSummary from './pages/ProjectSummary' -import ProjectCompare from './pages/ProjectCompare' +import Project from './pages/Project' +// import Footer from "./components/Footer" + +// adrien test 231021 to reset scroll on every page change +import ScrollToTop from "./scrollToTop"; + function App() { return (
+
diff --git a/frontend/src/Keycloak.ts b/frontend/src/Keycloak.ts index 498b190..d911340 100644 --- a/frontend/src/Keycloak.ts +++ b/frontend/src/Keycloak.ts @@ -1,7 +1,7 @@ import Keycloak from "keycloak-js"; const keycloak = new Keycloak({ "realm": "myc-convert", - "url": "http://0.0.0.0:8080/", + "url": "https://idpmyc.fabmob.io:8443/", "clientId": "react-web-app" }); diff --git a/frontend/src/components/ChoiceModal.tsx b/frontend/src/components/ChoiceModal.tsx index 405d462..d251fdb 100644 --- a/frontend/src/components/ChoiceModal.tsx +++ b/frontend/src/components/ChoiceModal.tsx @@ -1,5 +1,5 @@ -import React, {useState} from 'react' -import { Modal, Form, InputGroup, Button, Badge } from "react-bootstrap" +import React, {useState, useEffect} from 'react' +import { Modal, Form, InputGroup, Badge } from "react-bootstrap" const ChoiceModal = (props: { showModal: boolean, @@ -11,6 +11,12 @@ const ChoiceModal = (props: { }) => { const [inputVal, setInputVal] = useState("") const [validated, setValidated] = useState(false) + useEffect(() => { + if(props.showModal === true) { + // Clear validation when reopening popup + setValidated(false) + } + }, [props.showModal]) const checkFrom = (event: React.FormEvent) => { event.preventDefault() event.stopPropagation() @@ -27,16 +33,14 @@ const ChoiceModal = (props: { props.callback(choice) } return ( - props.setShowModal(false)}> + props.setShowModal(false)}>
- - + {/* */} { props.preventCreate ? Select an option : - Select an option or create one {props.valRange ? setInputVal(e.target.value)} /> @@ -47,18 +51,21 @@ const ChoiceModal = (props: { Please enter a source, select an existing one or close this modal. } + Select an option or create one } - {props.availableChoices && props.availableChoices.map(choice => ( -
setChoice(choice)} bg="info">+ {choice}
- )) - } +
+ {props.availableChoices && props.availableChoices.map(choice => ( +
setChoice(choice)} className="badge-default">{choice}
+ )) + } +
- + {/* - + */}
) diff --git a/frontend/src/components/DescAndNav.tsx b/frontend/src/components/DescAndNav.tsx index 5a64fd8..0aef5d2 100644 --- a/frontend/src/components/DescAndNav.tsx +++ b/frontend/src/components/DescAndNav.tsx @@ -2,7 +2,7 @@ import React from "react" import { useNavigate } from "react-router-dom" import { Button, Col, Row } from 'react-bootstrap' -type NavButton = {link?: string, variant: string, content: string, trigger?: Function} +type NavButton = {link?: string, variant: string, content: string, showArrow?: boolean, trigger?: Function} export default function DescAndNav (props: {children: React.ReactNode, prevNav?: NavButton, nextNav?: NavButton, seeMoreCallBack?: Function}) { const navigate = useNavigate() const handleClick = (nav: NavButton | undefined) => { @@ -16,24 +16,34 @@ export default function DescAndNav (props: {children: React.ReactNode, prevNav?: } } return ( - - - {props.children} + + +
+ {props.children}{props.seeMoreCallBack && + } +
- - - - {props.prevNav && } - - - {props.nextNav && } - - - - - {props.seeMoreCallBack && } - - + +
+
+ {props.prevNav && } +
+
+ {props.nextNav && } +
+
) diff --git a/frontend/src/components/EditEmissionFactors.tsx b/frontend/src/components/EditEmissionFactors.tsx index f686879..f32f0d5 100644 --- a/frontend/src/components/EditEmissionFactors.tsx +++ b/frontend/src/components/EditEmissionFactors.tsx @@ -2,7 +2,6 @@ import React, {useState, useEffect} from "react" import { ProjectType, InputInventoryStep7, FuelType} from "../frontendTypes" import {Table, Button, Badge, Form, Modal} from 'react-bootstrap' import ChoiceModal from "./ChoiceModal" -import TdDiagonalBar from "./TdDiagonalBar" import ValidSource from "./ValidSource" import ItemWithOverlay from "./ItemWithOverlay" @@ -97,12 +96,12 @@ export default function EditEmissionFactors (props: { } return (
- + setShowEmissionFactorsModal(false)}> Edit Emission factors - +

Emission factors are the last data you need to complete your GHG emissions calculation. Specific GHG emission factors (in kgCO2eq/TJ) apply according to the different fuel types (gasoline, diesel, CNG, LNG).

@@ -110,17 +109,26 @@ export default function EditEmissionFactors (props: { The table already integrates international ones that you can use as default values if you don’t have specific values.

- We would recommend to check out the to obtain those factors first. + We would recommend to check out the to obtain those factors first.

+ + {/* Fuels */} + {/* Source */} + {/* Lower heating value */} + {/* Fuel density */} + {/* CO2e TTW */} + {/* CO2e WTW (kg/Tj) */} + - - - - - - + + + + + + @@ -128,11 +136,11 @@ export default function EditEmissionFactors (props: { const ftype = ftypeString as FuelType const fuel = props.inputData.emissionFactors.WTW[ftype] return - + @@ -140,22 +148,30 @@ export default function EditEmissionFactors (props: { })} + + + + + + + +
🛈 Fuels🛈 SrcLower heating value (TJ/1000t or MJ/kWh)Fuel density (kg/kg or kg/l)🛈 CO2e TTW (kg/Tj)🛈 CO2e WTW (kg/Tj)FuelsSrcLower heating value (TJ/1000t or MJ/kWh)Fuel density (kg/kg or kg/l)CO2e TTW (kg/Tj)CO2e WTW (kg/Tj)
{ftype}{ftype} {fuel.source ? configureSource(ftype)}/> - : } + : } updateInput(ftype, "lowerHeatingValue", e.target.value)}> updateInput(ftype, "density", e.target.value)}> updateInput(ftype, "ges", e.target.value, 'WTW')}>
{props.inputData.note === undefined - ? + ? : - + setNote(e.target.value)} /> }
@@ -169,14 +185,14 @@ export default function EditEmissionFactors (props: { Emission factors methodology - +

GHG emissions consider CO2, CH4 and N20 that are transformed into kgCO2eq/TJ.

CO2eq is an index created by IPCC to simplify the comparison and cumulate all of the gases in one index. In order to obtain this data, you need to convert other gases emissions in CO2 emissions, multiplying the emission factors by the Global Warming Potential of the gas. It is a measure of how much energy the emissions of 1 ton of a gas will absorb over a given period of time, relative to the emissions of 1 ton of carbon dioxide (CO2) (EPA, 2022).

CH4 have a GWP of 28 years average and N20 have a GWP of 265 average. This means that 1ton of CH4 emitted represents 28 tons of CO2.

diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx new file mode 100644 index 0000000..0552ecc --- /dev/null +++ b/frontend/src/components/Footer.tsx @@ -0,0 +1,48 @@ +import React from "react"; + +const MyFooter = () => { + return ( + + ); +}; + +export default MyFooter; diff --git a/frontend/src/components/ItemWithOverlay.tsx b/frontend/src/components/ItemWithOverlay.tsx index d20c935..a36dc71 100644 --- a/frontend/src/components/ItemWithOverlay.tsx +++ b/frontend/src/components/ItemWithOverlay.tsx @@ -1,17 +1,16 @@ import React from "react" -import { useNavigate } from "react-router-dom" import { OverlayTrigger, Tooltip } from 'react-bootstrap' export default function ItemWithOverlay (props: {children: React.ReactNode, overlayContent: React.ReactNode}) { const tooltip = ( -
{props.children}
-
{props.overlayContent}
+
{props.children}
+
{props.overlayContent}
) return ( - - {props.children} + + {props.children} ) } diff --git a/frontend/src/components/Nav.tsx b/frontend/src/components/Nav.tsx index 3781ab3..b5ecc2f 100644 --- a/frontend/src/components/Nav.tsx +++ b/frontend/src/components/Nav.tsx @@ -1,10 +1,12 @@ import React from "react"; import { useKeycloak } from "@react-keycloak/web"; import Navbar from 'react-bootstrap/Navbar'; -import Container from 'react-bootstrap/Container'; +import { Container, Row, Col } from 'react-bootstrap'; import Nav from 'react-bootstrap/Nav'; import NavDropdown from 'react-bootstrap/NavDropdown'; import { NavLink } from "react-router-dom"; +import Button from 'react-bootstrap/Button'; +import { useLocation } from "react-router-dom"; const MyNav = () => { @@ -14,29 +16,57 @@ const MyNav = () => { if (roles && roles.indexOf('app-admin') > -1) { isAdmin = true } + /* adrien : discriminate homepage nav */ + const {pathname} = useLocation(); return ( - +
- - Moblise Your City - MYC GHG Emissions Calculator - - - - - {!!keycloak.authenticated && ( - - )} - + + + + + {/* Mobilise Your City */} + MYC GHG emissions calculator + + + + + {keycloak.authenticated ? + + : + + + + } + + + + + + + + + + + - +
); }; diff --git a/frontend/src/components/OutputNumberTd.tsx b/frontend/src/components/OutputNumberTd.tsx new file mode 100644 index 0000000..4cb0ebf --- /dev/null +++ b/frontend/src/components/OutputNumberTd.tsx @@ -0,0 +1,23 @@ +import React from "react" + + +const OutputNumberTd = ({value, decimals, ...props}: {value: number|string, decimals?: number, rowSpan?: number, style?: React.CSSProperties, cls?: string}) => { + const dec = decimals != undefined ? decimals : 3 + if (typeof value === "string") + value = parseFloat(value) + let roundedVal = "" + let fullVal = "" + if (!isNaN(value) && value != null) { + roundedVal = value.toFixed(dec) + fullVal = value.toFixed(Math.max(dec, (value.toString().split('.')[1] || []).length)) + } + const className = "outputNumber" + (props.cls ? " " + props.cls : "") + return ( + + {roundedVal} + {fullVal} + + ) +} + +export default OutputNumberTd diff --git a/frontend/src/components/Progress.css b/frontend/src/components/Progress.css deleted file mode 100644 index 0ff954a..0000000 --- a/frontend/src/components/Progress.css +++ /dev/null @@ -1,24 +0,0 @@ - - -.progressMenu .stepDisabled { - color: lightgray !important; -} -.progressMenu .currentStep { - font-weight: bold; -} -.progressMenu .currentStepDone { - font-weight: bold; -} -.progressMenu .currentStepDone:after { - content: '\2713'; - display: inline-block; - padding-left: 2px; - padding-right: 2px; -} - -.progressMenu .stepDone:after { - content: '\2713'; - display: inline-block; - padding-left: 2px; - padding-right: 2px; -} diff --git a/frontend/src/components/Progress.tsx b/frontend/src/components/Progress.tsx index bb04a9c..c6f4b05 100644 --- a/frontend/src/components/Progress.tsx +++ b/frontend/src/components/Progress.tsx @@ -2,7 +2,6 @@ import React from "react"; import { useNavigate } from "react-router-dom" import {Button, Row, Col,ProgressBar} from 'react-bootstrap' import {ProjectType, ProjectStage} from '../frontendTypes' -import './Progress.css' const steps : {[stage in ProjectStage]: string[]} = { "Inventory": [ @@ -31,12 +30,12 @@ const steps : {[stage in ProjectStage]: string[]} = { ] } const withoutUpstreamClimateSteps = [ - "1.1 Use of vehicles : avoided", - "2.1 Use of vehicles : added", - "2.2 vehicles load", - "2.3 vehicles shift", - "3.1 Fuel breakdown", - "3.2 Fuel consumption factors", + "Use of vehicles : avoided", + "Use of vehicles : added", + "Vehicles load", + "Vehicles shift", + "Fuel breakdown", + "Fuel consumption factors", "Results" ] @@ -56,6 +55,19 @@ const Progress = (props: {project: ProjectType, stage: ProjectStage, currentStep navigate('/project/' + props.project.id + '/' + props.stage + '/step/' + step) } } + let projectStep = props.project.stages?.[props.stage][0]?.step + if (props.project.stages?.[props.stage][0]?.step !== undefined && props.stage === "Climate") { + // climate has a hidden step 0, With or Without upstream, so one has to be removed from the counter + projectStep = props.project.stages?.[props.stage][props.climateScenarioId || 0]?.step + + projectStep -= 1 + } + + const stepsToUse = props.isWithoutUpstream ? withoutUpstreamClimateSteps : steps[props.stage] + if (props.project.stages?.[props.stage][0]?.step !== undefined && stepsToUse[projectStep-1] === "Results") { + // skip results + projectStep += 1 + } const getClassName = (step: number) => { // if (props.project.stages[props.stage][0].step === 9) { // // Step 9: overview is done by default @@ -63,53 +75,65 @@ const Progress = (props: {project: ProjectType, stage: ProjectStage, currentStep // } // const isStepResults = steps[props.stage][props.currentStep-1] === "Results" if (props.currentStep === step) { - if (props.project.stages?.[props.stage][0]?.step > props.currentStep ) { + if (projectStep > props.currentStep ) { return "currentStepDone" } return "currentStep" } - if (!(props.project.stages?.[props.stage][0]?.step > step)) { + // is the checked step equal to the current project progress + if (step === projectStep) { + // Not quite the current step, but still and accessible step (probably the next one), no need for specific class + return "" + } + // is the checked step later than the current project progress + if (projectStep === undefined || step > projectStep) { return "stepDisabled" } else { return "stepDone" } } - const progressValue = (props.project.stages?.[props.stage][0]?.step - 1) / steps[props.stage].length *100; - const stepsToUse = props.isWithoutUpstream ? withoutUpstreamClimateSteps : steps[props.stage] + const progressValue = (projectStep - 1) / stepsToUse.length *100; return (
- - -

{props.stage}

- - - - -
+
+

{props.stage}

+ +

- - + + + + - - Last saved {new Date(props.project.modifiedDate).toLocaleString()} + + Last saved : {new Date(props.project.modifiedDate).toLocaleString()} -
    - {stepsToUse.map((step, index) => ( -
  1. - -
  2. - ))} +
      + {stepsToUse.map((step, index) => { + const className = getClassName(index + 1) + return ( +
    1. { + if (projectStep < index + 1) return + return link((index + 1).toString()) + }}> + +
    2. + ) + })}
); diff --git a/frontend/src/components/ProjectNav.tsx b/frontend/src/components/ProjectNav.tsx index 8036ca1..f24047e 100644 --- a/frontend/src/components/ProjectNav.tsx +++ b/frontend/src/components/ProjectNav.tsx @@ -3,16 +3,16 @@ import { useNavigate } from "react-router-dom" import {ButtonGroup, Button} from 'react-bootstrap' import { ProjectType } from "../frontendTypes" -const ProjectNav = (props: {project: ProjectType, current: "Config" | "Edition" | "Compare"}) => { +const ProjectNav = (props: {project: ProjectType, current: "config" | "edit" | "viz"}) => { const navigate = useNavigate() const edit = () => navigate('/project/' + props.project.id + '/edit') const config = () => navigate('/project/' + props.project.id + '/config') const compare = () => navigate('/project/' + props.project.id + '/viz') return ( - - - + + + ) } diff --git a/frontend/src/components/ProjectStepContainerWrapper.tsx b/frontend/src/components/ProjectStepContainerWrapper.tsx index e376aba..5161213 100644 --- a/frontend/src/components/ProjectStepContainerWrapper.tsx +++ b/frontend/src/components/ProjectStepContainerWrapper.tsx @@ -2,8 +2,8 @@ import React from "react"; import {Container, Row, Col,Button, Form} from 'react-bootstrap' import {ProjectType, ProjectStage} from '../frontendTypes' import Progress from "./Progress" -import './Progress.css' import ItemWithOverlay from "./ItemWithOverlay"; +import Footer from "../components/Footer" const ProjectStepContainerWrapper = (props: { project: ProjectType, @@ -24,23 +24,35 @@ const ProjectStepContainerWrapper = (props: { }) } return ( - - - - - - - {props.children} - {props.setInputData && (props.noteValue === undefined - ? - : - - setNote(e.target.value)} /> - - )} - - - + <> +
+ + + + + + + {props.children} + {props.setInputData && (props.noteValue === undefined + ? + : + + setNote(e.target.value)} /> + + )} + {/* + */} +
+ {/* + */} + + + +
+ ) } diff --git a/frontend/src/components/ProjectViz.tsx b/frontend/src/components/ProjectViz.tsx deleted file mode 100644 index fb2e899..0000000 --- a/frontend/src/components/ProjectViz.tsx +++ /dev/null @@ -1,535 +0,0 @@ -import React, {useState, useEffect} from 'react' -import { useKeycloak } from "@react-keycloak/web" -import { useParams, useNavigate, Navigate } from "react-router-dom" -import { Container, Row, Col, Button, Card, Form, Alert, Badge } from 'react-bootstrap' -import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Tooltip, Legend, LabelList } from 'recharts'; -import { ProjectType, InputStep2 } from '../frontendTypes' -import { CSVLink } from "react-csv"; - -import '../pages/Project.css' -import '../print.css' - -type DataVkt = {[key: string]: number | string} -type DataModalShare = {[key : string]: number} & {name: number} - -export default function ProjectViz(props: {project: ProjectType}){ - const [project, setProject ] = useState({} as ProjectType) - const [typeOfGHGIsWTW, setTypeOfGHGIsWTW] = useState(true) - const [showPercents, setShowPercents] = useState(false) - const [showLabels, setShowLabels] = useState(false) - const [activeVtypesVkt, setActiveVtypesVkt] = useState([] as string[]) - const [dates, setDates] = useState([2020, 2025, 2030, 2035, 2040, 2050]) - const [csvExport, setCsvExport] = useState([] as string[][]) - const [dataVkt, setDataVkt] = useState([] as DataVkt[]) - const [colorsPerVtype, setColorsPerVtype] = useState({} as {[key: string]: string}) - const [dataPassengersModalShare, setDataPassengersModalShare] = useState([] as DataModalShare[]) - const [dataFreightModalShare, setDataFreightModalShare] = useState([] as DataModalShare[]) - const [activeVTypesPassengersModalShare, setActiveVTypesPassengersModalShare] = useState([] as string[]) - const [activeVTypesFreightModalShare, setActiveVTypesFreightModalShare] = useState([] as string[]) - const [dataEnergyWTW, setDataEnergyWTW] = useState([[], [], 0] as [string[], any[], number]) - const [dataEnergyTTW, setDataEnergyTTW] = useState([[], [], 0] as [string[], any[], number]) - const [dataEnergyDomain, setDataEnergyDomain] = useState([] as number[]) - - useEffect(() => { - init(props.project) - }, [props.project]) - const init = (_project: ProjectType) => { - console.log(_project) - setProject(_project) - if (!_project.stages || !_project.stages['Inventory'][0].steps) { - return - } - let _csvExport: string[][] = [] - _csvExport = [ - ["dataType", "dataName", "dataSource1", "dataSource2", "vehicleType", "fuelType"].concat(_project.referenceYears.map(e => e.toString())), - ["input", "population", _project.stages['Inventory'][0].steps[1].populationSource, _project.stages['Inventory'][0].steps[1].populationGrowthSource, "NA", "NA", _project.stages['Inventory'][0].steps[1].population].concat(_project.stages['Inventory'][0].steps[1].populationRate), - ["input", "gdp", _project.stages['Inventory'][0].steps[1].gdpSource, _project.stages['Inventory'][0].steps[1].gdpGrowthSource, "NA", "NA", _project.stages['Inventory'][0].steps[1].gdp].concat(_project.stages['Inventory'][0].steps[1].gdpRate), - ["output", "population", "computed", "", "NA", "NA"].concat((_project?.outputSocioEconomicDataComputed?.population || []).map(e => Math.round(e).toString())), - ["output", "gdp", "computed", "", "NA", "NA"].concat((_project?.outputSocioEconomicDataComputed?.gdp || []).map(e => e.toString())) - ] - let vehicleKilometresTravelledComputed = _project?.vehicleKilometresTravelledComputed || {} - let vtypesvkt = Object.keys(vehicleKilometresTravelledComputed) - let _activeVtypesVkt = [] as string[] - let _dataVkt : DataVkt[] = [ - {name: _project.referenceYears[0], total: 0, percent: "+0%"}, - {name: _project.referenceYears[1], total: 0, percent: ""}, - {name: _project.referenceYears[2], total: 0, percent: ""}, - {name: _project.referenceYears[3], total: 0, percent: ""}, - {name: _project.referenceYears[4], total: 0, percent: ""}, - {name: _project.referenceYears[5], total: 0, percent: ""} - ] - for (let i = 0; i < vtypesvkt.length; i++) { - let vtype = vtypesvkt[i] - if (!_project?.stages?.['Inventory']?.[0]?.steps?.[2]?.[vtype]?.isActive) - continue - for (let j = 0; j < 6; j++) { - if (vehicleKilometresTravelledComputed?.[vtype]?.[j]) { - _dataVkt[j][vtype] = Math.round((vehicleKilometresTravelledComputed?.[vtype]?.[j] || 0)) - _dataVkt[j].total = Math.round((vehicleKilometresTravelledComputed?.[vtype]?.[j] || 0)) + (_dataVkt[j].total as number) - if (_activeVtypesVkt.indexOf(vtypesvkt[i]) === -1) - _activeVtypesVkt.push(vtypesvkt[i]) - } - } - _csvExport.push(["output", "vkt", "computed", "", vtype, "NA"].concat(_dataVkt.map(e => e[vtype].toString()))) - if (_project.stages['Inventory'][0].steps[3][vtype].averageMileage && _project.stages['Inventory'][0].steps[3][vtype].averageMileage !== "0") { - _csvExport.push(["input", "averageMileage", _project.stages['Inventory'][0].steps[3].averageMileageSource, _project.stages['Inventory'][0].steps[3].vktGrowthSource, vtype, "NA", _project.stages['Inventory'][0].steps[3][vtype].averageMileage].concat(_project.stages['Inventory'][0].steps[3][vtype].vktRate)) - _csvExport.push(["input", "vehicleStock", _project.stages['Inventory'][0].steps[3].vehicleStockSource, _project.stages['Inventory'][0].steps[3].vktGrowthSource, vtype, "NA", _project.stages['Inventory'][0].steps[3][vtype].vehicleStock].concat(_project.stages['Inventory'][0].steps[3][vtype].vktRate)) - _csvExport.push(["input", "vkt", "computed", _project.stages['Inventory'][0].steps[3].vktGrowthSource, vtype, "NA", _project.stages['Inventory'][0].steps[3][vtype].vkt].concat(_project.stages['Inventory'][0].steps[3][vtype].vktRate)) - } else { - _csvExport.push(["input", "vkt", _project.stages['Inventory'][0].steps[3].vktSource, _project.stages['Inventory'][0].steps[3].vktGrowthSource, vtype, "NA", _project.stages['Inventory'][0].steps[3][vtype].vkt].concat(_project.stages['Inventory'][0].steps[3][vtype].vktRate)) - } - _csvExport.push(["input", "occupancy", _project.stages['Inventory'][0].steps[4].source, "", vtype, "NA", _project.stages['Inventory'][0].steps[4][vtype].occupancy, "", "", "", "", ""]) - Object.keys(_project.stages['Inventory'][0].steps[6][vtype]).map(ftype => { - if (ftype !== "None") { - _csvExport.push(["input", "fuelBreakdown", _project.stages['Inventory'][0].steps[6].source, "", vtype, ftype].concat(_project.stages['Inventory'][0].steps[6][vtype][ftype])) - _csvExport.push(["input", "fuelConsumption", _project.stages['Inventory'][0].steps[7].energySource, _project.stages['Inventory'][0].steps[7].energyGrowthSource, vtype, ftype].concat(_project.stages['Inventory'][0].steps[7][vtype][ftype])) - } - return null - }) - } - for (let j = 1; j < 6; j++) { - _dataVkt[j].percent = computePercentIncrease(_dataVkt[j].total as number, _dataVkt[j-1].total as number) - } - let outputPassengersModalShare = _project?.outputPassengersModalShare || {} - let vTypesPassengersModalShare = Object.keys(outputPassengersModalShare) - let _activeVTypesPassengersModalShare = [] - let _dataPassengersModalShare : DataModalShare[] = [ - {name: _project.referenceYears[0]}, - {name: _project.referenceYears[1]}, - {name: _project.referenceYears[2]}, - {name: _project.referenceYears[3]}, - {name: _project.referenceYears[4]}, - {name: _project.referenceYears[5]} - ] - for (let i = 0; i < vTypesPassengersModalShare.length; i++) { - let vtype = vTypesPassengersModalShare[i] - if (!_project?.stages?.['Inventory']?.[0]?.steps?.[2]?.[vtype]?.isActive) - continue - for (let j = 0; j < 6; j++) { - if (outputPassengersModalShare?.[vtype]?.[j] !== undefined) { - _dataPassengersModalShare[j][vtype] = Math.round((outputPassengersModalShare?.[vtype]?.[j] || 0) * 100) - if (_activeVTypesPassengersModalShare.indexOf(vTypesPassengersModalShare[i]) === -1) - _activeVTypesPassengersModalShare.push(vTypesPassengersModalShare[i]) - } - } - _csvExport.push(["output", "passengersModalShare", "computed", "", vtype, "NA"].concat(_dataPassengersModalShare.map(e => e[vtype].toString()))) - } - let outputFreightModalShare = _project?.outputFreightModalShare || {} - let vTypesFreightModalShare = Object.keys(outputFreightModalShare) - let _activeVTypesFreightModalShare = [] - let _dataFreightModalShare : DataModalShare[] = [ - {name: _project.referenceYears[0]}, - {name: _project.referenceYears[1]}, - {name: _project.referenceYears[2]}, - {name: _project.referenceYears[3]}, - {name: _project.referenceYears[4]}, - {name: _project.referenceYears[5]} - ] - for (let i = 0; i < vTypesFreightModalShare.length; i++) { - let vtype = vTypesFreightModalShare[i] - if (!_project?.stages?.['Inventory']?.[0]?.steps?.[2]?.[vtype]?.isActive) - continue - for (let j = 0; j < 6; j++) { - if (outputFreightModalShare?.[vtype]?.[j] !== undefined) { - _dataFreightModalShare[j][vtype] = Math.round((outputFreightModalShare?.[vtype]?.[j] || 0) * 100) - if (_activeVTypesFreightModalShare.indexOf(vTypesFreightModalShare[i]) === -1) - _activeVTypesFreightModalShare.push(vTypesFreightModalShare[i]) - } - } - _csvExport.push(["output", "freightModalShare", "computed", "", vtype, "NA"].concat(_dataFreightModalShare.map(e => e[vtype].toString()))) - } - - - - type OutputSumTotalEnergyAndEmissions = typeof _project.outputSumTotalEnergyAndEmissionsWTW - const computeDataEnergy = (outputSumTotalEnergyAndEmissions: OutputSumTotalEnergyAndEmissions) : [string[], any[], number] => { - let activeVtypesEnergy = [] - let dataEnergy : any[] = [ - {name: _project.referenceYears[0], total: 0, percent: "+0%"}, - {name: _project.referenceYears[1], total: 0, percent: ""}, - {name: _project.referenceYears[2], total: 0, percent: ""}, - {name: _project.referenceYears[3], total: 0, percent: ""}, - {name: _project.referenceYears[4], total: 0, percent: ""}, - {name: _project.referenceYears[5], total: 0, percent: ""} - ] - for (let i = 0; i < vTypesPassengersModalShare.length; i++) { - let vtype = vTypesPassengersModalShare[i] - // if (!_project?.stages?.['Inventory']?.[0]?.steps?.[2]?.[vtype]?.isActive) - // continue - for (let j = 0; j < 6; j++) { - let val = outputSumTotalEnergyAndEmissions?.[vtype]?.co2?.[j] - if (val !== undefined) { - dataEnergy[j][vtype] = Math.round(val * 1000) - dataEnergy[j].total += Math.round(val * 1000) - if (activeVtypesEnergy.indexOf(vtype) === -1) - activeVtypesEnergy.push(vtype) - } - } - _csvExport.push(["output", "ghg" + typeOfGHGIsWTW ? "WTW" : "TTW", "computed", "", vtype, "NA"].concat(dataEnergy.map(e => (e[vtype] || 0).toString()))) - } - for (let i = 0; i < vTypesFreightModalShare.length; i++) { - let vtype = vTypesFreightModalShare[i] - // if (!_project?.stages?.['Inventory']?.[0]?.steps?.[2]?.[vtype]?.isActive) - // continue - for (let j = 0; j < 6; j++) { - let val = outputSumTotalEnergyAndEmissions?.[vtype]?.co2?.[j] - if (val !== undefined) { - dataEnergy[j][vtype] = Math.round(val * 1000) - dataEnergy[j].total += Math.round(val * 1000) - if (activeVtypesEnergy.indexOf(vtype) === -1) - activeVtypesEnergy.push(vtype) - } - } - _csvExport.push(["output", "ghg" + typeOfGHGIsWTW ? "WTW" : "TTW", "computed", "", vtype, "NA"].concat(dataEnergy.map(e => (e[vtype] || 0).toString()))) - } - for (let j = 1; j < 6; j++) { - dataEnergy[j].percent = computePercentIncrease(dataEnergy[j].total as number, dataEnergy[j-1].total as number) - } - let maxVal = dataEnergy.map(e => e.total).reduce((c,n) => Math.max(c,n), 0) - return [activeVtypesEnergy, dataEnergy, maxVal] - } - const _dataEnergyWTW = computeDataEnergy(_project?.outputSumTotalEnergyAndEmissionsWTW || {}) - const _dataEnergyTTW = computeDataEnergy(_project?.outputSumTotalEnergyAndEmissionsTTW || {}) - const maxVal = Math.max(_dataEnergyTTW[2], _dataEnergyWTW[2]) - const roundFactor = Math.pow(10, maxVal.toString().length - 1) - const maxValRoundedAbove = Math.ceil(maxVal / roundFactor) * roundFactor - const _dataEnergyDomain = [0, maxValRoundedAbove] - let defaultColors = ["#FF7C7C", "#FFEB7C", "#7BFFE3", "#7C81FF", "#DF7CFF", "#FF9F7C", "#CAFF7C", "#7CDDFF", "#9E7CFF", "#FF7CEC", "#FFB77C"," #8AFF89", "#7CB1FF", "#FF7CB2"] - let colors = defaultColors.slice() - let _colorsPerVtype : {[key: string]: string} = {} - let vtypes = Object.keys(_project?.stages?.['Inventory']?.[0]?.steps?.[2] || {}).filter(vtype => _project?.stages?.['Inventory']?.[0]?.steps?.[2]?.[vtype]) - for (let i = 0; i < vtypes.length; i++) { - _colorsPerVtype[vtypes[i]] = colors.shift() || "black" - if (colors.length === 0) { - colors = defaultColors.slice() - } - } - _csvExport.sort((a, b) => (a[0]+a[1]).localeCompare(b[0]+b[1])) - - setDates(_project.referenceYears) - setActiveVtypesVkt(_activeVtypesVkt) - setDataVkt(_dataVkt) - setCsvExport(_csvExport) - setDataPassengersModalShare(_dataPassengersModalShare) - setActiveVTypesPassengersModalShare(_activeVTypesPassengersModalShare) - setDataFreightModalShare(_dataFreightModalShare) - setActiveVTypesFreightModalShare(_activeVTypesFreightModalShare) - setColorsPerVtype(_colorsPerVtype) - setDataEnergyWTW(_dataEnergyWTW) - setDataEnergyTTW(_dataEnergyTTW) - setDataEnergyDomain(_dataEnergyDomain) - } - - const filterByVtype = (selectedVtypes: InputStep2) => { - if (project?.stages?.['Inventory']?.[0]?.steps?.[2]) { - project.stages['Inventory'][0].steps[2] = selectedVtypes - init(project) - } - } - const computePercentIncrease = (currentVal: number, lastVal: number | undefined) : string => { - if (lastVal === undefined) { - return "+0%" - } - const percentIncrease = Math.round(currentVal*100/lastVal - 100) - if (percentIncrease >= 0) { - return "+" + percentIncrease + "%" - } - return percentIncrease + "%" - } - const validateProject = () => { - console.log("todo callback") - } - return ( -
-

Project overview

-

Project: {project.name} {project.status === 'draft' ? Draft : Validated}

-
- Project: {project.name}, author: {project.owner}, country: {project.country} {project.isSump && , city: {project.city}}, status: {project.status} -
- {project.status === 'draft' && - This project is still in a Draft state, once you are satistified with its content, click here to validate it - } - - -

Population evolution

- - Population evolution is computed using current population and expected annual growth

-
Inputs are in the Socio economic data step
- - - {project?.outputSocioEconomicDataComputed?.population !== undefined && - ({name:dates[i], population: Math.round(e), percent: computePercentIncrease(e, project?.outputSocioEconomicDataComputed?.population?.[i-1])}))}> - - new Intl.NumberFormat('fr').format(value)} /> - new Intl.NumberFormat('fr').format(value)}/> - - - - {showPercents && } - - - } - -
- - -

GDP evolution

- - GDP evolution is computed using current GDP and expected annual growth

-
Inputs are in the Socio economic data step
- - - {project?.outputSocioEconomicDataComputed?.gdp !== undefined && - ({name:dates[i], gdp: Math.round(e), percent: computePercentIncrease(e, project?.outputSocioEconomicDataComputed?.gdp?.[i-1])}))}> - - new Intl.NumberFormat('fr').format(value) + "Mrd$"} /> - new Intl.NumberFormat('fr').format(value)}/> - - - - {showPercents && } - - - } - -
- - -

Vkt

- - Vehicle Kilometers Traveled evolution is computed using current kilometers traveled per vehicle and expected annual growth

-
Inputs are in the Transport activity data step
- - - - - - new Intl.NumberFormat('fr').format(value) + "Mkm"} /> - new Intl.NumberFormat('fr').format(value)}/> - - {activeVtypesVkt.map((e, i) => ( - - - {i===0 && showPercents && } - - ))} - - - -
- - 1/2 - - - -
- Project: {project.name}, author: {project.owner}, country: {project.country} {project.isSump && , city: {project.city}} -
-

Passengers modal split evolution

- - The modal split helps to visualize which transport the population mostly uses for their travels.

- It is computed using total vkt and vehicle occupancy, expressed as a percent of passenger-kilometre (pkm)

-
Inputs are in the Transport activity data step
-
as well as the Vehicle occupancy step
- - - - - - new Intl.NumberFormat('fr').format(value) + "%"} domain={[0,100]}/> - new Intl.NumberFormat('fr').format(value)}/> - - {activeVTypesPassengersModalShare.map((e, i) => ( - - - - ))} - - - -
- -

Transport modal split evolution

- - The modal split helps to visualize which transport is mostly used in freight.

- It is computed using total vkt and vehicle load, expressed as a percent of tons-kilometre (tkm)

-
Inputs are in the Transport activity data step
-
as well as the Vehicle occupancy step
- - - - - - new Intl.NumberFormat('fr').format(value) + "%"} domain={[0,100]}/> - new Intl.NumberFormat('fr').format(value)}/> - - {activeVTypesFreightModalShare.map((e, i) => ( - - - - ))} - - - -
- - -

GHG evolution ({typeOfGHGIsWTW?"Well To Wheel":"Tank To Wheel"})

- - Estimated tons of greenhouse gases emissions for upcoming years per vehicle type.

- It is computed by multiplying for each fuel: vkt, average consumption and default emission factors.

-
Inputs are all the previous steps
- - - - - - new Intl.NumberFormat('fr').format(value) + 't'} domain={dataEnergyDomain}/> - new Intl.NumberFormat('fr').format(value)}/> - - {dataEnergyWTW[0].map((e:string, i:number) => ( - - - {i===0 && showPercents && } - - ))} - - - -
- - mobilise your city - - - 2/2 - - {project?.name &&
- - Download data as csv - -
} -
- ) -} - -const CustomLabel = (props: any) => { - const { x, y, width, height, value, offset, className } = props - const verticalOffset = 5 + offset; - if (height < 10) { - return <> - } - return ( - - - {new Intl.NumberFormat('fr', { notation: 'compact' }).format(value)} - - - ); -} -const PercentLabel = (props: any) => { - const { x, width, value, className } = props - return ( - - - {value} - - - ); -} -type SetBoolean = (key:boolean | ((k:boolean) => boolean)) => void -const Options = ( - {project, filterByVtype, typeOfGHGIsWTW, setTypeOfGHGIsWTW, showPercents, setShowPercents, showLabels, setShowLabels}: - {project: ProjectType, filterByVtype: (inputStep2: InputStep2) => void, typeOfGHGIsWTW: boolean, setTypeOfGHGIsWTW: SetBoolean, showPercents: boolean, setShowPercents: SetBoolean, showLabels: boolean, setShowLabels: SetBoolean} - ) => { - const [showBody, setShowBody] = useState(false) - const [pin, setPin] = useState(false) - const [selectedVtypes, setSelectedVtypes] = useState({} as InputStep2) - useEffect(() => { - const inputStep2 = project?.stages?.['Inventory']?.[0]?.steps?.[2] as InputStep2 - if (inputStep2) - setSelectedVtypes(inputStep2) - }, [project]) - useEffect(() => { - if (selectedVtypes) - filterByVtype(selectedVtypes) - }, [selectedVtypes]) - if (!project?.stages?.['Inventory']?.[0]?.steps?.[2]) { - return <> - } - const vtypes = Object.keys(selectedVtypes) - const updateSelectedVtypes = (event: React.BaseSyntheticEvent) => { - let target = event.target as HTMLInputElement - let vtype = target.name - setSelectedVtypes((prevSelectedVtypes) => { - const newSelectedVtypes = { - ...prevSelectedVtypes, - [vtype]: {isActive: !prevSelectedVtypes[vtype].isActive, isFreight: prevSelectedVtypes[vtype].isFreight} - } - return newSelectedVtypes - }) - } - return ( - <> - - setShowBody(p=>!p)} style={{cursor: "pointer"}}> - Vizualisations option (click to display) - {e.stopPropagation(); setPin(p => !p)}}>📌 - - {showBody && - - Displayed categories of transport - - {vtypes.map((vtype, index) => { - return ( - - - - {vtype} - - - ) - })} - - - - GHG emission type - setTypeOfGHGIsWTW(true)} - label="Well To Wheel (WTW)" - /> - setTypeOfGHGIsWTW(false)} - label="Tank To Wheel (TTW)" - /> - - - Graph content - - setShowPercents((p:boolean)=>!p)}/> - Display percents increase - - - setShowLabels((p:boolean)=>!p)}/> - Display labels - - - } - - - ); - } \ No newline at end of file diff --git a/frontend/src/components/TTWorWTWSelector.tsx b/frontend/src/components/TTWorWTWSelector.tsx index 2a4a058..11587e8 100644 --- a/frontend/src/components/TTWorWTWSelector.tsx +++ b/frontend/src/components/TTWorWTWSelector.tsx @@ -6,19 +6,31 @@ export default function TTWorWTWSelector (props: { setTtwOrWtw: React.Dispatch> }) { return ( -
- Results are computed using the - props.ttwOrWtw === "TTW" ? props.setTtwOrWtw("WTW") : props.setTtwOrWtw("TTW")}> - - {props.ttwOrWtw === "TTW" ? "Tank to Wheel (TTW)" : "Well to Wheel (WTW)"} - - - - {props.ttwOrWtw === "WTW" ? "Tank to Wheel (TTW)" : "Well to Wheel (WTW)"} - - - - approach +
+
+ Results are computed using the + props.ttwOrWtw === "TTW" ? props.setTtwOrWtw("WTW") : props.setTtwOrWtw("TTW")}> + + {props.ttwOrWtw === "TTW" ? "Tank to Wheel (TTW)" : "Well to Wheel (WTW)"} + + + + {props.ttwOrWtw === "WTW" ? "Tank to Wheel (TTW)" : "Well to Wheel (WTW)"} + + + + approach. +
) } diff --git a/frontend/src/components/TdDiagonalBar.tsx b/frontend/src/components/TdDiagonalBar.tsx index a218ed3..042cd56 100644 --- a/frontend/src/components/TdDiagonalBar.tsx +++ b/frontend/src/components/TdDiagonalBar.tsx @@ -2,8 +2,8 @@ import React from "react" export default function TdDiagonalBar (props: any) { return ( - - + + diff --git a/frontend/src/components/ValidSource.tsx b/frontend/src/components/ValidSource.tsx index ca77382..b363737 100644 --- a/frontend/src/components/ValidSource.tsx +++ b/frontend/src/components/ValidSource.tsx @@ -3,8 +3,10 @@ import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap' export default function ValidSource (props: {source: string, onClick?: Function}) { return ( - {props.source}}> - + {props.source}}> + + ) } diff --git a/frontend/src/components/viz/EmissionsBarChart.tsx b/frontend/src/components/viz/EmissionsBarChart.tsx index 5e004e2..ac806e1 100644 --- a/frontend/src/components/viz/EmissionsBarChart.tsx +++ b/frontend/src/components/viz/EmissionsBarChart.tsx @@ -36,14 +36,14 @@ export default function EmissionsBarChart (props: { const [showPercents, setShowPercents] = useState(false) const [showLabels, setShowLabels] = useState(false) return ( - - + + new Intl.NumberFormat('fr').format(value) + 't'} domain={[0, maxValRoundedAbove]}/> new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> {vtypes.map((vtype:string, i:number) => ( - + {i===0 && showPercents && } diff --git a/frontend/src/components/viz/EmissionsCompareBarChart.tsx b/frontend/src/components/viz/EmissionsCompareBarChart.tsx index 26cb5bd..c987788 100644 --- a/frontend/src/components/viz/EmissionsCompareBarChart.tsx +++ b/frontend/src/components/viz/EmissionsCompareBarChart.tsx @@ -1,11 +1,11 @@ -import React, {useCallback, useState} from "react" +import React, {useCallback} from "react" import { ProjectType } from "../../frontendTypes" import { Bar, BarChart, LabelList, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts' import { computePercentIncrease } from "../../utils/computePercentIncrease" import { CSVLink } from "react-csv" import { useCurrentPng } from "recharts-to-png" import { saveAs } from 'file-saver' -import { Button, Col, Container, Row } from "react-bootstrap" +import { Button } from "react-bootstrap" export default function EmissionsCompareBarChart (props: { bauEmissionsData: {[key: string]: {co2: number[], energy: number[]}}, @@ -84,50 +84,51 @@ export default function EmissionsCompareBarChart (props: { } }, [getPng]) return ( -
- -

Emissions

- - {props.project.name && - Dowload as csv - } - - - - -
-
- - - - new Intl.NumberFormat('fr').format(value) + 't'} domain={[0, maxValRoundedAbove]}/> - new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> - - {vtypes.map((vtype:string, i:number) => { - let jsx = [ - - - {/* {i===0 && props.showPercents && } */} - - ] - for (let c = 0; c < props.climateEmissionsData.length; c++) { - if (!props.displayedClimateScenarios[c]) continue - jsx.push( - - - {i===0 && props.showPercents && } +
+
+
+
+ + + + new Intl.NumberFormat('fr').format(value) + 't'} domain={[0, maxValRoundedAbove]}/> + new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> + + {vtypes.map((vtype:string, i:number) => { + let jsx = [ + + + {/* {i===0 && props.showPercents && } */} - ) - - } - return jsx - })} - - + ] + for (let c = 0; c < props.climateEmissionsData.length; c++) { + if (!props.displayedClimateScenarios[c]) continue + jsx.push( + + + {i===0 && props.showPercents && } + + ) + + } + return jsx + })} + + +
) diff --git a/frontend/src/components/viz/EmissionsPerUkmCompareBarChart.tsx b/frontend/src/components/viz/EmissionsPerUkmCompareBarChart.tsx index 38022ec..d83c5d0 100644 --- a/frontend/src/components/viz/EmissionsPerUkmCompareBarChart.tsx +++ b/frontend/src/components/viz/EmissionsPerUkmCompareBarChart.tsx @@ -1,11 +1,11 @@ -import React, {useCallback, useState} from "react" +import React, {useCallback} from "react" import { ProjectType, TransportPerformance } from "../../frontendTypes" import { Bar, BarChart, LabelList, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts' import { computePercentIncrease } from "../../utils/computePercentIncrease" import { CSVLink } from "react-csv" import { useCurrentPng } from "recharts-to-png" import { saveAs } from 'file-saver' -import { Button, Col, Row } from "react-bootstrap" +import { Button } from "react-bootstrap" export default function EmissionsPerUkmCompareBarChart (props: { title: string, @@ -91,52 +91,54 @@ export default function EmissionsPerUkmCompareBarChart (props: { }, [getPng]) const unit = props.title.includes("pkm") ? "gCO2/pkm" : "gCO2/pkm" return ( -
- -

{props.title}

- - {props.project.name && - Dowload as csv - } - - - - -
-
- - - - new Intl.NumberFormat('fr').format(value)} domain={[0, maxValRoundedAbove]}/> - new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> - - {vtypes.map((vtype:string, i:number) => { - let jsx = [ - - - {/* {i===0 && props.showPercents && } */} - - ] - for (let c = 0; c < props.climateEmissionsData.length; c++) { - if (!props.displayedClimateScenarios[c]) continue - jsx.push( - - - {i===0 && props.showPercents && } +
+
+
+
+ + + + new Intl.NumberFormat('fr').format(value)} domain={[0, maxValRoundedAbove]}/> + new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> + + {vtypes.map((vtype:string, i:number) => { + let jsx = [ + + + {/* {i===0 && props.showPercents && } */} - ) - - } - return jsx - })} - - + ] + for (let c = 0; c < props.climateEmissionsData.length; c++) { + if (!props.displayedClimateScenarios[c]) continue + jsx.push( + + + {i===0 && props.showPercents && } + + ) + + } + return jsx + })} + + +
+ ) } const CustomLabel = (props: any) => { diff --git a/frontend/src/components/viz/EmissionsTable.tsx b/frontend/src/components/viz/EmissionsTable.tsx index bbe438c..69e74e1 100644 --- a/frontend/src/components/viz/EmissionsTable.tsx +++ b/frontend/src/components/viz/EmissionsTable.tsx @@ -2,6 +2,7 @@ import React from "react" import { ProjectType } from "../../frontendTypes" import {Table, Badge} from 'react-bootstrap' import ItemWithOverlay from "../ItemWithOverlay" +import OutputNumberTd from "../OutputNumberTd" export default function EmissionsTable (props: { emissionsData: {[key: string]: {co2: number[], energy: number[]}}, @@ -10,25 +11,30 @@ export default function EmissionsTable (props: { return ( + + {/* Transport modes */} + {props.project.referenceYears && props.project.referenceYears.map((y, yearIndex) => ( + + ))} + - + {props.project.referenceYears && props.project.referenceYears.map((y, yearIndex) => ( - - + ))} @@ -36,14 +42,25 @@ export default function EmissionsTable (props: { {Object.keys(props.emissionsData).map((vtype, index) => { const vehicle = props.emissionsData[vtype] return ( - + {props.project.referenceYears && props.project.referenceYears.map((y, yearIndex) => ( - + ))} ) - })} - + + + + {props.project.referenceYears && props.project.referenceYears.map((y) => { + return ( + + ) + })} +
🛈 VehicleVehicle + Emissions (1000 tons of greenhouse gases) computed by the tool, using previous steps inputs. Values for each transport mode and fuel are computed as
- Fuel lower heating value (TJ/1000t) / 10^6 x Fuel density (kg/kg or kg/l) x Input VKT per fuel (Mkm) x 10^6 x Fuel consumption factor (l-kg-kwh/100km) / 100 x Fuel emission factor (kg/TJ) / 10^6 + Fuel lower heating value (TJ/1000t) / 10^6 × Fuel density (kg/kg or kg/l) × Input VKT per fuel (Mkm) × 10^6 × Fuel consumption factor (l-kg-kwh/100km) / 100 × Fuel emission factor (kg/TJ) / 10^6
These values per fuel are then summed per transport mode.
Lower heating value, fuel density and fuel emission factors use default values that can be edited using the Edit GHG emission factors link above. }> - 🛈 GHG {y} (1000t GHG) + GHG {y} (1000t GHG)
-
{vtype} + + {vtype} + + {vehicle.co2[yearIndex].toFixed(2)}
) diff --git a/frontend/src/components/viz/ModalShareCompareBarChart.tsx b/frontend/src/components/viz/ModalShareCompareBarChart.tsx index 5f23e4c..974d6eb 100644 --- a/frontend/src/components/viz/ModalShareCompareBarChart.tsx +++ b/frontend/src/components/viz/ModalShareCompareBarChart.tsx @@ -1,7 +1,7 @@ import React, {useCallback} from "react" import { ProjectType, VehicleKilometresTravelledComputed } from "../../frontendTypes" import { Bar, BarChart, LabelList, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts' -import { Button, Col, Row } from "react-bootstrap" +import { Button } from "react-bootstrap" import { CSVLink } from "react-csv" import { useCurrentPng } from "recharts-to-png" import { saveAs } from 'file-saver' @@ -64,50 +64,51 @@ export default function ModalShareCompareBarChart(props: { } }, [getPng, props.project]) return ( -
- -

{props.title}

- - {props.project.name && - Dowload as csv - } - - - - -
- -
- - - - new Intl.NumberFormat('fr').format(value) + '%'} domain={[0, 100]}/> - new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> - - {vtypes.map((vtype:string, i:number) => { - let jsx = [ - - - - ] - for (let c = 0; c < props.climateModalShareData.length; c++) { - if (!props.displayedClimateScenarios[c]) continue - jsx.push( - - +
+
+
+
+ + + + new Intl.NumberFormat('fr').format(value) + '%'} domain={[0, 100]}/> + new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> + + {vtypes.map((vtype:string, i:number) => { + let jsx = [ + + - ) - } - return jsx - })} - - + ] + for (let c = 0; c < props.climateModalShareData.length; c++) { + if (!props.displayedClimateScenarios[c]) continue + jsx.push( + + + + ) + } + return jsx + })} + +
+
+ ) } const CustomLabel = (props: any) => { @@ -124,13 +125,3 @@ const CustomLabel = (props: any) => { ); } -const PercentLabel = (props: any) => { - const { x, width, value, className } = props - return ( - - - {value} - - - ); -} \ No newline at end of file diff --git a/frontend/src/components/viz/TransportPerformanceCompareBarChart.tsx b/frontend/src/components/viz/TransportPerformanceCompareBarChart.tsx index f33b773..1a1d45f 100644 --- a/frontend/src/components/viz/TransportPerformanceCompareBarChart.tsx +++ b/frontend/src/components/viz/TransportPerformanceCompareBarChart.tsx @@ -1,11 +1,11 @@ -import React, {useCallback, useState} from "react" +import React, {useCallback} from "react" import { ProjectType, TransportPerformance } from "../../frontendTypes" import { Bar, BarChart, LabelList, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts' import { computePercentIncrease } from "../../utils/computePercentIncrease" import { CSVLink } from "react-csv" import { useCurrentPng } from "recharts-to-png" import { saveAs } from 'file-saver' -import { Button, Col, Row } from "react-bootstrap" +import { Button } from "react-bootstrap" export default function TransportPerformanceCompareBarChart (props: { title: string, @@ -83,49 +83,50 @@ export default function TransportPerformanceCompareBarChart (props: { }, [getPng]) const unit = props.title.includes("pkm") ? "pkm" : "tkm" return ( -
- -

{props.title}

- - {props.project.name && - Dowload as csv - } - - - - -
-
- - - - new Intl.NumberFormat('fr').format(value) + unit} domain={[0, maxValRoundedAbove]}/> - new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> - - {vtypes.map((vtype:string, i:number) => { - let jsx = [ - - - {/* {i===0 && props.showPercents && } */} - - ] - for (let c = 0; c < props.climateTransportPerformanceData.length; c++) { - if (!props.displayedClimateScenarios[c]) continue - jsx.push( - - - {i===0 && props.showPercents && } +
+
+
+
+ + + + new Intl.NumberFormat('fr').format(value) + unit} domain={[0, maxValRoundedAbove]}/> + new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> + + {vtypes.map((vtype:string, i:number) => { + let jsx = [ + + + {/* {i===0 && props.showPercents && } */} - ) - } - return jsx - })} - - + ] + for (let c = 0; c < props.climateTransportPerformanceData.length; c++) { + if (!props.displayedClimateScenarios[c]) continue + jsx.push( + + + {i===0 && props.showPercents && } + + ) + } + return jsx + })} + + +
) diff --git a/frontend/src/components/viz/VktCompareBarChart.tsx b/frontend/src/components/viz/VktCompareBarChart.tsx index 7722a84..57a6088 100644 --- a/frontend/src/components/viz/VktCompareBarChart.tsx +++ b/frontend/src/components/viz/VktCompareBarChart.tsx @@ -2,7 +2,7 @@ import React, {useCallback} from "react" import { ProjectType, VehicleKilometresTravelledComputed } from "../../frontendTypes" import { Bar, BarChart, LabelList, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts' import { computePercentIncrease } from "../../utils/computePercentIncrease" -import { Button, Col, Row } from "react-bootstrap" +import { Button } from "react-bootstrap" import { CSVLink } from "react-csv" import { useCurrentPng } from "recharts-to-png" import { saveAs } from 'file-saver' @@ -81,50 +81,51 @@ export default function VktCompareBarChart (props: { } }, [getPng, props.project]) return ( -
- -

Vehicle kilometer travelled

- - {props.project.name && - Dowload as csv - } - - - - -
-
- - - - new Intl.NumberFormat('fr').format(value) + 'MVkt'} domain={[0, maxValRoundedAbove]}/> - new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> - - {vtypes.map((vtype:string, i:number) => { - let jsx = [ - - - {/* {i===0 && props.showPercents && } */} - - ] - for (let c = 0; c < props.climateVktData.length; c++) { - if (!props.displayedClimateScenarios[c]) continue - jsx.push( - - - {i===0 && props.showPercents && } +
+
+
+
+ + + + new Intl.NumberFormat('fr').format(value) + 'MVkt'} domain={[0, maxValRoundedAbove]}/> + new Intl.NumberFormat('fr').format(value)} wrapperStyle={{zIndex: 10}}/> + + {vtypes.map((vtype:string, i:number) => { + let jsx = [ + + + {/* {i===0 && props.showPercents && } */} - ) - - } - return jsx - })} - - + ] + for (let c = 0; c < props.climateVktData.length; c++) { + if (!props.displayedClimateScenarios[c]) continue + jsx.push( + + + {i===0 && props.showPercents && } + + ) + + } + return jsx + })} + + +
) diff --git a/frontend/src/mycStyle.css b/frontend/src/mycStyle.css index 49997aa..2cf06f2 100644 --- a/frontend/src/mycStyle.css +++ b/frontend/src/mycStyle.css @@ -1,215 +1,1967 @@ -.App { - color: rgb(102, 102, 102); -} +/* alexs edits */ -.btn-primary, .btn-secondary { - padding: 8px 14px; - font-weight: 700; - border-radius: 10px; -} -.btn-primary,.btn-primary:active,.btn-primary:focus { - background-color: #a2217c; - border-color: #a2217c; - --bs-btn-active-border-color: #a2217c; - --bs-btn-active-bg: #a2217c; -} -.btn-primary:focus { - box-shadow: 0 0 0 0.25rem #a2217b6e; -} -.btn-primary:hover { - background-color: #caa6c0; - border-color: #caa6c0; - color: #A2217C; -} -.btn-secondary,.btn-secondary:active,.btn-secondary:focus { - background-color: #CE8DBB; - border-color: #CE8DBB; + b { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + } + + + + /* table */ + .inputTable thead th { + background-color: #9e2886; + color: white; + } + table thead tr { + border-top: none; + } + table tbody tr:last-child, + table tbody tr:last-child td { + border-bottom: none; + } + tbody { + border-top: none !important; + /* color: #B3B3B3; + --bs-table-color: #B3B3B3; */ + /* font-weight: 700; */ + } + table tr td:first-child, + table tr th:first-child { + border-left: none; + } + table tr td:last-child, + table tr th:last-child { + border-right: none; + } + td { + vertical-align: middle; + } + .cellError { + color: #FF5C77 !important; + } + input.cellError { + background-color: #FF5C771A !important; + } + .table { + --bs-table-bg: transparent; + } + + /* tooltip */ + .tooltip-button { + /* background: var(--c-edition-t0) !important; */ + } + .infoTooltip, .errorTooltip { + /* Prevents the tooltip from messing with the page when it's created */ + top:0; + left:0; + } + .errorTooltip .tooltip-inner { + background-color: #ffedf0; + color: #FF5C77; + } + .errorTooltip .tooltip-arrow::before { + border-bottom-color: #ffedf0; + } + .infoTooltip .tooltip-inner { + background-color: white; + color: #292929; + } + .infoTooltip .tooltip-arrow::before { + border-top-color: white; + } + + /* fonts */ + @font-face { + font-family: "Gravur Condensed"; + src: url("/public/fonts/GravurCondensed/GravurCondensed-Thin.otf") format("opentype"); + font-weight: 100; + } @font-face { + font-family: "Gravur Condensed"; + src: url("/public/fonts/GravurCondensed/GravurCondensed-Light.otf") format("opentype"); + font-weight: 300; + } @font-face { + font-family: "Gravur Condensed"; + src: url("/public/fonts/GravurCondensed/GravurCondensed-Regular.otf") format("opentype"); + font-weight: normal; + } @font-face { + font-family: "Gravur Condensed"; + src: url("/public/fonts/GravurCondensed/GravurCondensed-Bold.otf") format("opentype"); + font-weight: bold; + } @font-face { + font-family: "Gravur Condensed"; + src: url("/public/fonts/GravurCondensed/GravurCondensed-Black.otf") format("opentype"); + font-weight: 900; + } + + /* output number */ + .outputNumber { + /* text-align: right; */ /* finalement je pense que c'est moins pire à gauche 230924-192644 */ + .regular { + display: block + } + .hover { + display: none; + } + } + .outputNumber:hover, + .outputNumber:focus, + .outputNumber:active { + .regular { + display: none + } + .hover { + display: block; + overflow: hidden; + text-overflow: ellipsis; + } + } + + /* no need */ + /* .stepLeft { + background-color: rgb(237, 249, 253); + } */ + /* .stepLeft, .stepRight { + padding-top: 20px; + } */ + /* @media only screen and (min-width: 1400px) { + .stepLeft, .stepRight { + padding: 20px; + } + } */ + /* .btn-imglink { + padding-left: 0; + } + .btn-imglink:hover { + opacity: 0.6; + } + .btn-group-lg .btn { + border-radius: 10px; + } */ + /* .App { + color: rgb(102, 102, 102); + } */ + /* h1, h2, h3 { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + color: #292929; + } */ + /* body { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + background-color: #F5F5F5; + } */ + + + + + + + + + +/* adriens edits */ + +/* variables ————————————————————— */ + + :root { + /* spacing */ + --spacing-xxs: 0.8rem; + --spacing-xs: 1.2rem; + --spacing-s: 1.8rem; + --spacing-m: 2.4rem; + --spacing-l: 3.6rem; + --spacing-xl: 5.6rem; + --spacing-xxl: 7.2rem; + /* section paddings */ + --section-padding-l-top: 10rem; + --section-padding-l-right: 10rem; + --section-padding-l-bottom: 14rem; + --section-padding-l-left: 10rem; + --section-padding-s-top: 6rem; + --section-padding-s-right: 1.4rem; + --section-padding-s-bottom: 6rem; + --section-padding-s-left: 1.4rem; + /* colors */ + /*grey (luminance variations)*/ + --c-h0-s0-l0: #000000; + --c-h0-s0-l1: #292929; + --c-h0-s0-l2: #808080; + --c-h0-s0-l3: #B3B3B3; + --c-h0-s0-l4: #E6E6E6; + --c-h0-s0-l5: #F5F5F5; + --c-h0-s0-l6: #FFFFFF; + /*cyan (luminance variations)*/ + --c-h1-s0-l2: #2CB1D5; + --c-h1-s0-l3: #67CAE4; + --c-h1-s0-l4: #C5E8F2; + --c-h1-s0-l5: #EDF9FD; + /*magenta (luminance variations)*/ + --c-h2-s0_l2: #A2217C; + --c-h2-s0_l3: #CE8DBB; + --c-h2-s0_l4: #EBD1E1; + /*orange (transparency variations)*/ + --c-h3-s0-l0-t0: #FF6747; + --c-h3-s0-l0-t1: hsla(10,100%,64%,60%); + --c-h3-s0-l0-t2: hsla(10,100%,64%,10%); + /*red (transparency variations)*/ + --c-h4-s0-l0-t0: #FF5C77; + --c-h4-s0-l0-t1: hsla(350,100%,68%,60%); + --c-h4-s0-l0-t2: hsla(350,100%,68%,10%); + /*green (transparency variations)*/ + --c-h5-s0-l0-t0: #44AF69; + --c-h5-s0-l0-t1: hsla(141,44%,48%,60%); + --c-h5-s0-l0-t2: hsla(141,44%,48%,10%); + /*edition (transparency variations)*/ + --c-edition-t0: #9747FF; + --c-edition-t1: hsla(266,100%,64%,60%); + --c-edition-t2: hsla(266,100%,64%,10%); + /* fonts */ + /*for headings and body copy*/ + --t-lh-main: 140%; + /*for headings*/ + --t-ff-h: "Gravur Condensed"; + --t-fw-h: 1000; + --t-fs-h1: 4.8rem; /*big*/ + --t-lh-h1: 110%; + --t-fs-h2: 3.6rem; /*medium*/ + --t-lh-h2: 110%; + --t-fs-h3: 2.4rem; /*small*/ + --t-lh-h3: 110%; + /*for body copy*/ + --t-ff-p-mono: "Roboto Mono"; + --t-fw-p-mono: medium; + --t-fw-p-mono-bold: bold; + --t-ff-p-regular: "Roboto"; + --t-fw-p-regular: 500; + --t-fw-p-regular-bold: 700; + --t-fs-p1: 2.4rem; /*big*/ + --t-lh-p1: var(--t-lh-main); + --t-fs-p2: 1.8rem; /*medium*/ + --t-lh-p2: var(--t-lh-main); + --t-fs-p3: 1.2rem; /*small*/ + --t-lh-p3: 180%; + /* tables */ + --table-border-color: var(--c-h1-s0-l4); + --table-cell-padding: 0.6rem; + --table-cell-height: 3.7rem; + /* breakpoints */ + --breakpoint-m: 768px; + + } - --bs-btn-active-border-color: #CE8DBB; - --bs-btn-active-bg: #CE8DBB; -} -.btn-secondary:hover { - background-color: #caa6c0; - border-color: #caa6c0; - color: #A2217C; -} -.btn-sm { - padding: 0 0.5rem; - font-size: 12px; - border-radius: 10px; -} -.btn-action { - background-color: #2CB1D5; - border-color: #2CB1D5; - --bs-btn-disabled-bg: #2CB1D5; - --bs-btn-disabled-color: white; - color: white; - font-weight: 700; - border-radius: 0.25rem; -} -.btn-action:hover { - background-color: #AAE0EE; - border-color: #AAE0EE; - color: #2CB1D5; -} -.btn-link { - color: #808080; - text-decoration: none; - font-weight: 500; -} -.btn-link:hover { - color: #B3B3B3; -} -h1, h2, h3 { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 700; - color: #292929; -} -body { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - background-color: #F5F5F5; + + +/* html ————————————————————— */ + + + html { + /* management of ALL dimensions */ + /* setting 1rem to 10px. */ + /* all dimensions should now be set in rem. */ + /* this value can be used as a global zoom level. */ + /* it is handy to play with to achieve responsiveness. */ + font-size: 10px; + /* no anim when scrolling to top between pages */ + scroll-behavior: auto !important; + } + @media (max-width: 768px) { + html { + font-size: 8.5px; + } + } + + + +/* page sections, rows, columns ————————————————————— */ + + /* this is what the markup structure should be like in ALL cases : */ + /* ~/body + /.App [= the page] + /section [= a vertical section of the page] + /.container [= a bootstrap container containing the content of the section] + /.row [= a bootsrap row] + /.col [= a bootstrap column] + /* [= any piece of content] */ + + /* sections */ + /* size and pad sections */ + div.App header { + padding: 0 0 0 0; + } + div.App section { + width: 100%; + padding: + var(--section-padding-l-top) + var(--section-padding-l-right) + var(--section-padding-l-bottom) + var(--section-padding-l-left); + } + div.App section.footer { + width: 100%; + padding: + var(--section-padding-l-top) + var(--section-padding-l-right) + var(--section-padding-l-bottom) + var(--section-padding-l-left); + } + + @media (max-width: 768px) { + div.App header { + padding: 0 1.4rem 0 1.4rem; + padding: + 0 + var(--section-padding-s-right) + 0 + var(--section-padding-s-left); + } + div.App section { + --reduction-factor: 1/4; + width: 100%; + padding: + var(--section-padding-s-top) + var(--section-padding-s-right) + var(--section-padding-s-bottom) + var(--section-padding-s-left); + } + div.App section.footer { + padding: + var(--section-padding-s-top) + var(--section-padding-s-right) + var(--section-padding-s-bottom) + var(--section-padding-s-left); + } + } + + + /* columns */ + .container, + .row { /* for some reason these variables are set for these elements and not :root by bootstrap */ + /* edit gap between columns */ + --bs-gutter-x: 4.2rem; + /* edit gap between rows */ + --bs-gutter-y: 6.4rem; /* vertical distance between two columns when they stack up */ + /* box-shadow: 0 0 0 1rem hsla(40,100%,50%,100%); */ + } + .row > .col, + .row > [class^=col-], + .row > [class*= col-] { + /* background: var(--c-edition-t2); */ + } + + /* exceptions */ + .row > .col > *:not(button), + .row > [class^=col-] > *:not(button), + .row > [class*= col-] > *:not(button) { + /* background: var(--c-edition-t2); */ + } + .row > .col > *:not(:last-child), + .row > [class^=col-] > *:not(:last-child), + .row > [class*= col-] > *:not(:last-child) { + margin-bottom: var(--spacing-m) !important; + } + + + /*set medium column width to 600px*/ + /* @media (min-width: 1200px) { + .col-xl-8 { + flex: 0 0 auto; + width: calc(60rem + var(--bs-gutter-x)); + } + } */ + /* @media (min-width: 992px) { + .col-lg-8 { + flex: 0 0 auto; + width: calc(60rem + var(--bs-gutter-x)); + } + } */ + + +/* page header ————————————————————— */ + +header { + background-color: var(--c-h0-s0-l5); } -b { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; + +nav.navbar { + --bs-nav-link-padding-y: 0.5rem; /* bs default value */ + --bs-navbar-nav-link-padding-x: 0.8rem; + --bs-navbar-hover-color: var(--c-h0-s0-l1); + --bs-navbar-brand-hover-color: var(--bs-navbar-brand-hover-color); + height: 6.4rem; + padding: 1.45rem 0; /* to prevent it from going up when the toggler is triggered */ } +/* links */ + nav.navbar a, + nav.navbar button { + font-size: var(--t-fs-p2); + font-weight: var(--t-fw-p-regular); + color: var(--c-h0-s0-l3); + } + .navbar-nav .nav-link.active, + .navbar-nav .nav-link.show { + color: var(--bs-navbar-hover-color); + } + nav.navbar a.navbar-brand, + nav.navbar .nav-link, + nav.navbar #github-link { + padding: + var(--bs-nav-link-padding-y) + var(--bs-navbar-nav-link-padding-x) + var(--bs-nav-link-padding-y) + var(--bs-navbar-nav-link-padding-x); + } + nav.navbar a.navbar-brand, + nav.navbar #basic-navbar-nav { + margin: 0 calc(-1 * var(--bs-navbar-nav-link-padding-x)); + } +/* github-link */ + nav.navbar span.github-link-container a#github-link { + display: inline-block; + width: 4rem; + height: auto; + } + nav.navbar span.github-link-container a#github-link:hover, + nav.navbar span.github-link-container a#github-link:focus { + /* mimic .nav-link behavior */ + color: var(--bs-navbar-hover-color); + transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out; + } + nav.navbar span.github-link-container a#github-link > svg { + width: 100%; + fill: currentColor; + } + /* hide on small screens */ + @media(max-width:768px) { + nav.navbar span.github-link-container { + display: none; + } + } +/* dropdown menu */ + .dropdown-menu { + --bs-dropdown-spacer: 1rem; + border: 0; + border-radius: 0; + padding: 0; + margin: 0; + line-height: 0; + } + .dropdown-menu .dropdown-item { + line-height: 100%; + /* transition : same as nav-link */ + transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out; + transition-duration: 0.15s, 0.15s, 0.15s; + transition-timing-function: ease-in-out, ease-in-out, ease-in-out; + transition-delay: 0s, 0s, 0s; + } + .dropdown-menu .dropdown-item:hover, + .dropdown-menu .dropdown-item:focus, + .dropdown-menu .dropdown-item:active { + background-color: unset; + } + .dropdown-toggle::after { + display: none; + } + form .dropdown-item { + font-size: 18px; + } +/* navbar toggler */ + .navbar-toggler, + .navbar-toggler:hover, + .navbar-toggler:focus, + .navbar-toggler:active { + border: none !important; + border-radius: 0 !important; + box-shadow: none; + } + .navbar-toggler .navbar-toggler-icon { + background-image: url(/public/navbar-toggler-icon.svg); + } + .navbar-toggler:hover .navbar-toggler-icon, + .navbar-toggler:focus .navbar-toggler-icon, + .navbar-toggler:active .navbar-toggler-icon { + background-image: url(/public/navbar-toggler-icon.svg); + outline: 3px solid hsla(0,0%,50%,0.3); + outline-offset: 3px; + border-radius: 3px; + } + + +/* page footer */ + +/* on most pages, the footer is a section element (the last of the page). + on calculation steps, it is a div element (within the right hand side column). */ + + div.projectStepContainer .stepRight .footer { + margin-top: 12.8rem; + } + +/* logos of partners ————————————————————— */ + :root { + --logo-common-scale-base: 1rem; + /* --logo-common-scale-factor: 1; */ /* ISO with proto */ + --logo-common-scale-factor: 1.5; /* adjusted while working */ + } + .partnerLine { + display: flex; + flex-flow: row wrap; + /* gap: 3rem 3.7rem; */ /* ISO with proto */ + gap: 3rem 7rem; /* adjusted while working */ + align-items: center; + width: 100%; + margin: 0 auto 9.2rem auto; + } + .partnerLine .group { + display: flex; + flex-flow: row nowrap; + gap: 3rem 7rem; + align-items: center; + } + .partnerLine .group .img-wrapper { + height: 7rem; + display: flex; + align-items: center; + } + .partnerLine img { + height: fit-content; + } + .partnerLine img.logo-ADB { width: calc( var(--logo-common-scale-base) * 3.6 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-ADEME { width: calc( var(--logo-common-scale-base) * 4.2 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-AFD { width: calc( var(--logo-common-scale-base) * 6.0 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-Cerema { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-Codatu { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-DTFA { width: calc( var(--logo-common-scale-base) * 7.6 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-EB { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-ECF { width: calc( var(--logo-common-scale-base) * 4.6 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-EU { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-Euroclima { width: calc( var(--logo-common-scale-base) * 3.6 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-FFEM { width: calc( var(--logo-common-scale-base) * 3.8 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-FMECD { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-FMENCNSCP { width: calc( var(--logo-common-scale-base) * 10.1 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-GIZ { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-GPIT { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-ITDP { width: calc( var(--logo-common-scale-base) * 4.3 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-KFW { width: calc( var(--logo-common-scale-base) * 2.9 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-MP { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-MTE { width: calc( var(--logo-common-scale-base) * 5.9 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-Platforma { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-SMFA { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-SSATP { width: calc( var(--logo-common-scale-base) * 4.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-TRUFI { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-TUMI { width: calc( var(--logo-common-scale-base) * 5.1 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-UCLG { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-UNH { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + .partnerLine img.logo-WI { width: calc( var(--logo-common-scale-base) * 6.4 * var(--logo-common-scale-factor)) } + + + + +/* colors ————————————————————— */ + + /*text colors*/ + html, + body { + color: var(--c-h0-s0-l1); + } + body { + background: var(--c-h0-s0-l5); + } + h1,h2,h3, + p,span,text { + color: inherit; + } + h1,h2,h3 { + color: var(--c-h0-s0-l1); + } + body { + background-color: unset; + } + .reqStar:after { + content: "*"; + color: var(--c-h4-s0-l0-t0); + } + + + + +/* fonts ————————————————————— */ + + /* prevent bootstrap to set fontsizes that i don't want */ + p,span,text { + font-size: inherit; + } + + /* paragraph spacing */ + p:not(:last-of-type), text:not(:last-of-type) { + margin-bottom: 0.8em; + } + p:last-of-type, text:last-of-type { + margin-bottom: 0; + } + + /* 1. headings */ + h1, h2, h3 { + font-family: var(--t-ff-h); + font-weight: var(--t-fw-h); + line-height: var(--t-lh-main); + margin: 0.33em 0 0.67em 0; + } + h1 {font-size: var(--t-fs-h1); line-height: var(--t-lh-h1);} + h2 {font-size: var(--t-fs-h2); line-height: var(--t-lh-h2);} + h3 {font-size: var(--t-fs-h3); line-height: var(--t-lh-h3);} + h4, h5, h6 { + /*these are not supposed to exist*/ + font-family: "Times New Roman"; + color: red; + } + + /* 2. body copy */ + /* 2.1. body copy styles */ + /* body copy style: all */ + .p-reg, + .p-mono, + body { + line-height: var(--t-lh-main); + } + /* body copy style: regular */ + .p-reg, + body { + font-family: var(--t-ff-p-regular); + font-weight: var(--t-fw-p-regular); + } + /* body copy style: regular bold */ + .p-reg-bold { + font-family: var(--t-ff-p-regular); + font-weight: var(--t-fw-p-regular-bold); + } + /* body copy style: mono */ + .p-mono, + .card-body > span { + font-family: var(--t-ff-p-mono); + font-weight: var(--t-fw-p-mono); + } + /* body copy style: mono bold */ + .p-mono-bold, + form label.form-label, + form label.form-label span, + .badge { + font-family: var(--t-ff-p-mono); + font-weight: var(--t-fw-p-mono-bold); + } + /* 2.2. body copy sizes */ + /* body copy large */ + .p1 { + font-size: var(--t-fs-p1);} + /* body copy medium */ + .p2, + body { + font-size: var(--t-fs-p2);} + /* body copy small */ + .p3, + form label.form-label, + form label.form-label span, + .badge { + font-size: var(--t-fs-p3); + line-height: var(--t-lh-p3);} + + + + + +/* buttons ————————————————————— */ + + .btn, + [class^=btn-] { + /* --bs-btn-font-size: unset; */ + font-family: var(--t-ff-p-regular); + font-weight: var(--t-fw-p-regular-bold); + font-size: var(--t-fs-p3); + padding: 0.8rem 1.4rem; + border: none; + border-radius: 1rem; + transition: none; + /* the following is useful since we spanned all "item" elements */ + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: center; + + } + + :focus-visible { + background: hsla(0,0%,50%,0.2) !important; + /* outline: .4rem solid hsla(0,0%,50%,0.2) !important; + outline-offset: 0rem; */ + outline: none !important; + box-shadow: none !important; + transition: none; + } + + .btn-sm { + font-family: var(--t-ff-p-mono); + font-weight: var(--t-fw-p-mono-bold); + } + .btn-lg { + font-size: var(--t-fs-p2); + padding: 1rem 1.6rem; + border: none; + } + /* links */ + a, + link, + .btn-link { + display: inline-block; + font-family: var(--t-ff-p-regular); + font-weight: var(--t-fw-p-regular); + padding: 0; + border-radius: 0; + text-decoration: none; + } + /* if within text and a button, space it out */ + .text .btn-link:not(:first-child), + p .btn-link:not(:first-child) { + margin: 0 0.4em; + } + /* if within text, underline it */ + .text a, + .text link, + .text .btn-link, + p a, + p link, + p .btn-link { + text-decoration: underline; + text-underline-offset: 0.2em; + } + /* if within text, make it the size of the text */ + .text a, + .text link, + .text .btn-link, + p a, + p link, + p .btn-link { + font-size: inherit; + color: var(--c-h0-s0-l3); + vertical-align: baseline; + line-height: inherit; + + } + /* button colors : link */ + a, + link, + .btn-link, + a:hover, + link:hover, + .btn-link:hover, + a:focus, + link:focus, + .btn-link:focus, + a:active, + link:active, + .btn-link:active { + color: var(--c-h0-s0-l2); + text-decoration-color: var(--c-h0-s0-l3); + } + /* btn link active state */ + .btn-link.btn-check:checked+.btn, + .btn-link.btn.active, + .btn-link.btn.show, + .btn-link.btn:first-child:active, + :not(.btn-check)+.btn-link.btn:active { + color: var(--c-h0-s0-l2); + background-color: none; + border-color: none; + } + + /* button colors */ + /* button colors : primary */ + .btn-primary, + .btn-primary:active, + .btn-primary:focus { + /* color: var(--c-h0-s0-l6); */ + background-color: var(--c-h2-s0_l2); + --bs-btn-active-bg: var(--c-h2-s0_l2); + } + .btn-primary:focus { + box-shadow: none; + } + .btn-primary:hover { + color: var(--c-h2-s0_l2); + background-color: var(--c-h2-s0_l3); + } + /* button colors : secondary */ + .btn-secondary, + .btn-secondary:active, + .btn-secondary:focus { + /* color: var(--c-h0-s0-l6); */ + background-color: var(--c-h2-s0_l3); + border-color: var(--c-h2-s0_l3); + --bs-btn-active-bg: var(--c-h2-s0_l3); + } + .btn-secondary:hover { + color: var(--c-h2-s0_l2); + background-color: var(--c-h2-s0_l3); + } + /* button colors : various */ + .btn-action { + background-color: var(--c-h1-s0-l2); + color: var(--c-h0-s0-l6); + --bs-btn-disabled-bg: var(--c-h0-s0-l4); + --bs-btn-disabled-color: var(--c-h0-s0-l3); + } + .btn-action:hover { + background-color: var(--c-h1-s0-l3); + color: var(--c-h1-s0-l2); + } + .bg-success { + background-color: var(--c-h5-s0-l0-t0) !important; + } + .btn-success { + --bs-btn-color: var(--c-h0-s0-l6); + --bs-btn-bg: var(--c-h5-s0-l0-t0); + --bs-btn-hover-color: var(--c-h0-s0-l6); + --bs-btn-hover-bg: var(--c-h5-s0-l0-t1); + /* --bs-btn-focus-shadow-rgb: 60,153,110; */ + --bs-btn-active-color: var(--c-h0-s0-l6); + --bs-btn-active-bg: var(--c-h5-s0-l0-t1); + --bs-btn-active-shadow: none; + } + .btn-danger { + background-color: var(--c-h4-s0-l0-t0) !important; + } + .btn.disabled, + .btn:disabled, + fieldset:disabled .btn { + color: var(--c-h0-s0-l3); + background-color: var(--c-h0-s0-l4); + border-color: var(--c-h0-s0-l4); + opacity: 1; + pointer-events: none; + } + + + /* badges and small buttons ————————————————————— */ + + .badge, .btn-sm { + padding: 0 0.6rem; + /* padding: 0rem; */ + /* min-width: 2.2rem; */ + height: 2.2rem; + display: inline-flex; + align-items: center; + line-height: 100%; + border-radius: 0.6rem !important; + font-size: var(--t-fs-p3); + user-select: none; + text-align: initial; + vertical-align: text-top; + } + .badge > svg.icon:first-child { + margin-left: -0.4rem; + } + .badge > svg.icon:last-child { + margin-right: -0.4rem; + } + /*default colors*/ + .badge-default { + background-color: var(--c-h0-s0-l4) !important; + color: var(--c-h0-s0-l1); + cursor: pointer; + } + .badge-default:hover, + .badge-default:focus, + .badge-default:active { + background-color: var(--c-h0-s0-l5) !important; + } + .badge-read-only { + color: var(--c-h0-s0-l6); + background-color: var(--c-h0-s0-l3) !important; + cursor: default; + /*forced to use "!important" as it is used in the overritten rule*/ + } + + + + + + +/* form, form fields (like in /project/config-tab) ————————————————————— */ + + .form-control { + /* width: 100%; */ + padding: var(--table-cell-padding); + font-size: inherit; + font-weight: inherit; + line-height: inherit; + color: inherit; + background-color: unset; + border-radius: 0; + border-color: var(--table-border-color); + } + form label.form-label, + form label.form-label span { + color: var(--c-h0-s0-l3); + } + input, textarea, + .proj-year-options { + /* background-color: unset !important; */ + transition: none !important; + border: none !important; + border-radius: 0; + box-shadow: + 0 calc(var(--bs-border-width) * -1/2) 0 var(--table-border-color), + inset 0 calc(var(--bs-border-width) * 1/2) 0 var(--table-border-color); + } + input[type^='radio'], + input[type^='checkbox'] { + box-shadow: + inset 0 0 0 calc(var(--bs-border-width) * 1/2) var(--table-border-color), + 0 0 0 calc(var(--bs-border-width) * 1/2) var(--table-border-color); + } + .form-check-input:checked { + background-color: var(--c-h1-s0-l3); + } + + /* make outline larger when in focus */ + input:focus, textarea:focus { + box-shadow: + inset 0 0 0 calc(var(--bs-border-width) * 4/2) var(--c-h1-s0-l3), + 0 0 0 calc(var(--bs-border-width) * 4/2) var(--c-h1-s0-l3) !important; + /* background: unset !important; */ + outline: none !important; + } + /* some input fields are doubled. the clone needs to be put in the bg */ + input.rbt-input-hint { + z-index: -1; + } + /* deal with inputs inside tables */ + table td input, + table td textarea { + box-shadow: + inset 0 0 0 calc(var(--bs-border-width) * 4/2) transparent, + 0 0 0 calc(var(--bs-border-width) * 4/2) transparent !important; + } + /* on the config tab */ + /* respace sections of the form */ + .col > form > *:not(:last-child) { + margin-bottom: var(--spacing-m) !important; + } + + + + +/* tables ————————————————————— */ + + table.table { + border-collapse: collapse; + border-spacing: 0; /* ← this is cellspacing */ + /* overflow: hidden; */ + table-layout: fixed; + /* the following is to switch to an auto col width layout : */ + /* width: 100%; + table-layout: fixed; */ + } + + table.table tr, + table.table tr th, td { + /* reset padding */ + margin: 0 !important; + padding: 0 !important; + border-color: var(--table-border-color); + /* show overflow */ + /* overflow: hidden !important; */ + } + table.table tr th > :not(input), + table.table tr td > :not(input) { + /* hide overflow */ + /* overflow: hidden !important; */ + } + table.table tr td { + line-height: 0; + /* line-height: 26px; */ /* alexs edit 230921-141931 */ + font-size: var(--t-fs-p2); + } + table.table tr td > * { + line-height: 100%; + } + + /* table header cells */ + table.table thead tr th { + border-right: 0; + border-left: 0; + + cursor: pointer; + } + /* change bg on hover */ + table.table thead tr th:hover, + table.table thead tr th:focus, + table.table thead tr th:active { + background: var(--c-h0-s0-l5); + } + /* give the text an overflow ellipsis */ + table.table th .item { + max-width: 100%; + } + table.table th .item > span { + max-width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + /* lets temporarily hide the info icons */ + table.table th .item > .icon { + display: none; + } + + + /* table/cell */ + table.table tr td, + table.table tr th { + /* min-width: calc(var(--table-cell-height) * 1); /* temporary fix for modulo cell sizing */ + /* max-width: calc(var(--table-cell-height) * 4); /* temporary fix for modulo cell sizing */ + height: var(--table-cell-height) !important; /* temporary fix for modulo cell sizing */ + padding: var(--table-cell-padding) !important; + font-family: var(--t-ff-p-mono); + font-weight: var(--t-fw-p-mono-bold); + white-space: nowrap; + overflow: hidden; + vertical-align: middle; + color: var(--c-h0-s0-l3); + /* background-color: pink; */ + vertical-align: text-top; + } + table.table tr th { + font-size: var(--t-fs-p3); + } + /* table/cell/diagonal bar */ + .tdDiagonalBar { + width: 100%; + height: 100%; + } + svg.diagonalBar { + position: absolute; + width: calc(100%); + height: calc(100%); + margin: calc(-1 * var(--table-cell-padding)); + } + svg.diagonalBar line { + stroke: var(--table-border-color); + stroke-width: 2.375%; + } + + /* table/cell/form field */ + table.table td .form-control { + color: var(--c-h0-s0-l1); + width: calc(100% + 2 * var(--table-cell-padding)); + height: calc(100% + 2 * var(--table-cell-padding)); + margin: calc(-1 * var(--table-cell-padding)); + border: var(--bs-border-width) solid transparent; + } + + /* table/cell/button */ + table.table td button { + border-radius: 0.6rem; + } + + /* table/cell/badge and table/cell/button */ + table.table td .badge:not(:last-child), + table.table td button:not(:last-child) { + margin-right: var(--spacing-xxs); + } + + /* table/cell/multiple badges */ + table.table .hstack { + gap: 0rem !important; /* managing this with badge margins instead */ + } + .tablecolfluid { + width: 100%; + } + .tablecol1 { + width: calc(var(--table-cell-height) * 1); + } + .tablecol2 { + width: calc(var(--table-cell-height) * 2); + } + .tablecol3 { + width: calc(var(--table-cell-height) * 3); + } + .tablecol4 { + width: calc(var(--table-cell-height) * 4); + } + .tablecol5 { + width: calc(var(--table-cell-height) * 5); + } + .tablecol6 { + width: calc(var(--table-cell-height) * 6); + } + .tablecol7 { + width: calc(var(--table-cell-height) * 7); + } + + +/* "item" elements —————————————————————*/ + .item { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 700; - font-size: 18px; - line-height: 25px; - color: #B3B3B3; -} -.item-sm { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 700; - font-size: 12px; - line-height: 22px; - color: #B3B3B3 !important; - white-space: nowrap; -} -.bg-info { - background-color: #EBEBEB !important; - color: #292929; -} -.badge.bg-info:hover { - cursor: pointer; - background-color: #0000000d !important; -} -.bg-disabled { - background-color: #B3B3B3; -} -.inputTable thead th { - background-color: #9e2886; - color: white; + /* background: var(--c-edition-t2); */ + font-size: inherit; + line-height: inherit; /* not sure */ + color: inherit; + display: flex; + flex-flow: row nowrap; + align-items: center; + gap: 0.6em; + justify-content: center; + width: fit-content; } -td, tr, th { - border-color: #67CAE4; -} -table thead tr { - border-top: none; -} -table tbody tr:last-child { - border-bottom: none; -} -tbody { - border-top: none !important; - color: #B3B3B3; - --bs-table-color: #B3B3B3; - font-weight: 700; -} -table tr td:first-child, -table tr th:first-child { - border-left: none; -} -table tr td:last-child, -table tr th:last-child { - border-right: none; -} -td { - vertical-align: middle; -} -.cellError { - color: #FF5C77 !important; -} -input.cellError { - background-color: #FF5C771A !important; -} -.infoTooltip, .errorTooltip { - /* Prevents the tooltip from messing with the page when it's created */ - top:0; - left:0; -} -.infoTooltip.show, .errorTooltip.show { - opacity: 1 !important; + + + +/* icons ————————————————————— */ + + .icon { + fill: none; + stroke: currentColor; + stroke-linecap: square; + } + /* WORKING HERE 230901-175815 */ + /* trying to fix the spacing around icons / the size of a mono-icon badge */ + /* what has been tried (negative icon margin) is not satisfying as icons become offset in other contexts. */ + .icon-size-s { + stroke-width: calc(1.375rem * 0.14); + height: 1.55rem; + width: 1.55rem; + margin: -0.3rem; + } + .icon-size-m { + stroke-width: calc(2.25rem * 0.1); + height: 2.2rem; + width: 2.2rem; + margin: -0.4rem; + } + .icon-size-l { + stroke-width: calc(3rem * 0.1); + height: 2.56rem; + width: 2.56rem; + margin: -0.48rem; + } + + + + + + +/* cards ————————————————————— */ + + .card { + background-color: var(--c-h0-s0-l4); + border-radius: 2.4rem; + border: none; + padding: 3rem 3.6rem 3.6rem 3.6rem; + } + .card-header, + .card-body { + padding: 0; + } + .card > *:not(:last-child), + .card-body > *:not(:last-child) { + margin-bottom: 1.4rem; + } -} -.tooltip-inner { - padding: 20px; - text-align: left; - font-weight: 500; - max-width: 350px; -} -.errorTooltip .tooltip-inner { - background-color: #ffedf0; - color: #FF5C77; -} -.errorTooltip .tooltip-arrow::before { - border-bottom-color: #ffedf0; -} -.infoTooltip .tooltip-inner { - background-color: white; - color: #292929; -} -.infoTooltip .tooltip-arrow::before { - border-top-color: white; -} -.stepLeft { - background-color: rgb(237, 249, 253); -} -.stepLeft, .stepRight { - padding-top: 20px; -} -@media only screen and (min-width: 1400px) { - .stepLeft, .stepRight { - padding: 20px; + /* card header */ + .card-header { + background-color: unset; + border-bottom: 0; + display: flex; + justify-content: space-between; + flex-flow: row nowrap; + } + .card-header .title, + .card-header .nav { + width: auto; + gap: var(--spacing-xxs); + align-content: center; + } + .card-header .title h3 { + margin: 0; + } + .card-header .nav button[class^='btn-primary'], + .card-header .nav button[class*='btn-primary'] { + /* background: pink; */ + min-width: 8.6rem; + } + .card-header .nav button[title='Duplicate Scenario'] { + padding: 0 .6rem; + } + + /* card body */ + .card-body > span { + color: var(--c-h0-s0-l2); + padding: var(--table-cell-padding); /*cell padding - this is supposed to be in a table cell*/ + border-top: var(--bs-border-width) solid var(--table-border-color); + } + /* .h1, .h2, .h3, .h4, .h5, .h6, + h1, h2, h3, h4, h5, h6 { + margin-bottom: 0; + } */ + .card-body { + display: flex; + flex-flow: column nowrap; + } + .card-body div.table { + padding: 0; + overflow: hidden; + } + .card-body .results-preview { + display: flex; + flex-flow: column nowrap; + gap: var(--spacing-s); + margin: 0; + } + .card-body .results-preview > * { + width: 100%; + } + .card-body .results-preview .chart-content, + .chart-content { + background: var(--c-h0-s0-l5) !important; + padding: 4.8rem !important; + font-size: var(--t-fs-p3); + font-family: var(--t-ff-p-mono); + } + .card-body .results-preview .chart-content svg path, + .chart-content svg path{ + stroke: var(--c-h0-s0-l5); + stroke-width: 0rem; + stroke-linejoin: round; + } + /* .card-body .results-preview .chart svg path { + stroke: var(--c-h0-s0-l5); + stroke-width: 1rem; + } */ + + +/* charts ————————————————————— */ + .chart-header, + .chart-header .commands { + /* background:pink; */ + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: center; + gap: .8rem; } -} + .recharts-wrapper .recharts-surface { + overflow: visible; + } + /* chart legent */ + .recharts-wrapper ul.recharts-default-legend { + /* background: pink; */ + display: flex; + flex-flow: row nowrap; + overflow-x: auto; + + } + .recharts-wrapper ul.recharts-default-legend li { + /* background: yellow; */ + display: flex; + flex-flow: row nowrap; + white-space: nowrap; + } + .recharts-wrapper ul.recharts-default-legend li .recharts-legend-item-text { + /* background: yellow; */ + /* display: flex; + flex-flow: row nowrap; */ + white-space: nowrap; + color: var(--c-h0-s0-l2) !important; + } + /* chart tooltip */ + .recharts-wrapper p.recharts-tooltip-label { + /* font-size: var(--t-fs-p2); */ + font-weight: var(--t-fw-h); + color: var(--c-h0-s0-l1); + } + .recharts-wrapper ul.recharts-tooltip-item-list { + /* background: orange; */ + } + .recharts-wrapper ul.recharts-tooltip-item-list li { + /* background: cyan; */ + padding: 0 !important; + } -.btn-imglink { - padding-left: 0; -} -.btn-imglink:hover { - opacity: 0.6; -} -.btn-group-lg .btn { - border-radius: 10px; + +/* illustrations —————————————————————*/ + + div.illustration { + display: flex; + justify-content: center; + align-items: center; + background: var(--c-h0-s0-l5); + padding: var(--spacing-l); + width: 100%; + height: 100%; + min-height: 38rem; + overflow: hidden; + } + + + + +/* modals and tooltips ————————————————————— */ + /* variables */ + :root { + /* modal content width */ + --modal-xs-content-width: 30rem; + --modal-s-content-width: 30rem; + --modal-m-content-width: 40rem; + --modal-l-content-width: 60rem; + /* modal width */ + --modal-xs-width: calc( var(--modal-xs-content-width) + 2 * var(--modal-xs-base-padding) ); + --modal-s-width: calc( var(--modal-s-content-width) + 2 * var(--modal-s-base-padding) ); + --modal-m-width: calc( var(--modal-m-content-width) + 2 * var(--modal-m-base-padding) ); + --modal-l-width: calc( var(--modal-l-content-width) + 2 * var(--modal-l-base-padding) ); + /* modal vertical offset */ + --modal-xs-v-offset : 12.4rem; + --modal-s-v-offset : 12.4rem; + --modal-m-v-offset : 12.4rem; + --modal-l-v-offset : 12.4rem; + /* modal padding */ + --modal-xs-base-padding: 1.4rem; + --modal-xs-padding: + var(--modal-xs-base-padding) + var(--modal-xs-base-padding) + 2rem + var(--modal-xs-base-padding); + --modal-s-base-padding: 2.4rem; + --modal-s-padding: + var(--modal-s-base-padding) + var(--modal-s-base-padding) + 3.6rem + var(--modal-s-base-padding); + --modal-m-base-padding: 4.8rem; + --modal-m-padding: + var(--modal-m-base-padding) + var(--modal-m-base-padding) + 7.2rem + var(--modal-m-base-padding); + --modal-l-base-padding: 4.8rem; + --modal-l-padding: + var(--modal-l-base-padding) + var(--modal-l-base-padding) + 7.2rem + var(--modal-l-base-padding); + /* modal shadow */ + --modal-box-shadow-size-factor: 0.3; + --modal-box-shadow: + 0 calc(var(--modal-box-shadow-size-factor) * 17.900rem) calc(var(--modal-box-shadow-size-factor) * 28.800rem) rgba(0,0,0,0.1650), + 0 calc(var(--modal-box-shadow-size-factor) * 07.478rem) calc(var(--modal-box-shadow-size-factor) * 12.030rem) rgba(0,0,0,0.1186), + 0 calc(var(--modal-box-shadow-size-factor) * 03.998rem) calc(var(--modal-box-shadow-size-factor) * 06.433rem) rgba(0,0,0,0.0984), + 0 calc(var(--modal-box-shadow-size-factor) * 02.242rem) calc(var(--modal-box-shadow-size-factor) * 03.606rem) rgba(0,0,0,0.0825), + 0 calc(var(--modal-box-shadow-size-factor) * 01.190rem) calc(var(--modal-box-shadow-size-factor) * 01.915rem) rgba(0,0,0,0.0667), + 0 calc(var(--modal-box-shadow-size-factor) * 00.495rem) calc(var(--modal-box-shadow-size-factor) * 00.797rem) rgba(0,0,0,0.0464); + --modal-drop-shadow: + 0 calc(var(--modal-box-shadow-size-factor) * 17.900rem) calc(var(--modal-box-shadow-size-factor) * 28.800rem) rgba(0,0,0,0.1650), + 0 calc(var(--modal-box-shadow-size-factor) * 07.478rem) calc(var(--modal-box-shadow-size-factor) * 12.030rem) rgba(0,0,0,0.1186), + 0 calc(var(--modal-box-shadow-size-factor) * 03.998rem) calc(var(--modal-box-shadow-size-factor) * 06.433rem) rgba(0,0,0,0.0984), + 0 calc(var(--modal-box-shadow-size-factor) * 02.242rem) calc(var(--modal-box-shadow-size-factor) * 03.606rem) rgba(0,0,0,0.0825), + 0 calc(var(--modal-box-shadow-size-factor) * 01.190rem) calc(var(--modal-box-shadow-size-factor) * 01.915rem) rgba(0,0,0,0.0667), + 0 calc(var(--modal-box-shadow-size-factor) * 00.495rem) calc(var(--modal-box-shadow-size-factor) * 00.797rem) rgba(0,0,0,0.0464); + } + /* for all modals and tooltips */ + /* 1) main container, covering the entire screen surface */ + div.modal, + .tooltip { + /* default font style */ + font-size: var(--t-fs-p2); + font-weight: var(--t-fw-p-mono); + font-family: var(--t-ff-p-mono); + /* default colors */ + color: var(--c-h0-s0-l1); + border-top-color: var(--c-h0-s0-l6); /* will inherit to arrow */ + } + /* 2) secondary container, basically a column where to insert the modal */ + /* pad surface in which modal appears */ + .modal-dialog { + margin: var(--modal-m-v-offset) auto var(--modal-m-v-offset) auto; + } + /* 3) the modal itself */ + /* remove border radiuses */ + div.modal .modal-content, + .tooltip .tooltip-inner { + border: none; + border-radius: 0; + box-shadow: var(--modal-box-shadow); + /* default colors */ + background: var(--c-h0-s0-l6); + color: inherit; + } + /* hide modal content overflow */ + .tooltip .tooltip-inner, + div.modal .modal-content { + overflow: hidden; + } + + /* for all modals */ + /* 1) main container, covering the entire screen surface */ + /* set font size */ + div.modal { + font-size: var(--t-fs-p2); + } + div.modal, + div.modal .modal-dialog { + transition: none !important; + } + /* */ + /* 2) secondary container, basically a column where to insert the modal */ + div.modal .modal-dialog { + min-height: unset; /* override the default value */ + max-height: calc(100vh - 2 * var(--modal-m-v-offset)); + background: var(--c-edition-t1); + } + /* 3) the modal itself */ + div.modal .modal-dialog .modal-content { + /* no styles for now */ + max-height: inherit; /* same as secondary container */ + } + /* 4) the content of the modal */ + /* set content max-height */ + div.modal .modal-dialog .modal-content .modal-body { + overflow: scroll; + padding: 0; + } + div.modal .modal-dialog.modal-sm .modal-content .modal-body { + overflow: initial; + } + /* reset padding and margins of inner elements */ + div.modal .modal-dialog .modal-content > * { + padding: 0; + margin: 0; + } + /* space out inner elements */ + div.modal .modal-content > *:not(:last-child) { + margin-bottom: 1.4rem; + } + div.modal .modal-content .modal-header { + border-bottom: none; + } + div.modal .modal-content .modal-footer { + border-top: none; + gap: var(--spacing-xxs); + } + div.modal .modal-content .modal-footer > * { + margin: 0; + } + /*headers*/ + div.modal .modal-content .modal-header .modal-title { + /* it has an h4 tag ! inherited from the react component */ + font-size: var(--t-fs-h3); + font-weight: var(--t-fw-h); + font-family: var(--t-ff-h); + } + /* for modals of large size */ + .modal-xl { + width: var(--modal-l-width); + max-width: var(--modal-l-width); + } + .modal-xl .modal-content { + padding: var(--modal-l-padding); + } + /* for modals of medium size */ + .modal-md, + .modal-lg { + width: var(--modal-m-width); + max-width: var(--modal-m-width); + } + .modal-md .modal-content, + .modal-lg .modal-content { + padding: var(--modal-m-padding); + } + /* for modals of small size and tooltips */ + .modal-sm, + .tooltip .tooltip-inner { + width: var(--modal-s-width); + max-width: var(--modal-s-width); + } + .modal-sm .modal-content, + .tooltip .tooltip-inner { + padding: var(--modal-s-padding); + } + /* for modals of small size */ + /* for the selector modal */ + div.modal.selector .modal-body .input-group, + div.modal.selector .modal-body .form-label, + div.modal.selector .modal-body .option { + height: var(--table-cell-height); + margin: 0; + padding: 0; + } + div.modal.selector .modal-body .form-label, + div.modal.selector .modal-body .option { + padding: var(--table-cell-padding); + } + div.modal.selector .modal-body .form-label { + line-height: inherit; + } + div.modal.selector .modal-body .option { + border-top: var(--bs-border-width) solid var(--table-border-color); + } + /* div.modal.selector, + div.modal.selector .modal-dialog { + transition: none; + } */ + /* for tooltips */ + .infoTooltip.show, + .errorTooltip.show { + opacity: 1 !important; + } + .tooltip-inner { + /* padding: 20px; */ + text-align: left; + /* font-weight: 500; */ + /* max-width: 350px; */ + } + /* set font size */ + .tooltip { + font-size: var(--t-fs-p3); + } + /* bolden header */ + .tooltip .tooltip-header { + font-weight: var(--t-fw-p-mono-bold); + } + /* pad tooltip header */ + .tooltip .tooltip-inner .tooltip-header { + margin-bottom: var(--spacing-xxs); + } + /* set tooltip arrow size and specs */ + .tooltip .tooltip-arrow { + --bs-tooltip-arrow-width: 3.5355339059327376rem; + --bs-tooltip-arrow-height: 1.7677669529663688rem; + width: var(--bs-tooltip-arrow-width); + height: var(--bs-tooltip-arrow-height); + pointer-events: none; + border-top-color: inherit; + } + .tooltip .tooltip-arrow:before { + border-top-color: inherit; + } + /* hide info icon when cloned from page content */ + .tooltip .tooltip-inner .tooltip-header .icon { + display: none; + } + + + + +/* page : home ————————————————————— */ + + /* navbar */ + .homepage-header { + background-color: unset !important; + } + .homepage-header .navbar-brand, + .homepage-header .nav-link, + .homepage-header nav.navbar span.github-link-container a#github-link, + .homepage-header nav.navbar .dropdown .dropdown-item { + color: var(--c-h0-s0-l2); + } + .homepage-header .navbar-brand:focus, + .homepage-header .navbar-brand:hover, + .homepage-header .nav-link:focus, + .homepage-header .nav-link:hover, + .homepage-header nav.navbar span.github-link-container a#github-link:focus, + .homepage-header nav.navbar span.github-link-container a#github-link:hover, + .homepage-header nav.navbar .dropdown .dropdown-item:focus, + .homepage-header nav.navbar .dropdown .dropdown-item:hover { + color: var(--c-h0-s0-l6); + } + + /* section : hero */ + section.hero { + background: #390064; + background-image: url("/public/pictures/homepage-hero-bg.svg"); + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; + + margin-top: -6.4rem; /* header height */ + + padding-top: calc(var(--section-padding-l-top) + + 6.4rem /* header height */ + + 0rem /* additional padding */) !important; + padding-bottom: calc(var(--section-padding-l-bottom) + 3.6rem) !important; + + overflow: hidden; + } + + @media(max-width:768px) { + section.hero { + padding-top: calc(var(--section-padding-s-top) + + 6.4rem /* header height */ + + 0rem /* additional padding */) !important; + padding-bottom: calc(var(--section-padding-s-bottom) + 3.6rem) !important; + } + } + + /* illustration */ + section.hero .illustration { + display: flex; + flex-flow: row nowrap; + justify-content: center; + align-items: center; + /* ferdis version */ + height: 100%; + overflow: visible; + padding: 0; + background: none; + } + section.hero .illustration .layers-wrapper { + width: 100%; + height: 100%; + display: block; + position: relative; + } + section.hero .illustration .layers-wrapper img { + margin-top: auto; + width: 100%; + filter: drop-shadow(0 0.4rem 0.6rem hsla(0,0%,0%,0.2)) drop-shadow(0 0.4rem 0.6rem hsla(0,0%,0%,0.2)); + + position: absolute; + top: 0; + left: 0; + z-index: 0; + + border-radius: 0.8rem; + border: var(--bs-border-width) solid var(--c-h0-s0-l3); + } + + section.hero .illustration .layers-wrapper img:nth-child(1) { + top: 0; + /* left: 46rem; + width: 24rem; */ + } + section.hero .illustration .layers-wrapper img:nth-child(2) { + top: 90%; + left: 70%; + width: 34%; + } + @media (max-width: 768px) { + section.hero .illustration .layers-wrapper img:nth-child(2) { + top: 60%; + } + } + + /* text */ + section.hero h1, + section.hero .text p, + section.hero .text p a:hover, + section.hero .text p a:focus { + color: var(--c-h0-s0-l6); + text-decoration-color: var(--c-h0-s0-l5); + } + + section.hero .text p a { + color: var(--c-h0-s0-l4); + text-decoration-color: var(--c-h0-s0-l3); + } + + + + + +/* page : projects ————————————————————— */ + + .projectsLists { + padding: var(--spacing-xl) 0; /* needs to be bigger */ + } + .projectsLists .projectsList { /* would rather add bottom-only margin or padding so the element starts sharp */ + padding: 0 0 var(--spacing-xl) 0; + } + /* style project names to behave like buttons */ + .projectsLists .projectsList .project-name { + color: var(--c-h0-s0-l1); + cursor: pointer; + } + .projectsLists .projectsList .project-name:hover, + .projectsLists .projectsList .project-name:focus, + .projectsLists .projectsList .project-name:active { + color: var(--c-h0-s0-l2); + } + + +/* page : project config ————————————————————— */ + .proj-year-options { + padding: var(--table-cell-padding); + height: var(--table-cell-height); + display: flex; + align-items: center; + gap: var(--spacing-xxs); + } + + + +/* page : calculation/stepper ————————————————————— */ -} -.card { - background-color: #E6E6E6; - border-radius: 24px; - border: none; - padding: 30px 36px; -} -.card-header { - border: none; - background-color: inherit; -} -.card-header:first-child { - border-radius: 24px; -} -.card-header h2{ - font-size: 24px; -} -.table { - --bs-table-bg: transparent; -} \ No newline at end of file + .projectStepContainer > * { + display: flex; + gap: 4.2rem; + } + + /* step left */ + .stepLeft { + padding: 3rem; + background: var(--c-h1-s0-l5); + } + .stepLeft span, + .stepLeft li, + .stepLeft a, + .stepLeft .btn-link { /* ! overwriting default link styles here ! to iso the proto */ + font-size: var(--t-fs-p3); + font-weight: var(--t-fw-p-regular-bold); + text-decoration: none; + } + /* header */ + .projectStepContainer .stepLeft .progressMenu .header { + display: flex; + justify-content: space-between; + align-items: center; + } + .projectStepContainer .stepLeft .progressMenu .header h3 { + margin: 0.4rem 0 0 0; + } + .projectStepContainer .stepLeft .progressMenu .header > * { + width: auto; + flex: 0 0; + } + /* progress bar and last saved mention */ + .projectStepContainer .stepLeft .progressMenu .progressBar > span, + .projectStepContainer .stepLeft .progressMenu .lastSaved > span { + padding: 0rem 1rem; + display: inline-block; + width: 100%; + } + /* progress bar */ + /* bg */ + .projectStepContainer .stepLeft .progressMenu .progress { + height: 0.5rem; + background: var(--c-h0-s0-l4); + } + /* fg */ + .projectStepContainer .stepLeft .progressMenu .progress-bar { + background: var(--c-h1-s0-l2); + } + /* last saved mention */ + /*need a fix for the line height*/ + .projectStepContainer .stepLeft .progressMenu .lastSaved > span { + max-width: 100%; + white-space: break-spaces; + color: var(--c-h0-s0-l3); + } + /* progress links */ + /* text color */ + .progressMenu ol li button, + .progressMenu ol li button:hover { + color: var(--c-h0-s0-l1); + } + .progressMenu ol li.stepDisabled, + .progressMenu ol li.stepDisabled button { + color: var(--c-h0-s0-l3); + cursor: initial; + } + .progressMenu ol li.stepDisabled, .btn:disabled{ + background-color: initial; + } + .progressMenu ol { + list-style-position: inside; + padding-left: 0; + white-space: nowrap; /* pour empêcher break entre numéro et nom d'étape */ + } + /* aligning number and step name */ + .progressMenu ol li::marker, + .progressMenu ol li .item span { + line-height: var(--t-lh-p3); + } + .progressMenu ol li::marker, + .progressMenu ol li button { + vertical-align: baseline; + } + .progressMenu ol li { + flex-flow: row nowrap; + background: transparent; + padding: 0.6rem 1rem; + cursor: pointer; + } + .progressMenu li.currentStep, + .progressMenu li.currentStepDone { + background: var(--c-h1-s0-l4); + } + .progressMenu ol li > * { + padding: 0; + white-space: nowrap; + } + .progressMenu > *:not(:last-child) { + margin-bottom: 3rem; + } + /* step right */ + .projectStepContainer .stepRight { + padding: 0; + /* background: var(--c-edition-t2); */ + overflow: visible; + /* temp - before i put the logos in */ + /* padding-bottom: calc(3 * var(--spacing-l)); */ + } + /* desc and nav */ + .descAndNav { + position: relative; + } + .descAndNav .text.desc { + height: 16rem; + max-height: 16rem; + overflow: scroll; + } + .descAndNav .text.desc p:last-of-type { + display: inline; + } + .descAndNav .text.desc .info-button { + vertical-align: bottom; + } + .descAndNav .col-4 { + display: flex; + flex-flow: column nowrap; + justify-content: space-between; + } + .descAndNav .nav { + flex-flow: row nowrap; + align-items: center; + gap: var(--spacing-xxs); + margin-left: 0; + margin-right: 0; + } + .descAndNav .nav > * { + padding-left: 0; + padding-right: 0; + } + .descAndNav .prevNav, + .descAndNav .nextNav { + display: flex; + flex-flow: row nowrap; + flex: 1 1 0; + } + .descAndNav .prevNav button, + .descAndNav .nextNav button { + display: flex; + flex-flow: row nowrap; + white-space: nowrap; + justify-content: center; + width: 100%; + } + /* years horizontal tabs */ + ul.nav-tabs { + --bs-nav-tabs-border-width: 0; + --bs-nav-tabs-border-radius: 0; + --bs-nav-tabs-link-active-color: var(--c-h0-s0-l1); + --bs-nav-tabs-link-active-bg: var(--c-h1-s0-l4); + border-bottom: 0; + } + ul.nav-tabs li.nav-item button { + font-size: var(--t-fs-p3); + font-weight: var(--t-fw-p-regular-bold); + color: var(--c-h0-s0-l1); + padding: 0.6rem 1rem; + text-align: left; + transition: none; /* the existing transition is nice but it does not exist in the steps nav and i prefer to stay coherent */ + } + div.tab-content .tab-pane > hr { + margin-top: -1rem; /* dirty fix */ + margin-bottom: 1.4rem; + } + /* user note */ + .projectStepContainer .user-note { + font-size: var(--t-fs-p2); + font-weight: var(--t-fw-p-mono); + font-family: var(--t-ff-p-mono); + color: var(--c-h0-s0-l1); + margin-bottom: 0 !important; + } + .projectStepContainer .user-note .form-label { + margin: 0; + padding: 0; + width: 100%; + } + .projectStepContainer .user-note-button { + font-size: var(--t-fs-p3); + font-weight: var(--t-fw-p-mono-bold); + font-family: var(--t-ff-p-mono); + color: var(--c-h0-s0-l3); + width: 100%; + height: var(--table-cell-height); + padding: var(--table-cell-padding); + } + .projectStepContainer .user-note-button:hover, + .projectStepContainer .user-note-button:focus, + .projectStepContainer .user-note-button:active { + background: var(--c-h0-s0-l5); + } + .projectStepContainer .user-note .form-control { + min-height: 20rem; + } + + + + +/* overflows fading out */ + /* horizontal overflows */ + .recharts-wrapper ul.recharts-default-legend:after { + content: ""; + position: absolute; + right: 0; + height: 100%; + width: var(--spacing-l); + content: ""; + background: linear-gradient(to left, var(--c-h0-s0-l5), transparent); + pointer-events: none; /* so the text is still selectable */ + } + /* vertical overflows */ + .masked-overflow-y::-webkit-scrollbar{ + display: none; + + } + .masked-overflow-y { + /* scroll bar width, for use in mask calculations*/ + --scrollbar-width: 0.8rem; + /* mask fade distance, for use in mask calculations */ + --mask-height: 3.2rem; + /* If content exceeds height of container, overflow! */ + overflow-y: auto; + /* Our height limit */ + /* height: 300px; */ + /* Need to make sure container has bottom space, + otherwise content at the bottom is always faded out */ + padding-bottom: var(--mask-height) !important; + /* Keep some space between content and scrollbar */ + padding-right: 2rem; + /* The CSS mask */ + /* The content mask is a linear gradient from top to bottom */ + --mask-image-content: linear-gradient( + to bottom, + /* transparent, */ /* commented out to make it bottom-only */ + black var(--mask-height), + black calc(100% - var(--mask-height)), + transparent); + /* Here we scale the content gradient to the width of the container + minus the scrollbar width. The height is the full container height */ + --mask-size-content: calc(100% - var(--scrollbar-width)) 100%; + /* The scrollbar mask is a black pixel */ + --mask-image-scrollbar: linear-gradient(black, black); + /* The width of our black pixel is the width of the scrollbar. + The height is the full container height */ + --mask-size-scrollbar: var(--scrollbar-width) 100%; + /* Apply the mask image and mask size variables */ + mask-image: var(--mask-image-content), var(--mask-image-scrollbar); + mask-size: var(--mask-size-content), var(--mask-size-scrollbar); + /* Position the content gradient in the top left, and the + scroll gradient in the top right */ + mask-position: 0 0, 100% 0; + /* We don't repeat our mask images */ + mask-repeat: no-repeat, no-repeat; + } \ No newline at end of file diff --git a/frontend/src/pages/BAU/BAUIntro.tsx b/frontend/src/pages/BAU/BAUIntro.tsx index 1fd45d3..6d58230 100644 --- a/frontend/src/pages/BAU/BAUIntro.tsx +++ b/frontend/src/pages/BAU/BAUIntro.tsx @@ -1,12 +1,13 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import { Table, Container, Row, Col, Badge, OverlayTrigger, Tooltip } from 'react-bootstrap' +import { Table, Container, Row, Col, Badge } from 'react-bootstrap' import { ProjectType} from '../../frontendTypes' import '../Project.css' import DescAndNav from '../../components/DescAndNav' import ItemWithOverlay from '../../components/ItemWithOverlay' +import Footer from "../../components/Footer" export default function BAUIntro(){ @@ -36,90 +37,109 @@ export default function BAUIntro(){ }, [keycloak, initialized, projectId, navigate]) return ( - - - -

Business as usual (BAU) scenario

- ", variant: "primary"}} - > -

After defining the initial situation in your city/country, it is necessary to project transport emissions into the future on a business-as-usual basis.

-
-

- The intention is to show the difference compared to the situation when a strategy, policy, programme or project were to be introduced. The BAU scenario serves as a reference scenario (baseline emissions), which illustrates the results of current trends often in contrast to alternative scenarios that take into account specific interventions -

-

- You will need to project evolutions of transport activity on your territory. -

- ASIF Framework Diagram -

The calculation of transport related emissions requires information on

- - - - - - - - - - +

BAU Scenario

+ +

After defining the initial situation in your city/country, it is necessary to project transport emissions into the future on a business-as-usual basis.

+

The intention is to show the difference compared to the situation when a strategy, policy, programme or project were to be introduced. The BAU scenario serves as a reference scenario (baseline emissions), which illustrates the results of current trends often in contrast to alternative scenarios that take into account specific interventions

+

You will need to project evolutions of transport activity on your territory.

+
+
+ ASIF Framework Diagram +
+

Required for this calculation

+
DataUnit
- + <> +
+ + +
+ + {/* Data */} + {/* Unit */} + + + + + + + + + + - - - - - - - - - - - -
DataUnit
- 🛈 Projected transport activity + + Projected transport activity + - - vkt: vehicle-kilometre and tkm: ton-kilometre
- Projected share of the transport activity by vehicle category and fuel type - %vkt and %tkm
- Projected vehicle fuel consumption according to vehicle category and fuel type - l-kW-kg/100km
-

The tool will also offer you to add [optional but recommended]

- - - - - - - - - - + + + + + + + + + + + +
DataUnit
- + vkt: vehicle-kilometre and tkm: ton-kilometre
+ Projected share of the transport activity by vehicle category and fuel type + %vkt and %tkm
+ Projected vehicle fuel consumption according to vehicle category and fuel type + l-kW-kg/100km
+

Required for later calculations

+ + + {/* Data */} + {/* Unit */} + + + + + + + + + + - - - - + + + + - - - -
DataUnit
- 🛈 Projected occupation rate per transport category + + Projected occupation rate per transport category + - - passengers / load (tons)
- + passengers / load (tons)
- 🛈 Projected CO2 content of electricity and hydrogen production + + Projected CO2 content of electricity and hydrogen production + - - gCO2/kWh or gCO2/kg
- -
-
- + + gCO2/kWh or gCO2/kg + + + + +
+ + +
+ + + +
+ + + +
+ ) } diff --git a/frontend/src/pages/BAU/BAUStep1.tsx b/frontend/src/pages/BAU/BAUStep1.tsx index 4777a7d..d477ecb 100644 --- a/frontend/src/pages/BAU/BAUStep1.tsx +++ b/frontend/src/pages/BAU/BAUStep1.tsx @@ -1,17 +1,17 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Table, Button, Badge, Modal, Form, Tabs, Tab, Alert} from 'react-bootstrap' -import {FuelType, InputBAUStep1, InputInventoryStep2, ProjectType} from '../../frontendTypes' +import {Table, Button, Badge, Modal, Tabs, Tab, Alert} from 'react-bootstrap' +import {InputBAUStep1, InputInventoryStep2, ProjectType} from '../../frontendTypes' import ChoiceModal from '../../components/ChoiceModal' import '../Project.css' import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' -import TdDiagonalBar from '../../components/TdDiagonalBar' import PercentInput from '../../components/PercentInput' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function BAUStep1(){ const { keycloak, initialized } = useKeycloak(); @@ -134,30 +134,32 @@ export default function BAUStep1(){

Transport activity

{sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/BAU/intro', content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} seeMoreCallBack={()=>setShowInfo(true)} > -

- Mileage is the cornerstorne of the calculation of transport GHG emissions. Once the total vehicle mileage per vehicle category is known, expected yearly growth for the business as usual scenario can be added. -

+

Mileage is the cornerstorne of the calculation of transport GHG emissions. Once the total vehicle mileage per vehicle category is known, expected yearly growth for the business as usual scenario can be added.

+

The total vkt should comply with the actual transport activity within the city or country territory

-

- The total vkt should comply with the actual transport activity within the city or country territory -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + fill> + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( +
+ + {/* Transport modes */} + {/* VKT */} + {/* Source */} + {/* VKT growth */} + - - - - + + + + @@ -169,16 +171,21 @@ export default function BAUStep1(){ const vktRate = vehicle.vktRate[yearIndex] return ( - - + + + : } ) })} - + + + + + +
🛈 Vehicle🛈 Inventory VKT (Mkm/y)🛈 Src🛈 Yearly VKT growth (%)VehicleInv. VKT (Mkm/y)SrcYearly VKT growth (%)
{vtype}{inventoryVehicle.vkt}{vtype} {source ? configureSource(vtype)}/> - : } updateInputPercent(vtype, yearIndex, e.target.value)}>
))} @@ -195,14 +202,14 @@ export default function BAUStep1(){ Transport activity information - +

The composition of a city-specific vehicle fleet strongly influences local transport emissions. The more private cars are on the road and the larger or older the vehicles are, the higher their fuel consumption is and the higher the related GHG emissions are. In other words, GHG emissions depend on the vehicle fleet and on the distribution of VKT across the fleet's vehicle mix.

Data on the vehicle fleet is generally available from vehicle registration statistics for passenger cars, taxis, trucks, and motorcycles (e-bikes are mostly excluded), which includes technical specifications for the different vehicle types. Once the registered fleet is documented for the base year, e.g. 2015, only newly registered (and deregistered) vehicles have to be monitored each year.

If there are no big differences in the fleet compositions across different cities in a country, using national averages for urban fleet composition may be considered. Where the fleet is known to be quite specific, however, these local characteristics should be accounted for, e.g. prosperous metropolitan areas may have a larger number of new and larger cars than less prosperous mid-sized cities with a smaller but older fleet.

diff --git a/frontend/src/pages/BAU/BAUStep2.tsx b/frontend/src/pages/BAU/BAUStep2.tsx index f078c5e..721b8fc 100644 --- a/frontend/src/pages/BAU/BAUStep2.tsx +++ b/frontend/src/pages/BAU/BAUStep2.tsx @@ -1,7 +1,7 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Table, Button, Badge, Form, Tabs, Tab, Alert} from 'react-bootstrap' +import {Table, Button, Badge, Tabs, Tab, Alert} from 'react-bootstrap' import {FuelType, InputBAUStep2, ProjectType} from '../../frontendTypes' import ChoiceModal from '../../components/ChoiceModal' @@ -12,6 +12,7 @@ import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWr import TdDiagonalBar from '../../components/TdDiagonalBar' import PercentInput from '../../components/PercentInput' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function BAUStep2(){ const { keycloak, initialized } = useKeycloak(); @@ -173,36 +174,41 @@ export default function BAUStep2(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/BAU/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %. -

+

Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %.

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Fuels */} + {/* VKT */} + {/* Inventory VKT */} + {/* Source */} + {/* Share per fuel */} + - - - + + - - - + + + @@ -222,15 +228,15 @@ export default function BAUStep2(){ const ftype = ftypes[i] as FuelType const value = vehicle?.fuels[ftype]?.percent[yearIndex] || "" const percentSource = vehicle?.fuels[ftype]?.percentSource - const invPercent = project.stages.Inventory[0].steps?.[2].vtypes?.[vtype].fuels?.[ftype]?.percent || "?" + const invPercent = project.stages.Inventory[0].steps?.[2].vtypes?.[vtype]?.fuels?.[ftype]?.percent || "?" fuelJsx.push( - - + + - + - + @@ -249,7 +255,14 @@ export default function BAUStep2(){ fuelJsx ] })} - + + + + + + + +
🛈 Vehicle🛈 Fuels - VehicleFuelsVehicle kilometers travelled. Values for each fuel are computed as
- (Inventory VKT per vehicle and fuel (Mkm/y) x ( 1 + Yearly vkt growth (%) / 100) ^ Number of years) x VKT (%) / 100 + (Inv. VKT per vehicle and fuel (Mkm/y) × ( 1 + Yearly vkt growth (%) / 100) ^ Number of years) × VKT (%) / 100
- }>🛈 Computed VKT (Mkm/y)
+ }>Computed VKT (Mkm/y)
🛈 Inv. VKT (%)🛈 Src🛈 VKT (%)Inv. VKT (%)SrcVKT (%)
{ftype}{parseFloat((parseFloat(value) / 100 * vkt).toFixed(10)) || ""}{ftype} {invPercent} {percentSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputPercent(vtype, ftype, yearIndex, e.target.value)} invalid={totalPercent > 100}> @@ -239,9 +245,9 @@ export default function BAUStep2(){ } return [
{vtype}{vtype} All{vkt} 100 100 ? "cellError": ""}>{totalPercent || 0}
))} diff --git a/frontend/src/pages/BAU/BAUStep3.tsx b/frontend/src/pages/BAU/BAUStep3.tsx index f172799..377194b 100644 --- a/frontend/src/pages/BAU/BAUStep3.tsx +++ b/frontend/src/pages/BAU/BAUStep3.tsx @@ -10,6 +10,7 @@ import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function BAUStep3(){ const { keycloak, initialized } = useKeycloak(); @@ -154,36 +155,36 @@ export default function BAUStep3(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/BAU/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Once transport activity i.e. mileage by mode and fuel is known, it needs to be multiplied with adequate fuel consumption factors. -

+

Once transport activity i.e. mileage by mode and fuel is known, it needs to be multiplied with adequate fuel consumption factors.

+

Please enter the expected average fuel/energy consumption changes - for each vehicle category and per fuel type- for the following years (average fuel/energy consumption per vehicle per 100 km).

+

If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach.

+

Values are pre-filled with Inventory data if available; this is done to simplify the filling process. Please update values accordingly.

+
-

- Please enter the expected average fuel/energy consumption changes - for each vehicle category and per fuel type- for the following years (average fuel/energy consumption per vehicle per 100 km). -

-

- If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach. -

-

- Values are pre-filled with Inventory data if available; this is done to simplify the filling process. Please update values accordingly. -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Fuels */} + {/* Inv. cons. */} + {/* Source */} + {/* Consumption */} + - - - - - + + + + + @@ -198,13 +199,13 @@ export default function BAUStep3(){ const consSource = vehicle.fuels[ftype]?.consSource const invCons = project.stages.Inventory[0].steps[3].vtypes[vtype].fuels[ftype].cons fuelJsx.push( - {i===0 && } - - + {i===0 && } + + + + + + + +
🛈 Vehicle🛈 Fuels🛈 Inv. Cons🛈 Src🛈 Cons (l-kg-kwh/100km)VehicleFuelsInv. ConsSrcCons (l-kg-kwh/100km)
{vtype}{ftype}{invCons}{vtype}{ftype} {consSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputCons(vtype, ftype, yearIndex, e.target.value)}> @@ -215,7 +216,13 @@ export default function BAUStep3(){ fuelJsx ] })} - +
))} diff --git a/frontend/src/pages/BAU/BAUStep4.tsx b/frontend/src/pages/BAU/BAUStep4.tsx index 760cdeb..2d7fa23 100644 --- a/frontend/src/pages/BAU/BAUStep4.tsx +++ b/frontend/src/pages/BAU/BAUStep4.tsx @@ -10,6 +10,7 @@ import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function BAUStep4(){ const { keycloak, initialized } = useKeycloak(); @@ -116,35 +117,35 @@ export default function BAUStep4(){ return ( <> -

CO2 content of alternative energy production

+

CO2 content of alt. energy production

", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/BAU/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- In MobiliseYourCity methodology, transport related GHG emissions can integrate or not the CO2 content of the production of electricity and hydrogen (based on national/local energy mix). -

+

In MobiliseYourCity methodology, transport related GHG emissions can integrate or not the CO2 content of the production of electricity and hydrogen (based on national/local energy mix).

+

If you have this information, it will allow you to choose later between a TTW and a WTW approach for emissions calculation.

+

Please enter the predicted CO2 content of electricity and hydrogen production.

-

- If you have this information, it will allow you to choose later between a TTW and a WTW approach for emissions calculation. -

-

- Please enter the predicted CO2 content of electricity and hydrogen production. -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( -

Electricity

+ {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+

Electricity

+ + {/* Network */} + {/* Source */} + {/* Inventory emissions */} + {/* Emissions */} + - - - - + + + + @@ -154,29 +155,39 @@ export default function BAUStep4(){ const value = inputData.electricity[network].value[yearIndex] const inventoryValue = project.stages.Inventory[0].steps[4].electricity[network].value return ( - + - + )})} + + + + + +
🛈 Network🛈 Src🛈 Inv. Emissions🛈 Emissions (gCO2/kWh)NetworkSrcInv. EmissionsEmissions (gCO2/kWh)
{network} (electric){network} (electric) {source ? configureSource('electricity', network)}/> - : } - - {inventoryValue} + : } updateInput('electricity', network, yearIndex, e.target.value)}>
-

Hydrogen

+

Hydrogen

+ + {/* Network */} + {/* Source */} + {/* Inventory emissions */} + {/* Emissions */} + - - - - + + + + @@ -186,19 +197,23 @@ export default function BAUStep4(){ const value = inputData.hydrogen[network].value[yearIndex] const inventoryValue = project.stages.Inventory[0].steps[4].hydrogen[network].value return ( - + - + )})} + + + + + +
🛈 Network🛈 Src🛈 Inv. Emissions🛈 Emissions (gCO2/kg)NetworkSrcInv. EmissionsEmissions (gCO2/kg)
{network} (electric){network} (electric) {source ? configureSource('hydrogen', network)}/> - : } - - {inventoryValue} + : } updateInput('hydrogen', network, yearIndex, e.target.value)}>
))} diff --git a/frontend/src/pages/BAU/BAUStep5.tsx b/frontend/src/pages/BAU/BAUStep5.tsx index 260cad4..abd9e38 100644 --- a/frontend/src/pages/BAU/BAUStep5.tsx +++ b/frontend/src/pages/BAU/BAUStep5.tsx @@ -1,7 +1,6 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Button} from 'react-bootstrap' import {EmissionsResults, InputInventoryStep7, ProjectType} from '../../frontendTypes' import '../Project.css' @@ -105,12 +104,10 @@ export default function BAUStep5(){

Results

-

- This page displays a short summary of emissions for the BAU scenario. More tables and visualisations are available in the Compare section of the project. -

+

This page displays a short summary of emissions for the BAU scenario. More tables and visualisations are available in the Compare section of the project.

-

Emissions per year

+

Emissions per year

- + <> +
+ {/*
+

Passenger Modal Share

+
+ +
+
*/} +
+
+ +
+
+
+ +
) diff --git a/frontend/src/pages/Climate/ClimateIntro.tsx b/frontend/src/pages/Climate/ClimateIntro.tsx index ad0f880..711f2b0 100644 --- a/frontend/src/pages/Climate/ClimateIntro.tsx +++ b/frontend/src/pages/Climate/ClimateIntro.tsx @@ -1,12 +1,12 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import { Table, Container, Row, Col, Badge, OverlayTrigger, Tooltip, Modal, Button, Dropdown } from 'react-bootstrap' +import { Table, Container, Row, Col, Badge, Modal, Button, Dropdown } from 'react-bootstrap' import { ProjectType} from '../../frontendTypes' import '../Project.css' import DescAndNav from '../../components/DescAndNav' -import ChoiceModal from '../../components/ChoiceModal' +import Footer from "../../components/Footer" export default function ClimateIntro(){ @@ -64,13 +64,23 @@ export default function ClimateIntro(){ } const MethodSelector = () => { return ( -
+
method === "With upstream calculation" ? setMethod("Without upstream calculation") : setMethod("With upstream calculation")}> - + {method === "With upstream calculation" ? "With upstream calculation" : "Without upstream calculation"} - - + + {method === "Without upstream calculation" ? "With upstream calculation" : "Without upstream calculation"} @@ -79,97 +89,103 @@ export default function ClimateIntro(){ ) } return ( -
+ <> +
- - -

Climate scenario

+ + +

Climate Scenario

", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/edit', content: "Cancel", variant: "link"}} + nextNav={{trigger: nextTrigger, content: "Start", showArrow: true, variant: "primary"}} >

This step enables calculating a climate scenario, based on mitigations actions. Calculating the impact of NUMP/SUMP measures in the MYC Calculator requires bundling measures based on the ASI : Avoid-Shift-Improve.

+

In order to derive transport demand data for the calculations two different data input approaches are possible : . This will depend on if you count with a transport model. In order to avoid wrong results please choose and apply just one approach.

-

- In order to derive transport demand data for the calculations two different data input approaches are possible : . This will depend on if you count with a transport model. In order to avoid wrong results please choose and apply just one approach. -

- Avoid-Shift-Improve Approach (Transport NAMA Handbook, GIZ, 2015 based on Dalkmann and Brannigan 2007) +
+ Avoid-Shift-Improve Approach (Transport NAMA Handbook, GIZ, 2015 based on Dalkmann and Brannigan 2007) +

Calculation method

Method
-

{method}, the calculation of transport related emissions requires information on

+ {/*

{method}, the calculation of transport related emissions requires information on

*/} +

Required for this calculation

+ + {/* Data */} + {/* Unit */} + - - + + {method === "With upstream calculation" ? - + - + - + - + : - + - + - + - + - + - + } @@ -181,7 +197,7 @@ export default function ClimateIntro(){ With or without upstream calculation - +

The first approach “WITH UPSTREAM CALCULATIONS - INPUT FROM EXTERNAL TRANSPORT PLANNING TOOL” the user can directly enter transport performance (pkm/tkm) and the vehicle kilometers travelled (vkt) provided by an external transport planning tool.

If no data from a transport planning tool are available, the second approach “WITHOUT UPSTREAM CALCULATIONS - CALCULATIONS WITHIN THIS TOOL” can be applied. For calculating potential GHG savings from mitigation measures within this approach the “ASI” (Avoid/Shift/Improve) approach is applied. Users have to estimate the effect of transport related actions on the transport demand for each mitigation action type (Avoid, Shift or Improve). E.g. Parking area management may lead to 5% avoided passenger car vehicle kilometers travelled (vkt).

    @@ -200,6 +216,17 @@ export default function ClimateIntro(){ - + +
    + + +
+
+ + + + + + ) } diff --git a/frontend/src/pages/Climate/ClimateWithUpstreamStep1.tsx b/frontend/src/pages/Climate/ClimateWithUpstreamStep1.tsx index 8d93f50..581a88b 100644 --- a/frontend/src/pages/Climate/ClimateWithUpstreamStep1.tsx +++ b/frontend/src/pages/Climate/ClimateWithUpstreamStep1.tsx @@ -2,16 +2,15 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" import {Table, Button, Badge, Modal, Form, Tabs, Tab, Alert} from 'react-bootstrap' -import {FuelType, InputClimateWithUpstreamStep1, InputInventoryStep2, ProjectType} from '../../frontendTypes' +import {InputClimateWithUpstreamStep1, ProjectType} from '../../frontendTypes' import ChoiceModal from '../../components/ChoiceModal' import '../Project.css' import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' -import TdDiagonalBar from '../../components/TdDiagonalBar' -import PercentInput from '../../components/PercentInput' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithUpstreamStep1(){ const { keycloak, initialized } = useKeycloak(); @@ -150,27 +149,31 @@ export default function ClimateWithUpstreamStep1(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/intro', content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} seeMoreCallBack={()=>setShowInfo(true)} > -

- Mileage is the cornerstorne of the calculation of transport GHG emissions. Please enter VKT computed with your transport planning tool for the current climate scenario. -

+

Mileage is the cornerstorne of the calculation of transport GHG emissions. Please enter VKT computed with your transport planning tool for the current climate scenario.

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
DataUnitDataUnit
- Projected transport activity - mileage for each transport mode per year + Projected transport activity - mileage for each transport mode per year vkt: vehicle-kilometrevkt: vehicle-kilometre
- Projected transport performance for each transport mode per year + Projected transport performance for each transport mode per year pkm: passenger-km or tkm: tons-kmpkm: passenger-km or tkm: tons-km
- Projected share of the transport activity by vehicle category and fuel type + Projected share of the transport activity by vehicle category and fuel type %vkt and %tkm%vkt and %tkm
- Projected vehicle fuel consumption according to vehicle category and fuel type + Projected vehicle fuel consumption according to vehicle category and fuel type l-kW-kg/100kml-kW-kg/100km
- Projected avoided transport activity + Projected avoided transport activity vkt: vehicle-kilometrevkt: vehicle-kilometre
- Projected added transport activity + Projected added transport activity vkt: vehicle-kilometrevkt: vehicle-kilometre
- Projected vehicle load + Projected vehicle load passengers or tonspassengers or tons
- Projected vehicle shift - orgin of shifted trips + Projected vehicle shift - orgin of shifted trips % of trips% of trips
- Projected share of the transport activity by vehicle category and fuel type + Projected share of the transport activity by vehicle category and fuel type %vkt and %tkm%vkt and %tkm
- Projected vehicle fuel consumption according to vehicle category and fuel type + Projected vehicle fuel consumption according to vehicle category and fuel type l-kW-kg/100kml-kW-kg/100km
+ + {/* Vehicle */} + {/* BAU VKT */} + {/* Source */} + {/* Climate VKT */} + - - - - + + + + @@ -180,18 +183,23 @@ export default function ClimateWithUpstreamStep1(){ const vkt = vehicle.vkt?.[yearIndex] || "" return ( - - + + + : } ) })} - + + + + + +
🛈 Vehicle🛈 BAU VKT (Mkm/y)🛈 Src🛈 Climate VKT (Mkm/y)VehicleBAU VKT (Mkm/y)SrcClimate VKT (Mkm/y)
{vtype}{bAUVkt?.[vtype]?.[yearIndex + 1]}{vtype} {source ? configureSource(vtype)}/> - : } updateInput(vtype, yearIndex, e.target.value)}>
))} @@ -208,14 +216,14 @@ export default function ClimateWithUpstreamStep1(){ Transport activity information - +

The composition of a city-specific vehicle fleet strongly influences local transport emissions. The more private cars are on the road and the larger or older the vehicles are, the higher their fuel consumption is and the higher the related GHG emissions are. In other words, GHG emissions depend on the vehicle fleet and on the distribution of VKT across the fleet's vehicle mix.

Data on the vehicle fleet is generally available from vehicle registration statistics for passenger cars, taxis, trucks, and motorcycles (e-bikes are mostly excluded), which includes technical specifications for the different vehicle types. Once the registered fleet is documented for the base year, e.g. 2015, only newly registered (and deregistered) vehicles have to be monitored each year.

If there are no big differences in the fleet compositions across different cities in a country, using national averages for urban fleet composition may be considered. Where the fleet is known to be quite specific, however, these local characteristics should be accounted for, e.g. prosperous metropolitan areas may have a larger number of new and larger cars than less prosperous mid-sized cities with a smaller but older fleet.

diff --git a/frontend/src/pages/Climate/ClimateWithUpstreamStep2.tsx b/frontend/src/pages/Climate/ClimateWithUpstreamStep2.tsx index 9f6e52f..a49ce90 100644 --- a/frontend/src/pages/Climate/ClimateWithUpstreamStep2.tsx +++ b/frontend/src/pages/Climate/ClimateWithUpstreamStep2.tsx @@ -137,25 +137,28 @@ export default function ClimateWithUpstreamStep2(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/With/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Please enter the transport performances (Mio. passenger-kilometers or Mio. tons-kilometers). The same data can be entered for the following years taking into account the changes planned in the climate scenario. -

+

Please enter the transport performances (Mio. passenger-kilometers or Mio. tons-kilometers). The same data can be entered for the following years taking into account the changes planned in the climate scenario.

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Source */} + {/* Transport performance */} + - - - + + + @@ -165,17 +168,21 @@ export default function ClimateWithUpstreamStep2(){ const ukm = vehicle.ukm[yearIndex] return ( - + + : } ) })} - + + + + +
🛈 Vehicle🛈 SrcTransport performance (Mpkm or Mtkm)VehicleSrcTransport performance (Mpkm or Mtkm)
{vtype}{vtype} {source ? configureSource(vtype)}/> - : } updateInput(vtype, yearIndex, e.target.value)}>
))} diff --git a/frontend/src/pages/Climate/ClimateWithUpstreamStep3.tsx b/frontend/src/pages/Climate/ClimateWithUpstreamStep3.tsx index 13b1ef5..3b67572 100644 --- a/frontend/src/pages/Climate/ClimateWithUpstreamStep3.tsx +++ b/frontend/src/pages/Climate/ClimateWithUpstreamStep3.tsx @@ -1,7 +1,7 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Table, Button, Badge, Form, Tabs, Tab, Alert} from 'react-bootstrap' +import {Table, Button, Badge, Tabs, Tab, Alert} from 'react-bootstrap' import {FuelType, InputBAUStep2, InputClimateWithUpstreamStep1, ProjectType} from '../../frontendTypes' import ChoiceModal from '../../components/ChoiceModal' @@ -12,6 +12,7 @@ import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWr import TdDiagonalBar from '../../components/TdDiagonalBar' import PercentInput from '../../components/PercentInput' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithUpstreamStep3(){ const { keycloak, initialized } = useKeycloak(); @@ -167,39 +168,43 @@ export default function ClimateWithUpstreamStep3(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} - > -

- Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %. -

+ prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/With/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} + > +

Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %.

+

Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly.

-

- Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly. -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Fuels */} + {/* Computed VKT */} + {/* BAU VKT */} + {/* Source */} + {/* VKT */} + - - - + + - - - + + + @@ -222,13 +227,13 @@ export default function ClimateWithUpstreamStep3(){ const percentSource = vehicle?.fuels[ftype]?.percentSource const bAUPercent = project.stages.BAU[0].steps?.[2].vtypes?.[vtype].fuels?.[ftype]?.percent[yearIndex] || "?" fuelJsx.push( - - - + + + - + - - + + - + 100 ? "cellError": ""} value={totalPercent || 0} decimals={0}>, fuelJsx ] })} - + + + + + + + +
🛈 Vehicle🛈 Fuels + VehicleFuels Vehicle kilometers travelled. Values for each fuel are computed as
- Upstream VKT per vehicle (Mkm/y) x VKT (%) / 100 + Upstream VKT per vehicle (Mkm/y) × VKT (%) / 100
- }>🛈 Computed VKT (Mkm/y)
+ }>Computed VKT (Mkm/y)
🛈 BAU. VKT (%)🛈 Src🛈 VKT (%)BAU. VKT (%)SrcVKT (%)
{ftype}{parseFloat((parseFloat(value) / 100 * vkt).toFixed(10)) || ""}{bAUPercent}{ftype} {percentSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputPercent(vtype, ftype, yearIndex, e.target.value)} invalid={totalPercent > 100}> @@ -237,17 +242,24 @@ export default function ClimateWithUpstreamStep3(){ } return [
{vtype}{vtype} All{vkt}100 100 ? "cellError": ""}>{totalPercent || 0}
))} diff --git a/frontend/src/pages/Climate/ClimateWithUpstreamStep4.tsx b/frontend/src/pages/Climate/ClimateWithUpstreamStep4.tsx index 694ab5c..7503688 100644 --- a/frontend/src/pages/Climate/ClimateWithUpstreamStep4.tsx +++ b/frontend/src/pages/Climate/ClimateWithUpstreamStep4.tsx @@ -10,6 +10,7 @@ import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithUpstreamStep4(){ const { keycloak, initialized } = useKeycloak(); @@ -154,36 +155,35 @@ export default function ClimateWithUpstreamStep4(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/With/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Once transport activity i.e. mileage by mode and fuel is known, it needs to be multiplied with adequate fuel consumption factors. -

+

Once transport activity i.e. mileage by mode and fuel is known, it needs to be multiplied with adequate fuel consumption factors.

+

Please enter the expected average fuel/energy consumption changes - for each vehicle category and per fuel type- for the following years (average fuel/energy consumption per vehicle per 100 km).

+

If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach.

+

Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly.

-

- Please enter the expected average fuel/energy consumption changes - for each vehicle category and per fuel type- for the following years (average fuel/energy consumption per vehicle per 100 km). -

-

- If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach. -

-

- Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly. -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Fuels */} + {/* BAU cons. */} + {/* Source */} + {/* Cons. */} + - - - - - + + + + + @@ -198,13 +198,13 @@ export default function ClimateWithUpstreamStep4(){ const consSource = vehicle.fuels[ftype]?.consSource const bAUCons = project.stages.BAU[0].steps?.[3].vtypes?.[vtype].fuels?.[ftype]?.cons[yearIndex] || "?" fuelJsx.push( - {i===0 && } - - + {i===0 && } + + + + + + + +
🛈 Vehicle🛈 Fuels🛈 BAU. Cons🛈 Src🛈 Cons (l-kg-kwh/100km)VehicleFuelsBAU. ConsSrcCons (l-kg-kwh/100km)
{vtype}{ftype}{bAUCons}{vtype}{ftype} {consSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputCons(vtype, ftype, yearIndex, e.target.value)}> @@ -215,7 +215,13 @@ export default function ClimateWithUpstreamStep4(){ fuelJsx ] })} - +
))} diff --git a/frontend/src/pages/Climate/ClimateWithUpstreamStep5.tsx b/frontend/src/pages/Climate/ClimateWithUpstreamStep5.tsx index d5cd832..7cd7be3 100644 --- a/frontend/src/pages/Climate/ClimateWithUpstreamStep5.tsx +++ b/frontend/src/pages/Climate/ClimateWithUpstreamStep5.tsx @@ -1,7 +1,6 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import { Button, Modal} from 'react-bootstrap' import {EmissionsResults, InputInventoryStep7, ProjectType} from '../../frontendTypes' import '../Project.css' @@ -106,12 +105,10 @@ export default function ClimateWithUpstreamStep5(){

Results

-

- This page displays a short summary of emissions for this climate scenario. More tables and visualisations are available in the Compare section of the project. -

+

This page displays a short summary of emissions for this climate scenario. More tables and visualisations are available in the Compare section of the project.

-

Emissions

+

Emissions

diff --git a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep1.tsx b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep1.tsx index 6741994..3c9c959 100644 --- a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep1.tsx +++ b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep1.tsx @@ -11,6 +11,7 @@ import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import { computeVktAfterASI } from '../../utils/asiComputations' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithoutUpstreamStep1(){ const { keycloak, initialized } = useKeycloak(); @@ -150,36 +151,41 @@ export default function ClimateWithoutUpstreamStep1(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/intro', content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} seeMoreCallBack={()=>setShowInfo(true)} > -

- Please enter the percentage of vehicle kilometers travelled (vkt) that will be avoided with the planned mitigation measures. -

+

Please enter the percentage of vehicle kilometers travelled (vkt) that will be avoided with the planned mitigation measures.

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* BAU VKT */} + {/* Source */} + {/* Avoided VKT */} + {/* Climate VKT */} + - - - - - - + + + + + @@ -190,20 +196,27 @@ export default function ClimateWithoutUpstreamStep1(){ const avoidedVkt = vehicle.avoidedVkt?.[yearIndex] || "" return ( - - + + + : } - + ) })} - + + + + + + + +
🛈 Vehicle🛈 BAU VKT (Mkm)🛈 Src🛈 Avoided VKT (%) +
VehicleBAU VKT (Mkm)SrcAvoided VKT (%) Computed Climate VKT as
- (VKT at the end of previous year (Mkm) + BAU VKT added between years (Mkm)) x Avoided VKT (%) / 100 + (VKT at the end of previous year (Mkm) + BAU VKT added between years (Mkm)) × Avoided VKT (%) / 100
First VKT values come from reference year data (inventory). - }>🛈 Climate VKT (Mkm)
+ }>Climate VKT (Mkm)
{vtype}{bAUVkt?.[vtype]?.[yearIndex+1]}{vtype} {source ? configureSource(vtype)}/> - : } updateInput(vtype, yearIndex, e.target.value)}> {climateVkt[vtype]?.[yearIndex+1] || 0}
))}
@@ -219,7 +232,7 @@ export default function ClimateWithoutUpstreamStep1(){ Avoided vkt information - +

Example of measure: Parking space reduction

Remark 1: "Avoid" means that the trip does not take place at all. If the trip is just replaced by a trip with a different mode of transport, it has to be entered in section "Shift".

Remark 2: Only enter the effect for the first year it occurs. The tool automatically takes it into account for subsequent years.

@@ -227,7 +240,7 @@ export default function ClimateWithoutUpstreamStep1(){
diff --git a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep2.tsx b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep2.tsx index 5a5231b..d778707 100644 --- a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep2.tsx +++ b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep2.tsx @@ -11,6 +11,7 @@ import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import { computeVktAfterASI } from '../../utils/asiComputations' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithoutUpstreamStep2(){ const { keycloak, initialized } = useKeycloak(); @@ -149,38 +150,41 @@ export default function ClimateWithoutUpstreamStep2(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/Without/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Give the potential additional vehicle kilometers for the vehicle categories below if more vehicle kilometers are expected as a result of the planned measures for the corresponding year. -

+

Give the potential additional vehicle kilometers for the vehicle categories below if more vehicle kilometers are expected as a result of the planned measures for the corresponding year.

+

Examples of measure: Extension of the bus network (e.g. increase in frequency), expansion of railway networks or extension of service (increase in the frequency of freight trains for example) increase the number of vehicle kilometers.

-

- Examples of measure: Extension of the bus network (e.g. increase in frequency), expansion of railway networks or extension of service (increase in the frequency of freight trains for example) increase the number of vehicle kilometers. -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* BAU VKT */} + {/* Source */} + {/* Added VKT */} + {/* Climate VKT */} + - - - - - + + + + @@ -191,19 +195,25 @@ export default function ClimateWithoutUpstreamStep2(){ const addedVkt = vehicle.addedVkt?.[yearIndex] || "" return ( - - + + + : } - + ) })} - + + + + + + +
🛈 Vehicle🛈 BAU VKT (Mkm)🛈 Src🛈 Added VKT (Mkm) + VehicleBAU VKT (Mkm)SrcAdded VKT (Mkm) Computed Climate VKT using avoided VKT results as input
- VKT at the end of previous year (Mkm) + Added VKT between years (Mkm) + VKT at the end of previous year (Mkm) + Added VKT between years (Mkm)
First VKT values come from reference year data (inventory). - }>🛈 Climate VKT (Mkm)
+ }>Climate VKT (Mkm)
{vtype}{bAUVkt?.[vtype]?.[yearIndex + 1]}{vtype} {source ? configureSource(vtype)}/> - : } updateInput(vtype, yearIndex, e.target.value)}> {climateVkt[vtype]?.[yearIndex + 1] || ""}
))} diff --git a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep3.tsx b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep3.tsx index 48ca520..04341f6 100644 --- a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep3.tsx +++ b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep3.tsx @@ -10,6 +10,7 @@ import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithoutUpstreamStep2(){ const { keycloak, initialized } = useKeycloak(); @@ -138,29 +139,31 @@ export default function ClimateWithoutUpstreamStep2(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/Without/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Please adjust the vehicle load rate if it is expected to be impacted by the planned measures for the corresponding year. -

+

Please adjust the vehicle load rate if it is expected to be impacted by the planned measures for the corresponding year.

+

Examples: Decreasing the train ticket price would increase the number of passengers per train or increase in the load of freight trains due to a new software optimising the routing of trains

-

- Examples: Decreasing the train ticket price would increase the number of passengers per train or increase in the load of freight trains due to a new software optimising the routing of trains -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Inv. load */} + {/* Source */} + {/* Load */} + - - - - + + + + @@ -171,18 +174,23 @@ export default function ClimateWithoutUpstreamStep2(){ const invLoad = (project.stages.Inventory[0].steps[6] as InputInventoryStep6).vtypes[vtype].value return ( - - + + + : } ) })} - + + + + + +
🛈 Vehicle🛈 Inv. load🛈 Src🛈 Load (pass. or tons per vehicle)VehicleInv. loadSrcLoad (pass. or tons per vehicle)
{vtype}{invLoad}{vtype} {source ? configureSource(vtype)}/> - : } updateInput(vtype, yearIndex, e.target.value)}>
))} diff --git a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep4.tsx b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep4.tsx index 4a59278..a295614 100644 --- a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep4.tsx +++ b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep4.tsx @@ -12,6 +12,7 @@ import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWr import { computeVktAfterASI } from '../../utils/asiComputations' import TdDiagonalBar from '../../components/TdDiagonalBar' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithoutUpstreamStep4(){ const { keycloak, initialized } = useKeycloak(); @@ -229,51 +230,54 @@ export default function ClimateWithoutUpstreamStep4(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/Without/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- If one of the two previous parameters (vehicle kilometer or load) is impacted it is mandatory to specify the transport mode that is the source of these new trips in the table. -

+

If one of the two previous parameters (vehicle kilometer or load) is impacted it is mandatory to specify the transport mode that is the source of these new trips in the table.

+

Example: The goal transport modes categories are buses and train. Concerning the extensions of the train planned for 2020 the new users are expected to be for 50% of them previous bus users, 25% car users and 25% motorcycle users. And the users of a new bus line opening in 2025 are expected to be for 50% of them previous car users, 30% NMT users and 20% of trips will be induced.

+

Remark: Only enter the effect for the first year it occurs. The tool automatically takes it into account for subsequent years.

+

You might remember having to fill out the average trip length of each transport mode in the Base Year step. It will now be considered as a “proxy” in the calculation - meaning you use this data to estimate an other data that can’t get. The average trip length of transport modes helps the calculator understanding the weight of each transport modes in modal share

-

- Example: The goal transport modes categories are buses and train. Concerning the extensions of the train planned for 2020 the new users are expected to be for 50% of them previous bus users, 25% car users and 25% motorcycle users. And the users of a new bus line opening in 2025 are expected to be for 50% of them previous car users, 30% NMT users and 20% of trips will be induced. -

-

- Remark: Only enter the effect for the first year it occurs. The tool automatically takes it into account for subsequent years. -

-

- You might remember having to fill out the average trip length of each transport mode in the Base Year step. It will now be considered as a “proxy” in the calculation - meaning you use this data to estimate an other data that can’t get. The average trip length of transport modes helps the calculator understanding the weight of each transport modes in modal share -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( -

Passengers

+ {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+

Passengers

+ + {/* Target vehicle */} + {/* Ref. Pkm */} + {/* Pkm */} + {/* Added Pkm VKT */} + {/* Origin vehicle */} + {/* Inv. trip length */} + {/* Source */} + {/* Parts of trips to shift */} + {/* Shifted Pkm */} + - - {/* - */} - - - - - - - - + {/* + */} + + + + + + + + + }> pkm shifted to goal @@ -288,21 +292,21 @@ export default function ClimateWithoutUpstreamStep4(){ const pkmEndOfYear = Math.round((computedASI && computedASI.pkmsEndOfYear?.[yearIndex+1]?.[goalvtype]) || 0) return ( - {index === 0 && } + {index === 0 && } {/* {index === 0 && } {index === 0 && } */} - {index === 0 && } - {index === 0 && } - {index === 0 && } + {index === 0 && !isVtypeFreight(vtype)).length} cls={pkmStartOfYear < 0 ? "cellError": ""} value={pkmStartOfYear} decimals={0}>} + {index === 0 && !isVtypeFreight(vtype)).length} cls={pkmEndOfYear < 0 ? "cellError": ""} value={pkmEndOfYear} decimals={0}>} + {index === 0 && !isVtypeFreight(vtype)).length} value={computedASI ? computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype] : ""} decimals={0}>} - + {computedASI && computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype] > 0 ? + : } : } {computedASI && computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype] > 0 @@ -318,30 +322,50 @@ export default function ClimateWithoutUpstreamStep4(){ ) }) })} - + + + + + + + + + + +
🛈 Goal vehicle🛈 Vkt {project.referenceYears[yearIndex]} (Mkm)🛈 Vkt {y} (Mkm)🛈 Pkm {project.referenceYears[yearIndex]} (Mkm)🛈 Pkm {y} (Mkm)🛈 Added Pkm to distribute (Mpkm)🛈 Origin vehicle🛈 Inventory trip len (km)🛈 Src🛈 Part of trips to shift (%)Target vehicle Vkt {project.referenceYears[yearIndex]} (Mkm) Vkt {y} (Mkm){project.referenceYears[yearIndex]} Pkm (Mkm){y} Pkm (Mkm)Added Pkm to distribute (Mpkm)Origin vehicleInv. trip length (km)SrcPart of trips to shift (%) Computed pkm shifted from origin vehicle to goal vehicle
- Added Pkm to distribute (Mpkm) x Part of trips to shift (%) x Inventory trip len / SUM(Inventory Trips lengths (km) x Parts of trips to shift (%)) + Added Pkm to distribute (Mpkm) × Part of trips to shift (%) × Inv. trip length / SUM(Inv. trips lengths (km) × Parts of trips to shift (%))
Sums are done over goal vehicle - }>🛈 pkm shifted to goal
!isVtypeFreight(vtype)).length}>{goalvtype} !isVtypeFreight(vtype)).length}>{goalvtype} !isVtypeFreight(vtype)).length}>{computedASI && computedASI.baseVkt?.[goalvtype]?.[yearIndex].toFixed(0)} !isVtypeFreight(vtype)).length}>{computedASI && computedASI.baseVkt?.[goalvtype]?.[yearIndex+1].toFixed(0)} !isVtypeFreight(vtype)).length} className={pkmStartOfYear < 0 ? "cellError": ""}>{pkmStartOfYear} !isVtypeFreight(vtype)).length} className={pkmEndOfYear < 0 ? "cellError": ""}>{pkmEndOfYear} !isVtypeFreight(vtype)).length}>{computedASI && computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype]?.toFixed(0)}{originvtype === "Induced Traffic" - ? 🛈 Induced Traffic - : {originvtype} + ? Induced traffic + : {originvtype} }{invTripLen}{source ? configureSource(goalvtype, originvtype)}/> - : }
-

Freight

+

Freight

+ + {/* Target vehicle */} + {/* Ref. Pkm */} + {/* Pkm */} + {/* Added Pkm VKT */} + {/* Origin vehicle */} + {/* Source */} + {/* Parts of trips to shift */} + {/* Shifted Pkm */} + - - {/* - */} - - - - - - - + {/* + */} + + + + + + + + }>tkm shifted to goal @@ -353,17 +377,17 @@ export default function ClimateWithoutUpstreamStep4(){ const value = originvehicle.value?.[yearIndex] || "" return ( - {index === 0 && } + {index === 0 && } {/* {index === 0 && } {index === 0 && } */} - {index === 0 && } - {index === 0 && } - {index === 0 && } - + {index === 0 && isVtypeFreight(vtype)).length -1} value={computedASI ? computedASI.pkmsStartOfYear?.[yearIndex+1]?.[goalvtype] : ""} decimals={0}>} + {index === 0 && isVtypeFreight(vtype)).length -1} value={computedASI ? computedASI.pkmsEndOfYear?.[yearIndex+1]?.[goalvtype] : ""} decimals={0}>} + {index === 0 && isVtypeFreight(vtype)).length -1} value={computedASI ? computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype]: ""} decimals={0}>} + {computedASI && computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype] > 0 ? + : } : } {computedASI && computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype] > 0 @@ -379,7 +403,16 @@ export default function ClimateWithoutUpstreamStep4(){ ) }) })} - + + + + + + + + + +
🛈 Goal vehicle🛈 Vkt {project.referenceYears[yearIndex]} (Mkm)🛈 Vkt {y} (Mkm)🛈 Tkm {project.referenceYears[yearIndex]} (Mkm)🛈 Tkm {y} (Mkm)🛈 Added Tkm to distribute (Mtkm)🛈 Origin vehicle🛈 Src🛈 Part of tkm to shift (%)Target vehicle Vkt {project.referenceYears[yearIndex]} (Mkm) Vkt {y} (Mkm){project.referenceYears[yearIndex]} Tkm (Mkm){y} Tkm (Mkm)Added Tkm to distribute (Mtkm)Origin vehicleSrcPart of tkm to shift (%) Computed tkm shifted from origin vehicle to goal vehicle
- Added Tkm to distribute (Mtkm) x Part of tkm to shift (%) / SUM of parts of tkm to shift for goal vehicle (%) + Added Tkm to distribute (Mtkm) × Part of tkm to shift (%) / SUM of parts of tkm to shift for goal vehicle (%)
- }>🛈 tkm shifted to goal
isVtypeFreight(vtype)).length -1}>{goalvtype} isVtypeFreight(vtype)).length -1}>{goalvtype} isVtypeFreight(vtype)).length -1}>{computedASI && computedASI.baseVkt?.[goalvtype]?.[yearIndex].toFixed(0)} isVtypeFreight(vtype)).length -1}>{computedASI && computedASI.baseVkt?.[goalvtype]?.[yearIndex+1].toFixed(0)} isVtypeFreight(vtype)).length -1}>{computedASI && computedASI.pkmsStartOfYear?.[yearIndex+1]?.[goalvtype].toFixed(0)} isVtypeFreight(vtype)).length -1}>{computedASI && computedASI.pkmsEndOfYear?.[yearIndex+1]?.[goalvtype].toFixed(0)} isVtypeFreight(vtype)).length -1}>{computedASI && computedASI.pkmsAdded?.[yearIndex+1]?.[goalvtype]?.toFixed(0)}{originvtype}{originvtype}{source ? configureSource(goalvtype, originvtype)}/> - : }
))} diff --git a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep5.tsx b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep5.tsx index eb3a687..700e1fc 100644 --- a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep5.tsx +++ b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep5.tsx @@ -1,7 +1,7 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Table, Button, Badge, Form, Tabs, Tab, Alert} from 'react-bootstrap' +import {Table, Button, Badge, Tabs, Tab, Alert} from 'react-bootstrap' import {FuelType, InputBAUStep2, InputClimateWithoutUpstreamStep1, InputClimateWithoutUpstreamStep2, InputClimateWithoutUpstreamStep3, InputClimateWithoutUpstreamStep4, InputInventoryStep1, InputInventoryStep6, InputInventoryStep8, ProjectType, VehicleStats} from '../../frontendTypes' import ChoiceModal from '../../components/ChoiceModal' @@ -13,6 +13,7 @@ import TdDiagonalBar from '../../components/TdDiagonalBar' import PercentInput from '../../components/PercentInput' import { computeVktAfterASI } from '../../utils/asiComputations' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithoutUpstreamStep5(){ const { keycloak, initialized } = useKeycloak(); @@ -171,9 +172,6 @@ export default function ClimateWithoutUpstreamStep5(){ .then(() => navigate('/project/' + projectId + '/Climate/' + climateScenarioId + '/Without/step/' + (stepNumber + 1))); } const inputInventoryStep1 : InputInventoryStep1 = project.stages?.Inventory[0].steps?.[1] || {} - const isVtypeFreight = (vtype: string) => { - return inputInventoryStep1.vtypes[vtype].type === "freight" - } const inputClimateWithoutUpstreamStep1 : InputClimateWithoutUpstreamStep1 = project.stages?.Climate[climateScenarioId].steps?.[1] const inputClimateWithoutUpstreamStep2 : InputClimateWithoutUpstreamStep2 = project.stages?.Climate[climateScenarioId].steps?.[2] const inputClimateWithoutUpstreamStep3 : InputClimateWithoutUpstreamStep3 = project.stages?.Climate[climateScenarioId].steps?.[3] @@ -201,39 +199,43 @@ export default function ClimateWithoutUpstreamStep5(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/Without/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %. -

+

Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %.

+

Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly.

-

- Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly. -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Fuels */} + {/* Computed VKT */} + {/* BAU VKT */} + {/* Source */} + {/* VKT */} + - - - + + - - - + + + @@ -254,13 +256,13 @@ export default function ClimateWithoutUpstreamStep5(){ const percentSource = vehicle?.fuels[ftype]?.percentSource const bAUPercent = project.stages.BAU[0].steps?.[2].vtypes?.[vtype].fuels?.[ftype]?.percent[yearIndex] || "?" fuelJsx.push( - - - + + + - + - - + + - + 100 ? "cellError": ""} value={totalPercent || 0} decimals={0}>, fuelJsx ] })} - + + + + + + + +
🛈 Vehicle🛈 Fuels + VehicleFuels Vehicle kilometers travelled. Values for each fuel are computed as
- Computed VKT after shift (Mkm/y) x VKT (%) / 100 + Computed VKT after shift (Mkm/y) × VKT (%) / 100
- }>🛈 Computed VKT (Mkm/y)
+ }>Computed VKT (Mkm/y)
🛈 BAU. VKT (%)🛈 Src🛈 VKT (%)BAU. VKT (%)SrcVKT (%)
{ftype}{parseFloat((parseFloat(value) / 100 * (computedASI?.baseVkt?.[vtype]?.[yearIndex+1] || 0)).toFixed(10)) || ""}{bAUPercent}{ftype} {percentSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputPercent(vtype, ftype, yearIndex, e.target.value)} invalid={totalPercent > 100}> @@ -269,17 +271,24 @@ export default function ClimateWithoutUpstreamStep5(){ } return [
{vtype}{vtype} All{computedASI?.baseVkt?.[vtype]?.[yearIndex+1]}100 100 ? "cellError": ""}>{totalPercent || 0}
))} diff --git a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep6.tsx b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep6.tsx index a476211..9f2d9e8 100644 --- a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep6.tsx +++ b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep6.tsx @@ -10,6 +10,7 @@ import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function ClimateWithoutUpstreamStep6(){ const { keycloak, initialized } = useKeycloak(); @@ -154,36 +155,35 @@ export default function ClimateWithoutUpstreamStep6(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Climate/' + climateScenarioId + '/Without/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- Once transport activity i.e. mileage by mode and fuel is known, it needs to be multiplied with adequate fuel consumption factors. -

+

Once transport activity i.e. mileage by mode and fuel is known, it needs to be multiplied with adequate fuel consumption factors.

+

Please enter the expected average fuel/energy consumption changes - for each vehicle category and per fuel type- for the following years (average fuel/energy consumption per vehicle per 100 km).

+

If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach.

+

Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly.

-

- Please enter the expected average fuel/energy consumption changes - for each vehicle category and per fuel type- for the following years (average fuel/energy consumption per vehicle per 100 km). -

-

- If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach. -

-

- Values are pre-filled with BAU data if available; this is done to simplify the filling process. Please update values accordingly. -

- {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => ( + {project.referenceYears && project.referenceYears.slice(1).map((y, yearIndex) => (
+ + {/* Vehicle */} + {/* Fuels */} + {/* BAU cons. */} + {/* Source */} + {/* Cons. */} + - - - - - + + + + + @@ -198,13 +198,13 @@ export default function ClimateWithoutUpstreamStep6(){ const consSource = vehicle.fuels[ftype]?.consSource const bAUCons = project.stages.BAU[0].steps?.[3].vtypes?.[vtype].fuels?.[ftype]?.cons[yearIndex] || "?" fuelJsx.push( - {i===0 && } - - + {i===0 && } + + + + + + + +
🛈 Vehicle🛈 Fuels🛈 BAU. Cons🛈 Src🛈 Cons (l-kg-kwh/100km)VehicleFuelsBAU. ConsSrcCons (l-kg-kwh/100km)
{vtype}{ftype}{bAUCons}{vtype}{ftype} {consSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputCons(vtype, ftype, yearIndex, e.target.value)}> @@ -215,7 +215,13 @@ export default function ClimateWithoutUpstreamStep6(){ fuelJsx ] })} - +
))} diff --git a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep7.tsx b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep7.tsx index ea0faa0..48e2c87 100644 --- a/frontend/src/pages/Climate/ClimateWithoutUpstreamStep7.tsx +++ b/frontend/src/pages/Climate/ClimateWithoutUpstreamStep7.tsx @@ -1,7 +1,6 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Badge, Button, Dropdown} from 'react-bootstrap' import {EmissionsResults, InputInventoryStep7, ProjectType} from '../../frontendTypes' import '../Project.css' @@ -10,7 +9,6 @@ import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWr import EmissionsTable from '../../components/viz/EmissionsTable' import EmissionsBarChart from '../../components/viz/EmissionsBarChart' import EditEmissionFactors from '../../components/EditEmissionFactors' -import ChoiceModal from '../../components/ChoiceModal' import TTWorWTWSelector from '../../components/TTWorWTWSelector' export default function ClimateWithUpstreamStep7(){ @@ -107,15 +105,13 @@ export default function ClimateWithUpstreamStep7(){

Results

-

- This page displays a short summary of emissions for this climate scenario. More tables and visualisations are available in the Compare section of the project. -

+

This page displays a short summary of emissions for this climate scenario. More tables and visualisations are available in the Compare section of the project.

-

Emissions

+

Emissions

- + + <> +
+ {/*
+

Passenger Modal Share

+
+ +
+
*/} +
+
+ +
+
+
+ +
) diff --git a/frontend/src/pages/CreateProject.tsx b/frontend/src/pages/CreateProject.tsx index 8d2a0b4..21e9d86 100644 --- a/frontend/src/pages/CreateProject.tsx +++ b/frontend/src/pages/CreateProject.tsx @@ -2,14 +2,13 @@ import React, {useState, useMemo, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { Navigate, useNavigate, useParams } from 'react-router-dom' import countryList from 'react-select-country-list' -import { Button, Container, Row, Col, Form, InputGroup, OverlayTrigger, Tooltip, Badge, ButtonGroup } from 'react-bootstrap' +import { Button, Row, Col, Form, InputGroup, Badge } from 'react-bootstrap' import { Typeahead } from 'react-bootstrap-typeahead' import 'react-bootstrap-typeahead/css/Typeahead.css' import { ProjectType } from '../frontendTypes' import ChoiceModal from '../components/ChoiceModal' -import ProjectNav from '../components/ProjectNav' -export default function CreateProject() { +export default function CreateProject(props : {project: ProjectType}) { const navigate = useNavigate(); let params = useParams(); const { keycloak, initialized } = useKeycloak(); @@ -19,7 +18,6 @@ export default function CreateProject() { const [ partnerLocation, setPartnerLocation ] = useState("") const [ projectArea, setProjectArea ] = useState("") const [ projectReferenceYears, setProjectReferenceYears ] = useState(["2020","2025","2030","2035","2040","2050"]) - const [ geoData, setGeoData ] = useState("") const [ isSump, setIsSump ] = useState(true) const [validated, setValidated] = useState(false) const [ createWarning, setCreateWarning ] = useState(false) @@ -72,33 +70,19 @@ export default function CreateProject() { const countryOptions = useMemo(() => countryList().getData().filter(c => !blacklistCountries[c.label]), []) const projectId = params.projectId useEffect(() => { - if (initialized && keycloak.authenticated && projectId){ + if (initialized && keycloak.authenticated && props.project.id){ // We are not creating a new project, but editing one - const requestOptions = { - method: 'GET', - headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + keycloak.token } - }; - fetch(process.env.REACT_APP_BACKEND_API_BASE_URL + '/api/project/' + projectId, requestOptions) - .then(response => { - if (response.status !== 200) { - navigate('/') - } - return response.json() - }) - .then(data => { - console.log("get projetcs reply", data) - let loadedProject = data.project as ProjectType - setProject(loadedProject) - setProjectName(loadedProject.name) - setProjectCity([loadedProject.city]) - setProjectCountry([loadedProject.country]) - setPartnerLocation(loadedProject.partnerLocation) - setProjectArea(loadedProject.area) - setProjectReferenceYears(loadedProject.referenceYears.map(e => e.toString())) - setIsSump(loadedProject.isSump) - }); - } - }, [keycloak, initialized]) + let loadedProject = props.project + setProject(loadedProject) + setProjectName(loadedProject.name) + setProjectCity([loadedProject.city]) + setProjectCountry([loadedProject.country]) + setPartnerLocation(loadedProject.partnerLocation) + setProjectArea(loadedProject.area) + setProjectReferenceYears(loadedProject.referenceYears.map(e => e.toString())) + setIsSump(loadedProject.isSump) + } + }, [keycloak, initialized, props.project]) if (initialized && !keycloak.authenticated){ return } @@ -164,11 +148,6 @@ export default function CreateProject() { }); } } - const referenceYearTooltip = (props:any) => ( - - You can choose the year of reference based on your needs - - ); const setProjectReferenceYear = (index: number, year: string) => { setProjectReferenceYears((prevProjectReferenceYears) => { return prevProjectReferenceYears.map((e,i) => i === index ? year : e) @@ -189,120 +168,117 @@ export default function CreateProject() { return ( <> - - - -

{project.id ? projectName : "New Project"}

- {project.id && } -
-

Study

- - Study name - setProjectName(e.target.value)} isInvalid={createWarning}/> - {createWarning ? "This project name already exists" : "Please specify a project name"} - - - Plan type - setIsSump(true)} - label="Sustainable Urban Mobility Plan (SUMP)" - /> - setIsSump(false)} - label="National Urban Mobility Plan (NUMP)" - /> - - {project.createdDate && - Created - - } - {project.modifiedDate && - Modified - - } -

Area of study

- - Select country - { setProjectCountry([e])}} - onChange={o => { o.length && setProjectCountry(o)}} - options={countryOptions.map(e => e.label)} - /> - Please specify a country - - - {isSump && - Area - setProjectCity([e])} - onChange={o => { o.length && setProjectCity(o)}} - options={cityOptions[projectCountry[0]] || []} - /> - Please specify a city - } - - {/* - Geo. Data - setGeoData(e.target.value)}/> - */} - - - Partner name in city - setPartnerLocation(e.target.value)}/> - - - - Territory area (km²) - - setProjectArea(e.target.value)}/> - km² - - - -

Years of study

- - - - 🛈 Ref. year - - - - setProjectReferenceYear(0, e.target.value)} /> - Please enter a year between 1900 and 2500, avoid white spaces - - - - Proj. Year(s) - - {projectReferenceYears.map((year,i) => ( - (i>0) && {year} removeProjectReferenceYear(i)}>X - ))} - setShowProjectReferenceYearsModal(true)}>+ - - - - -
- +
+ + +

Study

+ + Study name + setProjectName(e.target.value)} isInvalid={createWarning}/> + {createWarning ? "This project name already exists" : "Please specify a project name"} + + + Plan type + setIsSump(true)} + label="Sustainable Urban Mobility Plan (SUMP)" + /> + setIsSump(false)} + label="National Urban Mobility Plan (NUMP)" + /> + + {project.createdDate && + Created + + } + {project.modifiedDate && + Modified + + } + +
+ + +

Area of study

+ + Select country + { setProjectCountry([e])}} + onChange={o => { o.length && setProjectCountry(o)}} + options={countryOptions.map(e => e.label)} + /> + Please specify a country + + {isSump && + Area + setProjectCity([e])} + onChange={o => { o.length && setProjectCity(o)}} + options={cityOptions[projectCountry[0]] || []} + /> + Please specify a city + } + {/* + Geo. Data + setGeoData(e.target.value)}/> + */} + + Partner name in city + setPartnerLocation(e.target.value)}/> + + + Territory area (km²) + + setProjectArea(e.target.value)}/> + {/* km² */} + + + +
+ + +

Years of study

+ + Reference year + + setProjectReferenceYear(0, e.target.value)} /> + Please enter a year between 1900 and 2500, avoid white spaces + + + + Projected year(s) + + {projectReferenceYears.map((year,i) => ( + (i>0) && {year} removeProjectReferenceYear(i)}> + ))} + + + + +
+ + + - +
+
- - -

Inventory / Base Year

+ + +

Inventory

", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/edit', content: "Cancel", variant: "link"}} + nextNav={{link: '/project/' + project.id + '/Inventory/step/1', content: "Start", showArrow: true, variant: "primary"}} >

The emission inventory for the transport sector is calculated using the bottom-up approach - based on transport activity. It relies on the ASIF framework, considering Activity, Structure, Intensity and Fuel.

- ASIF Framework Diagram -

The calculation of transport related emissions requires information on

+
+ ASIF Framework Diagram +
+ +

Required for this calculation

+ + {/* Data */} + {/* Unit */} + - - + + - + - + - + - +
DataUnitDataUnit
- - - 🛈 Transport activity - - + + + Transport activity + + vkt: vehicle-kilometre and tkm: ton-kilometrevkt: vehicle-kilometre and tkm: ton-kilometre
- Share of the transport activity by vehicle category and fuel type + Share of the transport activity by vehicle category and fuel type %vkt and %tkm%vkt and %tkm
- Vehicle fuel consumption according to vehicle category and fuel type + Vehicle fuel consumption according to vehicle category and fuel type l-kW-kg/100kml-kW-kg/100km
- - - 🛈 Emissions factors per fuel - - + + + Emissions factors per fuel + + kgC02/TJkgC02/TJ
-

The tool will also offer you to add [optional but recommended]

+ +

Required for later calculations

+ + {/* Data */} + {/* Unit */} + - - + + - + - + - + - +
DataUnitDataUnit
- - - 🛈 Occupation rate per transport category - - + + + Occupation rate per transport category + + passengers / load (tons)passengers / load (tons)
- - - 🛈 CO2 content of electricity and hydrogen production - - + + + CO2 content of alt. energy production + + gCO2/kWh or gCO2/kggCO2/kWh or gCO2/kg
- - - 🛈 Energy sales in the territory - - + + + Energy sales in the territory + + TOE: tons of oil equivalentTOE: tons of oil equivalent
- - - 🛈 Trip length per transport category - - + + + Trip length per transport category + + kmkm
- +
+
+ + + +
+ + + +
+ ) } diff --git a/frontend/src/pages/Inventory/InventoryStep1.tsx b/frontend/src/pages/Inventory/InventoryStep1.tsx index 5247e26..ed10c61 100644 --- a/frontend/src/pages/Inventory/InventoryStep1.tsx +++ b/frontend/src/pages/Inventory/InventoryStep1.tsx @@ -1,7 +1,7 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Table, Button, Badge, Modal, Alert, Stack} from 'react-bootstrap' +import {Table, Button, Badge, Modal, Alert} from 'react-bootstrap' import {FuelType, InputInventoryStep1, ProjectType} from '../../frontendTypes' import ChoiceModal from '../../components/ChoiceModal' @@ -245,55 +245,57 @@ export default function InventoryStep1(){

Vehicles and fuels in use

{error && {error}} ", variant: "primary"}} - seeMoreCallBack={()=>setShowInfo(true)} + prevNav={{link: '/project/' + project.id + '/Inventory/intro', content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} + >

Please define all combinations of vehicle and fuel types to be used in the following steps to calculate GHG emissions. You also need to specify :

+
    +
  • if they are using the road or rail network
  • +
  • if they are used for public transport, private transport or freight (if one transport mode is used for multiple, create two categories with unique names)
  • +
  • the fuel types that are used
  • +
+

You can also add a custom vehicle category below, if you don’t find it in the list. For each category, you will later need to fill the following information: Total vkt, Vehicle occupancy, Fuel consumptions, Vkt breakdown per fuel.

-
    -
  • if they are using the road or rail network
  • -
  • if they are used for public transport, private transport or freight (if one transport mode is used for multiple, create two categories with unique names)
  • -
  • the fuel types that are used
  • -
- -

You can also add a custom vehicle category below, if you don’t find it in the list. For each category, you will later need to fill the following information: Total vkt, Vehicle occupancy, Fuel consumptions, Vkt breakdown per fuel.

+ + {/* Vehicle */} + {/* Network */} + {/* Type */} + {/* Fuels */} + - - - - + + + + {Object.keys(inputData.vtypes).map((vtype, index) => { const vehicle = inputData.vtypes[vtype] - let networkBadge = networkTrigger(vtype)}>{vehicle.network} v - let typeBadge = typeTrigger(vtype)}>{vehicle.type} v + let networkBadge = networkTrigger(vtype)}>{vehicle.network} v + let typeBadge = typeTrigger(vtype)}>{vehicle.type} v if (defaultVehiclesParams[vtype]) { - networkBadge = {vehicle.network} - typeBadge = {vehicle.type} + networkBadge = {vehicle.network} + typeBadge = {vehicle.type} } return ( - + ) })} - + @@ -332,12 +334,12 @@ export default function InventoryStep1(){ Default categories of transport - + {Object.keys(defaultVehiclesParams).map((vtype, index) =>

{vtype} : {defaultVehiclesParams[vtype].desc}

)}
diff --git a/frontend/src/pages/Inventory/InventoryStep2.tsx b/frontend/src/pages/Inventory/InventoryStep2.tsx index afeb7ce..6e373db 100644 --- a/frontend/src/pages/Inventory/InventoryStep2.tsx +++ b/frontend/src/pages/Inventory/InventoryStep2.tsx @@ -12,6 +12,7 @@ import TdDiagonalBar from '../../components/TdDiagonalBar' import PercentInput from '../../components/PercentInput' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function InventoryStep2(){ const { keycloak, initialized } = useKeycloak(); @@ -102,7 +103,7 @@ export default function InventoryStep2(){ let tmp = {...prevInputData} let vkt = "" if (fleetStock) { - vkt = (parseFloat(tmp.vtypes[vtype].fleetMileage) * parseFloat(fleetStock)).toString() + vkt = (parseFloat(tmp.vtypes[vtype].fleetMileage) * parseFloat(fleetStock) / 1000000).toString() } tmp.vtypes[vtype].vkt = vkt tmp.vtypes[vtype].fleetStock = fleetStock @@ -201,18 +202,37 @@ export default function InventoryStep2(){ } const ApproachSelector = () => { return ( -
- is computed using the - computationApproach === "vkt" ? setComputationApproach("fleet") : setComputationApproach("vkt")}> - - {computationApproach === "vkt" ? "Vkt approach" : "Fleet approach"} - - - - {computationApproach === "fleet" ? "Vkt approach" : "Fleet approach"} - - - +
+
+ + {/* setShowComputationApproach(true)} href="">Vehicle mileage */} + is computed using the + computationApproach === "vkt" ? setComputationApproach("fleet") : setComputationApproach("vkt")}> + + {computationApproach === "vkt" ? "Vkt approach" : "Fleet approach"} + + + + {computationApproach === "fleet" ? "Vkt approach" : "Fleet approach"} + + + + . +
) } @@ -223,42 +243,62 @@ export default function InventoryStep2(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Inventory/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} seeMoreCallBack={()=>setShowInfo(true)} >

Mileage is the cornerstorne of the calculation of transport GHG emissions. Once the total vehicle mileage per vehicle category is known, it must be subdivided by fuel type e.g.the share of diesel car on the car category’s total mileage.

+

+ Please enter the vehicle kilometers travelled (Mio km) for the reference year. The total vkt should comply with the actual transport activity within the city or country territory. +

+

+ Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %. +

-

- Please enter the vehicle kilometers travelled (Mio km) for the reference year. The total vkt should comply with the actual transport activity within the city or country territory. -

-

- Please also enter the percentage of vehicle kilometers travelled (vkt) per fuel type. The sum of fuel shares in each vehicle category must be 100 %. -

🛈 Vehicle🛈 Network🛈 Type🛈 FuelsVehicleNetworkTypeFuels
removeVehicle(vtype)}>{vtype} XremoveVehicle(vtype)}>{vtype} {networkBadge} {typeBadge} - - {Object.keys(vehicle.fuels).map((ftype, index) => ( - removeFuel(vtype, ftype as FuelType)}>{ftype} X - ))} - - + {Object.keys(vehicle.fuels).map((ftype, index) => ( + removeFuel(vtype, ftype as FuelType)}>{ftype} + ))} + +
- -
+ {computationApproach === "fleet" + ? + {/* Vehicule */} + {/* Fuels */} + {/* Src */} + {/* Vehicule stock */} + {/* Avg mileage */} + {/* Computed VKT */} + {/* Src */} + {/* VKT% */} + + : + {/* Vehicule */} + {/* Fuels */} + {/* Src */} + {/* VKT Input */} + {/* Src */} + {/* VKT% */} + + } - - + + {computationApproach === "fleet" - ? - : + ? + : } - {computationApproach === "fleet" && } - {computationApproach === "fleet" && } - } + {computationApproach === "fleet" && } + - - + + @@ -283,13 +323,13 @@ export default function InventoryStep2(){ const percentSource = vehicle?.fuels[ftype]?.percentSource fuelJsx.push( - + - + - + {computationApproach === "fleet" && } {computationApproach === "fleet" && } {computationApproach === "vkt" && } - {computationApproach === "fleet" && } + {computationApproach === "fleet" && } , fuelJsx ] })} - + + + + + + + +
🛈 Vehicle🛈 FuelsVehicleFuels🛈 Src🛈 SrcSrcSrc🛈 Vehicle stock🛈 Avg mileage (km) + {computationApproach === "fleet" && Vehicle stockAvg mileage (km) Vehicle kilometers travelled. Values for each fuel are computed as -
Input VKT per vehicle (Mkm/y) x VKT (%) / 100
+
Input VKT per vehicle (Mkm/y) × VKT (%) / 100
Set VKT to zero for vehicles that are not yet used in reference year. - }>🛈 VKT (Mkm/y)
+ }>VKT (Mkm/y)
🛈 Src🛈 VKT (%)SrcVKT (%)
{ftype}{ftype} {parseFloat((parseFloat(value) / 100 * parseFloat(vkt)).toFixed(10)) || ""} {percentSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputPercent(vtype, ftype, e.target.value)} invalid={totalPercent > 100}> @@ -298,24 +338,31 @@ export default function InventoryStep2(){ } return [
{vtype}{vtype} All {vktSource ? configureSource(vtype)}/> - : } + : } updateInputFleetStock(vtype, e.target.value)}> updateInputFleetMileage(vtype, e.target.value)}> updateInputVkt(vtype, e.target.value)}>{vkt} 100 ? "cellError": ""}>{totalPercent || 0}
@@ -329,14 +376,14 @@ export default function InventoryStep2(){ Transport activity information - +

The composition of a city-specific vehicle fleet strongly influences local transport emissions. The more private cars are on the road and the larger or older the vehicles are, the higher their fuel consumption is and the higher the related GHG emissions are. In other words, GHG emissions depend on the vehicle fleet and on the distribution of VKT across the fleet's vehicle mix.

Data on the vehicle fleet is generally available from vehicle registration statistics for passenger cars, taxis, trucks, and motorcycles (e-bikes are mostly excluded), which includes technical specifications for the different vehicle types. Once the registered fleet is documented for the base year, e.g. 2015, only newly registered (and deregistered) vehicles have to be monitored each year.

If there are no big differences in the fleet compositions across different cities in a country, using national averages for urban fleet composition may be considered. Where the fleet is known to be quite specific, however, these local characteristics should be accounted for, e.g. prosperous metropolitan areas may have a larger number of new and larger cars than less prosperous mid-sized cities with a smaller but older fleet.

@@ -344,13 +391,13 @@ export default function InventoryStep2(){ There are two possible approaches to calculate vehicle mileage in the tool - +

VKT approach : The first possible methodology to calculate vehicle mileage is called vehicle kilometre approach. Data needed for this approach can be provided by a transport planning model or derived from traffic counts. For rail transport with freight and passenger trains, vehicle Stock and average annual mileage (or total mileage) may be available from the national rail operator.

Fleet approach : The so-called fleet approach is based on activity of vehicles. The number of registered vehicles in the urban area is multiplied with the average annual mileage per vehicle category.

diff --git a/frontend/src/pages/Inventory/InventoryStep3.tsx b/frontend/src/pages/Inventory/InventoryStep3.tsx index 651bfd4..34c1014 100644 --- a/frontend/src/pages/Inventory/InventoryStep3.tsx +++ b/frontend/src/pages/Inventory/InventoryStep3.tsx @@ -152,26 +152,32 @@ export default function InventoryStep3(){ {error && {error}} {sourceWarning && Warning: At least one source is missing. Please add missing sources below or click the Next button again to ignore this warning.} ", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Inventory/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} >

Once transport activity i.e. mileage by mode and fuel is known, it needs to be multiplied with adequate fuel consumption factors.

+

+ Please enter the expected average fuel/energy consumption changes for each vehicle category and per fuel type (average fuel/energy consumption per vehicle per 100 km). +

+

+ If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach. +

-

- Please enter the expected average fuel/energy consumption changes for each vehicle category and per fuel type (average fuel/energy consumption per vehicle per 100 km). -

-

- If there are no big differences in the fleet compositions across different cities within the country, using national averages for urban fleet composition is a possible approach. -

+ + {/* Vehicle */} + {/* Fuels */} + {/* Src */} + {/* Cons */} + - - - - + + + + @@ -185,12 +191,12 @@ export default function InventoryStep3(){ const cons = vehicle.fuels[ftype]?.cons || '' const consSource = vehicle.fuels[ftype]?.consSource fuelJsx.push( - {i===0 && } - + {i===0 && } + + + + + +
🛈 Vehicle🛈 Fuels🛈 Src🛈 Cons (l-kg-kwh/100km)VehicleFuelsSrcCons (l-kg-kwh/100km)
{vtype}{ftype}{vtype}{ftype} {consSource ? configureSource(vtype, ftype)}/> - : } + : } updateInputCons(vtype, ftype, e.target.value)}> @@ -201,7 +207,12 @@ export default function InventoryStep3(){ fuelJsx ] })} - +
diff --git a/frontend/src/pages/Inventory/InventoryStep4.tsx b/frontend/src/pages/Inventory/InventoryStep4.tsx index 227ba58..a112033 100644 --- a/frontend/src/pages/Inventory/InventoryStep4.tsx +++ b/frontend/src/pages/Inventory/InventoryStep4.tsx @@ -102,28 +102,33 @@ export default function InventoryStep4(){ return ( <> -

CO2 content of alternative energy production

+

CO2 content of alt. energy production

", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Inventory/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} >

In MobiliseYourCity methodology, transport related GHG emissions can integrate or not the CO2 content of the production of electricity and hydrogen (based on national/local energy mix).

+

+ If you have this information, it will allow you to choose later between a TTW and a WTW approach for emissions calculation. +

+

+ Please enter the CO2 content of electricity and hydrogen production, or skip to next step. +

-

- If you have this information, it will allow you to choose later between a TTW and a WTW approach for emissions calculation. -

-

- Please enter the CO2 content of electricity and hydrogen production, or skip to next step. -

-

Electricity

+

Electricity

+ + {/* Network */} + {/* Src */} + {/* Emissions Input */} + - - - + + + @@ -131,25 +136,35 @@ export default function InventoryStep4(){ const network = networkString as "road" | "rail" const source = inputData.electricity?.[network].source return ( - + )})} + + + + +
🛈 Network🛈 Src🛈 Emissions (gCO2/kWh)NetworkSrcEmissions (gCO2/kWh)
{network}{network} {source ? configureSource("electricity", network)}/> - : } + : } updateInput("electricity", network, e.target.value)}>
-

Hydrogen

+

Hydrogen

+ + {/* Network */} + {/* Src */} + {/* Emissions Input */} + - - - + + + @@ -157,16 +172,21 @@ export default function InventoryStep4(){ const network = networkString as "road" | "rail" const source = inputData.hydrogen?.[network].source return ( - + )})} + + + + +
🛈 Network🛈 Src🛈 Emissions (gCO2/kg)NetworkSrcEmissions (gCO2/kg)
{network}{network} {source ? configureSource("hydrogen", network)}/> - : } + : } updateInput("hydrogen", network, e.target.value)}>
diff --git a/frontend/src/pages/Inventory/InventoryStep5.tsx b/frontend/src/pages/Inventory/InventoryStep5.tsx index a15e06d..81dd5cf 100644 --- a/frontend/src/pages/Inventory/InventoryStep5.tsx +++ b/frontend/src/pages/Inventory/InventoryStep5.tsx @@ -10,7 +10,7 @@ import DescAndNav from '../../components/DescAndNav' import ValidSource from '../../components/ValidSource' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' import ItemWithOverlay from '../../components/ItemWithOverlay' -import EditEmissionFactors from '../../components/EditEmissionFactors' +import OutputNumberTd from '../../components/OutputNumberTd' export default function InventoryStep5(){ const { keycloak, initialized } = useKeycloak(); @@ -130,57 +130,65 @@ export default function InventoryStep5(){ return ( <> -

Top down validation

+

Top-down validation

", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Inventory/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} >

- The top down calculation is a well-known validation approach based on the on a given territory to evaluate your bottom-up results. + The top-down calculation is a well-known validation approach based on the on a given territory to evaluate your bottom-up results. +

+

+ It is particularly adapted for NUMPs, since it is easier to get the energy balance data at a national scale - but you can also use it if you have the local data. +

+

+ Differences within a range of +/- 10% are quitte common and should not be considered as error but as uncertainty. Check out the for uncertainty, for both calculation approach.

-

- It is particularly adapted for NUMPs, since it is easier to get the energy balance data at a national scale - but you can also use it if you have the local data. -

-

- Differences within a range of +/- 10% are quitte common and should not be considered as error but as uncertainty. Check out the for uncertainty, for both calculation approach. -

-

Energy balance

+

Energy balance

+ + {/* Network */} + {/* Fuels */} + {/* Src */} + {/* Energy balance */} + {/* Calculated */} + {/* Gap */} + - - - - + + + - - @@ -197,19 +205,17 @@ export default function InventoryStep5(){ const value = inputData.energy?.[networkName]?.fuels?.[ftype]?.value || '' const source = inputData.energy?.[networkName]?.fuels?.[ftype]?.source || '' fuelJsx.push( - {i===0 && } - + {i===0 && } + - + ) } @@ -217,45 +223,60 @@ export default function InventoryStep5(){ fuelJsx ] })} - + + + + + + + +
🛈 Network🛈 Fuels🛈 Src + NetworkFuelsSrc - 🛈 Energy balance (1000 TOE) + Energy balance (1000 TOE) + Energy balance (1000 Tons of oil equivalent) computed by the tool, using previous steps inputs. Values for each fuel are computed as
- Fuel lower heating value (TJ/1000t) / 10^6 x Fuel density (kg/kg or kg/l) x Input VKT per fuel (Mkm) x 10^6 x Fuel consumption factor (l-kg-kwh/100km) / 100 / TOE factor (0.041868 TJ) / 1000 + Fuel lower heating value (TJ/1000t) / 10^6 × Fuel density (kg/kg or kg/l) × Input VKT per fuel (Mkm) × 10^6 × Fuel consumption factor (l-kg-kwh/100km) / 100 / TOE factor (0.041868 TJ) / 1000
Lower heating value and fuel density use default values that can be edited at a later step. }> - 🛈 Calculated (1000 TOE) + Calculated (1000 TOE)
+ Difference between energy balance coming from energy sales and calculation.
- Calculated x 100 / Energy balance - 100 + Calculated × 100 / Energy balance - 100
Differences within a range of +/- 10% are quitte common and should not be considered as error but as uncertainty. }> - 🛈 Gap (%) + Gap (%)
{networkName}{ftype}{networkName}{ftype} {source ? configureSource("energy", networkName, ftype)}/> - : } + : } updateInput("energy", networkName, ftype, e.target.value)}> - {computedValue} - {Math.round(computedValue * 100 / parseFloat(value) - 100) || '0'}%
-

Emissions

+

Emissions

+ + {/* Network */} + {/* Fuels */} + {/* Src */} + {/* Emissions */} + {/* Calculated */} + {/* Gap */} + - - - - + + + - - @@ -272,19 +293,17 @@ export default function InventoryStep5(){ const value = inputData.emissions?.[networkName]?.fuels[ftype]?.value || '' const source = inputData.emissions?.[networkName]?.fuels[ftype]?.source fuelJsx.push( - {i===0 && } - + {i===0 && } + - + ) } @@ -292,7 +311,14 @@ export default function InventoryStep5(){ fuelJsx ] })} - + + + + + + + +
🛈 Network🛈 Fuels🛈 Src + NetworkFuelsSrc - 🛈 Emissions (1000t GHG) + Emissions (1000t GHG) + Emissions (1000 tons of greenhouse gases) computed by the tool, using previous steps inputs. Values for each fuel are computed as
- Fuel lower heating value (TJ/1000t) / 10^6 x Fuel density (kg/kg or kg/l) x Input VKT per fuel (Mkm) x 10^6 x Fuel consumption factor (l-kg-kwh/100km) / 100 x Fuel emission factor (kg/TJ) / 10^6 + Fuel lower heating value (TJ/1000t) / 10^6 × Fuel density (kg/kg or kg/l) × Input VKT per fuel (Mkm) × 10^6 × Fuel consumption factor (l-kg-kwh/100km) / 100 × Fuel emission factor (kg/TJ) / 10^6
Lower heating value, fuel density and fuel emission factors use default values that can be edited at a later step. }> - 🛈 Calculated (1000t GHG) + Calculated (1000t GHG)
+ Difference between emissions comming from energy sales and calculation.
- Calculated x 100 / Emissions - 100 + Calculated × 100 / Emissions - 100
Differences within a range of +/- 10% are quitte common and should not be considered as error but as uncertainty. }> - 🛈 Gap (%) + Gap (%)
{networkName}{ftype}{networkName}{ftype} {source ? configureSource("emissions", networkName, ftype)}/> - : } + : } updateInput("emissions", networkName, ftype, e.target.value)}> - {computedValue} - {Math.round(parseFloat(computedValue) * 100 / parseFloat(value) - 100) || '0'}%
@@ -306,12 +332,12 @@ export default function InventoryStep5(){ Energy sales - +

Emissions are calculated using a top-down approach based on statistics on fuel sales in the city. This approach only allows for a rough estimation since a purely sales-based approach does not provide any information on how much of the purchased fuel is actually used within the city. It also does not provide data on the actual transport activities that are related to the city, or their causes - information which is necessary for transport planning. Using energy sales data alone does not adequately monitor the effects of SUMPS, but it can be used to cross-check bottom-up calculations.

@@ -319,7 +345,7 @@ export default function InventoryStep5(){ Uncertainty reasons - +

Energy balance data are based on the total fuel sales within the country. According to the IPCC guidelines 2006, the final energy consumption for the GHG inventory should be calculated as follow: production + import - export - international bunkers - stock change.

One reason why the calculations would be different can be for example, that the energy balance does not include fuels bought in neighboring countries and consumed within the country. The emission inventory report should try explaining gaps and analyze the possibility to minimize the related uncertainties.

@@ -337,7 +363,7 @@ export default function InventoryStep5(){
diff --git a/frontend/src/pages/Inventory/InventoryStep6.tsx b/frontend/src/pages/Inventory/InventoryStep6.tsx index 0e0a6b5..3db67ea 100644 --- a/frontend/src/pages/Inventory/InventoryStep6.tsx +++ b/frontend/src/pages/Inventory/InventoryStep6.tsx @@ -114,25 +114,30 @@ export default function InventoryStep6(){

Vehicles load

", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Inventory/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} >

The vehicles load isn’t involved in the GHG emissions calculation but is useful to obtain passenger.km out of vehicle.km. It will allow you to compare your GHG emissions with the modal share in your territory on that step of the calculation process. That way you can understand how much GHG emissions represents one transport mode, but also how many passengers or tons it actually represents. It will be used in the Climate Scenario as well.

+

+ Conventionally drivers of public transport are not included as there are not passengers (incl. taxi), but for private transport drivers should be included if they travel for their own sake. +

+

+ Local city data can be used if available and robust. Otherwise for cities it is recommended to use national data and for countries to use regional specific data. +

-

- Conventionally drivers of public transport are not included as there are not passengers (incl. taxi), but for private transport drivers should be included if they travel for their own sake. -

-

- Local city data can be used if available and robust. Otherwise for cities it is recommended to use national data and for countries to use regional specific data. -

+ + {/* Vehicle */} + {/* Src */} + {/* Load */} + - - - + + + @@ -140,18 +145,22 @@ export default function InventoryStep6(){ const vehicle = inputData.vtypes[vtype] return ( - + ) })} - + + + + +
🛈 Vehicle🛈 Src🛈 Load (pass. or tons per vehicle)VehicleSrcLoad (pass. or tons per vehicle)
{vtype}{vtype} {vehicle.source ? configureSource(vtype)}/> - : } + : } updateInput(vtype, e.target.value)}>
diff --git a/frontend/src/pages/Inventory/InventoryStep7.tsx b/frontend/src/pages/Inventory/InventoryStep7.tsx index a7fa648..ae5ab6b 100644 --- a/frontend/src/pages/Inventory/InventoryStep7.tsx +++ b/frontend/src/pages/Inventory/InventoryStep7.tsx @@ -1,17 +1,18 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import {Table, Button, Badge} from 'react-bootstrap' -import {InputInventoryStep1, InputInventoryStep7, FuelType, ProjectType, TotalEnergyAndEmissions, ModalShare, EmissionParams} from '../../frontendTypes' +import {Table, Badge} from 'react-bootstrap' +import {InputInventoryStep7, FuelType, ProjectType, TotalEnergyAndEmissions, ModalShare} from '../../frontendTypes' import '../Project.css' import DescAndNav from '../../components/DescAndNav' import ProjectStepContainerWrapper from '../../components/ProjectStepContainerWrapper' -import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts' +import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip, Legend } from 'recharts' import EditEmissionFactors from '../../components/EditEmissionFactors' import TdDiagonalBar from '../../components/TdDiagonalBar' import TTWorWTWSelector from '../../components/TTWorWTWSelector' import ItemWithOverlay from '../../components/ItemWithOverlay' +import OutputNumberTd from '../../components/OutputNumberTd' export default function InventoryStep7(){ const { keycloak, initialized } = useKeycloak(); @@ -21,7 +22,6 @@ export default function InventoryStep7(){ const [emissionFactorsInputData, setEmissionFactorsInputData] = useState({} as InputInventoryStep7) const projectId = params.projectId const [ totalEnergyAndEmissions, setTotalEnergyAndEmissions] = useState({TTW: {} as TotalEnergyAndEmissions, WTW: {} as TotalEnergyAndEmissions}) - const [ emissionFactorsWTWComputedForElectric, setEmissionFactorsWTWComputedForElectric] = useState({ElectricRail: {} as EmissionParams, ElectricRoad: {} as EmissionParams}) const [ modalShare, setModalShare] = useState({ passengers: {} as ModalShare, freight: {} as ModalShare @@ -67,7 +67,6 @@ export default function InventoryStep7(){ WTW: data.totalEnergyAndEmissionsWTW, TTW: data.totalEnergyAndEmissionsTTW }) - setEmissionFactorsWTWComputedForElectric(data.emissionFactorsWTWComputedForElectric) setModalShare(data.modalShare) }) } @@ -128,32 +127,36 @@ export default function InventoryStep7(){

Results

", variant: "primary"}} + prevNav={{link: '/project/' + project.id + '/Inventory/step/' + (stepNumber - 1), content: "Prev", showArrow: true, variant: "secondary"}} + nextNav={{trigger: nextTrigger, content: "Next", showArrow: true, variant: "primary"}} > -

- This page displays a short summary of emissions for the reference year. More tables and visualisations are available in the Compare section of the project. -

+

This page displays a short summary of emissions for the reference year. More tables and visualisations are available in the Compare section of the project.

-

GHG emissions

+

GHG emissions

+ + {/* Vehicle */} + {/* Fuels */} + {/* Emissions Factor */} + {/* GHG Emissions */} + - - - - + + + @@ -168,36 +171,54 @@ export default function InventoryStep7(){ const co2 = fuels[ftype]?.co2 || '' let ges = emissionFactorsInputData.emissionFactors[ttwOrWtw][ftype].ges fuelJsx.push( - {i===0 && } - + {i===0 && } + {ftype !== "Electric" && ftype !== "Hydrogen" - ? + ? : } - + ) } return [ fuelJsx ] })} + + + + + +
🛈 Vehicle🛈 Fuels🛈 Emission Factor (kg/TJ) ({ttwOrWtw}) + VehicleFuelsEmission Factor (kg/TJ) ({ttwOrWtw}) Emissions (1000 tons of greenhouse gases) computed by the tool, using previous steps inputs. Values for each transport mode and fuel are computed as
- Fuel lower heating value (TJ/1000t) / 10^6 x Fuel density (kg/kg or kg/l) x Input VKT per fuel (Mkm) x 10^6 x Fuel consumption factor (l-kg-kwh/100km) / 100 x Fuel emission factor (kg/TJ) / 10^6 + Fuel lower heating value (TJ/1000t) / 10^6 × Fuel density (kg/kg or kg/l) × Input VKT per fuel (Mkm) × 10^6 × Fuel consumption factor (l-kg-kwh/100km) / 100 × Fuel emission factor (kg/TJ) / 10^6
Lower heating value, fuel density and fuel emission factors use default values that can be edited using the Edit GHG emission factors link below the pie chart. }> - 🛈 GHG emissions (1000t GHG) ({ttwOrWtw}) + GHG em. (1000t GHG - {ttwOrWtw})
{vtype}{ftype}{vtype}{ftype}{ges}{co2}
- - - entry.name + ": " + Math.round(entry.value) + " 1000t GHG"} - > - {emissionsPieData.map((entry, index) => ())} - - - - + <> +
+ {/*
+ +
*/} +
+
+ + + entry.name + ": " + Math.round(entry.value) + " 1000t GHG"} + > + {emissionsPieData.map((entry, index) => ())} + + + + + +
+
+
+ -

Passenger Modal Share

- - - ({name: vtype, value: Math.round(modalShare.passengers[vtype][0] * 100)}))} - cx="50%" - cy="50%" - outerRadius={80} - label={(entry) => entry.name + ": " + entry.value + "%"} - > - {Object.keys(modalShare.passengers || {}).map((vtype, index) => ())} - - - - -

Freight Modal Share

- - - ({name: vtype, value: Math.round(modalShare.freight[vtype][0] * 100)}))} - cx="50%" - cy="50%" - outerRadius={80} - label={(entry) => entry.name + ": " + entry.value + "%"} - > - {Object.keys(modalShare.freight || {}).map((vtype, index) => ())} - - - - + +
+
+

Passenger Modal Share

+
+ {/* */} +
+
+
+
+ + + ({name: vtype, value: Math.round(modalShare.passengers[vtype][0] * 100)}))} + cx="50%" + cy="50%" + outerRadius={80} + // label={(entry) => entry.name + ": " + entry.value + "%"} + > + {Object.keys(modalShare.passengers || {}).map((vtype, index) => ())} + + + + + +
+
+
+ +
+
+

Freight Modal Share

+
+ {/* */} +
+
+
+
+ + + ({name: vtype, value: Math.round(modalShare.freight[vtype][0] * 100)}))} + cx="50%" + cy="50%" + outerRadius={80} + // label={(entry) => entry.name + ": " + entry.value + "%"} + > + {Object.keys(modalShare.freight || {}).map((vtype, index) => ())} + + + + + +
+
+
+
diff --git a/frontend/src/pages/Inventory/InventoryStep8.tsx b/frontend/src/pages/Inventory/InventoryStep8.tsx index 6eb43fe..1d19f72 100644 --- a/frontend/src/pages/Inventory/InventoryStep8.tsx +++ b/frontend/src/pages/Inventory/InventoryStep8.tsx @@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" import {Table, Button, Badge, Form} from 'react-bootstrap' -import {FuelType, InputInventoryStep8, ProjectType} from '../../frontendTypes' +import {InputInventoryStep8, ProjectType} from '../../frontendTypes' import ChoiceModal from '../../components/ChoiceModal' import '../Project.css' @@ -115,7 +115,7 @@ export default function InventoryStep8(){

Vehicle trip length

@@ -123,33 +123,43 @@ export default function InventoryStep8(){ It is used to weight the modal shift effect of public transport in the Climate Scenario and it will then be considered constant during the whole MobiliseYourCity emissions calculation process. Go to modal shift in the Climate Scenario to learn more about it.

+

+ It is asked to fill it here because the data should be collected during the diagnostic process of collecting data. +

-

- It is asked to fill it here because the data should be collected during the diagnostic process of collecting data. -

+ + {/* Passenger transport */} + {/* Src */} + {/* Avg trip len */} + - - - + + + {Object.keys(inputData.vtypes).map((vtype, index) => { const vehicle = inputData.vtypes[vtype] return ( - + ) })} + + + + +
🛈 Passenger transport🛈 SrcAverage trip length (km)Passenger transportSrcAverage trip length (km)
{vtype}{vtype} {vehicle.source ? configureSource(vtype)}/> - : } + : } updateInput(vtype, e.target.value)}>
diff --git a/frontend/src/pages/Project.css b/frontend/src/pages/Project.css index 2378acb..a381550 100644 --- a/frontend/src/pages/Project.css +++ b/frontend/src/pages/Project.css @@ -46,13 +46,13 @@ border-color: initial; } -h1 { +/* h1 { margin-bottom: 40px !important; -} +} */ -.projectStepContainer { +/* .projectStepContainer { padding: 30px; -} +} */ .projectStepContainer .input-group-text { background-color: transparent !important; @@ -77,23 +77,14 @@ h1 { cursor: pointer; } -.recharts-legend-item-text { +/* .recharts-legend-item-text { color: black !important -} -.reqStar:after { - content:"*"; - color:red; -} +} */ + .form-switch { white-space: nowrap; } h2 .badge { font-size: 12px; vertical-align: middle; -} -.stickyOptions { - position: sticky !important; - top: 10px; - z-index: 1; - border: 1px solid gray !important; -} +} \ No newline at end of file diff --git a/frontend/src/pages/Project.tsx b/frontend/src/pages/Project.tsx new file mode 100644 index 0000000..656a89a --- /dev/null +++ b/frontend/src/pages/Project.tsx @@ -0,0 +1,71 @@ +import React, {useState, useEffect} from 'react' +import { useKeycloak } from "@react-keycloak/web" +import { useParams, useNavigate } from "react-router-dom" +import { Container, Row, Col } from 'react-bootstrap' +import Footer from "../components/Footer" + +import './Project.css' +import CreateProject from './CreateProject' +import { ProjectType } from '../frontendTypes' +import ProjectSummary from './ProjectSummary' +import ProjectCompare from './ProjectCompare' +import ProjectNav from '../components/ProjectNav' + +export default function Project({page}:{page:"config" | "edit" | "viz"}){ + const { keycloak, initialized } = useKeycloak() + const navigate = useNavigate() + const params = useParams() + const [project, setProject ] = useState({} as ProjectType) + const projectId = params.projectId + + useEffect(() => { + if (initialized && keycloak.authenticated && projectId){ + const requestOptions = { + method: 'GET', + headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + keycloak.token } + }; + fetch(process.env.REACT_APP_BACKEND_API_BASE_URL + '/api/project/' + projectId, requestOptions) + .then(response => { + if (response.status !== 200) { + navigate('/') + } + return response.json() + }) + .then(data => { + console.log("get projetcs reply", data) + setProject(data.project) + }); + } + }, [keycloak, initialized, projectId, navigate]) + + return ( + <> +
+ + + +

{project.name || "New Project"}

+ +
+ + + {project.id && } + {page === "config" && } + {page === "edit" && } + {page === "viz" && } + + +
+
+
+ + + +
+ + + +
+ + ) +} diff --git a/frontend/src/pages/ProjectCompare.tsx b/frontend/src/pages/ProjectCompare.tsx index b28c619..4d134d5 100644 --- a/frontend/src/pages/ProjectCompare.tsx +++ b/frontend/src/pages/ProjectCompare.tsx @@ -1,12 +1,10 @@ import React, {useState, useEffect, SetStateAction} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import { Button, Container, Row, Col, Card, Table, Badge, Form } from 'react-bootstrap' -import {ProjectType, TotalEnergyAndEmissions, EmissionsResults, InputStep2, InputInventoryStep1} from '../frontendTypes' -import ProjectNav from '../components/ProjectNav' +import { Button, Row, Col, Table, Badge, Form, Modal } from 'react-bootstrap' +import {ProjectType, EmissionsResults, InputInventoryStep1} from '../frontendTypes' import './Project.css' -import EmissionsTable from '../components/viz/EmissionsTable' import EmissionsCompareBarChart from '../components/viz/EmissionsCompareBarChart' import VktCompareBarChart from '../components/viz/VktCompareBarChart' import ModalShareCompareBarChart from '../components/viz/ModalShareCompareBarChart' @@ -15,12 +13,11 @@ import EmissionsPerUkmCompareBarChart from '../components/viz/EmissionsPerUkmCom import { inputsAsCsv } from '../utils/inputsAsCsv' import { CSVLink } from 'react-csv' -export default function ProjectCompare(){ +export default function ProjectCompare(props: {project: ProjectType}){ const { keycloak, initialized } = useKeycloak() const navigate = useNavigate() const params = useParams() const [project, setProject ] = useState({} as ProjectType) - const [inventoryTotalEnergyAndEmissions, setInventoryTotalEnergyAndEmissions] = useState({TTW: {} as TotalEnergyAndEmissions, WTW: {} as TotalEnergyAndEmissions}) const [bauResults, setBAUResults] = useState({} as EmissionsResults) const [climateResults, setClimateResults] = useState([]) const [displayedVtypes, setDisplayedVtypes] = useState({} as {[key: string]: boolean}) @@ -29,33 +26,20 @@ export default function ProjectCompare(){ const [showPercents, setShowPercents] = useState(false) const [showLabels, setShowLabels] = useState(false) const [highContrastColors, setHighContrastColors] = useState(false) + const [showOptionsModal, setShowOptionsModal] = useState(false) const projectId = params.projectId useEffect(() => { - if (initialized && keycloak.authenticated && projectId){ - const requestOptions = { - method: 'GET', - headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + keycloak.token } - }; - fetch(process.env.REACT_APP_BACKEND_API_BASE_URL + '/api/project/' + projectId, requestOptions) - .then(response => { - if (response.status !== 200) { - navigate('/') - } - return response.json() - }) - .then(data => { - console.log("get projetcs reply", data) - setProject(data.project) - fetchInventoryResults() - fetchBAUResults() - for (let i = 0; i < data.project.stages.Climate.length; i++) { - fetchClimateResults(data.project, i) - } - setDisplayedClimateScenarios(data.project.stages.Climate.map((e:any)=>true)) - }); + if (initialized && keycloak.authenticated && props.project.id){ + setProject(props.project) + fetchInventoryResults() + fetchBAUResults() + for (let i = 0; i < props.project.stages.Climate.length; i++) { + fetchClimateResults(props.project, i) } - }, [keycloak, initialized, projectId, navigate]) + setDisplayedClimateScenarios(props.project.stages.Climate.map((e:any)=>true)) + } + }, [keycloak, initialized, props.project, navigate]) const fetchInventoryResults = () => { const requestOptions = { @@ -71,10 +55,6 @@ export default function ProjectCompare(){ console.log(data.status) return } - setInventoryTotalEnergyAndEmissions({ - WTW: data.totalEnergyAndEmissionsWTW, - TTW: data.totalEnergyAndEmissionsTTW - }) }) } const fetchBAUResults = () => { @@ -138,175 +118,184 @@ export default function ProjectCompare(){ Climate: Array.from(sourcesSets.Climate).map(source => project.sources.find(e => e.value === source)?.sourceId).sort(), } return ( - - - -

{project.name}

- -

Graphs

- - typeOfGHGIsWTW ? e.emissions.WTW : e.emissions.TTW)) || []} - displayedVtypes={displayedVtypes} - displayedClimateScenarios={displayedClimateScenarios} - showPercents={showPercents} - showLabels={showLabels} - highContrastColors={highContrastColors} - > - e?.vkt) || []} - displayedVtypes={displayedVtypes} - displayedClimateScenarios={displayedClimateScenarios} - showPercents={showPercents} - showLabels={showLabels} - highContrastColors={highContrastColors} - > - e?.modalShare?.passengers) || []} - displayedClimateScenarios={displayedClimateScenarios} - showLabels={showLabels} - highContrastColors={highContrastColors} - > - e?.modalShare?.freight) || []} - displayedClimateScenarios={displayedClimateScenarios} - showLabels={showLabels} - highContrastColors={highContrastColors} - > - e?.transportPerformance?.passengers) || []} - displayedVtypes={displayedVtypes} - displayedClimateScenarios={displayedClimateScenarios} - showPercents={showPercents} - showLabels={showLabels} - highContrastColors={highContrastColors} - > - e?.transportPerformance?.freight) || []} - displayedVtypes={displayedVtypes} - displayedClimateScenarios={displayedClimateScenarios} - showPercents={showPercents} - showLabels={showLabels} - highContrastColors={highContrastColors} - > - typeOfGHGIsWTW ? e.emissions.WTW : e.emissions.TTW)) || []} - bauTransportPerformanceData={bauResults?.transportPerformance?.passengers || {}} - climateTransportPerformanceData={climateResults.map(e => e?.transportPerformance?.passengers) || []} - displayedVtypes={displayedVtypes} - displayedClimateScenarios={displayedClimateScenarios} - showPercents={showPercents} - showLabels={showLabels} - highContrastColors={highContrastColors} - > - typeOfGHGIsWTW ? e.emissions.WTW : e.emissions.TTW)) || []} - bauTransportPerformanceData={bauResults?.transportPerformance?.freight || {}} - climateTransportPerformanceData={climateResults.map(e => e?.transportPerformance?.freight) || []} - displayedVtypes={displayedVtypes} - displayedClimateScenarios={displayedClimateScenarios} - showPercents={showPercents} - showLabels={showLabels} - highContrastColors={highContrastColors} - > -

Datasets

- - - - - - - - - - - - - - - - - - - - - - - - - -
DatasetActionSources
Inventory{project.name && - Dowload input data as csv - }{sourcesUsed.Inventory.map(e => e ? "[" + e + "] ": "")}
BAU Scenario{project.name && - Dowload input data as csv - }{sourcesUsed.BAU.map(e => e ? "[" + e + "] ": "")}
Climate Scenarios{project.name && - Dowload input data as csv - }{sourcesUsed.Climate.map(e => e ? "[" + e + "] ": "")}
-

Sources

- - - - - - - - - {project?.sources?.map(({value, sourceId}, index) => { - return ( - - - ) - })} - -
SourceID
{value}[{sourceId}]
- -
-
- + <> +
+

Graphs

+
+ +
+
+ typeOfGHGIsWTW ? e.emissions.WTW : e.emissions.TTW)) || []} + displayedVtypes={displayedVtypes} + displayedClimateScenarios={displayedClimateScenarios} + showPercents={showPercents} + showLabels={showLabels} + highContrastColors={highContrastColors} + > + e?.vkt) || []} + displayedVtypes={displayedVtypes} + displayedClimateScenarios={displayedClimateScenarios} + showPercents={showPercents} + showLabels={showLabels} + highContrastColors={highContrastColors} + > + e?.modalShare?.passengers) || []} + displayedClimateScenarios={displayedClimateScenarios} + showLabels={showLabels} + highContrastColors={highContrastColors} + > + e?.modalShare?.freight) || []} + displayedClimateScenarios={displayedClimateScenarios} + showLabels={showLabels} + highContrastColors={highContrastColors} + > + e?.transportPerformance?.passengers) || []} + displayedVtypes={displayedVtypes} + displayedClimateScenarios={displayedClimateScenarios} + showPercents={showPercents} + showLabels={showLabels} + highContrastColors={highContrastColors} + > + e?.transportPerformance?.freight) || []} + displayedVtypes={displayedVtypes} + displayedClimateScenarios={displayedClimateScenarios} + showPercents={showPercents} + showLabels={showLabels} + highContrastColors={highContrastColors} + > + typeOfGHGIsWTW ? e.emissions.WTW : e.emissions.TTW)) || []} + bauTransportPerformanceData={bauResults?.transportPerformance?.passengers || {}} + climateTransportPerformanceData={climateResults.map(e => e?.transportPerformance?.passengers) || []} + displayedVtypes={displayedVtypes} + displayedClimateScenarios={displayedClimateScenarios} + showPercents={showPercents} + showLabels={showLabels} + highContrastColors={highContrastColors} + > + typeOfGHGIsWTW ? e.emissions.WTW : e.emissions.TTW)) || []} + bauTransportPerformanceData={bauResults?.transportPerformance?.freight || {}} + climateTransportPerformanceData={climateResults.map(e => e?.transportPerformance?.freight) || []} + displayedVtypes={displayedVtypes} + displayedClimateScenarios={displayedClimateScenarios} + showPercents={showPercents} + showLabels={showLabels} + highContrastColors={highContrastColors} + > +

Datasets

+ + + {/* Dataset */} + {/* Action */} + {/* Sources */} + + + + + + + + + + + + + + + + + + + + + + + + + +
DatasetActionSources
Inventory{project.name && + CSV + }{sourcesUsed.Inventory.map(e => e ? "[" + e + "] ": "")}
BAU Scenario{project.name && + CSV + }{sourcesUsed.BAU.map(e => e ? "[" + e + "] ": "")}
Climate Scenarios{project.name && + CSV + }{sourcesUsed.Climate.map(e => e ? "[" + e + "] ": "")}
+

Sources

+ + + {/* Source */} + {/* ID */} + + + + + + + + + {project?.sources?.map(({value, sourceId}, index) => { + return ( + + + ) + })} + +
SourceID
{value}[{sourceId}]
+ setShowOptionsModal(false)} + project={project} + setDisplayedVtypes={setDisplayedVtypes} + typeOfGHGIsWTW={typeOfGHGIsWTW} + setTypeOfGHGIsWTW={setTypeOfGHGIsWTW} + showPercents={showPercents} + setShowPercents={setShowPercents} + showLabels={showLabels} + setShowLabels={setShowLabels} + highContrastColors={highContrastColors} + setHighContrastColors={setHighContrastColors} + displayedClimateScenarios={displayedClimateScenarios} + setDisplayedClimateScenarios={setDisplayedClimateScenarios} + /> + ) } type SetBoolean = (key:boolean | ((k:boolean) => boolean)) => void -const Options = ( - {project, setDisplayedVtypes, typeOfGHGIsWTW, setTypeOfGHGIsWTW, showPercents, setShowPercents, showLabels, setShowLabels, highContrastColors, setHighContrastColors, displayedClimateScenarios, setDisplayedClimateScenarios}: - {project: ProjectType, setDisplayedVtypes: React.Dispatch>, typeOfGHGIsWTW: boolean, setTypeOfGHGIsWTW: SetBoolean, showPercents: boolean, setShowPercents: SetBoolean, showLabels: boolean, setShowLabels: SetBoolean, highContrastColors: boolean, setHighContrastColors: SetBoolean, displayedClimateScenarios: boolean[], setDisplayedClimateScenarios: React.Dispatch>} +const OptionsModal = ( + {project, show, onHide, setDisplayedVtypes, typeOfGHGIsWTW, setTypeOfGHGIsWTW, showPercents, setShowPercents, showLabels, setShowLabels, highContrastColors, setHighContrastColors, displayedClimateScenarios, setDisplayedClimateScenarios}: + {project: ProjectType, show: boolean, onHide: () => void, setDisplayedVtypes: React.Dispatch>, typeOfGHGIsWTW: boolean, setTypeOfGHGIsWTW: SetBoolean, showPercents: boolean, setShowPercents: SetBoolean, showLabels: boolean, setShowLabels: SetBoolean, highContrastColors: boolean, setHighContrastColors: SetBoolean, displayedClimateScenarios: boolean[], setDisplayedClimateScenarios: React.Dispatch>} ) => { - const [showBody, setShowBody] = useState(false) - const [pin, setPin] = useState(false) const [selectedVtypes, setSelectedVtypes] = useState({} as {[key:string]: boolean}) useEffect(() => { const inventoryStep1 : InputInventoryStep1 = project.stages?.Inventory?.[0]?.steps?.[1] || {} @@ -347,77 +336,74 @@ const Options = ( }) } return ( - <> - - setShowBody(p=>!p)} style={{cursor: "pointer"}}> - Visualisations option (click to display) - {e.stopPropagation(); setPin(p => !p)}}>📌 - - {showBody && - - Displayed categories of transport - - {vtypes.map((vtype, index) => { - return ( - - - - {vtype} - - - ) - })} - - - - Displayed Climate Scenarios - - {displayedClimateScenarios.map((displayed, index) => { - return ( - - - - {index+1} - - - ) - })} - - - - GHG emission type - setTypeOfGHGIsWTW(true)} - label="Well To Wheel (WTW)" - /> - setTypeOfGHGIsWTW(false)} - label="Tank To Wheel (TTW)" - /> - - - Graph content - - setShowPercents((p:boolean)=>!p)}/> - Display percents changes compared to BAU - - - setShowLabels((p:boolean)=>!p)}/> - Display labels - - - setHighContrastColors((p:boolean)=>!p)}/> - Use high contrast colors - - - } - - + + + Settings + + + + Displayed categories of transport +
+ {vtypes.map((vtype, index) => { + return ( + + + + {vtype} + + + ) + })} +
+
+ + Displayed Climate Scenarios + + {displayedClimateScenarios.map((displayed, index) => { + return ( + + + + {index+1} + + + ) + })} + + + + GHG emission type + setTypeOfGHGIsWTW(true)} + label="Well To Wheel (WTW)" + /> + setTypeOfGHGIsWTW(false)} + label="Tank To Wheel (TTW)" + /> + + + Graph content + + setShowPercents((p:boolean)=>!p)}/> + Display percents changes compared to BAU + + + setShowLabels((p:boolean)=>!p)}/> + Display labels + + + setHighContrastColors((p:boolean)=>!p)}/> + Use high contrast colors + + +
+
); } \ No newline at end of file diff --git a/frontend/src/pages/ProjectSummary.tsx b/frontend/src/pages/ProjectSummary.tsx index 23092af..68ada84 100644 --- a/frontend/src/pages/ProjectSummary.tsx +++ b/frontend/src/pages/ProjectSummary.tsx @@ -1,16 +1,15 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { useParams, useNavigate } from "react-router-dom" -import { Button, Container, Row, Col, Card, Table, Badge, Alert } from 'react-bootstrap' +import { Button, Row, Col, Card, Table, Badge, Alert } from 'react-bootstrap' import {ProjectStage, ProjectType, TotalEnergyAndEmissions, FuelType, EmissionsResults} from '../frontendTypes' -import ProjectNav from '../components/ProjectNav' -import { ResponsiveContainer, PieChart, Pie, Cell, Tooltip } from 'recharts' +import { ResponsiveContainer, PieChart, Pie, Cell, Tooltip, Legend } from 'recharts' import './Project.css' -import EmissionsTable from '../components/viz/EmissionsTable' import EmissionsBarChart from '../components/viz/EmissionsBarChart' +import OutputNumberTd from '../components/OutputNumberTd' -export default function ProjectSummary(){ +export default function ProjectSummary(props : {project: ProjectType}){ const { keycloak, initialized } = useKeycloak() const navigate = useNavigate() const params = useParams() @@ -26,31 +25,17 @@ export default function ProjectSummary(){ const defaultColors = ["#2CB1D5", "#A2217C", "#808080", "#67CAE4", "#CE8DBB", "#B3B3B3", "#C5E8F2", "#EBD1E1", "#E6E6E6"] useEffect(() => { - if (initialized && keycloak.authenticated && projectId){ - const requestOptions = { - method: 'GET', - headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + keycloak.token } - }; - fetch(process.env.REACT_APP_BACKEND_API_BASE_URL + '/api/project/' + projectId, requestOptions) - .then(response => { - if (response.status !== 200) { - navigate('/') - } - return response.json() - }) - .then(data => { - console.log("get projetcs reply", data) - setProject(data.project) - if (data.project?.stages?.Inventory?.length > 0) - fetchInventoryResults() - if (data.project?.stages?.BAU?.length > 0) - fetchBAUResults() - for (let i = 0; i < data.project.stages.Climate.length; i++) { - fetchClimateResults(data.project, i) - } - }); + if (initialized && keycloak.authenticated && props.project.id){ + setProject(props.project) + if (props.project?.stages?.Inventory?.length > 0) + fetchInventoryResults() + if (props.project?.stages?.BAU?.length > 0) + fetchBAUResults() + for (let i = 0; i < props.project?.stages?.Climate?.length; i++) { + fetchClimateResults(props.project, i) } - }, [keycloak, initialized, projectId, navigate]) + } + }, [keycloak, initialized, props.project, navigate]) const hide = (state: string) => { setHideParams(prevHideParams => { @@ -94,20 +79,21 @@ export default function ProjectSummary(){ } return ( - -

{props.title}

- +

{props.title}

+
{!hideParams[props.title] - ? - : + ? + : } - {(props.stage === "Climate" && props.stageId != undefined && project.stages?.Climate.length && ) || ""} + {(props.stage === "Climate" && props.stageId != undefined && project.stages?.Climate.length && ) || ""} - - - +
{!hideParams[props.title] && {props.children} @@ -200,114 +186,118 @@ export default function ProjectSummary(){ return res })as any[]) return ( - - - -

{project.name}

- - - Indexing - The GHG emission inventory for urban transport is the sum of all transport-related activities emissions that can be attributed to the city or country for a given year (base year). - {inventoryResultsError && Failed to compute inventory results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory steps again and fill missing data.} - {project.stages?.Inventory?.[0]?.step >= 7 && !inventoryResultsError && - - - - - - - - - - - {Object.keys(inventoryTotalEnergyAndEmissions["WTW"]).map((vtype, index) => { - const fuels = inventoryTotalEnergyAndEmissions["WTW"][vtype] - const ftypes = Object.keys(fuels) - let fuelJsx = [] - for (let i = 0; i < ftypes.length; i++) { - const ftype = ftypes[i] as FuelType - const co2 = fuels[ftype]?.co2 || '' - fuelJsx.push( - {i===0 && } - - - ) - } - return [ - fuelJsx - ] - })} - -
🛈 Vehicle🛈 Fuel🛈 GHG emissions (1000t GHG) ({"WTW"})
{vtype}{ftype}{co2}
- - - - - - {inventoryEmissionsPieData.map((entry, index) => ())} - - - - - -
- } -
- - Projecting - The Business-as-usual scenario aims to describe the transport related emissions if nothing changed in the years to come from the current status quo. - {bauResultsError && Failed to compute BAU results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory and BAU steps again and fill missing data.} - {bauResults?.emissions && - - - - - - - } - - - Comparing - The Climate Scenario aims to describe the predicted transport related emissions when a strategy, policy, programme or project were to be introduced. - {climateResultsError[0] && Failed to compute Climate results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory and scenarios steps again and fill missing data.} - {climateResults?.[0]?.emissions && - - + <> + + The GHG emission inventory for urban transport is the sum of all transport-related activities emissions that can be attributed to the city or country for a given year (base year). + {inventoryResultsError && Failed to compute inventory results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory steps again and fill missing data.} + {project.stages?.Inventory?.[0]?.step >= 7 && !inventoryResultsError && + + + + + {inventoryEmissionsPieData.map((entry, index) => ())} + + + + + + + + + + {/* Vehicle */} + {/* Fuel */} + {/* GHG emissions */} + + + + + + + + + + {Object.keys(inventoryTotalEnergyAndEmissions["WTW"]).map((vtype, index) => { + const fuels = inventoryTotalEnergyAndEmissions["WTW"][vtype] + const ftypes = Object.keys(fuels) + let fuelJsx = [] + for (let i = 0; i < ftypes.length; i++) { + const ftype = ftypes[i] as FuelType + const co2 = fuels[ftype]?.co2 || '' + fuelJsx.push( + {i===0 && } + + + ) + } + return [ + fuelJsx + ] + })} + + + + + + +
VehicleFuelGHG em. (1000t GHG - {"WTW"})
{vtype}{ftype}
+ +
+ } +
+ + The Business-as-usual scenario aims to describe the transport related emissions if nothing changed in the years to come from the current status quo. + {bauResultsError && Failed to compute BAU results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory and BAU steps again and fill missing data.} + {bauResults?.emissions && + + + + + + + } + + + The Climate Scenario aims to describe the predicted transport related emissions when a strategy, policy, programme or project were to be introduced. + {climateResultsError[0] && Failed to compute Climate results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory and scenarios steps again and fill missing data.} + {climateResults?.[0]?.emissions && + + + + + + + } + + {project?.stages?.Climate.map((scenario, index) => { + if (index === 0) { + return
+ } + return ( + + The Climate Scenario aims to describe the predicted transport related emissions when a strategy, policy, programme or project were to be introduced. + {climateResultsError[index] && Failed to compute Climate results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory and scenarios steps again and fill missing data.} + {climateResults?.[index]?.emissions && + + - + } - {project?.stages?.Climate.map((scenario, index) => { - if (index === 0) { - return
- } - return ( - - Comparing - The Climate Scenario aims to describe the predicted transport related emissions when a strategy, policy, programme or project were to be introduced. - {climateResultsError[index] && Failed to compute Climate results. This is often due to a vehicle or fuel being added after the first edits. Please go through the inventory and scenarios steps again and fill missing data.} - {climateResults?.[index]?.emissions && - - - - - - - } - - ) - })} - {project?.stages?.Climate?.length > 0 && -
- } - -
-
- + ) + })} + {project?.stages?.Climate?.length > 0 && +
+ } + ) } diff --git a/frontend/src/pages/Projects.tsx b/frontend/src/pages/Projects.tsx index d3e6463..b1d60ec 100644 --- a/frontend/src/pages/Projects.tsx +++ b/frontend/src/pages/Projects.tsx @@ -2,7 +2,8 @@ import React, {useState, useEffect} from 'react' import { useKeycloak } from "@react-keycloak/web" import { Navigate, useNavigate } from 'react-router-dom' import {ProjectType} from '../frontendTypes' -import { Container, Button, Row, Col, Stack, Modal, OverlayTrigger, Tooltip, Table, Badge } from 'react-bootstrap' +import { Container, Button, Row, Col, Modal, OverlayTrigger, Tooltip, Table, Badge } from 'react-bootstrap' +import Footer from "../components/Footer" export default function Projects(){ const { keycloak, initialized } = useKeycloak(); @@ -72,58 +73,69 @@ export default function Projects(){ .then(loadProjects) } } - const referenceYearTooltip = (props:any) => ( - - You can choose the year of reference based on your needs - - ); return ( <> - - - -

Projects

-

- A project is related to a specific MYC urban mobility plan. - It can be at a local level for Sustainable Urban Mobility plans (SUMP) or - at a national level for National Urban Mobility Plans (NUMP). - -

-

You can start by creating a new project, or checking public projects if available.

- - {initialized ? - - - - :
Loading...
- } - - +
+ + + {/* +

Projects

+ */} + +

Projects

+
+

+ A project is related to a specific MYC urban mobility plan. + It can be at a local level for Sustainable Urban Mobility plans (SUMP) or at a national level for National Urban Mobility Plans (NUMP).  +

+

You can start by creating a new project, or checking public projects if available.

+
+ {initialized ? + // + // + // + + :
Loading...
+ } + +
+ {initialized && } - -
+ +
+
+ + + +
+ + + +
Projects - -

- Projects are divided in three stages: -

-
    -
  • An inventory, where transport activity is described for a reference year, and current yearly GHG emissions are computed
  • -
  • A business as usual (BAU) scenario, where the transport related emissions are described if nothing changed in the years to come
  • -
  • One or more climate scenarios, where the predicted transport related emissions are described when a strategy, policy, programme or project were to be introduced
  • -
-

More information on these stages and which data they require will be detailed during the completion process.

-

Once your project is complete, you can validate it. This will let admins know that it can be consulted and maybe published as inspiration for other community members.

+ +
+

Projects are divided in three stages:

+
    +
  • An inventory, where transport activity is described for a reference year, and current yearly GHG emissions are computed
  • +
  • A business as usual (BAU) scenario, where the transport related emissions are described if nothing changed in the years to come
  • +
  • One or more climate scenarios, where the predicted transport related emissions are described when a strategy, policy, programme or project were to be introduced
  • +
+

More information on these stages and which data they require will be detailed during the completion process.

+

Once your project is complete, you can validate it. This will let admins know that it can be consulted and maybe published as inspiration for other community members.

+
- + {/* - + */}
) @@ -137,22 +149,40 @@ const ProjectsList = ({ownedProjects, publicProjects, adminProjects, handleEditP handleEditProject: (project: ProjectType, action: 'validate' | 'delete') => void, isAdmin: boolean }) => ( -
-

Your projects

- - {isAdmin ? - <> -

(Admin) Public projects

- -

(Admin) Private projects

- - - : <> -

Public projects

- - - } -
+ + + + + + + + {isAdmin ? + <> + + +

(Admin) Public projects

+ + +
+ + +

(Admin) Private projects

+ + +
+ + : + <> + + +

Public projects

+ + +
+ + } + + ) const DetailedProjects = ({projects, handleEditProject, showOwner}: {projects: ProjectType[], handleEditProject: (project: ProjectType, action: 'validate' | 'delete') => void, showOwner: boolean}) => { @@ -186,39 +216,54 @@ const DetailedProjects = ({projects, handleEditProject, showOwner}: return navigate(url) } return ( -
+ <> + + {/* Name */} + {/* Type */} + {/* Status */} + {/* Action */} + {showOwner && }{/* Author */} + - - + {/* */} + - {showOwner && } + {/* */} - - + + {showOwner && } {projects.map(project => - - - - {showOwner && } - - + {/* */} + + + + {showOwner && } )} + + + + {/* */} + + + {showOwner && } +
#Name#Project TypeAuthorInv. progressStatusInventory progressActionActionsAuthor
{project.id}{project.name} - {project.isSump && project.city + ", "}{project.country}}> - {project.isSump ? SUMP : NUMP} - - {project.owner}{project.status === 'draft' ? Draft : Validated}{project.id} openProject(project)}>{project.name}{project.isSump && project.city + ", "}{project.country}} offset={[0, 24]}> + {project.isSump ? + SUMP + : + NUMP} + {project.status === 'draft' ? Draft : Validated} - - - + {/* */} + + {project.owner}
-
+ ) } const ValidateConfirmModal = ({showValidateConfirmModal, handleCloseValidateConfirmModal, projectBeingEdited}: @@ -240,16 +285,16 @@ const ValidateConfirmModal = ({showValidateConfirmModal, handleCloseValidateConf Validate confirmation - +

Are you sure you want to validate the project {projectBeingEdited.name} ?

Validated projects are marked as complete, and might later be published to every user

@@ -260,30 +305,20 @@ const DeleteConfirmModal = ({showDeleteConfirmModal, handleCloseDeleteConfirmMod Delete confirmation - +

Are you sure you want to delete the project {projectBeingEdited.name} ?

This operation is definitive, all data related to this project will be lost.

) -const ProjectProgress = ({step} : {step: number}) => { - let res = "" - for (let i = 0; i < step; i++) { - res += '🟩' - } - for (let i = step; i < 8; i++) { - res += '🟧' - } - return {res} -} const PublicProjects = ({publicProjects, handleEditProject}: {publicProjects: ProjectType[], handleEditProject: (project: ProjectType, action: 'validate' | 'delete') => void}) => { const navigate = useNavigate() const openProject = (p: ProjectType) => { @@ -291,9 +326,15 @@ const PublicProjects = ({publicProjects, handleEditProject}: {publicProjects: Pr } return ( + + {/* Name */} + {/* Type */} + {/* Author */} + {/* Action */} + - + {/* */} @@ -303,19 +344,26 @@ const PublicProjects = ({publicProjects, handleEditProject}: {publicProjects: Pr {publicProjects.map(project => - - + )} + + + + + +
##Name Type Author
{project.id}{project.name} openProject(project)}>{project.name} {project.isSump && project.city + ", "}{project.country}}> - {project.isSump ? SUMP : NUMP} + {project.isSump ? SUMP : NUMP} {project.owner} - +
) diff --git a/frontend/src/pages/TopDown.tsx b/frontend/src/pages/TopDown.tsx index 7ee8b89..f275c18 100644 --- a/frontend/src/pages/TopDown.tsx +++ b/frontend/src/pages/TopDown.tsx @@ -120,9 +120,9 @@ export default function TopDown(){ return ( - + -

Top Down validation

+

Top-down validation

Project: {project.name}

A well-known validation approach especially for national GHG inventories is the comparison of the calculated fuel consumption (summed by fuel type and vehicle category) obtained from the present tool (bottom-up approach) with the national energy balance (top-down approach). Differences within a range of +/- 10% are quite common and should not be considered as error but as uncertainty. One reason can be for example, that the energy balance does not include fuels bought in neighboring countries and consumed within the country. The emission inventory report should try explaining gaps and analyze the possibility to minimize the related uncertainties.
@@ -202,9 +202,9 @@ export default function TopDown(){ ) })} -

Energy consumption for both transport types

+

Energy consumption for both transport types

- + { const ftype = _ftype as FuelType return { @@ -217,16 +217,16 @@ export default function TopDown(){ new Intl.NumberFormat('fr', { notation: 'compact' }).format(value)} /> new Intl.NumberFormat('fr', { notation: 'compact' }).format(value)}/> - - + +
diff --git a/frontend/src/pages/WelcomePage.css b/frontend/src/pages/WelcomePage.css index ac20bc0..d11d876 100644 --- a/frontend/src/pages/WelcomePage.css +++ b/frontend/src/pages/WelcomePage.css @@ -1,24 +1,39 @@ -.partnerLine { - margin-bottom: 20px; +/* .partnerLine { + display: inline-block; } -.partnerLine img { - max-width: 150px; - margin: 5px; - max-height: 80px; -} -header { +.partnerLine .wrap { + overflow: hidden; + position: relative; + float: left; +} +.partnerLine .wrap img.fake { + float: left; + visibility: hidden; + width: auto; +} +.partnerLine .img_wrap { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} +.partnerLine .img_wrap img.normal { + width: 50%; +} */ + +/* header { height: calc(100vh - 76px); box-shadow: 0 12px 12px -10px rgba(48, 41, 41, 0.1); - background: linear-gradient(90deg, #9e2886 -100%, #1bb9d9); + background: linear-gradient(90deg, .9e2886 -100%, #1bb9d9); display: flex; align-items: center; overflow: hidden; -} -header #container { +} */ +/* header #container { color: white; width: 80%; height: 70%; - text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); margin: 0 auto; padding-left: 1rem; @@ -34,8 +49,8 @@ header #container h1 { } header #container p { font-size: 100%; -} -header button { +} */ +/* header button { width: 300px; } footer { @@ -43,4 +58,4 @@ footer { } .shadowbelow { box-shadow: 0 9px 13px -13px grey -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/frontend/src/pages/WelcomePage.tsx b/frontend/src/pages/WelcomePage.tsx index ec6cc09..8f2f4ae 100644 --- a/frontend/src/pages/WelcomePage.tsx +++ b/frontend/src/pages/WelcomePage.tsx @@ -4,132 +4,147 @@ import { useNavigate } from 'react-router-dom' import Button from 'react-bootstrap/Button' import Row from 'react-bootstrap/Row' import Col from 'react-bootstrap/Col' +import Footer from "../components/Footer" -import './WelcomePage.css' import { Image } from 'react-bootstrap' export default function WelcomePage(){ return ( -
+ <> - - -
+ {/* */} +
+
+ + +
+ + +
+
+ ) } const Jumbo = () => { const { keycloak } = useKeycloak() const navigate = useNavigate() return ( -
-
-
-

Calculate and monitor transport related GHG emissions with MobiliseYourCity

-
+ <> +
+
- -

- This online tool allows greenhouse gas emission (GHG) calculations in the transport sector at the local level . -

-

- It enables calculating GHG inventories of cities as well as BAU (business as usual) scenarios. Governments can therefore calculate the environmental effects of local urban mobility activity and foresee the evolution if no actions is taken. -

- {keycloak.authenticated ? - - : - } + {/*

Get emissions from local transportation

*/} + +

Estimate emissions from local transports

+
+

The MYC Calculator helps government organizations and consulting agencies to estimate current and future GHG emissions, and allows them to compare their “Business As Usual” scenario to GHG reduction plans.

+

The MYC Calculator is fully open source and free to use.

+
+ {/* {keycloak.authenticated ? + + : + + } */} + + + +
+ {/*
+ + + +
*/} +
+ + +
+ {/* */} +
+ {/* this should be replaced with an illustration showing the map of a city or/and an emission graph */}
+
+
+ {/*
+
- map of partners +

MYC GHG emissions calculator

+ +
+

The MYC GHG emissions calculator helps governmental organizations and consulting agencies to estimate both present and future greenhouse gas (GHG) emissions for local transports.

+

Future estimations can be made according to the current emission trends ("BAU") and to GHG reduction plans ("Climate Scenarios").

+
+ + + +
+
+ + + +
+
+
-
+
*/} + ) } const Methodology = () => ( -
- - -

Methodology

-

It relies on a bottom-up model : calculations are based on distance travelled (whereas the top-down model bases the calculations on fuel/energy consumption).

-

The scope of the emission that should be taken into account is based on a territorial principle : basically all traffic within the city must taken into account. (For more details, go to chapter 3.2 of MYC-GHG Guidelines).

- - - methodology - -
+
+
+ + {/*

Methodology

*/} + +

Methodology

+
+

It relies on a bottom-up model : calculations are based on distance travelled (whereas the top-down model bases the calculations on fuel/energy consumption).

+

The scope of the emission that should be taken into account is based on a territorial principle : basically all traffic within the city must taken into account. For more details, go to chapter 3.2 of MYC-GHG Guidelines.

+
+ + +
+ methodology +
+ +
+
) const Devs = () => ( -
- - -

Creators

-

The tool was developed by Fabrique des Mobilités based on the excel model created by the Institute for Energy and Environmental Research in cooperation with the German and French development agencies GIZ and AFD. It is not allowed to use the tool for commercial purposes.

-

The developers are not responsible for the accuracy of the results. Any modification of the tool is the responsibility of the user.

- - - fabmob - ifeu - -
+
+
+ + {/*

Creators

*/} + +

Creators

+
+

The tool was developed by Fabrique des Mobilités based on the Excel model created by the Institute for Energy and Environmental Research in cooperation with the German and French development agencies GIZ and AFD.

+

The developers are not responsible for the accuracy of the results. Any modification of the tool is the responsibility of the user. It is not allowed to use the tool for commercial purposes.

+
+ + +
+ fabmob + ifeu +
+ +
+
) -const Partners = () => ( -
- - -
-

This project is taking part of the MobiliseYourCity partnership

-
-
Supported by
-
- European Commission - AFD - BMUV - BMZ - FFEM - Ministère de la transition écologique -
-
Implemented by
-
- AFD - GIZ - Ademe - Cerema - Codatu - European Bank - KFW - Wuppertal -
-
Knowledge and Network Partners
-
- ECF - GPIT - ITDP - Platforma - Ssatp - Trufi association - UCLG - UNHABITAT -
-
In collaboration with
-
- TUMI - Euroclima+ - ADB - Marrakech Partnership - SuM4All - DT4A -
- -
-
-) \ No newline at end of file diff --git a/frontend/src/scrollToTop.tsx b/frontend/src/scrollToTop.tsx new file mode 100644 index 0000000..8b76e36 --- /dev/null +++ b/frontend/src/scrollToTop.tsx @@ -0,0 +1,12 @@ +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +export default function ScrollToTop() { + const { pathname } = useLocation(); + + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + + return null; +} \ No newline at end of file diff --git a/frontend/src/utils/inputsAsCsv.tsx b/frontend/src/utils/inputsAsCsv.tsx index 5aaaba2..7ee2db5 100644 --- a/frontend/src/utils/inputsAsCsv.tsx +++ b/frontend/src/utils/inputsAsCsv.tsx @@ -42,7 +42,7 @@ export function inputsAsCsv (project: ProjectType, stage: ProjectStage) : string const networks : ("rail"|"road")[] = ["rail", "road"] for (let i = 0; i < networks.length; i++) { const network = networks[i] - const ftypes = Object.keys(inputInventoryStep5.emissions[network].fuels) + const ftypes = Object.keys(inputInventoryStep5.emissions?.[network]?.fuels || {}) for (let j = 0; j < ftypes.length; j++) { const ftype = ftypes[j] as FuelType csv.push(["input", "Inventory", "5", "TopDown Emissions (1000t GHG)", "", ftype, network, inputInventoryStep5.emissions[network].fuels[ftype]!.value, inputInventoryStep5.emissions[network].fuels[ftype]!.source]) diff --git a/tsconfig.json b/tsconfig.json index a646955..a3a3681 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -68,7 +68,7 @@ /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import × from y' when a module doesn't have a default export. */ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */