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/summary field config #8347

Merged
merged 11 commits into from
Jan 22, 2025
7 changes: 5 additions & 2 deletions packages/client/src/v2-events/components/forms/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ const initialValueMapping: Record<FieldType, FieldValue | null> = {
}

export function getInitialValues(fields: FieldConfig[]) {
return fields.reduce((initialValues, field) => {
return { ...initialValues, [field.id]: initialValueMapping[field.type] }
return fields.reduce((initialValues: Record<string, FieldValue>, field) => {
return {
...initialValues,
[field.id]: initialValueMapping[field.type]
}
}, {})
}

Expand Down
16 changes: 12 additions & 4 deletions packages/client/src/v2-events/features/events/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,24 @@ export const tennisClubMembershipEvent = {
},
summary: {
title: {
defaultMessage: '{applicant.firstname} {applicant.surname}',
description: 'This is the title of the summary',
id: 'event.tennis-club-membership.summary.title'
id: 'event.tennis-club-membership.summary.title',
label: {
defaultMessage: '{applicant.firstname} {applicant.surname}',
description: 'This is the title of the summary',
id: 'event.tennis-club-membership.summary.title.label'
}
},
fields: [
{
id: 'applicant.firstname',
value: {
defaultMessage: '{applicant.firstname}',
description: 'Value for the matching field on form.',
id: 'event.tennis-club-membership.summary.field.firstname.value'
},
label: {
defaultMessage: 'First name',
description: 'Label for the gien field from form.',
description: 'Label for the given field on form.',
id: 'event.tennis-club-membership.summary.field.firstname.label'
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,28 @@ function EventOverview({
const { eventConfiguration } = useEventConfiguration(event.type)
const intl = useIntlFormatMessageWithFlattenedParams()
const initialValues = getInitialValues(getAllFields(eventConfiguration))

const title = intl.formatMessage(summary.title.label, {
...initialValues,
...event.data
})

const fallbackTitle = summary.title.emptyValueMessage
? intl.formatMessage(summary.title.emptyValueMessage)
: ''
return (
<Content
icon={() => <IconWithName name={''} status={'orange'} />}
size={ContentSize.LARGE}
title={intl.formatMessage(summary.title, {
...initialValues,
...event.data
})}
title={title || fallbackTitle}
titleColor={event.id ? 'copy' : 'grey600'}
topActionButtons={[<ActionMenu key={event.id} eventId={event.id} />]}
>
<EventSummary event={event} summary={summary} />
<EventSummary
defaultValues={initialValues}
event={event}
summary={summary}
/>
<EventHistory history={history} />
</Content>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,29 @@
*/

import React from 'react'
import { useIntl } from 'react-intl'
import { Summary } from '@opencrvs/components/lib/Summary'
import { SummaryConfig } from '@opencrvs/commons/events'
import { EventIndex } from '@opencrvs/commons/client'
import { EventIndex, FieldValue } from '@opencrvs/commons/client'
import { useTransformer } from '@client/v2-events/hooks/useTransformer'
import { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/features/workqueues/utils'

/**
* Based on packages/client/src/views/RecordAudit/DeclarationInfo.tsx
*/

export function EventSummary({
event,
summary
summary,
defaultValues
}: {
event: EventIndex
summary: SummaryConfig
defaultValues: Record<string, FieldValue>
}) {
const intl = useIntl()
const intl = useIntlFormatMessageWithFlattenedParams()
const { toString } = useTransformer(event.type)
const data = toString(event.data)

return (
<>
<Summary id="summary">
Expand All @@ -43,7 +46,10 @@ export function EventSummary({
field.emptyValueMessage &&
intl.formatMessage(field.emptyValueMessage)
}
value={data[field.id]}
value={intl.formatMessage(field.value, {
...defaultValues,
...data
})}
/>
)
})}
Expand Down
22 changes: 14 additions & 8 deletions packages/client/src/v2-events/features/workqueues/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,21 @@ export function useIntlFormatMessageWithFlattenedParams() {
function formatMessage<T extends {}>(message: MessageDescriptor, params?: T) {
const variables = convertDotToTripleUnderscore(params ?? {})

return intl.formatMessage(
{
id: message.id,
description: message.description,
defaultMessage: convertDotInCurlyBraces(
message.defaultMessage as string
return (
intl
.formatMessage(
{
id: message.id,
description: message.description,
defaultMessage: convertDotInCurlyBraces(
message.defaultMessage as string
)
},
variables
)
},
variables
// When multiple variables are provided, we trim to ensure empty content in case both are missing.
// We might need to adjust this and allow more freedom for configuration (e.g. provide values and join pattern)
.trim()
)
}

Expand Down
8 changes: 2 additions & 6 deletions packages/commons/src/events/EventConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { z } from 'zod'
import { ActionConfig } from './ActionConfig'
import { TranslationConfig } from './TranslationConfig'
import { SummaryConfig, SummaryConfigInput } from './SummaryConfig'
import { SummaryConfig } from './SummaryConfig'
import { WorkqueueConfig } from './WorkqueueConfig'
import { FormConfig, FormConfigInput, FormPage } from './FormConfig'
import { DeduplicationConfig } from './DeduplicationConfig'
Expand All @@ -34,12 +34,8 @@ export const EventConfig = z.object({
deduplication: z.array(DeduplicationConfig).optional().default([])
})

export const EventConfigInput = EventConfig.extend({
summary: SummaryConfigInput
})

export type EventConfig = z.infer<typeof EventConfig>
export type EventConfigInput = z.input<typeof EventConfigInput>
export type EventConfigInput = z.input<typeof EventConfig>

export const defineForm = (form: FormConfigInput): FormConfig =>
FormConfig.parse(form)
Expand Down
19 changes: 9 additions & 10 deletions packages/commons/src/events/SummaryConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,25 @@ import { z } from 'zod'
import { TranslationConfig } from './TranslationConfig'

const Field = z.object({
id: z.string().describe('Id of a field defined under form.'),
id: z.string().describe('Id of summary field'),
value: TranslationConfig.describe(
'Summary field value. Can utilise values defined in configuration and EventMetadata'
),
label: TranslationConfig,
emptyValueMessage: TranslationConfig.optional()
})

const FieldInput = Field.extend({
// label is enforced during runtime.
label: TranslationConfig.optional()
const Title = z.object({
id: z.string(),
label: TranslationConfig.describe('Title content'),
emptyValueMessage: TranslationConfig.optional()
})

export const SummaryConfig = z
.object({
title: TranslationConfig.describe('Header title of summary'),
title: Title.describe('Title of summary view.'),
fields: z.array(Field).describe('Fields rendered in summary view.')
})
.describe('Configuration for summary in event.')

export const SummaryConfigInput = SummaryConfig.extend({
fields: z.array(FieldInput)
})

export type SummaryConfig = z.infer<typeof SummaryConfig>
export type SummaryConfigInput = z.input<typeof SummaryConfigInput>
6 changes: 1 addition & 5 deletions packages/commons/src/events/defineConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { findPageFields, resolveFieldLabels } from './utils'
* @param config - Event specific configuration
*/
export const defineConfig = (config: EventConfigInput) => {
const input = EventConfigInput.parse(config)
const input = EventConfig.parse(config)

const pageFields = findPageFields(input).map(({ id, label }) => ({
id,
Expand All @@ -26,10 +26,6 @@ export const defineConfig = (config: EventConfigInput) => {

return EventConfig.parse({
...input,
summary: resolveFieldLabels({
config: input.summary,
pageFields
}),
workqueues: input.workqueues.map((workqueue) =>
resolveFieldLabels({
config: workqueue,
Expand Down
3 changes: 1 addition & 2 deletions packages/commons/src/events/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { TranslationConfig } from './TranslationConfig'
import { EventMetadataKeys, eventMetadataLabelMap } from './EventMetadata'
import { flattenDeep } from 'lodash'
import { EventConfig, EventConfigInput } from './EventConfig'
import { SummaryConfigInput } from './SummaryConfig'
import { WorkqueueConfigInput } from './WorkqueueConfig'
import { FieldType } from './FieldConfig'

Expand Down Expand Up @@ -89,7 +88,7 @@ export const resolveFieldLabels = ({
config,
pageFields
}: {
config: SummaryConfigInput | WorkqueueConfigInput
config: WorkqueueConfigInput
pageFields: { id: string; label: TranslationConfig }[]
}) => {
return {
Expand Down
25 changes: 22 additions & 3 deletions packages/commons/src/fixtures/tennis-club-membership-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,32 @@ export const tennisClubMembershipEvent = defineConfig({
},
summary: {
title: {
defaultMessage: 'Summary',
description: 'This is the title of the summary',
id: 'event.tennis-club-membership.summary.title'
id: 'event.tennis-club-membership.summary.title',
label: {
defaultMessage: 'Summary',
description: 'This is the title of the summary',
id: 'event.tennis-club-membership.summary.title'
},
emptyValueMessage: {
defaultMessage: 'Membership application',
description:
'This is the message shown when the applicant name is missing',
id: 'event.tennis-club-membership.summary.title.empty'
}
},
fields: [
{
id: 'applicant.firstname',
label: {
defaultMessage: "Applicant's first name",
description: 'This is the label for the field',
id: 'event.tennis-club-membership.summary.field.firstname.label'
},
value: {
defaultMessage: '{applicant.firstname}',
description: 'This is the value to show in the summary',
id: 'event.tennis-club-membership.summary.field.firstname'
},
emptyValueMessage: {
defaultMessage: 'First name is not provided',
description: 'This is the message to show when the field is empty',
Expand Down
2 changes: 1 addition & 1 deletion packages/toolkit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opencrvs/toolkit",
"version": "0.0.14-events",
"version": "0.0.16-events",
"description": "OpenCRVS toolkit for building country configurations",
"license": "MPL-2.0",
"exports": {
Expand Down
Loading