Skip to content

Commit

Permalink
Merge pull request #1089 from digitalfabrik/1082-sozialpass-query-par…
Browse files Browse the repository at this point in the history
…ams-for-card-creation

1082: Prefill card form from query params
  • Loading branch information
sarahsporck authored Aug 18, 2023
2 parents 62d77c3 + f6532b8 commit 73e48c8
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 61 deletions.
4 changes: 2 additions & 2 deletions administration/src/bp-modules/cards/AddCardsController.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NonIdealState, Spinner } from '@blueprintjs/core'
import { useContext } from 'react'
import { useNavigate } from 'react-router-dom'
import { useContext, useEffect } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'

import { WhoAmIContext } from '../../WhoAmIProvider'
import { Region } from '../../generated/graphql'
Expand Down
26 changes: 20 additions & 6 deletions administration/src/bp-modules/cards/AddCardsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useCallback, useContext, useEffect, useRef } from 'react'
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react'
import FlipMove from 'react-flip-move'
import { useSearchParams } from 'react-router-dom'
import styled from 'styled-components'

import { CardBlueprint } from '../../cards/CardBlueprint'
import { Region } from '../../generated/graphql'
import { ProjectConfigContext } from '../../project-configs/ProjectConfigContext'
import CreateCardForm from './AddCardForm'
import CardFormButton from './CardFormButton'
import { getHeaders } from './ImportCardsController'

const FormsWrapper = styled(FlipMove)`
flex-wrap: wrap;
Expand Down Expand Up @@ -42,6 +44,23 @@ type CreateCardsFormProps = {

const CreateCardsForm = ({ region, cardBlueprints, setCardBlueprints }: CreateCardsFormProps) => {
const projectConfig = useContext(ProjectConfigContext)
const [searchParams, setSearchParams] = useSearchParams()

useEffect(() => {
if (cardBlueprints.length === 0) {
const headers = getHeaders(projectConfig)
const cardBlueprint = new CardBlueprint('', projectConfig.card, [region])
headers.forEach(header => {
const value = searchParams.get(header)
if (!value) {
return
}
cardBlueprint.setValue(header, value)
})
setCardBlueprints([cardBlueprint])
setSearchParams()
}
}, [cardBlueprints.length, projectConfig, region, searchParams, setCardBlueprints, setSearchParams])
const bottomRef = useRef<HTMLDivElement>(null)

const scrollToBottom = () => {
Expand All @@ -54,11 +73,6 @@ const CreateCardsForm = ({ region, cardBlueprints, setCardBlueprints }: CreateCa
scrollToBottom()
}, [cardBlueprints, projectConfig.card, region, setCardBlueprints])

useEffect(() => {
// create a form on mount
setCardBlueprints([new CardBlueprint('', projectConfig.card, [region])])
}, [projectConfig.card, region, setCardBlueprints])

const removeCardBlueprint = (oldBlueprint: CardBlueprint) => {
setCardBlueprints(cardBlueprints.filter(blueprint => blueprint !== oldBlueprint))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ const InnerImportCardsController = ({ region }: { region: Region }): ReactElemen
headers.forEach(header => {
const idx = csvHeader.indexOf(header)
if (idx === -1) {
// column is missing in csv
return
}
// column is missing in csv
cardBlueprint.setValue(header, line[idx])
})
return cardBlueprint
Expand Down
72 changes: 25 additions & 47 deletions administration/src/cards/CSVCard.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,48 @@
import { Region } from '../generated/graphql'
import { CardConfig } from '../project-configs/getProjectConfig'
import PlainDate from '../util/PlainDate'
import { CardBlueprint } from './CardBlueprint'

type HeaderMap = {
[header: string]: {
getValue: () => string | null
setValue: (value: string) => void
isValid: () => boolean
}
}

class CSVCard extends CardBlueprint {
private readonly headerMap: HeaderMap

constructor(cardConfig: CardConfig, region: Region) {
super('', cardConfig)

this.headerMap = {
[cardConfig.nameColumnName]: {
getValue: () => this.fullName ?? null,
setValue: value => (this.fullName = value),
isValid: () => this.isFullNameValid(),
},
[cardConfig.expiryColumnName]: {
getValue: () => (this.expirationDate ? this.expirationDate.format('dd.MM.yyyy') : null),
setValue: value => this.setExpirationDate(value),
isValid: () => this.isExpirationDateValid() || this.hasInfiniteLifetime(),
},
}

this.extensions.forEach((extension, idx) => {
const columnName = cardConfig.extensionColumnNames[idx]
if (!columnName) {
extension.setInitialState(region)
return
}

this.headerMap[columnName] = {
getValue: () => extension.toString(),
setValue: (value: string) => extension.fromString(value),
isValid: () => extension.isValid(),
}
})
}

setExpirationDate = (value: string) => {
if (value.length === 0) return null
try {
this.expirationDate = PlainDate.fromCustomFormat(value, 'dd.MM.yyyy')
} catch (error) {
this.expirationDate = null
console.error("Could not parse date from string '" + value + "' with format dd.MM.yyyy.", error)
getValue(key: string): string | null {
switch (key) {
case this.cardConfig.nameColumnName:
return this.fullName
case this.cardConfig.expiryColumnName:
return this.expirationDate ? this.expirationDate.format('dd.MM.yyyy') : null
default:
const extensionIdx = this.cardConfig.extensionColumnNames.indexOf(key)
if (extensionIdx === -1) {
return null
}
return this.extensions[extensionIdx].toString()
}
}

setValue = (header: string, value: string) => {
this.headerMap[header]?.setValue(value)
}

getValue = (header: string) => {
return this.headerMap[header]?.getValue() ?? null
}

isValueValid = (header: string): boolean => {
return this.headerMap[header]?.isValid() ?? false
isValueValid = (key: string): boolean => {
switch (key) {
case this.cardConfig.nameColumnName:
return this.isFullNameValid()
case this.cardConfig.expiryColumnName:
return this.isExpirationDateValid() || this.hasInfiniteLifetime()
default:
const extensionIdx = this.cardConfig.extensionColumnNames.indexOf(key)
if (extensionIdx === -1) {
return false
}
return this.extensions[extensionIdx].isValid()
}
}
}

Expand Down
28 changes: 28 additions & 0 deletions administration/src/cards/CardBlueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ export class CardBlueprint {
fullName: string
expirationDate: PlainDate | null
extensions: ExtensionInstance[]
cardConfig: CardConfig

constructor(fullName: string, cardConfig: CardConfig, initParams?: Parameters<CardBlueprint['initialize']>) {
this.cardConfig = cardConfig
this.fullName = fullName
this.expirationDate =
cardConfig.defaultValidity && initParams
Expand All @@ -44,6 +46,23 @@ export class CardBlueprint {
}
}

setValue(key: string, value: string): void {
switch (key) {
case this.cardConfig.nameColumnName:
this.fullName = value
break
case this.cardConfig.expiryColumnName:
this.setExpirationDate(value)
break
default:
const extensionIdx = this.cardConfig.extensionColumnNames.indexOf(key)
if (extensionIdx === -1) {
return
}
this.extensions[extensionIdx].fromString(value)
}
}

initialize(region: Region) {
this.extensions.forEach(ext => {
if (ext instanceof RegionExtension) ext.setInitialState(region)
Expand All @@ -69,6 +88,15 @@ export class CardBlueprint {
return this.expirationDate !== null && this.expirationDate.isAfter(today)
}

setExpirationDate(value: string) {
if (value.length === 0) return
try {
this.expirationDate = PlainDate.fromCustomFormat(value, 'dd.MM.yyyy')
} catch (error) {
console.error("Could not parse date from string '" + value + "' with format dd.MM.yyyy.", error)
}
}

isValid(): boolean {
return (
// Name valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class NuernbergPassIdExtension extends Extension<NuernbergPassIdState, null> {
}

fromString(state: string) {
this.state = { nuernbergPassId: parseInt(state, 10) }
const nuernbergPassId = parseInt(state, 10)
this.state = !isNaN(nuernbergPassId) ? { nuernbergPassId } : null
}

toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class NuernbergPassNumberExtension extends Extension<NuernbergPassNumberState, n
}

fromString(state: string) {
this.state = { passNumber: parseInt(state, 10) }
const passNumber = parseInt(state, 10)
this.state = !isNaN(passNumber) ? { passNumber } : null
}

toString() {
Expand Down
2 changes: 1 addition & 1 deletion administration/src/cards/extensions/extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { PartialMessage } from '@bufbuild/protobuf'
import { ReactElement } from 'react'

import { CardExtensions } from '../../generated/card_pb'
import AddressExtensions from './AddressFieldExtensons'
import AddressExtensions from './AddressFieldExtensions'
import BavariaCardTypeExtension from './BavariaCardTypeExtension'
import BirthdayExtension from './BirthdayExtension'
import NuernbergPassIdExtension from './NuernbergPassIdExtension'
Expand Down
2 changes: 1 addition & 1 deletion administration/src/project-configs/nuernberg/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AddressExtensions from '../../cards/extensions/AddressFieldExtensons'
import AddressExtensions from '../../cards/extensions/AddressFieldExtensions'
import BirthdayExtension from '../../cards/extensions/BirthdayExtension'
import NuernbergPassIdExtension from '../../cards/extensions/NuernbergPassIdExtension'
import NuernbergPassNumberExtension from '../../cards/extensions/NuernbergPassNumberExtension'
Expand Down
2 changes: 1 addition & 1 deletion administration/src/project-configs/nuernberg/pdf.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PDFForm, rgb } from 'pdf-lib'

import AddressExtensions from '../../cards/extensions/AddressFieldExtensons'
import AddressExtensions from '../../cards/extensions/AddressFieldExtensions'
import NuernbergPassIdExtension from '../../cards/extensions/NuernbergPassIdExtension'
import { findExtension } from '../../cards/extensions/extensions'
import { InfoParams } from '../../cards/pdf/PdfTextElement'
Expand Down

0 comments on commit 73e48c8

Please sign in to comment.