From 496cce259b5fde589afd2c2c22a456000f4cce14 Mon Sep 17 00:00:00 2001 From: Pyry Rouvila Date: Fri, 27 Oct 2023 09:43:37 +0300 Subject: [PATCH] feat: inform client which administrative level is leaf level (#765) --- src/form/addresses/address-fields.ts | 311 ++++++++++++++++----------- src/form/addresses/index.ts | 3 +- src/form/types/types.ts | 3 + src/form/types/validators.ts | 2 +- src/utils/address-utils.ts | 120 +++++++---- 5 files changed, 272 insertions(+), 167 deletions(-) diff --git a/src/form/addresses/address-fields.ts b/src/form/addresses/address-fields.ts index 5e7780ea1..40193ec53 100644 --- a/src/form/addresses/address-fields.ts +++ b/src/form/addresses/address-fields.ts @@ -73,12 +73,21 @@ export const getXAddressSameAsY = ( // A select field that uses the loaded administrative location levels from Humdata // We recommend that you do not edit this function -export function getAddressLocationSelect( - section: string, - location: string, - useCase: string, - locationIndex?: number -): SerializedFormField { +export function getAddressLocationSelect({ + section, + location, + useCase, + fhirLineArrayPosition, + isLowestAdministrativeLevel +}: { + section: string + location: string + useCase: string + /** Position where the location gets mapped into within a fhir.Address line-array */ + fhirLineArrayPosition?: number + /** If the structure the smallest possible level. Allows saving fhir.Address.partOf */ + isLowestAdministrativeLevel?: boolean +}): SerializedFormField { const fieldName = `${location}${sentenceCase(useCase)}${sentenceCase( section )}` @@ -113,14 +122,15 @@ export function getAddressLocationSelect( useCase as EventLocationAddressCases ) : getAddressConditionals(section, location, useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'SELECT_WITH_DYNAMIC_OPTIONS', + type: 'SELECT_WITH_DYNAMIC_OPTIONS', location, useCase, fieldName, - locationIndex - ) + fhirLineArrayPosition, + isLowestAdministrativeLevel + }) } } @@ -132,35 +142,78 @@ function getAdminLevelSelects( ): SerializedFormField[] { switch (ADMIN_LEVELS) { case 1: - return [getAddressLocationSelect(section, 'state', useCase)] + return [ + getAddressLocationSelect({ + section, + location: 'state', + useCase, + isLowestAdministrativeLevel: true + }) + ] case 2: return [ - getAddressLocationSelect(section, 'state', useCase), - getAddressLocationSelect(section, 'district', useCase) + getAddressLocationSelect({ section, location: 'state', useCase }), + getAddressLocationSelect({ + section, + location: 'district', + useCase, + isLowestAdministrativeLevel: true + }) ] case 3: return [ - getAddressLocationSelect(section, 'state', useCase), - getAddressLocationSelect(section, 'district', useCase), - getAddressLocationSelect(section, 'locationLevel3', useCase, 10) + getAddressLocationSelect({ section, location: 'state', useCase }), + getAddressLocationSelect({ section, location: 'district', useCase }), + getAddressLocationSelect({ + section, + location: 'locationLevel3', + useCase, + fhirLineArrayPosition: 10, + isLowestAdministrativeLevel: true + }) ] case 4: return [ - getAddressLocationSelect(section, 'state', useCase), - getAddressLocationSelect(section, 'district', useCase), - getAddressLocationSelect(section, 'locationLevel3', useCase, 10), - getAddressLocationSelect(section, 'locationLevel4', useCase, 11) + getAddressLocationSelect({ section, location: 'state', useCase }), + getAddressLocationSelect({ section, location: 'district', useCase }), + getAddressLocationSelect({ + section, + location: 'locationLevel3', + useCase, + fhirLineArrayPosition: 10 + }), + getAddressLocationSelect({ + section, + location: 'locationLevel4', + useCase, + fhirLineArrayPosition: 11, + isLowestAdministrativeLevel: true + }) ] case 5: return [ - getAddressLocationSelect(section, 'state', useCase), - getAddressLocationSelect(section, 'district', useCase), - getAddressLocationSelect(section, 'locationLevel3', useCase, 10), - getAddressLocationSelect(section, 'locationLevel4', useCase, 11), - getAddressLocationSelect(section, 'locationLevel5', useCase, 12) + getAddressLocationSelect({ section, location: 'state', useCase }), + getAddressLocationSelect({ section, location: 'district', useCase }), + getAddressLocationSelect({ + section, + location: 'locationLevel3', + useCase, + fhirLineArrayPosition: 10 + }), + getAddressLocationSelect({ + section, + location: 'locationLevel4', + useCase, + fhirLineArrayPosition: 11 + }), + getAddressLocationSelect({ + section, + location: 'locationLevel5', + useCase, + fhirLineArrayPosition: 12, + isLowestAdministrativeLevel: true + }) ] - default: - return [getAddressLocationSelect(section, 'state', useCase)] } } @@ -231,13 +284,13 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'country', useCase) : getAddressConditionals(section, 'country', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'SELECT_WITH_OPTIONS', - 'country', // Maps form value to FHIR prop. Use empty string for FHIR Address line index + type: 'SELECT_WITH_OPTIONS', + location: 'country', useCase, - `country${sentenceCase(useCase)}${sentenceCase(section)}` - ) + fieldName: `country${sentenceCase(useCase)}${sentenceCase(section)}` + }) }, // Required // Select fields are added for each administrative location level from Humdata ...getAdminLevelSelects(section, useCase), // Required @@ -261,14 +314,14 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'ruralOrUrban', useCase) : getAddressConditionals(section, 'ruralOrUrban', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'RADIO_GROUP', - '', // No FHIR prop exists for this custom address prop. Use empty string for FHIR Address line index + type: 'RADIO_GROUP', + location: '', useCase, - `country${sentenceCase(useCase)}${sentenceCase(section)}`, - 5 // The selected index in the FHIR Address line array to store this value - ) + fieldName: `country${sentenceCase(useCase)}${sentenceCase(section)}`, + fhirLineArrayPosition: 5 // The selected index in the FHIR Address line array to store this value + }) }, { name: `city${sentenceCase(useCase)}${sentenceCase(section)}`, @@ -288,13 +341,13 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'urban', useCase) : getAddressConditionals(section, 'urban', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - 'city', + type: 'TEXT', + location: 'city', useCase, - `city${sentenceCase(useCase)}${sentenceCase(section)}` - ) + fieldName: `city${sentenceCase(useCase)}${sentenceCase(section)}` + }) }, { name: `addressLine1UrbanOption${sentenceCase(useCase)}${sentenceCase( @@ -316,16 +369,16 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'urban', useCase) : getAddressConditionals(section, 'urban', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - '', + type: 'TEXT', + location: '', useCase, - `addressLine1UrbanOption${sentenceCase(useCase)}${sentenceCase( - section - )}`, - 2 - ) + fieldName: `addressLine1UrbanOption${sentenceCase( + useCase + )}${sentenceCase(section)}`, + fhirLineArrayPosition: 2 + }) }, { name: `addressLine2UrbanOption${sentenceCase(useCase)}${sentenceCase( @@ -347,16 +400,16 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'urban', useCase) : getAddressConditionals(section, 'urban', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - '', + type: 'TEXT', + location: '', useCase, - `addressLine2UrbanOption${sentenceCase(useCase)}${sentenceCase( - section - )}`, - 1 - ) + fieldName: `addressLine2UrbanOption${sentenceCase( + useCase + )}${sentenceCase(section)}`, + fhirLineArrayPosition: 1 + }) }, { name: `addressLine3UrbanOption${sentenceCase(useCase)}${sentenceCase( @@ -378,16 +431,16 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'urban', useCase) : getAddressConditionals(section, 'urban', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - '', + type: 'TEXT', + location: '', useCase, - `addressLine3UrbanOption${sentenceCase(useCase)}${sentenceCase( - section - )}`, - 0 - ) + fieldName: `addressLine3UrbanOption${sentenceCase( + useCase + )}${sentenceCase(section)}`, + fhirLineArrayPosition: 0 + }) }, { name: `postalCode${sentenceCase(useCase)}${sentenceCase(section)}`, @@ -407,13 +460,13 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'urban', useCase) : getAddressConditionals(section, 'urban', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - 'postalCode', + type: 'TEXT', + location: 'postalCode', useCase, - `postalCode${sentenceCase(useCase)}${sentenceCase(section)}` - ) + fieldName: `postalCode${sentenceCase(useCase)}${sentenceCase(section)}` + }) }, { name: `addressLine1RuralOption${sentenceCase(useCase)}${sentenceCase( @@ -435,16 +488,16 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'rural', useCase) : getAddressConditionals(section, 'rural', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - '', + type: 'TEXT', + location: '', useCase, - `addressLine1RuralOption${sentenceCase(useCase)}${sentenceCase( - section - )}`, - 4 - ) + fieldName: `addressLine1RuralOption${sentenceCase( + useCase + )}${sentenceCase(section)}`, + fhirLineArrayPosition: 4 + }) }, // INTERNATIONAL ADDRESSES ARE SUPPLIED BECAUSE INFORMANTS & CITIZENS MAY LIVE ABROAD & REGISTER AN EVENT AT ONE OF YOUR FOREIGN EMBASSIES // SOMETIMES THIS IS ALSO REQUIRED FOR DIPLOMATIC REASONS OR FOR MILITARY FORCES @@ -468,13 +521,15 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'international', useCase) : getAddressConditionals(section, 'international', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - 'state', + type: 'TEXT', + location: 'state', useCase, - `internationalState${sentenceCase(useCase)}${sentenceCase(section)}` - ) + fieldName: `internationalState${sentenceCase(useCase)}${sentenceCase( + section + )}` + }) }, { name: `internationalDistrict${sentenceCase(useCase)}${sentenceCase( @@ -496,13 +551,15 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'international', useCase) : getAddressConditionals(section, 'international', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - 'district', + type: 'TEXT', + location: 'district', useCase, - `internationalDistrict${sentenceCase(useCase)}${sentenceCase(section)}` - ) + fieldName: `internationalDistrict${sentenceCase(useCase)}${sentenceCase( + section + )}` + }) }, { name: `internationalCity${sentenceCase(useCase)}${sentenceCase(section)}`, @@ -522,13 +579,15 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'international', useCase) : getAddressConditionals(section, 'international', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - 'city', + type: 'TEXT', + location: 'city', useCase, - `internationalCity${sentenceCase(useCase)}${sentenceCase(section)}` - ) + fieldName: `internationalCity${sentenceCase(useCase)}${sentenceCase( + section + )}` + }) }, { name: `internationalAddressLine1${sentenceCase(useCase)}${sentenceCase( @@ -550,16 +609,16 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'international', useCase) : getAddressConditionals(section, 'international', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - '', + type: 'TEXT', + location: '', useCase, - `internationalAddressLine1${sentenceCase(useCase)}${sentenceCase( - section - )}`, - 6 - ) + fieldName: `internationalAddressLine1${sentenceCase( + useCase + )}${sentenceCase(section)}`, + fhirLineArrayPosition: 6 + }) }, { name: `internationalAddressLine2${sentenceCase(useCase)}${sentenceCase( @@ -581,16 +640,16 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'international', useCase) : getAddressConditionals(section, 'international', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - '', + type: 'TEXT', + location: '', useCase, - `internationalAddressLine2${sentenceCase(useCase)}${sentenceCase( - section - )}`, - 7 - ) + fieldName: `internationalAddressLine2${sentenceCase( + useCase + )}${sentenceCase(section)}`, + fhirLineArrayPosition: 7 + }) }, { name: `internationalAddressLine3${sentenceCase(useCase)}${sentenceCase( @@ -612,16 +671,16 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'international', useCase) : getAddressConditionals(section, 'international', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - '', + type: 'TEXT', + location: '', useCase, - `internationalAddressLine3${sentenceCase(useCase)}${sentenceCase( - section - )}`, - 8 - ) + fieldName: `internationalAddressLine3${sentenceCase( + useCase + )}${sentenceCase(section)}`, + fhirLineArrayPosition: 8 + }) }, { name: `internationalPostalCode${sentenceCase(useCase)}${sentenceCase( @@ -643,15 +702,15 @@ export function getAddressFields( conditionals: isUseCaseForPlaceOfEvent(useCase) ? getPlaceOfEventConditionals(section, 'international', useCase) : getAddressConditionals(section, 'international', useCase), - mapping: getMapping( + mapping: getMapping({ section, - 'TEXT', - 'postalCode', + type: 'TEXT', + location: 'postalCode', useCase, - `internationalPostalCode${sentenceCase(useCase)}${sentenceCase( - section - )}` - ) + fieldName: `internationalPostalCode${sentenceCase( + useCase + )}${sentenceCase(section)}` + }) } ] } diff --git a/src/form/addresses/index.ts b/src/form/addresses/index.ts index 01c6a17cc..ca63ae060 100644 --- a/src/form/addresses/index.ts +++ b/src/form/addresses/index.ts @@ -24,6 +24,7 @@ import { AddressCases, AddressCopyConfigCases, AddressSubsections, + AdministrativeLevel, EventLocationAddressCases, IAddressConfiguration } from '../types/types' @@ -34,7 +35,7 @@ import { // THEREFORE OUR ADMIN_LEVELS PROPERTY IS 2. // YOU CAN SET UP TO 5 SUPPORTED ADMINISTRATIVE LEVELS. -export const ADMIN_LEVELS: Number = 2 +export const ADMIN_LEVELS: AdministrativeLevel = 2 // ADDRESSES TAKE UP A LOT OF REPEATED CODE IN THE FORMS, MAKING THE BIRTH, MARRIAGE AND DEATH FORM CODE LONG AND DIFFICULT TO READ // THEREFORE WE DECORATE THE ADDRESSES DYNAMICALLY TO SECTIONS OF THE FORM USING THIS CONFIGURATION CONSTANT diff --git a/src/form/types/types.ts b/src/form/types/types.ts index ecc6351f3..45fe3dfb6 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -68,6 +68,7 @@ type IAddressLineMapper = { transformedFieldName?: string useCase?: string lineNumber?: number + isLowestAdministrativeLevel?: boolean } export type IQueryMapper = { @@ -930,3 +931,5 @@ export type AllowedAddressConfigurations = { yComparisonSection?: string conditionalCase?: string } + +export type AdministrativeLevel = 1 | 2 | 3 | 4 | 5 diff --git a/src/form/types/validators.ts b/src/form/types/validators.ts index bac46155b..34a690775 100644 --- a/src/form/types/validators.ts +++ b/src/form/types/validators.ts @@ -11,7 +11,7 @@ import * as customValidators from '../common/custom-validation-conditionals/custom-validators' -/** Validators that are built in to core. You can create your own ones, or override these ones in `src/features/config/form/validators.ts` */ +/** Validators that are built in to core. You can create your own ones, or override these ones in the custom validators file imported above */ type CoreValidator = | 'requiredBasic' | 'requiredSymbol' diff --git a/src/utils/address-utils.ts b/src/utils/address-utils.ts index 38e0c4213..c839b5d43 100644 --- a/src/utils/address-utils.ts +++ b/src/utils/address-utils.ts @@ -668,21 +668,21 @@ function getTemplateMapping( location: string, useCase: string, fieldName: string, - locationIndex?: number + fhirLineArrayPosition?: number ): IHandlebarTemplates { return isUseCaseForPlaceOfEvent(useCase) - ? locationIndex + ? fhirLineArrayPosition ? { fieldName, operation: 'eventLocationAddressLineTemplateTransformer', - parameters: [locationIndex, fieldName, location] + parameters: [fhirLineArrayPosition, fieldName, location] } : { fieldName, operation: 'eventLocationAddressFHIRPropertyTemplateTransformer', parameters: [location] } - : locationIndex + : fhirLineArrayPosition ? { fieldName, operation: 'addressLineTemplateTransformer', @@ -690,7 +690,7 @@ function getTemplateMapping( useCase.toUpperCase() === 'PRIMARY' ? AddressCases.PRIMARY_ADDRESS : AddressCases.SECONDARY_ADDRESS, - locationIndex, + fhirLineArrayPosition, fieldName, location ] @@ -708,27 +708,40 @@ function getTemplateMapping( } // You should never need to edit this function. If there is a bug here raise an issue in [Github](https://github.com/opencrvs/opencrvs-farajaland) -function getMutationMapping( - type: - | 'TEXT' - | 'RADIO_GROUP' - | 'SELECT_WITH_OPTIONS' - | 'SELECT_WITH_DYNAMIC_OPTIONS', - location: string, - useCase: string, - locationIndex?: number -): IMutationMapper { +function getMutationMapping({ + location, + useCase, + fhirLineArrayPosition, + isLowestAdministrativeLevel +}: { + location: string + useCase: string + fhirLineArrayPosition?: number + isLowestAdministrativeLevel?: boolean +}): IMutationMapper { return isUseCaseForPlaceOfEvent(useCase) - ? locationIndex || locationIndex === 0 + ? fhirLineArrayPosition || fhirLineArrayPosition === 0 ? { operation: 'eventLocationMutationTransformer', - parameters: [{ useCase, lineNumber: locationIndex }] + parameters: [ + { + useCase, + lineNumber: fhirLineArrayPosition, + isLowestAdministrativeLevel + } + ] } : { operation: 'eventLocationMutationTransformer', - parameters: [{ useCase, transformedFieldName: location }] + parameters: [ + { + useCase, + transformedFieldName: location, + isLowestAdministrativeLevel + } + ] } - : locationIndex || locationIndex === 0 + : fhirLineArrayPosition || fhirLineArrayPosition === 0 ? { operation: 'addressMutationTransformer', parameters: [ @@ -737,7 +750,8 @@ function getMutationMapping( useCase.toUpperCase() === 'PRIMARY' ? AddressCases.PRIMARY_ADDRESS : AddressCases.SECONDARY_ADDRESS, - lineNumber: locationIndex + lineNumber: fhirLineArrayPosition, + isLowestAdministrativeLevel } ] } @@ -749,7 +763,8 @@ function getMutationMapping( useCase.toUpperCase() === 'PRIMARY' ? AddressCases.PRIMARY_ADDRESS : AddressCases.SECONDARY_ADDRESS, - transformedFieldName: location + transformedFieldName: location, + isLowestAdministrativeLevel } ] } @@ -766,7 +781,7 @@ function getQueryMapping( location: string, useCase: string, fieldName: string, - locationIndex?: number + fhirLineArrayPosition?: number ): IQueryMapper { return isUseCaseForPlaceOfEvent(useCase) ? { @@ -789,7 +804,10 @@ function getQueryMapping( fieldName === `internationalCity${sentenceCase(useCase)}${sentenceCase(section)}` ? [ - { transformedFieldName: location, lineNumber: locationIndex }, + { + transformedFieldName: location, + lineNumber: fhirLineArrayPosition + }, { fieldsToIgnoreForLocalAddress: [ `internationalDistrict${sentenceCase( @@ -814,9 +832,9 @@ function getQueryMapping( ] } ] - : [{ lineNumber: locationIndex }] + : [{ lineNumber: fhirLineArrayPosition }] } - : locationIndex || locationIndex === 0 + : fhirLineArrayPosition || fhirLineArrayPosition === 0 ? { operation: 'addressQueryTransformer', parameters: [ @@ -825,7 +843,7 @@ function getQueryMapping( useCase.toUpperCase() === 'PRIMARY' ? AddressCases.PRIMARY_ADDRESS : AddressCases.SECONDARY_ADDRESS, - lineNumber: locationIndex + lineNumber: fhirLineArrayPosition } ] } @@ -844,42 +862,66 @@ function getQueryMapping( } // You should never need to edit this function. If there is a bug here raise an issue in [Github](https://github.com/opencrvs/opencrvs-farajaland) -export function getMapping( - section: string, +export function getMapping({ + section, + type, + location, + useCase, + fieldName, + fhirLineArrayPosition, + isLowestAdministrativeLevel +}: { + section: string type: | 'TEXT' | 'RADIO_GROUP' | 'SELECT_WITH_OPTIONS' - | 'SELECT_WITH_DYNAMIC_OPTIONS', - location: string, // used to filter offline locations and for FHIR props - use empty string for address lines - useCase: string, - fieldName: string, - locationIndex?: number -): IFormFieldMapping { + | 'SELECT_WITH_DYNAMIC_OPTIONS' + location: string + useCase: string + fieldName: string + fhirLineArrayPosition?: number + isLowestAdministrativeLevel?: boolean +}): IFormFieldMapping { if (type !== 'RADIO_GROUP') { return { - template: getTemplateMapping(location, useCase, fieldName, locationIndex), - mutation: getMutationMapping(type, location, useCase, locationIndex), + template: getTemplateMapping( + location, + useCase, + fieldName, + fhirLineArrayPosition + ), + mutation: getMutationMapping({ + location, + useCase, + fhirLineArrayPosition, + isLowestAdministrativeLevel + }), query: getQueryMapping( section, type, location, useCase, fieldName, - locationIndex + fhirLineArrayPosition ) } } else { // Radio Groups in addresses have no need for certificate template return { - mutation: getMutationMapping(type, location, useCase, locationIndex), + mutation: getMutationMapping({ + location, + useCase, + fhirLineArrayPosition, + isLowestAdministrativeLevel + }), query: getQueryMapping( section, type, location, useCase, fieldName, - locationIndex + fhirLineArrayPosition ) } }