Skip to content

Commit

Permalink
front: prevent user from saving multiple times
Browse files Browse the repository at this point in the history
  • Loading branch information
clarani committed Nov 17, 2023
1 parent ef61485 commit 832f812
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
import { getEditRouteState } from 'applications/editor/tools/routeEdition/utils';
import TOOL_TYPES from 'applications/editor/tools/toolTypes';
import { EditoastType } from 'applications/editor/tools/types';
import { getIsLoading } from 'reducers/main/mainSelector';
import { CustomFlagSignalCheckbox } from './CustomFlagSignalCheckbox';
import { PointEditionState } from './types';
import { formatSignalingSystems } from './utils';
Expand Down Expand Up @@ -192,6 +193,7 @@ export const PointEditionLeftPanel: FC<{ type: EditoastType }> = <Entity extends
const dispatch = useDispatch();
const { t } = useTranslation();
const infraID = useSelector(getInfraID);
const isLoading = useSelector(getIsLoading);
const { state, setState } = useContext(EditorContext) as ExtendedEditorContextType<
PointEditionState<Entity>
>;
Expand Down Expand Up @@ -333,7 +335,7 @@ export const PointEditionLeftPanel: FC<{ type: EditoastType }> = <Entity extends
<button
type="submit"
className="btn btn-primary"
disabled={!state.entity.properties?.track || !state.entity.geometry}
disabled={!state.entity.properties?.track || !state.entity.geometry || isLoading}
>
{t('common.save')}
</button>
Expand Down
5 changes: 3 additions & 2 deletions front/src/applications/editor/tools/pointEdition/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { BsSkipEnd } from 'react-icons/bs';
import { MdSensors } from 'react-icons/md';
import { FaMapSigns } from 'react-icons/fa';

import type { BufferStopEntity, DetectorEntity, SignalEntity } from 'types';

import { BasePointEditionLayers, SignalEditionLayers } from './components';
import getPointEditionTool from './tool-factory';
import { getNewBufferStop, getNewDetector, getNewSignal } from './utils';
import { BufferStopEntity, DetectorEntity, SignalEntity } from '../../../../types';
import { BasePointEditionLayers, SignalEditionLayers } from './components';

export const SignalEditionTool = getPointEditionTool<SignalEntity>({
layer: 'signals',
Expand Down
96 changes: 48 additions & 48 deletions front/src/applications/editor/tools/rangeEdition/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,35 @@ import { BsArrowBarRight } from 'react-icons/bs';
import { AiFillSave } from 'react-icons/ai';
import { MdShowChart } from 'react-icons/md';
import { FaFlagCheckered, FaTimes } from 'react-icons/fa';
import CheckboxRadioSNCF from 'common/BootstrapSNCF/CheckboxRadioSNCF';

import EditorContext from 'applications/editor/context';
import EntityError from 'applications/editor/components/EntityError';
import EntitySumUp from 'applications/editor/components/EntitySumUp';
import { NEW_ENTITY_ID } from 'applications/editor/data/utils';
import {
ExtendedEditorContextType,
PartialOrReducer,
} from 'applications/editor/tools/editorContextTypes';
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import CheckboxRadioSNCF from 'common/BootstrapSNCF/CheckboxRadioSNCF';
import { LoaderFill } from 'common/Loader';
import { save } from 'reducers/editor';
import { getIsLoading } from 'reducers/main/mainSelector';
import { getInfraID } from 'reducers/osrdconf/selectors';
import EditorContext from '../../context';
import { RangeEditionState } from './types';
import {
APPLICABLE_DIRECTIONS,
ApplicableDirection,
CatenaryEntity,
EntityObjectOperationResult,
SpeedSectionEntity,
SpeedSectionPslEntity,
} from '../../../../types';
import { NEW_ENTITY_ID } from '../../data/utils';
import { LoaderFill } from '../../../../common/Loader';
import EntitySumUp from '../../components/EntitySumUp';
import EntityError from '../../components/EntityError';
import { save } from '../../../../reducers/editor';
} from 'types';

import CatenaryMetadataForm from './catenary/CatenaryMetadataForm';
import EditPSLSection from './speedSection/EditPSLSection';
import { ExtendedEditorContextType, PartialOrReducer } from '../editorContextTypes';
import { getPointAt, speedSectionIsPsl } from './utils';
import SpeedSectionMetadataForm from './speedSection/SpeedSectionMetadataForm';
import CatenaryMetadataForm from './catenary/CatenaryMetadataForm';
import { RangeEditionState } from './types';
import { getPointAt, speedSectionIsPsl } from './utils';

const DEFAULT_DISPLAYED_RANGES_COUNT = 5;

Expand Down Expand Up @@ -210,8 +216,9 @@ export const RangeEditionLeftPanel: FC = () => {
} = useContext(EditorContext) as ExtendedEditorContextType<
RangeEditionState<SpeedSectionEntity | CatenaryEntity>
>;
const isLoading = useSelector(getIsLoading);

const isNew = entity.properties.id === NEW_ENTITY_ID;
const [isLoading, setIsLoading] = useState(false);
const isPSL = speedSectionIsPsl(entity as SpeedSectionEntity);

const infraID = useSelector(getInfraID);
Expand Down Expand Up @@ -255,43 +262,36 @@ export const RangeEditionLeftPanel: FC = () => {
className="btn btn-primary w-100 text-wrap"
disabled={isLoading || isEqual(entity, initialEntity)}
onClick={async () => {
setIsLoading(true);

try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const res: any = await dispatch(
save(
infraID,
!isNew
? {
update: [
{
source: initialEntity,
target: entity,
},
],
}
: { create: [entity] }
)
);
const operation = res[0] as EntityObjectOperationResult;
const { id } = operation.railjson;
setIsLoading(false);

const savedEntity =
id && id !== entity.properties.id
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const res: any = await dispatch(
save(
infraID,
!isNew
? {
...entity,
properties: { ...entity.properties, id: `${id}` },
update: [
{
source: initialEntity,
target: entity,
},
],
}
: entity;
setState({
entity: cloneDeep(savedEntity),
initialEntity: cloneDeep(savedEntity),
});
} catch (e: unknown) {
setIsLoading(false);
}
: { create: [entity] }
)
);
const operation = res[0] as EntityObjectOperationResult;
const { id } = operation.railjson;

const savedEntity =
id && id !== entity.properties.id
? {
...entity,
properties: { ...entity.properties, id: `${id}` },
}
: entity;
setState({
entity: cloneDeep(savedEntity),
initialEntity: cloneDeep(savedEntity),
});
}}
>
<AiFillSave className="mr-2" />
Expand Down
41 changes: 21 additions & 20 deletions front/src/applications/editor/tools/switchEdition/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,32 @@ import { featureCollection, point } from '@turf/helpers';
import nearestPoint from '@turf/nearest-point';
import { Position } from 'geojson';

import EditorContext from '../../context';
import { EntityObjectOperationResult, SwitchEntity, TrackSectionEntity } from '../../../../types';
import colors from '../../../../common/Map/Consts/colors';
import GeoJSONs from '../../../../common/Map/Layers/GeoJSONs';
import { SwitchEditionState } from './types';
import EditorForm from '../../components/EditorForm';
import { save } from '../../../../reducers/editor';
import EditorForm from 'applications/editor//components/EditorForm';
import EntitySumUp from 'applications/editor/components/EntitySumUp';
import EntityError from 'applications/editor/components/EntityError';
import EditorContext from 'applications/editor/context';
import { getEntity } from 'applications/editor/data/api';
import { flattenEntity } from 'applications/editor/data/utils';
import { ExtendedEditorContextType } from 'applications/editor/tools/editorContextTypes';
import colors from 'common/Map/Consts/colors';
import GeoJSONs from 'common/Map/Layers/GeoJSONs';
import { getSwitchesLayerProps, getSwitchesNameLayerProps } from 'common/Map/Layers/Switches';
import { save } from 'reducers/editor';
import { getInfraID } from 'reducers/osrdconf/selectors';
import { getMap } from 'reducers/map/selectors';
import { getIsLoading } from 'reducers/main/mainSelector';
import { EntityObjectOperationResult, SwitchEntity, TrackSectionEntity } from 'types';

import { FlatSwitchEntity, flatSwitchToSwitch, getNewSwitch, isSwitchValid } from './utils';
import {
getSwitchesLayerProps,
getSwitchesNameLayerProps,
} from '../../../../common/Map/Layers/Switches';
import { flattenEntity } from '../../data/utils';
import EntitySumUp from '../../components/EntitySumUp';
import EntityError from '../../components/EntityError';
import { getEntity } from '../../data/api';
import { getInfraID } from '../../../../reducers/osrdconf/selectors';
import { getMap } from '../../../../reducers/map/selectors';
import { ExtendedEditorContextType } from '../editorContextTypes';
import { CustomSchemaField } from './components/CustomSchemaField';
import { SwitchEditionState } from './types';
import useSwitch from './useSwitch';
import { CustomSchemaField } from './components/CustomSchemaField';

export const SwitchEditionLeftPanel: FC = () => {
const dispatch = useDispatch();
const { t } = useTranslation();
const infraID = useSelector(getInfraID);
const isLoading = useSelector(getIsLoading);
const { state, setState } = useContext(
EditorContext
) as ExtendedEditorContextType<SwitchEditionState>;
Expand Down Expand Up @@ -132,7 +132,8 @@ export const SwitchEditionLeftPanel: FC = () => {
disabled={
!switchType ||
!isSwitchValid(switchEntity, switchType) ||
state.portEditionState.type !== 'idle'
state.portEditionState.type !== 'idle' ||
isLoading
}
>
{t('common.save')}
Expand Down
42 changes: 24 additions & 18 deletions front/src/applications/editor/tools/trackEdition/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,35 @@ import { last } from 'lodash';
import { Position } from 'geojson';
import { BsBoxArrowInRight } from 'react-icons/bs';

import EditorForm from 'applications/editor/components/EditorForm';
import EntitySumUp from 'applications/editor/components/EntitySumUp';
import EntityError from 'applications/editor/components/EntityError';
import { getEntities } from 'applications/editor/data/api';
import { NEW_ENTITY_ID } from 'applications/editor/data/utils';
import EditorContext from 'applications/editor/context';
import {
getEditCatenaryState,
getEditSpeedSectionState,
} from 'applications/editor/tools/rangeEdition/utils';
import TOOL_TYPES from 'applications/editor/tools/toolTypes';
import { ExtendedEditorContextType } from 'applications/editor/tools/editorContextTypes';
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import EditorContext from '../../context';
import GeoJSONs from '../../../../common/Map/Layers/GeoJSONs';
import colors from '../../../../common/Map/Consts/colors';
import { TrackEditionState } from './types';
import EditorForm from '../../components/EditorForm';
import { save } from '../../../../reducers/editor';
import { Spinner } from 'common/Loader';
import GeoJSONs from 'common/Map/Layers/GeoJSONs';
import colors from 'common/Map/Consts/colors';
import { save } from 'reducers/editor';
import { getIsLoading } from 'reducers/main/mainSelector';
import { getMap } from 'reducers/map/selectors';
import { getInfraID } from 'reducers/osrdconf/selectors';
import {
CatenaryEntity,
EntityObjectOperationResult,
SpeedSectionEntity,
TrackSectionEntity,
} from '../../../../types';
} from 'types';

import { TrackEditionState } from './types';
import { injectGeometry } from './utils';
import { NEW_ENTITY_ID } from '../../data/utils';
import { getMap } from '../../../../reducers/map/selectors';
import { getInfraID } from '../../../../reducers/osrdconf/selectors';
import { getEntities } from '../../data/api';
import { Spinner } from '../../../../common/Loader';
import EntitySumUp from '../../components/EntitySumUp';
import { getEditCatenaryState, getEditSpeedSectionState } from '../rangeEdition/utils';
import TOOL_TYPES from '../toolTypes';
import { ExtendedEditorContextType } from '../editorContextTypes';
import EntityError from '../../components/EntityError';

export const TRACK_LAYER_ID = 'trackEditionTool/new-track-path';
export const POINTS_LAYER_ID = 'trackEditionTool/new-track-points';
Expand Down Expand Up @@ -351,6 +356,7 @@ export const TrackEditionLeftPanel: FC = () => {
const dispatch = useDispatch();
const { t } = useTranslation();
const infraID = useSelector(getInfraID);
const isLoading = useSelector(getIsLoading);
const { state, setState } = useContext(
EditorContext
) as ExtendedEditorContextType<TrackEditionState>;
Expand Down Expand Up @@ -399,7 +405,7 @@ export const TrackEditionLeftPanel: FC = () => {
<button
type="submit"
className="btn btn-primary"
disabled={state.track.geometry.coordinates.length < 2}
disabled={state.track.geometry.coordinates.length < 2 || isLoading}
>
{t('common.save')}
</button>
Expand Down
7 changes: 3 additions & 4 deletions front/src/common/Loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useSelector } from 'react-redux';
import cx from 'classnames';

import './Loader.scss';
import { getLoading } from 'reducers/main/mainSelector';
import { getIsLoading } from 'reducers/main/mainSelector';

type LoaderProps = {
msg?: string;
Expand Down Expand Up @@ -37,9 +37,8 @@ export const LoaderFill: FC<HTMLAttributes<HTMLDivElement>> = ({ className, ...p
);

export const LoaderState: FC<unknown> = () => {
const loading = useSelector(getLoading);
if (loading && loading > 0) return <LoaderSNCF position="top-right" />;
return null;
const isLoading = useSelector(getIsLoading);
return isLoading ? <LoaderSNCF position="top-right" /> : null;
};

export default LoaderSNCF;
3 changes: 2 additions & 1 deletion front/src/reducers/main/mainSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MainState } from 'reducers/main/index';
export const getMain = (state: RootState) => state.main;
const makeMainSelector = makeSubSelector<MainState>(getMain);

export const getLoading = makeMainSelector('loading');
const getLoading = makeMainSelector('loading');
export const getIsLoading = (state: RootState) => getLoading(state) > 0;
export const getNotifications = makeMainSelector('notifications');
export const getLastInterfaceVersion = makeMainSelector('lastInterfaceVersion');

0 comments on commit 832f812

Please sign in to comment.