diff --git a/packages/client/src/lib/time.ts b/packages/client/src/lib/time.ts
index 8883d3c1..bfdec718 100644
--- a/packages/client/src/lib/time.ts
+++ b/packages/client/src/lib/time.ts
@@ -2,7 +2,7 @@ import { differenceInDays } from "date-fns";
import { ENERGY_SHIFT_TARGET_YEAR } from "../modules/common/constants";
import { userLocale } from "../modules/translations";
-export { formatDate, getDaysTo2050, getDaysToEnergyShiftTargetYear };
+export { formatDate, getDaysToEnergyShiftTargetYear };
type DateFormat = "date-at-time" | "full-date-at-time";
@@ -37,14 +37,6 @@ function formatDate(
).format(new Date(date));
}
-// TODO: Replace use with getDaysToEnergyShiftTargetYear.
-/**
- * @deprecated Use getDaysToEnergyShiftTargetYear instead.
- */
-function getDaysTo2050() {
- return Math.round(differenceInDays(new Date("01/01/2050"), new Date()));
-}
-
function getDaysToEnergyShiftTargetYear(refDate: Date = new Date()) {
return Math.round(
differenceInDays(new Date(`01/01/${ENERGY_SHIFT_TARGET_YEAR}`), refDate)
diff --git a/packages/client/src/modules/charts/DetailsEnergyBars.tsx b/packages/client/src/modules/charts/DetailsEnergyBars.tsx
index 2622c621..23facf8d 100644
--- a/packages/client/src/modules/charts/DetailsEnergyBars.tsx
+++ b/packages/client/src/modules/charts/DetailsEnergyBars.tsx
@@ -9,12 +9,10 @@ import {
Cell,
} from "recharts";
import { EnergyPalette, ProductionPalette } from "../../utils/theme";
-import { hasNuclear, roundValue } from "../common/utils";
+import { roundValue } from "../common/utils";
import { ConsumptionDatum } from "../persona/consumption";
import { Persona } from "../persona/persona";
import { ProductionDatum } from "../persona/production";
-import { productionConstants } from "../play";
-import { usePlay } from "../play/context/playContext";
import { translateName, useTranslation } from "../translations";
import { Typography } from "../common/components/Typography";
@@ -44,20 +42,10 @@ function DetailsEnergyProductionBars({
persona: Persona;
}) {
const theme = useTheme();
- const { game } = usePlay();
-
- const personaValues = persona.production.filter(
- ({ type }: ProductionDatum) => {
- if (!hasNuclear(game) && type === productionConstants.NUCLEAR.name) {
- return false;
- }
- return true;
- }
- );
return DetailsEnergyBars(
"production",
- personaValues,
+ persona.productionDisplayed,
theme.palette.production,
title
);
diff --git a/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceDetailsForPlayerGraph.tsx b/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceDetailsForPlayerGraph.tsx
new file mode 100644
index 00000000..3e2ef394
--- /dev/null
+++ b/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceDetailsForPlayerGraph.tsx
@@ -0,0 +1,52 @@
+import React, { useMemo } from "react";
+import { Persona } from "../../persona/persona";
+import { useTranslation } from "../../translations/useTranslation";
+import {
+ DetailsEnergyConsumptionBars,
+ DetailsEnergyProductionBars,
+} from "../DetailsEnergyBars";
+import {
+ EnergyConsumptionButtons,
+ EnergyProductionButtons,
+} from "../../common/components/EnergyButtons";
+import { StepDatum } from "./types";
+import { getStackName } from "./utils";
+
+export { EnergyBalanceDetailsForPlayerGraph };
+
+function EnergyBalanceDetailsForPlayerGraph({
+ stepDatum,
+ getPersonaAtStep,
+}: {
+ stepDatum: StepDatum;
+ getPersonaAtStep: (step: number) => Persona;
+}) {
+ const { t } = useTranslation();
+
+ const DetailsContent = useMemo(() => {
+ const persona = getPersonaAtStep(stepDatum.step);
+
+ const graphTitle = t(
+ "page.player.statistics.tabs.energy-balance.graphs.details.title",
+ { stackName: getStackName({ stepDatum, t }) }
+ );
+
+ if (stepDatum.type === "consumption") {
+ return (
+ <>
+
+
+ >
+ );
+ } else if (stepDatum.type === "production") {
+ return (
+ <>
+
+
+ >
+ );
+ }
+ }, [stepDatum, getPersonaAtStep, t]);
+
+ return <>{DetailsContent}>;
+}
diff --git a/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceForPlayerChart.tsx b/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceForPlayerChart.tsx
new file mode 100644
index 00000000..a636fd49
--- /dev/null
+++ b/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceForPlayerChart.tsx
@@ -0,0 +1,99 @@
+import { Box } from "@mui/material";
+import _ from "lodash";
+import React, { useMemo, useState } from "react";
+import {
+ StackedBars,
+ StackedBarsStackData,
+ StackedBarsStacks,
+} from "../StackedBars";
+import { Persona } from "../../persona/persona";
+import { STEPS, getStepTypes } from "../../play";
+import { formatProduction } from "../../../lib/formatter";
+import { usePlay } from "../../play/context/playContext";
+import { useTranslation } from "../../translations/useTranslation";
+import { StepDatum } from "./types";
+import {
+ computeConsumptionBarsForPersona,
+ computeProductionBarsForPersona,
+ getStackName,
+} from "./utils";
+import { EnergyBalanceDetailsForPlayerGraph } from "./EnergyBalanceDetailsForPlayerGraph";
+import { buildStack } from "../utils";
+
+export { EnergyBalanceForPlayerChart };
+
+function EnergyBalanceForPlayerChart({
+ getPersonaAtStep,
+}: {
+ getPersonaAtStep: (step: number) => Persona;
+}) {
+ const { t } = useTranslation();
+ const { game } = usePlay();
+ const [selectedStepDatum, setSelectedStepDatum] = useState(
+ null
+ );
+
+ const stepDataToDisplay = useMemo((): StepDatum[] => {
+ const maxStep = STEPS.findIndex((s) => s.id === "final-situation");
+ const endStep = Math.min(game.lastFinishedStep + 1, maxStep);
+
+ return _.range(0, endStep)
+ .map(getStepTypes)
+ .map((stepTypes, idx) =>
+ stepTypes.map((stepType) => ({ step: idx, type: stepType }))
+ )
+ .flat();
+ }, [game.lastFinishedStep]);
+
+ const MainGraph = useMemo(() => {
+ const data = stepDataToDisplay.map((stepDatum): StackedBarsStackData => {
+ const computeBars =
+ stepDatum.type === "consumption"
+ ? computeConsumptionBarsForPersona
+ : computeProductionBarsForPersona;
+
+ return buildStack({
+ bars: computeBars({
+ persona: getPersonaAtStep(stepDatum.step),
+ t,
+ }),
+ label: getStackName({ stepDatum, t }),
+ });
+ });
+
+ const stacks: StackedBarsStacks = {
+ data,
+ yAxisUnitLabel: t("unit.watthour-per-day-bare.kilo"),
+ palettes: ["energy", "production"],
+ yAxisValueFormatter: (value) =>
+ formatProduction(value, { fractionDigits: 2 }),
+ yAxisTicksValueFormatter: (value) =>
+ formatProduction(value, { fractionDigits: 0 }),
+ };
+
+ return (
+ {
+ if (chartState?.activeTooltipIndex != null) {
+ const stepDatum = stepDataToDisplay[chartState.activeTooltipIndex];
+ setSelectedStepDatum(stepDatum);
+ }
+ }}
+ />
+ );
+ }, [stepDataToDisplay, getPersonaAtStep, t]);
+
+ return (
+
+ {MainGraph}
+ {selectedStepDatum && (
+
+ )}
+
+ );
+}
diff --git a/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceForTeamChart.tsx b/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceForTeamChart.tsx
new file mode 100644
index 00000000..44814522
--- /dev/null
+++ b/packages/client/src/modules/charts/EnergyBalanceCharts/EnergyBalanceForTeamChart.tsx
@@ -0,0 +1,75 @@
+import { Box } from "@mui/material";
+import React, { useMemo } from "react";
+import {
+ StackedBars,
+ StackedBarsStackData,
+ StackedBarsStacks,
+} from "../StackedBars";
+import { formatProduction, formatUserName } from "../../../lib/formatter";
+import { usePersonaByUserId, usePlay } from "../../play/context/playContext";
+import { useTranslation } from "../../translations/useTranslation";
+import {
+ computeConsumptionBarsForPersona,
+ computeProductionBarsForPersona,
+} from "./utils";
+import { ITeam } from "../../../utils/types";
+import { buildStack } from "../utils";
+
+export { EnergyBalanceForTeamChart };
+
+function EnergyBalanceForTeamChart({ team }: { team: ITeam }) {
+ const { t } = useTranslation();
+ const { players } = usePlay();
+
+ const playersInTeam = useMemo(
+ () => players.filter((p) => p.teamId === team.id),
+ [players, team]
+ );
+ const userIds = useMemo(
+ () => playersInTeam.map((p) => p.userId),
+ [playersInTeam]
+ );
+ const personaByUserId = usePersonaByUserId(userIds);
+
+ const Graph = useMemo(() => {
+ const consumptionStacks: StackedBarsStackData[] = playersInTeam.map(
+ (player) =>
+ buildStack({
+ bars: computeConsumptionBarsForPersona({
+ persona: personaByUserId[player.userId].currentPersona,
+ t,
+ }),
+ label: formatUserName(player.user),
+ })
+ );
+
+ const productionStack: StackedBarsStackData = buildStack({
+ bars: computeProductionBarsForPersona({
+ persona: personaByUserId[playersInTeam[0].userId].currentPersona,
+ t,
+ }),
+ label: t("graph.common.production"),
+ });
+
+ const data = [...consumptionStacks, productionStack];
+
+ const stacks: StackedBarsStacks = {
+ data,
+ yAxisUnitLabel: t("unit.watthour-per-day-bare.kilo"),
+ palettes: ["energy", "production"],
+ yAxisValueFormatter: (value) =>
+ formatProduction(value, { fractionDigits: 2 }),
+ yAxisTicksValueFormatter: (value) =>
+ formatProduction(value, { fractionDigits: 0 }),
+ };
+
+ return (
+
+ );
+ }, [personaByUserId, playersInTeam, t]);
+
+ return {Graph};
+}
diff --git a/packages/client/src/modules/charts/EnergyBalanceCharts/index.ts b/packages/client/src/modules/charts/EnergyBalanceCharts/index.ts
new file mode 100644
index 00000000..c093fdbc
--- /dev/null
+++ b/packages/client/src/modules/charts/EnergyBalanceCharts/index.ts
@@ -0,0 +1,2 @@
+export { EnergyBalanceForPlayerChart } from "./EnergyBalanceForPlayerChart";
+export { EnergyBalanceForTeamChart } from "./EnergyBalanceForTeamChart";
diff --git a/packages/client/src/modules/charts/EnergyBalanceCharts/types.ts b/packages/client/src/modules/charts/EnergyBalanceCharts/types.ts
new file mode 100644
index 00000000..a0c73c76
--- /dev/null
+++ b/packages/client/src/modules/charts/EnergyBalanceCharts/types.ts
@@ -0,0 +1,5 @@
+import { GameStepType } from "../../play";
+
+export type { StepDatum };
+
+type StepDatum = { step: number; type: GameStepType };
diff --git a/packages/client/src/modules/charts/EnergyBalanceCharts/utils.ts b/packages/client/src/modules/charts/EnergyBalanceCharts/utils.ts
new file mode 100644
index 00000000..cc253a37
--- /dev/null
+++ b/packages/client/src/modules/charts/EnergyBalanceCharts/utils.ts
@@ -0,0 +1,76 @@
+import { StackedBarsBar } from "../StackedBars";
+import { Persona } from "../../persona/persona";
+import { sumReducer } from "../../../lib/array";
+import { filterOutDuplicates } from "../../common/utils";
+import { I18nTranslateFunction } from "../../translations";
+import { StepDatum } from "./types";
+
+export {
+ computeConsumptionBarsForPersona,
+ computeProductionBarsForPersona,
+ getStackName,
+};
+
+function computeConsumptionBarsForPersona({
+ persona,
+ t,
+}: {
+ persona: Persona;
+ t: I18nTranslateFunction;
+}): StackedBarsBar[] {
+ const consumptionTypes = persona.consumption
+ .map((consumption) => consumption.type)
+ .filter(filterOutDuplicates)
+ .sort()
+ .reverse();
+
+ const bars: StackedBarsBar[] = consumptionTypes.map((type) => ({
+ key: type,
+ label: t(`energy.${type}`),
+ total: persona.consumption
+ .filter((datum) => datum.type === type)
+ .map((datum) => datum.value)
+ .reduce(sumReducer, 0),
+ }));
+
+ return bars;
+}
+
+function computeProductionBarsForPersona({
+ persona,
+ t,
+}: {
+ persona: Persona;
+ t: I18nTranslateFunction;
+}): StackedBarsBar[] {
+ const productionTypes = persona.productionDisplayed
+ .map((production) => production.type)
+ .filter(filterOutDuplicates)
+ .sort()
+ .reverse();
+
+ const bars: StackedBarsBar[] = productionTypes.map((type) => ({
+ key: type,
+ label: t(`graph.energy.${type}`),
+ total: persona.productionDisplayed
+ .filter((datum) => datum.type === type)
+ .map((datum) => datum.value)
+ .reduce(sumReducer, 0),
+ }));
+
+ return bars;
+}
+
+function getStackName({
+ stepDatum,
+ t,
+}: {
+ stepDatum: StepDatum;
+ t: I18nTranslateFunction;
+}) {
+ return stepDatum.step === 0
+ ? stepDatum.type === "consumption"
+ ? t("graph.step.first.consumption.name")
+ : t("graph.step.first.production.name")
+ : t("graph.step.other.name", { stepNumber: stepDatum.step });
+}
diff --git a/packages/client/src/modules/charts/ResourcesPerProductionTypeChart.tsx b/packages/client/src/modules/charts/ResourcesPerProductionTypeChart.tsx
index 7064ec2d..984997ff 100644
--- a/packages/client/src/modules/charts/ResourcesPerProductionTypeChart.tsx
+++ b/packages/client/src/modules/charts/ResourcesPerProductionTypeChart.tsx
@@ -26,7 +26,7 @@ function ResourcesPerProductionTypeChart({
const graphStacks: StackedBarsStacks = useMemo(() => {
const data: StackedBarsStackData[] = pipe(
- persona[resourceType],
+ persona[`${resourceType}Displayed`],
(resources: PhysicalResourceNeedDatum[]) =>
resources.reduce(
(
diff --git a/packages/client/src/modules/charts/ResourcesPerStepChart.tsx b/packages/client/src/modules/charts/ResourcesPerStepChart.tsx
index 11691f8f..1ec71441 100644
--- a/packages/client/src/modules/charts/ResourcesPerStepChart.tsx
+++ b/packages/client/src/modules/charts/ResourcesPerStepChart.tsx
@@ -48,7 +48,7 @@ function ResourcesPerStepChart({
const computeBarsForPersona = useCallback(
(persona: Persona): StackedBarsBar[] => {
const indexBarByResourceName = (persona: Persona) =>
- persona[resourceType].reduce(
+ persona[`${resourceType}Displayed`].reduce(
(barIndexedByResourceName, resourceDatum) => {
if (
!barIndexedByResourceName[
diff --git a/packages/client/src/modules/charts/StackedBars.tsx b/packages/client/src/modules/charts/StackedBars.tsx
index 37bed82c..81e86390 100644
--- a/packages/client/src/modules/charts/StackedBars.tsx
+++ b/packages/client/src/modules/charts/StackedBars.tsx
@@ -511,7 +511,11 @@ function StackedBars({
} />
{chartConfig.barsProps.map((props, idx) => (
-
+
))}
{chartConfig.linesProps.map((props, idx) => (
diff --git a/packages/client/src/modules/charts/StackedEnergyBars.tsx b/packages/client/src/modules/charts/StackedEnergyBars.tsx
deleted file mode 100644
index a355cd98..00000000
--- a/packages/client/src/modules/charts/StackedEnergyBars.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import { Card, Grid, useTheme } from "@mui/material";
-import {
- BarChart,
- Bar,
- XAxis,
- YAxis,
- Tooltip,
- Legend,
- ResponsiveContainer,
- TooltipProps,
-} from "recharts";
-import { CategoricalChartFunc } from "recharts/types/chart/generateCategoricalChart";
-import { EnergyPalette, ProductionPalette } from "../../utils/theme";
-import { hasNuclear, filterOutDuplicates } from "../common/utils";
-import { usePlay } from "../play/context/playContext";
-import { productionConstants } from "../play";
-import { t, useTranslation } from "../translations";
-import { ConsumptionType } from "../persona/consumption";
-import { ProductionActionType } from "../../utils/types";
-import { Typography } from "../common/components/Typography";
-
-export { StackedEnergyBars };
-
-function StackedEnergyBars({
- title,
- data,
- tick = true,
- onClick,
-}: {
- title?: string;
- data: any[];
- tick?: boolean;
- onClick?: CategoricalChartFunc;
-}) {
- const theme = useTheme();
- const { t } = useTranslation();
- const { game } = usePlay();
-
- const maximumTotal = Math.max(
- ...data.map((pileData: any) => pileData.total),
- 0
- );
-
- const CustomTooltip = ({
- active,
- payload,
- label,
- }: TooltipProps): JSX.Element => {
- if (active && payload && payload.length) {
- return (
-
-
- {label}
-
- {Object.entries(payload[0].payload)
- .filter(([key]) => key !== "name" && key !== "type")
- .filter(
- ([key]) =>
- key !== productionConstants.NUCLEAR.name || hasNuclear(game)
- )
- .map(([key, value]) => (
-
- {translateLabel(key)}:{" "}
- {t("unit.watthour-per-day.kilo", {
- value,
- })}
-
- ))}
-
- );
- }
- return <>>;
- };
-
- const uniqueBars = data
- .flatMap((d) =>
- Object.keys(d)
- .filter((key) => !["name", "total", "type"].includes(key))
- .filter(
- (key) => key !== productionConstants.NUCLEAR.name || hasNuclear(game)
- )
- )
- .filter(filterOutDuplicates)
- .reverse();
-
- return (
-
- {!!title && (
-
- {title}
-
- )}
-
-
-
-
- } />
-
- {uniqueBars.map((key, idx) => (
-
- ))}
-
-
-
- );
-}
-
-function translateLabel(value: string): string {
- return (
- t(`graph.energy.${value as ConsumptionType | ProductionActionType}`) ??
- "Unknown"
- );
-}
diff --git a/packages/client/src/modules/charts/index.ts b/packages/client/src/modules/charts/index.ts
index a055ce79..7d66850e 100644
--- a/packages/client/src/modules/charts/index.ts
+++ b/packages/client/src/modules/charts/index.ts
@@ -1,17 +1,21 @@
-import { StackedEnergyBars } from "./StackedEnergyBars";
import { LineEvolution } from "./LineEvolution";
import {
DetailsEnergyConsumptionBars,
DetailsEnergyProductionBars,
} from "./DetailsEnergyBars";
+import {
+ EnergyBalanceForPlayerChart,
+ EnergyBalanceForTeamChart,
+} from "./EnergyBalanceCharts";
import { ResourcesPerProductionTypeChart } from "./ResourcesPerProductionTypeChart";
import { ResourcesPerStepChart } from "./ResourcesPerStepChart";
export {
- StackedEnergyBars,
LineEvolution,
DetailsEnergyConsumptionBars,
DetailsEnergyProductionBars,
+ EnergyBalanceForPlayerChart,
+ EnergyBalanceForTeamChart,
ResourcesPerProductionTypeChart,
ResourcesPerStepChart,
};
diff --git a/packages/client/src/modules/charts/utils/index.ts b/packages/client/src/modules/charts/utils/index.ts
new file mode 100644
index 00000000..bb9d4615
--- /dev/null
+++ b/packages/client/src/modules/charts/utils/index.ts
@@ -0,0 +1,18 @@
+import sumBy from "lodash/sumBy";
+import { StackedBarsBar, StackedBarsStackData } from "../StackedBars";
+
+export { buildStack };
+
+function buildStack({
+ bars,
+ label,
+}: {
+ bars: StackedBarsBar[];
+ label: string;
+}): StackedBarsStackData {
+ return {
+ label,
+ total: sumBy(bars, "total"),
+ bars,
+ };
+}
diff --git a/packages/client/src/modules/common/components/EnergyButtons/index.tsx b/packages/client/src/modules/common/components/EnergyButtons/index.tsx
index 48546f1e..8d5f272c 100644
--- a/packages/client/src/modules/common/components/EnergyButtons/index.tsx
+++ b/packages/client/src/modules/common/components/EnergyButtons/index.tsx
@@ -1,90 +1,132 @@
import { Box, Button, useTheme, Tooltip } from "@mui/material";
-import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
+import { useMemo } from "react";
import { Persona } from "../../../persona/persona";
-import {
- I18nTranslateFunction,
- translateName,
- useTranslation,
-} from "../../../translations";
-import { hasNuclear, roundValue } from "../../utils";
-import { usePlay } from "../../../play/context/playContext";
+import { translateName, useTranslation } from "../../../translations";
+import { roundValue } from "../../utils";
+import { Icon } from "../Icon";
export { EnergyConsumptionButtons, EnergyProductionButtons };
-interface EnergyType {
- color: string;
- name: string;
- type: string;
-}
-
function EnergyConsumptionButtons({ persona }: { persona: Persona }) {
const theme = useTheme();
const { t } = useTranslation();
- const energies: EnergyType[] = [
- {
- color: theme.palette.energy.grey,
- name: t("energy.grey"),
- type: "grey",
- },
- {
- color: theme.palette.energy.fossil,
- name: t("energy.fossil"),
- type: "fossil",
- },
- {
- color: theme.palette.energy.renewable,
- name: t("energy.renewable"),
- type: "renewable",
- },
- {
- color: theme.palette.energy.mixte,
- name: t("energy.mixte"),
- type: "mixte",
- },
- ];
+ const items: EnergyItem[] = useMemo((): EnergyItem[] => {
+ const energyTypesDisplayed = new Set(
+ persona.consumption.map((datum) => datum.type)
+ );
+
+ const energies = [
+ {
+ color: theme.palette.energy.grey,
+ name: t("energy.grey"),
+ type: "grey",
+ },
+ {
+ color: theme.palette.energy.fossil,
+ name: t("energy.fossil"),
+ type: "fossil",
+ },
+ {
+ color: theme.palette.energy.renewable,
+ name: t("energy.renewable"),
+ type: "renewable",
+ },
+ {
+ color: theme.palette.energy.mixte,
+ name: t("energy.mixte"),
+ type: "mixte",
+ },
+ ] as const;
+
+ return energies
+ .filter((energy) => energyTypesDisplayed.has(energy.type))
+ .map((energy) => {
+ const details = persona.consumption
+ .filter(({ type }) => type === energy.type)
+ .map(
+ ({ name, value }) =>
+ `${translateName("consumption", name)} : ${t(
+ "unit.watthour-per-day.kilo",
+ {
+ value: roundValue(value),
+ }
+ )}`
+ )
+ .join("\n");
- return buildEnergyButtons("consumption", energies, persona, t);
+ return {
+ name: energy.name,
+ color: energy.color,
+ details,
+ };
+ });
+ }, [persona.consumption, theme, t]);
+
+ return ;
}
function EnergyProductionButtons({ persona }: { persona: Persona }) {
const theme = useTheme();
const { t } = useTranslation();
- const { game } = usePlay();
- const energies: EnergyType[] = [
- {
- color: theme.palette.production.offshore,
- name: t("graph.energy.offshore"),
- type: "offshore",
- },
- {
- color: theme.palette.production.terrestrial,
- name: t("graph.energy.terrestrial"),
- type: "terrestrial",
- },
- ];
+ const items: EnergyItem[] = useMemo((): EnergyItem[] => {
+ const energyTypesDisplayed = new Set(
+ persona.productionDisplayed.map((datum) => datum.type)
+ );
+
+ const energies = [
+ {
+ color: theme.palette.production.offshore,
+ name: t("graph.energy.offshore"),
+ type: "offshore",
+ },
+ {
+ color: theme.palette.production.terrestrial,
+ name: t("graph.energy.terrestrial"),
+ type: "terrestrial",
+ },
+ {
+ color: theme.palette.production.nuclear,
+ name: t("graph.energy.nuclear"),
+ type: "nuclear",
+ },
+ ] as const;
+
+ return energies
+ .filter((energy) => energyTypesDisplayed.has(energy.type))
+ .map((energy) => {
+ const details = persona.productionDisplayed
+ .filter(({ type }) => type === energy.type)
+ .map(
+ ({ name, value }) =>
+ `${translateName("production", name)} : ${t(
+ "unit.watthour-per-day.kilo",
+ {
+ value: roundValue(value),
+ }
+ )}`
+ )
+ .join("\n");
- if (hasNuclear(game)) {
- energies.push({
- color: theme.palette.production.nuclear,
- name: t("graph.energy.nuclear"),
- type: "nuclear",
- });
- }
+ return {
+ name: energy.name,
+ color: energy.color,
+ details,
+ };
+ });
+ }, [persona.productionDisplayed, theme, t]);
- return buildEnergyButtons("production", energies, persona, t);
+ return ;
}
-function buildEnergyButtons(
- stepType: string,
- energies: EnergyType[],
- persona: Persona,
- t: I18nTranslateFunction
-) {
- const personaValues: any =
- stepType === "consumption" ? persona.consumption : persona.production;
+interface EnergyItem {
+ name: string;
+ details: string;
+ color: string;
+}
+function EnergyButtons({ items }: { items: EnergyItem[] }) {
return (
- {energies.map((energyTypes) => (
+ {items.map((item) => (
- {personaValues
- .filter(
- ({ type }: { type: string }) => type === energyTypes.type
- )
- .map(({ name, value }: { name: string; value: number }) => {
- return `${translateName(stepType, name)} : ${t(
- "unit.watthour-per-day.kilo",
- {
- value: roundValue(value),
- }
- )}`;
- })
- .join("\n")}
-
- }
+ key={item.name}
+ title={{item.details}
}
arrow
>
))}
diff --git a/packages/client/src/modules/common/constants/constants.ts b/packages/client/src/modules/common/constants/constants.ts
index 4fed5ebf..bb0c070f 100644
--- a/packages/client/src/modules/common/constants/constants.ts
+++ b/packages/client/src/modules/common/constants/constants.ts
@@ -6,7 +6,6 @@ export {
WEB_SOCKET_URL,
};
-// TODO: use this constant where `2050` is hardcoded.
const ENERGY_SHIFT_TARGET_YEAR = 2050;
const TERMS_OF_USE_URL =
"https://ogre-public.s3.eu-west-3.amazonaws.com/cgu-latest.pdf";
diff --git a/packages/client/src/modules/common/utils.tsx b/packages/client/src/modules/common/utils.tsx
index 2d1f9e3e..81d941cb 100644
--- a/packages/client/src/modules/common/utils.tsx
+++ b/packages/client/src/modules/common/utils.tsx
@@ -1,14 +1,9 @@
-import { IGame } from "../../utils/types";
-import { getStepIndexById } from "../play";
+export { filterOutDuplicates, roundValue };
-export function roundValue(value: number) {
+function roundValue(value: number) {
return Math.round((value + Number.EPSILON) * 100) / 100;
}
-export function hasNuclear(game: IGame) {
- return game.step >= getStepIndexById("production-3");
-}
-
-export function filterOutDuplicates(value: any, index: number, array: any[]) {
+function filterOutDuplicates(value: any, index: number, array: any[]) {
return array.indexOf(value) === index;
}
diff --git a/packages/client/src/modules/persona/persona.ts b/packages/client/src/modules/persona/persona.ts
index d4c39f5d..2e1f187e 100644
--- a/packages/client/src/modules/persona/persona.ts
+++ b/packages/client/src/modules/persona/persona.ts
@@ -5,7 +5,7 @@ import {
computeCarbonFootprint,
} from "../play/utils/carbonFootprint";
import { ConsumptionDatum, getConsumptionFromProfile } from "./consumption";
-import { PRODUCTION, ProductionDatum } from "./production";
+import { ProductionDatum } from "./production";
import { computeIntermediateValues } from "./consumption/computing";
import {
computeMaterials,
@@ -13,6 +13,7 @@ import {
PhysicalResourceNeedDatum,
} from "../play/gameEngines/resourcesEngine";
import { ProductionAction, TeamAction } from "../../utils/types";
+import { computeEnergyProduction } from "../play/utils/production";
export { buildInitialPersona };
export type { Persona };
@@ -24,8 +25,11 @@ interface Persona {
points: number;
consumption: readonly ConsumptionDatum[];
production: ProductionDatum[];
+ productionDisplayed: ProductionDatum[];
materials: PhysicalResourceNeedDatum[];
+ materialsDisplayed: PhysicalResourceNeedDatum[];
metals: PhysicalResourceNeedDatum[];
+ metalsDisplayed: PhysicalResourceNeedDatum[];
}
const buildInitialPersona = (
@@ -42,19 +46,46 @@ const buildInitialPersona = (
intermediateValues
);
+ const production: ProductionDatum[] = Object.values(productionActionById).map(
+ (productionAction) => {
+ return {
+ name: productionAction.name,
+ type: productionAction.type,
+ carbonType: productionAction.carbonType,
+ revealOnStep: productionAction.revealOnStep,
+ value: computeEnergyProduction(
+ productionAction,
+ productionAction.defaultTeamValue
+ ),
+ };
+ }
+ );
+
+ const productionDisplayed = production.filter((p) => !p.revealOnStep);
+
const carbonProductionElectricMix =
- computeCarbonProductionElectricMix(PRODUCTION);
+ computeCarbonProductionElectricMix(production);
const carbonFootprint = computeCarbonFootprint(
carbonProductionElectricMix,
consumption as ConsumptionDatum[]
);
const materials = computeMaterials(
- PRODUCTION,
+ production,
+ teamActions,
+ productionActionById
+ );
+ const materialsDisplayed = computeMaterials(
+ productionDisplayed,
+ teamActions,
+ productionActionById
+ );
+ const metals = computeMetals(production, teamActions, productionActionById);
+ const metalsDisplayed = computeMetals(
+ productionDisplayed,
teamActions,
productionActionById
);
- const metals = computeMetals(PRODUCTION, teamActions, productionActionById);
const persona: Persona = {
budget: 13.7,
@@ -62,9 +93,12 @@ const buildInitialPersona = (
points: 0,
carbonFootprint,
consumption,
- production: PRODUCTION,
+ production,
+ productionDisplayed,
materials,
+ materialsDisplayed,
metals,
+ metalsDisplayed,
};
return persona;
diff --git a/packages/client/src/modules/persona/production.ts b/packages/client/src/modules/persona/production.ts
index 3753cd23..935930c5 100644
--- a/packages/client/src/modules/persona/production.ts
+++ b/packages/client/src/modules/persona/production.ts
@@ -3,9 +3,7 @@ import {
ProductionActionType,
ProductionCarbonType,
} from "../../utils/types";
-import { productionConstants } from "../play/constants";
-export { PRODUCTION };
export type { ProductionDatum };
interface ProductionDatum {
@@ -16,74 +14,3 @@ interface ProductionDatum {
value: number;
revealOnStep?: number;
}
-
-// TODO: should be moved to ProductionAction model in database
-const PRODUCTION: ProductionDatum[] = [
- {
- name: productionConstants.GEOTHERMAL.name,
- value: 0.0054,
- type: "offshore",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.HYDRAULIC.name,
- value: 2.67,
- type: "offshore",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.OFF_SHORE_WIND_TURBINE.name,
- value: 0.4,
- type: "offshore",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.TIDAL.name,
- value: 0.02226,
- type: "offshore",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.WAVE.name,
- value: 0.000167,
- type: "offshore",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.NUCLEAR.name,
- value: 0,
- type: "nuclear",
- carbonType: "decarbonated",
- revealOnStep: 5,
- },
- {
- name: productionConstants.BIOMASS.name,
- value: 7.14,
- type: "terrestrial",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.ON_SHORE_WIND_TURBINE.name,
- value: 2.08,
- type: "terrestrial",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.PHOTOVOLTAIC_FARM.name,
- value: 0.31,
- type: "terrestrial",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.PHOTOVOLTAIC_ROOF.name,
- value: 0.25,
- type: "terrestrial",
- carbonType: "decarbonated",
- },
- {
- name: productionConstants.THERMAL_SOLAR.name,
- value: 0.11,
- type: "terrestrial",
- carbonType: "decarbonated",
- },
-];
diff --git a/packages/client/src/modules/play/GameConsole/PlayerChart.tsx b/packages/client/src/modules/play/GameConsole/PlayerChart.tsx
index 3d735dfd..db87c212 100644
--- a/packages/client/src/modules/play/GameConsole/PlayerChart.tsx
+++ b/packages/client/src/modules/play/GameConsole/PlayerChart.tsx
@@ -1,15 +1,13 @@
import { ITeam } from "../../../utils/types";
-import { StackedEnergyBars } from "../../charts/StackedEnergyBars";
-import { sumAllValues, sumForAndFormat } from "../../persona";
import { usePersonaByUserId, usePlay } from "../context/playContext";
import { Tabs } from "../../common/components/Tabs";
import { useMemo } from "react";
import {
+ EnergyBalanceForTeamChart,
ResourcesPerProductionTypeChart,
ResourcesPerStepChart,
} from "../../charts";
import { useTranslation } from "../../translations/useTranslation";
-import { formatUserName } from "../../../lib/formatter";
export { PlayerChart };
@@ -24,16 +22,11 @@ function PlayerChart({ team }: { team: ITeam }) {
const personaByUserId = usePersonaByUserId(userIds);
const [firstPersona] = Object.values(personaByUserId);
- const data = useBuildData({ team });
- const consProdData = data.filter((data) =>
- ["consumption", "production"].includes(data.type)
- );
-
const tabs = useMemo(() => {
return [
{
label: t("page.teacher.statistics.tabs.energy-balance.label"),
- component: ,
+ component: ,
},
{
label: t("page.teacher.statistics.tabs.materials.label"),
@@ -66,67 +59,7 @@ function PlayerChart({ team }: { team: ITeam }) {
),
},
];
- }, [consProdData, firstPersona, t]);
+ }, [firstPersona, team, t]);
return ;
}
-
-function useBuildData({ team }: { team: ITeam }) {
- const { game, players } = usePlay();
-
- const playersInTeam = useMemo(
- () => players.filter((p) => p.teamId === team.id),
- [players, team]
- );
- const userIds = useMemo(
- () => playersInTeam.map((p) => p.userId),
- [playersInTeam]
- );
- const personaByUserId = usePersonaByUserId(userIds);
- const [firstPersona] = Object.values(personaByUserId);
-
- return [
- ...playersInTeam.map((player) => {
- const userId = player.userId;
- const personaBySteps = personaByUserId[userId]!.personaBySteps;
- const playerConsumption =
- personaBySteps[game.lastFinishedStep].consumption;
-
- return {
- name: formatUserName(player.user),
- type: "consumption",
- total: sumAllValues(playerConsumption) || 0,
- grey: sumForAndFormat(playerConsumption, "grey"),
- mixte: sumForAndFormat(playerConsumption, "mixte"),
- fossil: sumForAndFormat(playerConsumption, "fossil"),
- renewable: sumForAndFormat(playerConsumption, "renewable"),
- };
- }),
- firstPersona
- ? {
- name: "Production",
- type: "production",
- total: sumAllValues(firstPersona.currentPersona.production) || 0,
- offshore: sumForAndFormat(
- firstPersona.currentPersona.production,
- "offshore"
- ),
- nuclear: sumForAndFormat(
- firstPersona.currentPersona.production,
- "nuclear"
- ),
- terrestrial: sumForAndFormat(
- firstPersona.currentPersona.production,
- "terrestrial"
- ),
- }
- : {
- name: "Production",
- type: "production",
- total: 0,
- offshore: 0,
- nuclear: 0,
- terrestrial: 0,
- },
- ];
-}
diff --git a/packages/client/src/modules/play/GameConsole/utils/statsConsoleValues.tsx b/packages/client/src/modules/play/GameConsole/utils/statsConsoleValues.tsx
index 8bd85b89..1292d312 100644
--- a/packages/client/src/modules/play/GameConsole/utils/statsConsoleValues.tsx
+++ b/packages/client/src/modules/play/GameConsole/utils/statsConsoleValues.tsx
@@ -4,7 +4,7 @@ import {
formatCarbonFootprint,
formatPoints,
} from "../../../../lib/formatter";
-import { getDaysTo2050 } from "../../../../lib/time";
+import { getDaysToEnergyShiftTargetYear } from "../../../../lib/time";
import { IEnrichedGame, ITeam } from "../../../../utils/types";
import { MAX_TEAMS_POINTS } from "../../constants";
import { synthesisConstants } from "../../playerActions/constants/synthesis";
@@ -20,7 +20,9 @@ function computeBudget(
) {
if (isSynthesisStep) {
const budgetSpentTotalFrance =
- (budgetSpent * getDaysTo2050() * synthesisConstants.FRANCE_POPULATION) /
+ (budgetSpent *
+ getDaysToEnergyShiftTargetYear() *
+ synthesisConstants.FRANCE_POPULATION) /
synthesisConstants.MILLIARD;
return formatBudget(budgetSpentTotalFrance);
diff --git a/packages/client/src/modules/play/Stats/StatsGraphs.tsx b/packages/client/src/modules/play/Stats/StatsGraphs.tsx
index 38f771ee..0890b229 100644
--- a/packages/client/src/modules/play/Stats/StatsGraphs.tsx
+++ b/packages/client/src/modules/play/Stats/StatsGraphs.tsx
@@ -1,22 +1,11 @@
import { Box } from "@mui/material";
-import React, { useMemo, useState } from "react";
+import React, { useMemo } from "react";
import {
- StackedEnergyBars,
- DetailsEnergyConsumptionBars,
- DetailsEnergyProductionBars,
ResourcesPerProductionTypeChart,
ResourcesPerStepChart,
+ EnergyBalanceForPlayerChart,
} from "../../charts";
import { PlayBox } from "../Components";
-import {
- EnergyConsumptionButtons,
- EnergyProductionButtons,
-} from "../../common/components/EnergyButtons";
-import { STEPS } from "../constants";
-import _ from "lodash";
-import { usePlay } from "../context/playContext";
-import { sumAllValues, sumForAndFormat } from "../../persona";
-import { IGame } from "../../../utils/types";
import { Tabs } from "../../common/components/Tabs";
import { useTranslation } from "../../translations";
import { usePersona } from "../context/hooks/player";
@@ -24,18 +13,17 @@ import { Typography } from "../../common/components/Typography";
export { StatsGraphs };
-function isNotFinishedStep(step: number, game: IGame) {
- return step > game.lastFinishedStep;
-}
-
function StatsGraphs() {
const { t } = useTranslation();
+ const { getPersonaAtStep } = usePersona();
const tabs = useMemo(() => {
return [
{
label: t("page.player.statistics.tabs.energy-balance.label"),
- component: ,
+ component: (
+
+ ),
},
{
label: t("page.player.statistics.tabs.materials.label"),
@@ -46,7 +34,7 @@ function StatsGraphs() {
component: ,
},
];
- }, [t]);
+ }, [getPersonaAtStep, t]);
return (
@@ -62,134 +50,6 @@ function StatsGraphs() {
);
}
-function ConsumptionAndProductionGraph() {
- const { t } = useTranslation();
- const { game } = usePlay();
- const { personaBySteps, getPersonaAtStep } = usePersona();
- const [selectedBarIdx, setSelectedBarIdx] = useState();
-
- const bars = useMemo(() => {
- const initialPersona = getPersonaAtStep(0);
- const initialValues = [
- {
- name: t("graph.step.first.consumption.name"),
- total: sumAllValues(initialPersona.consumption) || 0,
- grey: sumForAndFormat(initialPersona.consumption, "grey"),
- mixte: sumForAndFormat(initialPersona.consumption, "mixte"),
- fossil: sumForAndFormat(initialPersona.consumption, "fossil"),
- renewable: sumForAndFormat(initialPersona.consumption, "renewable"),
- },
- {
- name: t("graph.step.first.production.name"),
- total: sumAllValues(initialPersona.production) || 0,
- offshore: sumForAndFormat(initialPersona.production, "offshore"),
- nuclear: sumForAndFormat(initialPersona.production, "nuclear"),
- terrestrial: sumForAndFormat(initialPersona.production, "terrestrial"),
- },
- ];
-
- const stepsDetails = _.range(1, game.lastFinishedStep + 1).map(
- (step: number) => {
- const persona = personaBySteps[step];
- if (STEPS[step]?.type === "consumption") {
- return {
- name: t("graph.step.other.name", { stepNumber: step }),
- total: sumAllValues(persona.consumption) || 0,
- grey: sumForAndFormat(persona.consumption, "grey"),
- mixte: sumForAndFormat(persona.consumption, "mixte"),
- fossil: sumForAndFormat(persona.consumption, "fossil"),
- renewable: sumForAndFormat(persona.consumption, "renewable"),
- };
- } else {
- return {
- name: t("graph.step.other.name", { stepNumber: step }),
- total: sumAllValues(persona.production) || 0,
- offshore: sumForAndFormat(persona.production, "offshore"),
- nuclear: sumForAndFormat(persona.production, "nuclear"),
- terrestrial: sumForAndFormat(persona.production, "terrestrial"),
- };
- }
- }
- );
-
- return [...initialValues, ...stepsDetails];
- }, [game.lastFinishedStep, personaBySteps, getPersonaAtStep, t]);
-
- return (
- <>
- {
- if (chartState?.activeTooltipIndex != null) {
- setSelectedBarIdx(chartState.activeTooltipIndex);
- }
- }}
- />
- {
-
- }
- >
- );
-}
-
-function ConsumptionAndProductionDetailsGraph({
- barIdx,
- bar,
-}: {
- barIdx: number | undefined;
- bar?: { name: string };
-}) {
- const { t } = useTranslation();
- const { game } = usePlay();
- const { getPersonaAtStep } = usePersona();
-
- const graphTitle = useMemo(() => {
- if (bar) {
- return t(
- "page.player.statistics.tabs.energy-balance.graphs.details.title",
- { stackName: bar.name }
- );
- }
- return "";
- }, [bar, t]);
-
- if (typeof barIdx === "undefined") return <>>;
-
- const step = Math.max(barIdx - 1, 0);
- if (isNotFinishedStep(step, game)) return <>>;
-
- const persona = getPersonaAtStep(step);
-
- return (
- <>
- {step === 0 && barIdx === 0 ? (
- <>
-
-
- >
- ) : step === 0 && barIdx === 1 ? (
- <>
-
-
- >
- ) : STEPS[step]?.type === "consumption" ? (
- <>
-
-
- >
- ) : (
- <>
-
-
- >
- )}
- >
- );
-}
-
function MaterialsGraphTab() {
const { currentPersona, getPersonaAtStep } = usePersona();
diff --git a/packages/client/src/modules/play/constants/steps.ts b/packages/client/src/modules/play/constants/steps.ts
index b555e27e..40231d72 100644
--- a/packages/client/src/modules/play/constants/steps.ts
+++ b/packages/client/src/modules/play/constants/steps.ts
@@ -6,6 +6,7 @@ export {
STEPS,
getStepId,
getStepIndexById,
+ getStepTypes,
isStepOfType,
};
export type { GameStepType, GameStepId, GameStep };
@@ -83,3 +84,9 @@ function getStepIndexById(id: GameStepId): number {
function isStepOfType(step: number, type: GameStepType) {
return step === 0 || step === STEPS.length - 1 || STEPS[step]?.type === type;
}
+
+function getStepTypes(step: number): GameStepType[] {
+ return (["consumption", "production"] as const).filter((type) =>
+ isStepOfType(step, type)
+ );
+}
diff --git a/packages/client/src/modules/play/context/playContext.tsx b/packages/client/src/modules/play/context/playContext.tsx
index 60933a11..ad779a8b 100644
--- a/packages/client/src/modules/play/context/playContext.tsx
+++ b/packages/client/src/modules/play/context/playContext.tsx
@@ -27,7 +27,7 @@ export {
usePersonaByUserId,
};
-export type { TeamIdToValues, TeamValues };
+export type { PersonaUsed, TeamIdToValues, TeamValues };
interface TeamIdToValues {
[k: string]: TeamValues;
@@ -390,10 +390,10 @@ function usePlay() {
return React.useContext(PlayContext);
}
-function usePersonaByUserId(userIds: number): ReturnType;
-function usePersonaByUserId(
- userIds: number[]
-): Record>;
+type PersonaUsed = ReturnType;
+
+function usePersonaByUserId(userIds: number): PersonaUsed;
+function usePersonaByUserId(userIds: number[]): Record;
function usePersonaByUserId(userIds: number | number[]) {
const { consumptionActionById, game, players, productionActionById, teams } =
useLoadedPlay();
diff --git a/packages/client/src/modules/play/context/usePlayStore.ts b/packages/client/src/modules/play/context/usePlayStore.ts
index 8566e62a..ae165440 100644
--- a/packages/client/src/modules/play/context/usePlayStore.ts
+++ b/packages/client/src/modules/play/context/usePlayStore.ts
@@ -9,7 +9,7 @@ import {
} from "../../../utils/types";
import { LARGE_GAME_TEAMS, STEPS } from "../constants";
import { indexArrayBy } from "../../../lib/array";
-import { PRODUCTION, ProductionDatum } from "../../persona/production";
+import { ProductionDatum } from "../../persona/production";
import { computeEnergyProduction } from "../utils/production";
export { usePlayStore };
@@ -49,11 +49,12 @@ function usePlayStore() {
);
const productionOfCountryToday: ProductionDatum[] = useMemo(() => {
- const productionByName = indexArrayBy(PRODUCTION, "name");
-
return productionActions.map((productionAction) => {
return {
- ...(productionByName[productionAction.name] || {}),
+ name: productionAction.name,
+ type: productionAction.type,
+ carbonType: productionAction.carbonType,
+ revealOnStep: productionAction.revealOnStep,
value: computeEnergyProduction(
productionAction,
productionAction.defaultTeamValue
diff --git a/packages/client/src/modules/play/utils/persona.ts b/packages/client/src/modules/play/utils/persona.ts
index eff65621..c997014a 100644
--- a/packages/client/src/modules/play/utils/persona.ts
+++ b/packages/client/src/modules/play/utils/persona.ts
@@ -168,16 +168,30 @@ function computeResultsByStep(
basePersona
);
+ const newProductionDisplayed = newProduction.filter(
+ (p) => (p.revealOnStep || 0) <= step
+ );
+
const newMaterials = computeMaterials(
newProduction,
teamActions,
productionActionById
);
+ const newMaterialsDisplayed = computeMaterials(
+ newProductionDisplayed,
+ teamActions,
+ productionActionById
+ );
const newMetals = computeMetals(
newProduction,
teamActions,
productionActionById
);
+ const newMetalsDisplayed = computeMetals(
+ newProductionDisplayed,
+ teamActions,
+ productionActionById
+ );
const { actionPointsUsedAtCurrentStep } = computePlayerActionsStats(
step,
@@ -199,7 +213,10 @@ function computeResultsByStep(
actionPoints: actionPointsUsedAtCurrentStep,
consumption: newConsumption,
production: newProduction,
+ productionDisplayed: newProductionDisplayed,
materials: newMaterials,
+ materialsDisplayed: newMaterialsDisplayed,
metals: newMetals,
+ metalsDisplayed: newMetalsDisplayed,
};
}
diff --git a/packages/client/src/modules/play/utils/production.ts b/packages/client/src/modules/play/utils/production.ts
index 615ac474..be6d38c5 100644
--- a/packages/client/src/modules/play/utils/production.ts
+++ b/packages/client/src/modules/play/utils/production.ts
@@ -1,8 +1,7 @@
-import { indexArrayBy } from "../../../lib/array";
import { fromEntries } from "../../../lib/object";
import { ProductionAction, TeamAction } from "../../../utils/types";
import { Persona } from "../../persona/persona";
-import { PRODUCTION, ProductionDatum } from "../../persona/production";
+import { ProductionDatum } from "../../persona/production";
export {
computeNewProductionData,
@@ -75,15 +74,16 @@ function computeNewProductionData(
productionActionById: Record,
persona: Persona
) {
- const productionByName = indexArrayBy(PRODUCTION, "name");
const productionNameToNewProduction = fromEntries(
performedTeamActions
.map((teamAction) => {
const productionAction = productionActionById[teamAction.actionId];
- const baseProduction = productionByName[productionAction.name];
return {
- ...baseProduction,
+ name: productionAction.name,
+ type: productionAction.type,
+ carbonType: productionAction.carbonType,
+ revealOnStep: productionAction.revealOnStep,
value: computeEnergyProduction(productionAction, teamAction.value),
};
})
diff --git a/packages/client/src/modules/translations/resources/en/common.json b/packages/client/src/modules/translations/resources/en/common.json
index 6ce971fc..df74b1a8 100644
--- a/packages/client/src/modules/translations/resources/en/common.json
+++ b/packages/client/src/modules/translations/resources/en/common.json
@@ -266,6 +266,10 @@
"graph.energy.terrestrial": "Onshore production",
"graph.energy.nuclear": "Nuclear energy",
+ "graph.energy-balance-for-player-graph.title": "Evolution of energy balance",
+
+ "graph.energy-balance-for-team-graph.title": "Energy balance of the team",
+
"graph.materials.steel": "Steel",
"graph.materials.cement": "Cement",
"graph.materials.glass": "Glass",
diff --git a/packages/client/src/modules/translations/resources/fr/common.json b/packages/client/src/modules/translations/resources/fr/common.json
index 1ffaaed0..c24a64f2 100644
--- a/packages/client/src/modules/translations/resources/fr/common.json
+++ b/packages/client/src/modules/translations/resources/fr/common.json
@@ -266,6 +266,10 @@
"graph.energy.terrestrial": "Production terrestre",
"graph.energy.nuclear": "Nucléaire",
+ "graph.energy-balance-for-player-graph.title": "Évolution de l'équilibre énergétique",
+
+ "graph.energy-balance-for-team-graph.title": "Équilibre énergétique de l'équipe",
+
"graph.materials.steel": "Acier",
"graph.materials.cement": "Ciment",
"graph.materials.glass": "Verre",
diff --git a/packages/client/src/utils/theme.ts b/packages/client/src/utils/theme.ts
index cc3a976d..809f24dc 100644
--- a/packages/client/src/utils/theme.ts
+++ b/packages/client/src/utils/theme.ts
@@ -145,6 +145,9 @@ const globalStyles: GlobalStylesProps["styles"] = {
html: {
overscrollBehavior: "none",
},
+ ".clickable": {
+ cursor: "pointer",
+ },
".text-em": {
color: `${theme.palette.secondary.main} !important`,
},
diff --git a/packages/client/src/utils/types.ts b/packages/client/src/utils/types.ts
index 7490b504..c6c8ffe5 100644
--- a/packages/client/src/utils/types.ts
+++ b/packages/client/src/utils/types.ts
@@ -180,6 +180,7 @@ interface ProductionActionCommon {
type: ProductionActionType;
order: number;
step: number;
+ revealOnStep?: number;
helpCardLink: string;
min: number;
max: number;
@@ -191,6 +192,7 @@ interface ProductionActionCommon {
pointsIntervals: PointsInterval[];
defaultTeamValue: number;
isPlayable: boolean;
+ carbonType: ProductionCarbonType;
}
interface ProductionActionArea extends ProductionActionCommon {
diff --git a/packages/server/prisma/migrations/20230815174943_add_production_action_carbon_type/migration.sql b/packages/server/prisma/migrations/20230815174943_add_production_action_carbon_type/migration.sql
new file mode 100644
index 00000000..097752c7
--- /dev/null
+++ b/packages/server/prisma/migrations/20230815174943_add_production_action_carbon_type/migration.sql
@@ -0,0 +1,5 @@
+-- CreateEnum
+CREATE TYPE "ProductionActionCarbonType" AS ENUM ('carbonated', 'decarbonated');
+
+-- AlterTable
+ALTER TABLE "ProductionAction" ADD COLUMN "carbonType" "ProductionActionCarbonType" NOT NULL DEFAULT E'decarbonated';
diff --git a/packages/server/prisma/migrations/20230815175025_add_production_action_reveal_on_step/migration.sql b/packages/server/prisma/migrations/20230815175025_add_production_action_reveal_on_step/migration.sql
new file mode 100644
index 00000000..d943c287
--- /dev/null
+++ b/packages/server/prisma/migrations/20230815175025_add_production_action_reveal_on_step/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "ProductionAction" ADD COLUMN "revealOnStep" INTEGER;
diff --git a/packages/server/prisma/schema.prisma b/packages/server/prisma/schema.prisma
index 4cb5b606..80872f8e 100644
--- a/packages/server/prisma/schema.prisma
+++ b/packages/server/prisma/schema.prisma
@@ -188,11 +188,12 @@ model PlayerActions {
}
model ProductionAction {
- id Int @id @default(autoincrement())
- name ProductionActionName @unique
+ id Int @id @default(autoincrement())
+ name ProductionActionName @unique
type ProductionActionType
order Int
step Int
+ revealOnStep Int?
helpCardLink String
unit ProductionActionUnit
min Float
@@ -206,7 +207,8 @@ model ProductionAction {
pointsIntervals PointsInterval[]
teamActions TeamActions[]
defaultTeamValue Float
- isPlayable Boolean @default(false)
+ isPlayable Boolean @default(false)
+ carbonType ProductionActionCarbonType @default(decarbonated)
}
enum ProductionActionName {
@@ -234,6 +236,11 @@ enum ProductionActionUnit {
percentage
}
+enum ProductionActionCarbonType {
+ carbonated
+ decarbonated
+}
+
model PointsInterval {
id Int @id @default(autoincrement())
min Float
diff --git a/packages/server/src/database/seeds/productionActionsSeed.ts b/packages/server/src/database/seeds/productionActionsSeed.ts
index 2bc26edf..f29f0136 100644
--- a/packages/server/src/database/seeds/productionActionsSeed.ts
+++ b/packages/server/src/database/seeds/productionActionsSeed.ts
@@ -96,6 +96,7 @@ function getProductionActionsDataForProductionStep1(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
{
name: productionActionNames.ON_SHORE_WIND_TURBINE,
@@ -126,6 +127,7 @@ function getProductionActionsDataForProductionStep1(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
{
name: productionActionNames.PHOTOVOLTAIC_FARM,
@@ -156,6 +158,7 @@ function getProductionActionsDataForProductionStep1(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
{
name: productionActionNames.PHOTOVOLTAIC_ROOF,
@@ -186,6 +189,7 @@ function getProductionActionsDataForProductionStep1(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
{
name: productionActionNames.THERMAL_SOLAR,
@@ -216,6 +220,7 @@ function getProductionActionsDataForProductionStep1(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
] as ProductionActionWithPointInterval[];
@@ -275,6 +280,7 @@ function getProductionActionsDataForProductionStep2(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
{
name: productionActionNames.OFF_SHORE_WIND_TURBINE,
@@ -305,6 +311,7 @@ function getProductionActionsDataForProductionStep2(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
{
name: productionActionNames.TIDAL,
@@ -335,6 +342,7 @@ function getProductionActionsDataForProductionStep2(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
{
name: productionActionNames.WAVE,
@@ -365,6 +373,7 @@ function getProductionActionsDataForProductionStep2(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
] as ProductionActionWithPointInterval[];
@@ -394,6 +403,7 @@ function getProductionActionsDataForProductionStep3(): {
powerNeededKWh: 3.82,
lcoe: 0.095,
order: 1,
+ revealOnStep: getStepIndexById("production-3"),
helpCardLink:
"https://drive.google.com/file/d/1g2lYTDT0x0kdcqlohWzfMK_VVItQQtCO/view?usp=share_link",
currentYearPowerNeedGw: 61.4,
@@ -411,6 +421,7 @@ function getProductionActionsDataForProductionStep3(): {
},
],
isPlayable: true,
+ carbonType: "decarbonated",
},
] as ProductionActionWithPointInterval[];
diff --git a/packages/server/src/modules/players/services/index.ts b/packages/server/src/modules/players/services/index.ts
index 8488975d..234cc3a2 100644
--- a/packages/server/src/modules/players/services/index.ts
+++ b/packages/server/src/modules/players/services/index.ts
@@ -1,11 +1,10 @@
-import { Personalization, Players as PlayersPrisma } from "@prisma/client";
+import { Personalization, Players } from "@prisma/client";
import { database } from "../../../database";
-import { Players } from "../types";
import * as playerActionsServices from "../../actions/services/playerActions";
import { batchItems } from "../../../lib/array";
const model = database.players;
-type Model = PlayersPrisma;
+type Model = Players;
export { services };
@@ -15,7 +14,6 @@ const crudServices = {
remove,
removeMany,
setDefaultProfiles,
- update,
updateMany,
validateProfiles,
};
@@ -30,31 +28,6 @@ async function getMany(
});
}
-async function update(
- gameId: number,
- userId: number,
- document: Partial>
-): Promise {
- // TODO: find a way to link Prisma typing with attributes included in `include` section.
- return model.update({
- where: {
- userId_gameId: {
- gameId,
- userId,
- },
- },
- data: document,
- include: {
- actions: {
- include: {
- action: true,
- },
- },
- team: true,
- },
- }) as unknown as Players;
-}
-
async function setDefaultProfiles(
gameId: number,
defaultPersonalization: Personalization
@@ -118,7 +91,7 @@ async function validateProfiles(gameId: number): Promise {
},
});
- return playersToValidate.forEach(async (player: PlayersPrisma) => {
+ return playersToValidate.forEach(async (player: Players) => {
if (player.profileId) {
await database.profile.update({
where: {
diff --git a/packages/server/src/modules/players/types/index.ts b/packages/server/src/modules/players/types/index.ts
deleted file mode 100644
index 3d8354ec..00000000
--- a/packages/server/src/modules/players/types/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Team, PlayerActions, Profile } from "@prisma/client";
-
-export type { Players };
-
-interface Players {
- gameId: number;
- teamId: number;
- userId: number;
- hasFinishedStep: boolean;
- profileId: number | null;
- team: Team;
- profile: Profile;
- actions: PlayerActions;
-}
diff --git a/packages/server/src/modules/productionActions/types/index.ts b/packages/server/src/modules/productionActions/types/index.ts
index 0e5b9fc4..5a7236c5 100644
--- a/packages/server/src/modules/productionActions/types/index.ts
+++ b/packages/server/src/modules/productionActions/types/index.ts
@@ -21,6 +21,7 @@ interface ProductionActionCommon {
type: ProductionActionType;
order: number;
step: number;
+ revealOnStep?: number;
helpCardLink: string;
min: number;
max: number;
@@ -31,6 +32,7 @@ interface ProductionActionCommon {
currentYearPowerNeedGw: number;
defaultTeamValue: number;
isPlayable: boolean;
+ carbonType: "carbonated" | "decarbonated";
}
interface PointsInterval {