From 6fe7af5784ca28315b63b53a200c397b7cde0e7a Mon Sep 17 00:00:00 2001 From: berglindoma13 Date: Mon, 9 Sep 2024 11:40:54 +0000 Subject: [PATCH] feat(id-card): updates to application (#15870) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new application setup * new application setup * continued work * stuff * working * State for parent B * adding overview section to review * confirmation, approved and rejected file * adding steps * AssignParentB api action * reject application email * adding submit application email * commenting out fjs code in service * adding chargefjsv2clientmodule to id card module file * adding rest of steps * merge conflicst * fixing merge issues * changes to reject conclusion screen * .. * accept reject buttons and modal * fixing conclusion screen * added disability check * dataschema update * klaraði bakendaföll eins og hægt er í bili * changes in prerequisites * changes to typeofidsubsection * chore: charts update dirty files * changes to condition description * finishing up * changing answers * small changes * stuff * payment fix * merging * finishing too many things for one commit * submit application * config update * updates * trying stuff * working on this * connection type to id call * working on this * rest of updates for applicatin * removed disability from dataprovider, not only with permission * removed space * payment codes * temp changes, todo finish * changes to condition for applying to id cards * finishind touches * small fix * typing iddocumenttype * fixing comments * fixing comments * small updates * fixes for build * fixes for build * small update to test * small update to test * small update to test * small update to test * small updates * updates to formatText, applicationCard and types * stuff * payment code changes * updates to aplication * updates to application * more updates * more updates * more updates * lokapunktar * cosnole log remove * more updates * more updates * updates after comment --------- Co-authored-by: Sigrún Tinna Gissurardóttir Co-authored-by: Sigrún Tinna Gissurardóttir Co-authored-by: andes-it Co-authored-by: Kristofer Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../national-registry.service.ts | 19 +++ .../modules/templates/id-card/constants.ts | 23 ---- .../templates/id-card/id-card.service.ts | 67 ++++++---- .../templates/id-card/project.json | 24 ++-- .../id-card/src/dataProviders/index.ts | 3 +- .../id-card/src/fields/ClearAnswers/index.tsx | 4 +- .../src/fields/RejectApproveButtons/index.tsx | 5 +- .../templates/id-card/src/forms/Approved.ts | 121 ++++++++++-------- .../IdCardForm/ApplicantInformation/index.ts | 28 ---- .../ChosenApplicantsSubSection.ts | 110 ++++++---------- .../IdInformation/ConditionInformation.ts | 19 +-- .../IdInformation/TypeOfIdSubSection.ts | 111 +++++++++------- .../src/forms/IdCardForm/PriceList/index.ts | 79 +++++++----- .../id-card/src/forms/IdCardForm/index.ts | 2 +- .../templates/id-card/src/forms/ParentB.ts | 61 +++++++++ .../id-card/src/forms/Prerequisites/index.ts | 2 + .../src/forms/Review/Overview/index.ts | 102 +++++++++------ .../src/forms/Review/State/StateParentB.ts | 70 ++++++++++ .../id-card/src/forms/Review/State/index.ts | 23 +--- .../id-card/src/lib/IdCardTemplate.ts | 68 ++++++++-- .../templates/id-card/src/lib/constants.ts | 67 ++++++---- .../templates/id-card/src/lib/dataSchema.ts | 6 +- .../id-card/src/lib/messages/application.ts | 4 +- .../id-card/src/lib/messages/confirmation.ts | 14 +- .../id-card/src/lib/messages/externalData.ts | 6 + .../id-card/src/lib/messages/idInformation.ts | 8 +- .../id-card/src/lib/messages/priceList.ts | 16 +-- .../id-card/src/lib/messages/state.ts | 6 +- .../templates/id-card/src/utils/formatText.ts | 8 -- .../id-card/src/utils/getChargeItemCodes.ts | 114 ++--------------- .../id-card/src/utils/getChosenApplicant.ts | 39 +++--- .../id-card/src/utils/getPriceList.ts | 34 +++++ .../id-card/src/utils/hasDiscount.ts | 5 + .../id-card/src/utils/hasReviewer.ts | 12 +- .../templates/id-card/src/utils/index.ts | 47 ++++++- .../src/utils/isAvailableForApplication.ts | 53 +++++--- .../id-card/src/utils/updateAnswers.ts | 6 +- libs/application/types/src/lib/Fields.ts | 18 ++- .../national-registry-user.ts | 1 + .../ActionCardListFormField.tsx | 40 +++++- libs/clients/passports/src/clientConfig.json | 12 ++ .../passports/src/lib/passportsApi.service.ts | 1 + .../passports/src/lib/passportsApi.types.ts | 6 + .../constants/src/lib/chargeItemCode.ts | 16 +-- 44 files changed, 888 insertions(+), 592 deletions(-) create mode 100644 libs/application/templates/id-card/src/forms/ParentB.ts create mode 100644 libs/application/templates/id-card/src/forms/Review/State/StateParentB.ts delete mode 100644 libs/application/templates/id-card/src/utils/formatText.ts create mode 100644 libs/application/templates/id-card/src/utils/getPriceList.ts diff --git a/libs/application/template-api-modules/src/lib/modules/shared/api/national-registry/national-registry.service.ts b/libs/application/template-api-modules/src/lib/modules/shared/api/national-registry/national-registry.service.ts index 1f861243d450..cb2b746c8b0d 100644 --- a/libs/application/template-api-modules/src/lib/modules/shared/api/national-registry/national-registry.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/shared/api/national-registry/national-registry.service.ts @@ -51,6 +51,25 @@ export class NationalRegistryService extends BaseTemplateApiService { await this.validateChildren(params, children) } + //allow parents whose children are icelandic citizenships in, but if no children, then check citizenship + if (params?.allowIfChildHasCitizenship) { + const children = await this.nationalRegistryApi.getCustodyChildren(auth) + if (children.length > 0) { + let foundChildWithIcelandicCitizenship = false + for (const child of children) { + const individual = await this.getIndividual(child) + if (individual?.citizenship?.code === 'IS') { + foundChildWithIcelandicCitizenship = true + break + } + } + //if child validates with icelandic citizenship, then do not check parents citizenship + if (foundChildWithIcelandicCitizenship) { + params = { ...params, icelandicCitizenship: false } + } + } + } + // Validate individual this.validateIndividual(individual, false, params) diff --git a/libs/application/template-api-modules/src/lib/modules/templates/id-card/constants.ts b/libs/application/template-api-modules/src/lib/modules/templates/id-card/constants.ts index a37f589a2429..3c4868e71220 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/id-card/constants.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/id-card/constants.ts @@ -12,26 +12,3 @@ export type DistrictCommissionerAgencies = { address: string key: string } - -export interface IdentityDocumentChild { - childNationalId: string - secondParent: string - secondParentName: string - childName: string - passports?: IdentityDocument[] -} - -export type IdentityDocument = { - number: string - type: string - verboseType: string - subType: string - status: string - issuingDate: string - expirationDate: string - displayFirstName: string - displayLastName: string - mrzFirstName: string - mrzLastName: string - sex: string -} diff --git a/libs/application/template-api-modules/src/lib/modules/templates/id-card/id-card.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/id-card/id-card.service.ts index 238827e8b161..daa1e7625091 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/id-card/id-card.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/id-card/id-card.service.ts @@ -4,16 +4,16 @@ import { LOGGER_PROVIDER } from '@island.is/logging' import { SharedTemplateApiService } from '../../shared' import { TemplateApiModuleActionProps } from '../../../types' import { coreErrorMessages, getValueViaPath } from '@island.is/application/core' -import { - DistrictCommissionerAgencies, - IdentityDocumentChild, -} from './constants' +import { DistrictCommissionerAgencies } from './constants' import { ChargeFjsV2ClientService, getPaymentIdFromExternalData, } from '@island.is/clients/charge-fjs-v2' import { generateAssignParentBApplicationEmail } from './emailGenerators/assignParentBEmail' -import { PassportsService } from '@island.is/clients/passports' +import { + IdentityDocumentChild, + PassportsService, +} from '@island.is/clients/passports' import { BaseTemplateApiService } from '../../base-template-api.service' import { ApplicationTypes, @@ -27,7 +27,7 @@ import { } from '@island.is/application/templates/id-card' import { generateApplicationRejectEmail } from './emailGenerators/rejectApplicationEmail' import { generateApplicationSubmittedEmail } from './emailGenerators/applicationSubmittedEmail' - +import { info } from 'kennitala' @Injectable() export class IdCardService extends BaseTemplateApiService { constructor( @@ -57,33 +57,44 @@ export class IdCardService extends BaseTemplateApiService { ) } - const expDate = identityDocument.userPassport?.expirationDate?.toString() // if applicant has valid id that is not withinExpirationDate, then not available for application, // otherwise available, either with no id or id within expiration limit // applicant can have a valid ID and apply for II - const applicantIdentityWithinLimits = expDate - ? isAvailableForApplication( - expDate, - 'ID', - `${identityDocument.userPassport?.type}${identityDocument.userPassport?.subType}`, - ) - : true + const applicantAge = info(auth.nationalId).age + const applicantInformation = { + age: applicantAge, + nationalId: auth.nationalId, + passport: identityDocument.userPassport, + children: identityDocument.childPassports, + } + const applicantIDWithinLimits = isAvailableForApplication( + 'ID', + applicantInformation, + ) + const applicantIIWithinLimits = isAvailableForApplication( + 'II', + applicantInformation, + ) let childIdentityWithinLimits = false identityDocument.childPassports?.map((child) => { if (child.passports && child.passports.length > 0) { child.passports.map((id) => { - const withinLimits = id.expirationDate - ? isAvailableForApplication( - id.expirationDate.toString(), - 'ID', - `${id.type}${id.subType}`, - ) - : true + if (child.childNationalId) { + const childInformation = { + age: info(child.childNationalId).age, + nationalId: child.childNationalId, + passport: child.passports?.[0], + } + const withinLimits = isAvailableForApplication( + 'ID', + childInformation, + ) - if (withinLimits) { - // if there is any id for any child that is within limits then user should be let through dataProvider - childIdentityWithinLimits = true + if (withinLimits) { + // if there is any id for any child that is within limits then user should be let through dataProvider + childIdentityWithinLimits = true + } } }) } else { @@ -91,7 +102,11 @@ export class IdCardService extends BaseTemplateApiService { } }) - if (!applicantIdentityWithinLimits && !childIdentityWithinLimits) { + if ( + !applicantIDWithinLimits && + !applicantIIWithinLimits && + !childIdentityWithinLimits + ) { throw new TemplateApiError( { title: coreErrorMessages.idCardApplicationRequirementsNotMet, @@ -278,7 +293,6 @@ export class IdCardService extends BaseTemplateApiService { guid: application.id, appliedForPersonId: auth.nationalId, priority: answers.priceList.priceChoice === Services.EXPRESS ? 1 : 0, - deliveryName: answers.priceList.location, contactInfo: { phoneAtHome: applicantInformation.phoneNumber, phoneAtWork: applicantInformation.phoneNumber, @@ -298,7 +312,6 @@ export class IdCardService extends BaseTemplateApiService { guid: application.id, appliedForPersonId: applicantInformation.nationalId, priority: answers.priceList.priceChoice === Services.EXPRESS ? 1 : 0, - deliveryName: answers.priceList.location, approvalA: { personId: firstGuardianInformation?.nationalId?.replace('-', '') || '', diff --git a/libs/application/templates/id-card/project.json b/libs/application/templates/id-card/project.json index eb0e10f3fd2f..7079ae061564 100644 --- a/libs/application/templates/id-card/project.json +++ b/libs/application/templates/id-card/project.json @@ -20,18 +20,18 @@ "options": { "jestConfig": "libs/application/templates/id-card/jest.config.ts", "passWithNoTests": true - }, - "extract-strings": { - "executor": "nx:run-commands", - "options": { - "command": "yarn ts-node -P libs/localization/tsconfig.lib.json libs/localization/scripts/extract 'libs/application/templates/id-card/src/**/*.{js,ts,tsx}'" - } - }, - "configurations": { - "ci": { - "ci": true, - "codeCoverage": true - } + } + }, + "extract-strings": { + "executor": "nx:run-commands", + "options": { + "command": "yarn ts-node -P libs/localization/tsconfig.lib.json libs/localization/scripts/extract 'libs/application/templates/id-card/src/**/*.{js,ts,tsx}'" + } + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true } } } diff --git a/libs/application/templates/id-card/src/dataProviders/index.ts b/libs/application/templates/id-card/src/dataProviders/index.ts index 8af9d63fe37a..c2417a209cd8 100644 --- a/libs/application/templates/id-card/src/dataProviders/index.ts +++ b/libs/application/templates/id-card/src/dataProviders/index.ts @@ -33,7 +33,7 @@ const defaultParams = { summary: error.invalidAgeDescription, }, icelandicCitizenship: true, - allowPassOnChild: true, + allowIfChildHasCitizenship: true, } export const NationalRegistryUser = NationalRegistryUserApi.configure({ @@ -45,6 +45,7 @@ export const NationalRegistryUserParentB = NationalRegistryUserApi.configure({ ...defaultParams, icelandicCitizenship: false, }, + externalDataId: 'nationalRegistryParentB', }) export const SyslumadurPaymentCatalogApi = PaymentCatalogApi.configure({ diff --git a/libs/application/templates/id-card/src/fields/ClearAnswers/index.tsx b/libs/application/templates/id-card/src/fields/ClearAnswers/index.tsx index 927ad8a65644..a5c057b5ee29 100644 --- a/libs/application/templates/id-card/src/fields/ClearAnswers/index.tsx +++ b/libs/application/templates/id-card/src/fields/ClearAnswers/index.tsx @@ -18,13 +18,13 @@ export const ClearAnswers: FC> = ({ setBeforeSubmitCallback && setBeforeSubmitCallback(async () => { const chosenApplicants = getValues(Routes.CHOSENAPPLICANTS) - + const newAnswers = updateAnswers(application, chosenApplicants, setValue) try { await updateApplication({ variables: { input: { id: application.id, - answers: updateAnswers(application, chosenApplicants, setValue), + answers: newAnswers, }, locale, }, diff --git a/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx b/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx index 4217888de436..1d3a19a11e16 100644 --- a/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx +++ b/libs/application/templates/id-card/src/fields/RejectApproveButtons/index.tsx @@ -51,7 +51,10 @@ export const RejectApproveButtons: FC< input: { id: application.id, event: DefaultEvents.SUBMIT, - answers: {}, + answers: { + ...application.answers, + 'secondGuardianInformation.approved': true, + }, }, }, }) diff --git a/libs/application/templates/id-card/src/forms/Approved.ts b/libs/application/templates/id-card/src/forms/Approved.ts index c7d1b9974497..0f6d01423b21 100644 --- a/libs/application/templates/id-card/src/forms/Approved.ts +++ b/libs/application/templates/id-card/src/forms/Approved.ts @@ -1,16 +1,14 @@ import { buildForm, - buildSection, - buildMultiField, buildAlertMessageField, - buildMessageWithLinkButtonField, - buildExpandableDescriptionField, - coreMessages, + getValueViaPath, } from '@island.is/application/core' import { Form, FormModes } from '@island.is/application/types' // import { Logo } from '../../assets/Logo' import { buildFormConclusionSection } from '@island.is/application/ui-forms' import { reviewConfirmation } from '../lib/messages' +import { getChosenApplicant, hasSecondGuardian } from '../utils' +import { IdentityDocumentChild } from '@island.is/clients/passports' export const Approved: Form = buildForm({ id: 'Approved', @@ -18,51 +16,6 @@ export const Approved: Form = buildForm({ // logo: Logo, mode: FormModes.APPROVED, children: [ - buildSection({ - id: 'uiForms.conclusionSection', - title: reviewConfirmation.general.sectionTitle, - children: [ - buildMultiField({ - id: 'uiForms.conclusionMultifield', - title: reviewConfirmation.general.sectionTitle, - children: [ - buildAlertMessageField({ - id: 'uiForms.conclusionAlert', - title: reviewConfirmation.general.alertTitle, - alertType: 'success', - }), - buildExpandableDescriptionField({ - id: 'uiForms.conclusionExpandableDescription', - title: reviewConfirmation.general.accordionTitle, - introText: '', - description: reviewConfirmation.general.accordionText, - startExpanded: true, - }), - buildAlertMessageField({ - id: 'uiForms.conclusionAlertInfo1', - title: '', - alertType: 'info', - message: reviewConfirmation.general.infoMessageText2, - marginBottom: 0, - }), - buildAlertMessageField({ - id: 'uiForms.conclusionAlertInfo2', - title: '', - alertType: 'info', - message: reviewConfirmation.general.infoMessageText2, - }), - buildMessageWithLinkButtonField({ - id: 'uiForms.conclusionBottomLink', - title: '', - url: '/minarsidur/umsoknir', - buttonTitle: coreMessages.openServicePortalButtonTitle, - message: reviewConfirmation.general.bottomButtonMessage, - marginBottom: [4, 4, 12], - }), - ], - }), - ], - }), buildFormConclusionSection({ sectionTitle: reviewConfirmation.general.sectionTitle, multiFieldTitle: reviewConfirmation.general.sectionTitle, @@ -70,8 +23,74 @@ export const Approved: Form = buildForm({ alertMessage: '', expandableHeader: reviewConfirmation.general.accordionTitle, expandableIntro: '', - expandableDescription: reviewConfirmation.general.accordionText, + expandableDescription: (application) => { + const applicantNationalId = getValueViaPath( + application.answers, + 'chosenApplicants', + '', + ) as string + const chosenApplicant = getChosenApplicant( + application.answers, + application.externalData, + applicantNationalId, + ) + + return !chosenApplicant.isApplicant + ? reviewConfirmation.general.accordionTextForChild + : reviewConfirmation.general.accordionText + }, bottomButtonMessage: reviewConfirmation.general.bottomButtonMessage, }), + buildAlertMessageField({ + id: 'uiForms.conclusionAlertInfo1', + title: '', + alertType: 'info', + message: reviewConfirmation.general.infoMessageText1, + marginBottom: 0, + condition: (formValue, externalData) => { + const applicantNationalId = getValueViaPath( + formValue, + 'chosenApplicants', + '', + ) as string + const chosenApplicant = getChosenApplicant( + formValue, + externalData, + applicantNationalId, + ) + + const applicantHasSecondGuardian = hasSecondGuardian( + formValue, + externalData, + ) + + return !chosenApplicant.isApplicant && applicantHasSecondGuardian + }, + }), + buildAlertMessageField({ + id: 'uiForms.conclusionAlertInfo2', + title: '', + alertType: 'info', + message: reviewConfirmation.general.infoMessageText2, + condition: (formValue, externalData) => { + const applicantNationalId = getValueViaPath( + formValue, + 'chosenApplicants', + '', + ) as string + const chosenApplicant = getChosenApplicant( + formValue, + externalData, + applicantNationalId, + ) + + const applicantHasSecondGuardian = hasSecondGuardian( + formValue, + externalData, + ) + + return !chosenApplicant.isApplicant && applicantHasSecondGuardian + }, + }), ], }) diff --git a/libs/application/templates/id-card/src/forms/IdCardForm/ApplicantInformation/index.ts b/libs/application/templates/id-card/src/forms/IdCardForm/ApplicantInformation/index.ts index a2c64e1e4d42..027d4df5a249 100644 --- a/libs/application/templates/id-card/src/forms/IdCardForm/ApplicantInformation/index.ts +++ b/libs/application/templates/id-card/src/forms/IdCardForm/ApplicantInformation/index.ts @@ -50,11 +50,6 @@ export const ApplicanInformationSubSection = buildSection({ title: applicantInformation.labels.applicantName, readOnly: true, width: 'half', - defaultValue: (application: Application) => { - const chosenApplicant = getChosenApplicant(application) - - return chosenApplicant.name - }, }), buildTextField({ id: `${Routes.APPLICANTSINFORMATION}.nationalId`, @@ -62,11 +57,6 @@ export const ApplicanInformationSubSection = buildSection({ readOnly: true, width: 'half', format: '######-####', - defaultValue: (application: Application) => { - const chosenApplicant = getChosenApplicant(application) - - return chosenApplicant.nationalId - }, }), buildTextField({ id: `${Routes.APPLICANTSINFORMATION}.email`, @@ -75,15 +65,6 @@ export const ApplicanInformationSubSection = buildSection({ condition: (answers, externalData) => { return !isChild(answers, externalData) }, - defaultValue: (application: Application) => { - const applicantUserProfile = getValueViaPath( - application.externalData, - 'userProfile.data', - undefined, - ) as UserProfile | undefined - - return applicantUserProfile?.email - }, }), buildPhoneField({ id: `${Routes.APPLICANTSINFORMATION}.phoneNumber`, @@ -92,15 +73,6 @@ export const ApplicanInformationSubSection = buildSection({ condition: (answers, externalData) => { return !isChild(answers, externalData) }, - defaultValue: (application: Application) => { - const applicantUserProfile = getValueViaPath( - application.externalData, - 'userProfile.data', - undefined, - ) as UserProfile | undefined - - return applicantUserProfile?.mobilePhoneNumber - }, }), /*** FIRST GUARDIAN ***/ diff --git a/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ChosenApplicantsSubSection.ts b/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ChosenApplicantsSubSection.ts index 0593e95d4b83..65f033a20439 100644 --- a/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ChosenApplicantsSubSection.ts +++ b/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ChosenApplicantsSubSection.ts @@ -2,16 +2,14 @@ import { buildMultiField, buildSubSection, buildRadioField, - getValueViaPath, buildCustomField, } from '@island.is/application/core' -import { - IdentityDocument, - IdentityDocumentChild, - Routes, -} from '../../../lib/constants' +import { IdentityDocument, Routes } from '../../../lib/constants' import { idInformation } from '../../../lib/messages/idInformation' -import { isAvailableForApplication } from '../../../utils' +import { + getCombinedApplicantInformation, + isAvailableForApplication, +} from '../../../utils' import { formatDate } from '../../../utils/formatDate' export const ChosenApplicantsSubSection = buildSubSection({ @@ -27,88 +25,61 @@ export const ChosenApplicantsSubSection = buildSubSection({ id: Routes.CHOSENAPPLICANTS, title: '', largeButtons: true, + required: true, options: (application) => { - const applicantName = getValueViaPath( - application.externalData, - 'nationalRegistry.data.fullName', - '', - ) as string - - const applicantNationalId = getValueViaPath( + const applicantInformation = getCombinedApplicantInformation( application.externalData, - 'nationalRegistry.data.nationalId', - '', - ) as string + ) - const applicantPassport = getValueViaPath( - application.externalData, - 'identityDocument.data.userPassport', - undefined, - ) as IdentityDocument | undefined + const applicantIIDisabled = !isAvailableForApplication( + 'II', + applicantInformation, + ) - const applicantChildren = getValueViaPath( - application.externalData, - 'identityDocument.data.childPassports', - [], - ) as Array - - const applicantIIDisabled = applicantPassport - ? !isAvailableForApplication( - applicantPassport.expirationDate, - 'II', - `${applicantPassport.type}${applicantPassport.subType}`, - ) - : false - - const applicantIDDisabled = applicantPassport - ? !isAvailableForApplication( - applicantPassport.expirationDate, - 'ID', - `${applicantPassport.type}${applicantPassport.subType}`, - ) - : false + const applicantIDDisabled = !isAvailableForApplication( + 'ID', + applicantInformation, + ) const passportList: Array = [ { - label: applicantName, - subLabel: applicantPassport + label: applicantInformation.name, + subLabel: applicantInformation.passport ? { ...idInformation.labels.idNumber, values: { - passportNumber: applicantPassport?.number, - expirationDate: formatDate( - new Date(applicantPassport.expirationDate), - ), + passportNumber: applicantInformation.passport?.number, + expirationDate: + applicantInformation.passport.expirationDate && + formatDate( + new Date( + applicantInformation.passport.expirationDate, + ), + ), }, } : { ...idInformation.labels.noIdNumber, }, - value: applicantNationalId, + value: applicantInformation.nationalId, disabled: applicantIIDisabled && applicantIDDisabled, }, ] - applicantChildren.map((item) => { + applicantInformation.children.map((item) => { + const isDisabledDueToCitizenship = item.citizenship?.kodi !== 'IS' const idDocument = item.passports && item.passports.length > 0 ? (item.passports[0] as IdentityDocument) : undefined - const IIDisabled = idDocument - ? !isAvailableForApplication( - idDocument.expirationDate, - 'II', - `${idDocument.type}${idDocument.subType}`, - ) - : false - const IDDisabled = idDocument - ? !isAvailableForApplication( - idDocument.expirationDate, - 'ID', - `${idDocument.type}${idDocument.subType}`, - ) - : false + const IIDisabled = !isAvailableForApplication('II', { + passport: idDocument, + }) + + const IDDisabled = !isAvailableForApplication('ID', { + passport: idDocument, + }) return passportList.push({ label: item.childName, subLabel: idDocument @@ -116,9 +87,9 @@ export const ChosenApplicantsSubSection = buildSubSection({ ...idInformation.labels.idNumber, values: { passportNumber: idDocument.number, - expirationDate: formatDate( - new Date(idDocument.expirationDate), - ), + expirationDate: + idDocument.expirationDate && + formatDate(new Date(idDocument.expirationDate)), }, } : { @@ -126,7 +97,8 @@ export const ChosenApplicantsSubSection = buildSubSection({ }, value: item.childNationalId, - disabled: IIDisabled && IDDisabled, + disabled: + (IIDisabled && IDDisabled) || isDisabledDueToCitizenship, }) }) diff --git a/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ConditionInformation.ts b/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ConditionInformation.ts index e82531b40e06..fc1297f675e7 100644 --- a/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ConditionInformation.ts +++ b/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/ConditionInformation.ts @@ -1,28 +1,19 @@ import { buildSubSection, - getValueViaPath, - buildMultiField, buildDescriptionField, } from '@island.is/application/core' import { Routes } from '../../../lib/constants' import { idInformation } from '../../../lib/messages/idInformation' +import { getChosenApplicant, hasReviewer } from '../../../utils' export const ConditionInformationSection = buildSubSection({ id: Routes.CONDITIONINFORMATION, title: idInformation.general.conditionSectionTitle, - condition: (formvalue, externalData) => { - const chosenApplicantNationalId = getValueViaPath( - formvalue, - Routes.CHOSENAPPLICANTS, - '', - ) as string + condition: (formValue, externalData) => { + const chosenApplicant = getChosenApplicant(formValue, externalData) + const applicantHasReviewer = hasReviewer(formValue, externalData) - const applicantNationalId = getValueViaPath( - externalData, - 'nationalRegistry.data.nationalId', - '', - ) as string - return chosenApplicantNationalId !== applicantNationalId + return !chosenApplicant.isApplicant && applicantHasReviewer }, children: [ buildDescriptionField({ diff --git a/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/TypeOfIdSubSection.ts b/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/TypeOfIdSubSection.ts index a031ead21e05..d6a0ad0ec889 100644 --- a/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/TypeOfIdSubSection.ts +++ b/libs/application/templates/id-card/src/forms/IdCardForm/IdInformation/TypeOfIdSubSection.ts @@ -5,14 +5,13 @@ import { buildAlertMessageField, getValueViaPath, } from '@island.is/application/core' -import { - IdentityDocument, - IdentityDocumentChild, - Routes, -} from '../../../lib/constants' +import { IdentityDocumentChild, Routes } from '../../../lib/constants' import { idInformation } from '../../../lib/messages/idInformation' -import { getChosenApplicant, isAvailableForApplication } from '../../../utils' -import { NationalRegistryIndividual } from '@island.is/application/types' +import { + getChosenApplicant, + getCombinedApplicantInformation, + isAvailableForApplication, +} from '../../../utils' export const TypeOfIdSubSection = buildSubSection({ id: Routes.TYPEOFID, @@ -29,67 +28,81 @@ export const TypeOfIdSubSection = buildSubSection({ width: 'half', required: true, options: (application) => { - const applicantNationalRegistry = getValueViaPath( + const combinedAppplicantInformation = + getCombinedApplicantInformation(application.externalData) + const chosenApplicant = getChosenApplicant( + application.answers, application.externalData, - 'nationalRegistry.data', - {}, - ) as NationalRegistryIndividual - const chosenApplicant = getChosenApplicant(application) - let applicantPassport: IdentityDocument | undefined - if ( - chosenApplicant.nationalId === - applicantNationalRegistry.nationalId - ) { - applicantPassport = getValueViaPath( - application.externalData, - 'identityDocument.data.userPassport', - undefined, - ) as IdentityDocument | undefined - } else { + ) + + if (!chosenApplicant.isApplicant) { const childPassports = getValueViaPath( application.externalData, 'identityDocument.data.childPassports', undefined, ) as Array | undefined - applicantPassport = - childPassports && - childPassports.find( - (x) => x.childNationalId === chosenApplicant.nationalId, - )?.passports?.[0] + combinedAppplicantInformation.passport = childPassports?.find( + (x) => x.childNationalId === chosenApplicant.nationalId, + )?.passports?.[0] } - const IIDisabled = applicantPassport - ? !isAvailableForApplication( - applicantPassport.expirationDate, - 'II', - `${applicantPassport.type}${applicantPassport.subType}`, - applicantNationalRegistry.age, - ) - : false - const IDDisabled = applicantPassport - ? !isAvailableForApplication( - applicantPassport.expirationDate, - 'ID', - `${applicantPassport.type}${applicantPassport.subType}`, - applicantNationalRegistry.age, - ) - : false + const IIDisabled = !isAvailableForApplication( + 'II', + combinedAppplicantInformation, + ) + + const IDDisabled = !isAvailableForApplication( + 'ID', + combinedAppplicantInformation, + ) return [ //II = Nafnskírteini ekki sem ferðaskilríki, ID = Nafnskírteini sem ferðaskilríki - { - label: idInformation.labels.typeOfIdRadioAnswerOne, - value: 'II', - disabled: IIDisabled, - }, { label: idInformation.labels.typeOfIdRadioAnswerTwo, value: 'ID', disabled: IDDisabled, }, + { + label: idInformation.labels.typeOfIdRadioAnswerOne, + value: 'II', + disabled: IIDisabled, + }, ] }, }), + buildAlertMessageField({ + id: `${Routes.TYPEOFID}.alertField`, + title: '', + alertType: 'warning', + message: idInformation.labels.warningText, + condition: (answers, externalData) => { + const combinedAppplicantInformation = + getCombinedApplicantInformation(externalData) + const chosenApplicant = getChosenApplicant(answers, externalData) + if (!chosenApplicant.isApplicant) { + const childPassports = getValueViaPath( + externalData, + 'identityDocument.data.childPassports', + undefined, + ) as Array | undefined + + combinedAppplicantInformation.passport = childPassports?.find( + (x) => x.childNationalId === chosenApplicant.nationalId, + )?.passports?.[0] + } + const IIDisabled = !isAvailableForApplication( + 'II', + combinedAppplicantInformation, + ) + + const IDDisabled = !isAvailableForApplication( + 'ID', + combinedAppplicantInformation, + ) + return IIDisabled || IDDisabled + }, + }), buildAlertMessageField({ id: `${Routes.TYPEOFID}.alertField`, title: '', diff --git a/libs/application/templates/id-card/src/forms/IdCardForm/PriceList/index.ts b/libs/application/templates/id-card/src/forms/IdCardForm/PriceList/index.ts index 65d147680fb7..bf877be3dc6c 100644 --- a/libs/application/templates/id-card/src/forms/IdCardForm/PriceList/index.ts +++ b/libs/application/templates/id-card/src/forms/IdCardForm/PriceList/index.ts @@ -5,14 +5,11 @@ import { buildRadioField, buildSelectField, } from '@island.is/application/core' -import { - DistrictCommissionerAgencies, - Routes, - Services, -} from '../../../lib/constants' +import { DistrictCommissionerAgencies, Routes } from '../../../lib/constants' import { priceList } from '../../../lib/messages/priceList' -import { checkForDiscount } from '../../../utils' +import { checkForDiscount, formatIsk, getPriceList } from '../../../utils' import { Application } from '@island.is/application/types' +import { Services } from '../../../shared/types' export const PriceListSubSection = buildSection({ id: Routes.PRICELIST, @@ -29,49 +26,61 @@ export const PriceListSubSection = buildSection({ width: 'half', options: (application: Application) => { const hasDiscount = checkForDiscount(application) + + const applicationPrices = getPriceList(application) + return [ { label: !hasDiscount - ? priceList.labels.regularPriceTitle - : priceList.labels.discountRegularPriceTitle, + ? { + id: priceList.labels.regularPriceTitle.id, + values: { + price: + applicationPrices.regularPrice?.priceAmount && + formatIsk( + applicationPrices.regularPrice?.priceAmount, + ), + }, + } + : { + id: priceList.labels.discountRegularPriceTitle.id, + values: { + price: + applicationPrices.regularDiscountPrice?.priceAmount && + formatIsk( + applicationPrices.regularDiscountPrice?.priceAmount, + ), + }, + }, subLabel: priceList.labels.regularPriceDescription, value: Services.REGULAR, }, { label: !hasDiscount - ? priceList.labels.fastPriceTitle - : priceList.labels.discountFastPriceTitle, + ? { + id: priceList.labels.fastPriceTitle.id, + values: { + price: + applicationPrices.fastPrice?.priceAmount && + formatIsk(applicationPrices.fastPrice?.priceAmount), + }, + } + : { + id: priceList.labels.discountFastPriceTitle.id, + values: { + price: + applicationPrices.fastDiscountPrice?.priceAmount && + formatIsk( + applicationPrices.fastDiscountPrice?.priceAmount, + ), + }, + }, subLabel: priceList.labels.fastPriceDescription, value: Services.EXPRESS, }, ] }, }), - buildDescriptionField({ - id: `${Routes.PRICELIST}.locationTitle`, - title: priceList.labels.locationTitle, - description: priceList.labels.locationDescription, - titleVariant: 'h3', - marginBottom: 'gutter', - marginTop: 'gutter', - }), - buildSelectField({ - id: `${Routes.PRICELIST}.location`, - title: priceList.labels.locationTitle, - placeholder: priceList.labels.locationPlaceholder, - options: ({ - externalData: { - deliveryAddress: { data }, - }, - }) => { - return (data as DistrictCommissionerAgencies[])?.map( - ({ key, name }) => ({ - value: key, - label: name, - }), - ) - }, - }), ], }), ], diff --git a/libs/application/templates/id-card/src/forms/IdCardForm/index.ts b/libs/application/templates/id-card/src/forms/IdCardForm/index.ts index ddae49d78774..31e64fbb55b0 100644 --- a/libs/application/templates/id-card/src/forms/IdCardForm/index.ts +++ b/libs/application/templates/id-card/src/forms/IdCardForm/index.ts @@ -10,7 +10,7 @@ import { getChargeItemCodes } from '../../utils' // import { Logo } from '../../assets/Logo' export const IdCardForm: Form = buildForm({ - id: 'UniversityFormDraft', + id: 'IdCardFormDraft', title: '', // logo: Logo, mode: FormModes.DRAFT, diff --git a/libs/application/templates/id-card/src/forms/ParentB.ts b/libs/application/templates/id-card/src/forms/ParentB.ts new file mode 100644 index 000000000000..37012d385f1e --- /dev/null +++ b/libs/application/templates/id-card/src/forms/ParentB.ts @@ -0,0 +1,61 @@ +import { + buildDataProviderItem, + buildDescriptionField, + buildExternalDataProvider, + buildForm, + buildMultiField, + buildSection, +} from '@island.is/application/core' +import { Form, FormModes } from '@island.is/application/types' +import { NationalRegistryUserParentB } from '../dataProviders' +import { externalData } from '../lib/messages' +import { StateParentBSection } from './Review/State/StateParentB' +import { OverviewSection } from './Review/Overview' + +export const ParentB: Form = buildForm({ + id: 'IdCardApplicationParentB', + title: '', + mode: FormModes.IN_PROGRESS, + renderLastScreenButton: true, + renderLastScreenBackButton: true, + children: [ + buildSection({ + id: 'preInformation', + title: externalData.preInformation.sectionTitle, + children: [ + buildMultiField({ + id: 'preInformation.multifield', + title: externalData.preInformation.title, + children: [ + buildDescriptionField({ + id: 'preInformation.parentB', + title: '', + description: externalData.preInformation.parentBIntroText, + }), + ], + }), + ], + }), + buildSection({ + id: 'externalDataSection', + title: externalData.dataProvider.sectionTitle, + children: [ + buildExternalDataProvider({ + id: 'approveExternalDataParentB', + title: externalData.dataProvider.pageTitle, + subTitle: externalData.dataProvider.subTitle, + checkboxLabel: externalData.dataProvider.checkboxLabel, + dataProviders: [ + buildDataProviderItem({ + provider: NationalRegistryUserParentB, + title: externalData.nationalRegistry.title, + subTitle: externalData.nationalRegistry.subTitle, + }), + ], + }), + ], + }), + StateParentBSection, + OverviewSection, + ], +}) diff --git a/libs/application/templates/id-card/src/forms/Prerequisites/index.ts b/libs/application/templates/id-card/src/forms/Prerequisites/index.ts index fefe4e6d0573..f9bfb2f5535a 100644 --- a/libs/application/templates/id-card/src/forms/Prerequisites/index.ts +++ b/libs/application/templates/id-card/src/forms/Prerequisites/index.ts @@ -40,6 +40,7 @@ export const Prerequisites: Form = buildForm({ buildSection({ id: 'preInformation', title: externalData.preInformation.sectionTitle, + children: [ buildMultiField({ id: 'preInformation.multifield', @@ -73,6 +74,7 @@ export const Prerequisites: Form = buildForm({ title: externalData.dataProvider.pageTitle, subTitle: externalData.dataProvider.subTitle, checkboxLabel: externalData.dataProvider.checkboxLabel, + enableMockPayment: true, submitField: buildSubmitField({ id: 'submit', placement: 'footer', diff --git a/libs/application/templates/id-card/src/forms/Review/Overview/index.ts b/libs/application/templates/id-card/src/forms/Review/Overview/index.ts index 4684ca5da7ed..b9749c2e585e 100644 --- a/libs/application/templates/id-card/src/forms/Review/Overview/index.ts +++ b/libs/application/templates/id-card/src/forms/Review/Overview/index.ts @@ -8,13 +8,17 @@ import { buildCustomField, } from '@island.is/application/core' import { format as formatNationalId } from 'kennitala' -import { - DistrictCommissionerAgencies, - Routes, - Services, -} from '../../../lib/constants' +import { DistrictCommissionerAgencies, Routes } from '../../../lib/constants' import { review, idInformation, priceList } from '../../../lib/messages' -import { isChild, formatPhoneNumber, hasSecondGuardian } from '../../../utils' +import { + isChild, + formatPhoneNumber, + hasSecondGuardian, + checkForDiscount, + getPriceList, + formatIsk, +} from '../../../utils' +import { Services } from '../../../shared/types' export const OverviewSection = buildSection({ id: 'reviewOverview', @@ -48,8 +52,7 @@ export const OverviewSection = buildSection({ colSpan: '6/12', paddingBottom: 3, value: ({ answers }) => - (getValueViaPath(answers, 'typeOfId', '') as string) === - 'WithTravel' + (getValueViaPath(answers, 'typeOfId', '') as string) === 'II' ? idInformation.labels.typeOfIdRadioAnswerOne : idInformation.labels.typeOfIdRadioAnswerTwo, }), @@ -225,46 +228,63 @@ export const OverviewSection = buildSection({ buildKeyValueField({ label: review.labels.deliveryOption, colSpan: '6/12', - value: ({ answers }) => { + value: (application) => { + const { answers } = application const priceChoice = getValueViaPath( answers, `${Routes.PRICELIST}.priceChoice`, ) as string - // TODO: Add priceAmount when we have all chargeItemCodes! - // And change the priceList messages - return priceChoice === Services.EXPRESS - ? [priceList.labels.fastPriceTitle, '**4.600 kr.**'] - : priceChoice === Services.EXPRESS_DISCOUNT - ? [priceList.labels.discountFastPriceTitle, '**4.600 kr.**'] - : priceChoice === Services.REGULAR - ? [priceList.labels.regularPriceTitle, '**4.600 kr.**'] - : priceChoice === Services.REGULAR_DISCOUNT - ? [priceList.labels.discountRegularPriceTitle, '**4.600 kr.**'] + const hasDiscount = checkForDiscount(application) + const applicationPrices = getPriceList(application) + return priceChoice === Services.EXPRESS && !hasDiscount + ? { + id: priceList.labels.fastPriceTitle.id, + values: { + price: + applicationPrices.fastPrice?.priceAmount && + formatIsk(applicationPrices.fastPrice?.priceAmount), + }, + } + : priceChoice === Services.EXPRESS && hasDiscount + ? [ + { + id: priceList.labels.discountFastPriceTitle, + values: { + price: + applicationPrices.fastDiscountPrice?.priceAmount && + formatIsk( + applicationPrices.fastDiscountPrice?.priceAmount, + ), + }, + }, + ] + : priceChoice === Services.REGULAR && !hasDiscount + ? [ + { + id: priceList.labels.regularPriceTitle.id, + values: { + price: + applicationPrices.regularPrice?.priceAmount && + formatIsk(applicationPrices.regularPrice?.priceAmount), + }, + }, + ] + : priceChoice === Services.REGULAR && hasDiscount + ? [ + { + id: priceList.labels.discountRegularPriceTitle.id, + values: { + price: + applicationPrices.regularDiscountPrice?.priceAmount && + formatIsk( + applicationPrices.regularDiscountPrice?.priceAmount, + ), + }, + }, + ] : '' }, }), - buildKeyValueField({ - label: review.labels.deliveryLocation, - colSpan: '6/12', - value: ({ - answers, - externalData: { - deliveryAddress: { data }, - }, - }) => { - const deliveryAddress = ( - data as DistrictCommissionerAgencies[] - )?.find( - ({ key }) => - key === - (getValueViaPath( - answers, - `${Routes.PRICELIST}.location`, - ) as string), - ) - return `${deliveryAddress?.name} - ${deliveryAddress?.street}, ${deliveryAddress?.zip} ${deliveryAddress?.city}` - }, - }), /* SUBMIT OR DECLINE */ buildCustomField({ diff --git a/libs/application/templates/id-card/src/forms/Review/State/StateParentB.ts b/libs/application/templates/id-card/src/forms/Review/State/StateParentB.ts new file mode 100644 index 000000000000..7d3e7216408d --- /dev/null +++ b/libs/application/templates/id-card/src/forms/Review/State/StateParentB.ts @@ -0,0 +1,70 @@ +import { + buildActionCardListField, + buildMultiField, + buildSection, + getValueViaPath, + buildAlertMessageField, +} from '@island.is/application/core' +import { Routes } from '../../../lib/constants' +import { state } from '../../../lib/messages' + +export const StateParentBSection = buildSection({ + id: 'reviewStateParentB', + title: state.general.sectionTitle, + children: [ + buildMultiField({ + id: `reviewStateMultiField`, + title: state.general.pageTitle, + description: ({ answers }) => ({ + ...state.general.description, + values: { + guardianName: getValueViaPath( + answers, + `${Routes.FIRSTGUARDIANINFORMATION}.name`, + '', + ) as string, + childName: getValueViaPath( + answers, + `${Routes.APPLICANTSINFORMATION}.name`, + '', + ) as string, + }, + }), + children: [ + buildAlertMessageField({ + id: 'stateAlertMessage', + title: '', + message: state.labels.alertMessage, + alertType: 'info', + }), + buildActionCardListField({ + id: 'approvalActionCard', + doesNotRequireAnswer: true, + marginTop: 2, + title: '', + items: (application) => { + const chosenApplicantName = getValueViaPath( + application.answers, + `${Routes.APPLICANTSINFORMATION}.name`, + '', + ) as string + return [ + { + heading: state.labels.actionCardTitle, + tag: { + label: state.labels.actionCardTag, + outlined: false, + variant: 'purple', + }, + text: { + id: state.labels.actionCardDescription.id, + values: { name: chosenApplicantName }, + }, + }, + ] + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/id-card/src/forms/Review/State/index.ts b/libs/application/templates/id-card/src/forms/Review/State/index.ts index 140a7ba18b65..ed9050aeeb31 100644 --- a/libs/application/templates/id-card/src/forms/Review/State/index.ts +++ b/libs/application/templates/id-card/src/forms/Review/State/index.ts @@ -3,11 +3,9 @@ import { buildMultiField, buildSection, getValueViaPath, - buildAlertMessageField, } from '@island.is/application/core' import { Routes } from '../../../lib/constants' import { state } from '../../../lib/messages' -// import { GetFormattedText } from '../../../utils' export const StateSection = buildSection({ id: 'reviewState', @@ -32,12 +30,6 @@ export const StateSection = buildSection({ }, }), children: [ - buildAlertMessageField({ - id: 'stateAlertMessage', - title: '', - message: state.labels.alertMessage, - alertType: 'info', - }), buildActionCardListField({ id: 'approvalActionCard', doesNotRequireAnswer: true, @@ -49,21 +41,18 @@ export const StateSection = buildSection({ `${Routes.APPLICANTSINFORMATION}.name`, '', ) as string - // const heading = GetFormattedText(state.labels.actionCardTitle) - // const description = GetFormattedText( - // state.labels.actionCardDescription, - // ) - // const label = GetFormattedText(state.labels.actionCardTag) - // TODO get a different way of rendering translation text return [ { - heading: state.labels.actionCardTitle.defaultMessage, + heading: state.labels.actionCardTitle, tag: { - label: state.labels.actionCardTag.defaultMessage, + label: state.labels.actionCardTag, outlined: false, variant: 'purple', }, - text: `${state.labels.actionCardDescription.defaultMessage} ${chosenApplicantName}`, + text: { + id: state.labels.actionCardDescription.id, + values: { name: chosenApplicantName }, + }, }, ] }, diff --git a/libs/application/templates/id-card/src/lib/IdCardTemplate.ts b/libs/application/templates/id-card/src/lib/IdCardTemplate.ts index 68d173ef1486..953b7f3d1773 100644 --- a/libs/application/templates/id-card/src/lib/IdCardTemplate.ts +++ b/libs/application/templates/id-card/src/lib/IdCardTemplate.ts @@ -1,5 +1,6 @@ import { coreHistoryMessages, + corePendingActionMessages, EphemeralStateLifeCycle, getValueViaPath, pruneAfterDays, @@ -17,35 +18,80 @@ import { DistrictsApi, InstitutionNationalIds, PassportsApi, + PendingAction, + StaticText, } from '@island.is/application/types' import { Features } from '@island.is/feature-flags' import { assign } from 'xstate' import { - // SyslumadurPaymentCatalogApi, DeliveryAddressApi, UserInfoApi, NationalRegistryUser, SyslumadurPaymentCatalogApi, IdentityDocumentApi, + NationalRegistryUserParentB, } from '../dataProviders' import { application as applicationMessage } from './messages' import { Events, Roles, States, ApiActions, Routes } from './constants' import { IdCardSchema } from './dataSchema' import { buildPaymentState } from '@island.is/application/utils' -import { getChargeItemCodes, hasReviewer } from '../utils' +import { getChargeItemCodes, hasReviewer, hasReviewerApproved } from '../utils' export const needsReview = (context: ApplicationContext) => { const { answers, externalData } = context.application return hasReviewer(answers, externalData) } +export const determineMessageFromApplicationAnswers = ( + application: Application, +): StaticText => { + const name = getValueViaPath( + application.answers, + 'applicantInformation.name', + ' ', + ) as string + + const nameObject = { id: applicationMessage.name.id, values: { name: name } } + + return nameObject +} + +const reviewStatePendingAction = ( + application: Application, + nationalId: string, +): PendingAction => { + const firstGuardianNationalId = getValueViaPath( + application.answers, + 'firstGuardianInformation.nationalId', + undefined, + ) as string | undefined + + if ( + nationalId && + firstGuardianNationalId !== nationalId && + !hasReviewerApproved(application.answers) + ) { + return { + title: corePendingActionMessages.waitingForReviewTitle, + content: corePendingActionMessages.youNeedToReviewDescription, + displayStatus: 'warning', + } + } else { + return { + title: corePendingActionMessages.waitingForReviewTitle, + content: corePendingActionMessages.waitingForReviewDescription, + displayStatus: 'info', + } + } +} + const IdCardTemplate: ApplicationTemplate< ApplicationContext, ApplicationStateSchema, Events > = { type: ApplicationTypes.ID_CARD, - name: applicationMessage.name, + name: applicationMessage.name, // TODO make dynamic if possible featureFlag: Features.idCardApplication, dataSchema: IdCardSchema, translationNamespaces: [ApplicationConfigurations.IdCard.translation], @@ -149,7 +195,7 @@ const IdCardTemplate: ApplicationTemplate< entry: 'assignToParentB', meta: { name: 'ParentB', - status: 'draft', + status: 'inprogress', lifecycle: pruneAfterDays(7), onEntry: defineTemplateApi({ action: ApiActions.assignParentB, @@ -166,22 +212,28 @@ const IdCardTemplate: ApplicationTemplate< { id: Roles.ASSIGNEE, formLoader: () => - import('../forms/Review').then((val) => - Promise.resolve(val.Review), + import('../forms/ParentB').then((val) => + Promise.resolve(val.ParentB), ), actions: [ { event: DefaultEvents.SUBMIT, name: '', type: 'primary' }, ], write: 'all', + api: [NationalRegistryUserParentB], }, ], actionCard: { + tag: { + label: applicationMessage.actionCardDraft, + variant: 'blue', + }, historyLogs: [ { logMessage: applicationMessage.historyWaitingForParentB, onEvent: DefaultEvents.SUBMIT, }, ], + pendingAction: reviewStatePendingAction, }, }, on: { @@ -193,7 +245,7 @@ const IdCardTemplate: ApplicationTemplate< meta: { name: 'Rejected', status: 'rejected', - lifecycle: pruneAfterDays(3 * 30), // TODO HOW MANY DAYS SHOULD THIS BE? + lifecycle: pruneAfterDays(3 * 30), onEntry: defineTemplateApi({ action: ApiActions.rejectApplication, }), @@ -219,7 +271,7 @@ const IdCardTemplate: ApplicationTemplate< meta: { name: 'Completed', status: 'completed', - lifecycle: pruneAfterDays(3 * 30), // TODO HOW MANY DAYS SHOULD THIS BE? + lifecycle: pruneAfterDays(3 * 30), onEntry: defineTemplateApi({ action: ApiActions.submitApplication, }), diff --git a/libs/application/templates/id-card/src/lib/constants.ts b/libs/application/templates/id-card/src/lib/constants.ts index 1ad7ca4048c5..3658c390d3cc 100644 --- a/libs/application/templates/id-card/src/lib/constants.ts +++ b/libs/application/templates/id-card/src/lib/constants.ts @@ -1,4 +1,5 @@ import { DefaultEvents } from '@island.is/application/types' +import { Services } from '../shared/types' export type Events = | { type: DefaultEvents.ASSIGN } @@ -31,13 +32,6 @@ export enum Roles { ASSIGNEE = 'assignee', //second guardian } -export enum Services { - REGULAR = 'regular', - EXPRESS = 'express', - REGULAR_DISCOUNT = 'regularDiscount', - EXPRESS_DISCOUNT = 'expressDiscount', -} - export enum ApiActions { assignParentB = 'assignParentB', submitApplication = 'submitApplication', @@ -49,6 +43,12 @@ export enum ApiActions { export const EXPIRATION_LIMIT_MONTHS = 9 +export type PaymentItem = { + chargeType: string + priceAmount: number + chargeItemCode: string +} + export type Service = { type: Services dropLocation: string @@ -97,27 +97,35 @@ export type ChildsPersonalInfo = { guardian2: Guardian } +export type Gender = 'F' | 'M' | 'X' + +export type ExpiryStatus = 'EXPIRED' | 'LOST' + export type IdentityDocument = { - number: string - type: string - verboseType: string - subType: string - status: string - issuingDate: string - expirationDate: string - displayFirstName: string - displayLastName: string - mrzFirstName: string - mrzLastName: string - sex: string + number?: string | null + type?: string | null + verboseType?: string | null + subType?: string | null + status?: string | null + issuingDate?: Date | null + expirationDate?: Date | null + displayFirstName?: string | null + displayLastName?: string | null + mrzFirstName?: string | null + mrzLastName?: string | null + sex?: Gender | null + numberWithType?: string + expiryStatus?: ExpiryStatus + expiresWithinNoticeTime?: boolean } export interface IdentityDocumentChild { - childNationalId: string - secondParent: string - secondParentName: string - childName: string + childNationalId?: string | null + secondParent?: string | null + secondParentName?: string | null + childName?: string | null passports?: IdentityDocument[] + citizenship?: Citizenship | null } export interface IdentityDocumentData { @@ -125,6 +133,19 @@ export interface IdentityDocumentData { childPassports: IdentityDocumentChild[] } +export interface Citizenship { + kodi?: string | null + land?: string | null +} + +export interface CombinedApplicantInformation { + name?: string + age?: number + nationalId?: string + passport?: IdentityDocument + children?: Array +} + export const twoDays = 24 * 3600 * 1000 * 2 export const sixtyDays = 24 * 3600 * 1000 * 60 export const sevenDays = 24 * 3600 * 1000 * 7 diff --git a/libs/application/templates/id-card/src/lib/dataSchema.ts b/libs/application/templates/id-card/src/lib/dataSchema.ts index b119c889f81a..f9f2f7508fcb 100644 --- a/libs/application/templates/id-card/src/lib/dataSchema.ts +++ b/libs/application/templates/id-card/src/lib/dataSchema.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { parsePhoneNumberFromString } from 'libphonenumber-js' import { error } from './messages' -import { Services } from './constants' +import { Services } from '../shared/types' const nationalIdRegex = /([0-9]){6}-?([0-9]){4}/ const emailRegex = @@ -31,7 +31,7 @@ const personInfo = z export const IdCardSchema = z.object({ approveExternalData: z.boolean().refine((v) => v), typeOfId: z.enum(['II', 'ID']), //II = Nafnskírteini sem ferðaskilríki, ID = Nafnskírteini ekki sem ferðaskilríki - chosenApplicants: z.string(), + chosenApplicants: z.string().min(1), applicantInformation: personInfo, firstGuardianInformation: personInfo, secondGuardianInformation: z.object({ @@ -43,10 +43,10 @@ export const IdCardSchema = z.object({ phoneNumber: z.string().refine((v) => isValidPhoneNumber(v) || v === '', { params: error.invalidValue, }), + approved: z.boolean().optional(), }), priceList: z.object({ priceChoice: z.enum([Services.EXPRESS, Services.REGULAR]), - location: z.string(), }), }) diff --git a/libs/application/templates/id-card/src/lib/messages/application.ts b/libs/application/templates/id-card/src/lib/messages/application.ts index 8cbc885eb137..be975b8c237b 100644 --- a/libs/application/templates/id-card/src/lib/messages/application.ts +++ b/libs/application/templates/id-card/src/lib/messages/application.ts @@ -2,8 +2,8 @@ import { defineMessages } from 'react-intl' export const application = defineMessages({ name: { - id: 'id.application:name', - defaultMessage: 'Umsókn um nafnskírteini', + id: 'id.application:name#markdown', + defaultMessage: 'Umsókn um nafnskírteini {name}', description: `Application's name`, }, institutionName: { diff --git a/libs/application/templates/id-card/src/lib/messages/confirmation.ts b/libs/application/templates/id-card/src/lib/messages/confirmation.ts index e131a3d80da0..12ec1ea81c91 100644 --- a/libs/application/templates/id-card/src/lib/messages/confirmation.ts +++ b/libs/application/templates/id-card/src/lib/messages/confirmation.ts @@ -43,14 +43,22 @@ export const reviewConfirmation = { description: 'Review confirmation accordion title', }, accordionText: { - id: 'id.application:reviewConfirmation.general.accordionText', + id: 'id.application:reviewConfirmation.general.accordionText#markdown', + defaultMessage: `* Þú þarft að mæta í myndatöku á næsta umsóknarstað. + \n* Umsókn er opin í 60 daga eftir forskráningu og mæta þarf til myndatöku innan þess tíma. Ef af einhverjum ástæðum er ekki mætt í myndatöku, eða hætt er við umsókn að þeim tíma liðnum, þarf umsækjandi sjálfur að óska eftir endurgreiðslu með því að senda póst á endurgreidsla@island.is + \n* Þú færð senda tilkynningu á Mínar síður þegar nafnskírteinið er tilbúið og hvenær hægt verður að sækja það á þann afhendingarstað sem þú valdir.\n`, + description: 'Review confirmation accordion text', + }, + accordionTextForChild: { + id: 'id.application:reviewConfirmation.general.accordionTextForChild#markdown', defaultMessage: `* Forsjáraðili þarf að mæta með barni í myndatöku á [umsóknarstað](https://island.is/nafnskirteini). \n* Hafi báðir forsjá raðiliar samþykkt útgáfu nafnskírteinis barns er hún opin í 60 daga. Ef að einhverjum ástæðum er ekki mætt í myndatöku að þeim tíma liðnum þarf að sækja um endurgreiðslu með því að senda póst á [endurgreidsla@island.is](endurgreidsla@island.is). \n* Geti FORSJÁRAÐILI ekki mætt með barni í myndatöku þarf að veita þriðja aðila [umboð til þess](https://island.is/nafnskirteini).\n`, - description: 'Review confirmation accordion text', + description: + 'Review confirmation accordion text when applying for a child', }, infoMessageText1: { id: 'id.application:reviewConfirmation.general.infoMessageText1', @@ -59,7 +67,7 @@ export const reviewConfirmation = { description: 'Review confirmation first information message', }, infoMessageText2: { - id: 'id.application:reviewConfirmation.general.infoMessageText1', + id: 'id.application:reviewConfirmation.general.infoMessageText2', defaultMessage: 'Athugaðu að eftir að báðir forsjáraðilar hafa samþykkt umsókn er hún opin í 60 daga inni á island.is - að þeim tíma liðnum þarf að sækja um endurgreiðslu ef ekki er mætt í myndatöku.', description: 'Review confirmation second information message', diff --git a/libs/application/templates/id-card/src/lib/messages/externalData.ts b/libs/application/templates/id-card/src/lib/messages/externalData.ts index 7f283da3f7f0..ee87f369e5d7 100644 --- a/libs/application/templates/id-card/src/lib/messages/externalData.ts +++ b/libs/application/templates/id-card/src/lib/messages/externalData.ts @@ -57,6 +57,12 @@ export const externalData = { 'Vinsamlega athugaðu að ef eldra skírteini hefur glatast þarf að [tilkynna](https://www.skra.is/umsoknir/eydublod-umsoknir-og-vottord/stok-vara/?productid=7a8b6878-757d-11e9-9452-005056851dd2) það.', description: 'Pre information has lost old card alert message', }, + parentBIntroText: { + id: 'id.application:externalData.preInformation.parentBIntroText#markdown', + defaultMessage: + 'Í þessu ferli samþykkir þú sem forsjáraðili umsókn **{guardianName}** um vegabréf fyrir **{childsName}**. Þegar þessi umsókn hefur verið samþykkt þarf viðkomandi að mæta í myndatöku hjá næsta sýslumanni til þess að vegabréfið geti farið í framleiðslu. Þegar vegabréfið er tilbúið verður hægt að sækja það hjá því sýslumannsembætti sem tilgreint var í umsóknarferlinu. Þetta ferli vistast sjálfkrafa á Mínar síður á Ísland.is. Þar getur þú einnig fylgst með stöðu umsóknar eftir að öll gögn hafa verið send inn.', + description: 'parent B preInformation description', + }, }), nationalRegistry: defineMessages({ title: { diff --git a/libs/application/templates/id-card/src/lib/messages/idInformation.ts b/libs/application/templates/id-card/src/lib/messages/idInformation.ts index 32bb3483e567..021508177acb 100644 --- a/libs/application/templates/id-card/src/lib/messages/idInformation.ts +++ b/libs/application/templates/id-card/src/lib/messages/idInformation.ts @@ -50,6 +50,12 @@ export const idInformation = { defaultMessage: 'Nafnskírteini sem ferðaskilríki', description: 'Type of id radio answer id card travel rights', }, + warningText: { + id: 'id.application:idInformation.labels.warningText', + defaultMessage: + 'Þú uppfyllir ekki skilyrði fyrir báðum útgáfum af nafnskírteini', + description: 'Warning alert for type of ID', + }, infoAlert: { id: 'id.application:idInformation.labels.infoAlert', defaultMessage: @@ -57,7 +63,7 @@ export const idInformation = { description: 'Information alert for type of ID', }, chosenApplicantsDescription: { - id: 'id.application:idInformation.labels.chosenApplicantsDescription', + id: 'id.application:idInformation.labels.chosenApplicantsDescription#markdown', defaultMessage: 'Þú getur sótt um nafnskírteini / ferðaskilríki fyrir þig og eftirfarandi einstaklinga í þinni umsjón. Veldu þann einstakling sem þú vilt hefja umsókn fyrir og haltu síðan áfram í næsta skref.', description: 'description of chosen applicants page', diff --git a/libs/application/templates/id-card/src/lib/messages/priceList.ts b/libs/application/templates/id-card/src/lib/messages/priceList.ts index 5650d70db3c3..1d228d5cd949 100644 --- a/libs/application/templates/id-card/src/lib/messages/priceList.ts +++ b/libs/application/templates/id-card/src/lib/messages/priceList.ts @@ -31,8 +31,8 @@ export const priceList = { description: 'Location select dropdown placeholder', }, regularPriceTitle: { - id: 'id.application:priceList.labels.regularPriceTitle', - defaultMessage: 'Almenn afgreiðsla: 18-66 ára - 9.200 kr.', + id: 'id.application:priceList.labels.regularPriceTitle#markdown', + defaultMessage: 'Almenn afgreiðsla: 18-66 ára - {price}', description: 'Regular price radio button title', }, regularPriceDescription: { @@ -41,8 +41,8 @@ export const priceList = { description: 'Regular price radio button description', }, fastPriceTitle: { - id: 'id.application:priceList.labels.fastPriceTitle', - defaultMessage: 'Hraðafgreiðsla: 18-66 ára 18.400 kr.', + id: 'id.application:priceList.labels.fastPriceTitle#markdown', + defaultMessage: 'Hraðafgreiðsla: 18-66 ára - {price}', description: 'Fast price radio button title', }, fastPriceDescription: { @@ -51,13 +51,13 @@ export const priceList = { description: 'Fast price radio button description', }, discountRegularPriceTitle: { - id: 'id.application:priceList.labels.discountRegularPriceTitle', - defaultMessage: 'Almenn afgreiðsla: börn, aldraðir, öryrkjar 4.600 kr.', + id: 'id.application:priceList.labels.discountRegularPriceTitle#markdown', + defaultMessage: 'Almenn afgreiðsla: börn, aldraðir, öryrkjar - {price}', description: 'Discount Regular price radio button title', }, discountFastPriceTitle: { - id: 'id.application:priceList.labels.discountFastPriceTitle', - defaultMessage: 'Hraðafgreiðsla: börn, aldraðir, öryrkjar - 9.200 kr.', + id: 'id.application:priceList.labels.discountFastPriceTitle#markdown', + defaultMessage: 'Hraðafgreiðsla: börn, aldraðir, öryrkjar - {price}', description: 'Discount Fast price radio button title', }, }), diff --git a/libs/application/templates/id-card/src/lib/messages/state.ts b/libs/application/templates/id-card/src/lib/messages/state.ts index b856b94c0a61..c844e817a946 100644 --- a/libs/application/templates/id-card/src/lib/messages/state.ts +++ b/libs/application/templates/id-card/src/lib/messages/state.ts @@ -13,7 +13,7 @@ export const state = { description: 'Review state page title', }, description: { - id: 'id.application:state.general.description', + id: 'id.application:state.general.description#markdown', defaultMessage: `{guardianName} hefur sótt um nafnkskírteini sem er ferðaskilríki fyrir: {childName}. Samþykki beggja forsjáraðila þarf til að útgáfa þess sé heimil. Í þessu ferli getur þú afgreitt @@ -35,9 +35,9 @@ export const state = { description: 'Action card title', }, actionCardDescription: { - id: 'id.application:state.labels.actionCardDescription', + id: 'id.application:state.labels.actionCardDescription#markdown', defaultMessage: - 'Beðið er eftir að samþykki fyrir umsókn um nafnskírteini fyrir: ', + 'Beðið er eftir að samþykki fyrir umsókn um nafnskírteini fyrir: {name}', description: 'Action card description', }, actionCardTag: { diff --git a/libs/application/templates/id-card/src/utils/formatText.ts b/libs/application/templates/id-card/src/utils/formatText.ts deleted file mode 100644 index 75216ce017f6..000000000000 --- a/libs/application/templates/id-card/src/utils/formatText.ts +++ /dev/null @@ -1,8 +0,0 @@ -// import { useLocale } from '@island.is/localization' -// import { MessageDescriptor } from 'react-intl' - -// export const GetFormattedText = (messageId: MessageDescriptor) => { -// const { formatMessage } = useLocale() -// const message = formatMessage(messageId) -// return `${message}` -// } diff --git a/libs/application/templates/id-card/src/utils/getChargeItemCodes.ts b/libs/application/templates/id-card/src/utils/getChargeItemCodes.ts index f0f074e14aa3..f2f3d9f40de5 100644 --- a/libs/application/templates/id-card/src/utils/getChargeItemCodes.ts +++ b/libs/application/templates/id-card/src/utils/getChargeItemCodes.ts @@ -1,112 +1,24 @@ import { getValueViaPath } from '@island.is/application/core' -import { - Application, - NationalRegistryIndividual, -} from '@island.is/application/types' +import { Application } from '@island.is/application/types' import { ChargeItemCode } from '@island.is/shared/constants' -import { Routes, Services } from '../lib/constants' -import { isChild } from './isChild' +import { Routes } from '../lib/constants' +import { checkForDiscount } from './hasDiscount' +import { Services } from '../shared/types' export const getChargeItemCodes = (application: Application): Array => { const chosenPaymentForm = getValueViaPath( application.answers, `${Routes.PRICELIST}.priceChoice`, ) - - const chosenTypeOfId = getValueViaPath(application.answers, Routes.TYPEOFID) - - const applicantInformation = getValueViaPath( - application.externalData, - 'nationalRegistry,data', - ) as NationalRegistryIndividual - - const paymentAsChild = isUnderAge(application, applicantInformation.age) - const hasDisability = getValueViaPath( - application.answers, - 'applicantInformation.hasDisabilityLicense', - ) as boolean | undefined - const isOver66 = applicantInformation.age > 66 - - if (isOver66) { - if (chosenPaymentForm === Services.REGULAR) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_OLDER_TRAVEL_REGULAR.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_OLDER_REGULAR.toString()] - } - } - if (chosenPaymentForm === Services.EXPRESS) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_OLDER_TRAVEL_EXPRESS.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_OLDER_EXPRESS.toString()] - } - } - } else if (paymentAsChild) { - if (chosenPaymentForm === Services.REGULAR) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_CHILDREN_TRAVEL_REGULAR.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_CHILDREN_REGULAR.toString()] - } - } - if (chosenPaymentForm === Services.EXPRESS) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_CHILDREN_TRAVEL_EXPRESS.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_CHILDREN_EXPRESS.toString()] - } - } - } else if (hasDisability) { - if (chosenPaymentForm === Services.REGULAR) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_DISABILITY_TRAVEL_REGULAR.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_DISABILITY_REGULAR.toString()] - } - } - if (chosenPaymentForm === Services.EXPRESS) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_DISABILITY_TRAVEL_EXPRESS.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_DISABILITY_EXPRESS.toString()] - } - } - } else { - if (chosenPaymentForm === Services.REGULAR) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_TRAVEL_REGULAR.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_REGULAR.toString()] - } - } - if (chosenPaymentForm === Services.EXPRESS) { - if (chosenTypeOfId === 'II') { - return [ChargeItemCode.ID_CARD_TRAVEL_EXPRESS.toString()] - } - if (chosenTypeOfId === 'ID') { - return [ChargeItemCode.ID_CARD_EXPRESS.toString()] - } - } + const hasDiscount = checkForDiscount(application) + if (chosenPaymentForm === Services.REGULAR && !hasDiscount) { + return [ChargeItemCode.ID_CARD_REGULAR.toString()] + } else if (chosenPaymentForm === Services.REGULAR && hasDiscount) { + return [ChargeItemCode.ID_CARD_OTHERS_REGULAR.toString()] + } else if (chosenPaymentForm === Services.EXPRESS && !hasDiscount) { + return [ChargeItemCode.ID_CARD_EXPRESS.toString()] + } else if (chosenPaymentForm === Services.EXPRESS && hasDiscount) { + return [ChargeItemCode.ID_CARD_OTHERS_EXPRESS.toString()] } - return [] } - -const isUnderAge = (application: Application, age: number) => { - if (age < 18) return true - - const applicantIsApplyingForChild = isChild( - application.answers, - application.externalData, - ) - - return applicantIsApplyingForChild -} diff --git a/libs/application/templates/id-card/src/utils/getChosenApplicant.ts b/libs/application/templates/id-card/src/utils/getChosenApplicant.ts index e44a15a8c6e3..62a534ddcad3 100644 --- a/libs/application/templates/id-card/src/utils/getChosenApplicant.ts +++ b/libs/application/templates/id-card/src/utils/getChosenApplicant.ts @@ -1,54 +1,47 @@ import { getValueViaPath } from '@island.is/application/core' import { - Application, + FormValue, NationalRegistryIndividual, } from '@island.is/application/types' -import { IdentityDocumentChild, Routes } from '../lib/constants' +import { IdentityDocumentChild } from '../lib/constants' export interface ChosenApplicant { - nationalId: string - name: string + name?: string | null + isApplicant: boolean + nationalId?: string | null } export const getChosenApplicant = ( - application: Application, - nationalId?: string, + answers: FormValue, + externalData: any, + nationalId?: string | null, ): ChosenApplicant => { const applicantIdentity = getValueViaPath( - application.externalData, + externalData, 'nationalRegistry.data', {}, ) as NationalRegistryIndividual const applicantChildren = getValueViaPath( - application.externalData, + externalData, 'identityDocument.data.childPassports', [], ) as Array - const chosenApplicantNationalId = - nationalId ?? - (getValueViaPath( - application.answers, - Routes.CHOSENAPPLICANTS, - '', - ) as string) - - if ( - chosenApplicantNationalId === '' || - applicantIdentity?.nationalId === chosenApplicantNationalId - ) { + if (!nationalId || applicantIdentity?.nationalId === nationalId) { return { - nationalId: applicantIdentity?.nationalId, name: applicantIdentity?.fullName, + isApplicant: true, + nationalId: applicantIdentity.nationalId, } } else { const chosenChild = applicantChildren.filter( - (x) => x.childNationalId === chosenApplicantNationalId, + (x) => x.childNationalId === nationalId, )?.[0] return { - nationalId: chosenChild.childNationalId, name: chosenChild.childName, + isApplicant: false, + nationalId: chosenChild.childNationalId, } } } diff --git a/libs/application/templates/id-card/src/utils/getPriceList.ts b/libs/application/templates/id-card/src/utils/getPriceList.ts new file mode 100644 index 000000000000..a20522704fd2 --- /dev/null +++ b/libs/application/templates/id-card/src/utils/getPriceList.ts @@ -0,0 +1,34 @@ +import { getValueViaPath } from '@island.is/application/core' +import { Application } from '@island.is/application/types' +import { ChargeItemCode } from '@island.is/shared/constants' +import { PaymentItem } from '../lib/constants' + +export const getPriceList = (application: Application) => { + const applicationPrices = getValueViaPath( + application.externalData, + 'payment.data', + [], + ) as Array + + const regularPrice = applicationPrices.find( + (x) => x.chargeItemCode === ChargeItemCode.ID_CARD_REGULAR.toString(), + ) + const regularDiscountPrice = applicationPrices.find( + (x) => + x.chargeItemCode === ChargeItemCode.ID_CARD_OTHERS_REGULAR.toString(), + ) + const fastPrice = applicationPrices.find( + (x) => x.chargeItemCode === ChargeItemCode.ID_CARD_EXPRESS.toString(), + ) + const fastDiscountPrice = applicationPrices.find( + (x) => + x.chargeItemCode === ChargeItemCode.ID_CARD_OTHERS_EXPRESS.toString(), + ) + + return { + regularPrice, + regularDiscountPrice, + fastPrice, + fastDiscountPrice, + } +} diff --git a/libs/application/templates/id-card/src/utils/hasDiscount.ts b/libs/application/templates/id-card/src/utils/hasDiscount.ts index 6e70a99b4072..a4bea0bcbe25 100644 --- a/libs/application/templates/id-card/src/utils/hasDiscount.ts +++ b/libs/application/templates/id-card/src/utils/hasDiscount.ts @@ -5,6 +5,11 @@ import { info } from 'kennitala' export const checkForDiscount = (application: Application) => { const { answers } = application + //so applicant is applying for a child + if (application.applicant !== answers.chosenApplicants) { + return true + } + const hasDisability = getValueViaPath( answers, 'applicantInformation.hasDisabilityLicense', diff --git a/libs/application/templates/id-card/src/utils/hasReviewer.ts b/libs/application/templates/id-card/src/utils/hasReviewer.ts index 36c23f9cf522..b24d3bde54f4 100644 --- a/libs/application/templates/id-card/src/utils/hasReviewer.ts +++ b/libs/application/templates/id-card/src/utils/hasReviewer.ts @@ -15,13 +15,23 @@ export const hasReviewer = (answers: FormValue, externalData: ExternalData) => { '', ) as string + const chosenTypeOfId = getValueViaPath(answers, Routes.TYPEOFID, '') as string + const applicantNationalId = getValueViaPath( externalData, 'nationalRegistry.data.nationalId', '', ) as string + + //logged in user is not the applicant if (chosenApplicantNationalId !== applicantNationalId) { - //logged in user is not the applicant + //if a parent is applying for nafnskírteini án ferðaskilríkja for a child, then there is no other reviewer needed + if (chosenTypeOfId === 'II') { + return false + } + + //if a parent is applying for nafnskíteini með ferðaskilríkjum for a child + //then check if there is another parent registered that needs to approve const chosenChild = applicantChildren.find( (x) => x.childNationalId === chosenApplicantNationalId, ) diff --git a/libs/application/templates/id-card/src/utils/index.ts b/libs/application/templates/id-card/src/utils/index.ts index 3e280dd3f8f1..9c3a25bbe5a5 100644 --- a/libs/application/templates/id-card/src/utils/index.ts +++ b/libs/application/templates/id-card/src/utils/index.ts @@ -1,5 +1,9 @@ import { parsePhoneNumberFromString } from 'libphonenumber-js' -// export * from './formatText' +import { getValueViaPath } from '@island.is/application/core' +import { NationalRegistryIndividual } from '@island.is/application/types' +import { IdentityDocument, IdentityDocumentChild } from '../lib/constants' +import { FormValue } from '@island.is/application/types' + export * from './getChosenApplicant' export * from './hasSecondGuardian' export * from './hasDiscount' @@ -8,8 +12,49 @@ export * from './getChargeItemCodes' export * from './updateAnswers' export * from './isChild' export * from './isAvailableForApplication' +export * from './getPriceList' export const formatPhoneNumber = (phoneNumber: string): string => { const phone = parsePhoneNumberFromString(phoneNumber, 'IS') return phone?.formatNational() || phoneNumber } + +export const hasReviewerApproved = (answers: FormValue): string => + getValueViaPath(answers, 'secondGuardianInformation.approved', '') as string + +export const getCombinedApplicantInformation = (externalData: any) => { + const applicantName = getValueViaPath( + externalData, + 'nationalRegistry.data.fullName', + '', + ) as string + + const applicantNationalRegistry = getValueViaPath( + externalData, + 'nationalRegistry.data', + {}, + ) as NationalRegistryIndividual + + const applicantPassport = getValueViaPath( + externalData, + 'identityDocument.data.userPassport', + undefined, + ) as IdentityDocument | undefined + + const applicantChildren = getValueViaPath( + externalData, + 'identityDocument.data.childPassports', + [], + ) as Array + + return { + name: applicantName, + age: applicantNationalRegistry.age, + nationalId: applicantNationalRegistry.nationalId, + passport: applicantPassport, + children: applicantChildren, + } +} + +export const formatIsk = (value: number): string => + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ' kr.' diff --git a/libs/application/templates/id-card/src/utils/isAvailableForApplication.ts b/libs/application/templates/id-card/src/utils/isAvailableForApplication.ts index e7c819841548..dbe307eab433 100644 --- a/libs/application/templates/id-card/src/utils/isAvailableForApplication.ts +++ b/libs/application/templates/id-card/src/utils/isAvailableForApplication.ts @@ -1,29 +1,50 @@ -import { EXPIRATION_LIMIT_MONTHS } from '../lib/constants' +import { + CombinedApplicantInformation, + EXPIRATION_LIMIT_MONTHS, +} from '../lib/constants' export const isAvailableForApplication = ( - dateStr: string, idTypeChosen: string, - oldIdType: string, - age?: number, + applicantInformation: CombinedApplicantInformation, ) => { - if (age && age < 18 && idTypeChosen === 'ID') { + if ( + applicantInformation.age && + applicantInformation.age < 18 && + idTypeChosen === 'ID' + ) { return false } - const inputDate = new Date(dateStr) - const today = new Date() - today.setHours(0, 0, 0, 0) + const oldPassportType = + applicantInformation.passport && + `${applicantInformation.passport.type}${applicantInformation.passport.subType}` + const dateStr = + applicantInformation.passport && + applicantInformation.passport.expirationDate - // Calculate the date x months from today based on expiration limit - const xMonthsLater = new Date(today) - xMonthsLater.setMonth(xMonthsLater.getMonth() + EXPIRATION_LIMIT_MONTHS) - - // Check if the input date is within the expiration limit - const withinExpirationDate = inputDate >= today && inputDate <= xMonthsLater - if (idTypeChosen === 'ID' && (oldIdType === 'II' || oldIdType === 'IG')) { + if ( + (idTypeChosen === 'ID' && oldPassportType === 'II') || + oldPassportType === 'IG' + ) { // if types are not the same, then expiration date does not matter since you can apply for an id card type that you don't have regardless of old one return true } - return withinExpirationDate //else return if the date is within the expiration date + //applicant has old id and we need to calculate expiry date + if (dateStr) { + // Calculate the date x months from today based on expiration limit + const inputDate = new Date(dateStr) + const today = new Date() + today.setHours(0, 0, 0, 0) + + const xMonthsLater = new Date(today) + xMonthsLater.setMonth(xMonthsLater.getMonth() + EXPIRATION_LIMIT_MONTHS) + + // Check if the input date is within the expiration limit + const withinExpirationDate = inputDate >= today && inputDate <= xMonthsLater + + return withinExpirationDate //return if the date is within the expiration date + } + + return true } diff --git a/libs/application/templates/id-card/src/utils/updateAnswers.ts b/libs/application/templates/id-card/src/utils/updateAnswers.ts index 476c778a898a..582681c09b2e 100644 --- a/libs/application/templates/id-card/src/utils/updateAnswers.ts +++ b/libs/application/templates/id-card/src/utils/updateAnswers.ts @@ -8,7 +8,11 @@ export const updateAnswers = ( nationalId: string, setValue: (name: string, value: unknown, config?: Object) => void, ): Object => { - const chosenApplicants = getChosenApplicant(application, nationalId) + const chosenApplicants = getChosenApplicant( + application.answers, + application.externalData, + nationalId, + ) const applicantUserProfile = getValueViaPath( application.externalData, 'userProfile.data', diff --git a/libs/application/types/src/lib/Fields.ts b/libs/application/types/src/lib/Fields.ts index 14a5ccc3d75a..a3631b76dc20 100644 --- a/libs/application/types/src/lib/Fields.ts +++ b/libs/application/types/src/lib/Fields.ts @@ -534,15 +534,31 @@ export interface NationalIdWithNameField extends BaseField { minAgePerson?: number } +type Modify = Omit & R + export type ActionCardListField = BaseField & { readonly type: FieldTypes.ACTION_CARD_LIST component: FieldComponents.ACTION_CARD_LIST - items: (application: Application) => ActionCardProps[] + items: (application: Application) => ApplicationActionCardProps[] space?: BoxProps['paddingTop'] marginBottom?: BoxProps['marginBottom'] marginTop?: BoxProps['marginTop'] } +export type ApplicationActionCardProps = Modify< + ActionCardProps, + { + heading?: FormText + text?: FormText + tag?: Modify + cta?: Modify + unavailable?: Modify< + ActionCardProps['unavailable'], + { label?: FormText; message?: FormText } + > + } +> + export type TableRepeaterField = BaseField & { readonly type: FieldTypes.TABLE_REPEATER component: FieldComponents.TABLE_REPEATER diff --git a/libs/application/types/src/lib/template-api/shared-api/shared-api-definitions/national-registry-user.ts b/libs/application/types/src/lib/template-api/shared-api/shared-api-definitions/national-registry-user.ts index c1f33d57daf2..824f9cd43e43 100644 --- a/libs/application/types/src/lib/template-api/shared-api/shared-api-definitions/national-registry-user.ts +++ b/libs/application/types/src/lib/template-api/shared-api/shared-api-definitions/national-registry-user.ts @@ -6,6 +6,7 @@ export interface NationalRegistryParameters { legalDomicileIceland?: boolean ageToValidateError?: ProviderErrorReason icelandicCitizenship?: boolean + allowIfChildHasCitizenship?: boolean validateAlreadyHasIcelandicCitizenship?: boolean allowPassOnChild?: boolean citizenshipWithinEES?: boolean diff --git a/libs/application/ui-fields/src/lib/ActionCardListFormField/ActionCardListFormField.tsx b/libs/application/ui-fields/src/lib/ActionCardListFormField/ActionCardListFormField.tsx index acaa73b1cd10..b2f63f1f93c1 100644 --- a/libs/application/ui-fields/src/lib/ActionCardListFormField/ActionCardListFormField.tsx +++ b/libs/application/ui-fields/src/lib/ActionCardListFormField/ActionCardListFormField.tsx @@ -4,6 +4,9 @@ import { } from '@island.is/application/types' import { ActionCard, Stack, Box } from '@island.is/island-ui/core' import { FC } from 'react' +import { useLocale } from '@island.is/localization' +import { formatText } from '@island.is/application/core' +import { ActionCardProps } from '@island.is/island-ui/core/types' interface Props extends FieldBaseProps { field: ActionCardListField @@ -11,13 +14,42 @@ interface Props extends FieldBaseProps { export const ActionCardListFormField: FC = ({ application, field }) => { const { items, marginBottom = 4, marginTop = 4, space = 2 } = field - + const { formatMessage } = useLocale() return ( - {items(application).map((item, index) => ( - - ))} + {items(application).map((item, index) => { + const itemWithTranslatedTexts: ActionCardProps = { + ...item, + heading: + item.heading && + formatText(item.heading, application, formatMessage), + text: + item.text && formatText(item.text, application, formatMessage), + tag: item.tag && { + ...item.tag, + label: formatText(item.tag?.label, application, formatMessage), + }, + cta: item.cta && { + ...item.cta, + label: formatText(item.cta?.label, application, formatMessage), + }, + unavailable: { + ...item.unavailable, + label: + item.unavailable?.label && + formatText(item.unavailable.label, application, formatMessage), + message: + item.unavailable?.message && + formatText( + item.unavailable.message, + application, + formatMessage, + ), + }, + } + return + })} ) diff --git a/libs/clients/passports/src/clientConfig.json b/libs/clients/passports/src/clientConfig.json index c8a5e526d197..07c0faf3af0e 100644 --- a/libs/clients/passports/src/clientConfig.json +++ b/libs/clients/passports/src/clientConfig.json @@ -375,9 +375,21 @@ "type": "array", "nullable": true, "items": { "$ref": "#/components/schemas/IdentityDocumentResponse" } + }, + "rikisfang": { + "nullable": true, + "oneOf": [{ "$ref": "#/components/schemas/Rikisfang" }] } } }, + "Rikisfang": { + "type": "object", + "additionalProperties": false, + "properties": { + "kodi": { "type": "string", "nullable": true }, + "land": { "type": "string", "nullable": true } + } + }, "Preregistration": { "type": "object", "additionalProperties": false, diff --git a/libs/clients/passports/src/lib/passportsApi.service.ts b/libs/clients/passports/src/lib/passportsApi.service.ts index fb4577797a98..4d7a6472b8f2 100644 --- a/libs/clients/passports/src/lib/passportsApi.service.ts +++ b/libs/clients/passports/src/lib/passportsApi.service.ts @@ -152,6 +152,7 @@ export class PassportsService { passports: child.identityDocumentResponses ? this.resolvePassports(child.identityDocumentResponses) : undefined, + citizenship: child.rikisfang, } }) diff --git a/libs/clients/passports/src/lib/passportsApi.types.ts b/libs/clients/passports/src/lib/passportsApi.types.ts index 62274ecb6299..7cd2fa0f3857 100644 --- a/libs/clients/passports/src/lib/passportsApi.types.ts +++ b/libs/clients/passports/src/lib/passportsApi.types.ts @@ -23,6 +23,12 @@ export interface IdentityDocumentChild { secondParentName?: string | null childName?: string | null passports?: IdentityDocument[] + citizenship?: Citizenship | null +} + +export interface Citizenship { + kodi?: string | null + land?: string | null } export interface ContactInfo { diff --git a/libs/shared/constants/src/lib/chargeItemCode.ts b/libs/shared/constants/src/lib/chargeItemCode.ts index 6fce9ba5c15f..816c9cd52e95 100644 --- a/libs/shared/constants/src/lib/chargeItemCode.ts +++ b/libs/shared/constants/src/lib/chargeItemCode.ts @@ -31,19 +31,7 @@ export enum ChargeItemCode { HEALTHCARE_LICENSE_CERTIFICATE = 'L6102', ID_CARD_REGULAR = 'AY155', //Nafnskírteini almennt gjald ID_CARD_EXPRESS = 'AY156', //Nafnskírteini skyndiútgáfa - ID_CARD_TRAVEL_REGULAR = 'AY157', //Nafnskírteini ferðaskilríki - ID_CARD_TRAVEL_EXPRESS = 'AY158', //Nafnskírteini ferðaskilríki skyndiútgáfa - ID_CARD_DISABILITY_REGULAR = 'AY159', //Nafnskírteini örykjar - ID_CARD_DISABILITY_EXPRESS = 'AY160', //Nafnskírteini örykjar skyndiútgáfa - ID_CARD_DISABILITY_TRAVEL_REGULAR = 'AY161', //Nafnskírteini öyrkjar ferðaskilríki - ID_CARD_DISABILITY_TRAVEL_EXPRESS = 'AY162', //Nafnskírteini örykjar ferðask. skyndiútgáfa - ID_CARD_CHILDREN_REGULAR = 'AY163', //Nafnskírteini börn - ID_CARD_CHILDREN_EXPRESS = 'AY164', //Nafnskírteini börn skyndiútgáfa - ID_CARD_CHILDREN_TRAVEL_REGULAR = 'AY165', //Nafnskírteini börn ferðaskilríki - ID_CARD_CHILDREN_TRAVEL_EXPRESS = 'AY166', //Nafnskírteini börn ferðaskilríki skyndiútg. - ID_CARD_OLDER_REGULAR = 'AY167', //Nafnskírteini 67 ára og eldri - ID_CARD_OLDER_EXPRESS = 'AY168', //Nafnskírteini 67 ára og eldri skyndiútgáfa - ID_CARD_OLDER_TRAVEL_REGULAR = 'AY169', //Nafnskírteini 67 ára og eldri ferðaskilríki - ID_CARD_OLDER_TRAVEL_EXPRESS = 'AY170', //Nafnskírteini 67 ára og eldri ferðask skyndiútgáfa + ID_CARD_OTHERS_REGULAR = 'AY157', //Nafnskírteini aðrir gjald + ID_CARD_OTHERS_EXPRESS = 'AY158', //Nafnskírteini aðrir skyndiútgáfa HEALTHCARE_WORK_PERMIT = 'L6101', }