diff --git a/administration/src/cards/extensions/BirthdayExtension.tsx b/administration/src/cards/extensions/BirthdayExtension.tsx index cf3bddbb6..77faee184 100644 --- a/administration/src/cards/extensions/BirthdayExtension.tsx +++ b/administration/src/cards/extensions/BirthdayExtension.tsx @@ -1,8 +1,10 @@ import { FormGroup } from '@blueprintjs/core' -import React, { ReactElement, useState } from 'react' +import React, { ReactElement, useContext, useState } from 'react' +import { useTranslation } from 'react-i18next' import CustomDatePicker from '../../bp-modules/components/CustomDatePicker' import FormErrorMessage from '../../bp-modules/self-service/components/FormErrorMessage' +import { ProjectConfigContext } from '../../project-configs/ProjectConfigContext' import PlainDate from '../../util/PlainDate' import { Extension, ExtensionComponentProps } from './extensions' @@ -11,6 +13,7 @@ export type BirthdayExtensionState = { [BIRTHDAY_EXTENSION_NAME]: PlainDate | nu const minBirthday = new PlainDate(1900, 1, 1) const getInitialState = (): BirthdayExtensionState => ({ birthday: null }) +let validateUnderAge: boolean const BirthdayForm = ({ value, @@ -21,13 +24,24 @@ const BirthdayForm = ({ const [touched, setTouched] = useState(false) const { birthday } = value const showErrorMessage = touched || showRequired + const projectConfig = useContext(ProjectConfigContext) + validateUnderAge = projectConfig.showBirthdayExtensionHint + const { t } = useTranslation('application') + const getErrorMessage = (): string | null => { + const today = PlainDate.fromLocalDate(new Date()) + const underAge = today.subtract({ years: 16 }) + if (!birthday) { return 'Bitte geben Sie ein gültiges Geburtsdatum an.' } - if (birthday.isAfter(PlainDate.fromLocalDate(new Date()))) { + if (birthday.isAfter(today)) { return 'Das Geburtsdatum darf nicht in der Zukunft liegen.' } + if (birthday.isAfter(underAge) && projectConfig.showBirthdayExtensionHint) { + return t('extensions.birthdayHint') + } + return null } @@ -66,7 +80,9 @@ const BirthdayExtension: Extension = { return false } const today = PlainDate.fromLocalDate(new Date()) - return !birthday.isBefore(minBirthday) && !birthday.isAfter(today) + const underAge = today.subtract({ years: 16 }) + const underAgeCheck = birthday.isAfter(underAge) && validateUnderAge + return !birthday.isBefore(minBirthday) && !birthday.isAfter(today) && !underAgeCheck }, fromString: (value: string) => { const birthday = PlainDate.safeFromCustomFormat(value) diff --git a/administration/src/cards/extensions/extensions.tsx b/administration/src/cards/extensions/extensions.tsx index 2f85404c2..fabd05e79 100644 --- a/administration/src/cards/extensions/extensions.tsx +++ b/administration/src/cards/extensions/extensions.tsx @@ -53,7 +53,6 @@ const Extensions = [ AddressPlzExtension, AddressLocationExtension, NuernbergPassIdExtension, - NuernbergPassIdExtension, EMailNotificationExtension, KoblenzReferenceNumberExtension, ] as const diff --git a/administration/src/project-configs/bayern/config.ts b/administration/src/project-configs/bayern/config.ts index c51997050..3f8323699 100644 --- a/administration/src/project-configs/bayern/config.ts +++ b/administration/src/project-configs/bayern/config.ts @@ -93,6 +93,7 @@ const config: ProjectConfig = { enabled: false, }, userImportApiEnabled: false, + showBirthdayExtensionHint: false, } export default config diff --git a/administration/src/project-configs/getProjectConfig.ts b/administration/src/project-configs/getProjectConfig.ts index 51263a6d1..beccd6754 100644 --- a/administration/src/project-configs/getProjectConfig.ts +++ b/administration/src/project-configs/getProjectConfig.ts @@ -119,6 +119,7 @@ export type ProjectConfig = { selfServiceEnabled: boolean storesManagement: StoresManagementConfig userImportApiEnabled: boolean + showBirthdayExtensionHint: boolean } export const setProjectConfigOverride = (hostname: string): void => { diff --git a/administration/src/project-configs/koblenz/config.ts b/administration/src/project-configs/koblenz/config.ts index d2084e629..be3d21114 100644 --- a/administration/src/project-configs/koblenz/config.ts +++ b/administration/src/project-configs/koblenz/config.ts @@ -36,6 +36,7 @@ const config: ProjectConfig = { selfServiceEnabled: true, storesManagement: storesManagementConfig, userImportApiEnabled: true, + showBirthdayExtensionHint: true, } export default config diff --git a/administration/src/project-configs/nuernberg/__tests__/csvExport.test.tsx b/administration/src/project-configs/nuernberg/__tests__/csvExport.test.tsx index 58e71057f..335e47296 100644 --- a/administration/src/project-configs/nuernberg/__tests__/csvExport.test.tsx +++ b/administration/src/project-configs/nuernberg/__tests__/csvExport.test.tsx @@ -9,7 +9,10 @@ jest.mock('csv-stringify/browser/esm/sync', () => ({ stringify: (input: string[][]) => input[0].join(','), })) -jest.mock('../../getProjectConfig') +jest.mock('../../getProjectConfig', () => ({ + __esModule: true, + default: jest.fn(), +})) describe('csvExport', () => { it('header should have same length as line', () => { diff --git a/administration/src/project-configs/nuernberg/config.ts b/administration/src/project-configs/nuernberg/config.ts index d493a758d..e6ed409e9 100644 --- a/administration/src/project-configs/nuernberg/config.ts +++ b/administration/src/project-configs/nuernberg/config.ts @@ -61,6 +61,7 @@ const config: ProjectConfig = { selfServiceEnabled: false, storesManagement: storesManagementConfig, userImportApiEnabled: false, + showBirthdayExtensionHint: false, } export default config diff --git a/administration/src/project-configs/showcase/config.ts b/administration/src/project-configs/showcase/config.ts index 86bccee46..afe035146 100644 --- a/administration/src/project-configs/showcase/config.ts +++ b/administration/src/project-configs/showcase/config.ts @@ -36,6 +36,7 @@ const config: ProjectConfig = { enabled: false, }, userImportApiEnabled: false, + showBirthdayExtensionHint: false, } export default config diff --git a/administration/src/util/PlainDate.ts b/administration/src/util/PlainDate.ts index 723906eb7..48c1dd002 100644 --- a/administration/src/util/PlainDate.ts +++ b/administration/src/util/PlainDate.ts @@ -1,4 +1,4 @@ -import { add, differenceInCalendarDays, format, isValid, parse } from 'date-fns' +import { add, differenceInCalendarDays, format, isValid, parse, sub } from 'date-fns' type DateDuration = { years?: number; months?: number; days?: number } @@ -125,6 +125,15 @@ class PlainDate { return PlainDate.fromLocalDate(add(this.toLocalDate(), duration)) } + /** + * Returns a new PlainDate corresponding to `this` minus `duration`. + * @param duration + */ + subtract(duration: DateDuration): PlainDate { + // Mirror the add() method but use date-fns/sub + return PlainDate.fromLocalDate(sub(this.toLocalDate(), duration)) + } + /** * Returns a new PlainDate corresponding to the result of adding `days` days to 1970-01-01 * @param days diff --git a/administration/src/util/translations/de.json b/administration/src/util/translations/de.json index 54559b8ba..5a558e4b9 100644 --- a/administration/src/util/translations/de.json +++ b/administration/src/util/translations/de.json @@ -65,7 +65,10 @@ "sentSuccessfully": "Erfolgreich gesendet", "sentError": "Fehler beim Senden", "positiveAnswer": "Ja", - "negativeAnswer": "Nein" + "negativeAnswer": "Nein", + "extensions": { + "birthdayHint": "Bei Minderjährigen unter 16 Jahren darf der KoblenzPass nur mit Einverständnis der Erziehungsberechtigten abgerufen werden." + } }, "applications": { "allApplications": "Alle Anträge",