diff --git a/package.json b/package.json index 8cfa7e2b9..096dea5dd 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@hapi/boom": "^9.1.1", "@hapi/hapi": "^20.0.1", "@hapi/inert": "^6.0.3", - "@opencrvs/toolkit": "0.0.13-events", + "@opencrvs/toolkit": "0.0.14-events", "@types/chalk": "^2.2.0", "@types/csv2json": "^1.4.0", "@types/fhir": "^0.0.30", diff --git a/src/api/custom-event/handler.ts b/src/api/custom-event/handler.ts index 70ae575f6..ff699df83 100644 --- a/src/api/custom-event/handler.ts +++ b/src/api/custom-event/handler.ts @@ -11,12 +11,13 @@ import * as Hapi from '@hapi/hapi' import { tennisClubMembershipEvent } from '@countryconfig/form/tennis-club-membership' import { EventDocument } from '@opencrvs/toolkit/events' +import { birthEvent } from '@countryconfig/form/v2/birth' export function getCustomEventsHandler( request: Hapi.Request, h: Hapi.ResponseToolkit ) { - return h.response([tennisClubMembershipEvent]).code(200) + return h.response([tennisClubMembershipEvent, birthEvent]).code(200) } export function onRegisterHandler( diff --git a/src/form/v2/birth/forms/child.ts b/src/form/v2/birth/forms/child.ts new file mode 100644 index 000000000..21f2a3f1b --- /dev/null +++ b/src/form/v2/birth/forms/child.ts @@ -0,0 +1,312 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { defineFormPage, TranslationConfig } from '@opencrvs/toolkit/events' +import { field } from '@opencrvs/toolkit/conditionals' +import { appendConditionalsToFields, createSelectOptions } from '../../utils' +import { getAddressFields } from '../../person/address' + +const GenderTypes = { + MALE: 'male', + FEMALE: 'female', + UNKNOWN: 'unknown' +} as const + +const TypeOfBirth = { + SINGLE: 'SINGLE', + TWIN: 'TWIN', + TRIPLET: 'TRIPLET', + QUADRUPLET: 'QUADRUPLET', + HIGHER_MULTIPLE_DELIVERY: 'HIGHER_MULTIPLE_DELIVERY' +} as const + +const AttendantAtBirth = { + PHYSICIAN: 'PHYSICIAN', + NURSE: 'NURSE', + MIDWIFE: 'MIDWIFE', + OTHER_PARAMEDICAL_PERSONNEL: 'OTHER_PARAMEDICAL_PERSONNEL', + LAYPERSON: 'LAYPERSON', + TRADITIONAL_BIRTH_ATTENDANT: 'TRADITIONAL_BIRTH_ATTENDANT', + NONE: 'NONE' +} as const + +const PlaceOfBirth = { + HEALTH_FACILITY: 'HEALTH_FACILITY', + PRIVATE_HOME: 'PRIVATE_HOME', + OTHER: 'OTHER' +} as const + +const genderMessageDescriptors = { + MALE: { + defaultMessage: 'Male', + description: 'Label for option male', + id: 'form.field.label.sexMale' + }, + FEMALE: { + defaultMessage: 'Female', + description: 'Label for option female', + id: 'form.field.label.sexFemale' + }, + UNKNOWN: { + defaultMessage: 'Unknown', + description: 'Label for option unknown', + id: 'form.field.label.sexUnknown' + } +} satisfies Record + +const typeOfBirthMessageDescriptors = { + SINGLE: { + defaultMessage: 'Single', + description: 'Label for single birth', + id: 'form.field.label.birthTypeSingle' + }, + TWIN: { + defaultMessage: 'Twin', + description: 'Label for twin birth', + id: 'form.field.label.birthTypeTwin' + }, + TRIPLET: { + defaultMessage: 'Triplet', + description: 'Label for triplet birth', + id: 'form.field.label.birthTypeTriplet' + }, + QUADRUPLET: { + defaultMessage: 'Quadruplet', + description: 'Label for quadruplet birth', + id: 'form.field.label.birthTypeQuadruplet' + }, + HIGHER_MULTIPLE_DELIVERY: { + defaultMessage: 'Higher multiple delivery', + description: 'Label for higher multiple delivery birth', + id: 'form.field.label.birthTypeHigherMultipleDelivery' + } +} satisfies Record + +const attendantAtBirthMessageDescriptors = { + PHYSICIAN: { + defaultMessage: 'Physician', + description: 'Label for physician attendant', + id: 'form.field.label.attendantAtBirthPhysician' + }, + NURSE: { + defaultMessage: 'Nurse', + description: 'Label for nurse attendant', + id: 'form.field.label.attendantAtBirthNurse' + }, + MIDWIFE: { + defaultMessage: 'Midwife', + description: 'Label for midwife attendant', + id: 'form.field.label.attendantAtBirthMidwife' + }, + OTHER_PARAMEDICAL_PERSONNEL: { + defaultMessage: 'Other paramedical personnel', + description: 'Label for other paramedical personnel', + id: 'form.field.label.attendantAtBirthOtherParamedicalPersonnel' + }, + LAYPERSON: { + defaultMessage: 'Layperson', + description: 'Label for layperson attendant', + id: 'form.field.label.attendantAtBirthLayperson' + }, + TRADITIONAL_BIRTH_ATTENDANT: { + defaultMessage: 'Traditional birth attendant', + description: 'Label for traditional birth attendant', + id: 'form.field.label.attendantAtBirthTraditionalBirthAttendant' + }, + NONE: { + defaultMessage: 'None', + description: 'Label for no attendant', + id: 'form.field.label.attendantAtBirthNone' + } +} satisfies Record + +const placeOfBirthMessageDescriptors = { + HEALTH_FACILITY: { + defaultMessage: 'Health Institution', + description: 'Select item for Health Institution', + id: 'form.field.label.healthInstitution' + }, + PRIVATE_HOME: { + defaultMessage: 'Residential address', + description: 'Select item for Private Home', + id: 'form.field.label.privateHome' + }, + OTHER: { + defaultMessage: 'Other', + description: 'Select item for Other location', + id: 'form.field.label.otherInstitution' + } +} satisfies Record + +const genderOptions = createSelectOptions(GenderTypes, genderMessageDescriptors) + +const placeOfBirthOptions = createSelectOptions( + PlaceOfBirth, + placeOfBirthMessageDescriptors +) + +const typeOfBirthOptions = createSelectOptions( + TypeOfBirth, + typeOfBirthMessageDescriptors +) + +const attendantAtBirthOptions = createSelectOptions( + AttendantAtBirth, + attendantAtBirthMessageDescriptors +) + +export const childPage = defineFormPage({ + id: 'child', + title: { + defaultMessage: "Child's details", + description: 'Form section title for Child', + id: 'form.birth.child.title' + }, + fields: [ + { + id: 'child.firstname', + type: 'TEXT', + required: true, + label: { + defaultMessage: 'First name(s)', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.firstname.label' + } + }, + { + id: 'child.surname', + type: 'TEXT', + required: true, + label: { + defaultMessage: 'Last name', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.surname.label' + } + }, + { + id: 'child.gender', + type: 'SELECT', + required: true, + label: { + defaultMessage: 'Sex', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.gender.label' + }, + options: genderOptions + }, + { + id: 'child.dob', + type: 'DATE', + required: true, + validation: [ + { + message: { + defaultMessage: 'Please enter a valid date', + description: 'This is the error message for invalid date', + id: 'event.birth.action.declare.form.section.child.field.dob.error' + }, + validator: field('child.dob').isBeforeNow() + } + ], + label: { + defaultMessage: 'Date of birth', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.dob.label' + } + }, + { + id: 'child.placeOfBirth', + type: 'SELECT', + required: true, + label: { + defaultMessage: 'Place of delivery', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.placeOfBirth.label' + }, + options: placeOfBirthOptions + }, + { + id: 'child.birthLocation', + type: 'TEXT', // @ToDo: select + required: true, + label: { + defaultMessage: 'Health Institution', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.birthLocation.label' + }, + conditionals: [ + { + type: 'HIDE', + conditional: field('child.placeOfBirth').isUndefinedOrNotInArray([ + 'HEALTH_FACILITY' + ]) + } + ] + }, + ...appendConditionalsToFields({ + inputFields: getAddressFields('child.privateHome'), + newConditionals: [ + { + type: 'HIDE', + conditional: field('child.placeOfBirth').isUndefinedOrNotInArray([ + 'PRIVATE_HOME' + ]) + } + ] + }), + ...appendConditionalsToFields({ + inputFields: getAddressFields('child.other'), + newConditionals: [ + { + type: 'HIDE', + conditional: field('child.placeOfBirth').isUndefinedOrNotInArray([ + 'OTHER' + ]) + } + ] + }), + { + id: 'child.attendantAtBirth', + type: 'SELECT', + required: false, + label: { + defaultMessage: 'Attendant at birth', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.attendantAtBirth.label' + }, + options: attendantAtBirthOptions + }, + { + id: 'child.birthType', + type: 'SELECT', + required: false, + label: { + defaultMessage: 'Type of birth', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.birthType.label' + }, + options: typeOfBirthOptions + }, + { + id: 'child.weightAtBirth', + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Weight at birth', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.child.field.weightAtBirth.label' + }, + options: { + type: 'number' + } + } + ] +}) diff --git a/src/form/v2/birth/forms/declare.ts b/src/form/v2/birth/forms/declare.ts new file mode 100644 index 000000000..e5506d616 --- /dev/null +++ b/src/form/v2/birth/forms/declare.ts @@ -0,0 +1,287 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { defineForm } from '@opencrvs/toolkit/events' +import { field, and } from '@opencrvs/toolkit/conditionals' +import { childPage } from './child' +import { informantPage, InformantTypes } from './informant' +import { appendConditionalsToFields } from '../../utils' +import { getPersonInputFields } from '../../person' + +export const BIRTH_DECLARE_FORM = defineForm({ + label: { + id: 'event.birth.action.declare.form.label', + defaultMessage: 'Birth decalration form', + description: 'This is what this form is referred as in the system' + }, + review: { + title: { + id: 'event.birth.action.declare.form.review.title', + defaultMessage: 'Birth declaration for {firstname} {surname}', + description: 'Title of the form to show in review page' + } + }, + active: true, + version: { + id: '1.0.0', + label: { + id: 'event.birth.action.declare.form.version.1', + defaultMessage: 'Version 1', + description: 'This is the first version of the form' + } + }, + pages: [ + { + id: 'introduction', + title: { + defaultMessage: + 'Introduce the birth registration process to the informant', + description: 'Event information title for the birth', + id: 'register.eventInfo.birth.title' + }, + fields: [ + { + type: 'BULLET_LIST', + id: 'form.section.information.birth.bulletList', + label: { + id: 'form.section.information.birth.bulletList.label', + defaultMessage: 'Birth Information', + description: 'Label for the birth information bullet list' + }, + items: [ + { + defaultMessage: + 'I am going to help you make a declaration of birth.', + description: 'Form information for birth', + id: 'form.section.information.birth.bullet1' + }, + { + defaultMessage: + 'As the legal Informant it is important that all the information provided by you is accurate.', + description: 'Form information for birth', + id: 'form.section.information.birth.bullet2' + }, + { + defaultMessage: + 'Once the declaration is processed you will receive an SMS to tell you when to visit the office to collect the certificate - Take your ID with you.', + description: 'Form information for birth', + id: 'form.section.information.birth.bullet3' + }, + { + defaultMessage: + 'Make sure you collect the certificate. A birth certificate is critical for this child, especially to make their life easy later on. It will help to access health services, school examinations and government benefits.', + description: 'Form information for birth', + id: 'form.section.information.birth.bullet4' + } + ], + font: 'reg16' + } + ] + }, + childPage, + informantPage, + { + id: 'mother', + title: { + defaultMessage: "Mother's details", + description: 'Form section title for mothers details', + id: 'form.section.mother.title' + }, + fields: [ + { + id: 'mother.detailsNotAvailable', + type: 'CHECKBOX', + required: true, + label: { + defaultMessage: "Mother's details not available", + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.mother.field.detailsNotAvailable.label` + }, + conditionals: [ + { + type: 'HIDE', + conditional: field('informant.relation').isInArray([ + InformantTypes.MOTHER + ]) + } + ] + }, + { + id: 'mother.reason', + type: 'TEXT', + required: true, + label: { + defaultMessage: 'Reason', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.mother.field.reason.label' + }, + conditionals: [ + { + type: 'HIDE', + conditional: field( + 'mother.detailsNotAvailable' + ).isUndefinedOrInArray(['false']) + } + ] + }, + ...appendConditionalsToFields({ + inputFields: [ + ...getPersonInputFields('mother'), + { + id: 'mother.previousBirths', + type: 'TEXT', + required: false, + label: { + defaultMessage: 'No. of previous births', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.mother.field.previousBirths.label' + } + } + ], + newConditionals: [ + { + type: 'HIDE', + conditional: and( + field('mother.detailsNotAvailable').isInArray(['true']), + field('informant.relation').isUndefinedOrNotInArray([ + InformantTypes.MOTHER + ]) + ) + } + ] + }) + ] + }, + + { + id: 'father', + title: { + defaultMessage: "Father's details", + description: 'Form section title for fathers details', + id: 'form.section.father.title' + }, + fields: [ + { + id: 'father.detailsNotAvailable', + type: 'CHECKBOX', + required: true, + label: { + defaultMessage: "Father's details not available", + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.father.field.detailsNotAvailable.label` + }, + conditionals: [ + { + type: 'HIDE', + conditional: field('informant.relation').isInArray([ + InformantTypes.FATHER + ]) + } + ] + }, + { + id: 'father.reason', + type: 'TEXT', + required: true, + label: { + defaultMessage: 'Reason', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.father.field.reason.label' + }, + conditionals: [ + { + type: 'HIDE', + conditional: field( + 'father.detailsNotAvailable' + ).isUndefinedOrInArray(['false']) + } + ] + }, + ...appendConditionalsToFields({ + inputFields: getPersonInputFields('father'), + newConditionals: [ + { + type: 'HIDE', + conditional: and( + field('father.detailsNotAvailable').isInArray(['true']), + field('informant.relation').isUndefinedOrNotInArray([ + InformantTypes.FATHER + ]) + ) + } + ] + }) + ] + }, + { + id: 'documents', + title: { + defaultMessage: 'Upload supporting documents', + description: 'Form section title for documents', + id: 'form.section.documents.title' + }, + fields: [ + { + id: `documents.helper`, + type: 'PARAGRAPH', + label: { + defaultMessage: 'The following documents are required', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.documents.field.helper.label` + }, + options: { fontVariant: 'reg16' } + }, + { + id: 'documents.proofOfBirth', + type: 'FILE', + required: false, + label: { + defaultMessage: 'Proof of birth', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.documents.field.proofOfBirth.label' + } + }, + { + id: 'documents.proofOfMother', + type: 'FILE', // @ToDo File upload with options + required: false, + label: { + defaultMessage: "Proof of mother's ID", + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.documents.field.proofOfMother.label' + } + }, + + { + id: 'documents.proofOfFather', + type: 'FILE', // @ToDo File upload with options + required: false, + label: { + defaultMessage: "Proof of father's ID", + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.documents.field.proofOfFather.label' + } + }, + + { + id: 'documents.proofOther', + type: 'FILE', // @ToDo File upload with options + required: false, + label: { + defaultMessage: 'Other', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.documents.field.proofOther.label' + } + } + ] + } + ] +}) diff --git a/src/form/v2/birth/forms/informant.ts b/src/form/v2/birth/forms/informant.ts new file mode 100644 index 000000000..8437c1121 --- /dev/null +++ b/src/form/v2/birth/forms/informant.ts @@ -0,0 +1,131 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { defineFormPage, TranslationConfig } from '@opencrvs/toolkit/events' +import { field } from '@opencrvs/toolkit/conditionals' +import { appendConditionalsToFields, createSelectOptions } from '../../utils' +import { getInformantFields } from '../../person' + +export const InformantTypes = { + MOTHER: 'MOTHER', + FATHER: 'FATHER', + OTHER: 'OTHER', + GRANDFATHER: 'GRANDFATHER', + GRANDMOTHER: 'GRANDMOTHER', + BROTHER: 'BROTHER', + SISTER: 'SISTER', + LEGAL_GUARDIAN: 'LEGAL_GUARDIAN' +} as const + +const informantMessageDescriptors = { + MOTHER: { + defaultMessage: 'Mother', + description: 'Label for option mother', + id: 'form.field.label.informantRelation.mother' + }, + FATHER: { + defaultMessage: 'Father', + description: 'Label for option father', + id: 'form.field.label.informantRelation.father' + }, + GRANDFATHER: { + defaultMessage: 'Grandfather', + description: 'Label for option Grandfather', + id: 'form.field.label.informantRelation.grandfather' + }, + GRANDMOTHER: { + defaultMessage: 'Grandmother', + description: 'Label for option Grandmother', + id: 'form.field.label.informantRelation.grandmother' + }, + BROTHER: { + defaultMessage: 'Brother', + description: 'Label for option brother', + id: 'form.field.label.informantRelation.brother' + }, + SISTER: { + defaultMessage: 'Sister', + description: 'Label for option Sister', + id: 'form.field.label.informantRelation.sister' + }, + LEGAL_GUARDIAN: { + defaultMessage: 'Legal guardian', + description: 'Label for option Legal Guardian', + id: 'form.field.label.informantRelation.legalGuardian' + }, + OTHER: { + defaultMessage: 'Someone else', + description: 'Label for option someone else', + id: 'form.field.label.informantRelation.others' + } +} satisfies Record + +const birthInformantTypeOptions = createSelectOptions( + InformantTypes, + informantMessageDescriptors +) + +export const informantPage = defineFormPage({ + id: 'informant', + title: { + defaultMessage: "Informant's details", + description: 'Form section title for informants details', + id: 'form.section.informant.title' + }, + fields: [ + { + id: 'informant.relation', + type: 'SELECT', + required: true, + label: { + defaultMessage: 'Relationship to child', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.informant.field.relation.label' + }, + options: birthInformantTypeOptions + }, + ...appendConditionalsToFields({ + inputFields: getInformantFields('informant'), + newConditionals: [ + { + type: 'HIDE', + conditional: field('informant.relation').isUndefinedOrInArray([ + InformantTypes.MOTHER, + InformantTypes.FATHER + ]) + } + ] + }), + { + id: 'informant.phoneNo', + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Phone number', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.informant.field.phoneNo.label' + } + }, + { + id: 'informant.email', + type: 'TEXT', + required: true, + label: { + defaultMessage: 'Email', + description: 'This is the label for the field', + id: 'event.birth.action.declare.form.section.informant.field.email.label' + }, + options: { + type: 'email' + } + } + ] +}) diff --git a/src/form/v2/birth/index.ts b/src/form/v2/birth/index.ts new file mode 100644 index 000000000..ce0ff48e6 --- /dev/null +++ b/src/form/v2/birth/index.ts @@ -0,0 +1,67 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { defineConfig } from '@opencrvs/toolkit/events' +import { + defineConditional, + eventHasAction, + not +} from '@opencrvs/toolkit/conditionals' +import { BIRTH_DECLARE_FORM } from './forms/declare' + +export const birthEvent = defineConfig({ + id: 'BIRTH', + label: { + defaultMessage: 'Birth declaration', + description: 'This is what this event is referred as in the system', + id: 'event.birth.label' + }, + summary: { + title: { + defaultMessage: '{applicant.firstname} {applicant.surname}', + description: 'This is the title of the summary', + id: 'event.birth.summary.title' + }, + fields: [] + }, + workqueues: [ + { + id: 'all', + title: { + defaultMessage: 'All birth events', + description: 'Label for all birth events workqueue', + id: 'event.birth.workqueue.all.label' + }, + fields: [ + { + id: 'child.firstname' + }, + { + id: 'child.surname' + } + ], + filters: [] + } + ], + actions: [ + { + type: 'DECLARE', + label: { + defaultMessage: 'Declare', + description: + 'This is shown as the action name anywhere the user can trigger the action from', + id: 'event.birth.action.declare.label' + }, + forms: [BIRTH_DECLARE_FORM], + allowedWhen: defineConditional(not(eventHasAction('DECLARE'))) + } + ] +}) diff --git a/src/form/v2/person/address.ts b/src/form/v2/person/address.ts new file mode 100644 index 000000000..1de79ce67 --- /dev/null +++ b/src/form/v2/person/address.ts @@ -0,0 +1,200 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { FieldConfig } from '@opencrvs/toolkit/events' +import { field } from '@opencrvs/toolkit/conditionals' +import { appendConditionalsToFields } from '../utils' + +export const getAddressFields = (person: string): FieldConfig[] => { + // @Todo: Same as mother or deseased + const prefix = `${person}.address` + + const genericAddressFields: FieldConfig[] = [ + { + id: `${prefix}.other.state`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'State', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.other.state.label` + } + }, + { + id: `${prefix}.other.district`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'District', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.other.district.label` + } + }, + { + id: `${prefix}.other.town`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'City / Town', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.other.town.label` + } + }, + { + id: `${prefix}.other.addressLine1`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Address Line 1', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.other.addressLine1.label` + } + }, + { + id: `${prefix}.other.addressLine2`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Address Line 2', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.other.addressLine2.label` + } + }, + { + id: `${prefix}.other.addressLine3`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Address Line 3', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.other.addressLine3.label` + } + }, + { + id: `${prefix}.other.zipCode`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Postcode / Zip', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.other.zipCode.label` + } + } + ] + + const farajalandAddressFields: FieldConfig[] = [ + { + id: `${prefix}.province`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'Province', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.province.label` + } + }, + { + id: `${prefix}.district`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'District', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.district.label` + } + }, + { + id: `${prefix}.town`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Town', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.town.label` + } + }, + { + id: `${prefix}.residentialArea`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Residential Area', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.residentialArea.label` + } + }, + { + id: `${prefix}.village`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Area / Ward / Mouja / Village', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.village.label` + } + }, + { + id: `${prefix}.number`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Number', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.number.label` + } + }, + { + id: `${prefix}.zipCode`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Postcode / Zip', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.zipCode.label` + } + } + ] + + return [ + { + id: `${prefix}.country`, + type: 'COUNTRY', + required: true, + label: { + defaultMessage: 'Country', + description: 'This is the label for the field', + id: `event.action.declare.form.section.person.field.address.country.label` + } + }, + ...appendConditionalsToFields({ + inputFields: genericAddressFields, + newConditionals: [ + { + type: 'HIDE', + conditional: field(`${person}.address.country`).isUndefinedOrInArray([ + 'FAR' + ]) + } + ] + }), + ...appendConditionalsToFields({ + inputFields: farajalandAddressFields, + newConditionals: [ + { + type: 'HIDE', + conditional: field( + `${person}.address.country` + ).isUndefinedOrNotInArray(['FAR']) + } + ] + }) + ] +} diff --git a/src/form/v2/person/index.ts b/src/form/v2/person/index.ts new file mode 100644 index 000000000..3855578f3 --- /dev/null +++ b/src/form/v2/person/index.ts @@ -0,0 +1,331 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { FieldConfig, TranslationConfig } from '@opencrvs/toolkit/events' +import { field } from '@opencrvs/toolkit/conditionals' +import { getAddressFields } from './address' +import { createSelectOptions } from '../utils' + +const IDTypes = { + NATIONAL_ID: 'NATIONAL_ID', + PASSPORT: 'PASSPORT', + BIRTH_REGISTRATION_NUMBER: 'BIRTH_REGISTRATION_NUMBER', + NONE: 'NONE' +} as const + +const MaritalStatus = { + SINGLE: 'SINGLE', + MARRIED: 'MARRIED', + WIDOWED: 'WIDOWED', + DIVORCED: 'DIVORCED', + SEPARATED: 'SEPARATED', + NOT_STATED: 'NOT_STATED' +} as const + +const EducationalAttainment = { + NO_SCHOOLING: 'NO_SCHOOLING', + PRIMARY_ISCED_1: 'PRIMARY_ISCED_1', + POST_SECONDARY_ISCED_4: 'POST_SECONDARY_ISCED_4', + FIRST_STAGE_TERTIARY_ISCED_5: 'FIRST_STAGE_TERTIARY_ISCED_5' +} as const + +const idTypeMessageDescriptors = { + NATIONAL_ID: { + defaultMessage: 'National ID', + description: 'Option for form field: Type of ID', + id: 'form.field.label.iDTypeNationalID' + }, + PASSPORT: { + defaultMessage: 'Passport', + description: 'Option for form field: Type of ID', + id: 'form.field.label.iDTypePassport' + }, + BIRTH_REGISTRATION_NUMBER: { + defaultMessage: 'Birth Registration Number', + description: 'Option for form field: Type of ID', + id: 'form.field.label.iDTypeBRN' + }, + NONE: { + defaultMessage: 'None', + description: 'Option for form field: Type of ID', + id: 'form.field.label.iDTypeNone' + } +} satisfies Record + +const maritalStatusMessageDescriptors = { + SINGLE: { + defaultMessage: 'Single', + description: 'Option for form field: Marital status', + id: 'form.field.label.maritalStatusSingle' + }, + MARRIED: { + defaultMessage: 'Married', + description: 'Option for form field: Marital status', + id: 'form.field.label.maritalStatusMarried' + }, + WIDOWED: { + defaultMessage: 'Widowed', + description: 'Option for form field: Marital status', + id: 'form.field.label.maritalStatusWidowed' + }, + DIVORCED: { + defaultMessage: 'Divorced', + description: 'Option for form field: Marital status', + id: 'form.field.label.maritalStatusDivorced' + }, + SEPARATED: { + defaultMessage: 'Separated', + description: 'Option for form field: Marital status', + id: 'form.field.label.maritalStatusSeparated' + }, + NOT_STATED: { + defaultMessage: 'Not stated', + description: 'Option for form field: Marital status', + id: 'form.field.label.maritalStatusNotStated' + } +} satisfies Record + +const educationalAttainmentMessageDescriptors = { + NO_SCHOOLING: { + defaultMessage: 'No schooling', + description: 'Option for form field: no education', + id: 'form.field.label.educationAttainmentNone' + }, + PRIMARY_ISCED_1: { + defaultMessage: 'Primary', + description: 'Option for form field: ISCED1 education', + id: 'form.field.label.educationAttainmentISCED1' + }, + POST_SECONDARY_ISCED_4: { + defaultMessage: 'Secondary', + description: 'Option for form field: ISCED4 education', + id: 'form.field.label.educationAttainmentISCED4' + }, + FIRST_STAGE_TERTIARY_ISCED_5: { + defaultMessage: 'Tertiary', + description: 'Option for form field: ISCED5 education', + id: 'form.field.label.educationAttainmentISCED5' + } +} satisfies Record + +const idTypeOptions = createSelectOptions(IDTypes, idTypeMessageDescriptors) + +const maritalStatusOptions = createSelectOptions( + MaritalStatus, + maritalStatusMessageDescriptors +) +const educationalAttainmentOptions = createSelectOptions( + EducationalAttainment, + educationalAttainmentMessageDescriptors +) + +const getIdFields = (person: string): FieldConfig[] => [ + { + id: `${person}.idType`, + type: 'SELECT', + required: true, + label: { + defaultMessage: 'Type of ID', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.idType.label` + }, + options: idTypeOptions + }, + { + id: `${person}.nid`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'ID Number', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.nid.label` + }, + conditionals: [ + { + type: 'HIDE', + conditional: field(`${person}.idType`).isUndefinedOrNotInArray([ + 'NATIONAL_ID' + ]) + } + ] + }, + { + id: `${person}.passport`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'ID Number', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.passport.label` + }, + conditionals: [ + { + type: 'HIDE', + conditional: field(`${person}.idType`).isUndefinedOrNotInArray([ + 'PASSPORT' + ]) + } + ] + }, + { + id: `${person}.brn`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'ID Number', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.brn.label` + }, + conditionals: [ + { + type: 'HIDE', + conditional: field(`${person}.idType`).isUndefinedOrNotInArray([ + 'BIRTH_REGISTRATION_NUMBER' + ]) + } + ] + } +] + +export const getInformantFields = (person: string): FieldConfig[] => [ + { + id: `${person}.firstname`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'First name(s)', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.firstname.label` + } + }, + { + id: `${person}.surname`, + type: 'TEXT', + required: true, + label: { + defaultMessage: 'Last name', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.surname.label` + } + }, + { + id: `${person}.dob`, + type: 'DATE', + required: true, + validation: [ + { + message: { + defaultMessage: 'Please enter a valid date', + description: 'This is the error message for invalid date', + id: `event.birth.action.declare.form.section.${person}.field.dob.error` + }, + validator: field(`${person}.dob`).isBeforeNow() + } + ], + label: { + defaultMessage: 'Date of birth', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.dob.label` + }, + conditionals: [ + { + type: 'HIDE', + conditional: field(`${person}.dobUnknown`).isEqualTo('true') + } + ] + }, + { + id: `${person}.dobUnknown`, + type: 'CHECKBOX', + required: true, + label: { + defaultMessage: 'Exact date of birth unknown', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.age.label` + } + }, + { + id: `${person}.age`, + type: 'TEXT', + required: true, + label: { + defaultMessage: `Age of ${person}`, + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.age.label` + }, + conditionals: [ + { + type: 'HIDE', + conditional: field(`${person}.dobUnknown`).isUndefinedOrInArray([ + 'false' + ]) + } + ] + }, + { + id: `${person}.nationality`, + type: 'COUNTRY', + required: true, + label: { + defaultMessage: 'Nationality', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.nationality.label` + } + }, + ...getIdFields(person), + { + id: `${person}.addressHelper`, + type: 'PARAGRAPH', + label: { + defaultMessage: 'Usual place of residence', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.addressHelper.label` + }, + options: { fontVariant: 'h2' } + }, + ...getAddressFields(person) +] + +export const getPersonInputFields = (person: string): FieldConfig[] => [ + ...getInformantFields(person), + { + id: `${person}.maritalStatus`, + type: 'SELECT', + required: false, + label: { + defaultMessage: 'Marital Status', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.maritalStatus.label` + }, + options: maritalStatusOptions + }, + { + id: `${person}.educationalAttainment`, + type: 'SELECT', + required: false, + label: { + defaultMessage: 'Level of education', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.educationalAttainment.label` + }, + options: educationalAttainmentOptions + }, + { + id: `${person}.occupation`, + type: 'TEXT', + required: false, + label: { + defaultMessage: 'Occupation', + description: 'This is the label for the field', + id: `event.birth.action.declare.form.section.${person}.field.occupation.label` + } + } +] diff --git a/src/form/v2/utils.ts b/src/form/v2/utils.ts new file mode 100644 index 000000000..1e06f1e4f --- /dev/null +++ b/src/form/v2/utils.ts @@ -0,0 +1,41 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { + FieldConditional, + FieldConfig, + SelectOption, + TranslationConfig +} from '@opencrvs/toolkit/events' + +export const appendConditionalsToFields = ({ + inputFields, + newConditionals +}: { + inputFields: FieldConfig[] + newConditionals: FieldConditional[] +}): FieldConfig[] => + inputFields.map((inputField) => ({ + ...inputField, + conditionals: [...(inputField.conditionals || []), ...newConditionals] + })) + +export const createSelectOptions = < + T extends Record, + M extends Record +>( + options: T, + messageDescriptors: M +): SelectOption[] => + Object.entries(options).map(([key, value]) => ({ + value, + label: messageDescriptors[key as keyof T] + })) diff --git a/yarn.lock b/yarn.lock index e558c8c1f..2c8c34061 100644 --- a/yarn.lock +++ b/yarn.lock @@ -790,10 +790,10 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@opencrvs/toolkit@0.0.13-events": - version "0.0.13-events" - resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-0.0.13-events.tgz#7b2c1fd73bba74c2cb6292a0ce4c6659e9711ee3" - integrity sha512-sdST83RXETbP6e1ETtM7VSUP2W+sYpz/DU9a8c036QlJ67I9306nuIqLePGU62pPggI8sI63fa2PL38bF3hAlg== +"@opencrvs/toolkit@0.0.14-events": + version "0.0.14-events" + resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-0.0.14-events.tgz#f99b2dc14c30ec925f373e19eea5ea731abb79b9" + integrity sha512-ZsSmmgB9anSVp2hxIquU/wfHVP8s//RydEVERmn7Dpr3S/6bVRsLS27P02lioJmCBMbEf8cNgstyMlgaE9XpAg== dependencies: ajv "^8.17.1"