From 01688a8ab95737984223f73d06fc807cc37e6d65 Mon Sep 17 00:00:00 2001 From: Tero Tikkanen Date: Wed, 20 Nov 2024 18:56:58 +0200 Subject: [PATCH] Use separateYears better for indirect emissions --- components/paths/graphs/DataTable.tsx | 58 +++++++++++++++---- .../paths/graphs/DimensionalNodePlot.tsx | 12 +++- .../paths/outcome/OutcomeNodeContent.tsx | 20 ++++++- components/paths/toolbar/MediumSettings.tsx | 27 +++++++-- 4 files changed, 98 insertions(+), 19 deletions(-) diff --git a/components/paths/graphs/DataTable.tsx b/components/paths/graphs/DataTable.tsx index 71b4d562..58ba4aa9 100644 --- a/components/paths/graphs/DataTable.tsx +++ b/components/paths/graphs/DataTable.tsx @@ -2,9 +2,18 @@ import { useTranslations } from 'next-intl'; import { Table } from 'reactstrap'; import Styled from 'styled-components'; +import type { OutcomeNodeFieldsFragment } from '@/common/__generated__/paths/graphql'; import { formatNumber } from '@/common/paths/preprocess'; // import { useFeatures } from '@/common/instance'; +interface DataTableProps { + node: OutcomeNodeFieldsFragment; + subNodes?: Node[]; + startYear: number; + endYear: number; + separateYears?: number[]; + goalName?: string; +} const TableWrapper = Styled.div` margin: 0 auto; @@ -19,14 +28,18 @@ const TableWrapper = Styled.div` font-size: 70%; `; -const DataTable = (props) => { - const { node, subNodes, startYear, endYear } = props; +const DataTable = (props: DataTableProps) => { + const { node, subNodes, startYear, endYear, separateYears, goalName } = props; const t = useTranslations(); - const totalHistoricalValues = node.metric.historicalValues.filter( - (value) => value.year >= startYear && value.year <= endYear + const totalHistoricalValues = node.metric.historicalValues.filter((value) => + separateYears + ? separateYears.includes(value.year) + : value.year >= startYear && value.year <= endYear ); - const totalForecastValues = node.metric.forecastValues.filter( - (value) => value.year >= startYear && value.year <= endYear + const totalForecastValues = node.metric.forecastValues.filter((value) => + separateYears + ? separateYears.includes(value.year) + : value.year >= startYear && value.year <= endYear ); //const maximumFractionDigits = useFeatures().maximumFractionDigits ?? undefined; const maximumFractionDigits = 3; @@ -35,17 +48,42 @@ const DataTable = (props) => { totalHistoricalValues.some((val) => val.value !== null) || totalForecastValues.some((val) => val.value !== null); + // Add this function to check if a subNode has any data + const hasData = (subNode) => { + const hasHistoricalData = subNode.metric.historicalValues + .filter((value) => + separateYears + ? separateYears.includes(value.year) + : value.year >= startYear && value.year <= endYear + ) + .some((value) => value.value !== null); + + const hasForecastData = subNode.metric.forecastValues + .filter((value) => + separateYears + ? separateYears.includes(value.year) + : value.year >= startYear && value.year <= endYear + ) + .some((value) => value.value !== null); + + return hasHistoricalData || hasForecastData; + }; + + // Filter subNodes to only include those with data + const validSubNodes = subNodes?.filter(hasData); + return (
- {node.name} ({startYear} - {endYear}) + {node.name}, {goalName} + {separateYears ? '' : ` (${startYear} - ${endYear})`}
- {subNodes?.map((subNode) => ( + {validSubNodes?.map((subNode) => ( ))} {hasTotalValues && } @@ -57,7 +95,7 @@ const DataTable = (props) => { - {subNodes?.map((subNode) => ( + {validSubNodes?.map((subNode) => ( - {subNodes?.map((subNode) => ( + {validSubNodes?.map((subNode) => (
{t('table-year')} {t('table-measure-type')}{subNode.name}{node.metric.name}
{metric.year} {t('table-historical')} {subNode.metric.historicalValues.find( (value) => value.year === metric.year @@ -92,7 +130,7 @@ const DataTable = (props) => {
{metric.year} {t('table-scenario-forecast')} {subNode.metric.forecastValues.find( (value) => value.year === metric.year diff --git a/components/paths/graphs/DimensionalNodePlot.tsx b/components/paths/graphs/DimensionalNodePlot.tsx index e859f930..542bae65 100644 --- a/components/paths/graphs/DimensionalNodePlot.tsx +++ b/components/paths/graphs/DimensionalNodePlot.tsx @@ -558,6 +558,16 @@ export default function DimensionalNodePlot({ const nrYears = usableEndYear - startYear; + const separateYearsConfig: Partial = separateYears + ? { + type: 'category' as const, + data: separateYears, + axisTick: { + alignWithLabel: true, + }, + } + : { range: [`${startYear - 1}-12-31`, `${usableEndYear}-02-01`] }; + const commonXAxisConfig: Partial = { domain: [0, 1], ticklen: 10, @@ -572,7 +582,7 @@ export default function DimensionalNodePlot({ const mainXAxisConfig: Partial = { ...commonXAxisConfig, - range: [`${startYear - 1}-12-31`, `${usableEndYear}-02-01`], + ...separateYearsConfig, }; const referenceXAxisConfig: Partial = { diff --git a/components/paths/outcome/OutcomeNodeContent.tsx b/components/paths/outcome/OutcomeNodeContent.tsx index 5bd24282..26b9a3f8 100644 --- a/components/paths/outcome/OutcomeNodeContent.tsx +++ b/components/paths/outcome/OutcomeNodeContent.tsx @@ -18,7 +18,9 @@ import DimensionalPieGraph from '@/components/paths/graphs/DimensionalPieGraph'; import HighlightValue from '@/components/paths/HighlightValue'; import OutcomeNodeDetails from '@/components/paths/outcome/OutcomeNodeDetails'; import ScenarioBadge from '@/components/paths/ScenarioBadge'; +import { activeGoalVar } from '@/context/paths/cache'; import { usePaths } from '@/context/paths/paths'; +import { useReactiveVar } from '@apollo/client'; const DisplayTab = styled(NavItem)` font-size: 0.9rem; @@ -130,10 +132,16 @@ const OutcomeNodeContent = ({ activeScenario, refetching, }: OutcomeNodeContentProps) => { - //console.log('node', node); const t = useTranslations(); const [activeTabId, setActiveTabId] = useState('graph'); const paths = usePaths(); + + const activeGoal = useReactiveVar(activeGoalVar); + const separateYears = + activeGoal?.dimensions[0].groups[0] === 'indirect' + ? [1990, 2010, 2015, 2020, 2022, 2023] + : null; + const instance = paths?.instance; if (!instance) return null; const showDistribution = subNodes.length > 1; @@ -158,6 +166,7 @@ const OutcomeNodeContent = ({ metric={node.metricDim!} startYear={startYear} endYear={endYear} + separateYears={separateYears} color={color} withControls={false} baselineForecast={node.metric?.baselineForecastValues ?? undefined} @@ -175,7 +184,12 @@ const OutcomeNodeContent = ({ const singleYearGraph = useMemo( () => (
- +
), [node, endYear, color] @@ -332,6 +346,8 @@ const OutcomeNodeContent = ({ ` `; const YearRangeSelector = (props) => { - const { minYear, maxYear, referenceYear } = props; + const { minYear, maxYear, referenceYear, disabled = false } = props; const inputReference = useRef(null); const triggerReference = useRef(null); const [popoverOpen, setPopoverOpen] = useState(false); + + useEffect(() => { + if (disabled) { + setPopoverOpen(false); + yearRangeVar([minYear, maxYear]); + } + }, [disabled, maxYear, minYear]); + const toggle = () => { setPopoverOpen(!popoverOpen); // Focus on the input when the popover is opened @@ -96,20 +104,21 @@ const YearRangeSelector = (props) => { [yearRangeVar] ); const t = useTranslations(); - + const buttonLabel = disabled ? '-' : `${yearRange[0]}–${yearRange[1]}`; if (!yearRange) return
Loading...
; return (
{t('comparing-years')} - {`${yearRange[0]}–${yearRange[1]}`} + {buttonLabel} { const nrGoals = instance.goals.length; const hasMultipleGoals = nrGoals > 1; const columnSizes = getColumnSizes(hasMultipleGoals); + const activeGoal = useReactiveVar(activeGoalVar); + // TODO: This is a hack. We should get this from the backend. + const goalHasSeparateYears = + activeGoal?.dimensions[0].groups[0] === 'indirect' ? true : false; + return ( @@ -172,6 +186,7 @@ const MediumSettings = (props) => { minYear={instance.minimumHistoricalYear} maxYear={instance.modelEndYear} referenceYear={instance.referenceYear} + disabled={goalHasSeparateYears} /> {hasMultipleGoals && (