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 #7812

Merged
merged 8 commits into from
Nov 11, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- Record audit action buttons are moved into action menu [#7390](https://github.com/opencrvs/opencrvs-core/issues/7390)
- Reoder the sytem user add/edit field for surname to be first, also change labels from `Last name` to `User's surname` and lastly remove the NID question from the form [#6830](https://github.com/opencrvs/opencrvs-core/issues/6830)
- Auth now allows exchanging user's token for a new record-specific token [#7728](https://github.com/opencrvs/opencrvs-core/issues/7728)
- 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)

## Bug fixes

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: `{lastName} {middleName} {firstName}`,
Zangetsu101 marked this conversation as resolved.
Show resolved Hide resolved
description: 'A localized order of the full name',
id: 'constants.humanName'
}
}
export const constantsMessages: Record<
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()
}
87 changes: 55 additions & 32 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: 'en',
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: Event.Birth,
savedOn: 1558037863335,
modifiedOn: 1558037867987
},
event: Event.Birth,
savedOn: 1558037863335,
modifiedOn: 1558037867987
})
).toBe('Mushraful Hoque')
intlEngish
)
).toBe('Hoque Mushraful')
})
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('Hoque Mushraful')
})
})
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: Event.Death,
savedOn: 1558037863335,
modifiedOn: 1558037867987
},
event: Event.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('Hoque Mushraful')
})
})
})
Expand Down
82 changes: 31 additions & 51 deletions packages/client/src/utils/draftUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
SUBMISSION_STATUS,
IPrintableDeclaration
} from '@client/declarations'
import { IFormSectionData } from '@client/forms'
import { Event, History, RegStatus } from '@client/utils/gateway'
import type {
GQLBirthEventSearchSet,
Expand All @@ -24,67 +23,48 @@ import type {
import { getEvent } from '@client/views/PrintCertificate/utils'
import { includes } from 'lodash'
import { EMPTY_STRING } from '@client/utils/constants'

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)
}
import { getLocalisedName } from './data-formatting'
import { IntlShape } from 'react-intl'

/*
* lastNameFirst needs to be removed in #4464
*/
export const getDeclarationFullName = (
draft: IDeclaration,
language?: string,
lastNameFirst?: boolean
intl: IntlShape
) => {
switch (draft.event) {
case Event.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 Event.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 Event.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
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,6 @@ class InProgressComponent extends React.Component<

transformDraftContent = () => {
const { intl } = this.props
const { locale } = intl
if (!this.props.drafts || this.props.drafts.length <= 0) {
return []
}
Expand All @@ -361,7 +360,7 @@ class InProgressComponent extends React.Component<
} 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
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ describe('In Progress tab', () => {
const EXPECTED_DATE_OF_REJECTION = formattedDuration(TIME_STAMP)

expect(data[0].id).toBe('e302f7c5-ad87-4117-91c1-35eaf2ea7be8')
expect(data[0].name).toBe('anik hoque')
expect(data[0].name).toBe('hoque anik')
expect(data[0].lastUpdated).toBe(EXPECTED_DATE_OF_REJECTION)
expect(data[0].event).toBe('Birth')
expect(data[0].actions).toBeDefined()
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/views/OfficeHome/outbox/Outbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export function Outbox() {

function transformDeclarationsReadyToSend() {
const items = declarations.map((declaration, index) => {
const name = getDeclarationFullName(declaration)
const name = getDeclarationFullName(declaration, intl)
let dateOfEvent
if (declaration.event && declaration.event.toString() === 'birth') {
dateOfEvent = declaration.data?.child?.childBirthDate as string
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/views/RecordAudit/RecordAudit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ describe('Record audit summary for WorkQueue declarations', () => {
component.find({ 'data-testid': 'type-value' }).hostNodes().text()
).toBe('Birth')
expect(component.find('#content-name').hostNodes().text()).toBe(
'Shakib Al Hasan'
'Al Hasan Shakib'
)
expect(
component
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/views/RecordAudit/RecordAudit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ const BodyContent = ({
assignment: data.fetchRegistration?.registration?.assignment
}
} else {
declaration = getGQLDeclaration(data.fetchRegistration, language)
declaration = getGQLDeclaration(data.fetchRegistration, intl)
}

return (
Expand Down Expand Up @@ -592,7 +592,7 @@ const BodyContent = ({
}
: getWQDeclarationData(
workqueueDeclaration as NonNullable<typeof workqueueDeclaration>,
language,
intl,
trackingId
)
const wqStatus = workqueueDeclaration?.registration
Expand Down
Loading
Loading