From 9d77d8adf77303dfb3d4aa5e892c0a327c1b81d7 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Mon, 17 Nov 2025 20:47:03 +0600 Subject: [PATCH 01/11] fix: pass event document to conditionals --- .../v2-events/features/events/actions/declare/Pages.tsx | 2 +- .../v2-events/features/events/actions/declare/Review.tsx | 2 +- .../v2-events/features/events/actions/register/Pages.tsx | 2 +- .../features/events/actions/register/Review.tsx | 2 +- .../v2-events/features/events/actions/validate/Pages.tsx | 2 +- .../features/events/actions/validate/Review.tsx | 2 +- packages/commons/src/conditionals/validate.ts | 9 ++++++--- 7 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx b/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx index 925f97876eb..81c761a5f7a 100644 --- a/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx @@ -109,7 +109,7 @@ export function Pages() { pageId={currentPageId} setFormData={(data) => setFormValues(data)} showReviewButton={searchParams.from === 'review'} - validatorContext={validatorContext} + validatorContext={{ ...validatorContext, event }} onPageChange={(nextPageId: string) => navigate( ROUTES.V2.EVENTS.DECLARE.PAGES.buildPath( diff --git a/packages/client/src/v2-events/features/events/actions/declare/Review.tsx b/packages/client/src/v2-events/features/events/actions/declare/Review.tsx index 297c925476e..3172f1a889f 100644 --- a/packages/client/src/v2-events/features/events/actions/declare/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/declare/Review.tsx @@ -200,7 +200,7 @@ export function Review() { formConfig={formConfig} reviewFields={reviewConfig.fields} title={formatMessage(reviewConfig.title, form)} - validatorContext={validatorContext} + validatorContext={{ ...validatorContext, event }} onAnnotationChange={(values) => setAnnotation(values)} onEdit={handleEdit} > diff --git a/packages/client/src/v2-events/features/events/actions/register/Pages.tsx b/packages/client/src/v2-events/features/events/actions/register/Pages.tsx index e373e7c98f0..c6ad8219957 100644 --- a/packages/client/src/v2-events/features/events/actions/register/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/register/Pages.tsx @@ -86,7 +86,7 @@ export function Pages() { pageId={currentPageId} setFormData={(data) => setFormValues(data)} showReviewButton={searchParams.from === 'review'} - validatorContext={validatorContext} + validatorContext={{ ...validatorContext, event }} onPageChange={(nextPageId: string) => navigate( ROUTES.V2.EVENTS.REGISTER.PAGES.buildPath( diff --git a/packages/client/src/v2-events/features/events/actions/register/Review.tsx b/packages/client/src/v2-events/features/events/actions/register/Review.tsx index a0350c81d80..4b237a9d993 100644 --- a/packages/client/src/v2-events/features/events/actions/register/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/register/Review.tsx @@ -217,7 +217,7 @@ export function Review() { previousFormValues={previousFormValues} reviewFields={reviewConfig.fields} title={formatMessage(reviewConfig.title, form)} - validatorContext={validatorContext} + validatorContext={{ ...validatorContext, event }} onAnnotationChange={(values) => setAnnotation(values)} onEdit={handleEdit} > diff --git a/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx b/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx index b9b0d31b6f2..7c388fc58ed 100644 --- a/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx @@ -88,7 +88,7 @@ export function Pages() { pageId={currentPageId} setFormData={(data) => setFormValues(data)} showReviewButton={searchParams.from === 'review'} - validatorContext={validatorContext} + validatorContext={{ ...validatorContext, event }} onPageChange={(nextPageId: string) => navigate( ROUTES.V2.EVENTS.VALIDATE.PAGES.buildPath( diff --git a/packages/client/src/v2-events/features/events/actions/validate/Review.tsx b/packages/client/src/v2-events/features/events/actions/validate/Review.tsx index a47da75139d..0f8547e45bd 100644 --- a/packages/client/src/v2-events/features/events/actions/validate/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/validate/Review.tsx @@ -223,7 +223,7 @@ export function Review() { previousFormValues={previousFormValues} reviewFields={reviewConfig.fields} title={formatMessage(reviewConfig.title, form)} - validatorContext={validatorContext} + validatorContext={{ ...validatorContext, event }} onAnnotationChange={(values) => setAnnotation(values)} onEdit={handleEdit} > diff --git a/packages/commons/src/conditionals/validate.ts b/packages/commons/src/conditionals/validate.ts index 7fcd7b66762..ed295e5cadf 100644 --- a/packages/commons/src/conditionals/validate.ts +++ b/packages/commons/src/conditionals/validate.ts @@ -30,7 +30,7 @@ import { TranslationConfig } from '../events/TranslationConfig' import { ITokenPayload } from '../authentication' import { UUID } from '../uuid' import { ageToDate } from '../events/utils' -import { ActionType } from '../client' +import { ActionType, EventDocument } from '../client' const ajv = new Ajv({ $data: true, @@ -185,7 +185,8 @@ export function isConditionMet( $now: formatISO(new Date(), { representation: 'date' }), $online: isOnline(), $user: context.user, - $leafAdminStructureLocationIds: context.leafAdminStructureLocationIds ?? [] + $leafAdminStructureLocationIds: context.leafAdminStructureLocationIds ?? [], + $event: context.event }) } @@ -215,6 +216,7 @@ export function areConditionsMet( export type ValidatorContext = { user?: ITokenPayload leafAdminStructureLocationIds?: Array<{ id: UUID }> + event?: EventDocument } function isFieldConditionMet( @@ -236,7 +238,8 @@ function isFieldConditionMet( $now: formatISO(new Date(), { representation: 'date' }), $online: isOnline(), $user: context.user, - $leafAdminStructureLocationIds: context.leafAdminStructureLocationIds ?? [] + $leafAdminStructureLocationIds: context.leafAdminStructureLocationIds ?? [], + $event: context.event }) return validConditionals.includes(conditionalType) From 7fc3c6a2687f2b00f82e05d421865b2ff0587055 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Tue, 18 Nov 2025 14:24:35 +0600 Subject: [PATCH 02/11] chore: also pass event in validate action middleware --- packages/events/src/router/middleware/validate/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/events/src/router/middleware/validate/index.ts b/packages/events/src/router/middleware/validate/index.ts index 4886b1d05ae..26712b4d5bb 100644 --- a/packages/events/src/router/middleware/validate/index.ts +++ b/packages/events/src/router/middleware/validate/index.ts @@ -362,7 +362,7 @@ export const validateAction: MiddlewareFunction< token: ctx.token }) - const context = await getValidatorContext(ctx.token) + const context = { ...(await getValidatorContext(ctx.token)), event } const declaration = getCurrentEventState(event, eventConfig).declaration From 8545e01321908ceb8c3bb3cefe2a959fda2eeb05 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Tue, 18 Nov 2025 17:06:45 +0600 Subject: [PATCH 03/11] fix: make changes to all possible required places --- .../features/events/ReadOnlyView.tsx | 2 +- .../correct/request/Onboarding/Onboarding.tsx | 3 +- .../events/actions/correct/request/Pages.tsx | 2 +- .../events/actions/correct/request/Review.tsx | 2 +- .../correct/request/Summary/Summary.tsx | 2 +- .../events/actions/correct/review/Review.tsx | 2 +- .../actions/dedup/DuplicateComparison.tsx | 66 ++++++++++++------- .../events/actions/dedup/ReviewDuplicate.tsx | 19 ++++-- .../actions/print-certificate/Pages.tsx | 2 +- .../actions/print-certificate/Review.tsx | 4 +- .../events/actions/register/Pages.tsx | 2 +- .../events/actions/register/Review.tsx | 2 +- .../events/actions/validate/Pages.tsx | 3 +- .../events/actions/validate/Review.tsx | 2 +- .../EventOverview/EventOverview.tsx | 7 +- .../EventOverview/components/EventSummary.tsx | 26 +++++--- .../v2-events/hooks/useValidatorContext.ts | 11 +++- .../src/router/middleware/validate/utils.ts | 2 +- 18 files changed, 97 insertions(+), 62 deletions(-) diff --git a/packages/client/src/v2-events/features/events/ReadOnlyView.tsx b/packages/client/src/v2-events/features/events/ReadOnlyView.tsx index 07e35f0d1bc..134a97c7d62 100644 --- a/packages/client/src/v2-events/features/events/ReadOnlyView.tsx +++ b/packages/client/src/v2-events/features/events/ReadOnlyView.tsx @@ -37,7 +37,7 @@ function ReadonlyView() { const { eventId } = useTypedParams(ROUTES.V2.EVENTS.DECLARE.REVIEW) const events = useEvents() const event = events.getEvent.viewEvent(eventId) - const validatorContext = useValidatorContext() + const validatorContext = useValidatorContext(event) const maybeAuth = useAuthentication() const authentication = getOrThrow( diff --git a/packages/client/src/v2-events/features/events/actions/correct/request/Onboarding/Onboarding.tsx b/packages/client/src/v2-events/features/events/actions/correct/request/Onboarding/Onboarding.tsx index 9a0474d228e..16a7fdf4ae0 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/request/Onboarding/Onboarding.tsx +++ b/packages/client/src/v2-events/features/events/actions/correct/request/Onboarding/Onboarding.tsx @@ -41,12 +41,13 @@ export function Onboarding() { const [{ workqueue }] = useTypedSearchParams( ROUTES.V2.EVENTS.REQUEST_CORRECTION.ONBOARDING ) - const validatorContext = useValidatorContext() + const events = useEvents() const annotation = useActionAnnotation((state) => state.getAnnotation()) const setAnnotation = useActionAnnotation((state) => state.setAnnotation) const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const navigate = useNavigate() const intl = useIntl() diff --git a/packages/client/src/v2-events/features/events/actions/correct/request/Pages.tsx b/packages/client/src/v2-events/features/events/actions/correct/request/Pages.tsx index a8cb0ead644..17d27cb3215 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/request/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/correct/request/Pages.tsx @@ -41,7 +41,6 @@ function getCorrectablePages(formPages: PageConfig[]) { } export function Pages() { - const validatorContext = useValidatorContext() const { eventId, pageId } = useTypedParams(ROUTES.V2.EVENTS.REGISTER.PAGES) const [searchParams] = useTypedSearchParams(ROUTES.V2.EVENTS.REGISTER.PAGES) const setFormValues = useEventFormData((state) => state.setFormValues) @@ -51,6 +50,7 @@ export function Pages() { const { modal } = useEventFormNavigation() const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration: configuration } = useEventConfiguration( event.type diff --git a/packages/client/src/v2-events/features/events/actions/correct/request/Review.tsx b/packages/client/src/v2-events/features/events/actions/correct/request/Review.tsx index 7bc75b46818..e8e2253918e 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/request/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/correct/request/Review.tsx @@ -42,9 +42,9 @@ export function Review() { const intl = useIntl() const navigate = useNavigate() const events = useEvents() - const validatorContext = useValidatorContext() const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration: configuration } = useEventConfiguration( event.type diff --git a/packages/client/src/v2-events/features/events/actions/correct/request/Summary/Summary.tsx b/packages/client/src/v2-events/features/events/actions/correct/request/Summary/Summary.tsx index 4f8f9af389c..0f5133e3c80 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/request/Summary/Summary.tsx +++ b/packages/client/src/v2-events/features/events/actions/correct/request/Summary/Summary.tsx @@ -80,7 +80,6 @@ export function Summary() { ROUTES.V2.EVENTS.REQUEST_CORRECTION.SUMMARY ) - const validatorContext = useValidatorContext() const [showPrompt, setShowPrompt] = React.useState(false) const eventFormNavigation = useEventFormNavigation() const navigate = useNavigate() @@ -88,6 +87,7 @@ export function Summary() { const events = useEvents() const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration } = useEventConfiguration(event.type) const eventIndex = getCurrentEventState(event, eventConfiguration) const togglePrompt = () => setShowPrompt(!showPrompt) diff --git a/packages/client/src/v2-events/features/events/actions/correct/review/Review.tsx b/packages/client/src/v2-events/features/events/actions/correct/review/Review.tsx index f03941c74c3..c33859a0397 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/review/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/correct/review/Review.tsx @@ -33,8 +33,8 @@ export function Review() { const { eventId } = useTypedParams(ROUTES.V2.EVENTS.REVIEW_CORRECTION.REVIEW) const events = useEvents() - const validatorContext = useValidatorContext() const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration: configuration } = useEventConfiguration( event.type diff --git a/packages/client/src/v2-events/features/events/actions/dedup/DuplicateComparison.tsx b/packages/client/src/v2-events/features/events/actions/dedup/DuplicateComparison.tsx index f08652045e7..dcf81ccb66c 100644 --- a/packages/client/src/v2-events/features/events/actions/dedup/DuplicateComparison.tsx +++ b/packages/client/src/v2-events/features/events/actions/dedup/DuplicateComparison.tsx @@ -13,6 +13,7 @@ import styled from 'styled-components' import { useIntl } from 'react-intl' import { DeclarationFormConfig, + EventDocument, EventIndex, EventState, FieldConfig, @@ -115,24 +116,31 @@ function UserFullName({ userId }: { userId: string }) { export function DuplicateComparison({ originalEvent, - potentialDuplicateEvent + potentialDuplicateEvent, + originalEventState, + potentialDuplicateEventState }: { - originalEvent: EventIndex - potentialDuplicateEvent: EventIndex + originalEvent: EventDocument + potentialDuplicateEvent: EventDocument + originalEventState: EventIndex + potentialDuplicateEventState: EventIndex }) { const intl = useIntl() - const validatorContext = useValidatorContext() + const validatorContextOfOriginalEvent = useValidatorContext(originalEvent) + const validatorContextOfPotentialDuplicateEvent = useValidatorContext( + potentialDuplicateEvent + ) const flattenedIntl = useIntlFormatMessageWithFlattenedParams() - const { eventConfiguration } = useEventConfiguration(originalEvent.type) + const { eventConfiguration } = useEventConfiguration(originalEventState.type) const flattenedPotentialDuplicateEvent = flattenEventIndex( - potentialDuplicateEvent + potentialDuplicateEventState ) - const flattenedOriginalEvent = flattenEventIndex(originalEvent) + const flattenedOriginalEvent = flattenEventIndex(originalEventState) - const originalDeclaration = originalEvent.declaration - const potentialDuplicateDeclaration = potentialDuplicateEvent.declaration + const originalDeclaration = originalEventState.declaration + const potentialDuplicateDeclaration = potentialDuplicateEventState.declaration const hideFieldTypes = [ ...FieldTypesToHideInReview, @@ -144,8 +152,16 @@ export function DuplicateComparison({ eventConfiguration.declaration.pages .filter( (page) => - isPageVisible(page, originalDeclaration, validatorContext) || - isPageVisible(page, potentialDuplicateDeclaration, validatorContext) + isPageVisible( + page, + originalDeclaration, + validatorContextOfOriginalEvent + ) || + isPageVisible( + page, + potentialDuplicateDeclaration, + validatorContextOfPotentialDuplicateEvent + ) ) .map((page) => ({ title: intl.formatMessage(page.title), @@ -155,12 +171,12 @@ export function DuplicateComparison({ isFieldDisplayedOnReview( field, originalDeclaration, - validatorContext + validatorContextOfOriginalEvent ) || isFieldDisplayedOnReview( field, potentialDuplicateDeclaration, - validatorContext + validatorContextOfPotentialDuplicateEvent ) ) .filter( @@ -209,10 +225,10 @@ export function DuplicateComparison({ { label: intl.formatMessage(summaryMessages.status.label), rightValue: flattenedIntl.formatMessage(summaryMessages.status.value, { - 'event.status': potentialDuplicateEvent.status + 'event.status': potentialDuplicateEventState.status }), leftValue: flattenedIntl.formatMessage(summaryMessages.status.value, { - 'event.status': originalEvent.status + 'event.status': originalEventState.status }) }, { @@ -225,13 +241,13 @@ export function DuplicateComparison({ rightValue: flattenedIntl.formatMessage( summaryMessages.trackingId.value, { - 'event.trackingId': potentialDuplicateEvent.trackingId + 'event.trackingId': potentialDuplicateEventState.trackingId } ), leftValue: flattenedIntl.formatMessage( summaryMessages.trackingId.value, { - 'event.trackingId': originalEvent.trackingId + 'event.trackingId': originalEventState.trackingId } ) }, @@ -286,10 +302,10 @@ export function DuplicateComparison({ { actualTrackingId: ( - {originalEvent.trackingId} + {originalEventState.trackingId} ), - duplicateTrackingId: potentialDuplicateEvent.trackingId + duplicateTrackingId: potentialDuplicateEventState.trackingId } )} > @@ -303,16 +319,16 @@ export function DuplicateComparison({ {sections.data.map((item, id) => ( @@ -355,7 +371,7 @@ export function DuplicateComparison({
- {originalEvent.trackingId} + {originalEventState.trackingId}
- {potentialDuplicateEvent.trackingId} + {potentialDuplicateEventState.trackingId} { if (!event) { @@ -157,7 +157,7 @@ function ReviewDuplicate() { const eventState = getCurrentEventState(event, configuration) const potentialDuplicates = eventState.potentialDuplicates.reduce< - Record + Record >((acc, { id, trackingId }) => { const localEvent = findLocalEventDocument(id) if (!localEvent) { @@ -165,7 +165,10 @@ function ReviewDuplicate() { `Event with id ${id} and trackingId ${trackingId} not found in cache.` ) } - acc[trackingId] = getCurrentEventState(localEvent, configuration) + acc[trackingId] = { + eventState: getCurrentEventState(localEvent, configuration), + event: localEvent + } return acc }, {}) @@ -224,8 +227,12 @@ function ReviewDuplicate() { ) : ( )} diff --git a/packages/client/src/v2-events/features/events/actions/print-certificate/Pages.tsx b/packages/client/src/v2-events/features/events/actions/print-certificate/Pages.tsx index bc12cfcbb16..863d9e54060 100644 --- a/packages/client/src/v2-events/features/events/actions/print-certificate/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/print-certificate/Pages.tsx @@ -49,7 +49,7 @@ export function Pages() { const { eventConfiguration: configuration } = useEventConfiguration( event.type ) - const validatorContext = useValidatorContext() + const validatorContext = useValidatorContext(event) const eventIndex = getCurrentEventState(event, configuration) const certTemplateFieldConfig = useCertificateTemplateSelectorFieldConfig( event.type, diff --git a/packages/client/src/v2-events/features/events/actions/print-certificate/Review.tsx b/packages/client/src/v2-events/features/events/actions/print-certificate/Review.tsx index cc50772abc3..ccf4ca764e9 100644 --- a/packages/client/src/v2-events/features/events/actions/print-certificate/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/print-certificate/Review.tsx @@ -151,7 +151,6 @@ export function Review() { const { getAnnotation } = useActionAnnotation() const annotation = getAnnotation() - const validatorContext = useValidatorContext() if (!templateId) { throw new Error('Please select a template from the previous step') @@ -163,8 +162,9 @@ export function Review() { const { getEvent, onlineActions } = useEvents() const fullEvent = getEvent.getFromCache(eventId) - + const validatorContext = useValidatorContext(fullEvent) const actions = getAcceptedActions(fullEvent) + const userIds = getUserIdsFromActions(actions, [SystemRole.enum.HEALTH]) const { getUsers } = useUsers() diff --git a/packages/client/src/v2-events/features/events/actions/register/Pages.tsx b/packages/client/src/v2-events/features/events/actions/register/Pages.tsx index c6ad8219957..18da94cc0db 100644 --- a/packages/client/src/v2-events/features/events/actions/register/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/register/Pages.tsx @@ -39,10 +39,10 @@ export function Pages() { const { saveAndExitModal, handleSaveAndExit } = useSaveAndExitModal() const navigate = useNavigate() const drafts = useDrafts() - const validatorContext = useValidatorContext() const { modal, closeActionView } = useEventFormNavigation() const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration: configuration } = useEventConfiguration( event.type ) diff --git a/packages/client/src/v2-events/features/events/actions/register/Review.tsx b/packages/client/src/v2-events/features/events/actions/register/Review.tsx index 4b237a9d993..fcc1b34c9f3 100644 --- a/packages/client/src/v2-events/features/events/actions/register/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/register/Review.tsx @@ -62,7 +62,6 @@ export function Review() { ) const events = useEvents() const drafts = useDrafts() - const validatorContext = useValidatorContext() const [modal, openModal] = useModal() const navigate = useNavigate() const { closeActionView: closeActionView } = useEventFormNavigation() @@ -72,6 +71,7 @@ export function Review() { const registerMutation = events.actions.register const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const previousAnnotation = getActionAnnotation({ event, diff --git a/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx b/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx index 7c388fc58ed..b778e03fd4e 100644 --- a/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx @@ -42,9 +42,8 @@ export function Pages() { const { modal, closeActionView } = useEventFormNavigation() const { saveAndExitModal, handleSaveAndExit } = useSaveAndExitModal() - const validatorContext = useValidatorContext() - const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration: configuration } = useEventConfiguration( event.type ) diff --git a/packages/client/src/v2-events/features/events/actions/validate/Review.tsx b/packages/client/src/v2-events/features/events/actions/validate/Review.tsx index 0f8547e45bd..876ba9bdc22 100644 --- a/packages/client/src/v2-events/features/events/actions/validate/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/validate/Review.tsx @@ -58,9 +58,9 @@ export function Review() { const [modal, openModal] = useModal() const navigate = useNavigate() const { closeActionView } = useEventFormNavigation() - const validatorContext = useValidatorContext() const event = events.getEvent.findFromCache(eventId).data + const validatorContext = useValidatorContext(event) useEffect(() => { if (!event) { diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.tsx index 304c3d9f541..d6e65544913 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.tsx @@ -59,7 +59,7 @@ function EventOverviewFull({ }) { const { eventConfiguration } = useEventConfiguration(event.type) const eventIndex = getCurrentEventState(event, eventConfiguration) - const validatorContext = useValidatorContext() + const validatorContext = useValidatorContext(event) const { status } = eventIndex const { getRemoteDraftByEventId } = useDrafts() const draft = getRemoteDraftByEventId(eventIndex.id, { @@ -114,8 +114,9 @@ function EventOverviewFull({ ]} > diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventSummary.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventSummary.tsx index 8ed0a87247c..4463a0925cd 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventSummary.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventSummary.tsx @@ -19,7 +19,8 @@ import { Flag, ActionFlag, InherentFlags, - TranslationConfig + TranslationConfig, + EventDocument } from '@opencrvs/commons/client' import { FieldValue } from '@opencrvs/commons/client' import { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils' @@ -147,17 +148,19 @@ const flagMessages = { export function EventSummary({ event, + eventIndex, eventConfiguration, flags, hideSecuredFields = false }: { - event: Record + event?: EventDocument + eventIndex: Record eventConfiguration: EventConfig flags: Flag[] hideSecuredFields?: boolean }) { const intl = useIntlFormatMessageWithFlattenedParams() - const validationContext = useValidatorContext() + const validationContext = useValidatorContext(event) const { summary, label: eventLabelMessage } = eventConfiguration const declarationFields = getDeclarationFields(eventConfiguration) const securedFields = declarationFields @@ -167,14 +170,14 @@ export function EventSummary({ const configuredFields = summary.fields.map((field) => { if ( field.conditionals && - !areConditionsMet(field.conditionals, event, validationContext) + !areConditionsMet(field.conditionals, eventIndex, validationContext) ) { return null } if ('fieldId' in field) { const config = declarationFields.find((f) => f.id === field.fieldId) - const value = getMixedPath(event, field.fieldId, '') + const value = getMixedPath(eventIndex, field.fieldId, '') if (!config) { return null @@ -205,7 +208,7 @@ export function EventSummary({ securedFields.includes(fieldId) ), emptyValueMessage: field.emptyValueMessage, - value: intl.formatMessage(field.value, event) + value: intl.formatMessage(field.value, eventIndex) } }) @@ -227,13 +230,13 @@ export function EventSummary({ placeholder={intl.formatMessage( messages.assignedTo.emptyValueMessage )} - value={intl.formatMessage(messages.assignedTo.value, event)} + value={intl.formatMessage(messages.assignedTo.value, eventIndex)} /> {configuredFields .filter((f): f is NonNullable => f !== null) diff --git a/packages/client/src/v2-events/hooks/useValidatorContext.ts b/packages/client/src/v2-events/hooks/useValidatorContext.ts index 9bd2c926727..415b4a8456d 100644 --- a/packages/client/src/v2-events/hooks/useValidatorContext.ts +++ b/packages/client/src/v2-events/hooks/useValidatorContext.ts @@ -10,7 +10,11 @@ */ import { useMemo } from 'react' -import { getOrThrow, ValidatorContext } from '@opencrvs/commons/client' +import { + EventDocument, + getOrThrow, + ValidatorContext +} from '@opencrvs/commons/client' import { getToken, getTokenPayload } from '@client/utils/authUtils' import { useAuthentication } from '../../utils/userUtils' import { useSuspenseAdminLeafLevelLocations } from './useLocations' @@ -29,12 +33,13 @@ function useUser() { ) } -export function useValidatorContext(): ValidatorContext { +export function useValidatorContext(event?: EventDocument): ValidatorContext { const leafAdminStructureLocationIds = useSuspenseAdminLeafLevelLocations() const user = useUser() return { user, - leafAdminStructureLocationIds + leafAdminStructureLocationIds, + event } } diff --git a/packages/events/src/router/middleware/validate/utils.ts b/packages/events/src/router/middleware/validate/utils.ts index 29f7dfcf433..a38c45ea67b 100644 --- a/packages/events/src/router/middleware/validate/utils.ts +++ b/packages/events/src/router/middleware/validate/utils.ts @@ -101,7 +101,7 @@ export function getInvalidUpdateKeys({ export async function getValidatorContext( token: string -): Promise { +): Promise> { const leafAdminStructureLocationIds = await getLeafLocationIds({ locationTypes: [LocationType.enum.ADMIN_STRUCTURE] }) From 59c72b6127e65fdad9dee0560f08f76c555f81e1 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Tue, 18 Nov 2025 19:51:41 +0600 Subject: [PATCH 04/11] chore: add tests for alpha-print-button that shows hasAction conditional --- packages/client/.storybook/decorators.tsx | 9 +- .../AlphaPrintButton.interaction.stories.tsx | 272 ++++++++++++++++++ .../AlphaPrintButton.stories.tsx | 96 ------- 3 files changed, 279 insertions(+), 98 deletions(-) create mode 100644 packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx delete mode 100644 packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx diff --git a/packages/client/.storybook/decorators.tsx b/packages/client/.storybook/decorators.tsx index 38441acd5da..3635e9dec10 100644 --- a/packages/client/.storybook/decorators.tsx +++ b/packages/client/.storybook/decorators.tsx @@ -11,6 +11,7 @@ import React from 'react' import type { Decorator } from '@storybook/react' import { + EventDocument, getOrThrow, getTokenPayload, LocationType, @@ -57,7 +58,10 @@ export const withValidatorContext: Decorator = (Story, context) => { /> ) } -export function getTestValidatorContext(userRole?: TestUserRole) { +export function getTestValidatorContext( + userRole?: TestUserRole, + event?: EventDocument +) { let token if (userRole === TestUserRole.Enum.FIELD_AGENT) { @@ -82,6 +86,7 @@ export function getTestValidatorContext(userRole?: TestUserRole) { return { user, - leafAdminStructureLocationIds + leafAdminStructureLocationIds, + event } } diff --git a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx new file mode 100644 index 00000000000..7ae5a824bf4 --- /dev/null +++ b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx @@ -0,0 +1,272 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react' +import { fn, within, expect } from '@storybook/test' +import React from 'react' +import styled from 'styled-components' +import { createTRPCMsw, httpLink } from '@vafanassieff/msw-trpc' +import superjson from 'superjson' +import { + ActionType, + and, + ConditionalType, + event, + FieldType, + tennisClubMembershipEvent, + TestUserRole, + user, + not, + ValidatorContext, + EventDocument +} from '@opencrvs/commons/client' +import { ROUTES } from '@client/v2-events/routes' +import { FormFieldGenerator } from '@client/v2-events/components/forms/FormFieldGenerator' +import { AppRouter, TRPCProvider } from '@client/v2-events/trpc' + +import { noop } from '@client/v2-events' +import { tennisClubMembershipEventDocument } from '../fixtures' +import { getTestValidatorContext } from '../../../../../.storybook/decorators' + +const meta: Meta = { + title: 'Inputs/AlphaPrintButton', + args: { onChange: fn() }, + decorators: [ + (Story) => ( + + + + ) + ] +} + +export default meta + +const tRPCMsw = createTRPCMsw({ + links: [ + httpLink({ + url: '/api/events' + }) + ], + transformer: { input: superjson, output: superjson } +}) + +const StyledFormFieldGenerator = styled(FormFieldGenerator)` + width: 400px; +` +export const Default: StoryObj = { + parameters: { + reactRouter: { + router: { + path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }), + element: ( + noop()} + /> + ) + }, + initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }) + }, + msw: { + handlers: { + event: [ + tRPCMsw.event.config.get.query(() => { + return [tennisClubMembershipEvent] + }) + ] + } + } + } +} + +function getParameters(element: React.ReactNode = null) { + return { + parameters: { + reactRouter: { + router: { + path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }) + }, + initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }), + element + }, + msw: { + handlers: { + event: [ + tRPCMsw.event.config.get.query(() => { + return [tennisClubMembershipEvent] + }) + ] + } + } + } + } +} + +export const WithEnableCondition: StoryObj<{ + role: TestUserRole + fullEvent: EventDocument +}> = { + parameters: { + reactRouter: { + router: { + path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }), + element: ( + action.type === ActionType.CREATE + ) + } + )} + onChange={() => noop()} + /> + ) + }, + initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }) + }, + msw: { + handlers: { + event: [ + tRPCMsw.event.config.get.query(() => { + return [tennisClubMembershipEvent] + }) + ] + } + } + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + const button = await canvas.findByTestId('storybook____name') + + await expect(button).toBeEnabled() + } +} + +export const WithDisableCondition: StoryObj<{ + role: TestUserRole + fullEvent: EventDocument +}> = { + parameters: { + reactRouter: { + router: { + path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }), + element: ( + noop()} + /> + ) + }, + initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }) + }, + msw: { + handlers: { + event: [ + tRPCMsw.event.config.get.query(() => { + return [tennisClubMembershipEvent] + }) + ] + } + } + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + const button = await canvas.findByTestId('storybook____name') + + await expect(button).toBeDisabled() + } +} diff --git a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx deleted file mode 100644 index 0796e67aa6a..00000000000 --- a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 type { Meta, StoryObj } from '@storybook/react' -import { fn } from '@storybook/test' -import React from 'react' -import styled from 'styled-components' -import { createTRPCMsw, httpLink } from '@vafanassieff/msw-trpc' -import superjson from 'superjson' -import { FieldType, tennisClubMembershipEvent } from '@opencrvs/commons/client' -import { ROUTES } from '@client/v2-events/routes' -import { FormFieldGenerator } from '@client/v2-events/components/forms/FormFieldGenerator' -import { AppRouter, TRPCProvider } from '@client/v2-events/trpc' - -import { noop } from '@client/v2-events' -import { tennisClubMembershipEventDocument } from '../fixtures' -import { getTestValidatorContext } from '../../../../../.storybook/decorators' - -const meta: Meta = { - title: 'Inputs/AlphaPrintButton', - args: { onChange: fn() }, - decorators: [ - (Story) => ( - - - - ) - ] -} - -export default meta - -const tRPCMsw = createTRPCMsw({ - links: [ - httpLink({ - url: '/api/events' - }) - ], - transformer: { input: superjson, output: superjson } -}) - -const StyledFormFieldGenerator = styled(FormFieldGenerator)` - width: 400px; -` -export const Default: StoryObj = { - parameters: { - reactRouter: { - router: { - path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }), - element: ( - noop()} - /> - ) - }, - initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }) - }, - msw: { - handlers: { - event: [ - tRPCMsw.event.config.get.query(() => { - return [tennisClubMembershipEvent] - }) - ] - } - } - } -} From 053d0b848f1ebac25e791274f35d57a2ce850d28 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Tue, 18 Nov 2025 19:57:48 +0600 Subject: [PATCH 05/11] chore: update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47f026cbe06..6d562f621c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ HTTP input now accepts `field('..')` references in the HTTP body definition. +## 1.9.2 + +### Bug fixes + +- Fixes an issue where `event.hasAction` was not working in form configurations + ## 1.9.1 ### Breaking changes From 3b42e45ee54e1371e1b9ac54e22fd9bd69e4a3ca Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Wed, 19 Nov 2025 15:55:48 +0600 Subject: [PATCH 06/11] fix: update implementations --- .../src/v2-events/features/events/actions/declare/Pages.tsx | 4 ++-- .../src/v2-events/features/events/actions/declare/Review.tsx | 4 ++-- .../src/v2-events/features/events/actions/register/Pages.tsx | 2 +- .../src/v2-events/features/events/actions/register/Review.tsx | 2 +- .../src/v2-events/features/events/actions/validate/Pages.tsx | 2 +- .../src/v2-events/features/events/actions/validate/Review.tsx | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx b/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx index 81c761a5f7a..a5e5c56e1db 100644 --- a/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/declare/Pages.tsx @@ -39,8 +39,8 @@ export function Pages() { const { saveAndExitModal, handleSaveAndExit } = useSaveAndExitModal() const { getFormValues, setFormValues } = useEventFormData() const formValues = getFormValues() - const validatorContext = useValidatorContext() const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration: configuration } = useEventConfiguration( event.type @@ -109,7 +109,7 @@ export function Pages() { pageId={currentPageId} setFormData={(data) => setFormValues(data)} showReviewButton={searchParams.from === 'review'} - validatorContext={{ ...validatorContext, event }} + validatorContext={validatorContext} onPageChange={(nextPageId: string) => navigate( ROUTES.V2.EVENTS.DECLARE.PAGES.buildPath( diff --git a/packages/client/src/v2-events/features/events/actions/declare/Review.tsx b/packages/client/src/v2-events/features/events/actions/declare/Review.tsx index 3172f1a889f..eaf39eb14a5 100644 --- a/packages/client/src/v2-events/features/events/actions/declare/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/declare/Review.tsx @@ -55,13 +55,13 @@ export function Review() { const drafts = useDrafts() const navigate = useNavigate() - const validatorContext = useValidatorContext() const [modal, openModal] = useModal() const { formatMessage } = useIntlFormatMessageWithFlattenedParams() const { closeActionView } = useEventFormNavigation() const { saveAndExitModal, handleSaveAndExit } = useSaveAndExitModal() const event = events.getEvent.getFromCache(eventId) + const validatorContext = useValidatorContext(event) const { eventConfiguration: config } = useEventConfiguration(event.type) @@ -200,7 +200,7 @@ export function Review() { formConfig={formConfig} reviewFields={reviewConfig.fields} title={formatMessage(reviewConfig.title, form)} - validatorContext={{ ...validatorContext, event }} + validatorContext={validatorContext} onAnnotationChange={(values) => setAnnotation(values)} onEdit={handleEdit} > diff --git a/packages/client/src/v2-events/features/events/actions/register/Pages.tsx b/packages/client/src/v2-events/features/events/actions/register/Pages.tsx index 18da94cc0db..b9b5fbb5e41 100644 --- a/packages/client/src/v2-events/features/events/actions/register/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/register/Pages.tsx @@ -86,7 +86,7 @@ export function Pages() { pageId={currentPageId} setFormData={(data) => setFormValues(data)} showReviewButton={searchParams.from === 'review'} - validatorContext={{ ...validatorContext, event }} + validatorContext={validatorContext} onPageChange={(nextPageId: string) => navigate( ROUTES.V2.EVENTS.REGISTER.PAGES.buildPath( diff --git a/packages/client/src/v2-events/features/events/actions/register/Review.tsx b/packages/client/src/v2-events/features/events/actions/register/Review.tsx index fcc1b34c9f3..09538d656bb 100644 --- a/packages/client/src/v2-events/features/events/actions/register/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/register/Review.tsx @@ -217,7 +217,7 @@ export function Review() { previousFormValues={previousFormValues} reviewFields={reviewConfig.fields} title={formatMessage(reviewConfig.title, form)} - validatorContext={{ ...validatorContext, event }} + validatorContext={validatorContext} onAnnotationChange={(values) => setAnnotation(values)} onEdit={handleEdit} > diff --git a/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx b/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx index b778e03fd4e..856b69e62cd 100644 --- a/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/validate/Pages.tsx @@ -87,7 +87,7 @@ export function Pages() { pageId={currentPageId} setFormData={(data) => setFormValues(data)} showReviewButton={searchParams.from === 'review'} - validatorContext={{ ...validatorContext, event }} + validatorContext={validatorContext} onPageChange={(nextPageId: string) => navigate( ROUTES.V2.EVENTS.VALIDATE.PAGES.buildPath( diff --git a/packages/client/src/v2-events/features/events/actions/validate/Review.tsx b/packages/client/src/v2-events/features/events/actions/validate/Review.tsx index 876ba9bdc22..29efba78524 100644 --- a/packages/client/src/v2-events/features/events/actions/validate/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/validate/Review.tsx @@ -223,7 +223,7 @@ export function Review() { previousFormValues={previousFormValues} reviewFields={reviewConfig.fields} title={formatMessage(reviewConfig.title, form)} - validatorContext={{ ...validatorContext, event }} + validatorContext={validatorContext} onAnnotationChange={(values) => setAnnotation(values)} onEdit={handleEdit} > From 4fadb774ef027996c65b0d8edf9c58e6c779895b Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Wed, 19 Nov 2025 17:47:38 +0600 Subject: [PATCH 07/11] fix: pass event in action procedure --- .../features/events/useEvents/procedures/actions/action.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/client/src/v2-events/features/events/useEvents/procedures/actions/action.ts b/packages/client/src/v2-events/features/events/useEvents/procedures/actions/action.ts index fbd57fcbc0f..d1b07ec2fdf 100644 --- a/packages/client/src/v2-events/features/events/useEvents/procedures/actions/action.ts +++ b/packages/client/src/v2-events/features/events/useEvents/procedures/actions/action.ts @@ -455,7 +455,10 @@ export function useEventAction

>( eventConfiguration, originalDeclaration, declarationDiff: params.declaration, - validatorContext + validatorContext: { + ...validatorContext, + event: findLocalEventDocument(eventId) + } }), annotation } @@ -513,7 +516,7 @@ export function useEventCustomAction( eventConfiguration, originalDeclaration, declarationDiff: params.declaration, - validatorContext + validatorContext: { ...validatorContext, event: localEvent } }) }) } From 9d9fe10ab3b2299e10a5dc44eb3ed98eeb14f186 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Wed, 19 Nov 2025 17:50:24 +0600 Subject: [PATCH 08/11] chore: update CHANGELOG.md to link the corresponding issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d562f621c7..9e78fe91282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ HTTP input now accepts `field('..')` references in the HTTP body definition. ### Bug fixes -- Fixes an issue where `event.hasAction` was not working in form configurations +- Fixes an issue where `event.hasAction` was not working in form configurations [#11074](https://github.com/opencrvs/opencrvs-core/issues/11074) ## 1.9.1 From 956ed8437a11a4bea129d124cfa914f5317712d1 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Wed, 26 Nov 2025 20:23:21 +0600 Subject: [PATCH 09/11] chore(wip): tests --- .../AlphaPrintButton.interaction.stories.tsx | 275 ++++++------------ .../AlphaPrintButton.stories.tsx | 96 ++++++ 2 files changed, 184 insertions(+), 187 deletions(-) create mode 100644 packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx diff --git a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx index 7ae5a824bf4..30426ce6c7f 100644 --- a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx +++ b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx @@ -25,7 +25,6 @@ import { TestUserRole, user, not, - ValidatorContext, EventDocument } from '@opencrvs/commons/client' import { ROUTES } from '@client/v2-events/routes' @@ -36,20 +35,6 @@ import { noop } from '@client/v2-events' import { tennisClubMembershipEventDocument } from '../fixtures' import { getTestValidatorContext } from '../../../../../.storybook/decorators' -const meta: Meta = { - title: 'Inputs/AlphaPrintButton', - args: { onChange: fn() }, - decorators: [ - (Story) => ( - - - - ) - ] -} - -export default meta - const tRPCMsw = createTRPCMsw({ links: [ httpLink({ @@ -59,37 +44,22 @@ const tRPCMsw = createTRPCMsw({ transformer: { input: superjson, output: superjson } }) -const StyledFormFieldGenerator = styled(FormFieldGenerator)` - width: 400px; -` -export const Default: StoryObj = { +const meta: Meta = { + title: 'Inputs/AlphaPrintButton/Interaction', + args: { onChange: fn() }, + decorators: [ + (Story) => ( + + + + ) + ], parameters: { reactRouter: { router: { path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ eventId: tennisClubMembershipEventDocument.id - }), - element: ( - noop()} - /> - ) + }) }, initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ eventId: tennisClubMembershipEventDocument.id @@ -107,96 +77,51 @@ export const Default: StoryObj = { } } -function getParameters(element: React.ReactNode = null) { - return { - parameters: { - reactRouter: { - router: { - path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }) - }, - initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }), - element - }, - msw: { - handlers: { - event: [ - tRPCMsw.event.config.get.query(() => { - return [tennisClubMembershipEvent] - }) +export default meta + +const StyledFormFieldGenerator = styled(FormFieldGenerator)` + width: 400px; +` + +export const WithEnableCondition: StoryObj<{}> = { + render: () => ( + = { - parameters: { - reactRouter: { - router: { - path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }), - element: ( - action.type === ActionType.CREATE - ) - } - )} - onChange={() => noop()} - /> - ) - }, - initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }) - }, - msw: { - handlers: { - event: [ - tRPCMsw.event.config.get.query(() => { - return [tennisClubMembershipEvent] - }) - ] - } - } - }, + ]} + id="my-form" + validatorContext={getTestValidatorContext( + TestUserRole.Enum.LOCAL_REGISTRAR, + { + ...tennisClubMembershipEventDocument, + actions: tennisClubMembershipEventDocument.actions.filter( + (action) => action.type === ActionType.CREATE + ) + } + )} + onChange={() => noop()} + /> + ), play: async ({ canvasElement }) => { const canvas = within(canvasElement) const button = await canvas.findByTestId('storybook____name') @@ -205,64 +130,40 @@ export const WithEnableCondition: StoryObj<{ } } -export const WithDisableCondition: StoryObj<{ - role: TestUserRole - fullEvent: EventDocument -}> = { - parameters: { - reactRouter: { - router: { - path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }), - element: ( - noop()} - /> - ) - }, - initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }) - }, - msw: { - handlers: { - event: [ - tRPCMsw.event.config.get.query(() => { - return [tennisClubMembershipEvent] - }) - ] - } - } - }, +export const WithDisableCondition: StoryObj<{}> = { + render: () => ( + noop()} + /> + ), play: async ({ canvasElement }) => { const canvas = within(canvasElement) const button = await canvas.findByTestId('storybook____name') diff --git a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx new file mode 100644 index 00000000000..0796e67aa6a --- /dev/null +++ b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.stories.tsx @@ -0,0 +1,96 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react' +import { fn } from '@storybook/test' +import React from 'react' +import styled from 'styled-components' +import { createTRPCMsw, httpLink } from '@vafanassieff/msw-trpc' +import superjson from 'superjson' +import { FieldType, tennisClubMembershipEvent } from '@opencrvs/commons/client' +import { ROUTES } from '@client/v2-events/routes' +import { FormFieldGenerator } from '@client/v2-events/components/forms/FormFieldGenerator' +import { AppRouter, TRPCProvider } from '@client/v2-events/trpc' + +import { noop } from '@client/v2-events' +import { tennisClubMembershipEventDocument } from '../fixtures' +import { getTestValidatorContext } from '../../../../../.storybook/decorators' + +const meta: Meta = { + title: 'Inputs/AlphaPrintButton', + args: { onChange: fn() }, + decorators: [ + (Story) => ( + + + + ) + ] +} + +export default meta + +const tRPCMsw = createTRPCMsw({ + links: [ + httpLink({ + url: '/api/events' + }) + ], + transformer: { input: superjson, output: superjson } +}) + +const StyledFormFieldGenerator = styled(FormFieldGenerator)` + width: 400px; +` +export const Default: StoryObj = { + parameters: { + reactRouter: { + router: { + path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }), + element: ( + noop()} + /> + ) + }, + initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }) + }, + msw: { + handlers: { + event: [ + tRPCMsw.event.config.get.query(() => { + return [tennisClubMembershipEvent] + }) + ] + } + } + } +} From 48fd3be4ebdd9be542231e4e1e88ea1298dafd89 Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Thu, 27 Nov 2025 20:52:54 +0600 Subject: [PATCH 10/11] refactor: get story parameters from conditional and validation context --- .../AlphaPrintButton.interaction.stories.tsx | 148 ++++++++---------- 1 file changed, 68 insertions(+), 80 deletions(-) diff --git a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx index 30426ce6c7f..8c54b586ad1 100644 --- a/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx +++ b/packages/client/src/v2-events/features/events/registered-fields/AlphaPrintButton.interaction.stories.tsx @@ -25,7 +25,9 @@ import { TestUserRole, user, not, - EventDocument + EventDocument, + FieldConditional, + ValidatorContext } from '@opencrvs/commons/client' import { ROUTES } from '@client/v2-events/routes' import { FormFieldGenerator } from '@client/v2-events/components/forms/FormFieldGenerator' @@ -55,16 +57,6 @@ const meta: Meta = { ) ], parameters: { - reactRouter: { - router: { - path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }) - }, - initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ - eventId: tennisClubMembershipEventDocument.id - }) - }, msw: { handlers: { event: [ @@ -82,45 +74,61 @@ export default meta const StyledFormFieldGenerator = styled(FormFieldGenerator)` width: 400px; ` +function createAlphaPrintButtonStoryParameters( + conditional: FieldConditional, + validatorContext: ValidatorContext +) { + return { + reactRouter: { + router: { + path: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }), + element: ( + noop()} + /> + ) + }, + initialPath: ROUTES.V2.EVENTS.DECLARE.REVIEW.buildPath({ + eventId: tennisClubMembershipEventDocument.id + }) + } + } +} export const WithEnableCondition: StoryObj<{}> = { - render: () => ( - action.type === ActionType.CREATE - ) - } - )} - onChange={() => noop()} - /> + parameters: createAlphaPrintButtonStoryParameters( + { + type: ConditionalType.ENABLE, + conditional: and( + user.hasRole(TestUserRole.Enum.LOCAL_REGISTRAR), + not(event.hasAction(ActionType.DECLARE)) + ) + }, + getTestValidatorContext(TestUserRole.Enum.LOCAL_REGISTRAR, { + ...tennisClubMembershipEventDocument, + actions: tennisClubMembershipEventDocument.actions.filter( + (action) => action.type === ActionType.CREATE + ) + }) ), play: async ({ canvasElement }) => { const canvas = within(canvasElement) @@ -131,38 +139,18 @@ export const WithEnableCondition: StoryObj<{}> = { } export const WithDisableCondition: StoryObj<{}> = { - render: () => ( - noop()} - /> + parameters: createAlphaPrintButtonStoryParameters( + { + type: ConditionalType.ENABLE, + conditional: and( + user.hasRole(TestUserRole.Enum.LOCAL_REGISTRAR), + not(event.hasAction(ActionType.DECLARE)) + ) + }, + getTestValidatorContext( + TestUserRole.Enum.LOCAL_REGISTRAR, + tennisClubMembershipEventDocument + ) ), play: async ({ canvasElement }) => { const canvas = within(canvasElement) From b3808bc48742df2d5dd87c9c638e3d4e70618a8e Mon Sep 17 00:00:00 2001 From: tahmidrahman-dsi Date: Fri, 12 Dec 2025 16:27:19 +0600 Subject: [PATCH 11/11] update CHANGELOG.md --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce8c116338a..3ebb7ed5215 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,16 +14,16 @@ HTTP input now accepts `field('..')` references in the HTTP body definition. - Introduced form page level config - `requireCompletionToContinue` to enforce full completion of the form page before moving to the next page. +### Bug fixes + +- Fixes an issue where `event.hasAction` was not working in form configurations [#11074](https://github.com/opencrvs/opencrvs-core/issues/11074) + ## 1.9.2 ### New features - Toolkit now exports `window().location.get` to country config that can be used as a template variable e.g. in HttpField request body. -### Bug fixes - -- Fixes an issue where `event.hasAction` was not working in form configurations [#11074](https://github.com/opencrvs/opencrvs-core/issues/11074) - ## 1.9.1 ### Breaking changes