From d92321a556781ce43dd99295154d49b4fceb0387 Mon Sep 17 00:00:00 2001 From: cibelius Date: Mon, 8 Dec 2025 19:47:51 +0200 Subject: [PATCH 01/45] define edit action config --- packages/commons/src/events/ActionInput.ts | 11 ++++++++++- packages/commons/src/events/ActionType.ts | 4 ++++ packages/commons/src/events/scopes.ts | 1 + packages/commons/src/scopes-v2.ts | 3 ++- packages/commons/src/scopes.ts | 1 + packages/events/src/router/event/actions/index.ts | 7 ++++++- .../src/router/event/event.actions.edit.test.ts | 0 packages/events/src/router/event/index.ts | 1 + packages/events/src/service/events/events.ts | 1 + 9 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 packages/events/src/router/event/event.actions.edit.test.ts diff --git a/packages/commons/src/events/ActionInput.ts b/packages/commons/src/events/ActionInput.ts index f6fa8d7566d..f22d4be404d 100644 --- a/packages/commons/src/events/ActionInput.ts +++ b/packages/commons/src/events/ActionInput.ts @@ -80,6 +80,14 @@ export const DeclareActionInput = BaseActionInput.extend( }).shape ) +export const EditActionInput = BaseActionInput.extend( + z.object({ + type: z.literal(ActionType.EDIT).default(ActionType.EDIT) + }).shape +) + +export type EditActionInput = z.infer + export const PrintCertificateActionInput = BaseActionInput.extend( z.object({ type: z @@ -262,7 +270,8 @@ export const ActionInput = z id: 'ApproveCorrectionActionInput' }), ReadActionInput.meta({ id: 'ReadActionInput' }), - CustomActionInput.meta({ id: 'CustomActionInput' }) + CustomActionInput.meta({ id: 'CustomActionInput' }), + EditActionInput.meta({ id: 'EditActionInput' }) ]) .meta({ id: 'ActionInput' diff --git a/packages/commons/src/events/ActionType.ts b/packages/commons/src/events/ActionType.ts index 97f3558a5b6..45182e5677b 100644 --- a/packages/commons/src/events/ActionType.ts +++ b/packages/commons/src/events/ActionType.ts @@ -23,6 +23,7 @@ export const ActionType = { DECLARE: 'DECLARE', VALIDATE: 'VALIDATE', REGISTER: 'REGISTER', + EDIT: 'EDIT', // Declaration system actions. Non-configurable. DUPLICATE_DETECTED: 'DUPLICATE_DETECTED', REJECT: 'REJECT', // REJECT_DECLARATION @@ -47,6 +48,7 @@ export type ActionType = (typeof ActionType)[keyof typeof ActionType] export const ConfirmableActions = [ ActionType.NOTIFY, ActionType.DECLARE, + ActionType.EDIT, ActionType.VALIDATE, ActionType.REGISTER, ActionType.REJECT, @@ -64,6 +66,7 @@ export const ActionTypes = z.enum([ 'CREATE', 'NOTIFY', 'DECLARE', + 'EDIT', 'VALIDATE', 'REGISTER', 'DUPLICATE_DETECTED', @@ -93,6 +96,7 @@ export type ClientSpecificAction = const declarationActionValues = [ ActionTypes.enum.DECLARE, + ActionTypes.enum.EDIT, ActionTypes.enum.VALIDATE, ActionTypes.enum.REGISTER, ActionTypes.enum.NOTIFY, diff --git a/packages/commons/src/events/scopes.ts b/packages/commons/src/events/scopes.ts index d75da355d15..d5b78634f71 100644 --- a/packages/commons/src/events/scopes.ts +++ b/packages/commons/src/events/scopes.ts @@ -35,6 +35,7 @@ export const ACTION_SCOPE_MAP = { 'record.declared.validate', 'record.register' ], + [ActionType.EDIT]: ['record.declared.edit'], [ActionType.DELETE]: ['record.declare'], [ActionType.VALIDATE]: ['record.declared.validate', 'record.register'], [ActionType.REGISTER]: ['record.register'], diff --git a/packages/commons/src/scopes-v2.ts b/packages/commons/src/scopes-v2.ts index 6b08fc4f1b4..3c7503c8f43 100644 --- a/packages/commons/src/scopes-v2.ts +++ b/packages/commons/src/scopes-v2.ts @@ -139,7 +139,8 @@ const v1ToV2ConfigScopeTypeMap: Record = { 'record.registered.print-certified-copies': 'record.print-certified-copies', 'record.registered.request-correction': 'record.request-correction', 'record.registered.correct': 'record.correct', - 'record.custom-action': 'record.custom-action' + 'record.custom-action': 'record.custom-action', + 'record.declared.edit': 'record.edit' } /** diff --git a/packages/commons/src/scopes.ts b/packages/commons/src/scopes.ts index ae6b613f94a..bb21b741715 100644 --- a/packages/commons/src/scopes.ts +++ b/packages/commons/src/scopes.ts @@ -279,6 +279,7 @@ export const RecordScopeType = z.enum([ 'record.read', 'record.declare', 'record.notify', + 'record.declared.edit', 'record.declared.validate', 'record.declared.reject', 'record.declared.archive', diff --git a/packages/events/src/router/event/actions/index.ts b/packages/events/src/router/event/actions/index.ts index d1237e49490..7576b677a76 100644 --- a/packages/events/src/router/event/actions/index.ts +++ b/packages/events/src/router/event/actions/index.ts @@ -32,7 +32,8 @@ import { getPendingAction, ActionInputWithType, EventConfig, - CustomActionInput + CustomActionInput, + EditActionInput } from '@opencrvs/commons/events' import { TokenUserType, @@ -79,6 +80,10 @@ const defaultConfig = { } as const const ACTION_PROCEDURE_CONFIG = { + [ActionType.EDIT]: { + ...defaultConfig, + inputSchema: EditActionInput + }, [ActionType.CUSTOM]: { ...defaultConfig, inputSchema: CustomActionInput diff --git a/packages/events/src/router/event/event.actions.edit.test.ts b/packages/events/src/router/event/event.actions.edit.test.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/events/src/router/event/index.ts b/packages/events/src/router/event/index.ts index 2d353146298..fc3f7573f78 100644 --- a/packages/events/src/router/event/index.ts +++ b/packages/events/src/router/event/index.ts @@ -215,6 +215,7 @@ export const eventRouter = router({ actions: router({ notify: router(getDefaultActionProcedures(ActionType.NOTIFY)), declare: router(declareActionProcedures()), + edit: router(getDefaultActionProcedures(ActionType.EDIT)), validate: router(getDefaultActionProcedures(ActionType.VALIDATE)), reject: router(getDefaultActionProcedures(ActionType.REJECT)), archive: router(getDefaultActionProcedures(ActionType.ARCHIVE)), diff --git a/packages/events/src/service/events/events.ts b/packages/events/src/service/events/events.ts index 7530887db2e..5d7de253f1d 100644 --- a/packages/events/src/service/events/events.ts +++ b/packages/events/src/service/events/events.ts @@ -319,6 +319,7 @@ export function buildAction( case ActionType.DUPLICATE_DETECTED: case ActionType.MARK_AS_NOT_DUPLICATE: case ActionType.MARK_AS_DUPLICATE: + case ActionType.EDIT: case ActionType.REQUEST_CORRECTION: { return commonAttributes } From b3ced825d590ffb54bd42686c3d96b3e76c5bccc Mon Sep 17 00:00:00 2001 From: cibelius Date: Tue, 9 Dec 2025 00:27:07 +0200 Subject: [PATCH 02/45] progress with ui --- .../events/actions/edit/EditActionMenu.tsx | 80 +++++++++++++ .../features/events/actions/edit/Pages.tsx | 100 +++++++++++++++++ .../features/events/actions/edit/Review.tsx | 106 ++++++++++++++++++ .../features/events/actions/edit/index.tsx | 18 +++ .../events/components/Action/utils.ts | 5 + .../useAllowedActionConfigurations.tsx | 19 ++++ .../src/v2-events/layouts/form/utils.ts | 1 + .../client/src/v2-events/routes/config.tsx | 23 ++++ .../client/src/v2-events/routes/routes.ts | 25 ++++- packages/commons/src/events/ActionConfig.ts | 1 + packages/commons/src/events/ActionDocument.ts | 7 ++ .../src/events/state/availableActions.ts | 6 +- 12 files changed, 384 insertions(+), 7 deletions(-) create mode 100644 packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx create mode 100644 packages/client/src/v2-events/features/events/actions/edit/Pages.tsx create mode 100644 packages/client/src/v2-events/features/events/actions/edit/Review.tsx create mode 100644 packages/client/src/v2-events/features/events/actions/edit/index.tsx diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx new file mode 100644 index 00000000000..63cee003a49 --- /dev/null +++ b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx @@ -0,0 +1,80 @@ +/* + * 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 React from 'react' +import { useIntl } from 'react-intl' +import { EventDocument } from '@opencrvs/commons/client' +import { PrimaryButton } from '@opencrvs/components/lib/buttons' +import { DropdownMenu } from '@opencrvs/components/lib/Dropdown' +import { CaretDown } from '@opencrvs/components/lib/Icon/all-icons' +import { Icon } from '@opencrvs/components' +import { messages } from '@client/i18n/messages/views/action' + +const actions = [ + { + icon: 'PaperPlaneTilt' as const, + label: { + defaultMessage: 'Register with edits', + description: 'Label for "Register with edits" in edit action menu', + id: 'event.edit.registerWithEdits' + }, + onClick: () => console.log('todo') + }, + { + icon: 'PaperPlaneTilt' as const, + label: { + defaultMessage: 'Declare with edits', + description: 'Label for "Declare with edits" in edit action menu', + id: 'event.edit.declareWithEdits' + }, + onClick: () => console.log('todo') + }, + { + icon: 'ArchiveBox' as const, + label: { + defaultMessage: 'Cancel edits', + description: 'Label for "Cancel edits" in edit action menu', + id: 'event.edit.cancelEdits' + }, + onClick: () => console.log('todo') + } +] + +/** + * Menu component available on the edit review page. + * */ +export function DeclareActionMenu({ event }: { event: EventDocument }) { + const intl = useIntl() + + return ( + <> + + + } + size="medium" + > + {intl.formatMessage(messages.action)} + + + + {actions.map(({ onClick, icon, label }, index) => ( + + + {intl.formatMessage(label)} + + ))} + + + {/* {modals} */} + + ) +} diff --git a/packages/client/src/v2-events/features/events/actions/edit/Pages.tsx b/packages/client/src/v2-events/features/events/actions/edit/Pages.tsx new file mode 100644 index 00000000000..47fbfd04192 --- /dev/null +++ b/packages/client/src/v2-events/features/events/actions/edit/Pages.tsx @@ -0,0 +1,100 @@ +/* + * 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 React, { useEffect } from 'react' +import { useNavigate } from 'react-router-dom' +import { + useTypedParams, + useTypedSearchParams +} from 'react-router-typesafe-routes/dom' +import { ActionType, getDeclarationPages } from '@opencrvs/commons/client' +import { Pages as PagesComponent } from '@client/v2-events/features/events/components/Pages' +import { useEventFormData } from '@client/v2-events/features/events/useEventFormData' +import { useEventFormNavigation } from '@client/v2-events/features/events/useEventFormNavigation' +import { FormLayout } from '@client/v2-events/layouts' +import { ROUTES } from '@client/v2-events/routes' +import { useDrafts } from '@client/v2-events/features/drafts/useDrafts' +import { isTemporaryId } from '@client/v2-events/utils' +import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' +import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration' +import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' + +export function Pages() { + const { eventId, pageId } = useTypedParams(ROUTES.V2.EVENTS.EDIT.PAGES) + const [searchParams] = useTypedSearchParams(ROUTES.V2.EVENTS.EDIT.PAGES) + const events = useEvents() + const navigate = useNavigate() + const drafts = useDrafts() + const { modal, closeActionView } = useEventFormNavigation() + const { getFormValues, setFormValues } = useEventFormData() + const formValues = getFormValues() + const validatorContext = useValidatorContext() + const event = events.getEvent.getFromCache(eventId) + + const { eventConfiguration: configuration } = useEventConfiguration( + event.type + ) + const declarationPages = getDeclarationPages(configuration) + + const currentPageId = + declarationPages.find((p) => p.id === pageId)?.id || declarationPages[0]?.id + + if (!currentPageId) { + throw new Error('Form does not have any pages') + } + + useEffect(() => { + if (pageId !== currentPageId) { + navigate( + ROUTES.V2.EVENTS.EDIT.PAGES.buildPath( + { + eventId, + pageId: currentPageId + }, + searchParams + ), + { replace: true } + ) + } + }, [pageId, currentPageId, navigate, eventId, searchParams]) + + return ( + + {modal} + setFormValues(data)} + showReviewButton={searchParams.from === 'review'} + validatorContext={validatorContext} + onPageChange={(nextPageId: string) => + navigate( + ROUTES.V2.EVENTS.EDIT.PAGES.buildPath( + { eventId, pageId: nextPageId }, + searchParams + ) + ) + } + onSubmit={() => + navigate( + ROUTES.V2.EVENTS.EDIT.REVIEW.buildPath( + { eventId }, + { workqueue: searchParams.workqueue } + ) + ) + } + /> + + ) +} diff --git a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx new file mode 100644 index 00000000000..4f47c89aa03 --- /dev/null +++ b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx @@ -0,0 +1,106 @@ +/* + * 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 React from 'react' +import { useNavigate } from 'react-router-dom' +import { + useTypedParams, + useTypedSearchParams +} from 'react-router-typesafe-routes/dom' +import { ActionType, getDeclaration } from '@opencrvs/commons/client' +import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration' +import { useEventFormData } from '@client/v2-events/features/events/useEventFormData' +import { useActionAnnotation } from '@client/v2-events/features/events/useActionAnnotation' +import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' +import { useModal } from '@client/v2-events/hooks/useModal' +import { ROUTES } from '@client/v2-events/routes' +import { Review as ReviewComponent } from '@client/v2-events/features/events/components/Review' +import { FormLayout } from '@client/v2-events/layouts' +import { makeFormFieldIdFormikCompatible } from '@client/v2-events/components/forms/utils' +import { withSuspense } from '@client/v2-events/components/withSuspense' +import { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils' +import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' + +export function Review() { + const { eventId } = useTypedParams(ROUTES.V2.EVENTS.EDIT.REVIEW) + const [{ workqueue: slug }] = useTypedSearchParams( + ROUTES.V2.EVENTS.EDIT.REVIEW + ) + const events = useEvents() + const navigate = useNavigate() + const validatorContext = useValidatorContext() + const [modal, openModal] = useModal() + const { formatMessage } = useIntlFormatMessageWithFlattenedParams() + const event = events.getEvent.getFromCache(eventId) + const { eventConfiguration: config } = useEventConfiguration(event.type) + const formConfig = getDeclaration(config) + const actionConfiguration = config.actions.find( + (a) => a.type === ActionType.DECLARE + ) + if (!actionConfiguration) { + throw new Error('Action configuration not found') + } + + const reviewConfig = actionConfiguration.review + const form = useEventFormData((state) => state.getFormValues()) + + const { setAnnotation, getAnnotation } = useActionAnnotation() + const annotation = getAnnotation() + + async function handleEdit({ + pageId, + fieldId, + confirmation + }: { + pageId: string + fieldId?: string + confirmation?: boolean + }) { + const confirmedEdit = + confirmation || + (await openModal((close) => ( + + ))) + + if (confirmedEdit) { + navigate( + ROUTES.V2.EVENTS.EDIT.PAGES.buildPath( + { pageId, eventId }, + { + from: 'review', + workqueue: slug + }, + fieldId ? makeFormFieldIdFormikCompatible(fieldId) : undefined + ) + ) + } + + return + } + + return ( + Foo} route={ROUTES.V2.EVENTS.EDIT}> + setAnnotation(values)} + onEdit={handleEdit} + /> + {modal} + + ) +} + +export const ReviewIndex = withSuspense(Review) diff --git a/packages/client/src/v2-events/features/events/actions/edit/index.tsx b/packages/client/src/v2-events/features/events/actions/edit/index.tsx new file mode 100644 index 00000000000..115cf6e9bfb --- /dev/null +++ b/packages/client/src/v2-events/features/events/actions/edit/index.tsx @@ -0,0 +1,18 @@ +/* + * 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 { withSuspense } from '@client/v2-events/components/withSuspense' +import { Pages } from './Pages' +import { Review } from './Review' + +const PagesIndex = withSuspense(Pages) +const ReviewIndex = withSuspense(Review) +export { PagesIndex as Pages, ReviewIndex as Review } diff --git a/packages/client/src/v2-events/features/events/components/Action/utils.ts b/packages/client/src/v2-events/features/events/components/Action/utils.ts index 21bf3617c00..8fbf38f41ca 100644 --- a/packages/client/src/v2-events/features/events/components/Action/utils.ts +++ b/packages/client/src/v2-events/features/events/components/Action/utils.ts @@ -52,6 +52,7 @@ export type AvailableActionTypes = Extract< | 'REQUEST_CORRECTION' | 'APPROVE_CORRECTION' | 'REJECT_CORRECTION' + | 'EDIT' > /** @@ -78,6 +79,10 @@ export function getPreviousDeclarationActionType( actionTypes = [ActionType.DECLARE, ActionType.NOTIFY] break } + case ActionType.EDIT: { + actionTypes = [ActionType.DECLARE] + break + } case ActionType.VALIDATE: { actionTypes = [ActionType.VALIDATE, ActionType.DECLARE] break diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx index ef52de69915..2637c2a191f 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx @@ -76,6 +76,9 @@ function getAvailableAssignmentActions( const assignmentStatus = getAssignmentStatus(event, authentication.sub) const eventStatus = event.status + console.log('scopes') + console.log(authentication.scope) + const mayUnassignOthers = configurableEventScopeAllowed( authentication.scope, ['record.unassign-others'], @@ -126,6 +129,11 @@ export const actionLabels = { 'This is shown as the action name anywhere the user can trigger the action from', id: 'event.birth.action.declare.label' }, + [ActionType.EDIT]: { + defaultMessage: 'Edit', + description: 'Edit action label', + id: 'actions.edit' + }, [ActionType.REJECT]: { defaultMessage: 'Reject', description: 'Label for reject button in dropdown menu', @@ -390,6 +398,17 @@ function useViewableActionConfigurations( disabled: !(isDownloadedAndAssignedToUser || hasDeclarationDraftOpen), hidden: shouldHideDeclareAction }, + [ActionType.EDIT]: { + icon: 'PencilLine' as const, + label: actionLabels[ActionType.EDIT], + onClick: (workqueue) => { + clearEphemeralFormState() + return navigate( + ROUTES.V2.EVENTS.EDIT.REVIEW.buildPath({ eventId }, { workqueue }) + ) + }, + disabled: !isDownloadedAndAssignedToUser + }, [ActionType.REJECT]: { label: actionLabels[ActionType.REJECT], icon: 'FileX', diff --git a/packages/client/src/v2-events/layouts/form/utils.ts b/packages/client/src/v2-events/layouts/form/utils.ts index 7ed92ce02b8..31d15ffe2ca 100644 --- a/packages/client/src/v2-events/layouts/form/utils.ts +++ b/packages/client/src/v2-events/layouts/form/utils.ts @@ -22,3 +22,4 @@ export type AllowedRouteWithEventId = | typeof ROUTES.V2.EVENTS.REQUEST_CORRECTION | typeof ROUTES.V2.EVENTS.REVIEW_CORRECTION | typeof ROUTES.V2.EVENTS.REVIEW_POTENTIAL_DUPLICATE + | typeof ROUTES.V2.EVENTS.EDIT diff --git a/packages/client/src/v2-events/routes/config.tsx b/packages/client/src/v2-events/routes/config.tsx index 2e7c5972bbc..999720a820b 100644 --- a/packages/client/src/v2-events/routes/config.tsx +++ b/packages/client/src/v2-events/routes/config.tsx @@ -164,6 +164,29 @@ export const routesConfig = { } ] }, + { + path: ROUTES.V2.EVENTS.EDIT.path, + element: ( + + + + ), + children: [ + { + index: true, + // TODO CIHAN: + element: + }, + { + path: ROUTES.V2.EVENTS.EDIT.PAGES.path, + element: + }, + { + path: ROUTES.V2.EVENTS.EDIT.REVIEW.path, + element: + } + ] + }, correctionRequestRouter, correctionReviewRouter, { diff --git a/packages/client/src/v2-events/routes/routes.ts b/packages/client/src/v2-events/routes/routes.ts index 19e7411e33c..ace773e7e07 100644 --- a/packages/client/src/v2-events/routes/routes.ts +++ b/packages/client/src/v2-events/routes/routes.ts @@ -63,15 +63,11 @@ export const ROUTES = { 'declare/:eventId', { params: { eventId: uuid().defined() }, - searchParams: { - workqueue: string() - } + searchParams: { workqueue: string() } }, { REVIEW: route('review', { - searchParams: { - workqueue: string() - } + searchParams: { workqueue: string() } }), PAGES: route('pages/:pageId', { params: { pageId: string() }, @@ -83,6 +79,23 @@ export const ROUTES = { }) } ), + EDIT: route( + 'edit/:eventId', + { + params: { eventId: uuid().defined() }, + searchParams: { workqueue: string() } + }, + { + REVIEW: route('review', { + searchParams: { workqueue: string() } + }), + PAGES: route('pages/:pageId', { + params: { pageId: string() }, + searchParams: { from: string(), workqueue: string() }, + hash: hashValues() + }) + } + ), PRINT_CERTIFICATE: route( 'print-certificate/:eventId', { diff --git a/packages/commons/src/events/ActionConfig.ts b/packages/commons/src/events/ActionConfig.ts index 983f6040935..036cc63a34a 100644 --- a/packages/commons/src/events/ActionConfig.ts +++ b/packages/commons/src/events/ActionConfig.ts @@ -119,6 +119,7 @@ const CustomActionConfig = ActionConfigBase.merge( ) }) ) + export type CustomActionConfig = z.infer export const ActionConfig = z diff --git a/packages/commons/src/events/ActionDocument.ts b/packages/commons/src/events/ActionDocument.ts index 75f50be4090..aa6fb1be836 100644 --- a/packages/commons/src/events/ActionDocument.ts +++ b/packages/commons/src/events/ActionDocument.ts @@ -200,6 +200,12 @@ const NotifiedAction = ActionBase.extend( }).shape ) +const EditAction = ActionBase.extend( + z.object({ + type: z.literal(ActionType.EDIT) + }).shape +) + export const PrintContent = z.object({ templateId: z.string().optional() }) @@ -273,6 +279,7 @@ export const ActionDocument = z UnassignedAction.meta({ id: 'UnassignedAction' }), PrintCertificateAction.meta({ id: 'PrintCertificateAction' }), ReadAction.meta({ id: 'ReadAction' }), + EditAction.meta({ id: 'EditAction' }), CustomAction.meta({ id: 'CustomAction' }) ]) .meta({ diff --git a/packages/commons/src/events/state/availableActions.ts b/packages/commons/src/events/state/availableActions.ts index 8df3a2331cc..9da3a195c3a 100644 --- a/packages/commons/src/events/state/availableActions.ts +++ b/packages/commons/src/events/state/availableActions.ts @@ -40,7 +40,8 @@ const AVAILABLE_ACTIONS_BY_EVENT_STATUS = { ActionType.MARK_AS_DUPLICATE, ActionType.ARCHIVE, ActionType.REJECT, - ActionType.CUSTOM + ActionType.CUSTOM, + ActionType.EDIT ], [EventStatus.enum.REGISTERED]: [ ActionType.READ, @@ -83,6 +84,9 @@ const ACTION_FILTERS: { [ActionType.VALIDATE]: (flags) => !flags.includes(InherentFlags.POTENTIAL_DUPLICATE) && !flags.some((flag) => flag.endsWith(':requested')), + [ActionType.EDIT]: (flags) => + !flags.includes(InherentFlags.POTENTIAL_DUPLICATE) && + !flags.some((flag) => flag.endsWith(':requested')), [ActionType.REGISTER]: (flags) => !flags.includes(InherentFlags.POTENTIAL_DUPLICATE) && !flags.some((flag) => flag.endsWith(':requested')), From a6d54d32190fbc47c574b795c9de88e78ac0e3b7 Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 10:03:04 +0700 Subject: [PATCH 03/45] add edit action menu --- .../events/actions/edit/EditActionMenu.tsx | 3 +- .../events/actions/edit/EditPageBanner.tsx | 84 +++++++++++++++++++ .../features/events/actions/edit/Pages.tsx | 61 +++++++------- .../features/events/actions/edit/Review.tsx | 43 ++++++---- .../features/events/actions/edit/index.tsx | 2 + .../components/EventHistory/EventHistory.tsx | 14 ++-- .../client/src/v2-events/routes/config.tsx | 8 +- 7 files changed, 160 insertions(+), 55 deletions(-) create mode 100644 packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx index 63cee003a49..c3d3302506d 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx @@ -17,6 +17,7 @@ import { CaretDown } from '@opencrvs/components/lib/Icon/all-icons' import { Icon } from '@opencrvs/components' import { messages } from '@client/i18n/messages/views/action' +// @TODO CIHAN: register/validate should be unavailable if declaration blocks it const actions = [ { icon: 'PaperPlaneTilt' as const, @@ -50,7 +51,7 @@ const actions = [ /** * Menu component available on the edit review page. * */ -export function DeclareActionMenu({ event }: { event: EventDocument }) { +export function EditActionMenu({ event }: { event: EventDocument }) { const intl = useIntl() return ( diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx new file mode 100644 index 00000000000..16932ae76fa --- /dev/null +++ b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx @@ -0,0 +1,84 @@ +/* + * 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 React from 'react' +import styled from 'styled-components' +import { defineMessage, useIntl } from 'react-intl' +import { useTypedParams } from 'react-router-typesafe-routes/dom' +import { Icon } from '@opencrvs/components/lib/Icon' +import { Text } from '@opencrvs/components' +import { ActionType, ActionStatus } from '@opencrvs/commons/client' +import { ROUTES } from '@client/v2-events/routes' +import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' +import { useUsers } from '@client/v2-events/hooks/useUsers' +import { useLocations } from '@client/v2-events/hooks/useLocations' +import { roleMessage } from '@client/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory' + +const Wrapper = styled.div` + display: flex; + flex: 1; + color: ${({ theme }) => theme.colors.white}; + background-color: ${({ theme }) => theme.colors.orangeDarker}; + padding: 8px 20px; + align-items: center; +` + +const StyledText = styled(Text)` + margin-left: 8px; +` + +const message = defineMessage({ + id: 'editPageBanner.message', + defaultMessage: + 'You are editing a birth record declared by Jane Smith ({role} at {location})', + description: 'The message for the edit page banner' +}) + +export function EditPageBanner() { + const { eventId } = useTypedParams(ROUTES.V2.EVENTS.EDIT.PAGES) + const intl = useIntl() + const events = useEvents() + const event = events.getEvent.getFromCache(eventId) + const { getUser } = useUsers() + const users = getUser.getAllCached() + + const { getLocations } = useLocations() + const locations = getLocations.useSuspenseQuery() + + const declarationActions = event.actions.filter( + (a) => a.type === ActionType.DECLARE && a.status === ActionStatus.Accepted + ) + + // Fetch createdAtLocation and createdBy from latest declaration action + const { createdAtLocation, createdBy } = + declarationActions[declarationActions.length - 1] + + const user = users.find((u) => u.id === createdBy) + + const location = createdAtLocation + ? locations.get(createdAtLocation) + : undefined + + // TODO CIHAN: get user name + console.log('foo') + console.log(user) + + const role = intl.formatMessage(roleMessage, { role: user?.role }) + + return ( + + + + {intl.formatMessage(message, { location: location?.name, role })} + + + ) +} diff --git a/packages/client/src/v2-events/features/events/actions/edit/Pages.tsx b/packages/client/src/v2-events/features/events/actions/edit/Pages.tsx index 47fbfd04192..46e793081bb 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/Pages.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/Pages.tsx @@ -21,18 +21,16 @@ import { useEventFormData } from '@client/v2-events/features/events/useEventForm import { useEventFormNavigation } from '@client/v2-events/features/events/useEventFormNavigation' import { FormLayout } from '@client/v2-events/layouts' import { ROUTES } from '@client/v2-events/routes' -import { useDrafts } from '@client/v2-events/features/drafts/useDrafts' -import { isTemporaryId } from '@client/v2-events/utils' import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration' import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' +import { EditPageBanner } from './EditPageBanner' export function Pages() { const { eventId, pageId } = useTypedParams(ROUTES.V2.EVENTS.EDIT.PAGES) const [searchParams] = useTypedSearchParams(ROUTES.V2.EVENTS.EDIT.PAGES) const events = useEvents() const navigate = useNavigate() - const drafts = useDrafts() const { modal, closeActionView } = useEventFormNavigation() const { getFormValues, setFormValues } = useEventFormData() const formValues = getFormValues() @@ -67,34 +65,37 @@ export function Pages() { }, [pageId, currentPageId, navigate, eventId, searchParams]) return ( - - {modal} - setFormValues(data)} - showReviewButton={searchParams.from === 'review'} - validatorContext={validatorContext} - onPageChange={(nextPageId: string) => - navigate( - ROUTES.V2.EVENTS.EDIT.PAGES.buildPath( - { eventId, pageId: nextPageId }, - searchParams + <> + + + {modal} + setFormValues(data)} + showReviewButton={searchParams.from === 'review'} + validatorContext={validatorContext} + onPageChange={(nextPageId: string) => + navigate( + ROUTES.V2.EVENTS.EDIT.PAGES.buildPath( + { eventId, pageId: nextPageId }, + searchParams + ) ) - ) - } - onSubmit={() => - navigate( - ROUTES.V2.EVENTS.EDIT.REVIEW.buildPath( - { eventId }, - { workqueue: searchParams.workqueue } + } + onSubmit={() => + navigate( + ROUTES.V2.EVENTS.EDIT.REVIEW.buildPath( + { eventId }, + { workqueue: searchParams.workqueue } + ) ) - ) - } - /> - + } + /> + + ) } diff --git a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx index 4f47c89aa03..0441fbea5da 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx @@ -15,7 +15,11 @@ import { useTypedParams, useTypedSearchParams } from 'react-router-typesafe-routes/dom' -import { ActionType, getDeclaration } from '@opencrvs/commons/client' +import { + ActionType, + getDeclaration, + getCurrentEventState +} from '@opencrvs/commons/client' import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration' import { useEventFormData } from '@client/v2-events/features/events/useEventFormData' import { useActionAnnotation } from '@client/v2-events/features/events/useActionAnnotation' @@ -28,6 +32,8 @@ import { makeFormFieldIdFormikCompatible } from '@client/v2-events/components/fo import { withSuspense } from '@client/v2-events/components/withSuspense' import { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils' import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' +import { EditActionMenu } from './EditActionMenu' +import { EditPageBanner } from './EditPageBanner' export function Review() { const { eventId } = useTypedParams(ROUTES.V2.EVENTS.EDIT.REVIEW) @@ -42,6 +48,8 @@ export function Review() { const event = events.getEvent.getFromCache(eventId) const { eventConfiguration: config } = useEventConfiguration(event.type) const formConfig = getDeclaration(config) + const currentEventState = getCurrentEventState(event, config) + const previousFormValues = currentEventState.declaration const actionConfiguration = config.actions.find( (a) => a.type === ActionType.DECLARE ) @@ -87,19 +95,26 @@ export function Review() { } return ( - Foo} route={ROUTES.V2.EVENTS.EDIT}> - setAnnotation(values)} - onEdit={handleEdit} - /> - {modal} - + <> + + } + route={ROUTES.V2.EVENTS.EDIT} + > + setAnnotation(values)} + onEdit={handleEdit} + /> + {modal} + + ) } diff --git a/packages/client/src/v2-events/features/events/actions/edit/index.tsx b/packages/client/src/v2-events/features/events/actions/edit/index.tsx index 115cf6e9bfb..64b396170f3 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/index.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/index.tsx @@ -12,7 +12,9 @@ import { withSuspense } from '@client/v2-events/components/withSuspense' import { Pages } from './Pages' import { Review } from './Review' +import { EditPageBanner } from './EditPageBanner' const PagesIndex = withSuspense(Pages) const ReviewIndex = withSuspense(Review) + export { PagesIndex as Pages, ReviewIndex as Review } diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx index 98899123b56..adff377b560 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx @@ -67,18 +67,20 @@ const TableDiv = styled.div` const DEFAULT_HISTORY_RECORD_PAGE_SIZE = 10 +export const roleMessage = { + id: 'event.history.role', + defaultMessage: + '{role, select, LOCAL_REGISTRAR {Local Registrar} HOSPITAL_CLERK {Hospital Clerk} FIELD_AGENT {Field Agent} POLICE_OFFICER {Police Officer} REGISTRATION_AGENT {Registration Agent} HEALTHCARE_WORKER {Healthcare Worker} COMMUNITY_LEADER {Community Leader} LOCAL_SYSTEM_ADMIN {Administrator} NATIONAL_REGISTRAR {Registrar General} PERFORMANCE_MANAGER {Operations Manager} NATIONAL_SYSTEM_ADMIN {National Administrator} HEALTH {Health integration} IMPORT_EXPORT {Import integration} NATIONAL_ID {National ID integration} RECORD_SEARCH {Record search integration} WEBHOOK {Webhook} other {Unknown}}', + description: 'Role of the user in the event history' +} + const messages = defineMessages({ timeFormat: { defaultMessage: 'MMMM dd, yyyy ยท hh.mm a', id: 'configuration.timeFormat', description: 'Time format for timestamps in event history' }, - role: { - id: 'event.history.role', - defaultMessage: - '{role, select, LOCAL_REGISTRAR {Local Registrar} HOSPITAL_CLERK {Hospital Clerk} FIELD_AGENT {Field Agent} POLICE_OFFICER {Police Officer} REGISTRATION_AGENT {Registration Agent} HEALTHCARE_WORKER {Healthcare Worker} COMMUNITY_LEADER {Community Leader} LOCAL_SYSTEM_ADMIN {Administrator} NATIONAL_REGISTRAR {Registrar General} PERFORMANCE_MANAGER {Operations Manager} NATIONAL_SYSTEM_ADMIN {National Administrator} HEALTH {Health integration} IMPORT_EXPORT {Import integration} NATIONAL_ID {National ID integration} RECORD_SEARCH {Record search integration} WEBHOOK {Webhook} other {Unknown}}', - description: 'Role of the user in the event history' - }, + role: roleMessage, system: { id: 'event.history.system', defaultMessage: 'System', diff --git a/packages/client/src/v2-events/routes/config.tsx b/packages/client/src/v2-events/routes/config.tsx index 999720a820b..67ba65c047d 100644 --- a/packages/client/src/v2-events/routes/config.tsx +++ b/packages/client/src/v2-events/routes/config.tsx @@ -19,6 +19,7 @@ import { Debug } from '@client/v2-events/features/debug/debug' import { router as correctionRequestRouter } from '@client/v2-events/features/events/actions/correct/request/router' import { router as correctionReviewRouter } from '@client/v2-events/features/events/actions/correct/review/router' import * as Declare from '@client/v2-events/features/events/actions/declare' +import * as Edit from '@client/v2-events/features/events/actions/edit' import { DeleteEventIndex } from '@client/v2-events/features/events/actions/delete' import * as PrintCertificate from '@client/v2-events/features/events/actions/print-certificate' import { @@ -174,16 +175,15 @@ export const routesConfig = { children: [ { index: true, - // TODO CIHAN: - element: + element: }, { path: ROUTES.V2.EVENTS.EDIT.PAGES.path, - element: + element: }, { path: ROUTES.V2.EVENTS.EDIT.REVIEW.path, - element: + element: } ] }, From 23d27d4d2750dda3a771c19497265c86038b136c Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 11:05:21 +0700 Subject: [PATCH 04/45] progress with action menu --- .../events/actions/edit/EditActionMenu.tsx | 156 ++++++++++++++---- .../events/actions/edit/EditPageBanner.tsx | 2 +- .../features/events/actions/edit/Review.tsx | 2 +- 3 files changed, 127 insertions(+), 33 deletions(-) diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx index c3d3302506d..27e54c18d4f 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx @@ -10,49 +10,143 @@ */ import React from 'react' import { useIntl } from 'react-intl' -import { EventDocument } from '@opencrvs/commons/client' +import { useTypedSearchParams } from 'react-router-typesafe-routes/dom' +import { ActionType, EventDocument } from '@opencrvs/commons/client' import { PrimaryButton } from '@opencrvs/components/lib/buttons' import { DropdownMenu } from '@opencrvs/components/lib/Dropdown' import { CaretDown } from '@opencrvs/components/lib/Icon/all-icons' import { Icon } from '@opencrvs/components' -import { messages } from '@client/i18n/messages/views/action' +import { useEventFormNavigation } from '@client/v2-events/features/events/useEventFormNavigation' +import { messages as actionMessages } from '@client/i18n/messages/views/action' +import { ROUTES } from '@client/v2-events/routes' +import { useModal } from '@client/v2-events/hooks/useModal' +import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' +import { Review } from '@client/v2-events/features/events/components/Review' +import { useUserAllowedActions } from '@client/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations' +import { useEventConfiguration } from '../../useEventConfiguration' -// @TODO CIHAN: register/validate should be unavailable if declaration blocks it -const actions = [ - { - icon: 'PaperPlaneTilt' as const, - label: { - defaultMessage: 'Register with edits', - description: 'Label for "Register with edits" in edit action menu', - id: 'event.edit.registerWithEdits' - }, - onClick: () => console.log('todo') +const messages = { + cancel: { + id: 'actionModal.cancel', + defaultMessage: 'Cancel', + description: 'The label for cancel button of action modal' + }, + confirm: { + id: 'actionModal.confirm', + defaultMessage: 'Confirm', + description: 'The label for confirm button of action modal' + }, + editAndRegisterLabel: { + defaultMessage: 'Register with edits', + description: 'Label for "Register with edits" in edit action menu', + id: 'event.edit.registerWithEdits.label' + }, + editAndDeclareLabel: { + defaultMessage: 'Declare with edits', + description: 'Label for "Declare with edits" in edit action menu', + id: 'event.edit.declareWithEdits.label' }, - { - icon: 'PaperPlaneTilt' as const, - label: { - defaultMessage: 'Declare with edits', - description: 'Label for "Declare with edits" in edit action menu', - id: 'event.edit.declareWithEdits' - }, - onClick: () => console.log('todo') + editAndRegisterDescription: { + id: 'event.edit.registerWithEdits.description', + description: 'Description for "Register with edits" in edit action menu', + defaultMessage: + 'Are you sure you want to register this event with these edits?' }, - { - icon: 'ArchiveBox' as const, - label: { - defaultMessage: 'Cancel edits', - description: 'Label for "Cancel edits" in edit action menu', - id: 'event.edit.cancelEdits' - }, - onClick: () => console.log('todo') + editAndDeclareDescription: { + id: 'event.edit.declareWithEdits.description', + description: 'Description for "Declare with edits" in edit action menu', + defaultMessage: + 'Are you sure you want to edit this declaration? By confirming you are redeclaring this event and overide past changes...' } -] +} + +// @TODO CIHAN: register/validate should be unavailable if declaration blocks it +function useEditActions(event: EventDocument) { + const eventType = event.type + const { eventConfiguration } = useEventConfiguration(eventType) + const { isActionAllowed } = useUserAllowedActions(eventType) + const [{ workqueue: slug }] = useTypedSearchParams( + ROUTES.V2.EVENTS.EDIT.REVIEW + ) + const { closeActionView } = useEventFormNavigation() + const [modal, openModal] = useModal() + const events = useEvents() + + return { + modals: [modal], + actions: [ + { + icon: 'PaperPlaneTilt' as const, + label: messages.editAndRegisterLabel, + onClick: async () => { + const confirm = await openModal((close) => { + return ( + + ) + }) + + if (confirm) { + // TODO CIHAN: call mutate fn + closeActionView(slug) + } + }, + hidden: !isActionAllowed(ActionType.REGISTER) + }, + { + icon: 'PaperPlaneTilt' as const, + label: messages.editAndDeclareLabel, + onClick: async () => { + const confirm = await openModal((close) => { + return ( + + ) + }) + + if (confirm) { + // TODO CIHAN: call mutate fn + closeActionView(slug) + } + } + }, + { + icon: 'ArchiveBox' as const, + label: { + defaultMessage: 'Cancel edits', + description: 'Label for "Cancel edits" in edit action menu', + id: 'event.edit.cancelEdits' + }, + onClick: () => closeActionView(slug) + } + ].filter((a) => !a.hidden) + } +} /** * Menu component available on the edit review page. * */ export function EditActionMenu({ event }: { event: EventDocument }) { const intl = useIntl() + const { actions, modals } = useEditActions(event) return ( <> @@ -63,7 +157,7 @@ export function EditActionMenu({ event }: { event: EventDocument }) { icon={() => } size="medium" > - {intl.formatMessage(messages.action)} + {intl.formatMessage(actionMessages.action)} @@ -75,7 +169,7 @@ export function EditActionMenu({ event }: { event: EventDocument }) { ))} - {/* {modals} */} + {modals} ) } diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx index 16932ae76fa..d87c6ae9547 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx @@ -38,7 +38,7 @@ const StyledText = styled(Text)` const message = defineMessage({ id: 'editPageBanner.message', defaultMessage: - 'You are editing a birth record declared by Jane Smith ({role} at {location})', + 'You are editing a record declared by TODO ({role} at {location})', description: 'The message for the edit page banner' }) diff --git a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx index 0441fbea5da..e87c04a5cc8 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx @@ -98,7 +98,7 @@ export function Review() { <> } + actionComponent={} route={ROUTES.V2.EVENTS.EDIT} > Date: Wed, 10 Dec 2025 11:42:03 +0700 Subject: [PATCH 05/45] progress --- .../events/actions/edit/EditPageBanner.tsx | 5 +- packages/commons/src/events/test.utils.ts | 21 +++ packages/commons/src/events/utils.ts | 15 +- .../event.actions.edit.test.ts.snap | 5 + ...vent.actions.printCertificate.test.ts.snap | 2 +- .../__snapshots__/event.delete.test.ts.snap | 2 +- .../router/event/event.actions.edit.test.ts | 169 ++++++++++++++++++ packages/events/src/tests/utils.ts | 1 + 8 files changed, 209 insertions(+), 11 deletions(-) create mode 100644 packages/events/src/router/event/__snapshots__/event.actions.edit.test.ts.snap diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx index d87c6ae9547..93987b5f052 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx @@ -67,12 +67,9 @@ export function EditPageBanner() { ? locations.get(createdAtLocation) : undefined - // TODO CIHAN: get user name - console.log('foo') - console.log(user) - const role = intl.formatMessage(roleMessage, { role: user?.role }) + // @TODO CIHAN: get user name return ( diff --git a/packages/commons/src/events/test.utils.ts b/packages/commons/src/events/test.utils.ts index e92213cfcb8..92d79360313 100644 --- a/packages/commons/src/events/test.utils.ts +++ b/packages/commons/src/events/test.utils.ts @@ -27,6 +27,7 @@ import { ArchiveActionInput, AssignActionInput, DeclareActionInput, + EditActionInput, MarkAsDuplicateActionInput, MarkNotDuplicateActionInput, NotifyActionInput, @@ -487,6 +488,26 @@ export function eventPayloadGenerator( keepAssignment: input.keepAssignment } }, + edit: ( + eventId: string, + input: Partial< + Pick< + EditActionInput, + 'transactionId' | 'declaration' | 'annotation' | 'keepAssignment' + > + > = {} + ) => ({ + type: ActionType.EDIT, + transactionId: input.transactionId ?? getUUID(), + declaration: + input.declaration ?? + generateActionDeclarationInput(configuration, ActionType.EDIT, rng), + annotation: + input.annotation ?? + generateActionAnnotationInput(configuration, ActionType.EDIT, rng), + eventId, + ...input + }), validate: ( eventId: string, input: Partial< diff --git a/packages/commons/src/events/utils.ts b/packages/commons/src/events/utils.ts index d6aff90e21f..d306eef0eea 100644 --- a/packages/commons/src/events/utils.ts +++ b/packages/commons/src/events/utils.ts @@ -96,8 +96,8 @@ export function getActionConfig({ return a.customActionType === customActionType } - // Notify uses the declare action config - if (actionType === ActionType.NOTIFY) { + // Notify and edit use the declare action config + if (actionType === ActionType.NOTIFY || actionType === ActionType.EDIT) { return a.type === ActionType.DECLARE } @@ -198,9 +198,14 @@ export function getActionReview( configuration: EventConfig, actionType: ActionType ) { - const [actionConfig] = configuration.actions.filter( - (a): a is DeclarationActionConfig => a.type === actionType - ) + const actionConfig = getActionConfig({ + eventConfiguration: configuration, + actionType + }) + + if (!actionConfig) { + throw 'Tried to get action review for an action that is not a declaration action' + } if ('review' in actionConfig) { return actionConfig.review diff --git a/packages/events/src/router/event/__snapshots__/event.actions.edit.test.ts.snap b/packages/events/src/router/event/__snapshots__/event.actions.edit.test.ts.snap new file mode 100644 index 00000000000..77f95e32bd3 --- /dev/null +++ b/packages/events/src/router/event/__snapshots__/event.actions.edit.test.ts.snap @@ -0,0 +1,5 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Validation error message contains all the offending fields 1`] = `[TRPCError: [{"message":"Invalid date field","id":"applicant.dob","value":"02-02"},{"message":"Please enter a valid date","id":"applicant.dob","value":"02-02"}]]`; + +exports[`when mandatory field is invalid, conditional hidden fields are still skipped 1`] = `[TRPCError: [{"message":"Invalid date field","id":"applicant.dob","value":"02-1-2024"},{"message":"Please enter a valid date","id":"applicant.dob","value":"02-1-2024"}]]`; diff --git a/packages/events/src/router/event/__snapshots__/event.actions.printCertificate.test.ts.snap b/packages/events/src/router/event/__snapshots__/event.actions.printCertificate.test.ts.snap index ba9edbe1705..53a4326326f 100644 --- a/packages/events/src/router/event/__snapshots__/event.actions.printCertificate.test.ts.snap +++ b/packages/events/src/router/event/__snapshots__/event.actions.printCertificate.test.ts.snap @@ -2,7 +2,7 @@ exports[`Has validation errors when required VERIFICATION page fields are missing 1`] = `[TRPCError: [{"message":"Verification page result is required","id":"collector.identity.verify"}]]`; -exports[`PRINT_CERTIFICATE action can not be performed on a declared, non-registered event 1`] = `[TRPCError: Action 'PRINT_CERTIFICATE' cannot be performed on an event in 'DECLARED' state with [] flags. Available actions: READ, VALIDATE, REGISTER, ARCHIVE, REJECT, CUSTOM.]`; +exports[`PRINT_CERTIFICATE action can not be performed on a declared, non-registered event 1`] = `[TRPCError: Action 'PRINT_CERTIFICATE' cannot be performed on an event in 'DECLARED' state with [] flags. Available actions: READ, VALIDATE, REGISTER, ARCHIVE, REJECT, CUSTOM, EDIT.]`; exports[`PRINT_CERTIFICATE is not allowed if the event is waiting for correction 1`] = `[TRPCError: Action 'PRINT_CERTIFICATE' cannot be performed on an event in 'REGISTERED' state with [pending-certification, correction-requested, validated] flags. Available actions: READ, APPROVE_CORRECTION, REJECT_CORRECTION, CUSTOM, REVIEW_CORRECTION_REQUEST.]`; diff --git a/packages/events/src/router/event/__snapshots__/event.delete.test.ts.snap b/packages/events/src/router/event/__snapshots__/event.delete.test.ts.snap index fc4f65a4639..2cea6db62bf 100644 --- a/packages/events/src/router/event/__snapshots__/event.delete.test.ts.snap +++ b/packages/events/src/router/event/__snapshots__/event.delete.test.ts.snap @@ -1,5 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`declared event can not be deleted 1`] = `[TRPCError: Action 'DELETE' cannot be performed on an event in 'DECLARED' state with [] flags. Available actions: READ, VALIDATE, REGISTER, ARCHIVE, REJECT, CUSTOM.]`; +exports[`declared event can not be deleted 1`] = `[TRPCError: Action 'DELETE' cannot be performed on an event in 'DECLARED' state with [] flags. Available actions: READ, VALIDATE, REGISTER, ARCHIVE, REJECT, CUSTOM, EDIT.]`; exports[`should return 404 if event does not exist 1`] = `[TRPCError: Event not found with ID: 00000000-0000-0000-0000-000000000000]`; diff --git a/packages/events/src/router/event/event.actions.edit.test.ts b/packages/events/src/router/event/event.actions.edit.test.ts index e69de29bb2d..dffb212d5f8 100644 --- a/packages/events/src/router/event/event.actions.edit.test.ts +++ b/packages/events/src/router/event/event.actions.edit.test.ts @@ -0,0 +1,169 @@ +/* + * 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 { TRPCError } from '@trpc/server' +import { + ActionStatus, + ActionType, + AddressType, + getUUID, + ActionUpdate +} from '@opencrvs/commons' + +import { createTestClient, setupTestCase } from '@events/tests/utils' + +test('prevents forbidden access if missing required scope', async () => { + const { user, generator } = await setupTestCase() + const client = createTestClient(user, []) + + await expect( + client.event.actions.edit.request( + generator.event.actions.edit('registered-event-test-id-12345') + ) + ).rejects.toMatchObject(new TRPCError({ code: 'FORBIDDEN' })) +}) + +test('allows access if required scope is present', async () => { + const { user, generator } = await setupTestCase() + const client = createTestClient(user) + + await expect( + client.event.actions.edit.request( + generator.event.actions.edit('registered-event-test-id-12345') + ) + ).rejects.not.toMatchObject(new TRPCError({ code: 'FORBIDDEN' })) +}) + +test('Validation error message contains all the offending fields', async () => { + const { user, generator } = await setupTestCase() + const client = createTestClient(user) + + const event = await client.event.create(generator.event.create()) + + const createAction = event.actions.filter( + (action) => action.type === ActionType.CREATE + ) + + const assignmentInput = generator.event.actions.assign(event.id, { + assignedTo: createAction[0].createdBy + }) + + await client.event.actions.assignment.assign(assignmentInput) + + await client.event.actions.declare.request( + generator.event.actions.declare(event.id) + ) + + /** Partial payload is accepted, so it should not complain about fields already send during declaration. */ + const data = generator.event.actions.edit(event.id, { + declaration: { + 'applicant.dob': '02-02', + 'applicant.dobUnknown': false, + 'recommender.none': true + } + }) + + await client.event.actions.assignment.assign({ + ...assignmentInput, + transactionId: getUUID() + }) + + await expect(client.event.actions.edit.request(data)).rejects.matchSnapshot() +}) + +test('when mandatory field is invalid, conditional hidden fields are still skipped', async () => { + const { user, generator } = await setupTestCase() + const client = createTestClient(user) + + const event = await client.event.create(generator.event.create()) + await client.event.actions.declare.request( + generator.event.actions.declare(event.id) + ) + + const createAction = event.actions.filter( + (action) => action.type === ActionType.CREATE + ) + + const assignmentInput = generator.event.actions.assign(event.id, { + assignedTo: createAction[0].createdBy + }) + + await client.event.actions.assignment.assign(assignmentInput) + + const data = generator.event.actions.edit(event.id, { + declaration: { + 'applicant.dob': '02-1-2024', + 'applicant.dobUnknown': false, + 'applicant.name': { + firstname: 'John', + surname: 'Doe' + }, + 'recommender.none': true + } + }) + + await expect(client.event.actions.edit.request(data)).rejects.matchSnapshot() +}) + +test('Skips required field validation when they are conditionally hidden', async () => { + const { user, generator } = await setupTestCase() + const client = createTestClient(user) + + const event = await client.event.create(generator.event.create()) + + await client.event.actions.declare.request( + generator.event.actions.declare(event.id) + ) + + const createAction = event.actions.filter( + (action) => action.type === ActionType.CREATE + ) + + const assignmentInput = generator.event.actions.assign(event.id, { + assignedTo: createAction[0].createdBy + }) + + await client.event.actions.assignment.assign(assignmentInput) + + const form = { + 'applicant.dob': '2024-02-01', + 'applicant.dobUnknown': false, + 'applicant.name': { + firstname: 'John', + surname: 'Doe' + }, + 'recommender.none': true, + 'applicant.address': { + country: 'FAR', + addressType: AddressType.DOMESTIC, + streetLevelDetails: { + state: 'State', + district2: 'District2' + } + } + } satisfies ActionUpdate + + const declaration = generator.event.actions.edit(event.id, { + declaration: form + }) + + const response = await client.event.actions.edit.request(declaration) + + const savedAction = response.actions.find( + (action) => + action.type === ActionType.EDIT && action.status === ActionStatus.Accepted + ) + + expect(savedAction).toMatchObject({ + status: ActionStatus.Accepted, + declaration: {} + }) +}) diff --git a/packages/events/src/tests/utils.ts b/packages/events/src/tests/utils.ts index fb02aff70f4..e654b18779d 100644 --- a/packages/events/src/tests/utils.ts +++ b/packages/events/src/tests/utils.ts @@ -99,6 +99,7 @@ export const TEST_USER_DEFAULT_SCOPES = [ 'record.read[event=birth|death|tennis-club-membership|child-onboarding]', 'record.notify[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declare[event=birth|death|tennis-club-membership|child-onboarding]', + 'record.declared.edit[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declared.validate[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declared.reject[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declared.archive[event=birth|death|tennis-club-membership|child-onboarding]', From 83810e3a9bacec6f2706719c23403b501b42686b Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 11:50:15 +0700 Subject: [PATCH 06/45] remove unused import --- packages/commons/src/events/utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/commons/src/events/utils.ts b/packages/commons/src/events/utils.ts index d306eef0eea..37741c4b293 100644 --- a/packages/commons/src/events/utils.ts +++ b/packages/commons/src/events/utils.ts @@ -46,8 +46,7 @@ import { getUUID, UUID } from '../uuid' import { ActionConfig, actionConfigTypes, - ActionConfigTypes, - DeclarationActionConfig + ActionConfigTypes } from './ActionConfig' import { FormConfig } from './FormConfig' import { getOrThrow } from '../utils' From 42fbcced7cffecd377bebdb184595db7aaffb1e1 Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 11:55:05 +0700 Subject: [PATCH 07/45] fix lint issue --- packages/commons/src/events/state/index.ts | 2 +- packages/commons/src/events/test.utils.ts | 33 ++++++++-------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/packages/commons/src/events/state/index.ts b/packages/commons/src/events/state/index.ts index 23a96b15763..35351484a02 100644 --- a/packages/commons/src/events/state/index.ts +++ b/packages/commons/src/events/state/index.ts @@ -65,8 +65,8 @@ export function getStatusFromActions(actions: Array) { case ActionType.MARK_AS_DUPLICATE: case ActionType.REJECT_CORRECTION: case ActionType.READ: - case ActionType.CUSTOM: case ActionType.VALIDATE: + case ActionType.EDIT: default: return status } diff --git a/packages/commons/src/events/test.utils.ts b/packages/commons/src/events/test.utils.ts index 92d79360313..dca6febf0b4 100644 --- a/packages/commons/src/events/test.utils.ts +++ b/packages/commons/src/events/test.utils.ts @@ -829,22 +829,25 @@ export function generateActionDocument({ } satisfies ActionBase switch (action) { + case ActionType.READ: + case ActionType.MARK_AS_NOT_DUPLICATE: + case ActionType.DECLARE: + case ActionType.UNASSIGN: + case ActionType.CREATE: + case ActionType.NOTIFY: + case ActionType.VALIDATE: + case ActionType.REGISTER: + case ActionType.REQUEST_CORRECTION: + case ActionType.EDIT: + return { ...actionBase, type: action } case ActionType.CUSTOM: return { ...actionBase, type: action, customActionType: 'CUSTOM_ACTION_TYPE' } - case ActionType.READ: - return { ...actionBase, type: action } - case ActionType.MARK_AS_NOT_DUPLICATE: - return { ...actionBase, type: action } case ActionType.MARK_AS_DUPLICATE: return { ...actionBase, type: action, content: undefined } - case ActionType.DECLARE: - return { ...actionBase, type: action } - case ActionType.UNASSIGN: - return { ...actionBase, type: action } case ActionType.ASSIGN: { const assignActionDefaults = defaults as | Partial> @@ -855,16 +858,11 @@ export function generateActionDocument({ type: action } } - case ActionType.VALIDATE: - return { ...actionBase, type: action } case ActionType.ARCHIVE: return { ...actionBase, type: action, content: { reason: 'Archive' } } case ActionType.REJECT: return { ...actionBase, type: action, content: { reason: 'Reject' } } - case ActionType.CREATE: - return { ...actionBase, type: action } - case ActionType.NOTIFY: - return { ...actionBase, type: action } + case ActionType.PRINT_CERTIFICATE: { const printActionDefaults = defaults as | Partial @@ -875,8 +873,6 @@ export function generateActionDocument({ content: printActionDefaults?.content } } - case ActionType.REQUEST_CORRECTION: - return { ...actionBase, type: action } case ActionType.APPROVE_CORRECTION: return { ...actionBase, requestId: getUUID(), type: action } case ActionType.REJECT_CORRECTION: @@ -886,11 +882,6 @@ export function generateActionDocument({ type: action, content: { reason: 'Correction rejection' } } - case ActionType.REGISTER: - return { - ...actionBase, - type: action - } case ActionType.DUPLICATE_DETECTED: { const duplicateActionDefaults = defaults as | Partial From 34be82f447ca9c613ec462fb68edf7abc99f5931 Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 11:59:22 +0700 Subject: [PATCH 08/45] fix test --- .../src/events/state/__snapshots__/availableActions.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/commons/src/events/state/__snapshots__/availableActions.test.ts.snap b/packages/commons/src/events/state/__snapshots__/availableActions.test.ts.snap index 69d81b2f460..957e44610c2 100644 --- a/packages/commons/src/events/state/__snapshots__/availableActions.test.ts.snap +++ b/packages/commons/src/events/state/__snapshots__/availableActions.test.ts.snap @@ -41,6 +41,7 @@ exports[`getAvailableActions should return the correct actions for "DECLARED" st "ARCHIVE", "REJECT", "CUSTOM", + "EDIT", ] `; From bd3ed9bea4ff03a8b73995dd369a7b2576dd4aec Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 12:05:42 +0700 Subject: [PATCH 09/45] fix lint issue --- packages/events/src/tests/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/events/src/tests/utils.ts b/packages/events/src/tests/utils.ts index e654b18779d..09c7709ca34 100644 --- a/packages/events/src/tests/utils.ts +++ b/packages/events/src/tests/utils.ts @@ -353,6 +353,7 @@ function actionToClientAction( case ActionType.DELETE: case ActionType.CUSTOM: case ActionType.READ: + case ActionType.EDIT: default: throw new Error( `Unsupported action type: ${action}. Create a case for it if you need it.` From 470fe7d7befdb1977079c033e38d4dda51baa49f Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 12:06:26 +0700 Subject: [PATCH 10/45] remove unused export --- .../src/v2-events/features/events/actions/edit/Review.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx index e87c04a5cc8..e412baaaa35 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx @@ -117,5 +117,3 @@ export function Review() { ) } - -export const ReviewIndex = withSuspense(Review) From f164ff4ec148ffc97f7938e2dd472b5c7d25c26c Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 12:39:23 +0700 Subject: [PATCH 11/45] refactor --- .../actions/correct/useActionForHistory.ts | 2 +- .../events/actions/edit/EditPageBanner.tsx | 21 ++-- .../components/EventHistory/EventHistory.tsx | 104 +++++------------ .../v2-events/hooks/useCurrentUserDetails.ts | 65 +++++++++++ .../src/v2-events/hooks/useSystemVariables.ts | 4 +- .../src/v2-events/hooks/useUserDetails.ts | 107 +++++++++++------- 6 files changed, 174 insertions(+), 129 deletions(-) create mode 100644 packages/client/src/v2-events/hooks/useCurrentUserDetails.ts diff --git a/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts b/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts index fcfa447b02d..6628a65ce4a 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts +++ b/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts @@ -30,7 +30,7 @@ import { hasAnnotationChanged, getDeclarationComparison } from './utils' * Indicates that declaration action changed declaration content. Satisfies V1 spec. */ export const DECLARATION_ACTION_UPDATE = 'UPDATE' as const -type DECLARATION_ACTION_UPDATE = typeof DECLARATION_ACTION_UPDATE +export type DECLARATION_ACTION_UPDATE = typeof DECLARATION_ACTION_UPDATE type UpdateActionDocument = Omit & { type: DECLARATION_ACTION_UPDATE diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx index 93987b5f052..5aaf5954ac1 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditPageBanner.tsx @@ -20,7 +20,7 @@ import { ROUTES } from '@client/v2-events/routes' import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' import { useUsers } from '@client/v2-events/hooks/useUsers' import { useLocations } from '@client/v2-events/hooks/useLocations' -import { roleMessage } from '@client/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory' +import { useUserDetails } from '@client/v2-events/hooks/useUserDetails' const Wrapper = styled.div` display: flex; @@ -38,7 +38,7 @@ const StyledText = styled(Text)` const message = defineMessage({ id: 'editPageBanner.message', defaultMessage: - 'You are editing a record declared by TODO ({role} at {location})', + 'You are editing a record declared by {name} ({role} at {location})', description: 'The message for the edit page banner' }) @@ -48,8 +48,6 @@ export function EditPageBanner() { const events = useEvents() const event = events.getEvent.getFromCache(eventId) const { getUser } = useUsers() - const users = getUser.getAllCached() - const { getLocations } = useLocations() const locations = getLocations.useSuspenseQuery() @@ -58,23 +56,26 @@ export function EditPageBanner() { ) // Fetch createdAtLocation and createdBy from latest declaration action - const { createdAtLocation, createdBy } = + const { createdAtLocation, createdBy, createdByRole, createdByUserType } = declarationActions[declarationActions.length - 1] - const user = users.find((u) => u.id === createdBy) + const { getUserDetails } = useUserDetails() + const { role, name } = getUserDetails({ + createdByUserType, + createdBy, + type: ActionType.DECLARE, + createdByRole + }) const location = createdAtLocation ? locations.get(createdAtLocation) : undefined - const role = intl.formatMessage(roleMessage, { role: user?.role }) - - // @TODO CIHAN: get user name return ( - {intl.formatMessage(message, { location: location?.name, role })} + {intl.formatMessage(message, { location: location?.name, role, name })} ) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx index adff377b560..9d3dad26ee8 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx @@ -13,7 +13,6 @@ import format from 'date-fns/format' import styled from 'styled-components' import { defineMessages, useIntl } from 'react-intl' import { useNavigate } from 'react-router-dom' -import { useSelector } from 'react-redux' import { useTypedParams } from 'react-router-typesafe-routes/dom' import { Link, Pagination } from '@opencrvs/components' import { ColumnContentAlignment } from '@opencrvs/components/lib/common-types' @@ -31,8 +30,6 @@ import { useModal } from '@client/v2-events/hooks/useModal' import * as routes from '@client/navigation/routes' import { formatUrl } from '@client/navigation' import { useEventOverviewContext } from '@client/v2-events/features/workqueues/EventOverview/EventOverviewContext' -import { getUsersFullName } from '@client/v2-events/utils' -import { getOfflineData } from '@client/offline/selectors' import { serializeSearchParams } from '@client/v2-events/features/events/Search/utils' import { expandWithClientSpecificActions, @@ -44,6 +41,7 @@ import { import { usePermissions } from '@client/hooks/useAuthorization' import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration' +import { useUserDetails } from '@client/v2-events/hooks/useUserDetails' import { useEventOverviewInfo } from '../useEventOverviewInfo' import { UserAvatar } from './UserAvatar' import { EventHistoryDialog } from './EventHistoryDialog/EventHistoryDialog' @@ -67,30 +65,17 @@ const TableDiv = styled.div` const DEFAULT_HISTORY_RECORD_PAGE_SIZE = 10 -export const roleMessage = { - id: 'event.history.role', - defaultMessage: - '{role, select, LOCAL_REGISTRAR {Local Registrar} HOSPITAL_CLERK {Hospital Clerk} FIELD_AGENT {Field Agent} POLICE_OFFICER {Police Officer} REGISTRATION_AGENT {Registration Agent} HEALTHCARE_WORKER {Healthcare Worker} COMMUNITY_LEADER {Community Leader} LOCAL_SYSTEM_ADMIN {Administrator} NATIONAL_REGISTRAR {Registrar General} PERFORMANCE_MANAGER {Operations Manager} NATIONAL_SYSTEM_ADMIN {National Administrator} HEALTH {Health integration} IMPORT_EXPORT {Import integration} NATIONAL_ID {National ID integration} RECORD_SEARCH {Record search integration} WEBHOOK {Webhook} other {Unknown}}', - description: 'Role of the user in the event history' -} - const messages = defineMessages({ timeFormat: { defaultMessage: 'MMMM dd, yyyy ยท hh.mm a', id: 'configuration.timeFormat', description: 'Time format for timestamps in event history' }, - role: roleMessage, system: { id: 'event.history.system', defaultMessage: 'System', description: 'Name for system initiated actions in the event history' }, - systemDefaultName: { - id: 'event.history.systemDefaultName', - defaultMessage: 'System integration', - description: 'Fallback for system integration name in the event history' - }, action: { defaultMessage: 'Action', description: 'Action Label', @@ -141,52 +126,20 @@ const SystemName = styled.div` } ` -interface ActionCreator { - type: 'user' | 'system' | 'integration' - name: string -} - -function useActionCreator() { - const intl = useIntl() - const { findUser } = useEventOverviewContext() - const { systems } = useSelector(getOfflineData) - - const getActionCreator = ( - action: EventHistoryActionDocument - ): ActionCreator => { - if (action.createdByUserType === 'system') { - const system = systems.find((s) => s._id === action.createdBy) - return { - type: 'integration', - name: system?.name ?? intl.formatMessage(messages.systemDefaultName) - } as const - } - if (action.type === ActionType.DUPLICATE_DETECTED) { - return { - type: 'system', - name: intl.formatMessage(messages.system) - } as const - } - const user = findUser(action.createdBy) - return { - type: 'user', - // @todo: - name: user ? getUsersFullName(user.name, intl.locale) : 'Missing user' - } as const - } - return { getActionCreator } -} - function User({ action }: { action: EventHistoryActionDocument }) { const intl = useIntl() const { findUser } = useEventOverviewContext() const navigate = useNavigate() const user = findUser(action.createdBy) const { canReadUser } = usePermissions() + const { getUserDetails } = useUserDetails() - const { getActionCreator } = useActionCreator() - - const { type, name } = getActionCreator(action) + const { type, name } = getUserDetails({ + createdByUserType: action.createdByUserType, + createdBy: action.createdBy, + type: action.type, + createdByRole: action.createdByRole + }) if (type !== 'user') { throw new Error('Expected action creator to be a user') @@ -220,9 +173,13 @@ function User({ action }: { action: EventHistoryActionDocument }) { } function Integration({ action }: { action: EventHistoryActionDocument }) { - const { getActionCreator } = useActionCreator() + const { getUserDetails } = useUserDetails() - const { type, name } = getActionCreator(action) + const { type, name } = getUserDetails({ + createdByUserType: action.createdByUserType, + createdBy: action.createdBy, + type: action.type + }) if (type !== 'integration') { throw new Error('Expected action creator to be an integration') @@ -256,24 +213,11 @@ function ActionCreator({ action }: { action: EventHistoryActionDocument }) { return } -function ActionRole({ action }: { action: EventHistoryActionDocument }) { - const intl = useIntl() - const role = action.createdByRole - const { getActionCreator } = useActionCreator() - const { type } = getActionCreator(action) - - if (type === 'system') { - return null - } - - return <>{intl.formatMessage(messages.role, { role })} -} - function ActionLocation({ action }: { action: EventHistoryActionDocument }) { const { findUser, getLocation } = useEventOverviewContext() const { canAccessOffice } = usePermissions() const navigate = useNavigate() - const { getActionCreator } = useActionCreator() + const { getUserDetails } = useUserDetails() const user = findUser(action.createdBy) const locationName = action.createdAtLocation @@ -286,7 +230,12 @@ function ActionLocation({ action }: { action: EventHistoryActionDocument }) { id: user.primaryOfficeId }) - const { type } = getActionCreator(action) + const { type } = getUserDetails({ + createdByUserType: action.createdByUserType, + createdBy: action.createdBy, + type: action.type, + createdByRole: action.createdByRole + }) if (type === 'system') { return null @@ -340,7 +289,7 @@ function EventHistory({ fullEvent }: { fullEvent: EventDocument }) { const intl = useIntl() const [modal, openModal] = useModal() const { getActionTypeForHistory } = useActionForHistory() - const { getActionCreator } = useActionCreator() + const { getUserDetails } = useUserDetails() const history = extractHistoryActions(fullEvent) @@ -410,7 +359,12 @@ function EventHistory({ fullEvent }: { fullEvent: EventDocument }) { currentPageNumber * DEFAULT_HISTORY_RECORD_PAGE_SIZE ) .map((action) => { - const { name: actionCreatorName } = getActionCreator(action) + const { name: actionCreatorName, role } = getUserDetails({ + createdByUserType: action.createdByUserType, + createdBy: action.createdBy, + type: action.type, + createdByRole: action.createdByRole + }) // Only configurable action types should call getActionConfig let actionConfig @@ -446,7 +400,7 @@ function EventHistory({ fullEvent }: { fullEvent: EventDocument }) { intl.formatMessage(messages.timeFormat) ), user: , - role: , + role, location: } }) diff --git a/packages/client/src/v2-events/hooks/useCurrentUserDetails.ts b/packages/client/src/v2-events/hooks/useCurrentUserDetails.ts new file mode 100644 index 00000000000..0a7144f658b --- /dev/null +++ b/packages/client/src/v2-events/hooks/useCurrentUserDetails.ts @@ -0,0 +1,65 @@ +/* + * 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 { useSelector } from 'react-redux' +import { LocationType } from '@opencrvs/commons/client' +import { getUserDetails } from '@client/profile/profileSelectors' +import { getOfflineData } from '@client/offline/selectors' +import { getAdminLevelHierarchy, getUsersFullName } from '../utils' +import { useLocations } from './useLocations' +import { useUsers } from './useUsers' + +export function useCurrentUserDetails() { + const { config } = useSelector(getOfflineData) + const appConfigAdminLevels = config.ADMIN_STRUCTURE + + const loggedInUser = useSelector(getUserDetails) + const { getUser } = useUsers() + const [user] = getUser.useSuspenseQuery(loggedInUser?.id ?? '') + + const { getLocations } = useLocations() + const locations = getLocations.useSuspenseQuery() + + const name = getUsersFullName(user.name, 'en') + + if (user.primaryOfficeId) { + const primaryOfficeLocation = locations.get(user.primaryOfficeId) + + const adminStructureLocations = new Map( + [...locations].filter( + ([, location]) => + location.locationType === LocationType.enum.ADMIN_STRUCTURE + ) + ) + + const adminLevelIds = appConfigAdminLevels.map((level) => level.id) + + const adminLevels = getAdminLevelHierarchy( + primaryOfficeLocation?.parentId ?? undefined, + adminStructureLocations, + adminLevelIds + ) + return { + name, + role: user.role, + district: '', + province: '', + ...adminLevels + } + } + + return { + name, + role: user.role, + district: '', + province: '' + } +} diff --git a/packages/client/src/v2-events/hooks/useSystemVariables.ts b/packages/client/src/v2-events/hooks/useSystemVariables.ts index dabf06d539e..8249f76384b 100644 --- a/packages/client/src/v2-events/hooks/useSystemVariables.ts +++ b/packages/client/src/v2-events/hooks/useSystemVariables.ts @@ -10,13 +10,13 @@ */ import { SystemVariables } from '@opencrvs/commons/client' -import { useUserDetails } from './useUserDetails' +import { useCurrentUserDetails } from './useCurrentUserDetails' /** * Exposes template variables such as `$user` for components to replace field values or other templates */ export function useSystemVariables() { - const user = useUserDetails() + const user = useCurrentUserDetails() const variables = { $user: user diff --git a/packages/client/src/v2-events/hooks/useUserDetails.ts b/packages/client/src/v2-events/hooks/useUserDetails.ts index 67f7ec1e550..222fb970925 100644 --- a/packages/client/src/v2-events/hooks/useUserDetails.ts +++ b/packages/client/src/v2-events/hooks/useUserDetails.ts @@ -9,57 +9,82 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ +import { useIntl, defineMessages } from 'react-intl' import { useSelector } from 'react-redux' -import { LocationType } from '@opencrvs/commons/client' -import { getUserDetails } from '@client/profile/profileSelectors' +import { ActionType, TokenUserType } from '@opencrvs/commons/client' import { getOfflineData } from '@client/offline/selectors' -import { getAdminLevelHierarchy, getUsersFullName } from '../utils' -import { useLocations } from './useLocations' -import { useUsers } from './useUsers' +import { getUsersFullName } from '@client/v2-events/utils' +import { useUsers } from '@client/v2-events/hooks/useUsers' +import { useEventOverviewContext } from '../features/workqueues/EventOverview/EventOverviewContext' +import { DECLARATION_ACTION_UPDATE } from '../features/events/actions/correct/useActionForHistory' -export function useUserDetails() { - const { config } = useSelector(getOfflineData) - const appConfigAdminLevels = config.ADMIN_STRUCTURE +const messages = defineMessages({ + systemDefaultName: { + id: 'event.history.systemDefaultName', + defaultMessage: 'System integration', + description: 'Fallback for system integration name in the event history' + }, + system: { + id: 'event.history.system', + defaultMessage: 'System', + description: 'Name for system initiated actions in the event history' + }, + role: { + id: 'event.history.role', + defaultMessage: + '{role, select, LOCAL_REGISTRAR {Local Registrar} HOSPITAL_CLERK {Hospital Clerk} FIELD_AGENT {Field Agent} POLICE_OFFICER {Police Officer} REGISTRATION_AGENT {Registration Agent} HEALTHCARE_WORKER {Healthcare Worker} COMMUNITY_LEADER {Community Leader} LOCAL_SYSTEM_ADMIN {Administrator} NATIONAL_REGISTRAR {Registrar General} PERFORMANCE_MANAGER {Operations Manager} NATIONAL_SYSTEM_ADMIN {National Administrator} HEALTH {Health integration} IMPORT_EXPORT {Import integration} NATIONAL_ID {National ID integration} RECORD_SEARCH {Record search integration} WEBHOOK {Webhook} other {Unknown}}', + description: 'Role of the user in the event history' + } +}) - const loggedInUser = useSelector(getUserDetails) +export function useUserDetails() { + const intl = useIntl() const { getUser } = useUsers() - const [user] = getUser.useSuspenseQuery(loggedInUser?.id ?? '') - - const { getLocations } = useLocations() - const locations = getLocations.useSuspenseQuery() - - const name = getUsersFullName(user.name, 'en') + const users = getUser.getAllCached() + const { systems } = useSelector(getOfflineData) - if (user.primaryOfficeId) { - const primaryOfficeLocation = locations.get(user.primaryOfficeId) + const getUserDetails = ({ + createdByUserType, + createdBy, + type, + createdByRole + }: { + createdByUserType: TokenUserType + createdBy: string + type: ActionType | DECLARATION_ACTION_UPDATE + createdByRole?: string + }): { + type: 'user' | 'system' | 'integration' + name: string + role: string | undefined + } => { + if (createdByUserType === 'system') { + const system = systems.find((s) => s._id === createdBy) + return { + type: 'integration', + name: system?.name ?? intl.formatMessage(messages.systemDefaultName), + role: undefined + } as const + } - const adminStructureLocations = new Map( - [...locations].filter( - ([, location]) => - location.locationType === LocationType.enum.ADMIN_STRUCTURE - ) - ) + if (type === ActionType.DUPLICATE_DETECTED) { + return { + type: 'system', + name: intl.formatMessage(messages.system), + role: undefined + } as const + } - const adminLevelIds = appConfigAdminLevels.map((level) => level.id) + const user = users.find((u) => u.id === createdBy) - const adminLevels = getAdminLevelHierarchy( - primaryOfficeLocation?.parentId ?? undefined, - adminStructureLocations, - adminLevelIds - ) return { - name, - role: user.role, - district: '', - province: '', - ...adminLevels - } + type: 'user', + name: user ? getUsersFullName(user.name, intl.locale) : 'Missing user', + role: createdByRole + ? intl.formatMessage(messages.role, { role: createdByRole }) + : undefined + } as const } - return { - name, - role: user.role, - district: '', - province: '' - } + return { getUserDetails } } From 97b77c52a122d28da65eb4dea2a473a58da7d97f Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 13:09:23 +0700 Subject: [PATCH 12/45] remove console log --- .../components/useAllowedActionConfigurations.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx index c678324331f..5a4a774a095 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations.tsx @@ -76,9 +76,6 @@ function getAvailableAssignmentActions( const assignmentStatus = getAssignmentStatus(event, authentication.sub) const eventStatus = event.status - console.log('scopes') - console.log(authentication.scope) - const mayUnassignOthers = configurableEventScopeAllowed( authentication.scope, ['record.unassign-others'], From 2cc3694f3e62034277a790f0b1f21b5d3b2e12e9 Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 13:32:25 +0700 Subject: [PATCH 13/45] fix role --- .../client/src/v2-events/hooks/useUserDetails.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/client/src/v2-events/hooks/useUserDetails.ts b/packages/client/src/v2-events/hooks/useUserDetails.ts index b4361424a7d..7f5ddcebaa1 100644 --- a/packages/client/src/v2-events/hooks/useUserDetails.ts +++ b/packages/client/src/v2-events/hooks/useUserDetails.ts @@ -57,12 +57,16 @@ export function useUserDetails() { name: string role: string | undefined } => { + const role = createdByRole + ? intl.formatMessage(messages.role, { role: createdByRole }) + : undefined + if (createdByUserType === 'system') { const system = systems.find((s) => s._id === createdBy) return { type: 'integration', name: system?.name ?? intl.formatMessage(messages.systemDefaultName), - role: undefined + role } as const } @@ -70,7 +74,7 @@ export function useUserDetails() { return { type: 'system', name: intl.formatMessage(messages.system), - role: undefined + role } as const } @@ -79,9 +83,7 @@ export function useUserDetails() { return { type: 'user', name: user ? getUsersFullName(user.name, intl.locale) : 'Missing user', - role: createdByRole - ? intl.formatMessage(messages.role, { role: createdByRole }) - : undefined + role } as const } From 5a361671b6a78bb5a0a56515f402dc3cc42b6f46 Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 14:51:57 +0700 Subject: [PATCH 14/45] allow edit only if form has changes and no errors --- .../client/src/v2-events/custom-api/index.ts | 53 +++++++++++++ .../actions/declare/DeclareActionMenu.tsx | 13 ++-- .../events/actions/edit/EditActionMenu.tsx | 75 +++++++++++++++++-- .../useEvents/procedures/actions/action.ts | 32 ++++++++ .../features/events/useEvents/useEvents.ts | 4 +- 5 files changed, 162 insertions(+), 15 deletions(-) diff --git a/packages/client/src/v2-events/custom-api/index.ts b/packages/client/src/v2-events/custom-api/index.ts index cd42246c9b8..2b830cfc763 100644 --- a/packages/client/src/v2-events/custom-api/index.ts +++ b/packages/client/src/v2-events/custom-api/index.ts @@ -104,6 +104,59 @@ export async function registerOnDeclare({ return latestResponse } +export async function editAndRegister({ + eventId, + eventConfiguration, + declaration, + transactionId, + annotation +}: CustomMutationParams) { + const editedEvent = await trpcClient.event.actions.edit.request.mutate({ + declaration, + annotation, + eventId, + transactionId, + keepAssignment: true + }) + + if (hasPotentialDuplicates(editedEvent, eventConfiguration)) { + return editedEvent + } + + return trpcClient.event.actions.register.request.mutate({ + declaration, + annotation, + eventId, + transactionId + }) +} + +export async function editAndDeclare({ + eventId, + eventConfiguration, + declaration, + transactionId, + annotation +}: CustomMutationParams) { + const editedEvent = await trpcClient.event.actions.edit.request.mutate({ + declaration, + annotation, + eventId, + transactionId, + keepAssignment: true + }) + + if (hasPotentialDuplicates(editedEvent, eventConfiguration)) { + return editedEvent + } + + return trpcClient.event.actions.declare.request.mutate({ + declaration, + annotation, + eventId, + transactionId + }) +} /** * Runs a sequence of actions from declare to validate. * diff --git a/packages/client/src/v2-events/features/events/actions/declare/DeclareActionMenu.tsx b/packages/client/src/v2-events/features/events/actions/declare/DeclareActionMenu.tsx index 3cd7b2ba2c7..0e4e3eee6f8 100644 --- a/packages/client/src/v2-events/features/events/actions/declare/DeclareActionMenu.tsx +++ b/packages/client/src/v2-events/features/events/actions/declare/DeclareActionMenu.tsx @@ -23,7 +23,8 @@ import { UUID, isActionAvailable, getActionConfig, - InherentFlags + InherentFlags, + getActionReview } from '@opencrvs/commons/client' import { PrimaryButton } from '@opencrvs/components/lib/buttons' import { DropdownMenu } from '@opencrvs/components/lib/Dropdown' @@ -98,15 +99,11 @@ function useDeclarationActions(event: EventDocument) { [ActionType.REGISTER]: events.customActions.registerOnDeclare.mutate } - const actionConfiguration = eventConfiguration.actions.find( - (a) => a.type === ActionType.DECLARE - ) - if (!actionConfiguration) { - throw new Error('Action configuration not found') + const reviewConfig = getActionReview(eventConfiguration, ActionType.EDIT) + if (!reviewConfig) { + throw new Error('Review config not found') } - const reviewConfig = actionConfiguration.review - /** * hasValidationErrors is true if: * - the form has any field validation errors or diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx index 27e54c18d4f..2314d5265e9 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx @@ -11,7 +11,14 @@ import React from 'react' import { useIntl } from 'react-intl' import { useTypedSearchParams } from 'react-router-typesafe-routes/dom' -import { ActionType, EventDocument } from '@opencrvs/commons/client' +import { + ActionType, + EventDocument, + getUUID, + getDeclaration, + getActionReview, + getCurrentEventState +} from '@opencrvs/commons/client' import { PrimaryButton } from '@opencrvs/components/lib/buttons' import { DropdownMenu } from '@opencrvs/components/lib/Dropdown' import { CaretDown } from '@opencrvs/components/lib/Icon/all-icons' @@ -23,7 +30,12 @@ import { useModal } from '@client/v2-events/hooks/useModal' import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' import { Review } from '@client/v2-events/features/events/components/Review' import { useUserAllowedActions } from '@client/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations' +import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' +import { validationErrorsInActionFormExist } from '@client/v2-events/components/forms/validation' import { useEventConfiguration } from '../../useEventConfiguration' +import { useActionAnnotation } from '../../useActionAnnotation' +import { useEventFormData } from '../../useEventFormData' +import { hasDeclarationFieldChanged } from '../correct/utils' const messages = { cancel: { @@ -71,6 +83,38 @@ function useEditActions(event: EventDocument) { const { closeActionView } = useEventFormNavigation() const [modal, openModal] = useModal() const events = useEvents() + const formConfig = getDeclaration(eventConfiguration) + const declaration = useEventFormData((state) => state.getFormValues()) + const { getAnnotation } = useActionAnnotation() + const annotation = getAnnotation() + const validatorContext = useValidatorContext() + const reviewConfig = getActionReview(eventConfiguration, ActionType.EDIT) + const eventIndex = getCurrentEventState(event, eventConfiguration) + + const formFields = formConfig.pages.flatMap((page) => page.fields) + const changedFields = formFields.filter((f) => + hasDeclarationFieldChanged( + f, + declaration, + eventIndex.declaration, + eventConfiguration, + validatorContext + ) + ) + + const anyValuesHaveChanged = changedFields.length > 0 + + if (!reviewConfig) { + throw new Error('Review config not found') + } + + const hasValidationErrors = validationErrorsInActionFormExist({ + formConfig, + form: declaration, + annotation, + context: validatorContext, + reviewFields: reviewConfig.fields + }) return { modals: [modal], @@ -96,10 +140,17 @@ function useEditActions(event: EventDocument) { }) if (confirm) { - // TODO CIHAN: call mutate fn + events.customActions.editAndRegister.mutate({ + eventId: event.id, + transactionId: getUUID(), + declaration, + annotation + }) + closeActionView(slug) } }, + disabled: hasValidationErrors || !anyValuesHaveChanged, hidden: !isActionAllowed(ActionType.REGISTER) }, { @@ -123,10 +174,18 @@ function useEditActions(event: EventDocument) { }) if (confirm) { - // TODO CIHAN: call mutate fn + events.customActions.editAndDeclare.mutate({ + eventId: event.id, + transactionId: getUUID(), + declaration, + annotation + }) + closeActionView(slug) } - } + }, + disabled: hasValidationErrors || !anyValuesHaveChanged, + hidden: !isActionAllowed(ActionType.DECLARE) }, { icon: 'ArchiveBox' as const, @@ -161,8 +220,12 @@ export function EditActionMenu({ event }: { event: EventDocument }) { - {actions.map(({ onClick, icon, label }, index) => ( - + {actions.map(({ onClick, icon, label, disabled }, index) => ( + {intl.formatMessage(label)} 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 4116ee7f958..a796f163000 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 @@ -150,6 +150,17 @@ setMutationDefaults(trpcOptionsProxy.event.actions.declare.request, { meta: { actionType: ActionType.DECLARE } }) +setMutationDefaults(trpcOptionsProxy.event.actions.edit.request, { + mutationFn: createEventActionMutationFn( + trpcOptionsProxy.event.actions.edit.request + ), + retry: retryUnlessConflict, + retryDelay, + onSuccess: deleteLocalEvent, + onError: errorToastOnConflict, + meta: { actionType: ActionType.EDIT } +}) + setMutationDefaults(trpcOptionsProxy.event.actions.register.request, { mutationFn: createEventActionMutationFn( trpcOptionsProxy.event.actions.register.request @@ -302,6 +313,8 @@ export const customMutationKeys = { validateOnDeclare: [['validateOnDeclare']], registerOnDeclare: [['registerOnDeclare']], registerOnValidate: [['registerOnValidate']], + editAndRegister: [['editAndRegister']], + editAndDeclare: [['editAndDeclare']], archiveOnDuplicate: [['archiveOnDuplicate']], makeCorrectionOnRequest: [['makeCorrectionOnRequest']] } satisfies Record @@ -310,6 +323,8 @@ interface CustomMutationTypes { validateOnDeclare: customApi.CustomMutationParams registerOnDeclare: customApi.CustomMutationParams registerOnValidate: customApi.CustomMutationParams + editAndRegister: customApi.CustomMutationParams + editAndDeclare: customApi.CustomMutationParams archiveOnDuplicate: customApi.ArchiveOnDuplicateParams makeCorrectionOnRequest: customApi.CorrectionRequestParams } @@ -341,6 +356,23 @@ queryClient.setMutationDefaults(customMutationKeys.registerOnValidate, { meta: { actionType: ActionType.REGISTER } }) +queryClient.setMutationDefaults(customMutationKeys.editAndRegister, { + mutationFn: customApi.editAndRegister, + retry: retryUnlessConflict, + retryDelay, + onSuccess: deleteLocalEvent, + onError: errorToastOnConflict, + meta: { actionType: ActionType.REGISTER } +}) + +queryClient.setMutationDefaults(customMutationKeys.editAndDeclare, { + mutationFn: customApi.editAndDeclare, + retry: retryUnlessConflict, + retryDelay, + onSuccess: deleteLocalEvent, + onError: errorToastOnConflict, + meta: { actionType: ActionType.DECLARE } +}) queryClient.setMutationDefaults(customMutationKeys.archiveOnDuplicate, { mutationFn: customApi.archiveOnDuplicate, retry: retryUnlessConflict, diff --git a/packages/client/src/v2-events/features/events/useEvents/useEvents.ts b/packages/client/src/v2-events/features/events/useEvents/useEvents.ts index 10283c0ec0a..0e913ed89fa 100644 --- a/packages/client/src/v2-events/features/events/useEvents/useEvents.ts +++ b/packages/client/src/v2-events/features/events/useEvents/useEvents.ts @@ -253,7 +253,9 @@ export function useEvents() { validateOnDeclare: useEventCustomAction('validateOnDeclare'), registerOnValidate: useEventCustomAction('registerOnValidate'), archiveOnDuplicate: useEventCustomAction('archiveOnDuplicate'), - makeCorrectionOnRequest: useEventCustomAction('makeCorrectionOnRequest') + makeCorrectionOnRequest: useEventCustomAction('makeCorrectionOnRequest'), + editAndRegister: useEventCustomAction('editAndRegister'), + editAndDeclare: useEventCustomAction('editAndDeclare') } } } From d50cded045e4cb9b49512da9e12c2f6f9bac8b71 Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 15:39:48 +0700 Subject: [PATCH 15/45] fix translation --- .../EventOverview/components/EventHistory/EventHistory.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx index 9d3dad26ee8..983b740f97a 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistory.tsx @@ -49,7 +49,7 @@ import { EventHistoryDialog } from './EventHistoryDialog/EventHistoryDialog' const eventHistoryStatusMessage = { id: 'events.history.status', defaultMessage: - '{status, select, Requested {Waiting for external validation} other {{action, select, CREATE {Draft} NOTIFY {Notified} VALIDATE {Validated} DRAFT {Draft} DECLARE {Sent for review} REGISTER {Registered} PRINT_CERTIFICATE {Certified} REJECT {Rejected} ARCHIVE {Archived} DUPLICATE_DETECTED {Flagged as potential duplicate} MARK_AS_DUPLICATE {Marked as a duplicate} CORRECTED {Record corrected} REQUEST_CORRECTION {Correction requested} APPROVE_CORRECTION {Correction approved} REJECT_CORRECTION {Correction rejected} READ {Viewed} ASSIGN {Assigned} UNASSIGN {Unassigned} UPDATE {Updated} other {Unknown}}}}' + '{status, select, Requested {Waiting for external validation} other {{action, select, CREATE {Draft} NOTIFY {Notified} EDIT {Edited} VALIDATE {Validated} DRAFT {Draft} DECLARE {Sent for review} REGISTER {Registered} PRINT_CERTIFICATE {Certified} REJECT {Rejected} ARCHIVE {Archived} DUPLICATE_DETECTED {Flagged as potential duplicate} MARK_AS_DUPLICATE {Marked as a duplicate} CORRECTED {Record corrected} REQUEST_CORRECTION {Correction requested} APPROVE_CORRECTION {Correction approved} REJECT_CORRECTION {Correction rejected} READ {Viewed} ASSIGN {Assigned} UNASSIGN {Unassigned} UPDATE {Updated} other {Unknown}}}}' } const LargeGreyedInfo = styled.div` From c47daf1654fd303de895e4914b2fa0b964e0eccf Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 15:56:03 +0700 Subject: [PATCH 16/45] fix role --- .../workqueues/EventOverview/EventOverview.stories.tsx | 2 +- packages/client/src/v2-events/hooks/useUserDetails.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.stories.tsx index fdc7517e6ba..09bc8acfcdc 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/EventOverview.stories.tsx @@ -661,7 +661,7 @@ const duplicateEvent = { generateActionDocument({ configuration: tennisClubMembershipEvent, action: ActionType.DUPLICATE_DETECTED, - defaults: actionDefaults + defaults: { ...actionDefaults, createdByRole: undefined } }), generateActionDocument({ configuration: tennisClubMembershipEvent, diff --git a/packages/client/src/v2-events/hooks/useUserDetails.ts b/packages/client/src/v2-events/hooks/useUserDetails.ts index 7f5ddcebaa1..a40e0ddf7f7 100644 --- a/packages/client/src/v2-events/hooks/useUserDetails.ts +++ b/packages/client/src/v2-events/hooks/useUserDetails.ts @@ -57,9 +57,9 @@ export function useUserDetails() { name: string role: string | undefined } => { - const role = createdByRole - ? intl.formatMessage(messages.role, { role: createdByRole }) - : undefined + const role = intl.formatMessage(messages.role, { + role: createdByRole || '' + }) if (createdByUserType === 'system') { const system = systems.find((s) => s._id === createdBy) From f7c91105afd1aa2b2ceed3d2254bc6fb7a1a08fb Mon Sep 17 00:00:00 2001 From: cibelius Date: Wed, 10 Dec 2025 16:24:09 +0700 Subject: [PATCH 17/45] dont use update action for edits --- .../features/events/actions/correct/useActionForHistory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts b/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts index 6628a65ce4a..6cb2b03bcd2 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts +++ b/packages/client/src/v2-events/features/events/actions/correct/useActionForHistory.ts @@ -139,7 +139,7 @@ export function expandWithClientSpecificActions( ): EventHistoryActionDocument[] { return extractHistoryActions(fullEvent).flatMap( (action) => { - if (isDeclarationAction(action)) { + if (isDeclarationAction(action) && action.type !== ActionType.EDIT) { if ( !hasDeclarationChanged( fullEvent, From 7e7b39e48752e78cf5914349dbca854185d23dcb Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 10:47:10 +0700 Subject: [PATCH 18/45] fixes --- .../token-generator.test.ts.snap | 6 +- .../client/src/tests/test-data-generators.ts | 11 +- .../events/actions/declare/Review.stories.tsx | 11 +- .../events/actions/edit/Review.stories.tsx | 100 ++++++++++++++++++ .../features/events/actions/edit/Review.tsx | 35 ++---- packages/commons/src/events/Flag.ts | 3 +- .../src/events/state/availableActions.ts | 5 +- packages/commons/src/events/state/flags.ts | 24 ++++- 8 files changed, 152 insertions(+), 43 deletions(-) create mode 100644 packages/client/src/v2-events/features/events/actions/edit/Review.stories.tsx diff --git a/packages/client/src/tests/__snapshots__/token-generator.test.ts.snap b/packages/client/src/tests/__snapshots__/token-generator.test.ts.snap index e1c40e18bec..e24a37c7723 100644 --- a/packages/client/src/tests/__snapshots__/token-generator.test.ts.snap +++ b/packages/client/src/tests/__snapshots__/token-generator.test.ts.snap @@ -1,11 +1,11 @@ // Vitest Snapshot v1 -exports[`Generates tokens > fieldAgent token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tc3VibWl0LWZvci1yZXZpZXciLCJzZWFyY2guYmlydGgiLCJzZWFyY2guZGVhdGgiLCJzZWFyY2gubWFycmlhZ2UiLCJ3b3JrcXVldWVbaWQ9YWxsLWV2ZW50c3xhc3NpZ25lZC10by15b3V8cmVjZW50fHJlcXVpcmVzLXVwZGF0ZXN8c2VudC1mb3ItcmV2aWV3XSIsInNlYXJjaFtldmVudD1iaXJ0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1kZWF0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1jaGlsZC1vbmJvYXJkaW5nLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PXRlbm5pcy1jbHViLW1lbWJlcnNoaXAsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Rk9PVEJBTExfQ0xVQl9NRU1CRVJTSElQLGFjY2Vzcz1hbGxdIiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5ub3RpZnlbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSJdLCJ1c2VyVHlwZSI6InVzZXIiLCJyb2xlIjoiRklFTERfQUdFTlQiLCJpYXQiOjE0ODcwNzY3MDgsImF1ZCI6Im9wZW5jcnZzOmdhdGV3YXktdXNlciIsImlzcyI6Im9wZW5jcnZzOmF1dGgtc2VydmljZSIsInN1YiI6IjY3ZWY3ZjgzZDZhOWNiOTJlOWVkYWE5OSJ9.d9Sw-6ISyCPnWF4VKF7lfpzEvm6dlZgOSd7YS2UJdnN8PpnF-oLWV4a8gfGDYNuZORBHSHoIcd7eaSYIBCZSFZl0hrBkH8mE408iZGSJwYStqHlEQ4iYZaqluid5jvmrHmr1u3uAmCf4NfV_TitYV--H2yY2cGtC7pzZs53mnqvdyxr0nAFO8qK-GMApavW89fuy6baMrztPjzV-VA9wVi8KSZccy2JoUuM91WvvhcYkxCGcHyHkpl9HKSTpfFdXBfkkiKO1L2IcZv5kgPmG5VBwx7RSiVQyNLFD1QXiPTSWZL8YVBtenSxh_AO9ZBJaPZ77szjByCdIVom9DckLpv_6aD3rysBrwCFP9Y5rpgLzK8ADR0pNctz5Ij2Q1zajMwnn5HvnOGEXFcRyvNPwLZyXTV5gAA09RXFSZ9G3BO8QSP-OHnaaljLhkhDUpLXu40DYtEw9s9LgciIXJKtrXXk4r8IneJyyqi-z16SrfWTkJqJ0GDPoKQBZX80CmFK46yxf1LkXjbrgs8bi3cEwWcZiDIAuMF9hzmIU631e0XykwnHTdrr7xyORYw-Sbg6UVLS47HknNndx75SOyWiOT5-EwvtLA0IftrIa2TNxhKXpVuxlt-BjX26GkzYSVz6O0LeuUlQwEoH0pxzmTrWsAE7vMktgqHB-cGhJVkuhzSg"`; +exports[`Generates tokens > fieldAgent token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tc3VibWl0LWZvci1yZXZpZXciLCJzZWFyY2guYmlydGgiLCJzZWFyY2guZGVhdGgiLCJzZWFyY2gubWFycmlhZ2UiLCJ3b3JrcXVldWVbaWQ9YWxsLWV2ZW50c3xhc3NpZ25lZC10by15b3V8cmVjZW50fHJlcXVpcmVzLXVwZGF0ZXN8c2VudC1mb3ItcmV2aWV3XSIsInNlYXJjaFtldmVudD1iaXJ0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1kZWF0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1jaGlsZC1vbmJvYXJkaW5nLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PXRlbm5pcy1jbHViLW1lbWJlcnNoaXAsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Rk9PVEJBTExfQ0xVQl9NRU1CRVJTSElQLGFjY2Vzcz1hbGxdIiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5ub3RpZnlbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC5lZGl0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iXSwidXNlclR5cGUiOiJ1c2VyIiwicm9sZSI6IkZJRUxEX0FHRU5UIiwiaWF0IjoxNDg3MDc2NzA4LCJhdWQiOiJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2N2VmN2Y4M2Q2YTljYjkyZTllZGFhOTkifQ.jTBvd6b-YDxPIOUql_qV3ebHx-ebSifnFzeIpvdf0z94DgfpuL76mUgbOR3TRkjSR1Rme-9KnCa9GnImt4fL25NethoPgZTmGvBAncpl-ngAKkDcFUYmjz1RcWDWDiih12M8G_HrtRj1jkuKW6FIuQ1lAcx3lonOubVFpPBE9ZV_Co9ILiidECUaAp3ukwItUb4UbG1cT-noRETc5dlnHIZYRckWWkLf1tdiwmETO56rRi0oUgyR5pJB5jOpZ7yqRJ2UIbZY9AVnEbER_RrUMRCsvrns_sb2mQ952M7dM719thV9YQusORBF9FxdkILjR7_0VgbC6FTbMiie7JkQS3QzZHuZ39cwtXoVGAFzYm5aCt-ZqY4Z9LsAhGsV0ak33pMKC_XkyEs5u5-2TqKrnQfVRLqmh8d-rW5F-s_v0wcUGDkoxA4DhKkVkgmUGkBEgmSb_wda_DkQGz1T1dha8ycUgl3qdwMQ_g5fDBYqvmiRGhhyDQp70BCq_tVqI2OF8O6ub1aEbsvr-JG1hS6q4utorahyxtOPfOdI9x1aMFom-5AREKPykiju1Tk62wmdQW8th-QaITzlF0O3zO1j9KmoQz2VY-QWcJvw95Vu7PJf2VqrfeVZNMU0bSl9qYgkq5mMBQANVofKoTOu287EXlADcHg63lNWkPb1Swo_vbU"`; -exports[`Generates tokens > localRegistrar token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5yZXZpZXctZHVwbGljYXRlcyIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJyZWNvcmQuY29uZmlybS1yZWdpc3RyYXRpb24iLCJyZWNvcmQucmVqZWN0LXJlZ2lzdHJhdGlvbiIsInBlcmZvcm1hbmNlLnJlYWQiLCJwZXJmb3JtYW5jZS5yZWFkLWRhc2hib2FyZHMiLCJwcm9maWxlLmVsZWN0cm9uaWMtc2lnbmF0dXJlIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInNlYXJjaC5iaXJ0aCIsInNlYXJjaC5kZWF0aCIsInNlYXJjaC5tYXJyaWFnZSIsIndvcmtxdWV1ZVtpZD1hbGwtZXZlbnRzfGFzc2lnbmVkLXRvLXlvdXxyZWNlbnR8cmVxdWlyZXMtY29tcGxldGlvbnxyZXF1aXJlcy11cGRhdGVzfGluLXJldmlldy1hbGx8aW4tZXh0ZXJuYWwtdmFsaWRhdGlvbnxyZWFkeS10by1wcmludHxyZWFkeS10by1pc3N1ZV0iLCJzZWFyY2hbZXZlbnQ9YmlydGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9ZGVhdGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Y2hpbGQtb25ib2FyZGluZyxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD10ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PUZPT1RCQUxMX0NMVUJfTUVNQkVSU0hJUCxhY2Nlc3M9YWxsXSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0IiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlYWRbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQucmVqZWN0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQuYXJjaGl2ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5wcmludC1jZXJ0aWZpZWQtY29waWVzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5jb3JyZWN0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQudW5hc3NpZ24tb3RoZXJzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQucmV2aWV3LWR1cGxpY2F0ZXNbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5jdXN0b20tYWN0aW9uW2V2ZW50PXRlbm5pcy1jbHViLW1lbWJlcnNoaXAsY3VzdG9tQWN0aW9uVHlwZT1BcHByb3ZlXSJdLCJ1c2VyVHlwZSI6InVzZXIiLCJyb2xlIjoiTE9DQUxfUkVHSVNUUkFSIiwiaWF0IjoxNDg3MDc2NzA4LCJhdWQiOiJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2ODIxYzE3NWRjZTRkNzg4NmQ0ZTgyMTAifQ.bOQVgD4oDl9EYsm7Zvcf8dB28woAwmCS1mZslceQIy-CY2h5u3nvOvKu1VYOBuSWGgg1BMmursX_0YNgEo_doZs_K1Cs5OGtclCcD2_UlZ42UQVZ_2qGHaH_oVHlIWSRsHBVP_2laG2D9HPDSXgiIHpgEsgc7LBhNEm2nW3r-2HC0PM2CxU5oKmhwJx7SqX9eR7ZTed-fKBVD1WdNwC30OOd94bzFA2ZfMlNdckWRaOsm1e0-waBbpkjxjO4MMSBRBXIDQc88xGbH9xdvJrbU-1YYLNfckOt3mDE6zuw9Ws8dRvoCrcVUPH6iZjGVvCdKDpyARy2_h_LBbOSzMv3ZPbYM-7XTHZnCn8y4hXuiBkhO-wuIeanxeU89J5AC5Q4TqttC1MV8PQiqCDfCXUz5sv1d7KfSla4yJHm8rNckyf8smzb8bztkREU3S6d-xWZJHMNce7lgfRZz70woqLe-S91tNh5uP8Twj_mTWZdlc9o9mCUnbJ7cy8KRadPjHH0pQXd_NT73ALxX0q5f-rKdUm_NyvP02QlPXW89IAoOG5pKZwZS_50U-Gr7blnGb-NoNe9dXaDEzodOzhv91SpiXWj8KIoRj06JQfPeIySV7LIz_esid30u-RGqn0b-X_gINBQEZACLIISCEUpHollUY29uR7lodEAbU0INtwrNOU"`; +exports[`Generates tokens > localRegistrar token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5yZXZpZXctZHVwbGljYXRlcyIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJyZWNvcmQuY29uZmlybS1yZWdpc3RyYXRpb24iLCJyZWNvcmQucmVqZWN0LXJlZ2lzdHJhdGlvbiIsInBlcmZvcm1hbmNlLnJlYWQiLCJwZXJmb3JtYW5jZS5yZWFkLWRhc2hib2FyZHMiLCJwcm9maWxlLmVsZWN0cm9uaWMtc2lnbmF0dXJlIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInNlYXJjaC5iaXJ0aCIsInNlYXJjaC5kZWF0aCIsInNlYXJjaC5tYXJyaWFnZSIsIndvcmtxdWV1ZVtpZD1hbGwtZXZlbnRzfGFzc2lnbmVkLXRvLXlvdXxyZWNlbnR8cmVxdWlyZXMtY29tcGxldGlvbnxyZXF1aXJlcy11cGRhdGVzfGluLXJldmlldy1hbGx8aW4tZXh0ZXJuYWwtdmFsaWRhdGlvbnxyZWFkeS10by1wcmludHxyZWFkeS10by1pc3N1ZV0iLCJzZWFyY2hbZXZlbnQ9YmlydGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9ZGVhdGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Y2hpbGQtb25ib2FyZGluZyxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD10ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PUZPT1RCQUxMX0NMVUJfTUVNQkVSU0hJUCxhY2Nlc3M9YWxsXSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0IiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlYWRbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQucmVqZWN0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQuYXJjaGl2ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQuZWRpdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyZWQucHJpbnQtY2VydGlmaWVkLWNvcGllc1tldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyZWQuY29ycmVjdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnVuYXNzaWduLW90aGVyc1tldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLnJldmlldy1kdXBsaWNhdGVzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuY3VzdG9tLWFjdGlvbltldmVudD10ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwLGN1c3RvbUFjdGlvblR5cGU9QXBwcm92ZV0iXSwidXNlclR5cGUiOiJ1c2VyIiwicm9sZSI6IkxPQ0FMX1JFR0lTVFJBUiIsImlhdCI6MTQ4NzA3NjcwOCwiYXVkIjoib3BlbmNydnM6Z2F0ZXdheS11c2VyIiwiaXNzIjoib3BlbmNydnM6YXV0aC1zZXJ2aWNlIiwic3ViIjoiNjgyMWMxNzVkY2U0ZDc4ODZkNGU4MjEwIn0.cBFP_JxzFm7n5hM2bEuNVZmaVW_ej6whaDSoV7PP1H3NYozaoLVkJ-BtYqb04z75xcvhpgr5YwxMbp1Gmvu2LeK4G3dm8YV96vC_OSaJQHtF3YbKqgoiEPmKLBe4RpuRo2OReXerT0C_60uLj2EqceHHdsC43NQfyYcpDB4wNn9cFGMpZxUgQkLNwKvEAWxgWHBhY9GT6ZQBzFEA8H3Ji_bJvR7EcBmw-ZL6s1bo8YHiaN-L_2bSYxsFi59u5_kLyjMBwo5ohSN4XJaw4FC4PalWh2IOl-ZV4dmAqa6F_POzBboxOse44s--XagPZxsIvnaWfhe0hZ9_ClR0cNgqbAzIC7o9VYS_yvBl82POztvPBWJra-RXuRG2kZqOM6NdBNbwNxrbYkDQy9WhYeMnBLB3LVZmyAhb8BgH4rfGe4l7p9KMjoK86dYMYW6td7mSulfIawykRUeCH2Ve8BDDCg9ZAU6qbher0liKC31M3e0fuYkMx-STKf3ofqf-IM6Y_SiJNtnduGFPIB6IuuNP0E_Td1lfZ_WhLHcA_zjxvEHo1RVD0DK5sILGGQJaikYiLO2WybM9cd9NxPjNtXxTW1XR2Mli0d6Bz7s7OQg_DjKoqEZX1LX7m-0qOWtWQpC3yswc0mWn1TEExsF3PbAwH6u3Pe_h31GQm_GCy1i7c4w"`; exports[`Generates tokens > localSystemAdmin token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJ1c2VyLnJlYWQ6bXktb2ZmaWNlIiwidXNlci5yZWFkOm15LWp1cmlzZGljdGlvbiIsInVzZXIudXBkYXRlOm15LWp1cmlzZGljdGlvbiIsIm9yZ2FuaXNhdGlvbi5yZWFkLWxvY2F0aW9uczpteS1qdXJpc2RpY3Rpb24iLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwicGVyZm9ybWFuY2Uudml0YWwtc3RhdGlzdGljcy1leHBvcnQiLCJvcmdhbmlzYXRpb24ucmVhZC1sb2NhdGlvbnM6bXktb2ZmaWNlIiwidXNlci5jcmVhdGVbcm9sZT1GSUVMRF9BR0VOVHxQT0xJQ0VfT0ZGSUNFUnxTT0NJQUxfV09SS0VSfEhFQUxUSENBUkVfV09SS0VSfExPQ0FMX0xFQURFUnxSRUdJU1RSQVRJT05fQUdFTlR8TE9DQUxfUkVHSVNUUkFSXSIsInVzZXIuZWRpdFtyb2xlPUZJRUxEX0FHRU5UfFBPTElDRV9PRkZJQ0VSfFNPQ0lBTF9XT1JLRVJ8SEVBTFRIQ0FSRV9XT1JLRVJ8TE9DQUxfTEVBREVSfFJFR0lTVFJBVElPTl9BR0VOVHxMT0NBTF9SRUdJU1RSQVJdIl0sInVzZXJUeXBlIjoidXNlciIsInJvbGUiOiJMT0NBTF9TWVNURU1fQURNSU4iLCJpYXQiOjE0ODcwNzY3MDgsImF1ZCI6Im9wZW5jcnZzOmdhdGV3YXktdXNlciIsImlzcyI6Im9wZW5jcnZzOmF1dGgtc2VydmljZSIsInN1YiI6IjY4Y2JkMjZmYzY0NzYxNTY1NDY5NTkxZCJ9.O-wogl0fSIAJJ1wd1o8fqo-NfucnEKr1dKbYgSdhnCljZk-0K0bYIQOpz6TqKOinAxFhRcy2hRpf2MS7Q-mBlW43pF_Rzcm7piz3Q14V8ZKgeY7XEdvtcbPvf004PnSd7gGqLLwmYM7WX4X9ok1_d5AS2p70z7oYb0XsrrJ1cE_hv5teNWZyjnUn3JIN2o4BBZYSCQ0IlNwC5uCL1givHiHGq3j6rmses-4QAhCPHXPLjWSoa272OEzQdIqEgZj4sZcNeAxMgFfU0BxUR5huviaXP-e7iPk_zbogCKFHG6wmr__FUTGL0QR4sOBXy52Rf1bExdu44CNXmagSofwsvkUYD5QPfxTtp6nJ3VXNUrhz_Ga66fV_m8EMcd_Hpqytfb1lo_5kkhuQRwgfiBRXcXXCEDkeYWPLnZtsrnSHmIl-LHHKpNEiIGP4phAo9uwM6cGEGAt9ewFTyHWvJ22PETyBocIXiGh0kCjZKI9tpV8QcGDKt7y4b_CQHI0Zd-A7ST3xZUsztavF_zH77DnU-vweEBp-EhEQ1zRwtEr3neWXeKbZBQz0gqbeq6zGl87S1ZflPTMOdbkYd7NlR0NiGy4CCysBlLxeo9Dgv_4uXXJcFgJOvGD5RDhKo-CA4Fcq64D_YXjhWmuC-5-pmxf5TpAb323upY4NqWlZ3cxh_6Y"`; exports[`Generates tokens > nationalSystemAdmin token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJjb25maWcudXBkYXRlOmFsbCIsIm9yZ2FuaXNhdGlvbi5yZWFkLWxvY2F0aW9uczphbGwiLCJ1c2VyLmNyZWF0ZTphbGwiLCJ1c2VyLnVwZGF0ZTphbGwiLCJ1c2VyLnJlYWQ6YWxsIiwicGVyZm9ybWFuY2UucmVhZCIsInBlcmZvcm1hbmNlLnJlYWQtZGFzaGJvYXJkcyIsInBlcmZvcm1hbmNlLnZpdGFsLXN0YXRpc3RpY3MtZXhwb3J0IiwicmVjb3JkLnJlaW5kZXgiLCJ1c2VyLmNyZWF0ZVtyb2xlPUZJRUxEX0FHRU5UfEhPU1BJVEFMX0NMRVJLfENPTU1VTklUWV9MRUFERVJ8UkVHSVNUUkFUSU9OX0FHRU5UfExPQ0FMX1JFR0lTVFJBUnxOQVRJT05BTF9SRUdJU1RSQVJ8TE9DQUxfU1lTVEVNX0FETUlOfE5BVElPTkFMX1NZU1RFTV9BRE1JTnxQRVJGT1JNQU5DRV9NQU5BR0VSXSIsInVzZXIuZWRpdFtyb2xlPUZJRUxEX0FHRU5UfEhPU1BJVEFMX0NMRVJLfENPTU1VTklUWV9MRUFERVJ8UkVHSVNUUkFUSU9OX0FHRU5UfExPQ0FMX1JFR0lTVFJBUnxOQVRJT05BTF9SRUdJU1RSQVJ8TE9DQUxfU1lTVEVNX0FETUlOfE5BVElPTkFMX1NZU1RFTV9BRE1JTnxQRVJGT1JNQU5DRV9NQU5BR0VSXSJdLCJ1c2VyVHlwZSI6InVzZXIiLCJyb2xlIjoiTkFUSU9OQUxfU1lTVEVNX0FETUlOIiwiaWF0IjoxNDg3MDc2NzA4LCJhdWQiOiJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2OGRmOTUyOWY4ZjNhNzMwMDdhNDQyN2MifQ.WGLo5JF0PlPhZoYFhn8lg1dt-iaDSUQFGUDolQsQHwuY_AEZpFIKjaKL-GYJMLpuHPahk8wK7OTjQJZ54lH5EUvL8jqRfIMvQTRK6IHi_jvMHJQbXIvS434za-rY99_vsBnWIhTvqawIbKkOG89FBwdF6OVI5SxNV39ltjPChrN4-Ke-8QjZjJyUfxLanh4NMfsd3ohnpH5q3QtJMfDTl8iJ7-cJ3lwpI29gVtiXyULfbCWGXMPjwutCKNyNGA-hIMzvFHsFFw23EzdwnIqLBNzjMwFjeB3RwWBPexC6dctKuHY8ooRL-oWSxran0rOUwNr3Y0VKIl5C1EoMkBkkwHgy7awLPYstZGP6aC-B4pdQp9t5dx8omB6OozG0WD2oJJx7uH8pbghTsLdnQr1STF0gpL4qGWIE5RQ-eFCVQAQlXDPrVCDBEkK5ip8_OEag7uoPdJEybmTUVitMpTcYEdXQzJ2GFIkShXH4nkHeF60JHyviPFM2ClWD_DLLCr1v6fWLSFgMjfakK_YtOzLRSIAnwcGRk1RwR_H9-YSI-fVxM39suMZ78o5iXQiifuFjZWLF9FBwwVDuM0XAf7gD_zGx9uHSOw2I88CUtZpmPfvsesUW7TmHWX_VkSztH1XQt9BkUrPZvS6sarFZeswYQiT7EgZ5KqMcsXhlk9tVQo8"`; -exports[`Generates tokens > registrationAgent token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0Iiwic2VhcmNoLmJpcnRoIiwic2VhcmNoLmRlYXRoIiwic2VhcmNoLm1hcnJpYWdlIiwid29ya3F1ZXVlW2lkPWFsbC1ldmVudHN8YXNzaWduZWQtdG8teW91fHJlY2VudHxyZXF1aXJlcy1jb21wbGV0aW9ufHJlcXVpcmVzLXVwZGF0ZXN8aW4tcmV2aWV3fHNlbnQtZm9yLWFwcHJvdmFsfGluLWV4dGVybmFsLXZhbGlkYXRpb258cmVhZHktdG8tcHJpbnR8cmVhZHktdG8taXNzdWVdIiwic2VhcmNoW2V2ZW50PWJpcnRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWRlYXRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWNoaWxkLW9uYm9hcmRpbmcsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9dGVubmlzLWNsdWItbWVtYmVyc2hpcCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1GT09UQkFMTF9DTFVCX01FTUJFUlNISVAsYWNjZXNzPWFsbF0iLCJyZWNvcmQuY3JlYXRlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVhZFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC52YWxpZGF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLnJlamVjdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLmFyY2hpdmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5yZWdpc3RlcmVkLnByaW50LWNlcnRpZmllZC1jb3BpZXNbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5yZWdpc3RlcmVkLnJlcXVlc3QtY29ycmVjdGlvbltldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIl0sInVzZXJUeXBlIjoidXNlciIsInJvbGUiOiJSRUdJU1RSQVRJT05fQUdFTlQiLCJpYXQiOjE0ODcwNzY3MDgsImF1ZCI6Im9wZW5jcnZzOmdhdGV3YXktdXNlciIsImlzcyI6Im9wZW5jcnZzOmF1dGgtc2VydmljZSIsInN1YiI6IjY3ZWY3ZjgzZDZhOWNiOTJlOWVkYWFhMSJ9.Xu69LUHgzXHzALiYhlCNOIE5bcrA5y7ykzGguPr9H7u3wBHgSK9Qts2PdG2X3ZdGMU0GyTY4KXnW8tHZDB8pAqufv-wilB6cmeudsMqiE9sotOa0pyVUvVMMtg07eQGoRvTfziunuhRxlpbD-TMTLRl0h0Pi2vMRiKx86NKK83_RGURAPQCJpy_DYKzyfe2eaisvZZDLSCEn1NK-xETfDwvM5D97NhuJtuYOuIrHty1-2qSBghsPqvjkeM-N1o-TiCyB-HVvDtv3J4ODDCTvhCXRohXu-1IoRzZ5F_1eMsQAs4CSLW1gg_A7UMjqD1trdyAceOyrtGiuAsjlHhT7HABNM6gidx37IfZM7I1Bry_iE-VnYB5gKLHTmnSBR2XtJRfjWA9U2O0Ul5TN5wC1kQku7axuvPmJpBQiwaY5RriHhtDLIrT8h9N-ZsVp183RYEpeajX8FMHgkOypWgCzzi4nk_IeQTjNIgTYnC_505gl-DyF_z4LEfJNgFjjWgRJYghCEVhB_yo4wO3PMhQBNhh95rLy5akXcsndF6db0VqdJnWHP8SZddm2mZ3fCIe8igz77d9RJE9Xl70bOsfDUZ-n0B1pNAQvhlvlzPgbQLRFim8VguR8efvGY3V2aOOwa0lyce5gWkf-bDCdKd0bXd9Ax7yokwXCA4BPxmni91I"`; +exports[`Generates tokens > registrationAgent token 1`] = `"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0Iiwic2VhcmNoLmJpcnRoIiwic2VhcmNoLmRlYXRoIiwic2VhcmNoLm1hcnJpYWdlIiwid29ya3F1ZXVlW2lkPWFsbC1ldmVudHN8YXNzaWduZWQtdG8teW91fHJlY2VudHxyZXF1aXJlcy1jb21wbGV0aW9ufHJlcXVpcmVzLXVwZGF0ZXN8aW4tcmV2aWV3fHNlbnQtZm9yLWFwcHJvdmFsfGluLWV4dGVybmFsLXZhbGlkYXRpb258cmVhZHktdG8tcHJpbnR8cmVhZHktdG8taXNzdWVdIiwic2VhcmNoW2V2ZW50PWJpcnRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWRlYXRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWNoaWxkLW9uYm9hcmRpbmcsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9dGVubmlzLWNsdWItbWVtYmVyc2hpcCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1GT09UQkFMTF9DTFVCX01FTUJFUlNISVAsYWNjZXNzPWFsbF0iLCJyZWNvcmQuY3JlYXRlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVhZFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC52YWxpZGF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLnJlamVjdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLmVkaXRbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC5hcmNoaXZlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5wcmludC1jZXJ0aWZpZWQtY29waWVzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5yZXF1ZXN0LWNvcnJlY3Rpb25bZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSJdLCJ1c2VyVHlwZSI6InVzZXIiLCJyb2xlIjoiUkVHSVNUUkFUSU9OX0FHRU5UIiwiaWF0IjoxNDg3MDc2NzA4LCJhdWQiOiJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2N2VmN2Y4M2Q2YTljYjkyZTllZGFhYTEifQ.Q4WKx6JP71cHPcywG_bm1_CoIRFgb4FTKvUGK8wgGRcRhqOH91ZAx_41FBeREic04Hu2jvRhDluijZ4pofBsytVz4t-ia_slpUdYeDVj2lKUtFnROA9hsPB0t6XtTBC5RKCqxVU9FtSXdRPvbjXJA9eSScFUNg7zpCngb4MNXUnPtosR6jV8NRGsXZFCuPQC0gUNF8ZGrwO3BWTOB3HOXk6KrH59EHm1obRDB-QU2OfqC9a6bXlUevCnTfXG9_Fx7UNVb_F4Ge3u9DlwhU2ffptKDRDGqhRvL8ngXpkEgbPPfB1DqcAJOew2IHLenNmeoSCmg6XFa530KslFPGoe0hw8FXpp42SJOE8yLRerV7HtTvp9KMKaQe0MusHkQEGZpq-HHNhJNhAGXYjuSc5Kf2e1AdoJ1Khjoddq_HM5WbmArdPmYjuq_lZ8s_r-5Kw4PQ8ob5vo1vDI1b1FVotF7jaz3cHhMh2tkNvrk7aA5-Bx_ln_dPuhr4Uj8u5sZBQe2_CJS3avtP1YYylf-iZE3312GC7kFxiyodwh8OxoGpbn97dyIcEi3glLR4Iows0jh5Ml32bNEBXmjaZGV_vfAughW_NJgO8lwyzpjcmrsgbenfiKubRDYbvVniKLMMs0_VAZCvvzd6SGj2Lb9Eha3WZ-F4jsUluFZ8stSbP3Mi0"`; diff --git a/packages/client/src/tests/test-data-generators.ts b/packages/client/src/tests/test-data-generators.ts index 1baeedfede6..7533fb1564c 100644 --- a/packages/client/src/tests/test-data-generators.ts +++ b/packages/client/src/tests/test-data-generators.ts @@ -55,11 +55,11 @@ export function testDataGenerator(rngSeed?: number) { */ fieldAgent: - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tc3VibWl0LWZvci1yZXZpZXciLCJzZWFyY2guYmlydGgiLCJzZWFyY2guZGVhdGgiLCJzZWFyY2gubWFycmlhZ2UiLCJ3b3JrcXVldWVbaWQ9YWxsLWV2ZW50c3xhc3NpZ25lZC10by15b3V8cmVjZW50fHJlcXVpcmVzLXVwZGF0ZXN8c2VudC1mb3ItcmV2aWV3XSIsInNlYXJjaFtldmVudD1iaXJ0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1kZWF0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1jaGlsZC1vbmJvYXJkaW5nLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PXRlbm5pcy1jbHViLW1lbWJlcnNoaXAsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Rk9PVEJBTExfQ0xVQl9NRU1CRVJTSElQLGFjY2Vzcz1hbGxdIiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5ub3RpZnlbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSJdLCJ1c2VyVHlwZSI6InVzZXIiLCJyb2xlIjoiRklFTERfQUdFTlQiLCJpYXQiOjE0ODcwNzY3MDgsImF1ZCI6Im9wZW5jcnZzOmdhdGV3YXktdXNlciIsImlzcyI6Im9wZW5jcnZzOmF1dGgtc2VydmljZSIsInN1YiI6IjY3ZWY3ZjgzZDZhOWNiOTJlOWVkYWE5OSJ9.d9Sw-6ISyCPnWF4VKF7lfpzEvm6dlZgOSd7YS2UJdnN8PpnF-oLWV4a8gfGDYNuZORBHSHoIcd7eaSYIBCZSFZl0hrBkH8mE408iZGSJwYStqHlEQ4iYZaqluid5jvmrHmr1u3uAmCf4NfV_TitYV--H2yY2cGtC7pzZs53mnqvdyxr0nAFO8qK-GMApavW89fuy6baMrztPjzV-VA9wVi8KSZccy2JoUuM91WvvhcYkxCGcHyHkpl9HKSTpfFdXBfkkiKO1L2IcZv5kgPmG5VBwx7RSiVQyNLFD1QXiPTSWZL8YVBtenSxh_AO9ZBJaPZ77szjByCdIVom9DckLpv_6aD3rysBrwCFP9Y5rpgLzK8ADR0pNctz5Ij2Q1zajMwnn5HvnOGEXFcRyvNPwLZyXTV5gAA09RXFSZ9G3BO8QSP-OHnaaljLhkhDUpLXu40DYtEw9s9LgciIXJKtrXXk4r8IneJyyqi-z16SrfWTkJqJ0GDPoKQBZX80CmFK46yxf1LkXjbrgs8bi3cEwWcZiDIAuMF9hzmIU631e0XykwnHTdrr7xyORYw-Sbg6UVLS47HknNndx75SOyWiOT5-EwvtLA0IftrIa2TNxhKXpVuxlt-BjX26GkzYSVz6O0LeuUlQwEoH0pxzmTrWsAE7vMktgqHB-cGhJVkuhzSg', + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tc3VibWl0LWZvci1yZXZpZXciLCJzZWFyY2guYmlydGgiLCJzZWFyY2guZGVhdGgiLCJzZWFyY2gubWFycmlhZ2UiLCJ3b3JrcXVldWVbaWQ9YWxsLWV2ZW50c3xhc3NpZ25lZC10by15b3V8cmVjZW50fHJlcXVpcmVzLXVwZGF0ZXN8c2VudC1mb3ItcmV2aWV3XSIsInNlYXJjaFtldmVudD1iaXJ0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1kZWF0aCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1jaGlsZC1vbmJvYXJkaW5nLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PXRlbm5pcy1jbHViLW1lbWJlcnNoaXAsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Rk9PVEJBTExfQ0xVQl9NRU1CRVJTSElQLGFjY2Vzcz1hbGxdIiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5ub3RpZnlbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC5lZGl0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iXSwidXNlclR5cGUiOiJ1c2VyIiwicm9sZSI6IkZJRUxEX0FHRU5UIiwiaWF0IjoxNDg3MDc2NzA4LCJhdWQiOiJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2N2VmN2Y4M2Q2YTljYjkyZTllZGFhOTkifQ.jTBvd6b-YDxPIOUql_qV3ebHx-ebSifnFzeIpvdf0z94DgfpuL76mUgbOR3TRkjSR1Rme-9KnCa9GnImt4fL25NethoPgZTmGvBAncpl-ngAKkDcFUYmjz1RcWDWDiih12M8G_HrtRj1jkuKW6FIuQ1lAcx3lonOubVFpPBE9ZV_Co9ILiidECUaAp3ukwItUb4UbG1cT-noRETc5dlnHIZYRckWWkLf1tdiwmETO56rRi0oUgyR5pJB5jOpZ7yqRJ2UIbZY9AVnEbER_RrUMRCsvrns_sb2mQ952M7dM719thV9YQusORBF9FxdkILjR7_0VgbC6FTbMiie7JkQS3QzZHuZ39cwtXoVGAFzYm5aCt-ZqY4Z9LsAhGsV0ak33pMKC_XkyEs5u5-2TqKrnQfVRLqmh8d-rW5F-s_v0wcUGDkoxA4DhKkVkgmUGkBEgmSb_wda_DkQGz1T1dha8ycUgl3qdwMQ_g5fDBYqvmiRGhhyDQp70BCq_tVqI2OF8O6ub1aEbsvr-JG1hS6q4utorahyxtOPfOdI9x1aMFom-5AREKPykiju1Tk62wmdQW8th-QaITzlF0O3zO1j9KmoQz2VY-QWcJvw95Vu7PJf2VqrfeVZNMU0bSl9qYgkq5mMBQANVofKoTOu287EXlADcHg63lNWkPb1Swo_vbU', localRegistrar: - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5yZXZpZXctZHVwbGljYXRlcyIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJyZWNvcmQuY29uZmlybS1yZWdpc3RyYXRpb24iLCJyZWNvcmQucmVqZWN0LXJlZ2lzdHJhdGlvbiIsInBlcmZvcm1hbmNlLnJlYWQiLCJwZXJmb3JtYW5jZS5yZWFkLWRhc2hib2FyZHMiLCJwcm9maWxlLmVsZWN0cm9uaWMtc2lnbmF0dXJlIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInNlYXJjaC5iaXJ0aCIsInNlYXJjaC5kZWF0aCIsInNlYXJjaC5tYXJyaWFnZSIsIndvcmtxdWV1ZVtpZD1hbGwtZXZlbnRzfGFzc2lnbmVkLXRvLXlvdXxyZWNlbnR8cmVxdWlyZXMtY29tcGxldGlvbnxyZXF1aXJlcy11cGRhdGVzfGluLXJldmlldy1hbGx8aW4tZXh0ZXJuYWwtdmFsaWRhdGlvbnxyZWFkeS10by1wcmludHxyZWFkeS10by1pc3N1ZV0iLCJzZWFyY2hbZXZlbnQ9YmlydGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9ZGVhdGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Y2hpbGQtb25ib2FyZGluZyxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD10ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PUZPT1RCQUxMX0NMVUJfTUVNQkVSU0hJUCxhY2Nlc3M9YWxsXSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0IiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlYWRbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQucmVqZWN0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQuYXJjaGl2ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5wcmludC1jZXJ0aWZpZWQtY29waWVzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5jb3JyZWN0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQudW5hc3NpZ24tb3RoZXJzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQucmV2aWV3LWR1cGxpY2F0ZXNbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5jdXN0b20tYWN0aW9uW2V2ZW50PXRlbm5pcy1jbHViLW1lbWJlcnNoaXAsY3VzdG9tQWN0aW9uVHlwZT1BcHByb3ZlXSJdLCJ1c2VyVHlwZSI6InVzZXIiLCJyb2xlIjoiTE9DQUxfUkVHSVNUUkFSIiwiaWF0IjoxNDg3MDc2NzA4LCJhdWQiOiJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2ODIxYzE3NWRjZTRkNzg4NmQ0ZTgyMTAifQ.bOQVgD4oDl9EYsm7Zvcf8dB28woAwmCS1mZslceQIy-CY2h5u3nvOvKu1VYOBuSWGgg1BMmursX_0YNgEo_doZs_K1Cs5OGtclCcD2_UlZ42UQVZ_2qGHaH_oVHlIWSRsHBVP_2laG2D9HPDSXgiIHpgEsgc7LBhNEm2nW3r-2HC0PM2CxU5oKmhwJx7SqX9eR7ZTed-fKBVD1WdNwC30OOd94bzFA2ZfMlNdckWRaOsm1e0-waBbpkjxjO4MMSBRBXIDQc88xGbH9xdvJrbU-1YYLNfckOt3mDE6zuw9Ws8dRvoCrcVUPH6iZjGVvCdKDpyARy2_h_LBbOSzMv3ZPbYM-7XTHZnCn8y4hXuiBkhO-wuIeanxeU89J5AC5Q4TqttC1MV8PQiqCDfCXUz5sv1d7KfSla4yJHm8rNckyf8smzb8bztkREU3S6d-xWZJHMNce7lgfRZz70woqLe-S91tNh5uP8Twj_mTWZdlc9o9mCUnbJ7cy8KRadPjHH0pQXd_NT73ALxX0q5f-rKdUm_NyvP02QlPXW89IAoOG5pKZwZS_50U-Gr7blnGb-NoNe9dXaDEzodOzhv91SpiXWj8KIoRj06JQfPeIySV7LIz_esid30u-RGqn0b-X_gINBQEZACLIISCEUpHollUY29uR7lodEAbU0INtwrNOU', + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5yZXZpZXctZHVwbGljYXRlcyIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJyZWNvcmQuY29uZmlybS1yZWdpc3RyYXRpb24iLCJyZWNvcmQucmVqZWN0LXJlZ2lzdHJhdGlvbiIsInBlcmZvcm1hbmNlLnJlYWQiLCJwZXJmb3JtYW5jZS5yZWFkLWRhc2hib2FyZHMiLCJwcm9maWxlLmVsZWN0cm9uaWMtc2lnbmF0dXJlIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInNlYXJjaC5iaXJ0aCIsInNlYXJjaC5kZWF0aCIsInNlYXJjaC5tYXJyaWFnZSIsIndvcmtxdWV1ZVtpZD1hbGwtZXZlbnRzfGFzc2lnbmVkLXRvLXlvdXxyZWNlbnR8cmVxdWlyZXMtY29tcGxldGlvbnxyZXF1aXJlcy11cGRhdGVzfGluLXJldmlldy1hbGx8aW4tZXh0ZXJuYWwtdmFsaWRhdGlvbnxyZWFkeS10by1wcmludHxyZWFkeS10by1pc3N1ZV0iLCJzZWFyY2hbZXZlbnQ9YmlydGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9ZGVhdGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9Y2hpbGQtb25ib2FyZGluZyxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD10ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PUZPT1RCQUxMX0NMVUJfTUVNQkVSU0hJUCxhY2Nlc3M9YWxsXSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0IiwicmVjb3JkLmNyZWF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlYWRbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQucmVqZWN0W2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQuYXJjaGl2ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuZGVjbGFyZWQuZWRpdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyZWQucHJpbnQtY2VydGlmaWVkLWNvcGllc1tldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnJlZ2lzdGVyZWQuY29ycmVjdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLnVuYXNzaWduLW90aGVyc1tldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLnJldmlldy1kdXBsaWNhdGVzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQuY3VzdG9tLWFjdGlvbltldmVudD10ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwLGN1c3RvbUFjdGlvblR5cGU9QXBwcm92ZV0iXSwidXNlclR5cGUiOiJ1c2VyIiwicm9sZSI6IkxPQ0FMX1JFR0lTVFJBUiIsImlhdCI6MTQ4NzA3NjcwOCwiYXVkIjoib3BlbmNydnM6Z2F0ZXdheS11c2VyIiwiaXNzIjoib3BlbmNydnM6YXV0aC1zZXJ2aWNlIiwic3ViIjoiNjgyMWMxNzVkY2U0ZDc4ODZkNGU4MjEwIn0.cBFP_JxzFm7n5hM2bEuNVZmaVW_ej6whaDSoV7PP1H3NYozaoLVkJ-BtYqb04z75xcvhpgr5YwxMbp1Gmvu2LeK4G3dm8YV96vC_OSaJQHtF3YbKqgoiEPmKLBe4RpuRo2OReXerT0C_60uLj2EqceHHdsC43NQfyYcpDB4wNn9cFGMpZxUgQkLNwKvEAWxgWHBhY9GT6ZQBzFEA8H3Ji_bJvR7EcBmw-ZL6s1bo8YHiaN-L_2bSYxsFi59u5_kLyjMBwo5ohSN4XJaw4FC4PalWh2IOl-ZV4dmAqa6F_POzBboxOse44s--XagPZxsIvnaWfhe0hZ9_ClR0cNgqbAzIC7o9VYS_yvBl82POztvPBWJra-RXuRG2kZqOM6NdBNbwNxrbYkDQy9WhYeMnBLB3LVZmyAhb8BgH4rfGe4l7p9KMjoK86dYMYW6td7mSulfIawykRUeCH2Ve8BDDCg9ZAU6qbher0liKC31M3e0fuYkMx-STKf3ofqf-IM6Y_SiJNtnduGFPIB6IuuNP0E_Td1lfZ_WhLHcA_zjxvEHo1RVD0DK5sILGGQJaikYiLO2WybM9cd9NxPjNtXxTW1XR2Mli0d6Bz7s7OQg_DjKoqEZX1LX7m-0qOWtWQpC3yswc0mWn1TEExsF3PbAwH6u3Pe_h31GQm_GCy1i7c4w', registrationAgent: - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0Iiwic2VhcmNoLmJpcnRoIiwic2VhcmNoLmRlYXRoIiwic2VhcmNoLm1hcnJpYWdlIiwid29ya3F1ZXVlW2lkPWFsbC1ldmVudHN8YXNzaWduZWQtdG8teW91fHJlY2VudHxyZXF1aXJlcy1jb21wbGV0aW9ufHJlcXVpcmVzLXVwZGF0ZXN8aW4tcmV2aWV3fHNlbnQtZm9yLWFwcHJvdmFsfGluLWV4dGVybmFsLXZhbGlkYXRpb258cmVhZHktdG8tcHJpbnR8cmVhZHktdG8taXNzdWVdIiwic2VhcmNoW2V2ZW50PWJpcnRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWRlYXRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWNoaWxkLW9uYm9hcmRpbmcsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9dGVubmlzLWNsdWItbWVtYmVyc2hpcCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1GT09UQkFMTF9DTFVCX01FTUJFUlNISVAsYWNjZXNzPWFsbF0iLCJyZWNvcmQuY3JlYXRlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVhZFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC52YWxpZGF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLnJlamVjdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLmFyY2hpdmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5yZWdpc3RlcmVkLnByaW50LWNlcnRpZmllZC1jb3BpZXNbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5yZWdpc3RlcmVkLnJlcXVlc3QtY29ycmVjdGlvbltldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIl0sInVzZXJUeXBlIjoidXNlciIsInJvbGUiOiJSRUdJU1RSQVRJT05fQUdFTlQiLCJpYXQiOjE0ODcwNzY3MDgsImF1ZCI6Im9wZW5jcnZzOmdhdGV3YXktdXNlciIsImlzcyI6Im9wZW5jcnZzOmF1dGgtc2VydmljZSIsInN1YiI6IjY3ZWY3ZjgzZDZhOWNiOTJlOWVkYWFhMSJ9.Xu69LUHgzXHzALiYhlCNOIE5bcrA5y7ykzGguPr9H7u3wBHgSK9Qts2PdG2X3ZdGMU0GyTY4KXnW8tHZDB8pAqufv-wilB6cmeudsMqiE9sotOa0pyVUvVMMtg07eQGoRvTfziunuhRxlpbD-TMTLRl0h0Pi2vMRiKx86NKK83_RGURAPQCJpy_DYKzyfe2eaisvZZDLSCEn1NK-xETfDwvM5D97NhuJtuYOuIrHty1-2qSBghsPqvjkeM-N1o-TiCyB-HVvDtv3J4ODDCTvhCXRohXu-1IoRzZ5F_1eMsQAs4CSLW1gg_A7UMjqD1trdyAceOyrtGiuAsjlHhT7HABNM6gidx37IfZM7I1Bry_iE-VnYB5gKLHTmnSBR2XtJRfjWA9U2O0Ul5TN5wC1kQku7axuvPmJpBQiwaY5RriHhtDLIrT8h9N-ZsVp183RYEpeajX8FMHgkOypWgCzzi4nk_IeQTjNIgTYnC_505gl-DyF_z4LEfJNgFjjWgRJYghCEVhB_yo4wO3PMhQBNhh95rLy5akXcsndF6db0VqdJnWHP8SZddm2mZ3fCIe8igz77d9RJE9Xl70bOsfDUZ-n0B1pNAQvhlvlzPgbQLRFim8VguR8efvGY3V2aOOwa0lyce5gWkf-bDCdKd0bXd9Ax7yokwXCA4BPxmni91I', + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInVzZXIucmVhZDpvbmx5LW15LWF1ZGl0Iiwic2VhcmNoLmJpcnRoIiwic2VhcmNoLmRlYXRoIiwic2VhcmNoLm1hcnJpYWdlIiwid29ya3F1ZXVlW2lkPWFsbC1ldmVudHN8YXNzaWduZWQtdG8teW91fHJlY2VudHxyZXF1aXJlcy1jb21wbGV0aW9ufHJlcXVpcmVzLXVwZGF0ZXN8aW4tcmV2aWV3fHNlbnQtZm9yLWFwcHJvdmFsfGluLWV4dGVybmFsLXZhbGlkYXRpb258cmVhZHktdG8tcHJpbnR8cmVhZHktdG8taXNzdWVdIiwic2VhcmNoW2V2ZW50PWJpcnRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWRlYXRoLGFjY2Vzcz1hbGxdIiwic2VhcmNoW2V2ZW50PWNoaWxkLW9uYm9hcmRpbmcsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9dGVubmlzLWNsdWItbWVtYmVyc2hpcCxhY2Nlc3M9YWxsXSIsInNlYXJjaFtldmVudD1GT09UQkFMTF9DTFVCX01FTUJFUlNISVAsYWNjZXNzPWFsbF0iLCJyZWNvcmQuY3JlYXRlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVhZFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC52YWxpZGF0ZVtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLnJlamVjdFtldmVudD1iaXJ0aHxkZWF0aHx0ZW5uaXMtY2x1Yi1tZW1iZXJzaGlwfGNoaWxkLW9uYm9hcmRpbmddIiwicmVjb3JkLmRlY2xhcmVkLmVkaXRbZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSIsInJlY29yZC5kZWNsYXJlZC5hcmNoaXZlW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5wcmludC1jZXJ0aWZpZWQtY29waWVzW2V2ZW50PWJpcnRofGRlYXRofHRlbm5pcy1jbHViLW1lbWJlcnNoaXB8Y2hpbGQtb25ib2FyZGluZ10iLCJyZWNvcmQucmVnaXN0ZXJlZC5yZXF1ZXN0LWNvcnJlY3Rpb25bZXZlbnQ9YmlydGh8ZGVhdGh8dGVubmlzLWNsdWItbWVtYmVyc2hpcHxjaGlsZC1vbmJvYXJkaW5nXSJdLCJ1c2VyVHlwZSI6InVzZXIiLCJyb2xlIjoiUkVHSVNUUkFUSU9OX0FHRU5UIiwiaWF0IjoxNDg3MDc2NzA4LCJhdWQiOiJvcGVuY3J2czpnYXRld2F5LXVzZXIiLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2N2VmN2Y4M2Q2YTljYjkyZTllZGFhYTEifQ.Q4WKx6JP71cHPcywG_bm1_CoIRFgb4FTKvUGK8wgGRcRhqOH91ZAx_41FBeREic04Hu2jvRhDluijZ4pofBsytVz4t-ia_slpUdYeDVj2lKUtFnROA9hsPB0t6XtTBC5RKCqxVU9FtSXdRPvbjXJA9eSScFUNg7zpCngb4MNXUnPtosR6jV8NRGsXZFCuPQC0gUNF8ZGrwO3BWTOB3HOXk6KrH59EHm1obRDB-QU2OfqC9a6bXlUevCnTfXG9_Fx7UNVb_F4Ge3u9DlwhU2ffptKDRDGqhRvL8ngXpkEgbPPfB1DqcAJOew2IHLenNmeoSCmg6XFa530KslFPGoe0hw8FXpp42SJOE8yLRerV7HtTvp9KMKaQe0MusHkQEGZpq-HHNhJNhAGXYjuSc5Kf2e1AdoJ1Khjoddq_HM5WbmArdPmYjuq_lZ8s_r-5Kw4PQ8ob5vo1vDI1b1FVotF7jaz3cHhMh2tkNvrk7aA5-Bx_ln_dPuhr4Uj8u5sZBQe2_CJS3avtP1YYylf-iZE3312GC7kFxiyodwh8OxoGpbn97dyIcEi3glLR4Iows0jh5Ml32bNEBXmjaZGV_vfAughW_NJgO8lwyzpjcmrsgbenfiKubRDYbvVniKLMMs0_VAZCvvzd6SGj2Lb9Eha3WZ-F4jsUluFZ8stSbP3Mi0', localSystemAdmin: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJ1c2VyLnJlYWQ6bXktb2ZmaWNlIiwidXNlci5yZWFkOm15LWp1cmlzZGljdGlvbiIsInVzZXIudXBkYXRlOm15LWp1cmlzZGljdGlvbiIsIm9yZ2FuaXNhdGlvbi5yZWFkLWxvY2F0aW9uczpteS1qdXJpc2RpY3Rpb24iLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwicGVyZm9ybWFuY2Uudml0YWwtc3RhdGlzdGljcy1leHBvcnQiLCJvcmdhbmlzYXRpb24ucmVhZC1sb2NhdGlvbnM6bXktb2ZmaWNlIiwidXNlci5jcmVhdGVbcm9sZT1GSUVMRF9BR0VOVHxQT0xJQ0VfT0ZGSUNFUnxTT0NJQUxfV09SS0VSfEhFQUxUSENBUkVfV09SS0VSfExPQ0FMX0xFQURFUnxSRUdJU1RSQVRJT05fQUdFTlR8TE9DQUxfUkVHSVNUUkFSXSIsInVzZXIuZWRpdFtyb2xlPUZJRUxEX0FHRU5UfFBPTElDRV9PRkZJQ0VSfFNPQ0lBTF9XT1JLRVJ8SEVBTFRIQ0FSRV9XT1JLRVJ8TE9DQUxfTEVBREVSfFJFR0lTVFJBVElPTl9BR0VOVHxMT0NBTF9SRUdJU1RSQVJdIl0sInVzZXJUeXBlIjoidXNlciIsInJvbGUiOiJMT0NBTF9TWVNURU1fQURNSU4iLCJpYXQiOjE0ODcwNzY3MDgsImF1ZCI6Im9wZW5jcnZzOmdhdGV3YXktdXNlciIsImlzcyI6Im9wZW5jcnZzOmF1dGgtc2VydmljZSIsInN1YiI6IjY4Y2JkMjZmYzY0NzYxNTY1NDY5NTkxZCJ9.O-wogl0fSIAJJ1wd1o8fqo-NfucnEKr1dKbYgSdhnCljZk-0K0bYIQOpz6TqKOinAxFhRcy2hRpf2MS7Q-mBlW43pF_Rzcm7piz3Q14V8ZKgeY7XEdvtcbPvf004PnSd7gGqLLwmYM7WX4X9ok1_d5AS2p70z7oYb0XsrrJ1cE_hv5teNWZyjnUn3JIN2o4BBZYSCQ0IlNwC5uCL1givHiHGq3j6rmses-4QAhCPHXPLjWSoa272OEzQdIqEgZj4sZcNeAxMgFfU0BxUR5huviaXP-e7iPk_zbogCKFHG6wmr__FUTGL0QR4sOBXy52Rf1bExdu44CNXmagSofwsvkUYD5QPfxTtp6nJ3VXNUrhz_Ga66fV_m8EMcd_Hpqytfb1lo_5kkhuQRwgfiBRXcXXCEDkeYWPLnZtsrnSHmIl-LHHKpNEiIGP4phAo9uwM6cGEGAt9ewFTyHWvJ22PETyBocIXiGh0kCjZKI9tpV8QcGDKt7y4b_CQHI0Zd-A7ST3xZUsztavF_zH77DnU-vweEBp-EhEQ1zRwtEr3neWXeKbZBQz0gqbeq6zGl87S1ZflPTMOdbkYd7NlR0NiGy4CCysBlLxeo9Dgv_4uXXJcFgJOvGD5RDhKo-CA4Fcq64D_YXjhWmuC-5-pmxf5TpAb323upY4NqWlZ3cxh_6Y', nationalSystemAdmin: @@ -367,6 +367,7 @@ export function testDataGenerator(rngSeed?: number) { 'record.declared.reject[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declared.archive[event=birth|death|tennis-club-membership|child-onboarding]', 'record.register[event=birth|death|tennis-club-membership|child-onboarding]', + 'record.declared.edit[event=birth|death|tennis-club-membership|child-onboarding]', 'record.registered.print-certified-copies[event=birth|death|tennis-club-membership|child-onboarding]', 'record.registered.correct[event=birth|death|tennis-club-membership|child-onboarding]', 'record.unassign-others[event=birth|death|tennis-club-membership|child-onboarding]', @@ -394,6 +395,7 @@ export function testDataGenerator(rngSeed?: number) { 'record.declare[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declared.validate[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declared.reject[event=birth|death|tennis-club-membership|child-onboarding]', + 'record.declared.edit[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declared.archive[event=birth|death|tennis-club-membership|child-onboarding]', 'record.registered.print-certified-copies[event=birth|death|tennis-club-membership|child-onboarding]', 'record.registered.request-correction[event=birth|death|tennis-club-membership|child-onboarding]' @@ -411,7 +413,8 @@ export function testDataGenerator(rngSeed?: number) { 'search[event=FOOTBALL_CLUB_MEMBERSHIP,access=all]', 'record.create[event=birth|death|tennis-club-membership|child-onboarding]', 'record.declare[event=birth|death|tennis-club-membership|child-onboarding]', - 'record.notify[event=birth|death|tennis-club-membership|child-onboarding]' + 'record.notify[event=birth|death|tennis-club-membership|child-onboarding]', + 'record.declared.edit[event=birth|death|tennis-club-membership|child-onboarding]' ], localSystemAdmin: [ SCOPES.USER_READ_MY_OFFICE, diff --git a/packages/client/src/v2-events/features/events/actions/declare/Review.stories.tsx b/packages/client/src/v2-events/features/events/actions/declare/Review.stories.tsx index 702e20c00c4..4b7d13c87eb 100644 --- a/packages/client/src/v2-events/features/events/actions/declare/Review.stories.tsx +++ b/packages/client/src/v2-events/features/events/actions/declare/Review.stories.tsx @@ -14,19 +14,14 @@ import { graphql, http, HttpResponse } from 'msw' import superjson from 'superjson' import { ActionType, - FullDocumentPath, generateEventDocument, generateEventDraftDocument, - tennisClubMembershipEvent, - UUID + tennisClubMembershipEvent } from '@opencrvs/commons/client' -import { AppRouter, trpcOptionsProxy } from '@client/v2-events/trpc' +import { AppRouter } from '@client/v2-events/trpc' import { ROUTES, routesConfig } from '@client/v2-events/routes' import { testDataGenerator } from '@client/tests/test-data-generators' -import { - tennisClubMembershipEventIndex, - TestImage -} from '@client/v2-events/features/events/fixtures' +import { TestImage } from '@client/v2-events/features/events/fixtures' import { ReviewIndex } from './Review' diff --git a/packages/client/src/v2-events/features/events/actions/edit/Review.stories.tsx b/packages/client/src/v2-events/features/events/actions/edit/Review.stories.tsx new file mode 100644 index 00000000000..6f2f39c7471 --- /dev/null +++ b/packages/client/src/v2-events/features/events/actions/edit/Review.stories.tsx @@ -0,0 +1,100 @@ +/* + * 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 { createTRPCMsw, httpLink } from '@vafanassieff/msw-trpc' +import { graphql, HttpResponse } from 'msw' +import superjson from 'superjson' +import { + ActionType, + generateEventDocument, + generateEventDraftDocument, + tennisClubMembershipEvent +} from '@opencrvs/commons/client' +import { AppRouter } from '@client/v2-events/trpc' +import { ROUTES, routesConfig } from '@client/v2-events/routes' +import { testDataGenerator } from '@client/tests/test-data-generators' +import { Review } from './index' + +const generator = testDataGenerator() + +const eventDocument = generateEventDocument({ + configuration: tennisClubMembershipEvent, + actions: [{ type: ActionType.CREATE }, { type: ActionType.DECLARE }] +}) + +const eventId = eventDocument.id + +const meta: Meta = { + title: 'Edit', + parameters: { offline: { events: [eventDocument] } } +} + +export default meta + +type Story = StoryObj +const tRPCMsw = createTRPCMsw({ + links: [ + httpLink({ + url: '/api/events' + }) + ], + transformer: { input: superjson, output: superjson } +}) + +const draft = generateEventDraftDocument({ + eventId, + actionType: ActionType.REGISTER +}) + +const mockUser = generator.user.fieldAgent().v2 + +export const ReviewForLocalRegistrarComplete: Story = { + parameters: { + reactRouter: { + router: routesConfig, + initialPath: ROUTES.V2.EVENTS.EDIT.REVIEW.buildPath({ + eventId + }) + }, + offline: { + drafts: [draft] + }, + msw: { + handlers: { + drafts: [ + tRPCMsw.event.draft.list.query(() => { + return [draft] + }) + ], + events: [ + tRPCMsw.event.config.get.query(() => { + return [tennisClubMembershipEvent] + }) + ], + user: [ + graphql.query('fetchUser', () => { + return HttpResponse.json({ + data: { + getUser: generator.user.localRegistrar().v1 + } + }) + }), + tRPCMsw.user.list.query(() => { + return [mockUser] + }), + tRPCMsw.user.get.query(() => { + return mockUser + }) + ] + } + } + } +} diff --git a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx index e412baaaa35..f5c78965ef8 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/Review.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/Review.tsx @@ -29,7 +29,6 @@ import { ROUTES } from '@client/v2-events/routes' import { Review as ReviewComponent } from '@client/v2-events/features/events/components/Review' import { FormLayout } from '@client/v2-events/layouts' import { makeFormFieldIdFormikCompatible } from '@client/v2-events/components/forms/utils' -import { withSuspense } from '@client/v2-events/components/withSuspense' import { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils' import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' import { EditActionMenu } from './EditActionMenu' @@ -43,7 +42,6 @@ export function Review() { const events = useEvents() const navigate = useNavigate() const validatorContext = useValidatorContext() - const [modal, openModal] = useModal() const { formatMessage } = useIntlFormatMessageWithFlattenedParams() const event = events.getEvent.getFromCache(eventId) const { eventConfiguration: config } = useEventConfiguration(event.type) @@ -63,33 +61,23 @@ export function Review() { const { setAnnotation, getAnnotation } = useActionAnnotation() const annotation = getAnnotation() - async function handleEdit({ + function handleEdit({ pageId, - fieldId, - confirmation + fieldId }: { pageId: string fieldId?: string - confirmation?: boolean }) { - const confirmedEdit = - confirmation || - (await openModal((close) => ( - - ))) - - if (confirmedEdit) { - navigate( - ROUTES.V2.EVENTS.EDIT.PAGES.buildPath( - { pageId, eventId }, - { - from: 'review', - workqueue: slug - }, - fieldId ? makeFormFieldIdFormikCompatible(fieldId) : undefined - ) + navigate( + ROUTES.V2.EVENTS.EDIT.PAGES.buildPath( + { pageId, eventId }, + { + from: 'review', + workqueue: slug + }, + fieldId ? makeFormFieldIdFormikCompatible(fieldId) : undefined ) - } + ) return } @@ -112,7 +100,6 @@ export function Review() { onAnnotationChange={(values) => setAnnotation(values)} onEdit={handleEdit} /> - {modal} ) diff --git a/packages/commons/src/events/Flag.ts b/packages/commons/src/events/Flag.ts index 8b526afa207..7907a0aaa7e 100644 --- a/packages/commons/src/events/Flag.ts +++ b/packages/commons/src/events/Flag.ts @@ -19,7 +19,8 @@ export const InherentFlags = { INCOMPLETE: 'incomplete', REJECTED: 'rejected', CORRECTION_REQUESTED: 'correction-requested', - POTENTIAL_DUPLICATE: 'potential-duplicate' + POTENTIAL_DUPLICATE: 'potential-duplicate', + EDIT_IN_PROGRESS: 'edit-in-progress' } as const export type InherentFlags = (typeof InherentFlags)[keyof typeof InherentFlags] diff --git a/packages/commons/src/events/state/availableActions.ts b/packages/commons/src/events/state/availableActions.ts index 9da3a195c3a..b59a88336a9 100644 --- a/packages/commons/src/events/state/availableActions.ts +++ b/packages/commons/src/events/state/availableActions.ts @@ -136,7 +136,10 @@ function getAvailableActionsWithoutFlagFilters( return AVAILABLE_ACTIONS_BY_EVENT_STATUS[status] } case EventStatus.enum.DECLARED: { - if (flags.includes(InherentFlags.REJECTED)) { + if ( + flags.includes(InherentFlags.REJECTED) || + flags.includes(InherentFlags.EDIT_IN_PROGRESS) + ) { return getAvailableActionsWithoutFlagFilters( EventStatus.enum.CREATED, flags.filter((flag) => flag !== InherentFlags.REJECTED) diff --git a/packages/commons/src/events/state/flags.ts b/packages/commons/src/events/state/flags.ts index a720a48afad..61c72064510 100644 --- a/packages/commons/src/events/state/flags.ts +++ b/packages/commons/src/events/state/flags.ts @@ -29,6 +29,10 @@ import { EventDocument } from '../EventDocument' import { JSONSchema } from '../../conditionals/conditionals' import { validate } from '../../conditionals/validate' +function isEditInProgress(actions: Action[]) { + return actions.at(-1)?.type === ActionType.EDIT +} + function isPendingCertification(actions: Action[]) { if (getStatusFromActions(actions) !== EventStatus.enum.REGISTERED) { return false @@ -127,11 +131,24 @@ export function resolveEventCustomFlags( event: EventDocument, eventConfiguration: EventConfig ): CustomFlag[] { - const acceptedActions = getAcceptedActions(event) - const actions = acceptedActions + const sortedActions = getAcceptedActions(event) .filter(({ type }) => !isMetaAction(type)) .sort((a, b) => a.createdAt.localeCompare(b.createdAt)) + // First find indices of all declare actions + const declareIndexes = sortedActions + .map((a, i) => (a.type === ActionType.DECLARE ? i : -1)) + .filter((i) => i !== -1) + + let actions = sortedActions + + // If there is more than one declare action, lets filter out all actions between the second last and last declare actions + if (declareIndexes.length >= 2) { + const secondLast = declareIndexes[declareIndexes.length - 2] + const last = declareIndexes[declareIndexes.length - 1] + actions = sortedActions.filter((_, idx) => idx < secondLast || idx >= last) + } + return actions.reduce((acc, action, idx) => { let actionConfig if (isActionConfigType(action.type)) { @@ -224,6 +241,9 @@ export function getEventFlags( if (isPotentialDuplicate(sortedActions)) { flags.push(InherentFlags.POTENTIAL_DUPLICATE) } + if (isEditInProgress(sortedActions)) { + flags.push(InherentFlags.EDIT_IN_PROGRESS) + } return [...flags, ...resolveEventCustomFlags(event, config)] } From f5cc2a4e06d5d57fb63becd1f6a0ecb6f5a2c903 Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 11:01:47 +0700 Subject: [PATCH 19/45] minor fixes --- packages/client/src/v2-events/custom-api/index.ts | 8 -------- packages/client/src/v2-events/messages/flags.ts | 5 +++++ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/client/src/v2-events/custom-api/index.ts b/packages/client/src/v2-events/custom-api/index.ts index 2b830cfc763..24ca1a4d79a 100644 --- a/packages/client/src/v2-events/custom-api/index.ts +++ b/packages/client/src/v2-events/custom-api/index.ts @@ -119,10 +119,6 @@ export async function editAndRegister({ keepAssignment: true }) - if (hasPotentialDuplicates(editedEvent, eventConfiguration)) { - return editedEvent - } - return trpcClient.event.actions.register.request.mutate({ declaration, annotation, @@ -146,10 +142,6 @@ export async function editAndDeclare({ keepAssignment: true }) - if (hasPotentialDuplicates(editedEvent, eventConfiguration)) { - return editedEvent - } - return trpcClient.event.actions.declare.request.mutate({ declaration, annotation, diff --git a/packages/client/src/v2-events/messages/flags.ts b/packages/client/src/v2-events/messages/flags.ts index 541471b4a6a..8e66e56eaee 100644 --- a/packages/client/src/v2-events/messages/flags.ts +++ b/packages/client/src/v2-events/messages/flags.ts @@ -43,6 +43,11 @@ const flagMessages = { id: 'flags.builtin.pending-certification.label', defaultMessage: 'Pending certification', description: 'Flag label for pending certification' + }, + [InherentFlags.EDIT_IN_PROGRESS]: { + id: 'flags.builtin.edit-in-progress.label', + defaultMessage: 'Edit in progress', + description: 'Flag label for edit in progress' } } satisfies Record From dae3a255aa828129d4697c748d90c08601fc3a9e Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 11:03:28 +0700 Subject: [PATCH 20/45] minor refactor --- packages/client/src/v2-events/custom-api/index.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/client/src/v2-events/custom-api/index.ts b/packages/client/src/v2-events/custom-api/index.ts index 24ca1a4d79a..cd7ebbc7711 100644 --- a/packages/client/src/v2-events/custom-api/index.ts +++ b/packages/client/src/v2-events/custom-api/index.ts @@ -106,12 +106,11 @@ export async function registerOnDeclare({ export async function editAndRegister({ eventId, - eventConfiguration, declaration, transactionId, annotation }: CustomMutationParams) { - const editedEvent = await trpcClient.event.actions.edit.request.mutate({ + await trpcClient.event.actions.edit.request.mutate({ declaration, annotation, eventId, @@ -129,12 +128,11 @@ export async function editAndRegister({ export async function editAndDeclare({ eventId, - eventConfiguration, declaration, transactionId, annotation }: CustomMutationParams) { - const editedEvent = await trpcClient.event.actions.edit.request.mutate({ + await trpcClient.event.actions.edit.request.mutate({ declaration, annotation, eventId, From 5323474ea6a5a156a4916a1c2b387e9d4991c9e9 Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 11:53:32 +0700 Subject: [PATCH 21/45] add edit audit details --- .../events/actions/edit/EditActionMenu.tsx | 1 - .../components/ActionTypeSpecificContent.tsx | 11 +++++ .../EventHistoryDialog/components/Edit.tsx | 44 +++++++++++++++++++ .../EventHistoryDialog/components/index.tsx | 1 + 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx index 2314d5265e9..11facb15971 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx @@ -72,7 +72,6 @@ const messages = { } } -// @TODO CIHAN: register/validate should be unavailable if declaration blocks it function useEditActions(event: EventDocument) { const eventType = event.type const { eventConfiguration } = useEventConfiguration(eventType) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/ActionTypeSpecificContent.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/ActionTypeSpecificContent.tsx index 51df080d1ed..7f9f4aef61a 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/ActionTypeSpecificContent.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/ActionTypeSpecificContent.tsx @@ -24,6 +24,7 @@ import { PrintCertificate } from './PrintCertificate' import { DeclarationUpdate } from './DeclarationUpdate' import { CustomActionContent } from './CustomAction' import { DetectedDuplicate } from './DetectedDuplicate' +import { Edit } from './Edit' const SyntheticDeclarationActionTypes = z.enum([DECLARATION_ACTION_UPDATE]) @@ -82,6 +83,16 @@ export function ActionTypeSpecificContent({ ) } + if (type === ActionType.EDIT) { + return ( + + ) + } + if (type === ActionType.DUPLICATE_DETECTED) { return } diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx new file mode 100644 index 00000000000..e126806a5ea --- /dev/null +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react' +import { EventDocument, ValidatorContext } from '@opencrvs/commons/client' +import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration' +import { withSuspense } from '@client/v2-events/components/withSuspense' +import { DeclarationComparisonTable } from '@client/v2-events/features/events/actions/correct/request/Summary/DeclarationComparisonTable' +import { EventHistoryActionDocument } from '@client/v2-events/features/events/actions/correct/useActionForHistory' + +/** + * + * Displays diff between declaration and edit of it. + */ +function EditComponent({ + action, + fullEvent, + validatorContext +}: { + action: EventHistoryActionDocument + fullEvent: EventDocument + validatorContext: ValidatorContext +}) { + const { eventConfiguration } = useEventConfiguration(fullEvent.type) + + return ( + + ) +} + +export const Edit = withSuspense(EditComponent) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx index 8d0f5386ede..b78fb21dcc5 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx @@ -12,4 +12,5 @@ export { DeclarationUpdate } from './DeclarationUpdate' export { RequestCorrection } from './RequestCorrection' export { PrintCertificate } from './PrintCertificate' +export { Edit } from './Edit' export { ActionTypeSpecificContent } from './ActionTypeSpecificContent' From 8c668430065dd706eaafb1cc89e26a2d09ce6c4f Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 12:10:53 +0700 Subject: [PATCH 22/45] fix allowed actions logic --- packages/commons/src/events/state/availableActions.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/commons/src/events/state/availableActions.ts b/packages/commons/src/events/state/availableActions.ts index b59a88336a9..3cffb2cea44 100644 --- a/packages/commons/src/events/state/availableActions.ts +++ b/packages/commons/src/events/state/availableActions.ts @@ -136,10 +136,7 @@ function getAvailableActionsWithoutFlagFilters( return AVAILABLE_ACTIONS_BY_EVENT_STATUS[status] } case EventStatus.enum.DECLARED: { - if ( - flags.includes(InherentFlags.REJECTED) || - flags.includes(InherentFlags.EDIT_IN_PROGRESS) - ) { + if (flags.includes(InherentFlags.REJECTED)) { return getAvailableActionsWithoutFlagFilters( EventStatus.enum.CREATED, flags.filter((flag) => flag !== InherentFlags.REJECTED) @@ -147,6 +144,11 @@ function getAvailableActionsWithoutFlagFilters( .filter((action) => action !== ActionType.DELETE) .concat(ActionType.ARCHIVE) } + + if (flags.includes(InherentFlags.EDIT_IN_PROGRESS)) { + return [ActionType.DECLARE, ActionType.REGISTER] + } + return AVAILABLE_ACTIONS_BY_EVENT_STATUS[status] } case EventStatus.enum.REGISTERED: { From f5e3efdfa48f4de7e26efebd8cd5bc71e0e6cb34 Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 12:20:23 +0700 Subject: [PATCH 23/45] minor fixes --- .../EventHistory/EventHistoryDialog/components/Edit.tsx | 5 +---- .../EventHistory/EventHistoryDialog/components/index.tsx | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx index e126806a5ea..0b8f1dec065 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx @@ -15,10 +15,7 @@ import { withSuspense } from '@client/v2-events/components/withSuspense' import { DeclarationComparisonTable } from '@client/v2-events/features/events/actions/correct/request/Summary/DeclarationComparisonTable' import { EventHistoryActionDocument } from '@client/v2-events/features/events/actions/correct/useActionForHistory' -/** - * - * Displays diff between declaration and edit of it. - */ +/** Displays diff between declaration and edit of it */ function EditComponent({ action, fullEvent, diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx index b78fb21dcc5..8d0f5386ede 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/index.tsx @@ -12,5 +12,4 @@ export { DeclarationUpdate } from './DeclarationUpdate' export { RequestCorrection } from './RequestCorrection' export { PrintCertificate } from './PrintCertificate' -export { Edit } from './Edit' export { ActionTypeSpecificContent } from './ActionTypeSpecificContent' From a517d3fe2745908d209258de4a79272e31e9aa46 Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 12:59:36 +0700 Subject: [PATCH 24/45] fix tests --- .../ActionMenuStories/ActionMenu.common.tsx | 6 +++++ .../Declared.stories.tsx | 26 ++++++++++++++++--- .../Validated.stories.tsx | 10 ++++--- .../Declared.stories.tsx | 9 ++++--- .../Validated.stories.tsx | 9 ++++--- .../Declared.stories.tsx | 9 ++++--- .../Validated.stories.tsx | 9 ++++--- 7 files changed, 60 insertions(+), 18 deletions(-) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/ActionMenu.common.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/ActionMenu.common.tsx index c1c221b3ddc..d3ab48a70c0 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/ActionMenu.common.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/ActionMenu.common.tsx @@ -199,6 +199,12 @@ function getMockActions(createdBy: string) { createdBy, id: generateUuid(rng), type: ActionType.CUSTOM + }, + [ActionType.EDIT]: { + ...actionProps, + createdBy, + id: generateUuid(rng), + type: ActionType.EDIT } } } diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Declared.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Declared.stories.tsx index 086329a4a5b..524aeefa118 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Declared.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Declared.stories.tsx @@ -17,7 +17,8 @@ import { getHiddenActions, createStoriesFromScenarios, Scenario, - UserRoles + UserRoles, + AssertType } from '../ActionMenu.common' export default { @@ -36,7 +37,9 @@ const declaredScenariosForFieldAgent: Scenario[] = [ ActionType.UNASSIGN ], expected: { - ...getHiddenActions() + ...getHiddenActions(), + ['Assign']: AssertType.ENABLED, + ['Edit']: AssertType.DISABLED } }, { @@ -50,7 +53,24 @@ const declaredScenariosForFieldAgent: Scenario[] = [ AssignmentStatus.ASSIGNED_TO_OTHERS ], expected: { - ...getHiddenActions() + ...getHiddenActions(), + ['Edit']: AssertType.DISABLED + } + }, + { + name: 'AssignedToSelf', + recordDownloaded: true, + actions: [ + ActionType.CREATE, + AssignmentStatus.ASSIGNED_TO_SELF, + ActionType.DECLARE, + ActionType.UNASSIGN, + AssignmentStatus.ASSIGNED_TO_SELF + ], + expected: { + ...getHiddenActions(), + ['Edit']: AssertType.ENABLED, + ['Unassign']: AssertType.ENABLED } } ] diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Validated.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Validated.stories.tsx index 9380ff61469..766e85647fe 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Validated.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/FieldAgentInteraction/Validated.stories.tsx @@ -17,7 +17,8 @@ import { getHiddenActions, createStoriesFromScenarios, Scenario, - UserRoles + UserRoles, + AssertType } from '../ActionMenu.common' export default { @@ -37,7 +38,9 @@ const validatedScenariosForFieldAgent: Scenario[] = [ ActionType.UNASSIGN ], expected: { - ...getHiddenActions() + ...getHiddenActions(), + ['Assign']: AssertType.ENABLED, + ['Edit']: AssertType.DISABLED } }, { @@ -52,7 +55,8 @@ const validatedScenariosForFieldAgent: Scenario[] = [ AssignmentStatus.ASSIGNED_TO_OTHERS ], expected: { - ...getHiddenActions() + ...getHiddenActions(), + ['Edit']: AssertType.DISABLED } } ] diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Declared.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Declared.stories.tsx index 1262aad1b04..9e556791c1a 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Declared.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Declared.stories.tsx @@ -41,7 +41,8 @@ const declaredScenariosForLocalRegistrar: Scenario[] = [ ['Assign']: AssertType.ENABLED, ['Register']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } }, { @@ -59,7 +60,8 @@ const declaredScenariosForLocalRegistrar: Scenario[] = [ ['Unassign']: AssertType.ENABLED, ['Register']: AssertType.ENABLED, ['Archive']: AssertType.ENABLED, - ['Reject']: AssertType.ENABLED + ['Reject']: AssertType.ENABLED, + ['Edit']: AssertType.ENABLED } }, { @@ -77,7 +79,8 @@ const declaredScenariosForLocalRegistrar: Scenario[] = [ ['Unassign']: AssertType.ENABLED, ['Register']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } } ] diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Validated.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Validated.stories.tsx index 478f3b11335..8040dc4a98a 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Validated.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/Validated.stories.tsx @@ -42,7 +42,8 @@ const validatedScenariosForLocalRegistrar: Scenario[] = [ ['Assign']: AssertType.ENABLED, ['Register']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } }, { @@ -61,7 +62,8 @@ const validatedScenariosForLocalRegistrar: Scenario[] = [ ['Unassign']: AssertType.ENABLED, ['Register']: AssertType.ENABLED, ['Archive']: AssertType.ENABLED, - ['Reject']: AssertType.ENABLED + ['Reject']: AssertType.ENABLED, + ['Edit']: AssertType.ENABLED } }, { @@ -80,7 +82,8 @@ const validatedScenariosForLocalRegistrar: Scenario[] = [ ['Unassign']: AssertType.ENABLED, ['Register']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } } ] diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Declared.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Declared.stories.tsx index 55e9f86824c..c36a733097e 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Declared.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Declared.stories.tsx @@ -41,7 +41,8 @@ const declaredScenariosForRegistrationAgent: Scenario[] = [ ['Assign']: AssertType.ENABLED, ['Validate']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } }, { @@ -59,7 +60,8 @@ const declaredScenariosForRegistrationAgent: Scenario[] = [ ['Unassign']: AssertType.ENABLED, ['Validate']: AssertType.ENABLED, ['Archive']: AssertType.ENABLED, - ['Reject']: AssertType.ENABLED + ['Reject']: AssertType.ENABLED, + ['Edit']: AssertType.ENABLED } }, { @@ -76,7 +78,8 @@ const declaredScenariosForRegistrationAgent: Scenario[] = [ ...getHiddenActions(), ['Validate']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } } ] diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Validated.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Validated.stories.tsx index ef54f0cdf4e..0fafcb3d0d9 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Validated.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/RegistrationAgentInteraction/Validated.stories.tsx @@ -41,7 +41,8 @@ const validatedScenariosForRegistrationAgent: Scenario[] = [ ...getHiddenActions(), ['Assign']: AssertType.ENABLED, ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } }, { @@ -58,7 +59,8 @@ const validatedScenariosForRegistrationAgent: Scenario[] = [ expected: { ...getHiddenActions(), ['Archive']: AssertType.DISABLED, - ['Reject']: AssertType.DISABLED + ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED } }, { @@ -74,7 +76,8 @@ const validatedScenariosForRegistrationAgent: Scenario[] = [ ...getHiddenActions(), ['Unassign']: AssertType.ENABLED, ['Archive']: AssertType.ENABLED, - ['Reject']: AssertType.ENABLED + ['Reject']: AssertType.ENABLED, + ['Edit']: AssertType.ENABLED } } ] From 52b1aa50af163c5d38e337a78464127c8d8073c5 Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 13:12:21 +0700 Subject: [PATCH 25/45] fix test --- .../LocalRegistrarInteraction/CustomAction.stories.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/CustomAction.stories.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/CustomAction.stories.tsx index 4a71bf5174d..c5d42b86bb3 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/CustomAction.stories.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/ActionMenuStories/LocalRegistrarInteraction/CustomAction.stories.tsx @@ -44,6 +44,7 @@ const customActionScenariosForLocalRegistrar: Scenario[] = [ ['Register']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED, ['Confirm']: AssertType.DISABLED } }, @@ -65,6 +66,7 @@ const customActionScenariosForLocalRegistrar: Scenario[] = [ ['Register']: AssertType.ENABLED, ['Archive']: AssertType.ENABLED, ['Reject']: AssertType.ENABLED, + ['Edit']: AssertType.ENABLED, ['Confirm']: AssertType.ENABLED } }, @@ -86,6 +88,7 @@ const customActionScenariosForLocalRegistrar: Scenario[] = [ ['Register']: AssertType.DISABLED, ['Archive']: AssertType.DISABLED, ['Reject']: AssertType.DISABLED, + ['Edit']: AssertType.DISABLED, ['Confirm']: AssertType.DISABLED } } From db5531ed5da1603815004c1ecd365e7f2fb6a9ef Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 14:13:18 +0700 Subject: [PATCH 26/45] progress with reason input --- .../client/src/v2-events/custom-api/index.ts | 6 ++- .../Summary/DeclarationComparisonTable.tsx | 1 - .../EventHistoryDialog/components/Edit.tsx | 51 +++++++++++++++---- packages/commons/src/events/ActionDocument.ts | 6 ++- packages/commons/src/events/ActionInput.ts | 3 +- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/packages/client/src/v2-events/custom-api/index.ts b/packages/client/src/v2-events/custom-api/index.ts index cd7ebbc7711..10bc671a351 100644 --- a/packages/client/src/v2-events/custom-api/index.ts +++ b/packages/client/src/v2-events/custom-api/index.ts @@ -115,7 +115,8 @@ export async function editAndRegister({ annotation, eventId, transactionId, - keepAssignment: true + keepAssignment: true, + content: { reason: 'TODO REASSON HERE' } }) return trpcClient.event.actions.register.request.mutate({ @@ -137,7 +138,8 @@ export async function editAndDeclare({ annotation, eventId, transactionId, - keepAssignment: true + keepAssignment: true, + content: { reason: 'TODO REASSON HERE' } }) return trpcClient.event.actions.declare.request.mutate({ diff --git a/packages/client/src/v2-events/features/events/actions/correct/request/Summary/DeclarationComparisonTable.tsx b/packages/client/src/v2-events/features/events/actions/correct/request/Summary/DeclarationComparisonTable.tsx index 77092afb813..76f6b42c3be 100644 --- a/packages/client/src/v2-events/features/events/actions/correct/request/Summary/DeclarationComparisonTable.tsx +++ b/packages/client/src/v2-events/features/events/actions/correct/request/Summary/DeclarationComparisonTable.tsx @@ -16,7 +16,6 @@ import { EventConfig, EventDocument, EventState, - getAcceptedActions, getDeclaration, isFieldDisplayedOnReview, ValidatorContext diff --git a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx index 0b8f1dec065..81382a980d9 100644 --- a/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx +++ b/packages/client/src/v2-events/features/workqueues/EventOverview/components/EventHistory/EventHistoryDialog/components/Edit.tsx @@ -9,32 +9,61 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import React from 'react' -import { EventDocument, ValidatorContext } from '@opencrvs/commons/client' +import { useIntl } from 'react-intl' +import styled from 'styled-components' +import { + EditAction, + EventDocument, + ValidatorContext +} from '@opencrvs/commons/client' +import { Text } from '@opencrvs/components/lib/Text' import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration' import { withSuspense } from '@client/v2-events/components/withSuspense' import { DeclarationComparisonTable } from '@client/v2-events/features/events/actions/correct/request/Summary/DeclarationComparisonTable' -import { EventHistoryActionDocument } from '@client/v2-events/features/events/actions/correct/useActionForHistory' -/** Displays diff between declaration and edit of it */ +const ReasonParagraph = styled(Text)` + padding: 16px 0; +` + +const reasonLabel = { + id: 'event.edit.reason.label', + defaultMessage: 'Reason of edit', + description: 'The label for the reason of edit' +} + +/** Displays reason for edit and made edits */ function EditComponent({ action, fullEvent, validatorContext }: { - action: EventHistoryActionDocument + action: EditAction fullEvent: EventDocument validatorContext: ValidatorContext }) { const { eventConfiguration } = useEventConfiguration(fullEvent.type) + const reason = action.content?.reason + const intl = useIntl() return ( - + <> + {reason && ( + + + {intl.formatMessage(reasonLabel)} + {':'} + {' '} + {reason} + + )} + + ) } diff --git a/packages/commons/src/events/ActionDocument.ts b/packages/commons/src/events/ActionDocument.ts index aa6fb1be836..5e87555890d 100644 --- a/packages/commons/src/events/ActionDocument.ts +++ b/packages/commons/src/events/ActionDocument.ts @@ -137,7 +137,7 @@ export const ReasonContent = z.object({ .string() .min(1, { error: 'Message cannot be empty' }) .describe( - 'Message describing the reason for rejecting or archiving the event.' + 'Message describing the reason for rejecting, archiving or editing the event.' ) }) @@ -202,9 +202,11 @@ const NotifiedAction = ActionBase.extend( const EditAction = ActionBase.extend( z.object({ - type: z.literal(ActionType.EDIT) + type: z.literal(ActionType.EDIT), + content: ReasonContent.optional() }).shape ) +export type EditAction = z.infer export const PrintContent = z.object({ templateId: z.string().optional() diff --git a/packages/commons/src/events/ActionInput.ts b/packages/commons/src/events/ActionInput.ts index f22d4be404d..49281cfa8d2 100644 --- a/packages/commons/src/events/ActionInput.ts +++ b/packages/commons/src/events/ActionInput.ts @@ -82,7 +82,8 @@ export const DeclareActionInput = BaseActionInput.extend( export const EditActionInput = BaseActionInput.extend( z.object({ - type: z.literal(ActionType.EDIT).default(ActionType.EDIT) + type: z.literal(ActionType.EDIT).default(ActionType.EDIT), + content: ReasonContent.optional() }).shape ) From 4821380c29578700500bd761ec2e2b4087edaa2c Mon Sep 17 00:00:00 2001 From: cibelius Date: Thu, 11 Dec 2025 22:35:28 +0700 Subject: [PATCH 27/45] progress with comments field --- .../client/src/v2-events/custom-api/index.ts | 21 ++- .../events/actions/edit/EditActionMenu.tsx | 154 +++++++++++++----- .../useEvents/procedures/actions/action.ts | 4 +- .../ActionMenuStories/ActionMenu.common.tsx | 3 +- .../EventHistoryDialog/components/Edit.tsx | 15 +- packages/commons/src/events/ActionDocument.ts | 6 +- packages/commons/src/events/ActionInput.ts | 4 +- 7 files changed, 143 insertions(+), 64 deletions(-) diff --git a/packages/client/src/v2-events/custom-api/index.ts b/packages/client/src/v2-events/custom-api/index.ts index 10bc671a351..bd4c3621086 100644 --- a/packages/client/src/v2-events/custom-api/index.ts +++ b/packages/client/src/v2-events/custom-api/index.ts @@ -20,7 +20,8 @@ import { ArchiveActionInput, MarkAsDuplicateActionInput, ActionStatus, - ValidatorContext + ValidatorContext, + EditActionInput } from '@opencrvs/commons/client' import { trpcClient } from '@client/v2-events/trpc' @@ -34,6 +35,10 @@ export interface CustomMutationParams { annotation?: EventState } +export interface EditRequestParams extends CustomMutationParams { + content: EditActionInput['content'] +} + export interface CorrectionRequestParams extends CustomMutationParams { event: EventDocument context: ValidatorContext @@ -108,15 +113,16 @@ export async function editAndRegister({ eventId, declaration, transactionId, - annotation -}: CustomMutationParams) { + annotation, + content +}: EditRequestParams) { await trpcClient.event.actions.edit.request.mutate({ declaration, annotation, eventId, transactionId, keepAssignment: true, - content: { reason: 'TODO REASSON HERE' } + content }) return trpcClient.event.actions.register.request.mutate({ @@ -131,15 +137,16 @@ export async function editAndDeclare({ eventId, declaration, transactionId, - annotation -}: CustomMutationParams) { + annotation, + content +}: EditRequestParams) { await trpcClient.event.actions.edit.request.mutate({ declaration, annotation, eventId, transactionId, keepAssignment: true, - content: { reason: 'TODO REASSON HERE' } + content }) return trpcClient.event.actions.declare.request.mutate({ diff --git a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx index 11facb15971..276775a98ed 100644 --- a/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx +++ b/packages/client/src/v2-events/features/events/actions/edit/EditActionMenu.tsx @@ -8,9 +8,10 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import React from 'react' -import { useIntl } from 'react-intl' +import React, { useState } from 'react' +import { useIntl, MessageDescriptor } from 'react-intl' import { useTypedSearchParams } from 'react-router-typesafe-routes/dom' +import styled from 'styled-components' import { ActionType, EventDocument, @@ -22,13 +23,19 @@ import { import { PrimaryButton } from '@opencrvs/components/lib/buttons' import { DropdownMenu } from '@opencrvs/components/lib/Dropdown' import { CaretDown } from '@opencrvs/components/lib/Icon/all-icons' -import { Icon } from '@opencrvs/components' +import { + Icon, + ResponsiveModal, + Button, + Stack, + Text, + TextArea +} from '@opencrvs/components' import { useEventFormNavigation } from '@client/v2-events/features/events/useEventFormNavigation' import { messages as actionMessages } from '@client/i18n/messages/views/action' import { ROUTES } from '@client/v2-events/routes' import { useModal } from '@client/v2-events/hooks/useModal' import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents' -import { Review } from '@client/v2-events/features/events/components/Review' import { useUserAllowedActions } from '@client/v2-events/features/workqueues/EventOverview/components/useAllowedActionConfigurations' import { useValidatorContext } from '@client/v2-events/hooks/useValidatorContext' import { validationErrorsInActionFormExist } from '@client/v2-events/components/forms/validation' @@ -37,6 +44,12 @@ import { useActionAnnotation } from '../../useActionAnnotation' import { useEventFormData } from '../../useEventFormData' import { hasDeclarationFieldChanged } from '../correct/utils' +export const commentLabel = { + id: 'event.edit.comment.label', + defaultMessage: 'Comments', + description: 'The label for the comment' +} + const messages = { cancel: { id: 'actionModal.cancel', @@ -68,10 +81,75 @@ const messages = { id: 'event.edit.declareWithEdits.description', description: 'Description for "Declare with edits" in edit action menu', defaultMessage: - 'Are you sure you want to edit this declaration? By confirming you are redeclaring this event and overide past changes...' + 'Are you sure you want to edit this declaration? By confirming you are redeclaring this event and override past changes...' } } +interface EditActionModalResult { + confirmed: boolean + comment?: string +} + +const CommentLabel = styled(Text)` + padding: 16px 0 4px 0; +` + +function EditActionModal({ + title, + description, + close +}: { + title: MessageDescriptor + description: MessageDescriptor + close: (result: EditActionModalResult) => void +}) { + const intl = useIntl() + const [comment, setComment] = useState('') + + return ( + close({ confirmed: false })} + > + {intl.formatMessage(messages.cancel)} + , + + ]} + handleClose={() => close({ confirmed: false })} + title={intl.formatMessage(title)} + width={800} + > + + + {intl.formatMessage(description)} + + + + {intl.formatMessage(commentLabel)} + +