Skip to content

Commit

Permalink
feat(ui-playlist): replace Handsontable
Browse files Browse the repository at this point in the history
  • Loading branch information
skamril committed Jan 15, 2025
1 parent a31eace commit 7bdf593
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 115 deletions.
7 changes: 3 additions & 4 deletions webapp/public/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"global.semicolon": "Semicolon",
"global.language": "Language",
"global.path": "Path",
"global.weight": "Weight",
"global.time.hourly": "Hourly",
"global.time.daily": "Daily",
"global.time.weekly": "Weekly",
Expand Down Expand Up @@ -136,8 +137,8 @@
"maintenance.error.messageInfoError": "Unable to retrieve message info",
"maintenance.error.maintenanceError": "Unable to retrieve maintenance status",
"form.submit.error": "Error while submitting",
"form.submit.inProgress": "The form is being submitted. Are you sure you want to leave the page?",
"form.changeNotSaved": "The form has not been saved. Are you sure you want to leave the page?",
"form.submit.inProgress": "The form is being submitted. Are you sure you want to close it?",
"form.changeNotSaved": "The form has not been saved. Are you sure you want to close it?",
"form.asyncDefaultValues.error": "Failed to get values",
"form.field.required": "Field required",
"form.field.duplicate": "Value already exists",
Expand Down Expand Up @@ -361,8 +362,6 @@
"study.configuration.general.mcScenarioPlaylist.action.disableAll": "Disable all",
"study.configuration.general.mcScenarioPlaylist.action.reverse": "Reverse",
"study.configuration.general.mcScenarioPlaylist.action.resetWeights": "Reset weights",
"study.configuration.general.mcScenarioPlaylist.status": "Status",
"study.configuration.general.mcScenarioPlaylist.weight": "Weight",
"study.configuration.general.geographicTrimming": "Geographic trimming",
"study.configuration.general.thematicTrimming": "Thematic trimming",
"study.configuration.general.thematicTrimming.action.enableAll": "Enable all",
Expand Down
9 changes: 4 additions & 5 deletions webapp/public/locales/fr/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"global.semicolon": "Point-virgule",
"global.language": "Langue",
"global.path": "Chemin",
"global.weight": "Poids",
"global.time.hourly": "Horaire",
"global.time.daily": "Journalier",
"global.time.weekly": "Hebdomadaire",
Expand Down Expand Up @@ -136,8 +137,8 @@
"maintenance.error.messageInfoError": "Impossible de récupérer le message d'info",
"maintenance.error.maintenanceError": "Impossible de récupérer le status de maintenance de l'application",
"form.submit.error": "Erreur lors de la soumission",
"form.submit.inProgress": "Le formulaire est en cours de soumission. Etes-vous sûr de vouloir quitter la page ?",
"form.changeNotSaved": "Le formulaire n'a pas été sauvegardé. Etes-vous sûr de vouloir quitter la page ?",
"form.submit.inProgress": "Le formulaire est en cours de soumission. Etes-vous sûr de vouloir le fermer ?",
"form.changeNotSaved": "Le formulaire n'a pas été sauvegardé. Etes-vous sûr de vouloir le fermer ?",
"form.asyncDefaultValues.error": "Impossible d'obtenir les valeurs",
"form.field.required": "Champ requis",
"form.field.duplicate": "Cette valeur existe déjà",
Expand Down Expand Up @@ -360,9 +361,7 @@
"study.configuration.general.mcScenarioPlaylist.action.enableAll": "Activer tous",
"study.configuration.general.mcScenarioPlaylist.action.disableAll": "Désactiver tous",
"study.configuration.general.mcScenarioPlaylist.action.reverse": "Inverser",
"study.configuration.general.mcScenarioPlaylist.action.resetWeights": "Reset weights",
"study.configuration.general.mcScenarioPlaylist.status": "Status",
"study.configuration.general.mcScenarioPlaylist.weight": "Weight",
"study.configuration.general.mcScenarioPlaylist.action.resetWeights": "Réinitialiser les poids",
"study.configuration.general.geographicTrimming": "Geographic trimming",
"study.configuration.general.thematicTrimming": "Thematic trimming",
"study.configuration.general.thematicTrimming.action.enableAll": "Activer tout",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,28 @@
* This file is part of the Antares project.
*/

import { Box, Button, Divider } from "@mui/material";
import { useRef } from "react";
import { Button, ButtonGroup, Divider } from "@mui/material";
import { useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import * as R from "ramda";
import * as RA from "ramda-adjunct";
import Handsontable from "handsontable";
import { StudyMetadata } from "../../../../../../../../common/types";
import type { StudyMetadata } from "../../../../../../../../common/types";
import usePromise from "../../../../../../../../hooks/usePromise";
import BasicDialog from "../../../../../../../common/dialogs/BasicDialog";
import TableForm from "../../../../../../../common/TableForm";
import UsePromiseCond from "../../../../../../../common/utils/UsePromiseCond";
import {
DEFAULT_WEIGHT,
getPlaylist,
PlaylistData,
setPlaylist,
type PlaylistData,
} from "./utils";
import { SubmitHandlerPlus } from "../../../../../../../common/Form/types";
import {
HandsontableProps,
HotTableClass,
} from "../../../../../../../common/Handsontable";
import type { SubmitHandlerPlus } from "../../../../../../../common/Form/types";
import DataGridForm, {
DataGridFormState,
type DataGridFormApi,
} from "@/components/common/DataGridForm";
import ConfirmationDialog from "@/components/common/dialogs/ConfirmationDialog";
import useConfirm from "@/hooks/useConfirm";

interface Props {
study: StudyMetadata;
Expand All @@ -44,51 +44,64 @@ interface Props {
function ScenarioPlaylistDialog(props: Props) {
const { study, open, onClose } = props;
const { t } = useTranslation();
const tableRef = useRef({} as HotTableClass);
const dataGridApiRef = useRef<DataGridFormApi<PlaylistData>>(null!);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isDirty, setIsDirty] = useState(false);
const closeAction = useConfirm();
const res = usePromise(() => getPlaylist(study.id), [study.id]);

const columns = useMemo(() => {
return [
{
id: "status",
title: t("global.status"),
grow: 1,
},
{
id: "weight",
title: t("global.weight"),
grow: 1,
},
];
}, []);

////////////////////////////////////////////////////////////////
// Event Handlers
////////////////////////////////////////////////////////////////

const handleUpdateStatus = (fn: RA.Pred) => () => {
const api = tableRef.current.hotInstance;
if (!api) {
return;
}

const changes: Array<[number, string, boolean]> = api
.getDataAtProp("status")
.map((status, index) => [index, "status", fn(status)]);

api.setDataAtRowProp(changes);
const { data, setData } = dataGridApiRef.current;
setData(R.map(R.evolve({ status: fn }), data));
};

const handleResetWeights = () => {
const api = tableRef.current.hotInstance as Handsontable;

api.setDataAtRowProp(
api.rowIndexMapper
.getIndexesSequence()
.map((rowIndex) => [rowIndex, "weight", DEFAULT_WEIGHT]),
);
const { data, setData } = dataGridApiRef.current;
setData(R.map(R.assoc("weight", DEFAULT_WEIGHT), data));
};

const handleSubmit = (data: SubmitHandlerPlus<PlaylistData>) => {
return setPlaylist(study.id, data.values);
};

const handleCellsRender: HandsontableProps["cells"] = function cells(
this,
row,
column,
prop,
) {
if (prop === "weight") {
const status = this.instance.getDataAtRowProp(row, "status");
return { readOnly: !status };
const handleClose = () => {
if (isSubmitting) {
return;
}
return {};

if (isDirty) {
return closeAction.showConfirm().then((confirm) => {
if (confirm) {
onClose();
}
});
}

onClose();
};

const handleFormStateChange = (formState: DataGridFormState) => {
setIsSubmitting(formState.isSubmitting);
setIsDirty(formState.isDirty);
};

////////////////////////////////////////////////////////////////
Expand All @@ -99,18 +112,25 @@ function ScenarioPlaylistDialog(props: Props) {
<BasicDialog
title={t("study.configuration.general.mcScenarioPlaylist")}
open={open}
onClose={onClose}
actions={<Button onClick={onClose}>{t("global.close")}</Button>}
// TODO: add `maxHeight` and `fullHeight` in BasicDialog`
PaperProps={{ sx: { height: 500 } }}
maxWidth="sm"
onClose={handleClose}
actions={
<Button onClick={handleClose} disabled={isSubmitting}>
{t("global.close")}
</Button>
}
PaperProps={{ sx: { height: 700 } }}
maxWidth="md"
fullWidth
>
<UsePromiseCond
response={res}
ifFulfilled={(defaultValues) => (
<>
<Box sx={{ display: "flex", justifyContent: "flex-end", gap: 1 }}>
<ButtonGroup
size="small"
disabled={isSubmitting}
sx={{ justifyContent: "flex-end", mb: 1 }}
>
<Button color="secondary" onClick={handleUpdateStatus(R.T)}>
{t(
"study.configuration.general.mcScenarioPlaylist.action.enableAll",
Expand All @@ -133,19 +153,28 @@ function ScenarioPlaylistDialog(props: Props) {
"study.configuration.general.mcScenarioPlaylist.action.resetWeights",
)}
</Button>
</Box>
<TableForm
defaultValues={defaultValues}
onSubmit={handleSubmit}
sx={{ pt: 2, overflow: "auto" }}
tableProps={{
rowHeaders: (row) => `MC Year ${row.id}`,
tableRef,
stretchH: "all",
className: "htCenter",
cells: handleCellsRender,
</ButtonGroup>
<DataGridForm
defaultData={defaultValues}
columns={columns}
rowMarkers={{
kind: "clickable-string",
getTitle: (index) => `MC Year ${index + 1}`,
}}
onSubmit={handleSubmit}
onStateChange={handleFormStateChange}
apiRef={dataGridApiRef}
enableColumnResize={false}
/>
<ConfirmationDialog
open={closeAction.isPending}
onConfirm={closeAction.yes}
onCancel={closeAction.no}
maxWidth="xs"
alert="warning"
>
{t("form.changeNotSaved")}
</ConfirmationDialog>
</>
)}
/>
Expand Down
Loading

0 comments on commit 7bdf593

Please sign in to comment.