diff --git a/app/package-lock.json b/app/package-lock.json index 946a6403c..0b1bf6384 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,12 +1,12 @@ { "name": "city-catalyst", - "version": "0.39.0-dev.0", + "version": "0.40.0-dev.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "city-catalyst", - "version": "0.39.0-dev.0", + "version": "0.40.0-dev.0", "dependencies": { "@ai-sdk/openai": "^1.0.8", "@chakra-ui/icons": "^2.1.0", diff --git a/app/package.json b/app/package.json index 5b4c6b20c..e0ce293a0 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "city-catalyst", - "version": "0.39.0-dev.0", + "version": "0.40.0-dev.0", "private": true, "type": "module", "scripts": { diff --git a/app/src/app/[lng]/[inventory]/InventoryResultTab/EmissionBySectorChart.tsx b/app/src/app/[lng]/[inventory]/InventoryResultTab/EmissionBySectorChart.tsx index 2ebf68ee9..a451d9ea1 100644 --- a/app/src/app/[lng]/[inventory]/InventoryResultTab/EmissionBySectorChart.tsx +++ b/app/src/app/[lng]/[inventory]/InventoryResultTab/EmissionBySectorChart.tsx @@ -1,6 +1,6 @@ import { SectorEmission } from "@/util/types"; import { ResponsiveBar } from "@nivo/bar"; -import { SECTORS } from "@/util/constants"; +import { allSectorColors, SECTORS } from "@/util/constants"; import { convertKgToKiloTonnes, convertKgToTonnes } from "@/util/helpers"; import { useTranslation } from "@/i18n/client"; import { toKebabCaseModified } from "@/app/[lng]/[inventory]/InventoryResultTab/index"; @@ -46,8 +46,6 @@ const EmissionBySectorChart: React.FC = ({ toKebabCaseModified(sector.name), ); - const colors = ["#5785F4", "#F17105", "#25AC4B", "#BFA937", "#F5D949"]; - return (
@@ -80,7 +78,7 @@ const EmissionBySectorChart: React.FC = ({ )} valueScale={{ type: "linear", min: 0, max: "auto" }} indexScale={{ type: "band", round: true }} - colors={colors} + colors={allSectorColors} borderColor={{ from: "color", modifiers: [["darker", 1.6]], @@ -136,7 +134,7 @@ const EmissionBySectorChart: React.FC = ({ > convertKgToTonnes(value), }} - colors={colors} + colors={allSectorColors} tooltip={({ point }) => { const year = point.data.x; const sumOfYs = data.reduce((sum, series) => { @@ -104,6 +110,14 @@ export const EmissionsForecastChart = ({ + + + + + + + + {data.map((series, index) => { const yearData = series.data.find( @@ -113,27 +127,23 @@ export const EmissionsForecastChart = ({ ? ((yearData.y / sumOfYs) * 100).toFixed(2) : 0; const sectorRefNo = getReferenceNumberByName( - point.serieId as keyof ISector, + toKebabCase(point.serieId as string) as keyof ISector, ); - + const yearGrowthRates = + yearData && forecast.growthRates[yearData.x as string]; + const growthRate = yearGrowthRates?.[sectorRefNo!]; return ( - + ); })} + + + - +
{t("sector")}{t("rate")}%{t("total-emissions")}
{series.id} - { - forecast.growthRates[point.data.x as number]?.[ - sectorRefNo! - ] - } - {growthRate} {percentage}% {convertKgToTonnes( @@ -143,12 +153,15 @@ export const EmissionsForecastChart = ({
{t("total")} {convertKgToTonnes(sumOfYs)}
diff --git a/app/src/app/[lng]/[inventory]/InventoryResultTab/TopEmissionsWidget.tsx b/app/src/app/[lng]/[inventory]/InventoryResultTab/TopEmissionsWidget.tsx index b1e648baf..8689e11d2 100644 --- a/app/src/app/[lng]/[inventory]/InventoryResultTab/TopEmissionsWidget.tsx +++ b/app/src/app/[lng]/[inventory]/InventoryResultTab/TopEmissionsWidget.tsx @@ -31,6 +31,7 @@ import { SegmentedProgressValues, } from "@/components/SegmentedProgress"; import { EmptyStateCardContent } from "@/app/[lng]/[inventory]/InventoryResultTab/EmptyStateCardContent"; +import { allSectorColors, SECTORS } from "@/util/constants"; const EmissionsTable = ({ topEmissions, @@ -96,11 +97,12 @@ const TopEmissionsWidget = ({ function getPercentagesForProgress(): SegmentedProgressValues[] { const bySector: SectorEmission[] = results?.totalEmissions.bySector ?? []; - return bySector.map(({ sectorName, co2eq, percentage }) => { + return SECTORS.map(({ name }) => { + const sector = bySector.find((sector) => sector.sectorName === name)!; return { - name: sectorName, - value: co2eq, - percentage: percentage, + name, + value: sector?.co2eq || 0, + percentage: sector?.percentage || 0, } as SegmentedProgressValues; }); } @@ -147,6 +149,7 @@ const TopEmissionsWidget = ({ values={getPercentagesForProgress()} total={results?.totalEmissions.total} t={t} + colors={allSectorColors} showLabels showHover /> diff --git a/app/src/backend/OpenClimateService.ts b/app/src/backend/OpenClimateService.ts index c40fc45f5..4513b4564 100644 --- a/app/src/backend/OpenClimateService.ts +++ b/app/src/backend/OpenClimateService.ts @@ -1,5 +1,6 @@ import { EmissionsForecastData } from "@/util/types"; import { GLOBAL_API_URL } from "@/services/api"; +import { logger } from "@/services/logger"; export type GrowthRatesResponse = Omit; @@ -10,8 +11,7 @@ export const getGrowthRatesFromOC = async ( try { const URL = `${GLOBAL_API_URL}/api/v0/ghgi/emissions_forecast/city/${encodeURIComponent(locode)}/${forecastYear}`; const response = await fetch(URL); - - console.info(`getGrowthRatesFromOC Status: ${response.status}`); + logger.info(`${URL} Response Status: ${response.status}`); const data = await response.json(); return { ...data, diff --git a/app/src/components/Modals/delete-inventory-modal.tsx b/app/src/components/Modals/delete-inventory-modal.tsx index 191f4f017..84cf172b5 100644 --- a/app/src/components/Modals/delete-inventory-modal.tsx +++ b/app/src/components/Modals/delete-inventory-modal.tsx @@ -192,7 +192,7 @@ const DeleteInventoryModal: FC = ({ error={errors.password} register={register} t={t} - name="Password" + name={t("password")} /> - {t(error.message)} + {t(error.message || "")}
)} diff --git a/app/src/components/recent-searches.tsx b/app/src/components/recent-searches.tsx index 8054659aa..48770739c 100644 --- a/app/src/components/recent-searches.tsx +++ b/app/src/components/recent-searches.tsx @@ -1,7 +1,8 @@ import { Box, Text } from "@chakra-ui/react"; +import { TFunction } from "i18next"; import React from "react"; -const RecentSearches = () => { +const RecentSearches = ({ t }: { t: TFunction }) => { const data = [ { id: 1, @@ -31,7 +32,7 @@ const RecentSearches = () => { fontSize="overline" fontFamily="heading" > - RECENT SEARCHES + {t("recent-searches-title")} {hasRecentSearches ? ( @@ -78,7 +79,7 @@ const RecentSearches = () => { lineHeight="24" letterSpacing="wide" > - You have no recent searches + {t("recent-searches-no-results")} )} diff --git a/app/src/components/steps/select-city-steps.tsx b/app/src/components/steps/select-city-steps.tsx index 19e338329..7d71bea56 100644 --- a/app/src/components/steps/select-city-steps.tsx +++ b/app/src/components/steps/select-city-steps.tsx @@ -320,7 +320,7 @@ export default function SelectCityStep({ shadow="2dp" className="h-auto max-h-[272px] transition-all duration-150 overflow-scroll flex flex-col py-3 gap-3 rounded-lg w-full absolute bg-white z-50 mt-2 border border-[1px solid #E6E7FF]" > - {!isLoading && !cityInputQuery && } + {!isLoading && !cityInputQuery && } {isLoading &&

Fetching Cities...

} {isSuccess && cities && diff --git a/app/src/i18n/locales/de/onboarding.json b/app/src/i18n/locales/de/onboarding.json index 4bef4d6af..5f94b27ad 100644 --- a/app/src/i18n/locales/de/onboarding.json +++ b/app/src/i18n/locales/de/onboarding.json @@ -68,5 +68,7 @@ "start-inventory": "Startinventar", "city-boundary-info": "Falls die geografische Grenze nicht korrekt ist <0><1>Kontaktieren Sie uns", "unselected-city-boundary-heading": "Suchen und wählen Sie die Stadt aus, die auf der Karte angezeigt werden soll", - "unselected-city-boundary-description": "Sie können die geografische Grenze für Ihren Bestand überprüfen" + "unselected-city-boundary-description": "Sie können die geografische Grenze für Ihren Bestand überprüfen", + "recent-searches-title": "Letzte Suchen", + "recent-searches-no-results": "Sie haben keine kürzlichen Suchen" } diff --git a/app/src/i18n/locales/en/onboarding.json b/app/src/i18n/locales/en/onboarding.json index 2111505ab..0caddb993 100644 --- a/app/src/i18n/locales/en/onboarding.json +++ b/app/src/i18n/locales/en/onboarding.json @@ -68,5 +68,7 @@ "start-inventory": "Start Inventory", "city-boundary-info": "In case the geographical boundary is not the right one <0><1>Contact Us", "unselected-city-boundary-heading": "Search and select the city to be shown on the map", - "unselected-city-boundary-description": "You will be able to check the geographical boundary for your inventory" + "unselected-city-boundary-description": "You will be able to check the geographical boundary for your inventory", + "recent-searches-title": "Recent Searches", + "recent-searches-no-results": "You have no recent searches" } diff --git a/app/src/i18n/locales/es/onboarding.json b/app/src/i18n/locales/es/onboarding.json index d7911e81b..f64e222f1 100644 --- a/app/src/i18n/locales/es/onboarding.json +++ b/app/src/i18n/locales/es/onboarding.json @@ -69,5 +69,7 @@ "start-inventory": "Inicio de Inventario", "city-boundary-info": "En caso de que el límite geográfico no sea el correcto <0><1>Contáctenos", "unselected-city-boundary-heading": "Busque y seleccione la ciudad que se mostrará en el mapa", - "unselected-city-boundary-description": "Podrá verificar el límite geográfico de su inventario" + "unselected-city-boundary-description": "Podrá verificar el límite geográfico de su inventario", + "recent-searches-title": "Búsquedas recientes", + "recent-searches-no-results": "No tienes búsquedas recientes" } diff --git a/app/src/i18n/locales/pt/onboarding.json b/app/src/i18n/locales/pt/onboarding.json index 5ca971550..9750bf445 100644 --- a/app/src/i18n/locales/pt/onboarding.json +++ b/app/src/i18n/locales/pt/onboarding.json @@ -68,6 +68,7 @@ "start-inventory": "Iniciar Inventário", "city-boundary-info": "Caso a fronteira geográfica não seja a correta <0><1>Contate-nos", "unselected-city-boundary-heading": "Pesquise e selecione a cidade a ser exibida no mapa", - "unselected-city-boundary-description": "Você poderá verificar a fronteira geográfica do seu inventário" - + "unselected-city-boundary-description": "Você poderá verificar a fronteira geográfica do seu inventário", + "recent-searches-title": "Pesquisas Recentes", + "recent-searches-no-results": "Você não tem pesquisas recentes" } diff --git a/app/src/lib/app-theme.ts b/app/src/lib/app-theme.ts index f890dcd94..29613b674 100644 --- a/app/src/lib/app-theme.ts +++ b/app/src/lib/app-theme.ts @@ -1,5 +1,13 @@ import { extendTheme, theme } from "@chakra-ui/react"; +export enum SectorColors { + I = "#575BF4", + II = "#DF2222", + III = "#F28C37", + IV = "#2D0D58", + V = "#CC6B1D", +} + export const appTheme = extendTheme({ colors: { brand: { @@ -15,6 +23,14 @@ export const appTheme = extendTheme({ alternative: "#001EA7", }, + sectors: { + I: SectorColors.I, + II: SectorColors.II, + III: SectorColors.III, + IV: SectorColors.IV, + V: SectorColors.V, + }, + semantic: { success: "#24BE00", successOverlay: "#EFFDE5", diff --git a/app/src/util/constants.ts b/app/src/util/constants.ts index e8feba72a..5af1b4354 100644 --- a/app/src/util/constants.ts +++ b/app/src/util/constants.ts @@ -3,6 +3,7 @@ import { PiPlant, PiTrashLight } from "react-icons/pi"; import { TbBuildingCommunity } from "react-icons/tb"; import { IconBaseProps } from "react-icons"; import { LiaIndustrySolid } from "react-icons/lia"; +import { appTheme, SectorColors } from "@/lib/app-theme"; // Import the appTheme export const maxPopulationYearDifference = 5; @@ -28,6 +29,7 @@ export interface ISector { scopes: number[]; }; }; + color: string; } export const getSectorsForInventory = (inventoryType?: InventoryType) => { @@ -65,6 +67,7 @@ export const SECTORS: ISector[] = [ name: "stationary-energy", description: "stationary-energy-description", icon: TbBuildingCommunity, + color: SectorColors.I, inventoryTypes: { [InventoryTypeEnum.GPC_BASIC]: { scopes: [1, 2] }, [InventoryTypeEnum.GPC_BASIC_PLUS]: { scopes: [1, 2, 3] }, @@ -77,6 +80,7 @@ export const SECTORS: ISector[] = [ name: "transportation", description: "transportation-description", icon: BsTruck, + color: SectorColors.II, inventoryTypes: { [InventoryTypeEnum.GPC_BASIC]: { scopes: [1, 2] }, [InventoryTypeEnum.GPC_BASIC_PLUS]: { scopes: [1, 2, 3] }, @@ -89,6 +93,7 @@ export const SECTORS: ISector[] = [ name: "waste", description: "waste-description", icon: PiTrashLight, + color: SectorColors.III, inventoryTypes: { [InventoryTypeEnum.GPC_BASIC]: { scopes: [1, 3] }, [InventoryTypeEnum.GPC_BASIC_PLUS]: { scopes: [1, 3] }, @@ -101,6 +106,7 @@ export const SECTORS: ISector[] = [ name: "ippu", description: "ippu-description", icon: LiaIndustrySolid, + color: SectorColors.IV, testId: "ippu-sector-card", inventoryTypes: { [InventoryTypeEnum.GPC_BASIC]: { scopes: [] }, @@ -113,6 +119,7 @@ export const SECTORS: ISector[] = [ name: "afolu", description: "afolu-description", icon: PiPlant, + color: SectorColors.V, testId: "afolu-sector-card", inventoryTypes: { [InventoryTypeEnum.GPC_BASIC]: { scopes: [] }, @@ -121,5 +128,11 @@ export const SECTORS: ISector[] = [ }, ]; +export const allSectorColors = SECTORS.map((sector) => { + return sector.color; +}); +export const getSectorByName = (name: string) => + SECTORS.find((s) => s.name === name); + export const getReferenceNumberByName = (name: keyof ISector) => findBy("name", name)?.referenceNumber; diff --git a/global-api/requirements.txt b/global-api/requirements.txt index b717d0f33..834f95a67 100644 --- a/global-api/requirements.txt +++ b/global-api/requirements.txt @@ -5,14 +5,14 @@ fastapi==0.115.6 flake8==7.1.1 fsspec==2024.* geopandas>=0.12,<1.1 -mypy==1.14.0 -osmnx==2.0.0 +mypy==1.14.1 +osmnx==2.0.1 pandas==2.2.3 psycopg2-binary==2.9.10 pydantic-settings==2.* pytest==8.3.4 rioxarray==0.18.* -scipy==1.14.* +scipy==1.15.* shapely==2.0.6 SQLAlchemy==2.0.36 tqdm==4.67.*