Skip to content

Commit

Permalink
feat: Add constants.humanName to allow countries to have custom full …
Browse files Browse the repository at this point in the history
…name format (#7957)

* Add humanName to constants.ts

To allow countries to have custom ordering for full names

#6830

* Use constants.humanName on the UserList.tsx comp

To ensure that we get the format from the country-config

#6830

* Create a utility function getLocalisedname()

To make the name more usable we had to extract the name formatting logic into it's own function.

#6830

* Use getLocalisedName on the UserList component

#6830

* Use getLocalisedName on the UserAudit comp

Replace older logic to get the name which was based on an assumption that we support names in multiple languages

#6830

* refactor: use getLocalisedName() @ InProgress.tsx

We've found cleaner way to make the rendered name customizable for each country through client copy from country-config

#6830

* Refactor the work queues to use getLocalisedName()

We need to update all the places where a citizen's name is being referenced to show it in the format that the country chooses

#6830

* Record changes in the CHANGELOG for this PR

#6830

* Use humanName for other search/transformer.ts

#6830

* Fix type error 4 the findSavedReference()

* Fix faling unit test after using humanName

The expected name should match the format for the given country.

#6830

* Add the correct local for intlBangla

#6830

* Pass intl object for myDrafts transformDraftContent

#6830
  • Loading branch information
Siyasanga authored and jamil314 committed Feb 3, 2025
1 parent 41ff313 commit a25ba87
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 161 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
- A new GraphQL mutation `upsertRegistrationIdentifier` is added to allow updating the patient identifiers of a registration record such as NID [#8034](https://github.com/opencrvs/opencrvs-core/pull/8034)
- A new GraphQL mutation `updateField` is added to allow updating any field in a record [#8291](https://github.com/opencrvs/opencrvs-core/pull/8291)
- Updated GraphQL mutation `confirmRegistration` to allow adding a `comment` for record audit [#8197](https://github.com/opencrvs/opencrvs-core/pull/8197)
- Introduced a new customisable UI component: Banner [#8276](https://github.com/opencrvs/opencrvs-core/issues/8276)
- Allow countries to customise the format of the full name in the sytem for `sytem users` and `citizens` e.g `{LastName} {MiddleName} {Firstname}`, in any case where one of the name is not provided e.g no `MiddleName`, we'll simply render e.g `{LastName} {FirstName}` without any extra spaces if that's the order set in `country-config`. [#6830](https://github.com/opencrvs/opencrvs-core/issues/6830)

### Improvements

Expand Down
6 changes: 6 additions & 0 deletions packages/client/src/i18n/messages/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ interface IConstantsMessages
refresh: MessageDescriptor
duplicateOf: MessageDescriptor
matchedTo: MessageDescriptor
humanName: MessageDescriptor
}
const messagesToDefine: IConstantsMessages = {
action: {
Expand Down Expand Up @@ -978,6 +979,11 @@ const messagesToDefine: IConstantsMessages = {
defaultMessage: `{registrationTargetDays} days - 1 year`,
description: `Label for registrations within {registrationTargetDays} days to 1 year`,
id: 'constants.withinTargetDaysTo1Year'
},
humanName: {
defaultMessage: `{firstName} {middleName} {lastName}`,
description: 'A localized order of the full name',
id: 'constants.humanName'
}
}
export const constantsMessages: Record<
Expand Down
49 changes: 20 additions & 29 deletions packages/client/src/search/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import { ITaskHistory } from '@client/declarations'
import { EMPTY_STRING, LANG_EN } from '@client/utils/constants'
import { createNamesMap } from '@client/utils/data-formatting'
import { EMPTY_STRING } from '@client/utils/constants'
import { getLocalisedName } from '@client/utils/data-formatting'
import { formatLongDate } from '@client/utils/date-formatting'
import {
EventSearchSet,
Expand All @@ -21,7 +21,6 @@ import {
import type {
GQLBirthEventSearchSet,
GQLDeathEventSearchSet,
GQLHumanName,
GQLMarriageEventSearchSet,
GQLRegStatus
} from '@client/utils/gateway-deprecated-do-not-use'
Expand Down Expand Up @@ -60,42 +59,39 @@ export const transformData = (
let birthReg
let deathReg
let marriageReg
let names
let groomNames
let brideNames
let name
let groomName
let brideName
let dateOfEvent
let mergedMarriageName
const assignedReg = reg

if (reg.registration) {
if (isBirthEvent(reg)) {
birthReg = reg
names = (birthReg.childName as GQLHumanName[]) || []
name = birthReg.childName
? getLocalisedName(intl, birthReg.childName[0] as HumanName)
: EMPTY_STRING
dateOfEvent = birthReg.dateOfBirth
} else if (isDeathEvent(reg)) {
deathReg = reg
names = (deathReg.deceasedName as GQLHumanName[]) || []
name = deathReg.deceasedName
? getLocalisedName(intl, deathReg.deceasedName[0] as HumanName)
: EMPTY_STRING
dateOfEvent = deathReg && deathReg.dateOfDeath
} else if (isMarriageEvent(reg)) {
marriageReg = reg
groomNames =
(marriageReg && (marriageReg.groomName as GQLHumanName[])) || []
brideNames =
(marriageReg && (marriageReg.brideName as GQLHumanName[])) || []
groomName = reg.groomName
? getLocalisedName(intl, reg.groomName[0] as HumanName)
: EMPTY_STRING
brideName = reg.brideName
? getLocalisedName(intl, reg.brideName[0] as HumanName)
: EMPTY_STRING

const groomName =
(createNamesMap(groomNames as HumanName[])[locale] as string) ||
(createNamesMap(groomNames as HumanName[])[LANG_EN] as string)
const brideName =
(createNamesMap(brideNames as HumanName[])[locale] as string) ||
(createNamesMap(brideNames as HumanName[])[LANG_EN] as string)

mergedMarriageName =
name =
brideName && groomName
? `${groomName} & ${brideName}`
: brideName || groomName || EMPTY_STRING

dateOfEvent = marriageReg && marriageReg.dateOfMarriage
dateOfEvent = marriageReg && reg.dateOfMarriage
}
}
const status =
Expand All @@ -104,12 +100,7 @@ export const transformData = (

return {
id: assignedReg.id,
name:
assignedReg.type === EventType.Marriage
? mergedMarriageName
: (createNamesMap(names as HumanName[])[locale] as string) ||
(createNamesMap(names as HumanName[])[LANG_EN] as string) ||
'',
name,
dob:
(birthReg?.dateOfBirth?.length &&
formatLongDate(birthReg.dateOfBirth, locale)) ||
Expand Down
16 changes: 16 additions & 0 deletions packages/client/src/utils/data-formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import { getDefaultLanguage } from '@client/i18n/utils'
import type { GQLComment } from '@client/utils/gateway-deprecated-do-not-use'
import { HumanName } from './gateway'
import { constantsMessages } from '@client/i18n/messages'
import { IntlShape } from 'react-intl'

interface INamesMap {
[key: string]: string
Expand Down Expand Up @@ -81,3 +83,17 @@ export const mergeArraysRemovingEmptyStrings = (
export function getPercentage(total: number, current: number) {
return current === 0 || total === 0 ? 0 : (current / total) * 100
}

export function getLocalisedName(
intl: IntlShape,
nameObject: HumanName
): string {
return intl
.formatMessage(constantsMessages.humanName, {
firstName: nameObject.firstNames,
middleName: nameObject.middleName,
lastName: nameObject.familyName
})
.replace(/\s+/g, ' ') // Remove extra spaces
.trim()
}
85 changes: 54 additions & 31 deletions packages/client/src/utils/draftUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,49 @@ import type {
GQLBirthEventSearchSet,
GQLDeathEventSearchSet
} from '@client/utils/gateway-deprecated-do-not-use'
import { createIntl, createIntlCache } from 'react-intl'

const cache = createIntlCache()
const intlEngish = createIntl(
{
locale: 'en',
messages: {}
},
cache
)
const intlBangla = createIntl(
{
locale: 'bn',
messages: {}
},
cache
)

describe('draftUtils tests', () => {
describe('getDraftInformantFullName()', () => {
describe('Birth event', () => {
it('Returns child english name properly', () => {
expect(
getDeclarationFullName({
id: '7b57d8f9-4d2d-4f12-8d0a-b042fe14f3d4',
data: {
child: {
firstNames: 'মুশ্রাফুল',
familyName: 'হক',
firstNamesEng: 'Mushraful',
familyNameEng: 'Hoque'
}
getDeclarationFullName(
{
id: '7b57d8f9-4d2d-4f12-8d0a-b042fe14f3d4',
data: {
child: {
firstNames: 'মুশ্রাফুল',
familyName: 'হক',
firstNamesEng: 'Mushraful',
familyNameEng: 'Hoque'
}
},
event: EventType.Birth,
savedOn: 1558037863335,
modifiedOn: 1558037867987
},
event: EventType.Birth,
savedOn: 1558037863335,
modifiedOn: 1558037867987
})
intlEngish
)
).toBe('Mushraful Hoque')
})
it('Returns child bangla name properly', () => {
it('Returns child English name properly even though localed is Bangla', () => {
expect(
getDeclarationFullName(
{
Expand All @@ -55,30 +75,33 @@ describe('draftUtils tests', () => {
savedOn: 1558037863335,
modifiedOn: 1558037867987
},
'bn'
intlBangla
)
).toBe('হক')
).toBe('Mushraful Hoque')
})
})
describe('Death event', () => {
it('Returns deceased english name properly', () => {
expect(
getDeclarationFullName({
id: '7b57d8f9-4d2d-4f12-8d0a-b042fe14f3d4',
data: {
deceased: {
firstNames: 'মুশ্রাফুল',
familyName: 'হক',
familyNameEng: 'Hoque'
}
getDeclarationFullName(
{
id: '7b57d8f9-4d2d-4f12-8d0a-b042fe14f3d4',
data: {
deceased: {
firstNames: 'মুশ্রাফুল',
familyName: 'হক',
familyNameEng: 'Hoque'
}
},
event: EventType.Death,
savedOn: 1558037863335,
modifiedOn: 1558037867987
},
event: EventType.Death,
savedOn: 1558037863335,
modifiedOn: 1558037867987
})
intlBangla
)
).toBe('Hoque')
})
it('Returns child bangla name properly', () => {
it('Returns child English name properly even when the current locale is Bangla', () => {
expect(
getDeclarationFullName(
{
Expand All @@ -95,9 +118,9 @@ describe('draftUtils tests', () => {
savedOn: 1558037863335,
modifiedOn: 1558037867987
},
'bn'
intlEngish
)
).toBe('মুশ্রাফুল হক')
).toBe('Mushraful Hoque')
})
})
})
Expand Down
87 changes: 32 additions & 55 deletions packages/client/src/utils/draftUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import {
IPrintableDeclaration,
SUBMISSION_STATUS
} from '@client/declarations'
import { IFormSectionData } from '@client/forms'
import { EMPTY_STRING } from '@client/utils/constants'
import { EventType, History, RegStatus } from '@client/utils/gateway'
import type {
GQLBirthEventSearchSet,
Expand All @@ -24,67 +22,46 @@ import type {
} from '@client/utils/gateway-deprecated-do-not-use'
import { getEvent } from '@client/views/PrintCertificate/utils'
import { includes } from 'lodash'
import { EMPTY_STRING } from '@client/utils/constants'
import { getLocalisedName } from './data-formatting'
import { IntlShape } from 'react-intl'

const getEngName = (
sectionData: IFormSectionData,
lastNameFirst: boolean
): string => {
if (lastNameFirst) {
return `${sectionData.familyNameEng ?? ''} ${
sectionData.firstNamesEng ?? ''
}`
}
return [
sectionData.firstNamesEng,
sectionData.middleNameEng,
sectionData.familyNameEng
]
.filter(Boolean)
.join(' ')
.trim()
}

const getOtherName = (sectionData: IFormSectionData): string => {
return [
sectionData.firstNames,
sectionData.middleName,
sectionData.familyName
]
.filter(Boolean)
.join(' ')
.trim()
}

const getFullName = (
sectionData: IFormSectionData,
language = 'en',
lastNameFirst = false
): string => {
if (!sectionData) {
return EMPTY_STRING
}
if (language === 'en') {
return getEngName(sectionData, lastNameFirst)
}
return getOtherName(sectionData) || getEngName(sectionData, lastNameFirst)
}

/*
* lastNameFirst needs to be removed in #4464
*/
export const getDeclarationFullName = (
draft: IDeclaration,
language?: string,
lastNameFirst?: boolean
intl: IntlShape
) => {
switch (draft.event) {
case EventType.Birth:
return getFullName(draft.data.child, language, lastNameFirst)
return draft.data.child
? getLocalisedName(intl, {
firstNames: draft.data.child.firstNamesEng as string,
middleName: draft.data.child.middleNameEng as string,
familyName: draft.data.child.familyNameEng as string
})
: EMPTY_STRING
case EventType.Death:
return getFullName(draft.data.deceased, language, lastNameFirst)
return draft.data.deceased
? getLocalisedName(intl, {
firstNames: draft.data.deceased.firstNamesEng as string,
middleName: draft.data.deceased.middleNameEng as string,
familyName: draft.data.deceased.familyNameEng as string
})
: EMPTY_STRING
case EventType.Marriage:
const brideName = getFullName(draft.data.bride, language, lastNameFirst)
const groomName = getFullName(draft.data.groom, language, lastNameFirst)
const brideName = draft.data.bride
? getLocalisedName(intl, {
firstNames: draft.data.bride.firstNamesEng as string,
middleName: draft.data.bride.middleNameEng as string,
familyName: draft.data.bride.familyNameEng as string
})
: EMPTY_STRING
const groomName = draft.data.groom
? getLocalisedName(intl, {
firstNames: draft.data.groom.firstNamesEng as string,
middleName: draft.data.groom.middleNameEng as string,
familyName: draft.data.groom.familyNameEng as string
})
: EMPTY_STRING
if (brideName && groomName) {
return `${groomName} & ${brideName}`
} else {
Expand Down
3 changes: 1 addition & 2 deletions packages/client/src/views/OfficeHome/myDrafts/MyDrafts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ export const MyDrafts: React.FC<{
}

const transformDraftContent = () => {
const { locale } = intl
if (drafts.length <= 0) {
return []
}
Expand All @@ -105,7 +104,7 @@ export const MyDrafts: React.FC<{
} else if (draft.event && draft.event.toString() === 'marriage') {
pageRoute = DRAFT_MARRIAGE_FORM_PAGE
}
const name = getDeclarationFullName(draft, locale)
const name = getDeclarationFullName(draft, intl)
const lastModificationDate = draft.modifiedOn || draft.savedOn
const actions: IAction[] = []

Expand Down
Loading

0 comments on commit a25ba87

Please sign in to comment.