Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add constants.humanName to allow countries to have custom full name format #7957

Merged
merged 14 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Introduced a new customisable UI component: Banner [#8276](https://github.com/opencrvs/opencrvs-core/issues/8276)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be something irrelevant

- 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
Loading