From 64f291255369ff9442775feb6d52537917f39557 Mon Sep 17 00:00:00 2001 From: Silke Bonnen <95029552+silkeholmebonnen@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:13:43 +0200 Subject: [PATCH] feat(web): add production source legends to data source accordion for carbon and emission chart (#6728) * add production source legends to data sources for carbon and emission chart * add legends to bar-breakdown * fix: assumes is not shown in data sources * add power generation and emission factor to all charts * remove data sources when graph is disabled * fix source alignment * hide when no sources * fix: use not ! instead of == undefined | null * fix: based on tonys comments * fix: create hook useZoneDataSources * renameing * fix: remove productionSourceLegendList from breakdownChart when it is disabled * remove padding and move files around * fix: sort imports --- web/src/features/charts/BreakdownChart.tsx | 73 +++++---- web/src/features/charts/CarbonChart.tsx | 24 ++- web/src/features/charts/DataSources.tsx | 51 ++++++ web/src/features/charts/EmissionChart.tsx | 24 ++- web/src/features/charts/NetExchangeChart.tsx | 2 +- web/src/features/charts/PriceChart.tsx | 2 +- .../ProductionSourceLegend.tsx | 0 .../ProductionSourceLegendList.tsx | 0 .../ProductionsSourceIcons.tsx | 0 .../{bar-breakdown => }/RoundedCard.tsx | 0 .../bar-breakdown/BarBreakdownChart.tsx | 125 +++++++-------- .../BarBreakdownEmissionsChart.tsx | 2 +- .../BarElectricityBreakdownChart.tsx | 2 +- .../charts/bar-breakdown/DataSources.tsx | 25 --- .../features/charts/bar-breakdown/utils.ts | 57 ------- .../charts/hooks/useBreakdownChartData.ts | 24 +-- .../charts/hooks/useCarbonChartData.ts | 9 +- .../charts/hooks/useEmissionChartData.ts | 9 +- .../charts/hooks/useZoneDataSources.ts | 150 ++++++++++++++++++ .../tooltips/AreaGraphTooltipHeader.tsx | 2 +- .../features/panels/zone/MethodologyCard.tsx | 2 +- 21 files changed, 366 insertions(+), 217 deletions(-) create mode 100644 web/src/features/charts/DataSources.tsx rename web/src/features/charts/{bar-breakdown => }/ProductionSourceLegend.tsx (100%) rename web/src/features/charts/{bar-breakdown => }/ProductionSourceLegendList.tsx (100%) rename web/src/features/charts/{bar-breakdown => }/ProductionsSourceIcons.tsx (100%) rename web/src/features/charts/{bar-breakdown => }/RoundedCard.tsx (100%) delete mode 100644 web/src/features/charts/bar-breakdown/DataSources.tsx create mode 100644 web/src/features/charts/hooks/useZoneDataSources.ts diff --git a/web/src/features/charts/BreakdownChart.tsx b/web/src/features/charts/BreakdownChart.tsx index 1334110d22..558b98ad3c 100644 --- a/web/src/features/charts/BreakdownChart.tsx +++ b/web/src/features/charts/BreakdownChart.tsx @@ -2,6 +2,7 @@ import Accordion from 'components/Accordion'; import { max, sum } from 'd3-array'; import Divider from 'features/panels/zone/Divider'; import { CircleBoltIcon } from 'icons/circleBoltIcon'; +import { IndustryIcon } from 'icons/industryIcon'; import { WindTurbineIcon } from 'icons/windTurbineIcon'; import { useTranslation } from 'react-i18next'; import { ElectricityModeType } from 'types'; @@ -10,15 +11,16 @@ import { Mode, TimeAverages, TrackEvent } from 'utils/constants'; import { formatCo2 } from 'utils/formatting'; import { dataSourcesCollapsedBreakdown } from 'utils/state/atoms'; -import { DataSources } from './bar-breakdown/DataSources'; -import ProductionSourceLegendList from './bar-breakdown/ProductionSourceLegendList'; -import { RoundedCard } from './bar-breakdown/RoundedCard'; import { ChartTitle } from './ChartTitle'; +import { DataSources } from './DataSources'; import { DisabledMessage } from './DisabledMessage'; import AreaGraph from './elements/AreaGraph'; import { getBadgeText, getGenerationTypeKey, noop } from './graphUtils'; import useBreakdownChartData from './hooks/useBreakdownChartData'; +import useZoneDataSources from './hooks/useZoneDataSources'; import { NotEnoughDataMessage } from './NotEnoughDataMessage'; +import ProductionSourceLegendList from './ProductionSourceLegendList'; +import { RoundedCard } from './RoundedCard'; import BreakdownChartTooltip from './tooltips/BreakdownChartTooltip'; import { AreaGraphElement } from './types'; @@ -33,7 +35,12 @@ function BreakdownChart({ datetimes, timeAverage, }: BreakdownChartProps) { - const { sources, data, mixMode } = useBreakdownChartData(); + const { data, mixMode } = useBreakdownChartData(); + const { + emissionFactorSources, + powerGenerationSources, + emissionFactorSourcesToProductionSources, + } = useZoneDataSources(); const { t } = useTranslation(); if (!data) { @@ -101,29 +108,41 @@ function BreakdownChart({ dangerouslySetInnerHTML={{ __html: t('country-panel.exchangesAreMissing') }} /> )} - - - { - trackEvent(TrackEvent.DATA_SOURCES_CLICKED, { - chart: displayByEmissions - ? 'emission-origin-chart' - : 'electricity-origin-chart', - }); - }} - title={t('data-sources.title')} - className="text-md" - isCollapsedAtom={dataSourcesCollapsedBreakdown} - > - } - sources={sources} - /> - + {!isBreakdownGraphOverlayEnabled && ( + <> + + + { + trackEvent(TrackEvent.DATA_SOURCES_CLICKED, { + chart: displayByEmissions + ? 'emission-origin-chart' + : 'electricity-origin-chart', + }); + }} + title={t('data-sources.title')} + className="text-md" + isCollapsedAtom={dataSourcesCollapsedBreakdown} + > + } + sources={powerGenerationSources} + /> + } + sources={emissionFactorSources} + emissionFactorSourcesToProductionSources={ + emissionFactorSourcesToProductionSources + } + /> + + + )} ); } diff --git a/web/src/features/charts/CarbonChart.tsx b/web/src/features/charts/CarbonChart.tsx index 526030b1be..0dcc533ed4 100644 --- a/web/src/features/charts/CarbonChart.tsx +++ b/web/src/features/charts/CarbonChart.tsx @@ -2,18 +2,20 @@ import Accordion from 'components/Accordion'; import Divider from 'features/panels/zone/Divider'; import { CloudArrowUpIcon } from 'icons/cloudArrowUpIcon'; import { IndustryIcon } from 'icons/industryIcon'; +import { WindTurbineIcon } from 'icons/windTurbineIcon'; import { useTranslation } from 'react-i18next'; import trackEvent from 'utils/analytics'; import { TimeAverages, TrackEvent } from 'utils/constants'; import { dataSourcesCollapsedEmission } from 'utils/state/atoms'; -import { DataSources } from './bar-breakdown/DataSources'; -import { RoundedCard } from './bar-breakdown/RoundedCard'; import { ChartTitle } from './ChartTitle'; +import { DataSources } from './DataSources'; import AreaGraph from './elements/AreaGraph'; import { getBadgeText, noop } from './graphUtils'; import { useCarbonChartData } from './hooks/useCarbonChartData'; +import useZoneDataSources from './hooks/useZoneDataSources'; import { NotEnoughDataMessage } from './NotEnoughDataMessage'; +import { RoundedCard } from './RoundedCard'; import CarbonChartTooltip from './tooltips/CarbonChartTooltip'; interface CarbonChartProps { @@ -22,8 +24,12 @@ interface CarbonChartProps { } function CarbonChart({ datetimes, timeAverage }: CarbonChartProps) { - const { data, emissionSourceToProductionSource, isLoading, isError } = - useCarbonChartData(); + const { data, isLoading, isError } = useCarbonChartData(); + const { + emissionFactorSources, + powerGenerationSources, + emissionFactorSourcesToProductionSources, + } = useZoneDataSources(); const { t } = useTranslation(); if (isLoading || isError || !data) { @@ -69,10 +75,18 @@ function CarbonChart({ datetimes, timeAverage }: CarbonChartProps) { className="text-md" isCollapsedAtom={dataSourcesCollapsedEmission} > + } + sources={powerGenerationSources} + /> } - sources={[...emissionSourceToProductionSource.keys()].sort()} + sources={emissionFactorSources} + emissionFactorSourcesToProductionSources={ + emissionFactorSourcesToProductionSources + } /> diff --git a/web/src/features/charts/DataSources.tsx b/web/src/features/charts/DataSources.tsx new file mode 100644 index 0000000000..60ef2c2654 --- /dev/null +++ b/web/src/features/charts/DataSources.tsx @@ -0,0 +1,51 @@ +import { ElectricityModeType } from 'types'; + +import ProductionSourceLegend from './ProductionSourceLegend'; + +export function DataSources({ + title, + icon, + sources, + emissionFactorSourcesToProductionSources, +}: { + title: string; + icon: React.ReactNode; + sources: string[]; + emissionFactorSourcesToProductionSources?: { [key: string]: string[] }; +}) { + const showDataSources = Boolean( + (sources && sources?.length > 0) || emissionFactorSourcesToProductionSources + ); + if (showDataSources == false) { + return null; + } + + return ( +
+
+
{icon}
+
{title}
+
+
+ {sources.sort().map((source, index) => ( +
+ {source} + {emissionFactorSourcesToProductionSources && ( + + {emissionFactorSourcesToProductionSources[source]?.map( + (productionSource, index) => ( + + + + ) + )} + + )} +
+ ))} +
+
+ ); +} diff --git a/web/src/features/charts/EmissionChart.tsx b/web/src/features/charts/EmissionChart.tsx index 4e5571b47f..eb6aa45a25 100644 --- a/web/src/features/charts/EmissionChart.tsx +++ b/web/src/features/charts/EmissionChart.tsx @@ -2,18 +2,20 @@ import Accordion from 'components/Accordion'; import Divider from 'features/panels/zone/Divider'; import { CloudArrowUpIcon } from 'icons/cloudArrowUpIcon'; import { IndustryIcon } from 'icons/industryIcon'; +import { WindTurbineIcon } from 'icons/windTurbineIcon'; import { useTranslation } from 'react-i18next'; import trackEvent from 'utils/analytics'; import { TimeAverages, TrackEvent } from 'utils/constants'; import { formatCo2 } from 'utils/formatting'; import { dataSourcesCollapsedEmission } from 'utils/state/atoms'; -import { DataSources } from './bar-breakdown/DataSources'; -import { RoundedCard } from './bar-breakdown/RoundedCard'; import { ChartTitle } from './ChartTitle'; +import { DataSources } from './DataSources'; import AreaGraph from './elements/AreaGraph'; import { getBadgeText, noop } from './graphUtils'; import { useEmissionChartData } from './hooks/useEmissionChartData'; +import useZoneDataSources from './hooks/useZoneDataSources'; +import { RoundedCard } from './RoundedCard'; import EmissionChartTooltip from './tooltips/EmissionChartTooltip'; interface EmissionChartProps { @@ -22,8 +24,12 @@ interface EmissionChartProps { } function EmissionChart({ timeAverage, datetimes }: EmissionChartProps) { - const { data, emissionSourceToProductionSource, isLoading, isError } = - useEmissionChartData(); + const { data, isLoading, isError } = useEmissionChartData(); + const { + emissionFactorSources, + powerGenerationSources, + emissionFactorSourcesToProductionSources, + } = useZoneDataSources(); const { t } = useTranslation(); if (isLoading || isError || !data) { return null; @@ -67,10 +73,18 @@ function EmissionChart({ timeAverage, datetimes }: EmissionChartProps) { className="text-md" isCollapsedAtom={dataSourcesCollapsedEmission} > + } + sources={powerGenerationSources} + /> } - sources={[...emissionSourceToProductionSource.keys()].sort()} + sources={emissionFactorSources} + emissionFactorSourcesToProductionSources={ + emissionFactorSourcesToProductionSources + } /> diff --git a/web/src/features/charts/NetExchangeChart.tsx b/web/src/features/charts/NetExchangeChart.tsx index 67e72d2988..97e2d99d4c 100644 --- a/web/src/features/charts/NetExchangeChart.tsx +++ b/web/src/features/charts/NetExchangeChart.tsx @@ -4,11 +4,11 @@ import { TimeAverages } from 'utils/constants'; import { formatCo2 } from 'utils/formatting'; import { displayByEmissionsAtom, productionConsumptionAtom } from 'utils/state/atoms'; -import { RoundedCard } from './bar-breakdown/RoundedCard'; import { ChartTitle } from './ChartTitle'; import AreaGraph from './elements/AreaGraph'; import { noop } from './graphUtils'; import { useNetExchangeChartData } from './hooks/useNetExchangeChartData'; +import { RoundedCard } from './RoundedCard'; import NetExchangeChartTooltip from './tooltips/NetExchangeChartTooltip'; interface NetExchangeChartProps { diff --git a/web/src/features/charts/PriceChart.tsx b/web/src/features/charts/PriceChart.tsx index 93f3602489..802537b002 100644 --- a/web/src/features/charts/PriceChart.tsx +++ b/web/src/features/charts/PriceChart.tsx @@ -2,13 +2,13 @@ import { CoinsIcon } from 'icons/coinsIcon'; import { useTranslation } from 'react-i18next'; import { TimeAverages } from 'utils/constants'; -import { RoundedCard } from './bar-breakdown/RoundedCard'; import { ChartTitle } from './ChartTitle'; import { DisabledMessage } from './DisabledMessage'; import AreaGraph from './elements/AreaGraph'; import { noop } from './graphUtils'; import { usePriceChartData } from './hooks/usePriceChartData'; import { NotEnoughDataMessage } from './NotEnoughDataMessage'; +import { RoundedCard } from './RoundedCard'; import PriceChartTooltip from './tooltips/PriceChartTooltip'; interface PriceChartProps { diff --git a/web/src/features/charts/bar-breakdown/ProductionSourceLegend.tsx b/web/src/features/charts/ProductionSourceLegend.tsx similarity index 100% rename from web/src/features/charts/bar-breakdown/ProductionSourceLegend.tsx rename to web/src/features/charts/ProductionSourceLegend.tsx diff --git a/web/src/features/charts/bar-breakdown/ProductionSourceLegendList.tsx b/web/src/features/charts/ProductionSourceLegendList.tsx similarity index 100% rename from web/src/features/charts/bar-breakdown/ProductionSourceLegendList.tsx rename to web/src/features/charts/ProductionSourceLegendList.tsx diff --git a/web/src/features/charts/bar-breakdown/ProductionsSourceIcons.tsx b/web/src/features/charts/ProductionsSourceIcons.tsx similarity index 100% rename from web/src/features/charts/bar-breakdown/ProductionsSourceIcons.tsx rename to web/src/features/charts/ProductionsSourceIcons.tsx diff --git a/web/src/features/charts/bar-breakdown/RoundedCard.tsx b/web/src/features/charts/RoundedCard.tsx similarity index 100% rename from web/src/features/charts/bar-breakdown/RoundedCard.tsx rename to web/src/features/charts/RoundedCard.tsx diff --git a/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx b/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx index 7c04ab2e8b..0481900275 100644 --- a/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx +++ b/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx @@ -21,12 +21,13 @@ import { } from 'utils/state/atoms'; import { useBreakpoint } from 'utils/styling'; +import { DataSources } from '../DataSources'; import { determineUnit } from '../graphUtils'; import useBarBreakdownChartData from '../hooks/useBarElectricityBreakdownChartData'; +import useZoneDataSources from '../hooks/useZoneDataSources'; import BreakdownChartTooltip from '../tooltips/BreakdownChartTooltip'; import BarBreakdownEmissionsChart from './BarBreakdownEmissionsChart'; import BarElectricityBreakdownChart from './BarElectricityBreakdownChart'; -import { DataSources } from './DataSources'; import BySource from './elements/BySource'; import EmptyBarBreakdownChart from './EmptyBarBreakdownChart'; import { useHeaderHeight } from './utils'; @@ -46,6 +47,14 @@ function BarBreakdownChart({ isLoading, height, } = useBarBreakdownChartData(); + + const { + capacitySources, + powerGenerationSources, + emissionFactorSources, + emissionFactorSourcesToProductionSources, + } = useZoneDataSources(); + const [displayByEmissions] = useAtom(displayByEmissionsAtom); const { ref, width: observerWidth = 0 } = useResizeObserver(); const { t } = useTranslation(); @@ -104,16 +113,17 @@ function BarBreakdownChart({ setTooltipData(null); }; - const emissionData = [ - ...new Set( - [ - ...Object.values(currentZoneDetail?.dischargeCo2IntensitySources || {}), - ...Object.values(currentZoneDetail?.productionCo2IntensitySources || {}), - ].flatMap((item) => item.split('; ')) - ), - ] - .filter((item) => !item.startsWith('assumes')) - .sort(); + const showPowerSources = Boolean( + powerGenerationSources && powerGenerationSources.length > 0 + ); + const showEmissionSources = Boolean( + emissionFactorSources && emissionFactorSources.length > 0 + ); + const showCapacitySources = Boolean(capacitySources && capacitySources.length > 0); + + const showDataSourceAccordion = Boolean( + showCapacitySources || showPowerSources || showEmissionSources + ); return (
)} - -
- { - trackEvent(TrackEvent.DATA_SOURCES_CLICKED, { chart: 'bar-breakdown-chart' }); - }} - title={t('data-sources.title')} - className="text-md" - isCollapsedAtom={dataSourcesCollapsedBarBreakdown} - > -
- {currentZoneDetail?.capacitySources && ( - } - sources={[ - ...GetSourceArrayFromDictionary(currentZoneDetail?.capacitySources), - ]} - /> - )} - {currentZoneDetail?.source && ( - } - sources={currentZoneDetail?.source} - /> - )} - {emissionData && ( - } - sources={emissionData} - /> - )} -
-
-
+ {showDataSourceAccordion && ( + <> + +
+ { + trackEvent(TrackEvent.DATA_SOURCES_CLICKED, { + chart: 'bar-breakdown-chart', + }); + }} + title={t('data-sources.title')} + className="text-md" + isCollapsedAtom={dataSourcesCollapsedBarBreakdown} + > +
+ } + sources={capacitySources} + /> + } + sources={powerGenerationSources} + /> + } + sources={emissionFactorSources} + emissionFactorSourcesToProductionSources={ + emissionFactorSourcesToProductionSources + } + /> +
+
+
{' '} + + )}
); } export default BarBreakdownChart; - -function GetSourceArrayFromDictionary(sourceDict: { - [key in ElectricityModeType]: string[] | null; -}): Set { - const sourcesWithoutDuplicates: Set = new Set(); - if (sourceDict == null) { - return sourcesWithoutDuplicates; - } - for (const key of Object.keys(sourceDict)) { - const capacitySource = sourceDict?.[key as ElectricityModeType]; - if (capacitySource != null) { - for (const source of capacitySource) { - sourcesWithoutDuplicates.add(source); - } - } - } - return sourcesWithoutDuplicates; -} diff --git a/web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx b/web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx index 20aca982f7..ef4753e871 100644 --- a/web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx +++ b/web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx @@ -7,11 +7,11 @@ import { ElectricityModeType, ZoneDetail, ZoneKey } from 'types'; import { modeColor } from 'utils/constants'; import { formatCo2 } from 'utils/formatting'; +import ProductionSourceLegend from '../ProductionSourceLegend'; import { LABEL_MAX_WIDTH, PADDING_X } from './constants'; import Axis from './elements/Axis'; import HorizontalBar from './elements/HorizontalBar'; import Row from './elements/Row'; -import ProductionSourceLegend from './ProductionSourceLegend'; import { ExchangeDataType, getDataBlockPositions, ProductionDataType } from './utils'; interface BarBreakdownEmissionsChartProps { diff --git a/web/src/features/charts/bar-breakdown/BarElectricityBreakdownChart.tsx b/web/src/features/charts/bar-breakdown/BarElectricityBreakdownChart.tsx index fa64d01b49..fb08e9ae80 100644 --- a/web/src/features/charts/bar-breakdown/BarElectricityBreakdownChart.tsx +++ b/web/src/features/charts/bar-breakdown/BarElectricityBreakdownChart.tsx @@ -10,11 +10,11 @@ import { modeColor, TimeAverages } from 'utils/constants'; import { formatEnergy, formatPower } from 'utils/formatting'; import { timeAverageAtom } from 'utils/state/atoms'; +import ProductionSourceLegend from '../ProductionSourceLegend'; import { LABEL_MAX_WIDTH, PADDING_X } from './constants'; import Axis from './elements/Axis'; import HorizontalBar from './elements/HorizontalBar'; import Row from './elements/Row'; -import ProductionSourceLegend from './ProductionSourceLegend'; import { ExchangeDataType, getDataBlockPositions, diff --git a/web/src/features/charts/bar-breakdown/DataSources.tsx b/web/src/features/charts/bar-breakdown/DataSources.tsx deleted file mode 100644 index eadd135902..0000000000 --- a/web/src/features/charts/bar-breakdown/DataSources.tsx +++ /dev/null @@ -1,25 +0,0 @@ -export function DataSources({ - title, - icon, - sources, -}: { - title: string; - icon: React.ReactNode; - sources: string[]; -}) { - return ( -
-
-
{icon}
-
{title}
-
-
- {sources.map((source, index) => ( -
- {source} -
- ))} -
-
- ); -} diff --git a/web/src/features/charts/bar-breakdown/utils.ts b/web/src/features/charts/bar-breakdown/utils.ts index 9039143531..dc61a930de 100644 --- a/web/src/features/charts/bar-breakdown/utils.ts +++ b/web/src/features/charts/bar-breakdown/utils.ts @@ -208,60 +208,3 @@ export const useHeaderHeight = () => { return headerHeight; }; - -export function getEmissionData(zoneData: ZoneDetails) { - const sourceInfoToProductionSource = new Map(); - - for (const state of Object.values(zoneData.zoneStates)) { - updateMapWithSources( - state.dischargeCo2IntensitySources, - sourceInfoToProductionSource, - true - ); - updateMapWithSources( - state.productionCo2IntensitySources, - sourceInfoToProductionSource - ); - } - - return sourceInfoToProductionSource; -} - -function updateMapWithSources( - sources: { [key: string]: string }, - sourceInfoToProductionSource: Map, - storageType?: boolean -) { - for (const entry of Object.entries(sources)) { - for (const sourceInfo of entry[1].split('; ')) { - const productionSource = getProductionSourcesToAdd( - entry, - sourceInfoToProductionSource.get(sourceInfo), - storageType - ); - if (productionSource.length > 0) { - sourceInfoToProductionSource.set(sourceInfo, productionSource); - } - } - } -} - -function getProductionSourcesToAdd( - entry: [string, string], - productionSourceArray: string[] | undefined, - storageType?: boolean -): string[] { - const productionSource = storageType ? `${entry[0]} storage` : entry[0]; - const sourceInfo = entry[1]; - - if (sourceInfo.startsWith('assumes')) { - return []; - } - if (productionSourceArray === undefined) { - return [productionSource]; - } else if (!productionSourceArray?.includes(productionSource)) { - productionSourceArray?.push(productionSource); - return productionSourceArray; - } - return []; -} diff --git a/web/src/features/charts/hooks/useBreakdownChartData.ts b/web/src/features/charts/hooks/useBreakdownChartData.ts index afe2a6de0c..4cc240b8cb 100644 --- a/web/src/features/charts/hooks/useBreakdownChartData.ts +++ b/web/src/features/charts/hooks/useBreakdownChartData.ts @@ -9,7 +9,6 @@ import { ElectricityStorageKeyType, ElectricityStorageType, ZoneDetail, - ZoneDetails, } from 'types'; import { Mode, @@ -130,23 +129,12 @@ export default function useBreakdownChartData() { layerStroke: undefined, }; - const sources = getSources(zoneData); - - return { sources, data: result, mixMode, isLoading, isError }; -} - -function getSources(zoneData: ZoneDetails) { - const sourceSet = new Set(); - - for (const state of Object.values(zoneData.zoneStates)) { - const currentSources = state.source; - for (const source of currentSources) { - sourceSet.add(source); - } - } - - const sources = [...sourceSet]; - return sources; + return { + data: result, + mixMode, + isLoading, + isError, + }; } function getStorageValue( diff --git a/web/src/features/charts/hooks/useCarbonChartData.ts b/web/src/features/charts/hooks/useCarbonChartData.ts index f7e1d45b0c..0f3efcaa46 100644 --- a/web/src/features/charts/hooks/useCarbonChartData.ts +++ b/web/src/features/charts/hooks/useCarbonChartData.ts @@ -4,7 +4,6 @@ import { useAtom } from 'jotai'; import { getCO2IntensityByMode } from 'utils/helpers'; import { productionConsumptionAtom } from 'utils/state/atoms'; -import { getEmissionData } from '../bar-breakdown/utils'; import { AreaGraphElement } from '../types'; export function useCarbonChartData() { @@ -50,7 +49,9 @@ export function useCarbonChartData() { layerFill, }; - const emissionSourceToProductionSource = getEmissionData(data); - - return { data: result, emissionSourceToProductionSource, isLoading, isError }; + return { + data: result, + isLoading, + isError, + }; } diff --git a/web/src/features/charts/hooks/useEmissionChartData.ts b/web/src/features/charts/hooks/useEmissionChartData.ts index 441ab3dd62..8ddc1a7c85 100644 --- a/web/src/features/charts/hooks/useEmissionChartData.ts +++ b/web/src/features/charts/hooks/useEmissionChartData.ts @@ -4,7 +4,6 @@ import { scaleLinear } from 'd3-scale'; import { useAtom } from 'jotai'; import { productionConsumptionAtom } from 'utils/state/atoms'; -import { getEmissionData } from '../bar-breakdown/utils'; import { getTotalEmissionsAvailable } from '../graphUtils'; import { AreaGraphElement } from '../types'; @@ -46,7 +45,9 @@ export function useEmissionChartData() { layerStroke: undefined, }; - const emissionSourceToProductionSource = getEmissionData(data); - - return { data: result, emissionSourceToProductionSource, isLoading, isError }; + return { + data: result, + isLoading, + isError, + }; } diff --git a/web/src/features/charts/hooks/useZoneDataSources.ts b/web/src/features/charts/hooks/useZoneDataSources.ts new file mode 100644 index 0000000000..6659e30d0d --- /dev/null +++ b/web/src/features/charts/hooks/useZoneDataSources.ts @@ -0,0 +1,150 @@ +import useGetZone from 'api/getZone'; +import { ElectricityModeType, ZoneDetails } from 'types'; + +export default function useZoneDataSources() { + const { data: zoneData, isSuccess } = useGetZone(); + + if (!isSuccess) { + return { + capacitySources: [], + powerGenerationSources: [], + emissionFactorSources: [], + emissionFactorSourcesToProductionSources: {}, + }; + } + + const capacitySources = getCapacitySources(zoneData); + const powerGenerationSources = getPowerGenerationSources(zoneData); + const emissionFactorSourcesToProductionSources = + getEmissionFactorSourcesToProductionSource(zoneData); + const emissionFactorSources = getEmissionFactorSource(zoneData); + + return { + capacitySources, + powerGenerationSources, + emissionFactorSources, + emissionFactorSourcesToProductionSources, + }; +} + +function getCapacitySources(zoneData: ZoneDetails) { + const capacitySources: string[] = []; + for (const state of Object.values(zoneData.zoneStates)) { + const currentSources = extractUniqueSourcesFromDictionary(state.capacitySources); + for (const source of currentSources) { + if (!capacitySources.includes(source)) { + capacitySources.push(source); + } + } + } + return capacitySources; +} + +function extractUniqueSourcesFromDictionary( + sourceDict: + | { + [key in ElectricityModeType]: string[] | null; + } + | undefined +): Set { + const sourcesWithoutDuplicates: Set = new Set(); + if (!sourceDict) { + return sourcesWithoutDuplicates; + } + for (const key of Object.keys(sourceDict)) { + const capacitySource = sourceDict?.[key as ElectricityModeType]; + if (capacitySource != null) { + for (const source of capacitySource) { + sourcesWithoutDuplicates.add(source); + } + } + } + return sourcesWithoutDuplicates; +} + +function getPowerGenerationSources(zoneData: ZoneDetails) { + const sourceSet = new Set(); + + for (const state of Object.values(zoneData.zoneStates)) { + const currentSources = state.source; + for (const source of currentSources) { + sourceSet.add(source); + } + } + + const sources = [...sourceSet]; + return sources; +} + +function getEmissionFactorSource(zoneData: ZoneDetails) { + const emissionFactorSources = new Set(); + + const processSources = (sources: Record) => { + for (const source of Object.entries(sources)) { + for (const emissionFactorSource of source[1].split('; ')) { + if (!emissionFactorSource.startsWith('assumes')) { + emissionFactorSources.add(emissionFactorSource); + } + } + } + }; + + for (const state of Object.values(zoneData.zoneStates)) { + processSources(state.productionCo2IntensitySources); + processSources(state.dischargeCo2IntensitySources); + } + + return [...emissionFactorSources]; +} + +function getEmissionFactorSourcesToProductionSource(zoneData: ZoneDetails) { + const emissionFactorsourceToProductionSource = {}; + + for (const state of Object.values(zoneData.zoneStates)) { + updateEmissionFactorSourcesWithProductionSources( + state.dischargeCo2IntensitySources, + emissionFactorsourceToProductionSource, + true + ); + updateEmissionFactorSourcesWithProductionSources( + state.productionCo2IntensitySources, + emissionFactorsourceToProductionSource + ); + } + + return emissionFactorsourceToProductionSource; +} + +function updateEmissionFactorSourcesWithProductionSources( + sources: { [key: string]: string }, + emissionFactorsourceToProductionSource: { [key: string]: string[] }, + storageType?: boolean +) { + for (const entry of Object.entries(sources)) { + for (const emissionFactorSource of entry[1].split('; ')) { + const productionSources = getProductionSourcesToAdd( + storageType ? `${entry[0]} storage` : entry[0], + emissionFactorsourceToProductionSource[emissionFactorSource], + emissionFactorSource + ); + if (productionSources.length > 0) { + emissionFactorsourceToProductionSource[emissionFactorSource] = productionSources; + } + } + } +} + +function getProductionSourcesToAdd( + productionSource: string, + productionSourceArray: string[] | undefined, + emissionFactorSource: string +): string[] { + if (emissionFactorSource.startsWith('assumes')) { + return []; + } else if (productionSourceArray == undefined) { + return [productionSource]; + } else if (!productionSourceArray?.includes(productionSource)) { + productionSourceArray?.push(productionSource); + } + return productionSourceArray; +} diff --git a/web/src/features/charts/tooltips/AreaGraphTooltipHeader.tsx b/web/src/features/charts/tooltips/AreaGraphTooltipHeader.tsx index e9e59eac37..86a8969252 100644 --- a/web/src/features/charts/tooltips/AreaGraphTooltipHeader.tsx +++ b/web/src/features/charts/tooltips/AreaGraphTooltipHeader.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { TimeAverages } from 'utils/constants'; import { formatDate } from 'utils/formatting'; -import ProductionSourceIcon from '../bar-breakdown/ProductionsSourceIcons'; +import ProductionSourceIcon from '../ProductionsSourceIcons'; interface AreaGraphToolTipHeaderProps { squareColor: string; diff --git a/web/src/features/panels/zone/MethodologyCard.tsx b/web/src/features/panels/zone/MethodologyCard.tsx index 723a25a712..c9a8a596ee 100644 --- a/web/src/features/panels/zone/MethodologyCard.tsx +++ b/web/src/features/panels/zone/MethodologyCard.tsx @@ -1,5 +1,5 @@ import Accordion from 'components/Accordion'; -import { RoundedCard } from 'features/charts/bar-breakdown/RoundedCard'; +import { RoundedCard } from 'features/charts/RoundedCard'; import { t } from 'i18next'; import { EmapsIcon } from 'icons/emapsIcon'; import trackEvent from 'utils/analytics';