From 0276c91e01ae5899326553900cafe40373dc3c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3hanna=20Magn=C3=BAsd=C3=B3ttir?= Date: Mon, 25 Nov 2024 08:45:00 +0000 Subject: [PATCH] fix(samgongustofa): Validate when buyer fields are empty in TransferOfVehicleOwnership (#16999) * Validate buyer + buyerCoOwnerAndOperator in TransferOfVehicleOwnership * Validate added co-owners i ChangeCoOwnerOfVehicle * Fix getApproveAnswers for changeOperatorOfVehicle * Add error message if no changes in ChangeOperatorOfVehicle * Revert "Validate added co-owners i ChangeCoOwnerOfVehicle" This reverts commit 719277e833dc596179d09c0ce3b319d2142e00f0. * Add error message if no changes in ChangeCoOwnerOfVehicle * Cleanup * Use watch instead of getApplicationInfo --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/fields/CoOwnerRepeater/index.tsx | 79 ++++++++++----- .../src/lib/messages/information.ts | 5 + .../src/fields/OperatorRepeater/index.tsx | 42 ++++++-- .../src/lib/messages/information.ts | 5 + .../src/utils/getApproveAnswers.ts | 2 +- .../BuyerItem.tsx | 0 .../src/fields/Buyer/index.tsx | 63 ++++++++++++ .../BuyerCoOwnerAndOperatorRepeater.css.ts} | 0 .../BuyerCoOwnerAndOperatorRepeaterItem.tsx} | 2 +- .../index.tsx | 98 ++++++------------- .../src/fields/index.ts | 3 +- .../InformationSection/buyerSubSection.ts | 9 +- 12 files changed, 204 insertions(+), 104 deletions(-) rename libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/{CoOwnerAndOperatorRepeater => Buyer}/BuyerItem.tsx (100%) create mode 100644 libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Buyer/index.tsx rename libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/{CoOwnerAndOperatorRepeater/CoOwnerAndOperatorRepeater.css.ts => BuyerCoOwnerAndOperatorRepeater/BuyerCoOwnerAndOperatorRepeater.css.ts} (100%) rename libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/{CoOwnerAndOperatorRepeater/CoOwnerAndOperatorRepeaterItem.tsx => BuyerCoOwnerAndOperatorRepeater/BuyerCoOwnerAndOperatorRepeaterItem.tsx} (98%) rename libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/{CoOwnerAndOperatorRepeater => BuyerCoOwnerAndOperatorRepeater}/index.tsx (61%) diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/CoOwnerRepeater/index.tsx b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/CoOwnerRepeater/index.tsx index 109a6bd97138..743efd1a7abd 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/CoOwnerRepeater/index.tsx +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/CoOwnerRepeater/index.tsx @@ -35,6 +35,9 @@ export const CoOwnerRepeater: FC> = ( ) as CoOwnersInformation[], ) const [identicalError, setIdenticalError] = useState(false) + const [noCoOwnerChangesError, setNoCoOwnerChangesError] = + useState(false) + const filteredCoOwners = coOwners.filter( ({ wasRemoved }) => wasRemoved !== 'true', ) @@ -42,25 +45,28 @@ export const CoOwnerRepeater: FC> = ( ({ wasRemoved }) => wasRemoved !== 'true', ) - const updateData = useCallback(async (position: number) => { - await updateApplication({ - variables: { - input: { - id: application.id, - answers: { - ownerCoOwners: ownerCoOwners.map((coOwner, index) => { - if (index === position) { - return { ...coOwner, wasRemoved: 'true' } - } else { - return coOwner - } - }), + const updateData = useCallback( + async (position: number) => { + await updateApplication({ + variables: { + input: { + id: application.id, + answers: { + ownerCoOwners: ownerCoOwners.map((coOwner, index) => { + if (index === position) { + return { ...coOwner, wasRemoved: 'true' } + } else { + return coOwner + } + }), + }, }, + locale, }, - locale, - }, - }) - }, []) + }) + }, + [application.id, locale, ownerCoOwners, updateApplication], + ) const addNationalIdToCoOwners = (nationalId: string, newIndex: number) => { setCoOwners( @@ -144,14 +150,29 @@ export const CoOwnerRepeater: FC> = ( } }, [coOwners, setValue]) - setBeforeSubmitCallback && - setBeforeSubmitCallback(async () => { - setIdenticalError(checkDuplicate()) - if (checkDuplicate()) { - return [false, 'Identical nationalIds'] - } - return [true, null] - }) + setBeforeSubmitCallback?.(async () => { + setIdenticalError(false) + setNoCoOwnerChangesError(false) + + const hasDuplicate = await checkDuplicate() + if (hasDuplicate) { + setIdenticalError(true) + return [false, 'Identical nationalIds'] + } + + const coOwnerWasAdded = + coOwners?.filter(({ wasRemoved }) => wasRemoved !== 'true').length > 0 + const coOwnerWasRemoved = !!ownerCoOwners?.find( + (x) => x.wasRemoved === 'true', + ) + const noCoOwnerChanges = !coOwnerWasAdded && !coOwnerWasRemoved + if (noCoOwnerChanges) { + setNoCoOwnerChangesError(true) + return [false, 'No co-owner has been added/removed'] + } + + return [true, null] + }) return ( @@ -206,6 +227,14 @@ export const CoOwnerRepeater: FC> = ( /> )} + {noCoOwnerChangesError && ( + + + + )} ) } diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/information.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/information.ts index f09661a73d91..59c21b22b038 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/information.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/information.ts @@ -175,6 +175,11 @@ export const information = { defaultMessage: 'Það má ekki nota sömu kennitölu tvisvar', description: 'coOwner identical error', }, + noChangesError: { + id: 'ta.ccov.application:information.labels.coOwner.noChangesError', + defaultMessage: 'Ekki er búið að gera neinar breytingar á meðeigendum', + description: 'coOwner no changes error', + }, }), vehicle: defineMessages({ sectionTitle: { diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/OperatorRepeater/index.tsx b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/OperatorRepeater/index.tsx index cf7756b69a5b..8154050934bf 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/OperatorRepeater/index.tsx +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/OperatorRepeater/index.tsx @@ -38,7 +38,10 @@ export const OperatorRepeater: FC> = ( [], ) as OldOperatorInformation[], ) + const [identicalError, setIdenticalError] = useState(false) + const [noOperatorChangesError, setNoOperatorChangesError] = + useState(false) const filteredOperators = operators.filter( ({ wasRemoved }) => wasRemoved !== 'true', @@ -212,14 +215,29 @@ export const OperatorRepeater: FC> = ( } }, [operators, setValue]) - setBeforeSubmitCallback && - setBeforeSubmitCallback(async () => { - setIdenticalError(checkDuplicate()) - if (checkDuplicate()) { - return [false, 'Identical nationalIds'] - } - return [true, null] - }) + setBeforeSubmitCallback?.(async () => { + setIdenticalError(false) + setNoOperatorChangesError(false) + + const hasDuplicate = await checkDuplicate() + if (hasDuplicate) { + setIdenticalError(true) + return [false, 'Identical nationalIds'] + } + + const operatorWasAdded = + operators?.filter(({ wasRemoved }) => wasRemoved !== 'true').length > 0 + const operatorWasRemoved = !!oldOperators?.find( + (x) => x.wasRemoved === 'true', + ) + const noOperatorChanges = !operatorWasAdded && !operatorWasRemoved + if (noOperatorChanges) { + setNoOperatorChangesError(true) + return [false, 'No operator has been added/removed'] + } + + return [true, null] + }) return ( @@ -288,6 +306,14 @@ export const OperatorRepeater: FC> = ( /> )} + {noOperatorChangesError && ( + + + + )} ) } diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/information.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/information.ts index aa99f73e9738..4469c5b14acb 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/information.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/information.ts @@ -217,6 +217,11 @@ export const information = { defaultMessage: 'Það má ekki nota sömu kennitölu tvisvar', description: 'operator identical error', }, + noChangesError: { + id: 'ta.cov.application:information.labels.operator.noChangesError', + defaultMessage: 'Ekki er búið að gera neinar breytingar á umráðamönnum', + description: 'operator no changes error', + }, }), mainOperator: defineMessages({ sectionTitle: { diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getApproveAnswers.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getApproveAnswers.ts index 0ab8a8e7d36c..eb262bf41054 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getApproveAnswers.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getApproveAnswers.ts @@ -17,7 +17,7 @@ export const getApproveAnswers = ( ) if (ownerCoOwner) { return { - ownerCoOwners: ownerCoOwners.map((ownerCoOwner) => { + ownerCoOwner: ownerCoOwners.map((ownerCoOwner) => { return { nationalId: ownerCoOwner.nationalId, name: ownerCoOwner.name, diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/BuyerItem.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Buyer/BuyerItem.tsx similarity index 100% rename from libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/BuyerItem.tsx rename to libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Buyer/BuyerItem.tsx diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Buyer/index.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Buyer/index.tsx new file mode 100644 index 000000000000..914a4e7f62c3 --- /dev/null +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Buyer/index.tsx @@ -0,0 +1,63 @@ +import { getValueViaPath } from '@island.is/application/core' +import { FieldBaseProps } from '@island.is/application/types' +import { Box } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { FC, useState, useCallback, useEffect } from 'react' +import { UserInformation } from '../../shared' +import { useMutation } from '@apollo/client' +import { UPDATE_APPLICATION } from '@island.is/application/graphql' +import { BuyerItem } from './BuyerItem' + +const emailRegex = + /^[\w!#$%&'*+/=?`{|}~^-]+(?:\.[\w!#$%&'*+/=?`{|}~^-]+)*@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$/i + +export const Buyer: FC> = (props) => { + const { locale } = useLocale() + const { application, field } = props + const { id } = field + + const [updateApplication] = useMutation(UPDATE_APPLICATION) + + const [buyer, setBuyer] = useState( + getValueViaPath(application.answers, 'buyer', { + name: '', + nationalId: '', + phone: '', + email: '', + }) as UserInformation, + ) + + const updateBuyer = useCallback( + async (buyer: UserInformation) => { + await updateApplication({ + variables: { + input: { + id: application.id, + answers: { + buyer: buyer, + }, + }, + locale, + }, + }) + }, + [application.id, locale, updateApplication], + ) + + useEffect(() => { + if ( + buyer.name.length > 0 && + buyer.nationalId.length === 10 && + buyer.phone.length >= 7 && + emailRegex.test(buyer.email) + ) { + updateBuyer(buyer) + } + }, [buyer, updateBuyer]) + + return ( + + + + ) +} diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/CoOwnerAndOperatorRepeater.css.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/BuyerCoOwnerAndOperatorRepeater.css.ts similarity index 100% rename from libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/CoOwnerAndOperatorRepeater.css.ts rename to libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/BuyerCoOwnerAndOperatorRepeater.css.ts diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/CoOwnerAndOperatorRepeaterItem.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/BuyerCoOwnerAndOperatorRepeaterItem.tsx similarity index 98% rename from libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/CoOwnerAndOperatorRepeaterItem.tsx rename to libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/BuyerCoOwnerAndOperatorRepeaterItem.tsx index 865335e36582..59e71a2136f7 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/CoOwnerAndOperatorRepeaterItem.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/BuyerCoOwnerAndOperatorRepeaterItem.tsx @@ -24,7 +24,7 @@ interface Props { addNationalIdToCoOwners: (nationalId: string, index: number) => void } -export const CoOwnerAndOperatorRepeaterItem: FC< +export const BuyerCoOwnerAndOperatorRepeaterItem: FC< React.PropsWithChildren > = ({ id, diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/index.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/index.tsx similarity index 61% rename from libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/index.tsx rename to libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/index.tsx index da30623f3486..5a29be5b7f7c 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwnerAndOperatorRepeater/index.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/BuyerCoOwnerAndOperatorRepeater/index.tsx @@ -2,32 +2,23 @@ import { getValueViaPath } from '@island.is/application/core' import { FieldBaseProps } from '@island.is/application/types' import { AlertMessage, Box, Button } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import { FC, useState, useCallback, useEffect } from 'react' +import { FC, useState } from 'react' import { information } from '../../lib/messages' -import { CoOwnerAndOperator, UserInformation } from '../../shared' -import { BuyerItem } from './BuyerItem' -import { repeaterButtons } from './CoOwnerAndOperatorRepeater.css' -import { CoOwnerAndOperatorRepeaterItem } from './CoOwnerAndOperatorRepeaterItem' -import { useMutation } from '@apollo/client' -import { UPDATE_APPLICATION } from '@island.is/application/graphql' +import { CoOwnerAndOperator } from '../../shared' +import { repeaterButtons } from './BuyerCoOwnerAndOperatorRepeater.css' +import { BuyerCoOwnerAndOperatorRepeaterItem } from './BuyerCoOwnerAndOperatorRepeaterItem' +import { useFormContext } from 'react-hook-form' -const emailRegex = - /^[\w!#$%&'*+/=?`{|}~^-]+(?:\.[\w!#$%&'*+/=?`{|}~^-]+)*@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$/i - -export const CoOwnerAndOperatorRepeater: FC< +export const BuyerCoOwnerAndOperatorRepeater: FC< React.PropsWithChildren > = (props) => { - const { locale, formatMessage } = useLocale() - const { application, setBeforeSubmitCallback } = props - const [updateApplication] = useMutation(UPDATE_APPLICATION) - const [buyer, setBuyer] = useState( - getValueViaPath(application.answers, 'buyer', { - name: '', - nationalId: '', - phone: '', - email: '', - }) as UserInformation, - ) + const { formatMessage } = useLocale() + const { application, field, setBeforeSubmitCallback } = props + const { id } = field + const { watch } = useFormContext() + + const [identicalError, setIdenticalError] = useState(false) + const [buyerCoOwnerAndOperator, setBuyerCoOwnerAndOperator] = useState< CoOwnerAndOperator[] >( @@ -37,7 +28,6 @@ export const CoOwnerAndOperatorRepeater: FC< [], ) as CoOwnerAndOperator[], ) - const [identicalError, setIdenticalError] = useState(false) const filteredCoOwnersAndOperators = buyerCoOwnerAndOperator.filter( ({ wasRemoved }) => wasRemoved !== 'true', @@ -49,42 +39,30 @@ export const CoOwnerAndOperatorRepeater: FC< (field) => field.type === 'coOwner', ) - const updateBuyer = useCallback(async (buyer: UserInformation) => { - await updateApplication({ - variables: { - input: { - id: application.id, - answers: { - buyer: buyer, - }, - }, - locale, - }, - }) - }, []) - const addNationalIdToCoOwners = (nationalId: string, newIndex: number) => { setBuyerCoOwnerAndOperator( - buyerCoOwnerAndOperator.map((coOwnerOroperator, index) => { + buyerCoOwnerAndOperator.map((coOwnerOrOperator, index) => { if (newIndex === index) { return { - ...coOwnerOroperator, + ...coOwnerOrOperator, nationalId, } } - return coOwnerOroperator + return coOwnerOrOperator }), ) } - const checkDuplicate = () => { + const checkDuplicate = async () => { const existingCoOwnersAndOperators = filteredCoOwnersAndOperators.map( ({ nationalId }) => { return nationalId }, ) - const jointOperators = [...existingCoOwnersAndOperators, buyer.nationalId] + const buyerNationalId = watch('buyer.nationalId') + + const jointOperators = [...existingCoOwnersAndOperators, buyerNationalId] return !!jointOperators.some((nationalId, index) => { return ( nationalId && @@ -110,47 +88,35 @@ export const CoOwnerAndOperatorRepeater: FC< const handleRemove = (position: number) => { if (position > -1) { setBuyerCoOwnerAndOperator( - buyerCoOwnerAndOperator.map((coOwnerOroperator, index) => { + buyerCoOwnerAndOperator.map((coOwnerOrOperator, index) => { if (index === position) { - return { ...coOwnerOroperator, wasRemoved: 'true' } + return { ...coOwnerOrOperator, wasRemoved: 'true' } } - return coOwnerOroperator + return coOwnerOrOperator }), ) } } - useEffect(() => { - if ( - buyer.name.length > 0 && - buyer.nationalId.length === 10 && - buyer.phone.length >= 7 && - emailRegex.test(buyer.email) - ) { - updateBuyer(buyer) + setBeforeSubmitCallback?.(async () => { + const hasDuplicate = await checkDuplicate() + setIdenticalError(hasDuplicate) + if (hasDuplicate) { + return [false, 'Identical nationalIds'] } - }, [buyer]) - - setBeforeSubmitCallback && - setBeforeSubmitCallback(async () => { - setIdenticalError(checkDuplicate()) - if (checkDuplicate()) { - return [false, 'Identical nationalIds'] - } - return [true, null] - }) + return [true, null] + }) return ( - {buyerCoOwnerAndOperator.map((field, index) => { const rowLocation = field.type === 'operator' ? allOperators.indexOf(field) : allCoOwners.indexOf(field) return ( -