From 7675446968304865aea22e80ed962f6010d5cecc Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Thu, 14 Mar 2024 14:09:10 +0100 Subject: [PATCH 01/32] Add support for downscaling based on region population and add regionPopulation --- .../api/v0/datasource/[inventoryId]/route.ts | 35 ++++++++++++++----- app/src/models/Population.ts | 8 +++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/app/src/app/api/v0/datasource/[inventoryId]/route.ts b/app/src/app/api/v0/datasource/[inventoryId]/route.ts index 445032dbd..5e6ce5460 100644 --- a/app/src/app/api/v0/datasource/[inventoryId]/route.ts +++ b/app/src/app/api/v0/datasource/[inventoryId]/route.ts @@ -14,6 +14,14 @@ import { z } from "zod"; import { logger } from "@/services/logger"; import { Publisher } from "@/models/Publisher"; +const downscaledByCountryPopulation = "global_api_downscaled_by_population"; +const downscaledByRegionPopulation = + "global_api_downscaled_by_region_population"; +const populationScalingRetrievalMethods = [ + downscaledByCountryPopulation, + downscaledByRegionPopulation, +]; + export const GET = apiHandler(async (_req: NextRequest, { params }) => { const inventory = await db.models.Inventory.findOne({ where: { inventoryId: params.inventoryId }, @@ -71,12 +79,12 @@ export const GET = apiHandler(async (_req: NextRequest, { params }) => { const applicableSources = DataSourceService.filterSources(inventory, sources); // determine scaling factor for downscaled sources - let populationScaleFactor = 1; + let countryPopulationScaleFactor = 1; + let regionPopulationScaleFactor = 1; let populationIssue: string | null = null; if ( - sources.some( - (source) => - source.retrievalMethod === "global_api_downscaled_by_population", + sources.some((source) => + populationScalingRetrievalMethods.includes(source.retrievalMethod ?? ""), ) ) { const population = await db.models.Population.findOne({ @@ -85,12 +93,18 @@ export const GET = apiHandler(async (_req: NextRequest, { params }) => { year: inventory.year, }, }); - if (!population?.population || !population?.countryPopulation) { + if ( + !population?.population || + !population?.countryPopulation || + !population?.regionPopulation + ) { populationIssue = - "City is missing population/ country population for the inventory year"; + "City is missing population/ region population/ country population for the inventory year"; } else { - populationScaleFactor = + countryPopulationScaleFactor = population.population / population.countryPopulation; + regionPopulationScaleFactor = + population.population / population.regionPopulation; } } @@ -107,8 +121,11 @@ export const GET = apiHandler(async (_req: NextRequest, { params }) => { } let scaleFactor = 1.0; let issue: string | null = null; - if (source.retrievalMethod === "global_api_downscaled_by_population") { - scaleFactor = populationScaleFactor; + if (source.retrievalMethod === downscaledByCountryPopulation) { + scaleFactor = countryPopulationScaleFactor; + issue = populationIssue; + } else if (source.retrievalMethod === downscaledByRegionPopulation) { + scaleFactor = regionPopulationScaleFactor; issue = populationIssue; } return { source, data: { ...data, scaleFactor, issue } }; diff --git a/app/src/models/Population.ts b/app/src/models/Population.ts index 086009cde..bdd8ba814 100644 --- a/app/src/models/Population.ts +++ b/app/src/models/Population.ts @@ -7,6 +7,7 @@ export interface PopulationAttributes { cityId: string; population?: number; countryPopulation?: number; + regionPopulation?: number; year: number; created?: Date; lastUpdated?: Date; @@ -18,6 +19,7 @@ export type PopulationId = Population[PopulationPk]; export type PopulationOptionalAttributes = | "population" | "countryPopulation" + | "regionPopulation" | "created" | "lastUpdated" | "datasourceId"; @@ -33,6 +35,7 @@ export class Population cityId!: string; population?: number; countryPopulation?: number; + regionPopulation?: number; year!: number; created?: Date; lastUpdated?: Date; @@ -74,6 +77,11 @@ export class Population allowNull: true, field: "country_population", }, + regionPopulation: { + type: DataTypes.BIGINT, + allowNull: true, + field: "region_population", + }, year: { type: DataTypes.INTEGER, allowNull: false, From b2c462849c1b9f26c320ddd0464c98642ca85ed6 Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Thu, 14 Mar 2024 14:17:56 +0100 Subject: [PATCH 02/32] Allow population entries from +-5 years of the inventory year --- .../api/v0/datasource/[inventoryId]/route.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/src/app/api/v0/datasource/[inventoryId]/route.ts b/app/src/app/api/v0/datasource/[inventoryId]/route.ts index 5e6ce5460..f2aeeadc7 100644 --- a/app/src/app/api/v0/datasource/[inventoryId]/route.ts +++ b/app/src/app/api/v0/datasource/[inventoryId]/route.ts @@ -14,6 +14,7 @@ import { z } from "zod"; import { logger } from "@/services/logger"; import { Publisher } from "@/models/Publisher"; +const maxPopulationYearDifference = 5; const downscaledByCountryPopulation = "global_api_downscaled_by_population"; const downscaledByRegionPopulation = "global_api_downscaled_by_region_population"; @@ -90,16 +91,24 @@ export const GET = apiHandler(async (_req: NextRequest, { params }) => { const population = await db.models.Population.findOne({ where: { cityId: inventory.cityId, - year: inventory.year, + year: { + [Op.between]: [ + inventory.year! - maxPopulationYearDifference, + inventory.year! + maxPopulationYearDifference, + ], + }, }, + order: [["year", "DESC"]], // favor more recent population entries }); + // TODO allow country downscaling to work if there is no region population? if ( - !population?.population || - !population?.countryPopulation || - !population?.regionPopulation + !population || + !population.population || + !population.countryPopulation || + !population.regionPopulation ) { - populationIssue = - "City is missing population/ region population/ country population for the inventory year"; + // City is missing population/ region population/ country population for the inventory year + populationIssue = "missing_population"; } else { countryPopulationScaleFactor = population.population / population.countryPopulation; From da8e2892e8ee1b6f466b43c08ed214303231d78c Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Thu, 14 Mar 2024 18:56:03 +0100 Subject: [PATCH 03/32] Add city, region and country population to onboarding setup step --- app/src/app/[lng]/onboarding/setup/page.tsx | 231 ++++++++++++++++++-- app/src/i18n/locales/en/onboarding.json | 14 +- 2 files changed, 222 insertions(+), 23 deletions(-) diff --git a/app/src/app/[lng]/onboarding/setup/page.tsx b/app/src/app/[lng]/onboarding/setup/page.tsx index d691d2a63..453235de9 100644 --- a/app/src/app/[lng]/onboarding/setup/page.tsx +++ b/app/src/app/[lng]/onboarding/setup/page.tsx @@ -18,6 +18,7 @@ import { getShortenNumberUnit, shortenNumber } from "@/util/helpers"; import { ArrowBackIcon, CheckIcon, + InfoIcon, InfoOutlineIcon, SearchIcon, } from "@chakra-ui/icons"; @@ -28,8 +29,10 @@ import { Card, Flex, FormControl, + FormErrorIcon, FormErrorMessage, FormLabel, + HStack, Heading, Icon, Input, @@ -59,6 +62,12 @@ const CityMap = dynamic(() => import("@/components/CityMap"), { ssr: false }); type Inputs = { city: string; year: number; + cityPopulation: number; + cityPopulationYear: number; + regionPopulation: number; + regionPopulationYear: number; + countryPopulation: number; + countryPopulationYear: number; }; function SetupStep({ @@ -66,42 +75,44 @@ function SetupStep({ register, t, setValue, + watch, }: { errors: FieldErrors; register: UseFormRegister; t: TFunction; setValue: any; + watch: Function; }) { const currentYear = new Date().getFullYear(); const years = Array.from({ length: 7 }, (_x, i) => currentYear - i); + const dispatch = useAppDispatch(); const [onInputClicked, setOnInputClicked] = useState(false); const [cityInputQuery, setCityInputQuery] = useState(""); const [isCityNew, setIsCityNew] = useState(false); - const [isYearSelected, setIsYearSelected] = useState(false); - const [yearValue, setYearValue] = useState(); - const dispatch = useAppDispatch(); + + const yearValue = watch("year"); + const cityPopulationYear = watch("cityPopulationYear"); + const regionPopulationYear = watch("regionPopulationYear"); + const countryPopulationYear = watch("countryPopulationYear"); const handleInputOnChange = (e: React.ChangeEvent) => { e.preventDefault(); setCityInputQuery(e.target.value); setOnInputClicked(true); + setValue("city", e.target.value); }; const handleSetCity = (city: OCCityArributes) => { setCityInputQuery(city.name); setOnInputClicked(false); dispatch(set(city)); + setValue("city", city.name); // TODO: chech whether city exists or not setIsCityNew(true); }; - const handleYear = (e: any) => { - setIsYearSelected(true); - setYearValue(e.target.value); - }; - useEffect(() => { setValue("city", cityInputQuery); }, [cityInputQuery, setValue]); @@ -111,10 +122,7 @@ function SetupStep({ setOnInputClicked(false); setIsCityNew(false); } - if (!yearValue) { - setIsYearSelected(false); - } - }, [cityInputQuery, yearValue]); + }, [cityInputQuery]); // import custom redux hooks const { @@ -144,16 +152,16 @@ function SetupStep({ return ( <> -
+ {t("setup-heading")} {t("setup-details")} -
-
+ + -
- + + {t("select-city")} @@ -162,7 +170,6 @@ function SetupStep({ )} @@ -237,7 +244,6 @@ function SetupStep({ {...register("year", { required: t("inventory-year-required"), })} - onChange={handleYear} > {years.map((year: number, i: number) => ( + + + + {t("information-required")} + + + + + {t("city-population-title")} + + + + {errors.cityPopulation && errors.cityPopulation.message} + + + + + {t("population-year")} + + + + {cityPopulationYear && ( + + )} + + + + + {errors.cityPopulationYear && + errors.cityPopulationYear.message} + + + + + + {t("region-population-title")} + + + + {errors.regionPopulation && errors.regionPopulation.message} + + + + {t("population-year")} + + + + {regionPopulationYear && ( + + )} + + + + + {errors.regionPopulationYear && + errors.regionPopulationYear.message} + + + + + + {t("country-population-title")} + + + + {errors.countryPopulation && errors.countryPopulation.message} + + + + {t("population-year")} + + + + {countryPopulationYear && ( + + )} + + + + + {errors.countryPopulationYear && + errors.countryPopulationYear.message} + + +
{t("gpc-basic-message")} -
+ ); } @@ -366,6 +551,7 @@ export default function OnboardingSetup({ register, getValues, setValue, + watch, formState: { errors, isSubmitting }, } = useForm(); @@ -539,6 +725,7 @@ export default function OnboardingSetup({ errors={errors} setValue={setValue} register={register} + watch={watch} t={t} /> )} diff --git a/app/src/i18n/locales/en/onboarding.json b/app/src/i18n/locales/en/onboarding.json index bc0557cf5..d94be2649 100644 --- a/app/src/i18n/locales/en/onboarding.json +++ b/app/src/i18n/locales/en/onboarding.json @@ -28,5 +28,17 @@ "done-heading": "Your City Inventory Profile
Was Successfully Created", "inventory-title": "GPC Basic Emission Inventory - Year {{year}}", "done-details": "You created your city profile to start your GPC Basic GHG inventory.", - "check-dashboard": "Check Dashboard" + "check-dashboard": "Check Dashboard", + + "information-required": "This information is required and affects your GHG inventory accuracy.\nPlease use data from the year closest to your selected inventory year.", + "year-placeholder": "Year", + "required": "Required", + "population-required": "Please enter value & year for population", + "city-population-title": "City population", + "city-population-placeholder": "City population number", + "population-year": "Population year", + "region-population-title": "Region population", + "region-population-placeholder": "Region population number", + "country-population-title": "Country population", + "country-population-placeholder": "Country population number" } From c92a6dd18da59f1aa0b4c1e0d323b6a3c8699594 Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Thu, 14 Mar 2024 18:57:19 +0100 Subject: [PATCH 04/32] Use NEXT_PUBLIC_OPENCLIMATE_API_URL instead so it's accessible in frontend --- app/src/services/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/services/api.ts b/app/src/services/api.ts index e902ece3b..663dea206 100644 --- a/app/src/services/api.ts +++ b/app/src/services/api.ts @@ -383,7 +383,7 @@ export const api = createApi({ export const openclimateAPI = createApi({ reducerPath: "openclimateapi", baseQuery: fetchBaseQuery({ - baseUrl: process.env.OPENCLIMATE_API_URL, + baseUrl: process.env.NEXT_PUBLIC_OPENCLIMATE_API_URL, }), endpoints: (builder) => ({ getOCCity: builder.query({ From ecb1bd668862f850c6498e2b9b07314450e2dd65 Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Thu, 14 Mar 2024 18:59:35 +0100 Subject: [PATCH 05/32] Change env var names in k8s config and env.example --- app/env.example | 1 + k8s/cc-web-deploy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/env.example b/app/env.example index 3a6cb9c49..d969549ca 100644 --- a/app/env.example +++ b/app/env.example @@ -17,3 +17,4 @@ VERIFICATION_TOKEN_SECRET="80c70dfdeedf2c01757b880d39c79214e915c786dd48d5473c9c0 HUGGINGFACE_API_KEY=hf_MY_SECRET_KEY OPENAI_API_KEY=sk-MY_SECRET_KEY CHAT_PROVIDER=huggingface +NEXT_PUBLIC_OPENCLIMATE_API_URL="https://openclimate.openearth.dev" diff --git a/k8s/cc-web-deploy.yml b/k8s/cc-web-deploy.yml index 20c5fdb37..e4e08a968 100644 --- a/k8s/cc-web-deploy.yml +++ b/k8s/cc-web-deploy.yml @@ -46,7 +46,7 @@ spec: value: "587" - name: GLOBAL_API_URL value: "https://ccglobal.openearth.dev" - - name: OPENCLIMATE_API_URL + - name: NEXT_PUBLIC_OPENCLIMATE_API_URL value: "https://openclimate.openearth.dev" resources: limits: From bd39f61f92978f8995d6a8a70fbdcf8980da4ba5 Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Mon, 18 Mar 2024 11:56:26 +0100 Subject: [PATCH 06/32] Creat migration that adds region_population to Population table --- .../20240318105130-region-population.cjs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 app/migrations/20240318105130-region-population.cjs diff --git a/app/migrations/20240318105130-region-population.cjs b/app/migrations/20240318105130-region-population.cjs new file mode 100644 index 000000000..cb3e1f097 --- /dev/null +++ b/app/migrations/20240318105130-region-population.cjs @@ -0,0 +1,16 @@ +"use strict"; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn( + "Population", + "region_population", + Sequelize.BIGINT, + ); + }, + + async down(queryInterface) { + await queryInterface.removeColumn("Population", "region_population"); + }, +}; From e440b2d2984d24a33a2b0da9c4190e11cd1fcc67 Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Mon, 18 Mar 2024 12:12:45 +0100 Subject: [PATCH 07/32] Reformat information text on onboarding setup page and add spanish translation keys --- app/src/app/[lng]/onboarding/setup/page.tsx | 21 ++++++++++----------- app/src/i18n/locales/en/onboarding.json | 4 ++-- app/src/i18n/locales/es/onboarding.json | 18 +++++++++++++++--- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/src/app/[lng]/onboarding/setup/page.tsx b/app/src/app/[lng]/onboarding/setup/page.tsx index 453235de9..80271c5d2 100644 --- a/app/src/app/[lng]/onboarding/setup/page.tsx +++ b/app/src/app/[lng]/onboarding/setup/page.tsx @@ -18,7 +18,6 @@ import { getShortenNumberUnit, shortenNumber } from "@/util/helpers"; import { ArrowBackIcon, CheckIcon, - InfoIcon, InfoOutlineIcon, SearchIcon, } from "@chakra-ui/icons"; @@ -266,16 +265,6 @@ function SetupStep({ {errors.year && errors.year.message} - - - - {t("information-required")} - - {t("city-population-title")} @@ -445,6 +434,16 @@ function SetupStep({ + + + + {t("information-required")} + + diff --git a/app/src/i18n/locales/en/onboarding.json b/app/src/i18n/locales/en/onboarding.json index d94be2649..d19d7a666 100644 --- a/app/src/i18n/locales/en/onboarding.json +++ b/app/src/i18n/locales/en/onboarding.json @@ -14,7 +14,7 @@ "inventory-year": "Inventory year", "inventory-year-placeholder": "Select year", "inventory-year-required": "Year is required", - "gpc-basic-message": "Only GPC Basic Inventories are supported momentarily", + "gpc-basic-message": "Only GPC Basic Inventories are supported momentarily. For population values the closest year to the city inventory is recommended, as it will be used for calculations and included in your GPC report.", "save-button": "Save and Continue", "search-city-button": "Search for another City", "confirm-button": "Confirm and Continue", @@ -30,7 +30,7 @@ "done-details": "You created your city profile to start your GPC Basic GHG inventory.", "check-dashboard": "Check Dashboard", - "information-required": "This information is required and affects your GHG inventory accuracy.\nPlease use data from the year closest to your selected inventory year.", + "information-required": "This information is required and affects your GHG inventory accuracy.", "year-placeholder": "Year", "required": "Required", "population-required": "Please enter value & year for population", diff --git a/app/src/i18n/locales/es/onboarding.json b/app/src/i18n/locales/es/onboarding.json index 3bf04c033..d6ef3f086 100644 --- a/app/src/i18n/locales/es/onboarding.json +++ b/app/src/i18n/locales/es/onboarding.json @@ -14,13 +14,13 @@ "inventory-year": "Año del inventario", "inventory-year-placeholder": "Seleccionar año", "inventory-year-required": "El año es requerido", - "gpc-basic-message": "Solo los inventarios básicos de GPC son compatibles momentáneamente", + "gpc-basic-message": "Solo los inventarios básicos de GPC son compatibles momentáneamente. Utilice datos del año más cercano al año de inventario seleccionado.", "save-button": "Guardar y Continuar", "search-city-button": "Buscar otra Ciudad", "confirm-button": "Confirmar y Continuar", "confirm-heading": "Confirmar Información de la Ciudad", - "confirm-details": "Revisa y confirma esta información sobre tu ciudad. Si hay un error, por favor envíanos un correo electrónico para editarlo.

Utilizamos <2>fuentes de datos abiertos para completar previamente el perfil de la ciudad.", + "confirm-details": "Revisa y confirma esta información sobre tu ciudad. Si hay un error, por favor envíanos un correo electrónico para editarlo.

Utilizamos <2>fuentes de datos abiertos para completar previamente el perfil de la ciudad.", "total-population": "Población total", "total-land-area": "Superficie total del terreno", "geographical-boundaries": "Límites geográficos", @@ -28,5 +28,17 @@ "done-heading": "Inventario creado con éxito
", "inventory-title": "Inventario Básico de Emisiones GPC - Año {{year}}", "done-details": "Creaste tu perfil de ciudad para comenzar tu inventario de GEI con metodología GPC Básico.", - "check-dashboard": "Ir al Inicio" + "check-dashboard": "Ir al Inicio", + + "information-required": "Esta información es obligatoria y afecta la precisión de su inventario de GEI.", + "year-placeholder": "Año", + "required": "Necesario", + "population-required": "Por favor ingrese el valor y el año de la población", + "city-population-title": "Población de la ciudad", + "city-population-placeholder": "Número de población de la ciudad", + "population-year": "Año de población", + "region-population-title": "Población de la región", + "region-population-placeholder": "Número de población de la región", + "country-population-title": "Población del país", + "country-population-placeholder": "Número de población del país" } From d4e672f61deecd706f9239a044afdcf6ba030278 Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Mon, 18 Mar 2024 12:15:53 +0100 Subject: [PATCH 08/32] Add regionPopulation to POST population route validation --- app/src/util/validation.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/util/validation.ts b/app/src/util/validation.ts index f8f63785d..3392faf0e 100644 --- a/app/src/util/validation.ts +++ b/app/src/util/validation.ts @@ -106,6 +106,7 @@ export type CreateUserRequest = z.infer; export const createPopulationRequest = z.object({ cityId: z.string().uuid(), population: z.number().optional(), + regionPopulation: z.number().optional(), countryPopulation: z.number().optional(), year: z.number(), datasourceId: z.string().optional(), From ed1420e9b84d04cf1da6f15b9bbedcd3d55f7b13 Mon Sep 17 00:00:00 2001 From: Milan Gruner Date: Mon, 18 Mar 2024 12:47:21 +0100 Subject: [PATCH 09/32] Add region population and find closest year instead of exact match --- app/src/app/[lng]/onboarding/setup/page.tsx | 87 ++++++++++++++++----- app/src/services/api.ts | 1 + 2 files changed, 69 insertions(+), 19 deletions(-) diff --git a/app/src/app/[lng]/onboarding/setup/page.tsx b/app/src/app/[lng]/onboarding/setup/page.tsx index 80271c5d2..f671b92dd 100644 --- a/app/src/app/[lng]/onboarding/setup/page.tsx +++ b/app/src/app/[lng]/onboarding/setup/page.tsx @@ -465,7 +465,7 @@ function ConfirmStep({ t: TFunction; locode: string; area: number; - population: number; + population?: number; }) { return ( <> @@ -537,6 +537,32 @@ function ConfirmStep({ ); } +type PopulationEntry = { + year: number; + population: number; + datasource_id: string; +}; + +type PopulationData = { + year?: number; + population?: number; + regionPopulation?: number; + regionYear?: number; + countryPopulation?: number; + countryYear?: number; + datasourceId?: string; +}; + +function findClosestYear( + populationData: PopulationEntry[] | undefined, + year: number, +): PopulationEntry | undefined { + const sortedEntries = populationData?.sort((a, b) => { + return Math.abs(year - a.year) - Math.abs(year - b.year); + }); + return sortedEntries?.[0]; +} + export default function OnboardingSetup({ params: { lng }, }: { @@ -551,7 +577,7 @@ export default function OnboardingSetup({ getValues, setValue, watch, - formState: { errors, isSubmitting }, + formState: { errors, isSubmitting, isValid }, } = useForm(); const steps = [{ title: t("setup-step") }, { title: t("confirm-step") }]; @@ -572,12 +598,7 @@ export default function OnboardingSetup({ }>({ name: "", locode: "", year: -1 }); const [isConfirming, setConfirming] = useState(false); - const [populationData, setPopulationData] = useState<{ - year: number; - population: number; - datasourceId: string; - }>({ year: 0, population: 0, datasourceId: "" }); - const [countryPopulation, setCountryPopulation] = useState(0); + const [populationData, setPopulationData] = useState({}); const storedData = useAppSelector((state) => state.openClimateCity); @@ -601,9 +622,14 @@ export default function OnboardingSetup({ }); const countryLocode = data.locode.length > 0 ? data.locode.split(" ")[0] : null; + const regionLocode = cityData?.data.region; + console.log("Region locode", regionLocode); const { data: countryData } = useGetOCCityDataQuery(countryLocode!, { skip: !countryLocode, }); + const { data: regionData } = useGetOCCityDataQuery(regionLocode!, { + skip: !regionLocode, + }); const makeErrorToast = (title: string, description?: string) => { toast({ @@ -624,13 +650,11 @@ export default function OnboardingSetup({ useEffect(() => { if (cityData) { - const population = cityData?.data.population.filter( - (item: any) => item.year === data.year, - ); + const population = findClosestYear(cityData?.data.population, data.year); const populationObject = { - year: population[0]?.year, - population: population[0]?.population, - datasourceId: population[0]?.datasource_id, + year: population?.year, + population: population?.population, + datasourceId: population?.datasource_id, }; setPopulationData(populationObject); @@ -652,13 +676,36 @@ export default function OnboardingSetup({ useEffect(() => { if (countryData) { - const population = countryData?.data.population.filter( - (item: any) => item.year === data.year, + const population = findClosestYear( + countryData?.data.population, + data.year, ); - setCountryPopulation(population[0]?.population); + const newPopulation = { + ...populationData, + countryPopulation: population?.population, + }; + setPopulationData(newPopulation); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [countryData, data.year]); + useEffect(() => { + if (regionData) { + const population = findClosestYear(regionData.data.population, data.year); + const newPopulation = { + ...populationData, + regionPopulation: population?.population, + }; + setPopulationData(newPopulation); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [regionData, data.year]); + + // TODO update form with population values from API + const cityPopulation = watch("cityPopulation"); + const regionPopulation = watch("regionPopulation"); + const countryPopulation = watch("countryPopulation"); + const onConfirm = async () => { // save data in backend setConfirming(true); @@ -674,8 +721,9 @@ export default function OnboardingSetup({ await addCityPopulation({ cityId: city.cityId, locode: city.locode!, - population: populationData.population, - countryPopulation: countryPopulation, + population: cityPopulation!, + regionPopulation: regionPopulation!, + countryPopulation: countryPopulation!, year: data.year, }).unwrap(); } catch (err: any) { @@ -749,6 +797,7 @@ export default function OnboardingSetup({