Skip to content

Commit

Permalink
feat(ui-thermal): minor user experience improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
hdinia committed Oct 10, 2023
1 parent 404db0e commit 304d18d
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 114 deletions.
2 changes: 1 addition & 1 deletion webapp/public/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@
"study.modelization.map.districts.edit": "Edit districts",
"study.modelization.map.districts.delete.confirm": "Are you sure you want to delete '{{0}}' district?",
"study.modelization.load": "Load",
"study.modelization.thermal": "Thermal Clus.",
"study.modelization.thermal": "Thermal",
"study.modelization.hydro": "Hydro",
"study.modelization.hydro.correlation.viewMatrix": "View all correlations",
"study.modelization.hydro.correlation.coefficient": "Coeff. (%)",
Expand Down
2 changes: 1 addition & 1 deletion webapp/public/locales/fr/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@
"study.modelization.map.districts.edit": "Modifier un district",
"study.modelization.map.districts.delete.confirm": "Êtes-vous sûr de vouloir supprimer le district '{{0}}' ?",
"study.modelization.load": "Conso",
"study.modelization.thermal": "Clus. Thermiques",
"study.modelization.thermal": "Thermiques",
"study.modelization.hydro": "Hydro",
"study.modelization.hydro.correlation.viewMatrix": "Voir les correlations",
"study.modelization.hydro.correlation.coefficient": "Coeff. (%)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ function ThermalForm() {
// Event handlers
////////////////////////////////////////////////////////////////

const handleSubmit =
(areaId: string, clusterId: string) =>
({ dirtyValues }: SubmitHandlerPlus<ThermalCluster>) => {
return updateThermalCluster(study.id, areaId, clusterId, dirtyValues);
};
const handleSubmit = ({ dirtyValues }: SubmitHandlerPlus<ThermalCluster>) => {
return updateThermalCluster(study.id, areaId, clusterId, dirtyValues);
};

////////////////////////////////////////////////////////////////
// JSX
Expand All @@ -55,7 +53,7 @@ function ThermalForm() {
return getThermalCluster(study.id, areaId, clusterId);
},
}}
onSubmit={handleSubmit(areaId, clusterId)}
onSubmit={handleSubmit}
autoSubmit
>
<Fields />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable camelcase */
import { useMemo } from "react";
import { MRT_ColumnDef } from "material-react-table";
import { Box, Chip, Stack } from "@mui/material";
import { useOutletContext } from "react-router-dom";
import { Box, Chip } from "@mui/material";
import { useLocation, useNavigate, useOutletContext } from "react-router-dom";
import { StudyMetadata } from "../../../../../../../common/types";
import {
ThermalClusterGroup,
Expand All @@ -11,6 +11,7 @@ import {
createThermalCluster,
deleteThermalClusters,
capacityAggregationFn,
CLUSTER_GROUP_OPTIONS,
} from "./utils";
import useAppSelector from "../../../../../../../redux/hooks/useAppSelector";
import { getCurrentAreaId } from "../../../../../../../redux/selectors";
Expand All @@ -20,18 +21,22 @@ import SimpleLoader from "../../../../../../common/loaders/SimpleLoader";

function Thermal() {
const { study } = useOutletContext<{ study: StudyMetadata }>();
const currentArea = useAppSelector(getCurrentAreaId);
const navigate = useNavigate();
const location = useLocation();
const currentAreaId = useAppSelector(getCurrentAreaId);
const groups = Object.values(ThermalClusterGroup);

const { data: clusters } = usePromise(
() => getThermalClusters(study.id, currentArea),
[study.id, currentArea]
const { data: clusters, isLoading } = usePromise(
() => getThermalClusters(study.id, currentAreaId),
[study.id, currentAreaId]
);

/**
* Calculates the installed and enabled capacity for each thermal cluster.
* @installedCapacity = unitCount * nominalCapacity.
* @enabledCapacity = unitCount * nominalCapacity if enabled is true else = 0.
* Calculate the installed and enabled capacity for each thermal cluster.
* - `installedCapacity` is calculated as the product of `unitCount` and `nominalCapacity`.
* - `enabledCapacity` is the product of `unitCount` and `nominalCapacity` if the cluster is enabled, otherwise it's 0.
* @function
* @returns {Array} - An array of cluster objects, each augmented with `installedCapacity` and `enabledCapacity`.
*/
const clustersWithCapacity = useMemo(
() =>
Expand All @@ -44,47 +49,89 @@ function Thermal() {
[clusters]
);

const totalUnitCount = useMemo(
() => clusters?.reduce((acc, curr) => acc + curr.unitCount, 0),
[clusters]
);

const totalInstalledCapacity = useMemo(
() =>
clusters?.reduce(
(acc, curr) => acc + curr.unitCount * curr.nominalCapacity,
0
),
[clusters]
);
const { totalUnitCount, totalInstalledCapacity, totalEnabledCapacity } =
useMemo(() => {
if (!clusters) {
return {
totalUnitCount: 0,
totalInstalledCapacity: 0,
totalEnabledCapacity: 0,
};
}

const totalEnabledCapacity = useMemo(
() =>
clusters?.reduce(
(acc, curr) =>
acc + (curr.enabled ? curr.unitCount * curr.nominalCapacity : 0),
0
),
[clusters]
);
return clusters.reduce(
(acc, { unitCount, nominalCapacity, enabled }) => {
acc.totalUnitCount += unitCount;
acc.totalInstalledCapacity += unitCount * nominalCapacity;
acc.totalEnabledCapacity += enabled ? unitCount * nominalCapacity : 0;
return acc;
},
{
totalUnitCount: 0,
totalInstalledCapacity: 0,
totalEnabledCapacity: 0,
}
);
}, [clusters]);

const columns = useMemo<MRT_ColumnDef<ThermalCluster>[]>(
() => [
{
accessorKey: "name",
header: "Name",
size: 100,
Cell: ({ renderedCellValue, row }) => {
const clusterId = row.original.id;
return (
<Box
sx={{
cursor: "pointer",
"&:hover": {
color: "primary.main",
textDecoration: "underline",
},
}}
onClick={() => navigate(`${location.pathname}/${clusterId}`)}
>
{renderedCellValue}
</Box>
);
},
},
{
accessorKey: "group",
header: "Group",
size: 50,
filterVariant: "select",
filterSelectOptions: CLUSTER_GROUP_OPTIONS,
muiTableHeadCellProps: {
align: "left",
},
muiTableBodyCellProps: {
align: "left",
},
Footer: () => (
<Box sx={{ display: "flex", alignItems: "flex-start" }}>Total:</Box>
),
},
{
accessorKey: "enabled",
header: "Enabled",
size: 50,
filterVariant: "checkbox",
Cell: ({ cell }) => (
<Chip
label={cell.getValue<boolean>() ? "Yes" : "No"}
color={cell.getValue<boolean>() ? "success" : "error"}
size="small"
/>
),
},
{
accessorKey: "mustRun",
header: "Must Run",
size: 50,
filterVariant: "checkbox",
Cell: ({ cell }) => (
<Chip
label={cell.getValue<boolean>() ? "Yes" : "No"}
Expand All @@ -103,12 +150,7 @@ function Thermal() {
{cell.getValue<number>()}
</Box>
),
Footer: () => (
<Stack>
Total Units:
<Box color="warning.main">{totalUnitCount}</Box>
</Stack>
),
Footer: () => <Box color="warning.main">{totalUnitCount}</Box>,
},
{
accessorKey: "nominalCapacity",
Expand All @@ -119,7 +161,7 @@ function Thermal() {
},
{
accessorKey: "installedCapacity",
header: "Installed Capacity",
header: "Enabled / Installed",
size: 50,
aggregationFn: capacityAggregationFn,
AggregatedCell: ({ cell }) => (
Expand All @@ -129,17 +171,14 @@ function Thermal() {
),
Cell: ({ row }) => (
<>
{row.original.enabledCapacity ?? 0}/
{row.original.enabledCapacity ?? 0} /{" "}
{row.original.installedCapacity ?? 0} MW
</>
),
Footer: () => (
<Stack>
Total Installed:
<Box color="warning.main">
{totalEnabledCapacity}/{totalInstalledCapacity} MW
</Box>
</Stack>
<Box color="warning.main">
{totalEnabledCapacity} / {totalInstalledCapacity} MW
</Box>
),
},
{
Expand All @@ -149,29 +188,37 @@ function Thermal() {
Cell: ({ cell }) => <>{cell.getValue<number>().toFixed(2)} €/MWh</>,
},
],
[totalEnabledCapacity, totalInstalledCapacity, totalUnitCount]
[
location.pathname,
navigate,
totalEnabledCapacity,
totalInstalledCapacity,
totalUnitCount,
]
);

////////////////////////////////////////////////////////////////
// Event handlers
////////////////////////////////////////////////////////////////

const handleCreateRow = ({ name, group }: ThermalCluster) => {
return createThermalCluster(study.id, currentArea, {
name,
group,
});
const handleCreateRow = ({
id,
installedCapacity,
enabledCapacity,
...cluster
}: ThermalCluster) => {
return createThermalCluster(study.id, currentAreaId, cluster);
};

const handleDeleteSelection = (ids: string[]) => {
return deleteThermalClusters(study.id, currentArea, ids);
return deleteThermalClusters(study.id, currentAreaId, ids);
};

////////////////////////////////////////////////////////////////
// JSX
////////////////////////////////////////////////////////////////

if (!clusters) {
if (isLoading) {
return <SimpleLoader />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,16 @@ export const POLLUTANT_NAMES: Array<keyof ThermalClusterPollutants> = [
// Functions
////////////////////////////////////////////////////////////////

const CLUSTERS_URL = (
const getClustersUrl = (
studyId: StudyMetadata["id"],
areaId: Area["name"]
): string => `/v1/studies/${studyId}/areas/${areaId}/clusters/thermal`;

const CLUSTER_URL = (
const getClusterUrl = (
studyId: StudyMetadata["id"],
areaId: Area["name"],
clusterId: Cluster["id"]
): string => `${CLUSTERS_URL(studyId, areaId)}/${clusterId}`;
): string => `${getClustersUrl(studyId, areaId)}/${clusterId}`;

async function makeRequest<T>(
method: "get" | "post" | "patch" | "delete",
Expand All @@ -132,7 +132,7 @@ export async function getThermalClusters(
studyId: StudyMetadata["id"],
areaId: Area["name"]
): Promise<ThermalCluster[]> {
return makeRequest<ThermalCluster[]>("get", CLUSTERS_URL(studyId, areaId));
return makeRequest<ThermalCluster[]>("get", getClustersUrl(studyId, areaId));
}

export async function getThermalCluster(
Expand All @@ -142,7 +142,7 @@ export async function getThermalCluster(
): Promise<ThermalCluster> {
return makeRequest<ThermalCluster>(
"get",
CLUSTER_URL(studyId, areaId, clusterId)
getClusterUrl(studyId, areaId, clusterId)
);
}

Expand All @@ -154,7 +154,7 @@ export async function updateThermalCluster(
): Promise<ThermalCluster> {
return makeRequest<ThermalCluster>(
"patch",
CLUSTER_URL(studyId, areaId, clusterId),
getClusterUrl(studyId, areaId, clusterId),
data
);
}
Expand All @@ -166,7 +166,7 @@ export async function createThermalCluster(
): Promise<ThermalCluster> {
return makeRequest<ThermalCluster>(
"post",
CLUSTERS_URL(studyId, areaId),
getClustersUrl(studyId, areaId),
data
);
}
Expand All @@ -176,7 +176,7 @@ export function deleteThermalClusters(
areaId: Area["name"],
clusterIds: Array<Cluster["id"]>
): Promise<void> {
return makeRequest<void>("delete", CLUSTERS_URL(studyId, areaId), {
return makeRequest<void>("delete", getClustersUrl(studyId, areaId), {
data: clusterIds,
});
}
Expand Down Expand Up @@ -206,5 +206,5 @@ export const capacityAggregationFn: MRT_AggregationFn<ThermalCluster> = (
{ enabledCapacitySum: 0, installedCapacitySum: 0 }
);

return `${enabledCapacitySum}/${installedCapacitySum}`;
return `${enabledCapacitySum} / ${installedCapacitySum}`;
};
Loading

0 comments on commit 304d18d

Please sign in to comment.