Skip to content
Snippets Groups Projects
Commit cc506177 authored by Andreas Ellewsen's avatar Andreas Ellewsen
Browse files

Merge branch 'GREG-120-reg-form-validation' into 'master'

Modify register step form for guests

See merge request !204
parents 049e7c86 64abe018
No related branches found
No related tags found
1 merge request!204Modify register step form for guests
Pipeline #104462 passed
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
"startDateMustBeSet": "Start date must be set", "startDateMustBeSet": "Start date must be set",
"startDateMustBeBeforeEndDate": "Start date has to be before end date", "startDateMustBeBeforeEndDate": "Start date has to be before end date",
"passportNationalityAndNumber": "Both passport nationality and number need to be set", "passportNationalityAndNumber": "Both passport nationality and number need to be set",
"doubleIdentity": "*You have input both national ID and passport. Please choose only one.",
"nationalIdOrPassport": "National ID or passport information need to be entered" "nationalIdOrPassport": "National ID or passport information need to be entered"
}, },
"button": { "button": {
...@@ -134,6 +135,8 @@ ...@@ -134,6 +135,8 @@
"registerNewGuest": "Register new guest", "registerNewGuest": "Register new guest",
"guestOverview": "Guest overview", "guestOverview": "Guest overview",
"guestRegisterWizardText": { "guestRegisterWizardText": {
"identityHeader": "Identify yourself ",
"identityBody": "Enter national identity number if you have one. <1>Otherwise</1> use passport information.",
"yourContactInformation": "Your contact information", "yourContactInformation": "Your contact information",
"contactInformationDescription": "Fill in your mobile phone number.", "contactInformationDescription": "Fill in your mobile phone number.",
"yourGuestPeriod": "Your guest period", "yourGuestPeriod": "Your guest period",
......
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
"startDateMustBeSet": "Startdato må være satt", "startDateMustBeSet": "Startdato må være satt",
"startDateMustBeBeforeEndDate": "Startdato må være før sluttdato", "startDateMustBeBeforeEndDate": "Startdato må være før sluttdato",
"passportNationalityAndNumber": "Både passnasjonalitet og nummer må være satt", "passportNationalityAndNumber": "Både passnasjonalitet og nummer må være satt",
"doubleIdentity": "*Du har fylt inn begge feltene over. Bruk kun ett av de.",
"nationalIdOrPassport": "Fødselsnummer/D-nummer eller passinformasjon må spesifiseres" "nationalIdOrPassport": "Fødselsnummer/D-nummer eller passinformasjon må spesifiseres"
}, },
"button": { "button": {
...@@ -134,6 +135,8 @@ ...@@ -134,6 +135,8 @@
"registerNewGuest": "Registrer ny gjest", "registerNewGuest": "Registrer ny gjest",
"guestOverview": "Oversikt over gjest", "guestOverview": "Oversikt over gjest",
"guestRegisterWizardText": { "guestRegisterWizardText": {
"identityHeader": "Identifiser deg ",
"identityBody": "Fyll inn fødselsnummer hvis du har. <1>Eller</1> nasjonalitet og passnummer.",
"yourContactInformation": "Din kontaktinfo", "yourContactInformation": "Din kontaktinfo",
"contactInformationDescription": "Fyll inn ditt mobilnummer.", "contactInformationDescription": "Fyll inn ditt mobilnummer.",
"yourGuestPeriod": "Din gjesteperiode", "yourGuestPeriod": "Din gjesteperiode",
......
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
"startDateMustBeSet": "Startdato må vere satt", "startDateMustBeSet": "Startdato må vere satt",
"startDateMustBeBeforeEndDate": "Startdato må vere før sluttdato", "startDateMustBeBeforeEndDate": "Startdato må vere før sluttdato",
"passportNationalityAndNumber": "Både passnasjonalitet og nummer må vere satt", "passportNationalityAndNumber": "Både passnasjonalitet og nummer må vere satt",
"doubleIdentity": "*Du har fylt inn begge feltene over. Bruk kun ett av de.",
"nationalIdOrPassport": "Fødselsnummer/D-nummer eller passinformasjon må spesifiserast" "nationalIdOrPassport": "Fødselsnummer/D-nummer eller passinformasjon må spesifiserast"
}, },
"button": { "button": {
...@@ -135,6 +136,8 @@ ...@@ -135,6 +136,8 @@
"registerNewGuest": "Registrer ny gjest", "registerNewGuest": "Registrer ny gjest",
"guestOverview": "Oversikt over gjest", "guestOverview": "Oversikt over gjest",
"guestRegisterWizardText": { "guestRegisterWizardText": {
"identityHeader": "Identifiser deg ",
"identityBody": "Fyll inn fødselsnummer hvis du har. <1>Eller</1> nasjonalitet og passnummer.",
"yourContactInformation": "Din kontaktinfo", "yourContactInformation": "Din kontaktinfo",
"contactInformationDescription": "Fyll inn ditt mobilnummer.", "contactInformationDescription": "Fyll inn ditt mobilnummer.",
"yourGuestPeriod": "Din gjesteperiode", "yourGuestPeriod": "Din gjesteperiode",
......
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { FetchedGuest, Guest } from 'interfaces' import { FetchedGuest, Guest } from 'interfaces'
import { parseIdentity, parseRole } from 'utils' import { parseIdentity, parseRole, fetchJsonOpts } from 'utils'
const useGuests = () => { const useGuests = () => {
const [guests, setGuests] = useState<Guest[]>([]) const [guests, setGuests] = useState<Guest[]>([])
const getGuestsInfo = async () => { const getGuestsInfo = () =>
try { fetch('/api/ui/v1/guests/', fetchJsonOpts())
const response = await fetch('/api/ui/v1/guests/?format=json') .then((response) => (response.ok ? response.json() : []))
const jsonResponse = await response.json() .then((persons) => {
if (response.ok) {
const persons = await jsonResponse
setGuests( setGuests(
persons.map( persons.map(
(person: FetchedGuest): Guest => ({ (person: FetchedGuest): Guest => ({
...@@ -28,11 +26,8 @@ const useGuests = () => { ...@@ -28,11 +26,8 @@ const useGuests = () => {
}) })
) )
) )
} })
} catch (error) { .catch(() => setGuests([]))
setGuests([])
}
}
const reloadGuests = () => { const reloadGuests = () => {
getGuestsInfo() getGuestsInfo()
......
...@@ -50,7 +50,7 @@ test('Field showing values correctly', async () => { ...@@ -50,7 +50,7 @@ test('Field showing values correctly', async () => {
await screen.findByDisplayValue(testData.person.fnr) await screen.findByDisplayValue(testData.person.fnr)
// Passport nationality. The i18n-mock sets up en as the i18n.language property, so look for the English name // Passport nationality. The i18n-mock sets up en as the i18n.language property, so look for the English name
await screen.findByText('Denmark') await screen.findByText('DK')
await screen.findByDisplayValue('123456') await screen.findByDisplayValue('123456')
await screen.findByDisplayValue(testData.person.date_of_birth) await screen.findByDisplayValue(testData.person.date_of_birth)
......
import { import {
Box, Box,
Divider,
MenuItem, MenuItem,
Select, Select,
SelectChangeEvent, SelectChangeEvent,
...@@ -16,7 +17,7 @@ import React, { ...@@ -16,7 +17,7 @@ import React, {
useImperativeHandle, useImperativeHandle,
useState, useState,
} from 'react' } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation, Trans } from 'react-i18next'
import { import {
CountryCallingCode, CountryCallingCode,
CountryCode, CountryCode,
...@@ -81,6 +82,7 @@ const GuestRegisterStep = forwardRef( ...@@ -81,6 +82,7 @@ const GuestRegisterStep = forwardRef(
console.log('submit data is', data) console.log('submit data is', data)
const result = await trigger() const result = await trigger()
console.log('trigger result is', result) console.log('trigger result is', result)
const tohandler = data
if ( if (
!data.nationalIdNumber && !data.nationalIdNumber &&
!data.passportNumber && !data.passportNumber &&
...@@ -92,6 +94,22 @@ const GuestRegisterStep = forwardRef( ...@@ -92,6 +94,22 @@ const GuestRegisterStep = forwardRef(
return return
} }
// Users should only input NIN or passport
if (data.passportNumber && data.nationalIdNumber) {
setIdErrorState(t('validation.doubleIdentity'))
return
}
// Reset passportNationality if NIN is set and passport is empty
if (
data.nationalIdNumber &&
!data.passportNumber &&
data.passportNationality
) {
setValue('passportNationality', '')
setPassportNationality('')
tohandler.passportNationality = ''
}
// if one on the passport fields are set, check that both are set // if one on the passport fields are set, check that both are set
if ( if (
(data.passportNumber && !data.passportNationality) || (data.passportNumber && !data.passportNationality) ||
...@@ -105,7 +123,7 @@ const GuestRegisterStep = forwardRef( ...@@ -105,7 +123,7 @@ const GuestRegisterStep = forwardRef(
console.log('register submit errors', errors) console.log('register submit errors', errors)
if (!Object.keys(errors).length) { if (!Object.keys(errors).length) {
nextHandler(data) nextHandler(tohandler)
} }
} }
const onSubmit = handleSubmit<GuestRegisterData>(submit) const onSubmit = handleSubmit<GuestRegisterData>(submit)
...@@ -173,21 +191,41 @@ const GuestRegisterStep = forwardRef( ...@@ -173,21 +191,41 @@ const GuestRegisterStep = forwardRef(
useImperativeHandle(ref, () => ({ doSubmit: () => onSubmit() })) useImperativeHandle(ref, () => ({ doSubmit: () => onSubmit() }))
const passportCountries = Object.keys(getAlpha2Codes())
.map((countryAlphaCode: string) => {
const countryTuple: [string, string] = [
countryAlphaCode,
getName(countryAlphaCode, i18n.language),
]
return countryTuple
})
.filter(
(countryTuple: [string, string]) =>
// All countries are expected to have a name, this filtering
// is here to make some tests run in an environment where the
// internationalization is not set up
countryTuple[1] !== undefined
)
.sort(
(countryTuple1: [string, string], countryTuple2: [string, string]) =>
countryTuple1[1].localeCompare(countryTuple2[1])
)
return ( return (
<> <>
<Typography
variant="h5"
sx={{
paddingTop: '1rem',
paddingBottom: '1rem',
}}
>
{t('guestRegisterWizardText.yourContactInformation')}
</Typography>
<Typography sx={{ paddingBottom: '2rem' }}>
{t('guestRegisterWizardText.contactInformationDescription')}
</Typography>
<Box sx={{ maxWidth: '30rem' }}> <Box sx={{ maxWidth: '30rem' }}>
<Typography
variant="h5"
sx={{
paddingTop: '1rem',
paddingBottom: '1rem',
}}
>
{t('guestRegisterWizardText.yourContactInformation')}
</Typography>
<Typography sx={{ paddingBottom: '2rem' }}>
{t('guestRegisterWizardText.contactInformationDescription')}
<Divider sx={{ border: '1px solid' }} />
</Typography>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
<Stack spacing={2}> <Stack spacing={2}>
{/* The name is only editable if it is it is not coming from some trusted source */} {/* The name is only editable if it is it is not coming from some trusted source */}
...@@ -395,6 +433,16 @@ const GuestRegisterStep = forwardRef( ...@@ -395,6 +433,16 @@ const GuestRegisterStep = forwardRef(
{initialGuestData.authentication_method === {initialGuestData.authentication_method ===
AuthenticationMethod.Invite && ( AuthenticationMethod.Invite && (
<> <>
<Typography variant="h5" sx={{ paddingTop: '1rem' }}>
{t('guestRegisterWizardText.identityHeader')}
</Typography>
<Typography sx={{ paddingBottom: '1rem' }}>
<Trans i18nKey="common:guestRegisterWizardText.identityBody">
Enter national identity number if you have one.{' '}
<strong>Otherwise</strong> use passport information.
</Trans>
<Divider sx={{ border: '1px solid' }} />
</Typography>
{/* The guest should fill in one of national ID number or passport number */} {/* The guest should fill in one of national ID number or passport number */}
<Controller <Controller
name="nationalIdNumber" name="nationalIdNumber"
...@@ -419,68 +467,54 @@ const GuestRegisterStep = forwardRef( ...@@ -419,68 +467,54 @@ const GuestRegisterStep = forwardRef(
/> />
)} )}
/> />
<Box
<Controller
name="passportNumber"
control={control}
render={({ field }) => (
<TextField
id="passportNumber"
data-testid="passport_number_input"
value={field.value}
label={t('input.passportNumber')}
onChange={field.onChange}
/>
)}
/>
<Select
sx={{ sx={{
maxHeight: '2.5rem', display: 'flex',
}} flexDirection: 'row',
id="passport-nationality-id"
labelId="passport-nationality-label"
label={t('input.passportNationality')}
displayEmpty
value={passportNationality ?? ''}
onChange={handlePassportNationalityChange}
renderValue={(selected: any) => {
if (!selected || selected.length === 0) {
return t('input.passportNationality')
}
return getName(selected, i18n.language)
}} }}
> >
<MenuItem disabled value=""> <Select
{t('input.passportNationality')} sx={{
</MenuItem> maxHeight: '2.5rem',
{Object.keys(getAlpha2Codes()) minWidth: '5rem',
.map((countryAlphaCode: string) => { marginRight: '0.5rem',
const countryTuple: [string, string] = [ }}
countryAlphaCode, id="passport-nationality-id"
getName(countryAlphaCode, i18n.language), labelId="passport-nationality-label"
] label={t('input.passportNationality')}
return countryTuple displayEmpty
}) value={passportNationality ?? ''}
.filter( onChange={handlePassportNationalityChange}
(countryTuple: [string, string]) => renderValue={(selected: any) => {
// All countries are expected to have a name, this filtering if (!selected || selected.length === 0) {
// is here to make some tests run in an environment where the return t('input.passportNationality')
// internationalization is not set up }
countryTuple[1] !== undefined return selected
) }}
.sort( >
( <MenuItem disabled value="">
countryTuple1: [string, string], {t('input.passportNationality')}
countryTuple2: [string, string] </MenuItem>
) => countryTuple1[1].localeCompare(countryTuple2[1]) {passportCountries.map((countryTuple) => (
) <MenuItem key={countryTuple[1]} value={countryTuple[0]}>
.map((countryTuple) => ( {`${countryTuple[1]} (${countryTuple[0]})`}
<MenuItem key={countryTuple[0]} value={countryTuple[0]}>
{countryTuple[1]}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
<Controller
name="passportNumber"
control={control}
render={({ field }) => (
<TextField
id="passportNumber"
data-testid="passport_number_input"
value={field.value}
label={t('input.passportNumber')}
onChange={field.onChange}
/>
)}
/>
</Box>
{idErrorState && ( {idErrorState && (
<Typography color="error">{idErrorState}</Typography> <Typography color="error">{idErrorState}</Typography>
)} )}
...@@ -502,6 +536,7 @@ const GuestRegisterStep = forwardRef( ...@@ -502,6 +536,7 @@ const GuestRegisterStep = forwardRef(
</Typography> </Typography>
<Typography sx={{ paddingBottom: '1rem' }}> <Typography sx={{ paddingBottom: '1rem' }}>
{t('guestRegisterWizardText.guestPeriodDescription')} {t('guestRegisterWizardText.guestPeriodDescription')}
<Divider sx={{ border: '1px solid' }} />
</Typography> </Typography>
<TextField <TextField
id="ou-unit" id="ou-unit"
......
...@@ -24,7 +24,7 @@ function ChooseRegistrationMethod() { ...@@ -24,7 +24,7 @@ function ChooseRegistrationMethod() {
<p>{t('description')}</p> <p>{t('description')}</p>
<FlexDiv> <FlexDiv>
<HrefButton to="/oidc/authenticate/">{t('login')}</HrefButton> <HrefButton to="/oidc/authenticate/">{t('login')}</HrefButton>
<HrefLineButton to="/guestregister/">{t('manual')}</HrefLineButton> <HrefLineButton to="/guestregister">{t('manual')}</HrefLineButton>
</FlexDiv> </FlexDiv>
</Page> </Page>
) )
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment