diff --git a/README.md b/README.md index 8241227c..8c16c268 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,29 @@ This is the official source code for [klimatkollen.se](https://klimatkollen.se). -## About +## Free our climate data! -Free our climate data! Klimatkollen is an open source and citizen-driven climate data platform aimed at visualising local climate data. +Klimatkollen is an open source and citizen-driven climate data platform aimed at visualising local climate data in Sweden. -The problem: Most of Sweden’s 290 cities and municipalities are not slashing carbon emissions fast enough to be in line with the Paris Agreement. Furthermore, climate data that can help us is often locked behind paywalls or sits in complex government databases. If we can’t clearly see how much CO2 is being emitted, from which sources and how quickly we need to decarbonise – we can’t create a public opinion strong enough to change the course of our future. +The problem: Sweden’s 290 cities and municipalities are not slashing carbon emissions fast enough to be in line with the Paris Agreement. Climate data that can help us is often locked behind paywalls, or sits in complex government databases. If we can’t clearly see how much CO2 is being emitted, from which sources and how quickly we need to decarbonise – we can’t create a public opinion strong enough to change the course of our future. This needs to change. -This needs to change. +That’s why we’re building a data-driven movement of climate-savvy developers to help us find and visualise climate data for the public. Climb aboard! -That’s why we’re building a data-driven movement of climate-savvy developers to help us find and visualise climate data for the public. +Join our [Discord](https://discord.gg/N5P64QPQ6v) and set our climate data free! -Please climb onboard to help us set our climate data free! #Klimatkollen /#FreeClimateData +#Klimatkollen #FreeClimateData -## Get started +## Climate Data Pipeline Overview + +Please see full [description here](data/README.md). + +Feel free to explore the repository to understand more about how we collect, process, and display climate data. + +## Building and running locally -Detailed step-by-step setup instructions, including getting the code and installing dependencies, can be found in [doc/getting-started.md](doc/getting-started.md). +If your're starting from scratch, and working with GitHub, NodeJS and so on is new to you, read [doc/getting-started.md](doc/getting-started.md). -We use next.js and Typescript and it's pretty straightforward to get started. Just clone the repo and run: +We use next.js and Typescript and it's pretty straightforward to get started. After cloning the repo run: npm ci npm run dev @@ -33,24 +39,19 @@ The project can also be run with docker (although with much slower refresh time) # starts the container docker run -t -i --rm -p 3000:3000 --name klimatkollen klimatkollen - -## Contribute +## Contributing The idea behind Klimatkollen is to give citizens access to the climate data we need to meet the goals of the Paris Agreement – and save our own future. -Do you have an idea for a feature you think should be added to the project? Please develop it and create a pull request where you explain what you've done. If you can't develop the feature yourself, please submit an [issue](https://github.com/Klimatbyran/klimatkollen/issues) where you explain your suggestion. - -Looking for ideas on what needs to be done? Take a look at our [issues](https://github.com/Klimatbyran/klimatkollen/issues) and/or [pull requests](https://github.com/Klimatbyran/klimatkollen/pulls). Testing, bug fixes, typos or fact checking our data is highly appreciated. - -Feedback? Either submit an [issue](https://github.com/Klimatbyran/klimatkollen/issues) or send an email to [hej@klimatkollen.se](mailto:hej@klimatkollen.se). +Do you have an idea for a feature you think should be added to the project? Before jumping into the code, it's a good idea to discuss your idea in the Discord server. That way you can find out if someone is already planning work in that area, or if your suggestion aligns with other people's thoughts. You can always submit an [issue](https://github.com/Klimatbyran/klimatkollen/issues) explaining your suggestion. We try to review the issues as soon as possible, but be aware that the team is at times very busy. Again, feel free to ask for a review on Discord! -Please star this repo if you want to follow the progress and feel free to join our [Discord](https://discord.gg/N5P64QPQ6v). +Looking for ideas on what needs to be done? We appreciate help on existing [issues](https://github.com/Klimatbyran/klimatkollen/issues) very much. If you pick one up, remember to leave a comment saying you're working on it, and roughly when you expect to report progress. This helps others avoid double work and know what to expect. -## Climate Data Pipeline Overview +Testing, bug fixes, typos or fact checking of our data is highly appreciated. -Please see full [description here](data/README.md). +## Contact -Feel free to explore the repository to understand more about how we collect, process, and display climate data. +Join the Discord server in the introduction or send an email to [hej@klimatkollen.se](mailto:hej@klimatkollen.se). ## Supporters and Partners diff --git a/components/Footer/FooterSupporters.tsx b/components/Footer/FooterSupporters.tsx index c85ae64e..72a439b3 100644 --- a/components/Footer/FooterSupporters.tsx +++ b/components/Footer/FooterSupporters.tsx @@ -35,14 +35,6 @@ function Supporters() { alt="Google.org logo" /> - - Postkodstiftelsen logo - ) } diff --git a/pages/in-english/index.tsx b/pages/in-english/index.tsx index b19ef132..f5b4d163 100644 --- a/pages/in-english/index.tsx +++ b/pages/in-english/index.tsx @@ -15,6 +15,7 @@ import { Grid, GridImage, GridItem } from '../../components/shared' const Ola = '/team/ola.jpg' const Frida = '/team/frida.jpg' const Elvira = '/team/elvira.jpg' +const Alex = '/team/alex.jpg' const Container = styled.section` display: flex; @@ -169,6 +170,27 @@ function InEnglish() { + + + Alexandra Palmquist, Climate Data + Environmental scientist with a focus on climate and corporate data. + Previous experience with the UN in Latin America, now well-established in Stockholm's + tech startup scene to drive climate transition. + + + LinkedIn logo + + +

Want to use your skills for climate impact?

diff --git a/pages/om-oss/index.tsx b/pages/om-oss/index.tsx index 87efe39c..7135d1f5 100644 --- a/pages/om-oss/index.tsx +++ b/pages/om-oss/index.tsx @@ -20,6 +20,7 @@ import Markdown from '../../components/Markdown' const Ola = '/team/ola.jpg' const Frida = '/team/frida.jpg' const Elvira = '/team/elvira.jpg' +const Alex = '/team/alex.jpg' const Anna = '/board/anna.jpg' const CJ = '/board/carl-johan.jpg' const Christian = '/board/christian.jpg' @@ -100,6 +101,16 @@ function OmOss() { {t('about:bios.elvira.name')} {t('about:bios.elvira.text')} + + + {t('about:bios.alex.name')} + {t('about:bios.alex.text')} + )} /> diff --git a/public/locales/sv/about.json b/public/locales/sv/about.json index 7fb4798c..941891a6 100644 --- a/public/locales/sv/about.json +++ b/public/locales/sv/about.json @@ -16,6 +16,10 @@ "name": "Elvira Boman", "text": "Tech lead och teknisk fysiker på Klimatkollen och Precisit. Lång erfarenhet av grön och cirkulär tech vid flera prisbelönta startups och del av ledarteamet på IT-konsultbyrån Precisit." }, + "alex": { + "name": "Alexandra Palmquist", + "text": "Miljövetare med fokus på klimat och företagsdata. Tidigare erfarenhet från FN i Latinamerika, numera hemmastadd i Stockholms tech startup-scen för att driva klimatomställningen." + }, "frida": { "name": "Frida Berry Eklund", "text": "Klimatspecialist och medgrundare till Klimatkollen. Initiativtagare till Our Kids’ Climate och författare till boken “Prata med barn om klimatet” (Natur & Kultur). EU Climate Pact Ambassador och Climate Reality Leader." @@ -34,7 +38,7 @@ "title": "Vår styrelse" }, "content": { - "text": "I dag kan du på Klimatkollen se:\n\n- Koldioxidbudgetar för landets alla 290 kommuner. Klimatkollen utgår ifrån en [nationell koldioxidbudget](https://klimatkollen.se/Paris_compliant_Swedish_CO2_budgets-March_2022-Stoddard&Anderson.pdf) som beräknats av forskare vid Uppsala universitet enligt Tyndall-modellen och som sedan fördelats ut på kommunerna av Klimatkollen.\n- En översikt i både kartvy och listvy över koldioxidutsläppen i landets kommuner. Kommunerna rankas baserat på genomsnittlig årlig utsläppsminskningstakt sedan Parisavtalet undertecknades 2015.\n- En visualisering av hur det gått med koldioxidutsläppen för varje kommun från 1990 tills idag, hur det borde gå för att klara Parisavtalets 1,5-gradersmål och en prognos för hur det går om utsläppen i kommunen fortsätter att utvecklas som de senaste fem åren.\n- För varje kommun finns även annan nyckelfakta, som koldioxidbudget, datum när budgeten tar slut med nuvarande utsläppstakt, politiskt styre och koldioxidutsläpp per invånare.\n\nKlimatkollen är utvecklad med öppen källkod. Det betyder att alla kan vara med och utveckla och förbättra sajten via vårt [Github-repo](https://github.com/Klimatbyran/klimatkollen).", + "text": "I dag kan du på Klimatkollen se:\n\n- Koldioxidbudgetar för landets alla 290 kommuner. Klimatkollen utgår ifrån en [nationell koldioxidbudget](https://www.cemus.uu.se/wp-content/uploads/2023/12/Paris-compliant-carbon-budgets-for-Swedens-counties-.pdf) som beräknats av forskare vid Uppsala universitet enligt Tyndall-modellen och som sedan fördelats ut på kommunerna av Klimatkollen. (2022 koldioxidbudget [här](https://klimatkollen.se/Paris_compliant_Swedish_CO2_budgets-March_2022-Stoddard&Anderson.pdf))\n- En översikt i både kartvy och listvy över koldioxidutsläppen i landets kommuner. Kommunerna rankas baserat på genomsnittlig årlig utsläppsminskningstakt sedan Parisavtalet undertecknades 2015.\n- En visualisering av hur det gått med koldioxidutsläppen för varje kommun från 1990 tills idag, hur det borde gå för att klara Parisavtalets 1,5-gradersmål och en prognos för hur det går om utsläppen i kommunen fortsätter att utvecklas som de senaste fem åren.\n- För varje kommun finns även annan nyckelfakta, som koldioxidbudget, datum när budgeten tar slut med nuvarande utsläppstakt, politiskt styre och koldioxidutsläpp per invånare.\n\nKlimatkollen är utvecklad med öppen källkod. Det betyder att alla kan vara med och utveckla och förbättra sajten via vårt [Github-repo](https://github.com/Klimatbyran/klimatkollen).", "title": "Vad finns på Klimatkollen?" }, "earlierProjects": { diff --git a/public/locales/sv/common.json b/public/locales/sv/common.json index f5b5e91d..7a2faddf 100644 --- a/public/locales/sv/common.json +++ b/public/locales/sv/common.json @@ -33,7 +33,7 @@ "body": "Datum då kommunens koldioxidbudget tar slut om utsläppen fortsätter enligt nuvarande trend. Några kommuner kommer att hålla budgeten om trenden står sig.", "columnHeader": "Budget tar slut", "source": "Källor: [Nationella emissionsdatabasen](https://nationellaemissionsdatabasen.smhi.se/) och [Uppsala universitet](http://www.cemus.uu.se/wp-content/uploads/2023/12/Paris-compliant-carbon-budgets-for-Swedens-counties-.pdf)", - "title": "Budget slut om", + "title": "Koldioxidbudget", "followingBudget": "Håller budget", "labels": ["2 år -", "2-3 år", "3-4 år", "4-5 år", "5 år +", "Håller budget"], "name": "Koldioxidbudgetarna" diff --git a/public/locales/sv/municipality.json b/public/locales/sv/municipality.json index 3276c168..eef10523 100644 --- a/public/locales/sv/municipality.json +++ b/public/locales/sv/municipality.json @@ -20,7 +20,7 @@ "title": "Klimatplan" }, "co2budget": { - "info": "Mängden koldioxid kvar att släppa ut för att klara Parisavtalets 1,5-gradersmål, läs mer om koldioxidbudgetar [här](https://klimatkollen.se/Paris_compliant_Swedish_CO2_budgets-March_2022-Stoddard&Anderson.pdf).", + "info": "Mängden koldioxid kvar att släppa ut för att klara Parisavtalets 1,5-gradersmål, läs mer om koldioxidbudgetar [här](https://www.cemus.uu.se/wp-content/uploads/2023/12/Paris-compliant-carbon-budgets-for-Swedens-counties-.pdf).", "title": "Koldioxidbudget" }, "emissionReduction": { diff --git a/public/locales/sv/partierna.json b/public/locales/sv/partierna.json index 0226a7e4..fec4e996 100644 --- a/public/locales/sv/partierna.json +++ b/public/locales/sv/partierna.json @@ -4,6 +4,6 @@ "title": "Klimatkollen – Analys av partiernas klimatmål" }, "part1": "Inför valet genomför Klimatkollen en granskning av riksdagspartiernas klimatpolitik. Nu är första delen klar, en analys av partiernas klimatmål jämfört med Parisavtalets 1,5-gradersmål, där hänsyn tagits till avtalets rättviseaspekt.\n\nBakom projektet står forskarnätverket Researchers’ Desk, Världsnaturfonden WWF, Våra barns klimat och ClimateView, i samarbete med PwC och Naturskyddsföreningen.\n\nAnalysen visar att två partiers klimatmål, Miljöpartiets och Vänsterpartiets, är nära Parisavtalets 1,5-gradersmål. Ytterligare två partier, Centerpartiet och Liberalerna, går längre än Sveriges klimatmål, men når inte lika långt. Kristdemokraterna, Moderaterna, Socialdemokraterna och Sverigedemokraterna har mål i linje med det svenska klimatmålet, men är längre ifrån Parisavtalets 1,5-gradersmål.", - "part2": "För att vara i linje med Parisavtalets 1,5-gradersmål bör Sverige maximalt släppa ut cirka 170 miljoner ton koldioxid. Det utgör Sveriges andel av den globala utsläppsbudgeten för Parisavtalets 1,5-gradersmål, med hänsyn tagen till avtalets rättviseaspekt, där rika och tidigt industrialiserade länder ska gå före.\n\nDen framräknade nationella koldioxidbudgeten ligger även till grund för jämförelsen på [startsidan](/) på Klimatkollen. Läs mer om hur den är beräknad [här](/Paris_compliant_Swedish_CO2_budgets-March_2022-Stoddard&Anderson.pdf)\n\nAnalysen baseras på två frågor i en enkät som skickats till samtliga riksdagspartier under våren. Den första frågan var ”Vilken utsläppsbudget eller klimatmål och takt för utsläppsminskningar vill ert parti se?”. Den andra frågan var ”Står ert parti bakom EU:s nya förslag till mål om att Sverige ska ha en nettoinbindning av kolinlagring från skog och mark på 47,3 miljoner ton CO₂e årligen till 2030?”\n\nAlla partier utom Moderaterna har valt att svara på enkäten. Svaren har sedan analyserats av forskare från nätverket Researchers’ Desk och WWF. Moderaterna är dock med i analysen av klimatmålen, trots att de inte svarade på enkäten, eftersom de tidigare ställt sig bakom Sveriges nuvarande klimatmål och därmed beräknas utifrån det.\n\nKristdemokraternas, Liberalernas, Moderaternas, Socialdemokraternas och Sverigedemokraternas ambitioner ligger i linje med Sveriges nuvarande klimatmål, som kräver en koldioxidbudget på 355 miljoner ton CO₂. Det är cirka 2,1 gånger mer än Sveriges andel av den globala budgeten för Parisavtalets 1,5 gradersmål, med hänsyn tagen till avtalets rättviseaspekt.\n\nCenterpartiets klimatmål är ”netto noll år 2040”. Detta innebär utsläpp av 311 miljoner ton CO₂, vilket är cirka 1,8 gånger mer än den nationella koldioxidbudgeten.\n\nVänsterpartiets klimatmål är ”noll utsläpp till 2035”. Detta medför utsläpp av 217 miljoner ton CO₂, vilket är cirka 1,3 gånger mer än den nationella koldioxidbudgeten.\n\nMiljöpartiets klimatmål är ”en koldioxidbudget på 180 miljoner ton CO₂”. Detta motsvarar att uppnå ”nollutsläpp” för CO₂ till 2034 vilket är cirka 1,1 gånger mer än Sveriges andel av den nationella koldioxidbudgeten.\n\nInkluderar man partiernas mål för kompletterande åtgärder, där kolinlagring i skog och mark och även bio-CCS (lagring av koldioxid från biobränsle) ingår, så sticker fortfarande Miljöpartiet och Vänsterpartiet ut som mest långtgående, men även Liberalerna utmärker sig, med omfattande mål för bio-CCS.\n\nPM med analysen av partiernas klimatmål i sin helhet finns [här](/Carbon_budgets-Analysis_final.pdf) och en fördjupning om skog och mark finns [här](/Klimatkollen_LULUCF_final.pdf).", + "part2": "För att vara i linje med Parisavtalets 1,5-gradersmål bör Sverige maximalt släppa ut cirka 170 miljoner ton koldioxid. Det utgör Sveriges andel av den globala utsläppsbudgeten för Parisavtalets 1,5-gradersmål, med hänsyn tagen till avtalets rättviseaspekt, där rika och tidigt industrialiserade länder ska gå före.\n\nDen framräknade nationella koldioxidbudgeten ligger även till grund för jämförelsen på [startsidan](/) på Klimatkollen. Läs mer om hur den är beräknad [här](/Paris_compliant_Swedish_CO2_budgets-March_2022-Stoddard&Anderson.pdf) (2023 dokument [här](https://www.cemus.uu.se/wp-content/uploads/2023/12/Paris-compliant-carbon-budgets-for-Swedens-counties-.pdf)). \n\nAnalysen baseras på två frågor i en enkät som skickats till samtliga riksdagspartier under våren. Den första frågan var ”Vilken utsläppsbudget eller klimatmål och takt för utsläppsminskningar vill ert parti se?”. Den andra frågan var ”Står ert parti bakom EU:s nya förslag till mål om att Sverige ska ha en nettoinbindning av kolinlagring från skog och mark på 47,3 miljoner ton CO₂e årligen till 2030?”\n\nAlla partier utom Moderaterna har valt att svara på enkäten. Svaren har sedan analyserats av forskare från nätverket Researchers’ Desk och WWF. Moderaterna är dock med i analysen av klimatmålen, trots att de inte svarade på enkäten, eftersom de tidigare ställt sig bakom Sveriges nuvarande klimatmål och därmed beräknas utifrån det.\n\nKristdemokraternas, Liberalernas, Moderaternas, Socialdemokraternas och Sverigedemokraternas ambitioner ligger i linje med Sveriges nuvarande klimatmål, som kräver en koldioxidbudget på 355 miljoner ton CO₂. Det är cirka 2,1 gånger mer än Sveriges andel av den globala budgeten för Parisavtalets 1,5 gradersmål, med hänsyn tagen till avtalets rättviseaspekt.\n\nCenterpartiets klimatmål är ”netto noll år 2040”. Detta innebär utsläpp av 311 miljoner ton CO₂, vilket är cirka 1,8 gånger mer än den nationella koldioxidbudgeten.\n\nVänsterpartiets klimatmål är ”noll utsläpp till 2035”. Detta medför utsläpp av 217 miljoner ton CO₂, vilket är cirka 1,3 gånger mer än den nationella koldioxidbudgeten.\n\nMiljöpartiets klimatmål är ”en koldioxidbudget på 180 miljoner ton CO₂”. Detta motsvarar att uppnå ”nollutsläpp” för CO₂ till 2034 vilket är cirka 1,1 gånger mer än Sveriges andel av den nationella koldioxidbudgeten.\n\nInkluderar man partiernas mål för kompletterande åtgärder, där kolinlagring i skog och mark och även bio-CCS (lagring av koldioxid från biobränsle) ingår, så sticker fortfarande Miljöpartiet och Vänsterpartiet ut som mest långtgående, men även Liberalerna utmärker sig, med omfattande mål för bio-CCS.\n\nPM med analysen av partiernas klimatmål i sin helhet finns [här](/Carbon_budgets-Analysis_final.pdf) och en fördjupning om skog och mark finns [här](/Klimatkollen_LULUCF_final.pdf).", "title": "Analys av riksdagspartiernas klimatmål – sex av åtta partier missar helt Parisavtalets 1,5-gradersmål" } diff --git a/public/logos/supporters/postkodstiftelsen.svg b/public/logos/supporters/postkodstiftelsen.svg deleted file mode 100644 index a86999c7..00000000 --- a/public/logos/supporters/postkodstiftelsen.svg +++ /dev/null @@ -1,108 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/public/team/alex.jpg b/public/team/alex.jpg new file mode 100644 index 00000000..6967f4c9 Binary files /dev/null and b/public/team/alex.jpg differ diff --git a/utils/createMunicipalityList.tsx b/utils/createMunicipalityList.tsx index bcf15807..331c6d22 100644 --- a/utils/createMunicipalityList.tsx +++ b/utils/createMunicipalityList.tsx @@ -1,4 +1,4 @@ -import { ColumnDef } from '@tanstack/react-table' +import { CellContext, ColumnDef, Row } from '@tanstack/react-table' import { TFunction } from 'next-i18next' import { Municipality, DatasetKey } from './types' @@ -9,40 +9,72 @@ import { type RowData = { name: string - dataPoint: string | number | Date | JSX.Element + dataPoint: string | number | Date formattedDataPoint: string index: number climatePlanYearAdapted?: string procurementLink?: string } -export const calculateClimatePlanRankings = ( - data: Array<{ name: string; dataPoint: string | number | Date | JSX.Element; formattedDataPoint: string }>, -) => data.map((item) => ({ - ...item, - index: item.dataPoint === climatePlanMissing ? 1 : -1, -})) +const getCustomSortFn = ({ + stringsOnTop = false, sortAscending = false, +}: { + stringsOnTop?: boolean, sortAscending?: boolean} = {}) => (a: RowData['dataPoint'], b: RowData['dataPoint']) => { + // Handle NaN values + const aIsNaN = Number.isNaN(a) + const bIsNaN = Number.isNaN(b) + if (aIsNaN && bIsNaN) { + return 0 + } + if (aIsNaN || bIsNaN) { + // eslint-disable-next-line no-nested-ternary + return stringsOnTop ? (aIsNaN ? -1 : 1) : (aIsNaN ? 1 : -1) + } + + // Sort non-NaN values normally + // @ts-expect-error treat Date objects as numbers since they can be compared like numbers. + return sortAscending ? a - b : b - a +} + +const sortClimatePlans = (aVal: string, bVal: string) => { + const a = aVal === climatePlanMissing ? aVal : Number(aVal) + const b = bVal === climatePlanMissing ? bVal : Number(bVal) + + // If both A and B are missing climate plans, then return 0 + if (a === climatePlanMissing && b === climatePlanMissing) { + return 0 + } + + // If A is missing a climate plan, but B has one, then A should be after B, and we should return 1 + if (a === climatePlanMissing && !Number.isNaN(b)) { + return 1 + } + + // If A has a climate plan, but B is missing one, then A should be before B, and we should return -1 + if (!Number.isNaN(a) && b === climatePlanMissing) { + return -1 + } + + // If both A and B have climate plans, then we should compare the years when they were adopted + return (b as number) - (a as number) +} + +const climatePlansSortingFn = (rowA: Row, rowB: Row) => ( + sortClimatePlans(rowA.original.climatePlanYearAdapted!, rowB.original.climatePlanYearAdapted!) +) + +export const calculateClimatePlanRankings = (data: Omit[]) => ( + data + .map((item, i) => ({ ...item, index: i + 1 })) + .sort((rowA, rowB) => sortClimatePlans(rowA.climatePlanYearAdapted!, rowB.climatePlanYearAdapted!)) +) export const calculateRankings = ( data: Array<{ name: string; dataPoint: number; formattedDataPoint: string }>, sortAscending: boolean, stringsOnTop: boolean, ) => { - const customSort = (a: number, b: number) => { - // Handle NaN values - const aIsNaN = Number.isNaN(a) - const bIsNaN = Number.isNaN(b) - if (aIsNaN && bIsNaN) { - return 0 - } - if (aIsNaN || bIsNaN) { - // eslint-disable-next-line no-nested-ternary - return stringsOnTop ? (aIsNaN ? -1 : 1) : (aIsNaN ? 1 : -1) - } - - // Sort non-NaN values normally - return sortAscending ? a - b : b - a - } + const customSort = getCustomSortFn({ sortAscending, stringsOnTop }) const sortedData = data.sort((a, b) => customSort(a.dataPoint, b.dataPoint)) return sortedData.map((item, index) => ({ @@ -105,8 +137,9 @@ export const listColumns = ( return t('startPage:ranking') } - const firstColumnClimatePlans = (index: number, dataPoint: string) => (index === -1 - ? ( + const firstColumnClimatePlans = (dataPoint: string) => (dataPoint === climatePlanMissing + ? t('common:no') + : ( e.stopPropagation()} @@ -117,14 +150,13 @@ export const listColumns = ( {t('common:yes')} ) - : t('common:no') ) - const getFirstColumnCell = (row: { row: { original: RowData } }) => { - const { index, dataPoint } = row.row.original + const getFirstColumnCell = (props: CellContext) => { + const { index, dataPoint } = props.row.original if (isClimatePlan) { - return firstColumnClimatePlans(index, dataPoint as string) + return firstColumnClimatePlans(dataPoint as string) } if (isProcurement) { @@ -134,12 +166,13 @@ export const listColumns = ( return index } - const getThirdColumnCell = (row: { row: { original: RowData } }) => { + const getThirdColumnCell = (props: CellContext) => { const { dataPoint, formattedDataPoint, climatePlanYearAdapted, procurementLink, - } = row.row.original + } = props.row.original if (isClimatePlan) { + // NOTE: We might want to show missing climate plans with a gray text here, and use the orange text to only highight climatePlanYearAdapted return dataPoint !== climatePlanMissing ? climatePlanYearAdapted : climatePlanMissing @@ -163,21 +196,82 @@ export const listColumns = ( return formattedDataPoint } + const getFirstColumnSortingFn = (datasetKey: DatasetKey) => { + if (datasetKey === 'klimatplanerna') { + return (rowA: Row, rowB: Row) => { + const aHasPlan = rowA.original.dataPoint !== climatePlanMissing + const bHasPlan = rowB.original.dataPoint !== climatePlanMissing + + if (aHasPlan && !bHasPlan) { + return -1 + } + + if (!aHasPlan && bHasPlan) { + return 1 + } + + // If both either have plans or don't have plans, we don't need to re-order them. + return 0 + } + } + + // By default, use the standard @tanstack/table sorting functions. + return undefined + } + + // TODO: Move these custom sorting functions to the dataset definitions instead and keep all config together + const getThirdColumnSortingFn = (datasetKey: DatasetKey) => { + if (datasetKey === 'klimatplanerna') { + return climatePlansSortingFn + } + + let customSort: ReturnType | undefined + // TODO: Get the params passed to getCustomSortFn() from config, rather than duplicating and hard coding sortAscending and stringsOnTop + + if (datasetKey === 'koldioxidbudgetarna') { + customSort = getCustomSortFn({ stringsOnTop: true }) + } else if (datasetKey === 'laddarna') { + customSort = getCustomSortFn({ sortAscending: true }) + } + + if (customSort) { + return (rowA: Row, rowB: Row) => customSort!(rowA.original.dataPoint, rowB.original.dataPoint) + } + + // By default, use the standard @tanstack/table sorting functions. + return undefined + } + + const firstColumnSortingFn = getFirstColumnSortingFn(selectedData) + const thirdColumnSortingFn = getThirdColumnSortingFn(selectedData) + return [ { header: getFirstColumnHeader(), - cell: (row) => getFirstColumnCell(row), + cell: getFirstColumnCell, accessorKey: 'index', + + // NOTE: we can't pass an explicit prop sortingFn with the value undefined, since this crashes @tanstack/table when sorting the column + // This is due to a bug either in their implementation or in their TS definitions. + // The workaround is to only add the property when we actually need it. + ...(firstColumnSortingFn ? { sortingFn: firstColumnSortingFn } : {}), }, { header: t('common:municipality'), - cell: (row: { renderValue: () => unknown }) => row.renderValue(), + cell: (row) => row.renderValue(), accessorKey: 'name', }, { header: () => columnHeader, - cell: (row) => getThirdColumnCell(row), + cell: getThirdColumnCell, accessorKey: 'dataPoint', + // NOTE: if we need to sort other columns than the third, this would be better to keep in the dataset definitions. + // But for now, this is a quick and dirty hack + + // NOTE: we can't pass an explicit prop sortingFn with the value undefined, since this crashes @tanstack/table when sorting the column + // This is due to a bug either in their implementation or in their TS definitions. + // The workaround is to only add the property when we actually need it. + ...(thirdColumnSortingFn ? { sortingFn: thirdColumnSortingFn } : {}), }, ] }