Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(application-system): Add phone and email fields to tablerepeater #17219

Merged
merged 15 commits into from
Dec 16, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ const careerHistoryCompaniesValidation = (data: any) => {
}
export const ExampleSchema = z.object({
approveExternalData: z.boolean().refine((v) => v),
tableRepeaterField: z.array(
z.object({
nationalIdWithName: z.object({
name: z.string().min(1).max(256),
nationalId: z.string().refine((n) => n && kennitala.isValid(n), {
params: m.dataSchemeNationalId,
}),
phone: z.string().refine(isValidNumber, {
params: m.dataSchemePhoneNumber,
}),
email: z.string().email(),
}),
}),
),
person: z.object({
name: z.string().min(1).max(256),
age: z.string().refine((x) => {
Expand Down
6 changes: 6 additions & 0 deletions libs/application/types/src/lib/Fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export type RepeaterItem = {
*/
displayInTable?: boolean
label?: StaticText
phoneLabel?: StaticText
emailLabel?: StaticText
placeholder?: StaticText
options?: TableRepeaterOptions
backgroundColor?: 'blue' | 'white'
Expand All @@ -99,6 +101,10 @@ export type RepeaterItem = {
activeField?: Record<string, string>,
) => boolean
dataTestId?: string
showPhoneField?: boolean
phoneRequired?: boolean
showEmailField?: boolean
emailRequired?: boolean
readonly?:
| boolean
| ((
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { FC, useEffect, useState } from 'react'
import { GridRow, GridColumn } from '@island.is/island-ui/core'
import { useLocale } from '@island.is/localization'
import {
coreErrorMessages,
getErrorViaPath,
getValueViaPath,
} from '@island.is/application/core'
import { coreErrorMessages, getValueViaPath } from '@island.is/application/core'
import { Application, StaticText } from '@island.is/application/types'
import { gql, useLazyQuery } from '@apollo/client'
import {
IdentityInput,
Query,
RskCompanyInfoInput,
} from '@island.is/api/schema'
import { InputController } from '@island.is/shared/form-fields'
import {
InputController,
PhoneInputController,
} from '@island.is/shared/form-fields'
import { useFormContext } from 'react-hook-form'
import * as kennitala from 'kennitala'
import debounce from 'lodash/debounce'
Expand All @@ -27,14 +26,23 @@ interface NationalIdWithNameProps {
customId?: string
customNationalIdLabel?: StaticText
customNameLabel?: StaticText
phoneLabel?: StaticText
emailLabel?: StaticText
phoneRequired?: boolean
emailRequired?: boolean
onNationalIdChange?: (s: string) => void
onNameChange?: (s: string) => void
nationalIdDefaultValue?: string
nameDefaultValue?: string
phoneDefaultValue?: string
emailDefaultValue?: string
errorMessage?: string
minAgePerson?: number
searchPersons?: boolean
searchCompanies?: boolean
showPhoneField?: boolean
showEmailField?: boolean
error?: string
}

export const NationalIdWithName: FC<
Expand All @@ -46,19 +54,30 @@ export const NationalIdWithName: FC<
required,
customId = '',
customNationalIdLabel = '',
phoneLabel = '',
emailLabel = '',
phoneRequired = false,
emailRequired = false,
customNameLabel = '',
onNationalIdChange,
onNameChange,
nationalIdDefaultValue,
nameDefaultValue,
phoneDefaultValue,
emailDefaultValue,
errorMessage,
minAgePerson,
searchPersons = true,
searchCompanies = false,
showPhoneField = false,
showEmailField = false,
error,
}) => {
const fieldId = customId.length > 0 ? customId : id
const nameField = `${fieldId}.name`
const nationalIdField = `${fieldId}.nationalId`
const emailField = `${fieldId}.email`
const phoneField = `${fieldId}.phone`

const { formatMessage } = useLocale()
const {
Expand All @@ -69,12 +88,18 @@ export const NationalIdWithName: FC<
const [personName, setPersonName] = useState('')
const [companyName, setCompanyName] = useState('')

// get name validation errors
const nameFieldErrors = errorMessage
? nameDefaultValue?.length === 0
? errorMessage
: undefined
: getErrorViaPath(errors, nameField)
const getFieldErrorString = (
error: unknown,
id: string,
): string | undefined => {
if (!error || typeof error !== 'object') return undefined

const errorList = error as Record<string, unknown>[]
if (!Array.isArray(errorList)) return undefined

const fieldError = errorList[id as any]
return typeof fieldError === 'string' ? fieldError : undefined
}

// get national id validation errors
let nationalIdFieldErrors: string | undefined
Expand All @@ -91,7 +116,7 @@ export const NationalIdWithName: FC<
{ minAge: minAgePerson },
)
} else if (!errorMessage) {
nationalIdFieldErrors = getErrorViaPath(errors, nationalIdField)
nationalIdFieldErrors = getFieldErrorString(error, 'nationalId')
}

// get default values
Expand All @@ -101,6 +126,12 @@ export const NationalIdWithName: FC<
const defaultName = nameDefaultValue
? nameDefaultValue
: getValueViaPath(application.answers, nameField, '')
const defaultPhone = phoneDefaultValue
? phoneDefaultValue
: getValueViaPath<string>(application.answers, phoneField, '')
const defaultEmail = emailDefaultValue
? emailDefaultValue
: getValueViaPath<string>(application.answers, emailField, '')

// query to get name by national id
const [getIdentity, { data, loading: queryLoading, error: queryError }] =
Expand Down Expand Up @@ -180,62 +211,95 @@ export const NationalIdWithName: FC<
}, [personName, companyName, setValue, nameField, application.answers])

return (
<GridRow>
<GridColumn span={['1/1', '1/1', '1/1', '1/2']} paddingTop={2}>
<InputController
id={nationalIdField}
label={
customNationalIdLabel
? formatMessage(customNationalIdLabel)
: formatMessage(coreErrorMessages.nationalRegistryNationalId)
}
defaultValue={defaultNationalId}
format="######-####"
required={required}
backgroundColor="blue"
onChange={debounce((v) => {
setNationalIdInput(v.target.value.replace(/\W/g, ''))
onNationalIdChange &&
onNationalIdChange(v.target.value.replace(/\W/g, ''))
})}
loading={searchPersons ? queryLoading : companyQueryLoading}
error={nationalIdFieldErrors}
disabled={disabled}
/>
</GridColumn>
<GridColumn span={['1/1', '1/1', '1/1', '1/2']} paddingTop={2}>
<InputController
id={nameField}
defaultValue={defaultName}
label={
customNameLabel
? formatMessage(customNameLabel)
: formatMessage(coreErrorMessages.nationalRegistryName)
}
required={required}
error={
searchPersons
? queryError || data?.identity === null
? formatMessage(
coreErrorMessages.nationalRegistryNameNotFoundForNationalId,
)
: nameFieldErrors && !data
? nameFieldErrors
: undefined
: searchCompanies
? companyQueryError ||
companyData?.companyRegistryCompany === null
? formatMessage(
coreErrorMessages.nationalRegistryNameNotFoundForNationalId,
)
: nameFieldErrors && !companyData
? nameFieldErrors
<>
<GridRow>
<GridColumn span={['1/1', '1/1', '1/1', '1/2']} paddingTop={2}>
<InputController
id={nationalIdField}
label={
customNationalIdLabel
? formatMessage(customNationalIdLabel)
: formatMessage(coreErrorMessages.nationalRegistryNationalId)
}
defaultValue={defaultNationalId}
format="######-####"
required={required}
backgroundColor="blue"
onChange={debounce((v) => {
setNationalIdInput(v.target.value.replace(/\W/g, ''))
onNationalIdChange &&
onNationalIdChange(v.target.value.replace(/\W/g, ''))
})}
loading={searchPersons ? queryLoading : companyQueryLoading}
error={nationalIdFieldErrors}
disabled={disabled}
/>
</GridColumn>
<GridColumn span={['1/1', '1/1', '1/1', '1/2']} paddingTop={2}>
<InputController
id={nameField}
defaultValue={defaultName}
label={
customNameLabel
? formatMessage(customNameLabel)
: formatMessage(coreErrorMessages.nationalRegistryName)
}
required={required}
error={
searchPersons
? queryError || data?.identity === null
? formatMessage(
coreErrorMessages.nationalRegistryNameNotFoundForNationalId,
)
: getFieldErrorString(error, 'name') && !data
? getFieldErrorString(error, 'name')
: undefined
: searchCompanies
? companyQueryError ||
companyData?.companyRegistryCompany === null
? formatMessage(
coreErrorMessages.nationalRegistryNameNotFoundForNationalId,
)
: getFieldErrorString(error, 'name') && !companyData
? getFieldErrorString(error, 'name')
: undefined
: undefined
: undefined
}
disabled
/>
</GridColumn>
</GridRow>
}
disabled
/>
</GridColumn>
</GridRow>
{(showPhoneField || showEmailField) && (
<GridRow>
{showPhoneField && (
<GridColumn span={['1/1', '1/1', '1/1', '1/2']} paddingTop={2}>
<PhoneInputController
id={phoneField}
label={formatMessage(phoneLabel)}
defaultValue={defaultPhone}
required={phoneRequired}
backgroundColor="blue"
error={getFieldErrorString(error, 'phone')}
disabled={disabled}
/>
</GridColumn>
)}
{showEmailField && (
<GridColumn span={['1/1', '1/1', '1/1', '1/2']} paddingTop={2}>
<InputController
id={emailField}
label={formatMessage(emailLabel)}
defaultValue={defaultEmail}
type="email"
required={emailRequired}
backgroundColor="blue"
error={getFieldErrorString(error, 'email')}
disabled={disabled}
/>
</GridColumn>
)}
</GridRow>
)}
</>
)
}
Loading