diff --git a/administration/src/cards/card.test.ts b/administration/src/cards/card.test.ts index ec28dc40e3..01a49b2434 100644 --- a/administration/src/cards/card.test.ts +++ b/administration/src/cards/card.test.ts @@ -16,6 +16,7 @@ import { initializeCardFromCSV, isValid, isValueValid, + updateCard, } from './card' import AddressExtensions from './extensions/AddressFieldExtensions' import BavariaCardTypeExtension, { @@ -51,6 +52,29 @@ describe('Card', () => { expect(card.extensions[REGION_EXTENSION_NAME]).toEqual(region.id) }) + describe('updateCard', () => { + it('should clear the expirationDate when the card becomes an infinite-lifetime Goldkarte', () => { + const card = initializeCard(cardConfig, region, { fullName: 'Thea Test' }) + expect(card.expirationDate).not.toBeNull() + + const goldCard = updateCard(card, { + extensions: { [BAVARIA_CARD_TYPE_EXTENSION_NAME]: 'Goldkarte' }, + }) + + expect(goldCard.expirationDate).toBeNull() + expect(isValid(goldCard, cardConfig)).toBe(true) + }) + + it('should keep the expirationDate for a card with finite lifetime', () => { + const card = initializeCard(cardConfig, region, { fullName: 'Thea Test' }) + const newExpirationDate = Temporal.PlainDate.from('2022-06-15') + + const updated = updateCard(card, { expirationDate: newExpirationDate }) + + expect(updated.expirationDate).toEqual(newExpirationDate) + }) + }) + it('should generate CardInfo even with invalid expiration date', () => { const card = initializeCard(cardConfig, region, { fullName: '', @@ -103,13 +127,13 @@ describe('Card', () => { it('should correctly set and get value', () => { const dateString = '03.04.2022' const date = parseGermanPlainDateString(dateString) - const line = ['Thea Test', dateString, 'Goldkarte'] + const line = ['Thea Test', dateString, 'Standard'] const headers = ['Name', 'Ablaufdatum', 'Kartentyp'] const card = initializeCardFromCSV(cardConfig, line, headers, region) expect(card.fullName).toBe('Thea Test') expect(card.expirationDate).toEqual(date) - expect(card.extensions[BAVARIA_CARD_TYPE_EXTENSION_NAME]).toBe('Goldkarte') + expect(card.extensions[BAVARIA_CARD_TYPE_EXTENSION_NAME]).toBe('Standard') expect(isValueValid(card, cardConfig, 'Name')).toBeTruthy() expect(isValueValid(card, cardConfig, 'Ablaufdatum')).toBeTruthy() @@ -120,7 +144,31 @@ describe('Card', () => { expect(getValueByCSVHeader(card, cardConfig, 'Ablaufdatum')).toBe( formatDateDefaultGerman(date), ) - expect(getValueByCSVHeader(card, cardConfig, 'Kartentyp')).toBe('Goldkarte') + expect(getValueByCSVHeader(card, cardConfig, 'Kartentyp')).toBe('Standard') + }) + + it('should treat a gold card with an expiration date as invalid', () => { + const line = ['Thea Test', '03.04.2022', 'Goldkarte'] + const headers = ['Name', 'Ablaufdatum', 'Kartentyp'] + const card = initializeCardFromCSV(cardConfig, line, headers, region) + + expect(card.expirationDate).not.toBeNull() + expect(card.extensions[BAVARIA_CARD_TYPE_EXTENSION_NAME]).toBe('Goldkarte') + + expect(isValueValid(card, cardConfig, 'Ablaufdatum')).toBeFalsy() + expect(isValid(card, cardConfig)).toBeFalsy() + }) + + it('should treat a gold card without an expiration date as valid', () => { + const line = ['Thea Test', '', 'Goldkarte'] + const headers = ['Name', 'Ablaufdatum', 'Kartentyp'] + const card = initializeCardFromCSV(cardConfig, line, headers, region) + + expect(card.expirationDate).toBeNull() + expect(card.extensions[BAVARIA_CARD_TYPE_EXTENSION_NAME]).toBe('Goldkarte') + + expect(isValueValid(card, cardConfig, 'Ablaufdatum')).toBeTruthy() + expect(isValid(card, cardConfig)).toBeTruthy() }) it('should not modify value for invalid header', () => { diff --git a/administration/src/cards/card.ts b/administration/src/cards/card.ts index ae2e016a56..3c299982c6 100644 --- a/administration/src/cards/card.ts +++ b/administration/src/cards/card.ts @@ -141,7 +141,11 @@ export const isExpirationDateValid = (card: Card, { nullable } = { nullable: fal const startDay = card.extensions.startDay if (card.expirationDate === null) { - return nullable + return nullable || hasInfiniteLifetime(card) + } + + if (hasInfiniteLifetime(card)) { + return false } return ( @@ -167,8 +171,7 @@ export const isValid = ( ): boolean => isFullNameValid(card) && getExtensions(card).every(({ extension, state }) => extension.isValid(state)) && - (isExpirationDateValid(card, { nullable: expirationDateNullable }) || - hasInfiniteLifetime(card)) && + isExpirationDateValid(card, { nullable: expirationDateNullable }) && cardHasAllMandatoryExtensions(card, cardConfig) export const generateCardInfo = (card: Card): CardInfo => { @@ -203,7 +206,7 @@ export const isValueValid = (card: Card, cardConfig: CardConfig, columnHeader: s case cardConfig.nameColumnName: return isFullNameValid(card) case cardConfig.expiryColumnName: - return isExpirationDateValid(card) || hasInfiniteLifetime(card) + return isExpirationDateValid(card) default: { const extensionName = getExtensionNameByCSVHeader(cardConfig, columnHeader) const extension = cardConfig.extensions.find(extension => extension.name === extensionName) @@ -269,14 +272,14 @@ export const initializeCardFromCSV = ( } } -export const updateCard = (oldCard: Card, updatedCard: Partial): Card => ({ - ...oldCard, - ...updatedCard, - extensions: { - ...oldCard.extensions, - ...(updatedCard.extensions ?? {}), - }, -}) +export const updateCard = (oldCard: Card, updatedCard: Partial): Card => { + const mergedCard = { + ...oldCard, + ...updatedCard, + extensions: { ...oldCard.extensions, ...updatedCard.extensions }, + } + return hasInfiniteLifetime(mergedCard) ? { ...mergedCard, expirationDate: null } : mergedCard +} export const getFullNameValidationErrorMessage = (name: string): string => { const normalizedName = normalizeWhitespace(name) diff --git a/administration/src/components/FormAlert.tsx b/administration/src/components/FormAlert.tsx index d1adcc795b..455d79d34c 100644 --- a/administration/src/components/FormAlert.tsx +++ b/administration/src/components/FormAlert.tsx @@ -31,7 +31,12 @@ const FormAlert = ({ severity = 'error', }: FormAlertProps): ReactElement | null => errorMessage != null ? ( - + {errorMessage} diff --git a/administration/src/routes/antrag-verifizieren/ApplicationVerifierView.tsx b/administration/src/routes/antrag-verifizieren/ApplicationVerifierView.tsx index 3773f7d75c..7954faa744 100644 --- a/administration/src/routes/antrag-verifizieren/ApplicationVerifierView.tsx +++ b/administration/src/routes/antrag-verifizieren/ApplicationVerifierView.tsx @@ -1,9 +1,10 @@ import { Check, Close } from '@mui/icons-material' -import { Alert, Button, Card, Divider, Typography, styled } from '@mui/material' +import { Button, Card, Divider, Typography, styled } from '@mui/material' import React, { ReactElement, useContext } from 'react' import { Trans, useTranslation } from 'react-i18next' import { Temporal } from 'temporal-polyfill' +import AlertBox from '../../components/AlertBox' import JsonFieldView from '../../components/JsonFieldView' import PageLayout from '../../components/PageLayout' import { GetApplicationByApplicantQuery } from '../../graphql' @@ -17,11 +18,6 @@ const ApplicationViewCard = styled(Card)` margin: 16px auto 16px auto; ` -const StyledAlert = styled(Alert)` - margin: 20px 0; - background-color: transparent; -` - const ButtonContainer = styled('div')` display: flex; width: inherit; @@ -78,9 +74,11 @@ const ApplicationVerifierView = ({ values={{ organizationName: verification.organizationName }} /> - - - + } + sx={{ my: 3 }} + />