Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
716cdce
wip
dbraquart Jan 23, 2026
dc18fc3
Merge branch 'main' into dbraquart/move-site-creation-modification-to…
dbraquart Jan 23, 2026
fe5e9b5
update rest calls and typing
dbraquart Jan 23, 2026
29c999d
mv translations
dbraquart Jan 26, 2026
28de2e4
use FetchStatus (with same values than grid-study)
dbraquart Jan 26, 2026
9fd917c
fix linter
dbraquart Jan 26, 2026
ef8338a
fix quality gate
dbraquart Jan 26, 2026
27be2c3
change Icon imports (to avoid crash with npm start)
dbraquart Jan 26, 2026
a61a50f
add search and copy
dbraquart Jan 27, 2026
c2debc3
Merge branch 'main' into dbraquart/move-site-creation-modification-to…
dbraquart Jan 27, 2026
cfa6a45
add language (to translate countries)
dbraquart Jan 27, 2026
d290068
fix linter
dbraquart Jan 27, 2026
885738c
fix circ dep
dbraquart Jan 27, 2026
b959169
use enum FetchStatus
dbraquart Jan 27, 2026
a350ba9
forgot to move this one (unused actually)
dbraquart Jan 28, 2026
10a1451
use more StudyContext type
dbraquart Jan 29, 2026
f393ccb
no need csv-picker translations
dbraquart Jan 29, 2026
04fb1c2
Merge branch 'main' into dbraquart/move-site-creation-modification-to…
dbraquart Jan 29, 2026
0b75373
report from grid-study "Add modification submit button data test id (…
dbraquart Jan 30, 2026
8c9ae95
simplify EquipmentInfosTypes as enum
dbraquart Feb 3, 2026
2539d07
rev remark: add copyright in headers
dbraquart Feb 3, 2026
15f6e3a
rev remark: use camelCase for new files
dbraquart Feb 3, 2026
71ff052
rev remark: add ModificationReadError header msg
dbraquart Feb 3, 2026
5859426
fix: import only useful parts from study
TheMaskedTurtle Feb 5, 2026
f155bdd
fix(rhf-inputs): re-render issues on form update
TheMaskedTurtle Feb 5, 2026
4173e48
fix: reduce diffs
TheMaskedTurtle Feb 5, 2026
31c031a
fix: eslint
TheMaskedTurtle Feb 5, 2026
bb9f2b9
fix: revert unnecessary changes
TheMaskedTurtle Feb 5, 2026
756ee18
fix: revert unnecessary changes
TheMaskedTurtle Feb 5, 2026
fa4bcb0
refactor: reorganize some utils
TheMaskedTurtle Feb 5, 2026
c7ef269
fix: typo in adornment
TheMaskedTurtle Feb 6, 2026
0a4948b
fix
TheMaskedTurtle Feb 6, 2026
aec1533
fix: don't use [FieldConstants.] when not necessary
TheMaskedTurtle Feb 6, 2026
fe1f71d
Merge branch 'main' into dbraquart/move-site-creation-modification-to…
TheMaskedTurtle Feb 6, 2026
1be6071
fix: copyright and unused imports
TheMaskedTurtle Feb 6, 2026
83ccfb0
fix: sonar and improve typing on filled property
TheMaskedTurtle Feb 6, 2026
b23bd5a
Merge branch 'main' into dbraquart/move-site-creation-modification-to…
dbraquart Feb 9, 2026
33f5bcf
fix: base CountrySelectionInput on InltProvider language
TheMaskedTurtle Feb 9, 2026
8e16332
fix: less verbose in console
TheMaskedTurtle Feb 10, 2026
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
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export * from './parameters';
export * from './menus';
export * from './muiTable';
export * from './resizablePanels';
export * from './network-modifications';
27 changes: 27 additions & 0 deletions src/components/inputs/reactHookForm/CountrySelectionInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2023, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { useIntl } from 'react-intl';
import { AutocompleteInput, AutocompleteInputProps } from './autocompleteInputs';
import { useLocalizedCountries } from '../../../hooks';
import { GsLang } from '../../../utils';

interface CountrySelectionInputProps extends Omit<AutocompleteInputProps, 'options' | 'getOptionLabel'> {}

export function CountrySelectionInput(props: Readonly<CountrySelectionInputProps>) {
const { locale } = useIntl();
const { translate, countryCodes } = useLocalizedCountries(locale as GsLang);

return (
<AutocompleteInput
options={countryCodes}
// TODO: the way Option is managed in AutocompleteInput is confusing, maybe make AutocompleteInput more generic in the future
getOptionLabel={(countryCode) => translate(countryCode as string)}
{...props}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function AutocompleteInput({

return (
<Autocomplete
value={selectedValues}
value={selectedValues ?? null} // Ensure null instead of undefined otherwise it switches to uncontrolled mode
onChange={(_, data) => handleChange(data as Option)}
{...(allowNewValue && {
freeSolo: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { PropsWithChildren, useState } from 'react';
import { useIntl } from 'react-intl';
import { Grid, IconButton, Tooltip } from '@mui/material';
import { Delete as DeleteIcon, RestoreFromTrash as RestoreFromTrashIcon } from '@mui/icons-material';

export interface DeletableRowProps extends PropsWithChildren {
alignItems: string;
onClick: () => void;
deletionMark?: boolean | null;
disabledDeletion?: boolean | null;
}

export function DeletableRow({
alignItems,
onClick,
deletionMark,
disabledDeletion,
children,
}: Readonly<DeletableRowProps>) {
const intl = useIntl();
const [isMouseHover, setIsMouseHover] = useState(false);

return (
<Grid
container
spacing={2}
item
alignItems={alignItems}
onMouseEnter={() => setIsMouseHover(true)}
onMouseLeave={() => setIsMouseHover(false)}
>
{children}
<Grid item xs={1}>
{isMouseHover && !disabledDeletion && (
<Tooltip
title={intl.formatMessage({
id: deletionMark ? 'button.restore' : 'DeleteRows',
})}
>
<IconButton onClick={onClick}>
{deletionMark ? <RestoreFromTrashIcon /> : <DeleteIcon />}
</IconButton>
</Tooltip>
)}
</Grid>
</Grid>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* Copyright (c) 2023, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { useFieldArray } from 'react-hook-form';
import { Button, Grid } from '@mui/material';
import { ControlPoint as AddIcon } from '@mui/icons-material';
import { FormattedMessage } from 'react-intl';
import { DeletableRow } from './DeletableRow';
import { ErrorInput, MidFormError } from '../errorManagement';
import { mergeSx, type MuiStyles } from '../../../../utils';

const styles = {
button: (theme) => ({
justifyContent: 'flex-start',
fontSize: 'small',
marginTop: theme.spacing(1),
}),
paddingButton: (theme) => ({
paddingLeft: theme.spacing(2),
}),
} as const satisfies MuiStyles;

export interface ExpandableInputProps {
name: string;
Field: React.ComponentType<any>;
fieldProps?: any;
addButtonLabel?: string;
initialValue?: any;
getDeletionMark?: (idx: number) => boolean;
deleteCallback?: (idx: number) => boolean;
alignItems?: string;
watchProps?: boolean;
disabled?: boolean;
disabledDeletion?: (idx: number) => boolean;
}

// This component is used to display Array of objects.
// We can manage 2 states for deletion:
// - only 1 state and 1 delete icon that removes the current line
// - a second state "mark for deletion" with a second icon: the line is not removed
// and we can cancel this mark to go back to normal state.
export function ExpandableInput({
name,
Field, // Used to display each object of an array
fieldProps, // Props to pass to Field
addButtonLabel,
initialValue, // Initial value to display when we add a new entry to array
getDeletionMark,
deleteCallback,
alignItems = 'stretch', // default value for a flex container
watchProps = true,
disabled = false,
disabledDeletion,
}: Readonly<ExpandableInputProps>) {
const {
fields: values,
append,
remove,
} = useFieldArray({
name,
});

return (
<Grid item container spacing={2}>
<Grid item xs={12}>
<ErrorInput name={name} InputField={MidFormError} />
</Grid>
{watchProps &&
values.map((value, idx) => (
<DeletableRow
key={value.id}
alignItems={alignItems}
onClick={() => {
const shouldRemove = deleteCallback ? deleteCallback(idx) : true;
if (shouldRemove) {
remove(idx);
}
}}
deletionMark={getDeletionMark?.(idx)}
disabledDeletion={disabledDeletion?.(idx)}
>
<Field name={name} index={idx} {...fieldProps} />
</DeletableRow>
))}
<span>
<Button
disabled={disabled}
fullWidth
sx={mergeSx(styles.button, styles.paddingButton)}
startIcon={<AddIcon />}
onClick={() => append(initialValue)}
>
{addButtonLabel && <FormattedMessage id={addButtonLabel} />}
</Button>
</span>
</Grid>
);
}
8 changes: 8 additions & 0 deletions src/components/inputs/reactHookForm/expandableInput/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

export * from './ExpandableInput';
2 changes: 2 additions & 0 deletions src/components/inputs/reactHookForm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ export * from './tableInputs';
export * from './text';
export * from './utils';
export * from './constants';
export * from './expandableInput';
export * from './CountrySelectionInput';
2 changes: 1 addition & 1 deletion src/components/inputs/reactHookForm/text/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export function TextInput({
fullWidth
id={id ?? label}
label={fieldLabel}
value={transformedValue}
value={transformedValue ?? ''}
onChange={handleValueChanged}
disabled={disabled}
InputProps={{
Expand Down
20 changes: 20 additions & 0 deletions src/components/network-modifications/common/form.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { FilledTextFieldProps, StandardTextFieldProps } from '@mui/material';

export const filledTextField: FilledTextFieldProps = {
variant: 'filled',
};

export const standardTextField: StandardTextFieldProps = {
variant: 'standard',
};

export const italicFontTextField = {
style: { fontStyle: 'italic' },
};
9 changes: 9 additions & 0 deletions src/components/network-modifications/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

export * from './properties';
export * from './form.utils';
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Grid } from '@mui/material';
import { useCallback, useEffect, useState } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';
import { fetchPredefinedProperties, initializedProperty } from './propertyUtils';
import { PropertyForm } from './PropertyForm';
import GridSection from '../../../grid/grid-section';
import { FieldConstants, PredefinedProperties } from '../../../../utils';
import { ExpandableInput } from '../../../inputs';

type PropertiesFormProps = {
id?: string;
networkElementType?: string;
isModification?: boolean;
};

export function PropertiesForm({ id, networkElementType, isModification = false }: Readonly<PropertiesFormProps>) {
const additionalProperties = id
? `${id}.${FieldConstants.ADDITIONAL_PROPERTIES}`
: FieldConstants.ADDITIONAL_PROPERTIES;
const watchProps = useWatch({
name: additionalProperties,
});
const { getValues, setValue } = useFormContext();
const [predefinedProperties, setPredefinedProperties] = useState<PredefinedProperties>({});

useEffect(() => {
if (networkElementType) {
fetchPredefinedProperties(networkElementType).then((res) => {
if (res) {
setPredefinedProperties(res);
}
});
}
}, [networkElementType]);

const getDeletionMark = useCallback(
(idx: number) => {
const properties = getValues(`${additionalProperties}`);
if (properties && typeof properties[idx] !== 'undefined') {
return watchProps && properties[idx][FieldConstants.DELETION_MARK];
}
return false;
},
[getValues, watchProps, additionalProperties]
);

const deleteCallback = useCallback(
(idx: number) => {
let markedForDeletion = false;
const properties = getValues(`${additionalProperties}`);
if (properties && typeof properties[idx] !== 'undefined') {
markedForDeletion = properties[idx][FieldConstants.DELETION_MARK];
} else {
return false;
}

let canRemoveLine = true;
if (markedForDeletion) {
// just unmark
setValue(`${additionalProperties}.${idx}.${FieldConstants.DELETION_MARK}`, false, {
shouldDirty: true,
});
canRemoveLine = false;
} else if (
properties[idx][FieldConstants.PREVIOUS_VALUE] &&
properties[idx][FieldConstants.ADDED] === false
) {
// we should mark for deletion a property that actually exists in the network and not delete the property line straight away
setValue(`${additionalProperties}.${idx}.${FieldConstants.DELETION_MARK}`, true, {
shouldDirty: true,
});
canRemoveLine = false;
}
// otherwise just delete the line
return canRemoveLine;
},
[getValues, setValue, additionalProperties]
);

const modificationProperties = isModification
? {
getDeletionMark,
deleteCallback,
watchProps,
}
: {};

const additionalProps = (
<ExpandableInput
name={additionalProperties}
Field={PropertyForm}
fieldProps={{ predefinedProperties }}
addButtonLabel="AddProperty"
initialValue={initializedProperty()}
{...modificationProperties}
/>
);

return (
<Grid container>
<GridSection title="AdditionalInformation" />
{additionalProps}
</Grid>
);
}
Loading
Loading