From bb7223451103cdb8b9862474362cafea3d851846 Mon Sep 17 00:00:00 2001
From: Hatim Dinia <33469289+hdinia@users.noreply.github.com>
Date: Tue, 17 Dec 2024 15:26:18 +0100
Subject: [PATCH] feat(ui-links): set first link as default when component
mounts (#2268)
---
webapp/public/locales/en/main.json | 6 +-
webapp/public/locales/fr/main.json | 6 +-
.../BindingConstView/index.tsx | 19 ++----
.../Modelization/BindingConstraints/index.tsx | 24 +++----
.../Modelization/Links/LinkView/LinkForm.tsx | 2 +-
.../explore/Modelization/Links/index.tsx | 48 ++++++++++----
.../explore/Modelization/index.tsx | 2 +-
.../explore/TableModeList/index.tsx | 13 +++-
.../Xpansion/Candidates/CandidateForm.tsx | 5 +-
.../explore/Xpansion/Candidates/index.tsx | 62 ++++++++++++-------
.../explore/Xpansion/Settings/index.tsx | 32 +++++-----
.../src/components/common/PropertiesView.tsx | 2 +-
.../components/common/page/ViewWrapper.tsx | 1 +
13 files changed, 134 insertions(+), 88 deletions(-)
diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json
index 98abe54461..250a75f2e6 100644
--- a/webapp/public/locales/en/main.json
+++ b/webapp/public/locales/en/main.json
@@ -253,7 +253,8 @@
"study.links": "Links",
"study.diskUsage": "Disk usage",
"study.district": "District",
- "study.bindingconstraints": "Binding Constraints",
+ "study.bindingConstraints": "Binding Constraints",
+ "study.bindingConstraints.empty": "No Binding Constraints",
"study.debug": "Debug",
"study.debug.file.unsupported": "Unsupported file type",
"study.debug.file.deleteConfirm.title": "Delete File?",
@@ -295,6 +296,7 @@
"study.outputFilters": "Output print status",
"study.outputFilters.filterByYear": "Output year by year",
"study.outputFilters.filterSynthesis": "Synthesis outputs",
+ "study.modelization.links.empty": "No links available",
"study.modelization.links.hurdleCost": "Hurdle costs",
"study.modelization.links.loopFlows": "Loop flows",
"study.modelization.links.pst": "PST",
@@ -566,6 +568,7 @@
"study.modelization.bindingConst.offset": "Offset",
"study.modelization.bindingConst.question.deleteConstraintTerm": "Are you sure you want to delete this constraint term?",
"study.modelization.bindingConst.question.deleteBindingConstraint": "Are you sure you want to delete this binding constraint?",
+ "study.tableMode.empty": "No table available",
"study.tableMode": "Table Mode",
"study.tableMode.dialog.add.title": "Add table",
"study.tableMode.dialog.edit.title": "Edit table",
@@ -703,6 +706,7 @@
"variants.commands.exportMatrices": "Export matrices",
"variants.commands.question.deleteAll": "Are you sure you want to delete all commands?",
"variants.commands.question.delete": "Are you sure you want to delete this command?",
+ "xpansion.candidates.empty": "No Xpansion candidates",
"xpansion.timeSeries": "Time-Series",
"xpansion.link": "Link",
"xpansion.annualCost": "Annual cost per MW",
diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json
index 8e64c877be..98d6ee71b9 100644
--- a/webapp/public/locales/fr/main.json
+++ b/webapp/public/locales/fr/main.json
@@ -253,7 +253,8 @@
"study.links": "Liens",
"study.diskUsage": "Espace disque",
"study.district": "District",
- "study.bindingconstraints": "Contraintes Couplantes",
+ "study.bindingConstraints": "Contraintes Couplantes",
+ "study.bindingConstraints.empty": "Aucune Contraintes Couplantes",
"study.debug": "Debug",
"study.debug.folder.empty": "Le dossier est vide",
"study.debug.file.unsupported": "Type de fichier non supporté",
@@ -295,6 +296,7 @@
"study.outputFilters": "Affichage des sorties",
"study.outputFilters.filterByYear": "Sorties année par année",
"study.outputFilters.filterSynthesis": "Sorties de la synthèse",
+ "study.modelization.links.empty": "Aucun lien disponible",
"study.modelization.links.hurdleCost": "Hurdle costs",
"study.modelization.links.loopFlows": "Loop flows",
"study.modelization.links.pst": "PST",
@@ -566,6 +568,7 @@
"study.modelization.bindingConst.offset": "Décalage",
"study.modelization.bindingConst.question.deleteConstraintTerm": "Êtes-vous sûr de vouloir supprimer ce terme ?",
"study.modelization.bindingConst.question.deleteBindingConstraint": "Êtes-vous sûr de vouloir supprimer cette contrainte couplante ?",
+ "study.tableMode.empty": "Aucun tableau disponible",
"study.tableMode": "Table Mode",
"study.tableMode.dialog.add.title": "Ajouter une table",
"study.tableMode.dialog.edit.title": "Modifier une table",
@@ -703,6 +706,7 @@
"variants.commands.exportMatrices": "Exporter les matrices",
"variants.commands.question.deleteAll": "Êtes-vous sûr de vouloir supprimer toutes les commandes ?",
"variants.commands.question.delete": "Êtes-vous sûr de vouloir supprimer cette commande ?",
+ "xpansion.candidates.empty": "Aucun candidats Xpansion",
"xpansion.timeSeries": "Séries temporelles",
"xpansion.link": "Lien",
"xpansion.annualCost": "Coût annuel par mw",
diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx
index 5b33ac97d8..6360007bef 100644
--- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx
@@ -13,7 +13,7 @@
*/
import { BindingConstraint } from "./utils";
-import { Box, Button, Paper, Skeleton } from "@mui/material";
+import { Box, Button, Skeleton } from "@mui/material";
import Form from "../../../../../../common/Form";
import UsePromiseCond, {
mergeResponses,
@@ -120,20 +120,11 @@ function BindingConstView({ constraintId }: Props) {
////////////////////////////////////////////////////////////////
return (
-
+ <>
(
- <>
+
- >
+
)}
ifPending={() => }
/>
@@ -184,7 +175,7 @@ function BindingConstView({ constraintId }: Props) {
)}
)}
-
+ >
);
}
diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/index.tsx
index 40d0f528c3..c1e04de1e0 100644
--- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/index.tsx
@@ -12,10 +12,8 @@
* This file is part of the Antares project.
*/
-import { Box } from "@mui/material";
import { useOutletContext } from "react-router";
import { StudyMetadata } from "../../../../../../common/types";
-import SimpleLoader from "../../../../../common/loaders/SimpleLoader";
import EmptyView from "../../../../../common/page/SimpleContent";
import BindingConstPropsView from "./BindingConstPropsView";
import {
@@ -31,9 +29,12 @@ import { getBindingConstraintList } from "../../../../../../services/api/studyda
import UsePromiseCond from "../../../../../common/utils/UsePromiseCond";
import { useEffect } from "react";
import SplitView from "../../../../../common/SplitView";
+import ViewWrapper from "@/components/common/page/ViewWrapper";
+import { useTranslation } from "react-i18next";
function BindingConstraints() {
const { study } = useOutletContext<{ study: StudyMetadata }>();
+ const [t] = useTranslation();
const dispatch = useAppDispatch();
const currentConstraintId = useAppSelector(getCurrentBindingConstId);
@@ -42,14 +43,13 @@ function BindingConstraints() {
getBindingConst(state, study.id),
);
- // TODO find better name
- const constraints = usePromise(
+ const constraintsRes = usePromise(
() => getBindingConstraintList(study.id),
[study.id, bindingConstraints],
);
useEffect(() => {
- const { data } = constraints;
+ const { data } = constraintsRes;
if (!data || data.length === 0 || currentConstraintId) {
return;
@@ -57,7 +57,8 @@ function BindingConstraints() {
const firstConstraintId = data[0].id;
dispatch(setCurrentBindingConst(firstConstraintId));
- }, [constraints, currentConstraintId, dispatch]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [constraintsRes.data, currentConstraintId, dispatch]);
////////////////////////////////////////////////////////////////
// Event Handlers
@@ -73,8 +74,7 @@ function BindingConstraints() {
return (
}
+ response={constraintsRes}
ifFulfilled={(data) => (
{/* Left */}
@@ -82,16 +82,16 @@ function BindingConstraints() {
list={data}
onClick={handleConstraintChange}
currentConstraint={currentConstraintId}
- reloadConstraintsList={constraints.reload}
+ reloadConstraintsList={constraintsRes.reload}
/>
{/* Right */}
-
+
{data.length > 0 && currentConstraintId ? (
) : (
-
+
)}
-
+
)}
ifRejected={(error) => }
diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx
index 9721b9b82c..dbfccc9e82 100644
--- a/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx
@@ -241,7 +241,7 @@ function LinkForm(props: Props) {
width: "100%",
display: "flex",
flexDirection: "column",
- height: "500px",
+ height: "70vh",
}}
>
{isTabMatrix ? (
diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Links/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Links/index.tsx
index f00359449c..3749bb784b 100644
--- a/webapp/src/components/App/Singlestudy/explore/Modelization/Links/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Links/index.tsx
@@ -16,30 +16,52 @@ import { useOutletContext } from "react-router";
import { StudyMetadata } from "../../../../../../common/types";
import EmptyView from "../../../../../common/page/SimpleContent";
import LinkPropsView from "./LinkPropsView";
-import useStudySynthesis from "../../../../../../redux/hooks/useStudySynthesis";
import { getCurrentLink } from "../../../../../../redux/selectors";
import useAppDispatch from "../../../../../../redux/hooks/useAppDispatch";
+import useAppSelector from "../../../../../../redux/hooks/useAppSelector";
import { setCurrentLink } from "../../../../../../redux/ducks/studySyntheses";
import LinkView from "./LinkView";
+import usePromise from "../../../../../../hooks/usePromise";
+import { getLinks } from "@/services/api/studies/links";
import UsePromiseCond from "../../../../../common/utils/UsePromiseCond";
import SplitView from "../../../../../common/SplitView";
-import ViewWrapper from "../../../../../common/page/ViewWrapper";
+import { useEffect } from "react";
+import { makeLinkId } from "@/redux/utils";
+import ViewWrapper from "@/components/common/page/ViewWrapper";
+import { useTranslation } from "react-i18next";
function Links() {
const { study } = useOutletContext<{ study: StudyMetadata }>();
- const res = useStudySynthesis({
- studyId: study.id,
- selector: getCurrentLink,
- });
-
+ const [t] = useTranslation();
const dispatch = useAppDispatch();
+ const currentLink = useAppSelector((state) =>
+ getCurrentLink(state, study.id),
+ );
+
+ const linksRes = usePromise(
+ () => getLinks({ studyId: study.id }),
+ [study.id],
+ );
+
+ // Handle automatic selection of the first link
+ useEffect(() => {
+ const { data } = linksRes;
+
+ if (!data || data.length === 0 || currentLink) {
+ return;
+ }
+
+ const firstLinkId = makeLinkId(data[0].area1, data[0].area2);
+ dispatch(setCurrentLink(firstLinkId));
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [linksRes.data, currentLink, dispatch]);
////////////////////////////////////////////////////////////////
// Event Handlers
////////////////////////////////////////////////////////////////
- const handleLinkClick = (linkName: string): void => {
- dispatch(setCurrentLink(linkName));
+ const handleLinkClick = (linkId: string) => {
+ dispatch(setCurrentLink(linkId));
};
////////////////////////////////////////////////////////////////
@@ -53,12 +75,12 @@ function Links() {
{/* Right */}
- currentLink ? (
+ response={linksRes}
+ ifFulfilled={(data) =>
+ data.length > 0 && currentLink ? (
) : (
-
+
)
}
/>
diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx
index 629e345f3c..0099e90986 100644
--- a/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx
@@ -76,7 +76,7 @@ function Modelization() {
disabled: links.length === 0,
},
{
- label: t("study.bindingconstraints"),
+ label: t("study.bindingConstraints"),
path: `${basePath}/bindingcontraint`,
},
];
diff --git a/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx b/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx
index ab4dcd787c..bf4655465a 100644
--- a/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx
@@ -12,7 +12,7 @@
* This file is part of the Antares project.
*/
-import { useState } from "react";
+import { useEffect, useState } from "react";
import { MenuItem } from "@mui/material";
import { useOutletContext } from "react-router";
import { useUpdateEffect } from "react-use";
@@ -32,6 +32,7 @@ import ConfirmationDialog from "../../../../common/dialogs/ConfirmationDialog";
import TableMode from "../../../../common/TableMode";
import SplitView from "../../../../common/SplitView";
import ViewWrapper from "../../../../common/page/ViewWrapper";
+import EmptyView from "@/components/common/page/SimpleContent";
function TableModeList() {
const { t } = useTranslation();
@@ -56,6 +57,13 @@ function TableModeList() {
const dialogTemplate =
dialog && templates.find((tp) => tp.id === dialog.templateId);
+ // Handle automatic selection of the first element
+ useEffect(() => {
+ if (templates.length > 0 && !selectedTemplate) {
+ setSelectedTemplateId(templates[0].id);
+ }
+ }, [templates, selectedTemplate]);
+
// Update local storage
useUpdateEffect(() => {
storage.setItem(
@@ -132,6 +140,9 @@ function TableModeList() {
/>
{/* Right */}
+ {!templates.length && (
+
+ )}
{selectedTemplate && (
+
)}
-
+
);
}
diff --git a/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/index.tsx b/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/index.tsx
index e9880194d4..fafe0236ed 100644
--- a/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/index.tsx
@@ -12,7 +12,7 @@
* This file is part of the Antares project.
*/
-import { useState } from "react";
+import { useEffect, useState } from "react";
import { useOutletContext, useNavigate } from "react-router-dom";
import { AxiosError } from "axios";
import { useTranslation } from "react-i18next";
@@ -45,6 +45,8 @@ import EmptyView from "../../../../../common/page/SimpleContent";
import SplitView from "../../../../../common/SplitView";
import { getLinks } from "@/services/api/studies/links";
import { MatrixDataDTO } from "@/components/common/Matrix/shared/types";
+import ViewWrapper from "@/components/common/page/ViewWrapper";
+import SimpleLoader from "@/components/common/loaders/SimpleLoader";
function Candidates() {
const [t] = useTranslation();
@@ -63,7 +65,7 @@ function Candidates() {
const {
data: candidates,
- isLoading,
+ isLoading: isCandidatesLoading,
isRejected,
reload,
} = usePromiseWithSnackbarError(
@@ -96,22 +98,30 @@ function Candidates() {
},
);
- const { data: capaLinks } = usePromiseWithSnackbarError(
- async () => {
- if (!study) {
+ const { data: capaLinks, isLoading: isLinksLoading } =
+ usePromiseWithSnackbarError(
+ async () => {
+ if (!study) {
+ return {};
+ }
+ const exist = await xpansionConfigurationExist(study.id);
+ if (exist) {
+ return {
+ capacities: await getAllCapacities(study.id),
+ links: await getLinks({ studyId: study.id }),
+ };
+ }
return {};
- }
- const exist = await xpansionConfigurationExist(study.id);
- if (exist) {
- return {
- capacities: await getAllCapacities(study.id),
- links: await getLinks({ studyId: study.id }),
- };
- }
- return {};
- },
- { errorMessage: t("xpansion.error.loadConfiguration"), deps: [study] },
- );
+ },
+ { errorMessage: t("xpansion.error.loadConfiguration"), deps: [study] },
+ );
+
+ // Handle automatic selection of the first element
+ useEffect(() => {
+ if (candidates && candidates.length > 0 && !selectedItem) {
+ setSelectedItem(candidates[0].name);
+ }
+ }, [candidates, selectedItem]);
const deleteXpansion = async () => {
try {
@@ -224,13 +234,17 @@ function Candidates() {
}
};
+ if (isCandidatesLoading || isLinksLoading) {
+ return ;
+ }
+
if (isRejected) {
return ;
}
return (
<>
-
+
-
- {renderView()}
-
+
+ {!candidates?.length ? (
+
+ ) : (
+ renderView()
+ )}
+
theme.zIndex.drawer + 1,
diff --git a/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/index.tsx b/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/index.tsx
index e418bb0220..ea11bd06a0 100644
--- a/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/index.tsx
@@ -16,7 +16,6 @@ import { useState } from "react";
import { useOutletContext } from "react-router-dom";
import { AxiosError } from "axios";
import { useTranslation } from "react-i18next";
-import { Box, Paper } from "@mui/material";
import { useSnackbar } from "notistack";
import { StudyMetadata } from "../../../../../../common/types";
import { XpansionResourceType, XpansionSettings } from "../types";
@@ -31,10 +30,11 @@ import {
} from "../../../../../../services/api/xpansion";
import SettingsForm from "./SettingsForm";
import useEnqueueErrorSnackbar from "../../../../../../hooks/useEnqueueErrorSnackbar";
-import SimpleLoader from "../../../../../common/loaders/SimpleLoader";
import { removeEmptyFields } from "../../../../../../services/utils/index";
import DataViewerDialog from "../../../../../common/dialogs/DataViewerDialog";
import usePromiseWithSnackbarError from "../../../../../../hooks/usePromiseWithSnackbarError";
+import ViewWrapper from "@/components/common/page/ViewWrapper";
+import UsePromiseCond from "@/components/common/utils/UsePromiseCond";
const resourceContentFetcher = (
resourceType: string,
@@ -56,11 +56,7 @@ function Settings() {
const enqueueErrorSnackbar = useEnqueueErrorSnackbar();
const { enqueueSnackbar } = useSnackbar();
- const {
- data: settings,
- isLoading: settingsLoading,
- reload: reloadSettings,
- } = usePromiseWithSnackbarError(
+ const settingsRes = usePromiseWithSnackbarError(
async () => {
if (study) {
return getXpansionSettings(study.id);
@@ -129,7 +125,7 @@ function Settings() {
} catch (e) {
enqueueErrorSnackbar(t("xpansion.error.updateSettings"), e as AxiosError);
} finally {
- reloadSettings();
+ settingsRes.reload();
enqueueSnackbar(t("studies.success.saveData"), {
variant: "success",
});
@@ -160,23 +156,23 @@ function Settings() {
return (
<>
- {!settingsLoading && settings ? (
-
-
+
+ (
-
-
- ) : (
-
- )}
- {!!resourceViewDialog && (
+ )}
+ />
+
+
+ {resourceViewDialog && (
)}
{onAdd && (
-
+