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: Reason for delayed registration in v2 birth #8533

Merged
merged 14 commits into from
Feb 7, 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
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
getConditionalActionsForField,
getDependentFields,
handleInitialValue,
hasInitialValueDependencyInfo
hasInitialValueDependencyInfo,
makeDatesFormatted
} from './utils'
import { Errors, getValidationErrorsForForm } from './validation'

Expand Down Expand Up @@ -486,6 +487,7 @@ class FormSectionComponent extends React.Component<AllProps> {
...field,
id: field.id.replaceAll('.', FIELD_SEPARATOR)
}))
const valuesWithFormattedDate = makeDatesFormatted(fieldsWithDotIds, values)

return (
<section>
Expand All @@ -501,7 +503,9 @@ class FormSectionComponent extends React.Component<AllProps> {
const conditionalActions: string[] = getConditionalActionsForField(
field,
{
$form: makeFormikFieldIdsOpenCRVSCompatible(values),
$form: makeFormikFieldIdsOpenCRVSCompatible(
valuesWithFormattedDate
),
$now: formatISO(new Date(), { representation: 'date' })
}
)
Expand Down Expand Up @@ -554,7 +558,7 @@ class FormSectionComponent extends React.Component<AllProps> {
* Because our form field ids can have dots in them, we temporarily transform those dots
* to a different character before passing the data to Formik. This function unflattens
*/
const FIELD_SEPARATOR = '____'
export const FIELD_SEPARATOR = '____'
function makeFormFieldIdsFormikCompatible<T>(data: Record<string, T>) {
return Object.fromEntries(
Object.entries(data).map(([key, value]) => [
Expand Down Expand Up @@ -593,7 +597,9 @@ export const FormFieldGenerator: React.FC<ExposedProps> = (props) => {
validate={(values) =>
getValidationErrorsForForm(
props.fields,
makeFormikFieldIdsOpenCRVSCompatible(values),
makeFormikFieldIdsOpenCRVSCompatible(
makeDatesFormatted(props.fields, values)
),
props.requiredErrorMessage
)
}
Expand Down
43 changes: 43 additions & 0 deletions packages/client/src/v2-events/components/forms/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* OpenCRVS is also distributed under the terms of the Civil Registration
* & Healthcare Disclaimer located at http://opencrvs.org/license.
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/

import { formatDateFieldValue } from './utils'

describe('Verify formatDateFieldValue', () => {
it('Formats date properly', () => {
const cases = [
{
input: '--',
expectedOutput: '00-00-00'
},
{
input: '01-02-',
expectedOutput: '01-02-00'
},
{
input: '1-2-3',
expectedOutput: '01-02-03'
},
{
input: '01-2-32',
expectedOutput: '01-02-32'
},
{
input: '2025-2-3',
expectedOutput: '2025-02-03'
}
]

cases.forEach(({ input, expectedOutput }) =>
expect(formatDateFieldValue(input)).toBe(expectedOutput)
)
})
})
33 changes: 33 additions & 0 deletions packages/client/src/v2-events/components/forms/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
validate
} from '@opencrvs/commons/client'
import { DependencyInfo } from '@client/forms'
import { FIELD_SEPARATOR } from './FormFieldGenerator'

export function handleInitialValue(
field: FieldConfig,
Expand Down Expand Up @@ -106,3 +107,35 @@ export function setEmptyValuesForFields(fields: FieldConfig[]) {
export interface Stringifiable {
toString(): string
}

/**
*
* @param fields field config in OpenCRVS format (separated with `.`)
* @param values form values in formik format (separated with `FIELD_SEPARATOR`)
* @returns adds 0 before single digit days and months to make them 2 digit
* because ajv's `formatMaximum` and `formatMinimum` does not allow single digit day or months
*/
export function makeDatesFormatted(
fields: FieldConfig[],
values: Record<string, FieldValue>
) {
return fields.reduce((acc, field) => {
const fieldId = field.id.replaceAll('.', FIELD_SEPARATOR)

if (field.type === 'DATE' && fieldId in values) {
const value = values[fieldId as keyof typeof values]
if (typeof value === 'string') {
const formattedDate = formatDateFieldValue(value)
return { ...acc, [fieldId]: formattedDate }
}
}
return acc
}, values)
}

export function formatDateFieldValue(value: string) {
return value
.split('-')
.map((d: string) => d.padStart(2, '0'))
.join('-')
}
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.37-ml",
"version": "0.0.38-jr",
"description": "OpenCRVS toolkit for building country configurations",
"license": "MPL-2.0",
"exports": {
Expand Down
133 changes: 115 additions & 18 deletions packages/toolkit/src/conditionals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,30 @@ export function eventHasAction(type: ActionType): AjvJSONSchema {
}
}

type DateBoundary = {
now: () => FieldAPI
date: (
// date should be in yyyy-mm-dd format
date: `${number}${number}${number}${number}-${number}${number}-${number}${number}`
) => FieldAPI
days: (days: number) => {
inPast: () => FieldAPI
inFuture: () => FieldAPI
}
}

export type FieldAPI = {
inArray: (values: string[]) => FieldAPI
isBeforeNow: () => FieldAPI
/**
* Checks if the date is within `days` days in the past from now.
*/
isBefore: () => DateBoundary
isAfter: () => DateBoundary
isEqualTo: (value: string | boolean) => FieldAPI
isUndefined: () => FieldAPI
not: {
isBefore: () => DateBoundary
isAfter: () => DateBoundary
inArray: (values: string[]) => FieldAPI
equalTo: (value: string | boolean) => FieldAPI
}
Expand Down Expand Up @@ -152,29 +170,76 @@ export function field(fieldId: string) {
return api
}

const api: FieldAPI = {
isBeforeNow: () =>
addCondition({
const getDateFromNow = (days: number) =>
new Date(Date.now() - days * 24 * 60 * 60 * 1000)
.toISOString()
.split('T')[0]

const getDateRange = (
date: string,
clause: 'formatMinimum' | 'formatMaximum'
) => ({
type: 'object',
properties: {
$form: {
type: 'object',
properties: {
$form: {
type: 'object',
properties: {
[fieldId]: {
type: 'string',
format: 'date',
formatMaximum: { $data: '2/$now' }
}
},
required: [fieldId]
},
$now: {
[fieldId]: {
type: 'string',
format: 'date',
[clause]: date
}
},
required: [fieldId]
}
},
required: ['$form']
})

const getNegativeDateRange = (
date: string,
clause: 'formatMinimum' | 'formatMaximum'
) => ({
type: 'object',
properties: {
$form: {
type: 'object',
properties: {
[fieldId]: {
type: 'string',
format: 'date'
not: {
format: 'date',
[clause]: date
}
}
},
required: ['$form', '$now']
required: [fieldId]
}
},
required: ['$form']
})

const api: FieldAPI = {
isAfter: () => ({
days: (days: number) => ({
inPast: () =>
addCondition(getDateRange(getDateFromNow(days), 'formatMinimum')),
inFuture: () =>
addCondition(getDateRange(getDateFromNow(-days), 'formatMinimum'))
}),
date: (date: string) => addCondition(getDateRange(date, 'formatMinimum')),
now: () => addCondition(getDateRange(getDateFromNow(0), 'formatMinimum'))
}),
isBefore: () => ({
days: (days: number) => ({
inPast: () =>
addCondition(getDateRange(getDateFromNow(days), 'formatMaximum')),
inFuture: () =>
addCondition(getDateRange(getDateFromNow(-days), 'formatMaximum'))
}),
date: (date: string) => addCondition(getDateRange(date, 'formatMaximum')),
now: () => addCondition(getDateRange(getDateFromNow(0), 'formatMaximum'))
}),
isEqualTo: (value: string | boolean) =>
addCondition({
type: 'object',
Expand Down Expand Up @@ -228,6 +293,38 @@ export function field(fieldId: string) {
required: ['$form']
}),
not: {
isAfter: () => ({
days: (days: number) => ({
inPast: () =>
addCondition(
getNegativeDateRange(getDateFromNow(days), 'formatMinimum')
),
inFuture: () =>
addCondition(
getNegativeDateRange(getDateFromNow(-days), 'formatMinimum')
)
}),
date: (date: string) =>
addCondition(getNegativeDateRange(date, 'formatMinimum')),
now: () =>
addCondition(getNegativeDateRange(getDateFromNow(0), 'formatMinimum'))
}),
isBefore: () => ({
days: (days: number) => ({
inPast: () =>
addCondition(
getNegativeDateRange(getDateFromNow(days), 'formatMaximum')
),
inFuture: () =>
addCondition(
getNegativeDateRange(getDateFromNow(-days), 'formatMaximum')
)
}),
date: (date: string) =>
addCondition(getNegativeDateRange(date, 'formatMaximum')),
now: () =>
addCondition(getNegativeDateRange(getDateFromNow(0), 'formatMaximum'))
}),
inArray: (values: string[]) =>
addCondition({
type: 'object',
Expand Down
Loading