Skip to content

Commit 83d734b

Browse files
committed
Line: Ensure VehicleMode and LineType match
1 parent 24dcffe commit 83d734b

7 files changed

Lines changed: 91 additions & 10 deletions

File tree

cypress/e2e/lines.cy.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,16 @@ describe('Lines', { tags: [Tag.Lines] }, () => {
133133
ObservationDateControl.setObservationDate(withinValidityPeriod);
134134
cy.getByTestId('LineMissingBox::notValidText').should('not.exist');
135135
});
136+
137+
it('should error on invalid vehicle mode and line type combination', () => {
138+
LineDetailsPage.getEditLineButton().click();
139+
140+
LineForm.selectVehicleType('Raitiovaunu');
141+
142+
LineForm.save();
143+
cy.getByTestId('ValidationError::message::typeOfLine').shouldHaveText(
144+
'Linjan tyyppi ei vastaa sen kulkumuotoa',
145+
);
146+
});
136147
});
137148
});

cypress/pageObjects/forms/LineForm.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export class LineForm {
5959
}
6060

6161
static save() {
62+
cy.getByTestId('LineForm::saveButton').scrollIntoView();
6263
return cy.getByTestId('LineForm::saveButton').click();
6364
}
6465

ui/src/components/forms/line/LineForm.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
FormState as LinePropertiesFormState,
2020
schema as linePropertiesFormSchema,
2121
} from './LinePropertiesForm';
22+
import { lineTypesByVehicleMode } from './LineTypeDropdown';
2223

2324
export type FormState = LinePropertiesFormState & ChangeValidityFormState;
2425

@@ -27,9 +28,21 @@ const testIds = {
2728
cancelButton: 'LineForm::cancelButton',
2829
};
2930

31+
const INVALID_LINE_TYPE = 'invalidLineType';
32+
3033
const formSchema = linePropertiesFormSchema
3134
.merge(changeValidityFormSaveFormSchema)
32-
.superRefine(refineValidityPeriodSchema);
35+
.superRefine(refineValidityPeriodSchema)
36+
.superRefine((line, ctx) => {
37+
const validLineTypes = lineTypesByVehicleMode[line.primaryVehicleMode];
38+
if (!validLineTypes.includes(line.typeOfLine)) {
39+
ctx.addIssue({
40+
code: 'custom',
41+
message: INVALID_LINE_TYPE,
42+
path: ['typeOfLine'],
43+
});
44+
}
45+
});
3346

3447
type LineFormProps = {
3548
readonly defaultValues: Partial<FormState>;

ui/src/components/forms/line/LinePropertiesForm.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,15 @@ export const LinePropertiesForm: FC<LinePropertiesFormProps> = ({
5656
editing,
5757
}) => {
5858
const { t } = useTranslation();
59-
const { getValues } = useFormContext<FormState>();
59+
const { getValues, watch } = useFormContext<FormState>();
6060

6161
// if either own of {Swedish name, Finnish short name, Swedish short name} are not defined, open
6262
// the "name versions" section by default
63-
const defaultShowNameVersions =
64-
!getValues('name.sv_FI') ||
65-
!getValues('shortName.fi_FI') ||
66-
!getValues('shortName.sv_FI');
6763
const [showNameVersions, setShowNameVersions] = useState<boolean>(
68-
defaultShowNameVersions,
64+
() =>
65+
!getValues('name.sv_FI') ||
66+
!getValues('shortName.fi_FI') ||
67+
!getValues('shortName.sv_FI'),
6968
);
7069

7170
return (
@@ -175,6 +174,7 @@ export const LinePropertiesForm: FC<LinePropertiesFormProps> = ({
175174
<LineTypeDropdown
176175
// eslint-disable-next-line react/jsx-props-no-spreading
177176
{...props}
177+
vehicleMode={watch('primaryVehicleMode')}
178178
/>
179179
)}
180180
/>

ui/src/components/forms/line/LineTypeDropdown.tsx

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,72 @@
1+
import without from 'lodash/without';
12
import { FC } from 'react';
23
import { useTranslation } from 'react-i18next';
3-
import { RouteTypeOfLineEnum } from '../../../generated/graphql';
4+
import {
5+
ReusableComponentsVehicleModeEnum,
6+
RouteTypeOfLineEnum,
7+
} from '../../../generated/graphql';
48
import { mapLineTypeToUiName } from '../../../i18n/uiNameMappings';
59
import { FormInputProps } from '../../../uiComponents';
610
import { AllOptionEnum } from '../../../utils';
711
import { EnumDropdown } from '../common';
812

13+
type LineTypesByVehicleMode = Record<
14+
ReusableComponentsVehicleModeEnum,
15+
ReadonlyArray<RouteTypeOfLineEnum>
16+
>;
17+
18+
export const lineTypesByVehicleMode: Readonly<LineTypesByVehicleMode> = {
19+
[ReusableComponentsVehicleModeEnum.Bus]: [
20+
RouteTypeOfLineEnum.ExpressBusService,
21+
RouteTypeOfLineEnum.DemandAndResponseBusService,
22+
RouteTypeOfLineEnum.RegionalBusService,
23+
RouteTypeOfLineEnum.StoppingBusService,
24+
],
25+
26+
[ReusableComponentsVehicleModeEnum.Ferry]: [RouteTypeOfLineEnum.FerryService],
27+
28+
[ReusableComponentsVehicleModeEnum.Train]: [
29+
RouteTypeOfLineEnum.RegionalRailService,
30+
RouteTypeOfLineEnum.SuburbanRailway,
31+
],
32+
33+
[ReusableComponentsVehicleModeEnum.Tram]: [
34+
RouteTypeOfLineEnum.CityTramService,
35+
RouteTypeOfLineEnum.RegionalTramService,
36+
],
37+
38+
[ReusableComponentsVehicleModeEnum.Metro]: [RouteTypeOfLineEnum.MetroService],
39+
};
40+
41+
const allRouteTypes: ReadonlyArray<RouteTypeOfLineEnum> =
42+
Object.values(RouteTypeOfLineEnum);
43+
const disabledRouteTypesByVehicleMode: Readonly<LineTypesByVehicleMode> =
44+
Object.values(ReusableComponentsVehicleModeEnum).reduce(
45+
(disabled, vehicleMode) => ({
46+
...disabled,
47+
[vehicleMode]: without(
48+
allRouteTypes,
49+
...lineTypesByVehicleMode[vehicleMode],
50+
),
51+
}),
52+
{} as Record<
53+
ReusableComponentsVehicleModeEnum,
54+
ReadonlyArray<RouteTypeOfLineEnum>
55+
>,
56+
);
57+
958
type LineTypeDropdownProps = FormInputProps & {
1059
readonly id?: string;
1160
readonly testId?: string;
1261
readonly includeAllOption?: boolean;
62+
readonly vehicleMode?: ReusableComponentsVehicleModeEnum;
1363
};
1464

1565
export const LineTypeDropdown: FC<LineTypeDropdownProps> = ({
1666
id,
1767
testId,
1868
includeAllOption,
69+
vehicleMode,
1970
...formInputProps
2071
}) => {
2172
const { t } = useTranslation();
@@ -28,6 +79,9 @@ export const LineTypeDropdown: FC<LineTypeDropdownProps> = ({
2879
placeholder={t(($) => $.lines.chooseTypeOfLine)}
2980
uiNameMapper={(value) => mapLineTypeToUiName(t, value)}
3081
includeAllOption={!!includeAllOption}
82+
disabledOptions={
83+
vehicleMode && disabledRouteTypesByVehicleMode[vehicleMode]
84+
}
3185
// eslint-disable-next-line react/jsx-props-no-spreading
3286
{...formInputProps}
3387
/>

ui/src/locales/en-US/common.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,8 @@
11401140
"invalidValidityEndDate": "Invalid validity end date",
11411141
"invalidValidityStartDate": "Invalid validity start date",
11421142
"validityEndDateBeforeStartDate": "Validity end date cannot be before start date",
1143-
"nameValueTooLong": "Name has over 21 characters."
1143+
"nameValueTooLong": "Name has over 21 characters.",
1144+
"invalidLineType": "Line type must match with it's vehicle mode"
11441145
},
11451146
"errors": {
11461147
"deleteFailed": "Deletion failed",

ui/src/locales/fi-FI/common.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,8 @@
11401140
"invalidValidityEndDate": "Päättymispäivämäärä on virheellinen",
11411141
"invalidValidityStartDate": "Alkupäivämäärä on virheellinen",
11421142
"validityEndDateBeforeStartDate": "Päättymispäivämäärä ei voi olla ennen alkamispäivämäärää",
1143-
"nameValueTooLong": "Nimessä on yli 21 merkkiä."
1143+
"nameValueTooLong": "Nimessä on yli 21 merkkiä.",
1144+
"invalidLineType": "Linjan tyyppi ei vastaa sen kulkumuotoa"
11441145
},
11451146
"errors": {
11461147
"deleteFailed": "Poistaminen epäonnistui",

0 commit comments

Comments
 (0)