-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #290 from dataforgoodfr/feat/D4G-289-show-most-imp…
…actful-actions feat(SynthesisPage): Implement most impactful actions
- Loading branch information
Showing
17 changed files
with
516 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
packages/client/src/modules/common/components/Card/Card.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Box, styled } from "@mui/material"; | ||
|
||
export { Card }; | ||
|
||
const Card = styled(Box)(({ theme }) => ({ | ||
borderRadius: "10px", | ||
border: "2px solid", | ||
borderColor: "#ffffff", | ||
backgroundColor: theme.palette.primary.main, | ||
color: "#ffffff", | ||
overflow: "hidden", | ||
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Card } from "./Card"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { styled } from "@mui/material"; | ||
import { ReactNode } from "react"; | ||
|
||
export { Tag }; | ||
|
||
function Tag({ | ||
type, | ||
color, | ||
icon, | ||
children, | ||
}: { | ||
type?: "success" | "error" | "secondary"; | ||
color?: string; | ||
icon?: ReactNode; | ||
children: ReactNode; | ||
}) { | ||
const className = type || ""; | ||
|
||
return ( | ||
<TagStyled className={className} sx={{ backgroundColor: color }}> | ||
{icon} | ||
{children} | ||
</TagStyled> | ||
); | ||
} | ||
|
||
const TagStyled = styled("span")(({ theme }) => { | ||
return { | ||
display: "flex", | ||
alignItems: "center", | ||
gap: theme.spacing(0.5), | ||
flexGrow: 0, | ||
flexShrink: 0, | ||
padding: theme.spacing(0.5), | ||
borderRadius: "10px", | ||
color: "#ffffff", | ||
"&.success": { | ||
backgroundColor: theme.palette.status.success, | ||
}, | ||
"&.error": { | ||
backgroundColor: theme.palette.status.error, | ||
}, | ||
"&.secondary": { | ||
backgroundColor: "hsl(0, 50%, 100%)", | ||
}, | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Tag } from "./Tag"; |
60 changes: 60 additions & 0 deletions
60
packages/client/src/modules/common/components/TagNumber/TagNumber.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { useMemo } from "react"; | ||
import { Icon } from "../Icon"; | ||
import { Tag } from "../Tag"; | ||
import { Typography } from "../Typography"; | ||
|
||
export { TagNumber }; | ||
|
||
function TagNumber({ | ||
value, | ||
successDirection = "increase", | ||
formatter, | ||
}: { | ||
value: number; | ||
successDirection?: "increase" | "decrease"; | ||
formatter: (nb: number) => string; | ||
}) { | ||
const tagType = useMemo(() => { | ||
const directionFactor = successDirection === "increase" ? 1 : -1; | ||
if (value * directionFactor > 0) { | ||
return "success"; | ||
} | ||
if (value * directionFactor < 0) { | ||
return "error"; | ||
} | ||
return "secondary"; | ||
}, [successDirection, value]); | ||
|
||
const icon = useMemo(() => { | ||
if (value > 0) { | ||
return ( | ||
<Icon name="number-increase" sx={{ width: "1rem", height: "1rem" }} /> | ||
); | ||
} | ||
if (value < 0) { | ||
return ( | ||
<Icon name="number-decrease" sx={{ width: "1rem", height: "1rem" }} /> | ||
); | ||
} | ||
return "•"; | ||
}, [value]); | ||
|
||
const sign = useMemo(() => { | ||
if (value > 0) { | ||
return "+"; | ||
} | ||
if (value < 0) { | ||
return "-"; | ||
} | ||
return ""; | ||
}, [value]); | ||
|
||
return ( | ||
<Tag type={tagType} icon={icon}> | ||
<Typography as="span"> | ||
{sign} | ||
{formatter(Math.abs(value))} | ||
</Typography> | ||
</Tag> | ||
); | ||
} |
1 change: 1 addition & 0 deletions
1
packages/client/src/modules/common/components/TagNumber/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { TagNumber } from "./TagNumber"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./useCurrentPlayer"; | ||
export * from "./useMostImpactfulActions"; | ||
export * from "./usePersona"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135
packages/client/src/modules/play/context/hooks/player/useMostImpactfulActions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { sumBy } from "lodash"; | ||
import { computeNewConsumptionData } from "../../../utils/consumption"; | ||
import { sumFor } from "../../../../persona"; | ||
import { | ||
ConsumptionDatum, | ||
ConsumptionType, | ||
} from "../../../../persona/consumption"; | ||
import { indexArrayBy } from "../../../../../lib/array"; | ||
import { fromEntries } from "../../../../../lib/object"; | ||
import { useMemo } from "react"; | ||
import { Action } from "../../../../../utils/types"; | ||
import { useCurrentPlayer } from "./useCurrentPlayer"; | ||
import { usePersona } from "./usePersona"; | ||
import { usePlay } from "../../playContext"; | ||
|
||
export { useMostImpactfulActions }; | ||
export type { ImpactfulAction }; | ||
|
||
type ImpactfulAction = { | ||
action: Action; | ||
isPerformed: boolean; | ||
consumptionImpacts: { | ||
type: ConsumptionType; | ||
initial: number; | ||
final: number; | ||
absolute: number; | ||
relative: number; | ||
}[]; | ||
}; | ||
|
||
function useMostImpactfulActions({ limit = 5 }: { limit?: number } = {}) { | ||
const { consumptionActionById } = usePlay(); | ||
const { personalization, playerActions } = useCurrentPlayer(); | ||
const { personaBySteps } = usePersona(); | ||
|
||
const mostImpactfulActions = useMemo(() => { | ||
const initialPersona = personaBySteps[0]; | ||
const actions = playerActions.map( | ||
(playerAction) => consumptionActionById[playerAction.actionId] | ||
); | ||
const PlayerActionByActionId = indexArrayBy(playerActions, "actionId"); | ||
|
||
const initialConsumptionByType = computeConsumptionByType( | ||
initialPersona.consumption | ||
); | ||
|
||
const mostImpactfulActions: ImpactfulAction[] = actions | ||
.map((action) => { | ||
const consumptionData = computeNewConsumptionData( | ||
[action.name], | ||
personalization | ||
); | ||
|
||
return { | ||
action, | ||
consumptionData, | ||
totalConsumptionKwh: sumBy(consumptionData, "value"), | ||
}; | ||
}) | ||
.sort( | ||
(consoA, consoB) => | ||
consoA.totalConsumptionKwh - consoB.totalConsumptionKwh | ||
) | ||
.slice(0, limit) | ||
.map(({ action, consumptionData }) => { | ||
const consumptionByType = computeConsumptionByType(consumptionData); | ||
const consumptionImpacts = computeConsumptionDifference( | ||
consumptionByType, | ||
initialConsumptionByType | ||
); | ||
|
||
return { | ||
action, | ||
isPerformed: PlayerActionByActionId[action.id].isPerformed, | ||
consumptionImpacts, | ||
}; | ||
}); | ||
|
||
return mostImpactfulActions; | ||
}, [ | ||
consumptionActionById, | ||
limit, | ||
personaBySteps, | ||
personalization, | ||
playerActions, | ||
]); | ||
|
||
return { | ||
mostImpactfulActions, | ||
}; | ||
} | ||
|
||
function computeConsumptionByType( | ||
consumptionData: readonly ConsumptionDatum[] | ||
): Record<ConsumptionType, number> { | ||
const consumptionTypes = consumptionData.map((c) => c.type); | ||
|
||
const consumptionByType = fromEntries( | ||
consumptionTypes.map((type) => [type, sumFor(consumptionData, type)]) | ||
); | ||
|
||
return consumptionByType; | ||
} | ||
|
||
function computeConsumptionDifference( | ||
consumptionByType: Record<ConsumptionType, number>, | ||
refConsumptionByType: Record<ConsumptionType, number> | ||
): { | ||
type: ConsumptionType; | ||
initial: number; | ||
final: number; | ||
absolute: number; | ||
relative: number; | ||
}[] { | ||
const consumptionTypes = Object.keys( | ||
refConsumptionByType | ||
) as ConsumptionType[]; | ||
|
||
const consumptionImpacts = consumptionTypes | ||
.map((type) => { | ||
const absoluteDifference = | ||
consumptionByType[type] - refConsumptionByType[type]; | ||
|
||
return { | ||
type, | ||
initial: refConsumptionByType[type], | ||
final: consumptionByType[type], | ||
absolute: absoluteDifference, | ||
relative: absoluteDifference / refConsumptionByType[type], | ||
}; | ||
}) | ||
.filter((difference) => difference.absolute !== 0); | ||
|
||
return consumptionImpacts; | ||
} |
Oops, something went wrong.