Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/components/app-wrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import {
equipmentShortFr,
equipmentTagEn,
equipmentTagFr,
equipmentTypesEn,
equipmentTypesFr,
errorsEn,
errorsFr,
exportParamsEn,
Expand Down Expand Up @@ -405,6 +407,7 @@ const messages = {
...equipmentSearchEn,
...equipmentShortEn,
...equipmentTagEn,
...equipmentTypesEn,
...directoryItemsInputEn,
...cardErrorBoundaryEn,
...flatParametersEn,
Expand Down Expand Up @@ -447,6 +450,7 @@ const messages = {
...equipmentSearchFr,
...equipmentShortFr,
...equipmentTagFr,
...equipmentTypesFr,
...directoryItemsInputFr,
...cardErrorBoundaryFr,
...flatParametersFr,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { useCallback, useEffect } from 'react';
import { useWatch } from 'react-hook-form';
import EditIcon from '@mui/icons-material/Edit';
import { FloatInput, KilometerAdornment } from '@gridsuite/commons-ui';
import { ButtonReadOnlyInput, FloatInput, KilometerAdornment, ReadOnlyInput } from '@gridsuite/commons-ui';
import { IconButton } from '@mui/material';
import {
SEGMENT_DISTANCE_VALUE,
Expand All @@ -17,8 +17,6 @@ import {
SEGMENT_SUSCEPTANCE,
SEGMENT_TYPE_VALUE,
} from '../../utils/field-constants';
import { ReadOnlyInput } from '../../utils/rhf-inputs/read-only/read-only-input';
import { ButtonReadOnlyInput } from '../../utils/rhf-inputs/read-only/button-read-only-input';
import GridItem from '../commons/grid-item';

export interface LineTypeSegmentCreationProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { Box, Grid } from '@mui/material';
import { FormattedMessage, useIntl } from 'react-intl';
import { ReadOnlyInput } from '../../utils/rhf-inputs/read-only/read-only-input';
import {
FINAL_CURRENT_LIMITS,
SEGMENT_CURRENT_LIMITS,
Expand All @@ -34,6 +33,7 @@ import {
ExpandableInput,
fetchStudyMetadata,
type MuiStyles,
ReadOnlyInput,
snackWithFallback,
useSnackMessage,
} from '@gridsuite/commons-ui';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import { Box, Grid } from '@mui/material';
import { getAssignmentInitialValue } from './assignment/assignment-utils';
import { useFormContext } from 'react-hook-form';
import SelectWithConfirmationInput from '../../../commons/select-with-confirmation-input';
import { ExpandableInput, mergeSx, unscrollableDialogStyles } from '@gridsuite/commons-ui';
import { ExpandableInput, mergeSx, unscrollableDialogStyles, useGetLabelEquipmentTypes } from '@gridsuite/commons-ui';
import { EQUIPMENTS_FIELDS, EquipmentTypeOptionType } from './assignment/assignment-constants';
import useGetLabelEquipmentTypes from '../../../../../hooks/use-get-label-equipment-types';
import GridItem from '../../../commons/grid-item';

interface ModificationByAssignmentFormProps {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
import { Grid } from '@mui/material';
import { useCallback, useMemo } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';
import { AutocompleteInput, DirectoryItemsInput, ElementType } from '@gridsuite/commons-ui';
import { AutocompleteInput, DirectoryItemsInput, ElementType, useGetLabelEquipmentTypes } from '@gridsuite/commons-ui';
import { FILTERS, TYPE } from 'components/utils/field-constants';
import { richTypeEquals } from 'components/utils/utils';
import { EQUIPMENT_TYPES } from 'components/utils/equipment-types';
import useGetLabelEquipmentTypes from '../../../../../hooks/use-get-label-equipment-types';
import GridItem from '../../../commons/grid-item';

const ByFilterDeletionForm = () => {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,38 @@
*/

import { yupResolver } from '@hookform/resolvers/yup';
import yup from 'components/utils/yup-config';
import { DELETION_SPECIFIC_DATA, EQUIPMENT_ID, TYPE } from '../../../utils/field-constants';
import {
CustomFormProvider,
DeepNullable,
equipmentDeletionEmptyFormData,
EquipmentDeletionDto,
EquipmentType,
snackWithFallback,
useSnackMessage,
equipmentDeletionFormSchema,
EquipmentDeletionFormData,
EquipmentDeletionForm,
equipmentDeletionDtoToForm,
equipmentDeletionFormToDto,
} from '@gridsuite/commons-ui';
import { useForm } from 'react-hook-form';
import { useCallback, useEffect, useMemo } from 'react';
import { ModificationDialog } from '../../commons/modificationDialog';
import DeleteEquipmentForm from './equipment-deletion-form';
import { useOpenShortWaitFetching } from 'components/dialogs/commons/handle-modification-form';
import { FORM_LOADING_DELAY } from 'components/network/constants';
import { deleteEquipment } from '../../../../services/study/network-modifications';
import { UUID } from 'node:crypto';
import { FetchStatus } from 'services/utils.type';
import { EquipmentDeletionInfos } from './equipement-deletion-dialog.type';
import { NetworkModificationDialogProps } from '../../../graph/menus/network-modifications/network-modification-menu.type';
import { getHvdcLccDeletionSchema } from './hvdc-lcc-deletion/hvdc-lcc-deletion-utils';

const formSchema = yup
.object()
.shape({
[EQUIPMENT_ID]: yup.string().nullable().required(),
[TYPE]: yup.mixed<EquipmentType>().oneOf(Object.values(EquipmentType)).nullable().required(),
[DELETION_SPECIFIC_DATA]: getHvdcLccDeletionSchema(),
})
.required();

export type EquipmentDeletionFormInfos = yup.InferType<typeof formSchema>;

const emptyFormData: DeepNullable<EquipmentDeletionFormInfos> = {
[EQUIPMENT_ID]: '',
[TYPE]: null,
[DELETION_SPECIFIC_DATA]: null,
};
import { WithModificationId } from 'services/network-modification-types';
import { fetchEquipmentsIds, fetchHvdcLineWithShuntCompensators } from 'services/study/network-map';

export interface EquipmentDeletionDtoWithId extends EquipmentDeletionDto, WithModificationId {}

type EquipmentDeletionDialogProps = NetworkModificationDialogProps & {
editData?: EquipmentDeletionInfos;
editData?: EquipmentDeletionDtoWithId;
defaultIdValue?: UUID;
equipmentType: EquipmentType;
editDataFetchStatus?: FetchStatus;
equipmentType?: EquipmentType;
};

/**
Expand All @@ -59,8 +47,8 @@ type EquipmentDeletionDialogProps = NetworkModificationDialogProps & {
* @param currentRootNetworkUuid
* @param editData the data to edit
* @param isUpdate check if edition form
* @param defaultIdValue the default equipment id
* @param equipmentType
* @param defaultIdValue in creation mode, we can specify the equipment ID we want to delete
* @param equipmentType in creation mode, we can specify the equipment type we want to delete
* @param editDataFetchStatus indicates the status of fetching EditData
* @param onClose a callback when dialog has been closed
* @param onValidated a callback when dialog has been validated
Expand All @@ -83,79 +71,69 @@ const EquipmentDeletionDialog = ({

const { snackError } = useSnackMessage();

const formMethods = useForm<DeepNullable<EquipmentDeletionFormInfos>>({
defaultValues: emptyFormData,
resolver: yupResolver<DeepNullable<EquipmentDeletionFormInfos>>(formSchema),
const formMethods = useForm<DeepNullable<EquipmentDeletionFormData>>({
defaultValues: equipmentDeletionEmptyFormData,
resolver: yupResolver<DeepNullable<EquipmentDeletionFormData>>(equipmentDeletionFormSchema),
});

const { reset } = formMethods;

const fromEditDataToFormValues = useCallback(
(editData: EquipmentDeletionInfos) => {
(editData: EquipmentDeletionDto) => {
const formData = equipmentDeletionDtoToForm(editData);
reset({
[TYPE]: editData.equipmentType,
[EQUIPMENT_ID]: editData.equipmentId,
[DELETION_SPECIFIC_DATA]: null,
...formData,
equipmentInfos: null, // this prop will be computed (cf LCC specific part)
});
},
[reset]
);

const fromMenuDataValues = useCallback(
(menuSelectId: UUID) => {
const presetTypeAndId = useCallback(
(equipmentType: EquipmentType, equipmentId: UUID) => {
reset({
[TYPE]: EquipmentType.HVDC_LINE,
[EQUIPMENT_ID]: menuSelectId,
[DELETION_SPECIFIC_DATA]: null,
equipmentID: equipmentId,
type: equipmentType,
equipmentInfos: null,
});
},
[reset]
);

const resetFormWithEquipmentType = useCallback(
const presetType = useCallback(
(equipmentType: EquipmentType) => {
reset({
[TYPE]: equipmentType,
type: equipmentType,
});
},
[reset]
);

useEffect(() => {
if (editData) {
// edition mode
fromEditDataToFormValues(editData);
} else if (defaultIdValue) {
fromMenuDataValues(defaultIdValue);
} else if (equipmentType && defaultIdValue) {
// creation mode with both Id and Type (ex: from diagram)
presetTypeAndId(equipmentType, defaultIdValue);
} else if (equipmentType) {
resetFormWithEquipmentType(equipmentType);
// creation mode with only Type (ex: from node modifications menu)
presetType(equipmentType);
}
}, [
fromEditDataToFormValues,
editData,
fromMenuDataValues,
defaultIdValue,
equipmentType,
resetFormWithEquipmentType,
]);
}, [defaultIdValue, editData, equipmentType, fromEditDataToFormValues, presetType, presetTypeAndId]);
Comment on lines 112 to +123
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard the new creation presets from stale edit data.

A late edit-data response can still land after a previous edit dialog was closed. With the new equipmentType / defaultIdValue branches, this effect will still prefer any truthy editData, so reopening the dialog in creation mode can reset the form with stale deletion values instead of the requested preset.

Suggested fix
-        if (editData) {
+        if (isUpdate && editData) {
             // edition mode
             fromEditDataToFormValues(editData);
         } else if (equipmentType && defaultIdValue) {
             // creation mode with both Id and Type (ex: from diagram)
             presetTypeAndId(equipmentType, defaultIdValue);
         } else if (equipmentType) {
             // creation mode with only Type (ex: from node modifications menu)
             presetType(equipmentType);
         }
-    }, [defaultIdValue, editData, equipmentType, fromEditDataToFormValues, presetType, presetTypeAndId]);
+    }, [defaultIdValue, editData, equipmentType, fromEditDataToFormValues, isUpdate, presetType, presetTypeAndId]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx`
around lines 108 - 119, The effect currently always prefers truthy editData
which can be stale; update the useEffect logic (the block that calls
fromEditDataToFormValues, presetTypeAndId, presetType) to only apply
fromEditDataToFormValues when editData actually corresponds to the dialog
instance (e.g. editData?.id === defaultIdValue or editData?.type ===
equipmentType or another identifying field), otherwise fall through to creation
presets; specifically modify the first branch to guard editData with a match
check and keep presetTypeAndId/presetType branches when
equipmentType/defaultIdValue indicate creation mode so stale editData won't
overwrite presets.


const onSubmit = useCallback(
(formData: EquipmentDeletionFormInfos) => {
deleteEquipment({
studyUuid,
nodeUuid: currentNodeUuid,
uuid: editData?.uuid,
equipmentId: formData[EQUIPMENT_ID] as UUID,
equipmentType: formData[TYPE],
equipmentSpecificInfos: formData[DELETION_SPECIFIC_DATA] ?? undefined,
}).catch((error) => {
(formData: EquipmentDeletionFormData) => {
const dto = equipmentDeletionFormToDto(formData);
deleteEquipment(studyUuid, currentNodeUuid, editData?.uuid, dto).catch((error: unknown) => {
snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' });
});
},
[currentNodeUuid, editData, snackError, studyUuid]
);

const clear = useCallback(() => {
reset(emptyFormData);
reset(equipmentDeletionEmptyFormData);
}, [reset]);

const open = useOpenShortWaitFetching({
Expand All @@ -169,8 +147,20 @@ const EquipmentDeletionDialog = ({
[editDataFetchStatus, isUpdate]
);

const fetchEquipmentIds = useCallback(
(equipmentType: EquipmentType) =>
fetchEquipmentsIds(studyUuid, currentNodeUuid, currentRootNetworkUuid, undefined, equipmentType, true),
[studyUuid, currentNodeUuid, currentRootNetworkUuid]
);

const fetchHvdcWithShuntCompensators = useCallback(
(hvdcLineId: UUID) =>
fetchHvdcLineWithShuntCompensators(studyUuid, currentNodeUuid, currentRootNetworkUuid, hvdcLineId),
[studyUuid, currentNodeUuid, currentRootNetworkUuid]
);

return (
<CustomFormProvider validationSchema={formSchema} {...formMethods}>
<CustomFormProvider validationSchema={equipmentDeletionFormSchema} {...formMethods}>
<ModificationDialog
fullWidth
maxWidth="md"
Expand All @@ -183,11 +173,10 @@ const EquipmentDeletionDialog = ({
isDataFetching={waitingForData}
{...dialogProps}
>
<DeleteEquipmentForm
studyUuid={studyUuid}
currentNode={currentNode}
currentRootNetworkUuid={currentRootNetworkUuid}
<EquipmentDeletionForm
editData={editData}
fetchEquipmentIds={fetchEquipmentIds}
fetchHvdcWithShuntCompensators={fetchHvdcWithShuntCompensators}
/>
</ModificationDialog>
</CustomFormProvider>
Expand Down
Loading
Loading