From 6a2eec79a5e4ee111af78a5fa0080e84788ac290 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:22:06 +0100 Subject: [PATCH 1/7] feat(tablemode): rename endpoint --- antarest/study/web/study_data_blueprint.py | 8 ++++---- tests/integration/test_integration.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index d45d4bb709..a1d74379ba 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -757,14 +757,14 @@ def set_timeseries_form_values( study_service.ts_config_manager.set_field_values(study, field_values) @bp.get( - path="/studies/{uuid}/tablemode/form", + path="/studies/{uuid}/tablemode", tags=[APITag.study_data], summary="Get table data for table form", # `Any` because `Union[AreaColumns, LinkColumns]` not working response_model=Dict[str, Dict[str, Any]], response_model_exclude_none=True, ) - def get_table_data( + def get_table_mode( uuid: str, table_type: TableTemplateType, columns: str, @@ -780,11 +780,11 @@ def get_table_data( return study_service.table_mode_manager.get_table_data(study, table_type, columns.split(",")) @bp.put( - path="/studies/{uuid}/tablemode/form", + path="/studies/{uuid}/tablemode", tags=[APITag.study_data], summary="Set table data with values from table form", ) - def set_table_data( + def set_table_mode( uuid: str, table_type: TableTemplateType, data: Dict[str, ColumnsModelTypes], diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 41bceae525..ed4f1ed8af 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -1271,7 +1271,7 @@ def test_area_management(client: TestClient, admin_access_token: str, study_id: # --- TableMode START --- - table_mode_url = f"/v1/studies/{study_id}/tablemode/form" + table_mode_url = f"/v1/studies/{study_id}/tablemode" # Table Mode - Area From ece3de8480ecdeb6ba442e032f17f40187eb0899 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:16:39 +0100 Subject: [PATCH 2/7] feat(ui-common): update TableForm * rename component * add `autoSubmit` and `enableUndoRedo` props * keep save button visible at bottom * use `setValues` instead of `setValue` table --- .../ScenarioBuilderDialog/tabs/Table.tsx | 4 +- .../dialogs/ScenarioPlaylistDialog/index.tsx | 4 +- .../TimeSeriesManagement/Fields.tsx | 2 +- .../Map/MapConfig/Districts/index.tsx | 4 +- .../Map/MapConfig/Layers/index.tsx | 4 +- webapp/src/components/common/Form/index.tsx | 5 +-- .../common/{FormTable => TableForm}/Table.tsx | 19 ++++++++-- .../common/{FormTable => TableForm}/index.tsx | 38 ++++++++++++------- .../common/{FormTable => TableForm}/utils.ts | 0 9 files changed, 51 insertions(+), 29 deletions(-) rename webapp/src/components/common/{FormTable => TableForm}/Table.tsx (85%) rename webapp/src/components/common/{FormTable => TableForm}/index.tsx (75%) rename webapp/src/components/common/{FormTable => TableForm}/utils.ts (100%) diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx index aeff62fb0d..61fc35ffe2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx @@ -11,7 +11,7 @@ import { getAreas, getLinks, } from "../../../../../../../../../redux/selectors"; -import FormTable from "../../../../../../../../common/FormTable"; +import TableForm from "../../../../../../../../common/TableForm"; import ConfigContext from "../ConfigContext"; import { updateScenarioBuilderConfig } from "../utils"; import { SubmitHandlerPlus } from "../../../../../../../../common/Form/types"; @@ -116,7 +116,7 @@ function Table(props: Props) { //////////////////////////////////////////////////////////////// return ( - - + diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx index e012a6f0d6..901f6f3dec 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx @@ -10,7 +10,7 @@ import { getStudyMapDistrictsById, } from "../../../../../../../../redux/selectors"; import { SubmitHandlerPlus } from "../../../../../../../common/Form/types"; -import FormTable from "../../../../../../../common/FormTable"; +import TableForm from "../../../../../../../common/TableForm"; import CreateDistrictDialog from "./CreateDistrictDialog"; import useAppDispatch from "../../../../../../../../redux/hooks/useAppDispatch"; import { updateStudyMapDistrict } from "../../../../../../../../redux/ducks/studyMaps"; @@ -123,7 +123,7 @@ function Districts() { {columns.length > 0 && ( - {columns.length > 0 && ( - ( return ( ( )} {showFooter && ( - + {showSubmitButton && ( <> @@ -47,9 +48,18 @@ function Table(props: TableProps) { const handleAfterChange: HandsontableProps["afterChange"] = function afterChange(this: unknown, changes, ...rest): void { - changes?.forEach(([row, column, _, nextValue]) => { - setValue(`${data[row].id}.${column}`, nextValue); - }); + const newValues = changes?.reduce( + (acc, [row, column, _, nextValue]) => { + acc[`${data[row].id}.${column}`] = nextValue; + return acc; + }, + {} as Record, + ); + + if (newValues) { + setValues(newValues); + } + restProps.afterChange?.call(this, changes, ...rest); }; @@ -59,6 +69,7 @@ function Table(props: TableProps) { return ( >; -export interface FormTableProps< +export interface TableFormProps< TFieldValues extends TableFieldValuesByRow = TableFieldValuesByRow, > { defaultValues: DefaultValues; onSubmit?: FormProps["onSubmit"]; onInvalid?: FormProps["onInvalid"]; + autoSubmit?: FormProps["autoSubmit"]; + enableUndoRedo?: FormProps["enableUndoRedo"]; formApiRef?: FormProps["apiRef"]; sx?: SxProps; tableProps?: Omit & { @@ -34,13 +36,15 @@ export interface FormTableProps< }; } -function FormTable( - props: FormTableProps, +function TableForm( + props: TableFormProps, ) { const { defaultValues, onSubmit, onInvalid, + autoSubmit = true, // TODO: change to false after testing all table forms + enableUndoRedo, sx, formApiRef, tableProps = {}, @@ -84,25 +88,33 @@ function FormTable( config={{ defaultValues }} onSubmit={onSubmit} onInvalid={onInvalid} - autoSubmit + autoSubmit={autoSubmit} + enableUndoRedo={enableUndoRedo} sx={mergeSxProp( { width: 1, height: 1, - pt: 0, - overflow: "hidden", // https://handsontable.com/docs/12.0/grid-size/#define-the-size-in-css-styles + display: "flex", + flexDirection: "column", }, sx, )} apiRef={formApiRef} > -
+ +
+ ); } -export default FormTable; +export default TableForm; diff --git a/webapp/src/components/common/FormTable/utils.ts b/webapp/src/components/common/TableForm/utils.ts similarity index 100% rename from webapp/src/components/common/FormTable/utils.ts rename to webapp/src/components/common/TableForm/utils.ts From 9563f5d197736d5ee7c0bed2124ed8abb512165b Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:02:24 +0100 Subject: [PATCH 3/7] feat(ui-utils): add `format` in stringUtils --- webapp/src/components/App/Settings/Groups/index.tsx | 2 +- webapp/src/components/App/Settings/Tokens/index.tsx | 2 +- webapp/src/components/App/Settings/Users/index.tsx | 2 +- .../dialogs/ScenarioBuilderDialog/tabs/Thermal.tsx | 2 +- .../General/dialogs/ThematicTrimmingDialog/index.tsx | 2 +- .../Singlestudy/explore/Modelization/Map/Areas/index.tsx | 2 +- .../Singlestudy/explore/Results/ResultDetails/index.tsx | 2 +- webapp/src/utils/{textUtils.ts => stringUtils.ts} | 9 +++++++++ webapp/src/utils/studiesUtils.ts | 2 +- 9 files changed, 17 insertions(+), 8 deletions(-) rename webapp/src/utils/{textUtils.ts => stringUtils.ts} (56%) diff --git a/webapp/src/components/App/Settings/Groups/index.tsx b/webapp/src/components/App/Settings/Groups/index.tsx index bbabb41bb6..b662134427 100644 --- a/webapp/src/components/App/Settings/Groups/index.tsx +++ b/webapp/src/components/App/Settings/Groups/index.tsx @@ -31,7 +31,7 @@ import Header from "./Header"; import UpdateGroupDialog from "./dialog/UpdateGroupDialog"; import { getAuthUser } from "../../../../redux/selectors"; import useAppSelector from "../../../../redux/hooks/useAppSelector"; -import { isSearchMatching } from "../../../../utils/textUtils"; +import { isSearchMatching } from "../../../../utils/stringUtils"; /** * Types diff --git a/webapp/src/components/App/Settings/Tokens/index.tsx b/webapp/src/components/App/Settings/Tokens/index.tsx index fce914d821..bbbe3d192e 100644 --- a/webapp/src/components/App/Settings/Tokens/index.tsx +++ b/webapp/src/components/App/Settings/Tokens/index.tsx @@ -35,7 +35,7 @@ import Header from "./Header"; import { getAuthUser } from "../../../../redux/selectors"; import TokenInfoDialog from "./dialog/TokenInfoDialog"; import useAppSelector from "../../../../redux/hooks/useAppSelector"; -import { isSearchMatching } from "../../../../utils/textUtils"; +import { isSearchMatching } from "../../../../utils/stringUtils"; /** * Types diff --git a/webapp/src/components/App/Settings/Users/index.tsx b/webapp/src/components/App/Settings/Users/index.tsx index 66d63c40dd..7af7ee658d 100644 --- a/webapp/src/components/App/Settings/Users/index.tsx +++ b/webapp/src/components/App/Settings/Users/index.tsx @@ -29,7 +29,7 @@ import { RESERVED_USER_NAMES } from "../utils"; import { UserDetailsDTO } from "../../../../common/types"; import UpdateUserDialog from "./dialog/UpdateUserDialog"; import { sortByName } from "../../../../services/utils"; -import { isSearchMatching } from "../../../../utils/textUtils"; +import { isSearchMatching } from "../../../../utils/stringUtils"; /** * Types diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Thermal.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Thermal.tsx index 79fd84fc80..f84cf81a78 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Thermal.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Thermal.tsx @@ -1,7 +1,7 @@ import { useContext, useEffect, useMemo, useState } from "react"; import useStudySynthesis from "../../../../../../../../../redux/hooks/useStudySynthesis"; import { getAreas } from "../../../../../../../../../redux/selectors"; -import { isSearchMatching } from "../../../../../../../../../utils/textUtils"; +import { isSearchMatching } from "../../../../../../../../../utils/stringUtils"; import PropertiesView from "../../../../../../../../common/PropertiesView"; import SplitLayoutView from "../../../../../../../../common/SplitLayoutView"; import UsePromiseCond from "../../../../../../../../common/utils/UsePromiseCond"; diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx index dff0551500..290ca05c3c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx @@ -9,7 +9,7 @@ import { UseFormReturnPlus, } from "../../../../../../../common/Form/types"; import SearchFE from "../../../../../../../common/fieldEditors/SearchFE"; -import { isSearchMatching } from "../../../../../../../../utils/textUtils"; +import { isSearchMatching } from "../../../../../../../../utils/stringUtils"; import FormDialog from "../../../../../../../common/dialogs/FormDialog"; import { getFieldNames, diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/Areas/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/Areas/index.tsx index 74680200cd..104dada6f6 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/Areas/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/Areas/index.tsx @@ -11,7 +11,7 @@ import { } from "../../../../../../../redux/selectors"; import useAppDispatch from "../../../../../../../redux/hooks/useAppDispatch"; import AreaConfig from "./AreaConfig"; -import { isSearchMatching } from "../../../../../../../utils/textUtils"; +import { isSearchMatching } from "../../../../../../../utils/stringUtils"; import { setCurrentArea } from "../../../../../../../redux/ducks/studySyntheses"; import { StudyMapNode } from "../../../../../../../redux/ducks/studyMaps"; diff --git a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx index 1f6b4a9bdc..eab7f08950 100644 --- a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx @@ -27,7 +27,7 @@ import { getStudyOutput, } from "../../../../../../redux/selectors"; import { getStudyData } from "../../../../../../services/api/study"; -import { isSearchMatching } from "../../../../../../utils/textUtils"; +import { isSearchMatching } from "../../../../../../utils/stringUtils"; import EditableMatrix from "../../../../../common/EditableMatrix"; import PropertiesView from "../../../../../common/PropertiesView"; import SplitLayoutView from "../../../../../common/SplitLayoutView"; diff --git a/webapp/src/utils/textUtils.ts b/webapp/src/utils/stringUtils.ts similarity index 56% rename from webapp/src/utils/textUtils.ts rename to webapp/src/utils/stringUtils.ts index d5ad945296..1b83ea6b6d 100644 --- a/webapp/src/utils/textUtils.ts +++ b/webapp/src/utils/stringUtils.ts @@ -9,3 +9,12 @@ export const isSearchMatching = R.curry( return RA.ensureArray(values).find(isMatching); }, ); + +/** + * Formats a string with values. + * @example + * format("Hello {name}", { name: "John" }); // returns "Hello John" + */ +export function format(str: string, values: Record): string { + return str.replace(/{([a-zA-Z0-9]+)}/g, (_, key) => values[key]); +} diff --git a/webapp/src/utils/studiesUtils.ts b/webapp/src/utils/studiesUtils.ts index a4184f5a24..ca3fc3da57 100644 --- a/webapp/src/utils/studiesUtils.ts +++ b/webapp/src/utils/studiesUtils.ts @@ -3,7 +3,7 @@ import * as R from "ramda"; import * as RA from "ramda-adjunct"; import { StudyMetadata, StudyType } from "../common/types"; import { StudiesSortConf, StudyFilters } from "../redux/ducks/studies"; -import { isSearchMatching } from "./textUtils"; +import { isSearchMatching } from "./stringUtils"; //////////////////////////////////////////////////////////////// // Sort From f87ca422be874e8a47aeda473b6925d0afe8d995 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Fri, 5 Jan 2024 18:03:24 +0100 Subject: [PATCH 4/7] feat(ui-tablemode): update Table Mode list view * move to top menu * rename component TableModeList * remove frozen table templates * create the generic component TableMode * update style of api functions --- webapp/public/locales/en/main.json | 11 +- webapp/public/locales/fr/main.json | 12 +- .../explore/Modelization/TableMode/index.tsx | 196 ------------------ .../explore/Modelization/TableMode/utils.ts | 130 ------------ .../explore/Modelization/index.tsx | 4 - .../dialogs/CreateTemplateTableDialog.tsx | 12 +- .../dialogs/TableTemplateFormDialog.tsx | 17 +- .../dialogs/UpdateTemplateTableDialog.tsx | 4 +- .../explore/TableModeList/index.tsx | 163 +++++++++++++++ .../explore/TableModeList/utils.ts | 37 ++++ .../src/components/App/Singlestudy/index.tsx | 4 + webapp/src/components/App/index.tsx | 4 +- webapp/src/components/common/TableMode.tsx | 56 +++++ webapp/src/services/api/constants.ts | 4 + webapp/src/services/api/forms/tableMode.ts | 39 ---- .../api/studies/tableMode/constants.ts | 73 +++++++ .../services/api/studies/tableMode/index.ts | 35 ++++ .../services/api/studies/tableMode/type.ts | 12 ++ webapp/src/services/utils/localStorage.ts | 2 +- 19 files changed, 408 insertions(+), 407 deletions(-) delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/utils.ts rename webapp/src/components/App/Singlestudy/explore/{Modelization/TableMode => TableModeList}/dialogs/CreateTemplateTableDialog.tsx (84%) rename webapp/src/components/App/Singlestudy/explore/{Modelization/TableMode => TableModeList}/dialogs/TableTemplateFormDialog.tsx (82%) rename webapp/src/components/App/Singlestudy/explore/{Modelization/TableMode => TableModeList}/dialogs/UpdateTemplateTableDialog.tsx (90%) create mode 100644 webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts create mode 100644 webapp/src/components/common/TableMode.tsx create mode 100644 webapp/src/services/api/constants.ts delete mode 100644 webapp/src/services/api/forms/tableMode.ts create mode 100644 webapp/src/services/api/studies/tableMode/constants.ts create mode 100644 webapp/src/services/api/studies/tableMode/index.ts create mode 100644 webapp/src/services/api/studies/tableMode/type.ts diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index b4a9e308f7..72f8f6ed00 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -266,13 +266,6 @@ "study.modelization.links.matrix.columns.loopFlow": "Loop flow", "study.modelization.links.matrix.columns.pShiftMin": "P.Shift Min", "study.modelization.links.matrix.columns.pShiftMax": "P.Shift Max", - "study.modelization.tableMode": "Table Mode", - "study.modelization.tableMode.template.economicOpt": "Economic Opt.", - "study.modelization.tableMode.template.geographicTrimmingAreas": "Geographic Trimming (areas)", - "study.modelization.tableMode.template.geographicTrimmingLinks": "Geographic Trimming (links)", - "study.modelization.tableMode.dialog.add.title": "Add table", - "study.modelization.tableMode.dialog.edit.title": "Edit table", - "study.modelization.tableMode.dialog.delete.text": "Are you sure you want to delete '{{0}}' table?", "study.configuration.general.legend.simulation": "Simulation", "study.configuration.general.legend.calendar": "Calendar", "study.configuration.general.legend.monteCarloScenarios": "Monte-Carlo Scenarios", @@ -494,6 +487,10 @@ "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": "Table Mode", + "study.tableMode.dialog.add.title": "Add table", + "study.tableMode.dialog.edit.title": "Edit table", + "study.tableMode.dialog.delete.text": "Are you sure you want to delete '{{name}}' table?", "study.results.mc": "Monte-Carlo", "study.results.display": "Display", "study.results.temporality": "Temporality", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index 2a8d80a726..fcee42b459 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -195,7 +195,6 @@ "study.archiveOutputMode": "Mode archivé", "study.postProcessing": "Post-traitement", "study.timeLimit": "Limite de temps (h)", - "study.timeLimitHelper": "(heures) max: {{max}}h", "study.nbCpu": "Nombre de coeurs", "study.clusterLoad": "Charge du cluster", "study.synthesis": "Synthèse", @@ -267,13 +266,6 @@ "study.modelization.links.matrix.columns.loopFlow": "Loop flow", "study.modelization.links.matrix.columns.pShiftMin": "P.Shift Min", "study.modelization.links.matrix.columns.pShiftMax": "P.Shift Max", - "study.modelization.tableMode": "Table Mode", - "study.modelization.tableMode.template.economicOpt": "Options économiques", - "study.modelization.tableMode.template.geographicTrimmingAreas": "Filtre géographique (zones)", - "study.modelization.tableMode.template.geographicTrimmingLinks": "Filtre géographique (liens)", - "study.modelization.tableMode.dialog.add.title": "Ajouter une table", - "study.modelization.tableMode.dialog.edit.title": "Modifier une table", - "study.modelization.tableMode.dialog.delete.text": "Êtes-vous sûr de vouloir supprimer la table '{{0}}' ?", "study.configuration.general.legend.simulation": "Simulation", "study.configuration.general.legend.calendar": "Calendrier", "study.configuration.general.legend.monteCarloScenarios": "Scénarios Monte-Carlo", @@ -495,6 +487,10 @@ "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": "Table Mode", + "study.tableMode.dialog.add.title": "Ajouter une table", + "study.tableMode.dialog.edit.title": "Modifier une table", + "study.tableMode.dialog.delete.text": "Êtes-vous sûr de vouloir supprimer la table '{{name}}' ?", "study.results.mc": "Monte-Carlo", "study.results.display": "Affichage", "study.results.temporality": "Temporalité", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx deleted file mode 100644 index 3c41a7d932..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { useState } from "react"; -import { MenuItem } from "@mui/material"; -import { useOutletContext } from "react-router"; -import { useUpdateEffect } from "react-use"; -import { useTranslation } from "react-i18next"; -import DeleteIcon from "@mui/icons-material/Delete"; -import { v4 as uuidv4 } from "uuid"; -import PropertiesView from "../../../../../common/PropertiesView"; -import SplitLayoutView from "../../../../../common/SplitLayoutView"; -import ListElement from "../../common/ListElement"; -import usePromise from "../../../../../../hooks/usePromise"; -import UsePromiseCond from "../../../../../common/utils/UsePromiseCond"; -import { SubmitHandlerPlus } from "../../../../../common/Form/types"; -import FormTable from "../../../../../common/FormTable"; -import { - DEFAULT_TABLE_TEMPLATES, - DEFAULT_TABLE_TEMPLATE_IDS, - TableData, - TableTemplate, -} from "./utils"; -import storage, { - StorageKey, -} from "../../../../../../services/utils/localStorage"; -import { StudyMetadata } from "../../../../../../common/types"; -import CreateTemplateTableDialog from "./dialogs/CreateTemplateTableDialog"; -import UpdateTemplateTableDialog from "./dialogs/UpdateTemplateTableDialog"; -import ConfirmationDialog from "../../../../../common/dialogs/ConfirmationDialog"; -import * as api from "../../../../../../services/api/forms/tableMode"; - -function TableMode() { - const { t } = useTranslation(); - - const [templates, setTemplates] = useState(() => [ - ...DEFAULT_TABLE_TEMPLATES.map((tp) => ({ - ...tp, - name: t(`study.modelization.tableMode.template.${tp.name}`), - })), - ...(storage.getItem(StorageKey.StudiesModelTableModeTemplates) || []).map( - (tp) => ({ ...tp, id: uuidv4() }), - ), - ]); - - const [selectedTemplateId, setSelectedTemplateId] = useState(templates[0].id); - - const [dialog, setDialog] = useState<{ - type: "add" | "edit" | "delete"; - templateId: TableTemplate["id"]; - } | null>(null); - - const { study } = useOutletContext<{ study: StudyMetadata }>(); - - const selectedTemplate = - templates.find((tp) => tp.id === selectedTemplateId) || templates[0]; - - const res = usePromise(async () => { - const { type, columns } = selectedTemplate; - return api.getTableData(study.id, type, columns); - }, [selectedTemplate]); - - // Update local storage - useUpdateEffect(() => { - storage.setItem( - StorageKey.StudiesModelTableModeTemplates, - templates - .filter((tp) => !DEFAULT_TABLE_TEMPLATE_IDS.includes(tp.id)) - // It is useless to keep template ids in local storage - .map(({ id, ...rest }) => rest), - ); - }, [templates]); - - //////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////// - - const closeDialog = () => setDialog(null); - - //////////////////////////////////////////////////////////////// - // Event Handlers - //////////////////////////////////////////////////////////////// - - const handleSubmit = (data: SubmitHandlerPlus) => { - return api.setTableData(study.id, selectedTemplate.type, data.dirtyValues); - }; - - const handleDeleteTemplate = () => { - setTemplates((templates) => - templates.filter((tp) => tp.id !== dialog?.templateId), - ); - closeDialog(); - }; - - //////////////////////////////////////////////////////////////// - // JSX - //////////////////////////////////////////////////////////////// - - return ( - <> - setSelectedTemplateId(id)} - contextMenuContent={({ element, close }) => { - const isNotAllowed = DEFAULT_TABLE_TEMPLATE_IDS.includes( - element.id, - ); - return ( - <> - { - event.stopPropagation(); - setDialog({ - type: "edit", - templateId: element.id, - }); - close(); - }} - disabled={isNotAllowed} - > - Edit - - { - event.stopPropagation(); - setDialog({ - type: "delete", - templateId: element.id, - }); - close(); - }} - disabled={isNotAllowed} - > - Delete - - - ); - }} - /> - } - onAdd={() => setDialog({ type: "add", templateId: "" })} - /> - } - right={ - ( - - )} - /> - } - /> - {dialog?.type === "add" && ( - - )} - {dialog?.type === "edit" && ( - tp.id === dialog.templateId) as TableTemplate - } - templates={templates} - setTemplates={setTemplates} - onCancel={closeDialog} - open - /> - )} - {dialog?.type === "delete" && ( - - {t("study.modelization.tableMode.dialog.delete.text", { - 0: templates.find((tp) => tp.id === dialog.templateId)?.name, - })} - - )} - - ); -} - -export default TableMode; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/utils.ts deleted file mode 100644 index 4c85691af3..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/utils.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -export enum TableTemplateType { - Area = "area", - Link = "link", - Cluster = "cluster", - Renewable = "renewable", - BindingConstraint = "binding constraint", -} - -export const TABLE_TEMPLATE_TYPE_OPTIONS = Object.values(TableTemplateType); - -const TABLE_TEMPLATE_COLUMNS_BY_TYPE = { - [TableTemplateType.Area]: [ - // Optimization - Nodal optimization - "nonDispatchablePower", - "dispatchableHydroPower", - "otherDispatchablePower", - "averageUnsuppliedEnergyCost", - "spreadUnsuppliedEnergyCost", - "averageSpilledEnergyCost", - "spreadSpilledEnergyCost", - // Optimization - Filtering - "filterSynthesis", - "filterYearByYear", - // Adequacy patch - "adequacyPatchMode", - ], - [TableTemplateType.Link]: [ - "hurdlesCost", - "loopFlow", - "usePhaseShifter", - "transmissionCapacities", - "assetType", - "linkStyle", - "linkWidth", - "displayComments", - "filterSynthesis", - "filterYearByYear", - ], - [TableTemplateType.Cluster]: [ - "group", - "enabled", - "mustRun", - "unitCount", - "nominalCapacity", - "minStablePower", - "spinning", - "minUpTime", - "minDownTime", - "co2", - "marginalCost", - "fixedCost", - "startupCost", - "marketBidCost", - "spreadCost", - "tsGen", - "volatilityForced", - "volatilityPlanned", - "lawForced", - "lawPlanned", - ], - [TableTemplateType.Renewable]: [ - "group", - "tsInterpretation", - "enabled", - "unitCount", - "nominalCapacity", - ], - [TableTemplateType.BindingConstraint]: ["type", "operator", "enabled"], -} as const; - -export type TableTemplateColumnsForType = Array< - (typeof TABLE_TEMPLATE_COLUMNS_BY_TYPE)[T][number] ->; - -export interface TableTemplate< - T extends TableTemplateType = TableTemplateType, -> { - id: string; - name: string; - type: T; - columns: TableTemplateColumnsForType; - frozen: true; -} - -/** - * Allows to check columns validity for specified type. - */ -export function createTableTemplate( - name: string, - type: T, - columns: TableTemplateColumnsForType, -): TableTemplate { - return { id: uuidv4(), name, type, columns, frozen: true }; -} - -export const DEFAULT_TABLE_TEMPLATES: TableTemplate[] = [ - createTableTemplate("economicOpt", TableTemplateType.Area, [ - "averageUnsuppliedEnergyCost", - "spreadUnsuppliedEnergyCost", - "averageSpilledEnergyCost", - "spreadSpilledEnergyCost", - "nonDispatchablePower", - "dispatchableHydroPower", - "otherDispatchablePower", - ]), - createTableTemplate("geographicTrimmingAreas", TableTemplateType.Area, [ - "filterYearByYear", - "filterSynthesis", - ]), - createTableTemplate("geographicTrimmingLinks", TableTemplateType.Link, [ - "filterYearByYear", - "filterSynthesis", - ]), -]; - -export const DEFAULT_TABLE_TEMPLATE_IDS = DEFAULT_TABLE_TEMPLATES.map( - (t) => t.id, -); - -export function getTableColumnsForType(type: TableTemplateType): string[] { - // Arrays have a numeric index signature because of `as const` - return Object.values(TABLE_TEMPLATE_COLUMNS_BY_TYPE[type]); -} - -export type TableData = Record< - string, - Record ->; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx index 9a5ee203ec..121c3fdc58 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx @@ -62,10 +62,6 @@ function Modelization() { label: t("study.debug"), path: `${basePath}/debug`, }, - { - label: t("study.modelization.tableMode"), - path: `${basePath}/tablemode`, - }, ]; }, [areaId, areas, dispatch, navigate, study?.id, t]); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/dialogs/CreateTemplateTableDialog.tsx b/webapp/src/components/App/Singlestudy/explore/TableModeList/dialogs/CreateTemplateTableDialog.tsx similarity index 84% rename from webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/dialogs/CreateTemplateTableDialog.tsx rename to webapp/src/components/App/Singlestudy/explore/TableModeList/dialogs/CreateTemplateTableDialog.tsx index 24c4aea21c..24f7accc77 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/dialogs/CreateTemplateTableDialog.tsx +++ b/webapp/src/components/App/Singlestudy/explore/TableModeList/dialogs/CreateTemplateTableDialog.tsx @@ -1,14 +1,10 @@ import { useTranslation } from "react-i18next"; import AddCircleIcon from "@mui/icons-material/AddCircle"; -import { - createTableTemplate, - TableTemplate, - TableTemplateType, -} from "../utils"; +import { createTableTemplate, type TableTemplate } from "../utils"; import TableTemplateFormDialog, { TableTemplateFormDialogProps, } from "./TableTemplateFormDialog"; -import { SubmitHandlerPlus } from "../../../../../../common/Form/types"; +import { SubmitHandlerPlus } from "../../../../../common/Form/types"; interface Props extends Pick { @@ -42,12 +38,12 @@ function CreateTemplateTableDialog(props: Props) { return ( resetField("columns")} name="type" diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/dialogs/UpdateTemplateTableDialog.tsx b/webapp/src/components/App/Singlestudy/explore/TableModeList/dialogs/UpdateTemplateTableDialog.tsx similarity index 90% rename from webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/dialogs/UpdateTemplateTableDialog.tsx rename to webapp/src/components/App/Singlestudy/explore/TableModeList/dialogs/UpdateTemplateTableDialog.tsx index 31238d8b91..ba8569d0a3 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/dialogs/UpdateTemplateTableDialog.tsx +++ b/webapp/src/components/App/Singlestudy/explore/TableModeList/dialogs/UpdateTemplateTableDialog.tsx @@ -4,7 +4,7 @@ import TableTemplateFormDialog, { TableTemplateFormDialogProps, } from "./TableTemplateFormDialog"; import { TableTemplate } from "../utils"; -import { SubmitHandlerPlus } from "../../../../../../common/Form/types"; +import { SubmitHandlerPlus } from "../../../../../common/Form/types"; interface Props extends Pick { @@ -36,7 +36,7 @@ function UpdateTemplateTableDialog(props: Props) { return ( (() => { + const list = + storage.getItem(StorageKey.StudiesModelTableModeTemplates) || []; + return list.map((tp) => ({ ...tp, id: uuidv4() })); + }); + + const [selectedTemplateId, setSelectedTemplateId] = useState< + TableTemplate["id"] | undefined + >(templates[0]?.id); + + const [dialog, setDialog] = useState<{ + type: "add" | "edit" | "delete"; + templateId: TableTemplate["id"]; + } | null>(null); + + const { study } = useOutletContext<{ study: StudyMetadata }>(); + const selectedTemplate = templates.find((tp) => tp.id === selectedTemplateId); + const dialogTemplate = + dialog && templates.find((tp) => tp.id === dialog.templateId); + + // Update local storage + useUpdateEffect(() => { + storage.setItem( + StorageKey.StudiesModelTableModeTemplates, + templates + // It is useless to keep template ids in local storage + .map(({ id, ...rest }) => rest), + ); + }, [templates]); + + //////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////// + + const closeDialog = () => setDialog(null); + + //////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////// + + const handleDeleteTemplate = () => { + setTemplates((templates) => + templates.filter((tp) => tp.id !== dialog?.templateId), + ); + closeDialog(); + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + <> + setSelectedTemplateId(id)} + contextMenuContent={({ element, close }) => ( + <> + { + event.stopPropagation(); + setDialog({ + type: "edit", + templateId: element.id, + }); + close(); + }} + > + Edit + + { + event.stopPropagation(); + setDialog({ + type: "delete", + templateId: element.id, + }); + close(); + }} + > + Delete + + + )} + /> + } + onAdd={() => setDialog({ type: "add", templateId: "" })} + /> + } + right={ + selectedTemplate && ( + + ) + } + /> + {dialog?.type === "add" && ( + + )} + {dialog?.type === "edit" && dialogTemplate && ( + + )} + {dialog?.type === "delete" && dialogTemplate && ( + + {t("study.tableMode.dialog.delete.text", { + name: dialogTemplate.name, + })} + + )} + + ); +} + +export default TableModeList; diff --git a/webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts b/webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts new file mode 100644 index 0000000000..bcd307a954 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts @@ -0,0 +1,37 @@ +import { v4 as uuidv4 } from "uuid"; +import { + TableModeColumnsForType, + TableModeType, +} from "../../../../../services/api/studies/tableMode/type"; +import { TABLE_MODE_COLUMNS_BY_TYPE } from "../../../../../services/api/studies/tableMode/constants"; + +//////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////// + +export interface TableTemplate { + id: string; + name: string; + type: T; + columns: TableModeColumnsForType; +} + +//////////////////////////////////////////////////////////////// +// Functions +//////////////////////////////////////////////////////////////// + +/** + * Allows to check columns validity for specified type. + */ +export function createTableTemplate( + name: string, + type: T, + columns: TableModeColumnsForType, +): TableTemplate { + return { id: uuidv4(), name, type, columns }; +} + +export function getTableColumnsForType(type: TableModeType): readonly string[] { + // Arrays have a numeric index signature because of `as const` + return TABLE_MODE_COLUMNS_BY_TYPE[type]; +} diff --git a/webapp/src/components/App/Singlestudy/index.tsx b/webapp/src/components/App/Singlestudy/index.tsx index 0aa33abc1a..10f8b87f4f 100644 --- a/webapp/src/components/App/Singlestudy/index.tsx +++ b/webapp/src/components/App/Singlestudy/index.tsx @@ -57,6 +57,10 @@ function SingleStudy(props: Props) { label: t("study.configuration"), path: `/studies/${studyId}/explore/configuration`, }, + { + label: t("study.tableMode"), + path: `/studies/${studyId}/explore/tablemode`, + }, { label: "Xpansion", path: `/studies/${studyId}/explore/xpansion` }, { label: t("study.results"), diff --git a/webapp/src/components/App/index.tsx b/webapp/src/components/App/index.tsx index 1a7fda5c94..e34ca31a37 100644 --- a/webapp/src/components/App/index.tsx +++ b/webapp/src/components/App/index.tsx @@ -41,7 +41,7 @@ import Renewables from "./Singlestudy/explore/Modelization/Areas/Renewables"; import ResultDetails from "./Singlestudy/explore/Results/ResultDetails"; import Constraints from "./Singlestudy/explore/Xpansion/Constraints"; import Weights from "./Singlestudy/explore/Xpansion/Weights"; -import TableMode from "./Singlestudy/explore/Modelization/TableMode"; +import TableModeList from "./Singlestudy/explore/TableModeList"; import ManagementOptions from "./Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions"; import { HYDRO_ROUTES, @@ -141,7 +141,6 @@ function App() { element={} /> } /> - } /> } /> } /> @@ -149,6 +148,7 @@ function App() { path="configuration" element={} /> + } /> }> } /> { + studyId: StudyMetadata["id"]; + type: T; + columns: TableModeColumnsForType; +} + +function TableMode(props: TableModeProps) { + const { studyId, type, columns } = props; + + const res = usePromise(async () => { + return getTableMode(studyId, type, columns); + }, [studyId, type, JSON.stringify(columns)]); + + //////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////// + + const handleSubmit = (data: SubmitHandlerPlus) => { + return setTableMode(studyId, type, data.dirtyValues); + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + ( + + )} + /> + ); +} + +export default TableMode; diff --git a/webapp/src/services/api/constants.ts b/webapp/src/services/api/constants.ts new file mode 100644 index 0000000000..421ab814ed --- /dev/null +++ b/webapp/src/services/api/constants.ts @@ -0,0 +1,4 @@ +const API_URL_BASE = "v1"; +const STUDIES_API_URL = `${API_URL_BASE}/studies/{studyId}`; + +export const TABLE_MODE_API_URL = `${STUDIES_API_URL}/tablemode`; diff --git a/webapp/src/services/api/forms/tableMode.ts b/webapp/src/services/api/forms/tableMode.ts deleted file mode 100644 index ab146fa1fc..0000000000 --- a/webapp/src/services/api/forms/tableMode.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { snakeCase } from "lodash"; -import { DeepPartial } from "react-hook-form"; -import { StudyMetadata } from "../../../common/types"; -import { - TableData, - TableTemplateColumnsForType, - TableTemplateType, -} from "../../../components/App/Singlestudy/explore/Modelization/TableMode/utils"; -import client from "../client"; - -function makeRequestURL(studyId: StudyMetadata["id"]): string { - return `v1/studies/${studyId}/tablemode/form`; -} - -export async function getTableData( - studyId: StudyMetadata["id"], - type: T, - columns: TableTemplateColumnsForType, -): Promise { - const res = await client.get(makeRequestURL(studyId), { - params: { - table_type: type, - columns: columns.map(snakeCase).join(","), - }, - }); - return res.data; -} - -export function setTableData( - studyId: StudyMetadata["id"], - type: TableTemplateType, - data: DeepPartial, -): Promise { - return client.put(makeRequestURL(studyId), data, { - params: { - table_type: type, - }, - }); -} diff --git a/webapp/src/services/api/studies/tableMode/constants.ts b/webapp/src/services/api/studies/tableMode/constants.ts new file mode 100644 index 0000000000..70526c7484 --- /dev/null +++ b/webapp/src/services/api/studies/tableMode/constants.ts @@ -0,0 +1,73 @@ +const AREA = "area"; +const LINK = "link"; +const CLUSTER = "cluster"; +const RENEWABLE = "renewable"; +const BINDING_CONSTRAINT = "binding constraint"; + +export const TABLE_MODE_TYPES = [ + AREA, + LINK, + CLUSTER, + RENEWABLE, + BINDING_CONSTRAINT, +] as const; + +export const TABLE_MODE_COLUMNS_BY_TYPE = { + [AREA]: [ + // Optimization - Nodal optimization + "nonDispatchablePower", + "dispatchableHydroPower", + "otherDispatchablePower", + "averageUnsuppliedEnergyCost", + "spreadUnsuppliedEnergyCost", + "averageSpilledEnergyCost", + "spreadSpilledEnergyCost", + // Optimization - Filtering + "filterSynthesis", + "filterYearByYear", + // Adequacy patch + "adequacyPatchMode", + ], + [LINK]: [ + "hurdlesCost", + "loopFlow", + "usePhaseShifter", + "transmissionCapacities", + "assetType", + "linkStyle", + "linkWidth", + "displayComments", + "filterSynthesis", + "filterYearByYear", + ], + [CLUSTER]: [ + "group", + "enabled", + "mustRun", + "unitCount", + "nominalCapacity", + "minStablePower", + "spinning", + "minUpTime", + "minDownTime", + "co2", + "marginalCost", + "fixedCost", + "startupCost", + "marketBidCost", + "spreadCost", + "tsGen", + "volatilityForced", + "volatilityPlanned", + "lawForced", + "lawPlanned", + ], + [RENEWABLE]: [ + "group", + "tsInterpretation", + "enabled", + "unitCount", + "nominalCapacity", + ], + [BINDING_CONSTRAINT]: ["type", "operator", "enabled"], +} as const; diff --git a/webapp/src/services/api/studies/tableMode/index.ts b/webapp/src/services/api/studies/tableMode/index.ts new file mode 100644 index 0000000000..cd77a71891 --- /dev/null +++ b/webapp/src/services/api/studies/tableMode/index.ts @@ -0,0 +1,35 @@ +import { snakeCase } from "lodash"; +import { DeepPartial } from "react-hook-form"; +import { StudyMetadata } from "../../../../common/types"; +import client from "../../client"; +import { format } from "../../../../utils/stringUtils"; +import { TABLE_MODE_API_URL } from "../../constants"; +import type { TableData, TableModeColumnsForType, TableModeType } from "./type"; + +export async function getTableMode( + studyId: StudyMetadata["id"], + type: T, + columns: TableModeColumnsForType, +): Promise { + const url = format(TABLE_MODE_API_URL, { studyId }); + const res = await client.get(url, { + params: { + table_type: type, + columns: columns.map(snakeCase).join(","), + }, + }); + return res.data; +} + +export function setTableMode( + studyId: StudyMetadata["id"], + type: TableModeType, + data: DeepPartial, +): Promise { + const url = format(TABLE_MODE_API_URL, { studyId }); + return client.put(url, data, { + params: { + table_type: type, + }, + }); +} diff --git a/webapp/src/services/api/studies/tableMode/type.ts b/webapp/src/services/api/studies/tableMode/type.ts new file mode 100644 index 0000000000..71b751d875 --- /dev/null +++ b/webapp/src/services/api/studies/tableMode/type.ts @@ -0,0 +1,12 @@ +import { TABLE_MODE_COLUMNS_BY_TYPE, TABLE_MODE_TYPES } from "./constants"; + +export type TableModeType = (typeof TABLE_MODE_TYPES)[number]; + +export type TableModeColumnsForType = Array< + (typeof TABLE_MODE_COLUMNS_BY_TYPE)[T][number] +>; + +export type TableData = Record< + string, + Record +>; diff --git a/webapp/src/services/utils/localStorage.ts b/webapp/src/services/utils/localStorage.ts index ef01aae017..ee8b599ded 100644 --- a/webapp/src/services/utils/localStorage.ts +++ b/webapp/src/services/utils/localStorage.ts @@ -1,7 +1,7 @@ import * as RA from "ramda-adjunct"; import packages from "../../../package.json"; import { UserInfo } from "../../common/types"; -import { TableTemplate } from "../../components/App/Singlestudy/explore/Modelization/TableMode/utils"; +import { TableTemplate } from "../../components/App/Singlestudy/explore/TableModeList/utils"; import { StudiesSortConf, StudiesState } from "../../redux/ducks/studies"; import { UIState } from "../../redux/ducks/ui"; From 01aed56023f6be7ef7a64a4561aefdeedc708dca Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Fri, 5 Jan 2024 18:05:00 +0100 Subject: [PATCH 5/7] feat(ui-config): move frozen Table Mode templates in Configuration --- webapp/public/locales/en/main.json | 3 ++ webapp/public/locales/fr/main.json | 3 ++ .../explore/Configuration/index.tsx | 46 ++++++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 72f8f6ed00..0daa43c3de 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -367,6 +367,9 @@ "study.configuration.advancedParameters.unitCommitmentMode": "Unit commitment mode", "study.configuration.advancedParameters.simulationCores": "Simulation cores", "study.configuration.advancedParameters.renewableGenerationModeling": "Renewable generation modeling", + "study.configuration.economicOpt": "Economic Opt.", + "study.configuration.geographicTrimmingAreas": "Geographic Trimming (areas)", + "study.configuration.geographicTrimmingLinks": "Geographic Trimming (links)", "study.modelization.properties": "Properties", "study.modelization.properties.posX": "Position X", "study.modelization.properties.posY": "Position Y", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index fcee42b459..02d345d17c 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -367,6 +367,9 @@ "study.configuration.advancedParameters.unitCommitmentMode": "Unit commitment mode", "study.configuration.advancedParameters.simulationCores": "Simulation cores", "study.configuration.advancedParameters.renewableGenerationModeling": "Renewable generation modeling", + "study.configuration.economicOpt": "Options économiques", + "study.configuration.geographicTrimmingAreas": "Filtre géographique (zones)", + "study.configuration.geographicTrimmingLinks": "Filtre géographique (liens)", "study.modelization.properties": "Propriétés", "study.modelization.properties.posX": "Position X", "study.modelization.properties.posY": "Position Y", diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx index f31ec83894..bd56ed29fc 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx @@ -3,6 +3,7 @@ import { Paper } from "@mui/material"; import * as R from "ramda"; import { useMemo, useState } from "react"; import { useOutletContext } from "react-router"; +import { useTranslation } from "react-i18next"; import { StudyMetadata } from "../../../../../common/types"; import UnderConstruction from "../../../../common/page/UnderConstruction"; import PropertiesView from "../../../../common/PropertiesView"; @@ -14,10 +15,12 @@ import General from "./General"; import Optimization from "./Optimization"; import RegionalDistricts from "./RegionalDistricts"; import TimeSeriesManagement from "./TimeSeriesManagement"; +import TableMode from "../../../../common/TableMode"; function Configuration() { const { study } = useOutletContext<{ study: StudyMetadata }>(); const [currentTabIndex, setCurrentTabIndex] = useState(0); + const { t } = useTranslation(); // TODO i18n const tabList = useMemo( @@ -29,8 +32,11 @@ function Configuration() { { id: 3, name: "Optimization preferences" }, Number(study.version) >= 830 && { id: 4, name: "Adequacy Patch" }, { id: 5, name: "Advanced parameters" }, + { id: 6, name: t("study.configuration.economicOpt") }, + { id: 7, name: t("study.configuration.geographicTrimmingAreas") }, + { id: 8, name: t("study.configuration.geographicTrimmingLinks") }, ].filter(Boolean), - [study.version], + [study.version, t], ); return ( @@ -58,6 +64,44 @@ function Configuration() { [R.equals(3), () => ], [R.equals(4), () => ], [R.equals(5), () => ], + [ + R.equals(6), + () => ( + + ), + ], + [ + R.equals(7), + () => ( + + ), + ], + [ + R.equals(8), + () => ( + + ), + ], ])(tabList[currentTabIndex].id)} } From 931380e5aad0df6e03a9d18a9c1de9959216a281 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:26:33 +0100 Subject: [PATCH 6/7] feat(ui-common): create TabsView component --- webapp/src/components/common/TabsView.tsx | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 webapp/src/components/common/TabsView.tsx diff --git a/webapp/src/components/common/TabsView.tsx b/webapp/src/components/common/TabsView.tsx new file mode 100644 index 0000000000..3c58e07c3c --- /dev/null +++ b/webapp/src/components/common/TabsView.tsx @@ -0,0 +1,60 @@ +/* eslint-disable react/no-array-index-key */ +import { TabContext, TabList, TabListProps, TabPanel } from "@mui/lab"; +import { Tab } from "@mui/material"; +import { useState } from "react"; +import { mergeSxProp } from "../../utils/muiUtils"; + +interface TabsViewProps { + items: Array<{ + label: string; + content?: React.ReactNode; + }>; + TabListProps?: TabListProps; +} + +function TabsView({ items, TabListProps }: TabsViewProps) { + const [value, setValue] = useState("0"); + + //////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////// + + const handleChange = (event: React.SyntheticEvent, newValue: string) => { + setValue(newValue); + TabListProps?.onChange?.(event, newValue); + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + + {items.map(({ label }, index) => ( + + ))} + + {items.map(({ content }, index) => ( + + {content} + + ))} + + ); +} + +export default TabsView; From 64542678777bf8d8bf464d16166bdba2c73f261a Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:27:35 +0100 Subject: [PATCH 7/7] feat(ui-adq): add new table form --- webapp/public/locales/en/main.json | 3 +- webapp/public/locales/fr/main.json | 3 +- .../Configuration/AdequacyPatch/Fields.tsx | 5 +-- .../Configuration/AdequacyPatch/index.tsx | 44 ++++++++++++++----- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 0daa43c3de..cd3534f6c6 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -330,7 +330,8 @@ "study.configuration.optimization.exportMps": "Export MPS", "study.configuration.optimization.unfeasibleProblemBehavior": "Unfeasible problem behavior", "study.configuration.optimization.simplexOptimizationRange": "Simplex optimization range", - "study.configuration.adequacyPatch.legend.general": "General", + "study.configuration.adequacyPatch.tab.general": "General", + "study.configuration.adequacyPatch.tab.perimeter": "Perimeter", "study.configuration.adequacyPatch.legend.localMatchingRule": "Local matching rule", "study.configuration.adequacyPatch.legend.curtailmentSharing": "Curtailment sharing", "study.configuration.adequacyPatch.legend.advanced": "Advanced", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index 02d345d17c..596c5d5c94 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -330,7 +330,8 @@ "study.configuration.optimization.exportMps": "Export MPS", "study.configuration.optimization.unfeasibleProblemBehavior": "Unfeasible problem behavior", "study.configuration.optimization.simplexOptimizationRange": "Simplex optimization range", - "study.configuration.adequacyPatch.legend.general": "Générale", + "study.configuration.adequacyPatch.tab.general": "Général", + "study.configuration.adequacyPatch.tab.perimeter": "Périmètre", "study.configuration.adequacyPatch.legend.localMatchingRule": "Règle de correspondance locale", "study.configuration.adequacyPatch.legend.curtailmentSharing": "Partage de réduction", "study.configuration.adequacyPatch.legend.advanced": "Avancée", diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx index e2567b0c08..ebb63fb13b 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx @@ -17,10 +17,7 @@ function Fields() { return ( -
+
(); + const { t } = useTranslation(); //////////////////////////////////////////////////////////////// // Event Handlers @@ -27,16 +31,36 @@ function AdequacyPatch() { //////////////////////////////////////////////////////////////// return ( -
getAdequacyPatchFormFields(study.id), - }} - onSubmit={handleSubmit} - enableUndoRedo - > - - + getAdequacyPatchFormFields(study.id), + }} + onSubmit={handleSubmit} + enableUndoRedo + > + + + ), + }, + { + label: t("study.configuration.adequacyPatch.tab.perimeter"), + content: ( + + ), + }, + ]} + TabListProps={{ sx: { mt: -2 } }} + /> ); }