Repository: opencrvs/opencrvs-core
Pull request #9090: view only mode added for declarations
Author: login: tareq89
Created: 2025-03-26T13:38:23Z | Merged: 2025-03-26T17:52:14Z

https://github.com/opencrvs/opencrvs-core/issues/8979


Diff:
diff --git a/packages/client/src/v2-events/components/forms/FormFieldGenerator.tsx b/packages/client/src/v2-events/components/forms/FormFieldGenerator.tsx
index ee5189af8b9..be4ca1a5392 100644
--- a/packages/client/src/v2-events/components/forms/FormFieldGenerator.tsx
+++ b/packages/client/src/v2-events/components/forms/FormFieldGenerator.tsx
@@ -143,6 +143,7 @@ interface GeneratedInputFieldProps<T extends FieldConfig> {
   requiredErrorMessage?: MessageDescriptor
   eventConfig?: EventConfig
   event?: EventIndex
+  readonlyMode?: boolean
 }
 
 const GeneratedInputField = React.memo(
@@ -156,7 +157,8 @@ const GeneratedInputField = React.memo(
     value,
     formData,
     disabled,
-    eventConfig
+    eventConfig,
+    readonlyMode
   }: GeneratedInputFieldProps<T>) => {
     const intl = useIntl()
 
@@ -168,7 +170,7 @@ const GeneratedInputField = React.memo(
           ? undefined
           : intl.formatMessage(fieldDefinition.label),
       required: fieldDefinition.required,
-      disabled: fieldDefinition.disabled,
+      disabled: fieldDefinition.disabled || readonlyMode,
       error,
       touched
     }
@@ -179,7 +181,7 @@ const GeneratedInputField = React.memo(
       onChange,
       onBlur,
       value,
-      disabled: fieldDefinition.disabled ?? disabled,
+      disabled: disabled || fieldDefinition.disabled || readonlyMode,
       error: Boolean(error),
       touched: Boolean(touched),
       placeholder:
@@ -299,7 +301,6 @@ const GeneratedInputField = React.memo(
         >
           <Number.Input
             {...inputProps}
-            disabled={disabled}
             value={field.value}
             onChange={(val) => setFieldValue(fieldDefinition.id, val)}
             min={field.config.configuration?.min}
@@ -324,7 +325,6 @@ const GeneratedInputField = React.memo(
         >
           <TextArea
             {...inputProps}
-            disabled={disabled}
             maxLength={field.config.configuration?.maxLength}
             value={field.value}
           />
@@ -411,7 +411,9 @@ const GeneratedInputField = React.memo(
     }
 
     if (isSignatureFieldType(field)) {
-      return (
+      return readonlyMode ? (
+        <></>
+      ) : (
         <InputField {...inputFieldProps}>
           <SignatureUploader
             name={fieldDefinition.id}
@@ -547,6 +549,7 @@ interface ExposedProps {
   initialValues?: EventState
   eventConfig?: EventConfig
   eventDeclarationData?: EventState
+  readonlyMode?: boolean
 }
 
 type AllProps = ExposedProps &
@@ -669,7 +672,8 @@ class FormSectionComponent extends React.Component<AllProps> {
       touched,
       intl,
       className,
-      eventDeclarationData
+      eventDeclarationData,
+      readonlyMode
     } = this.props
 
     const language = this.props.intl.locale
@@ -732,6 +736,7 @@ class FormSectionComponent extends React.Component<AllProps> {
                         this.props.onUploadingStateChanged
                       }
                       eventConfig={this.props.eventConfig}
+                      readonlyMode={readonlyMode}
                     />
                   )
                 }}
diff --git a/packages/client/src/v2-events/features/events/ReadOnlyView.stories.tsx b/packages/client/src/v2-events/features/events/ReadOnlyView.stories.tsx
new file mode 100644
index 00000000000..6e7e4831e3f
--- /dev/null
+++ b/packages/client/src/v2-events/features/events/ReadOnlyView.stories.tsx
@@ -0,0 +1,97 @@
+/*
+ * 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'
+// eslint-disable-next-line
+import { testDataGenerator } from '@client/tests/test-data-generators'
+import { tennisClubMembershipEventIndex } from '@client/v2-events/features/events/fixtures'
+import { ReadOnlyView } from './ReadOnlyView'
+
+const generator = testDataGenerator()
+
+const meta: Meta<typeof ReadOnlyView> = {
+  title: 'ReadOnlyView'
+}
+
+export default meta
+
+type Story = StoryObj<typeof ReadOnlyView>
+const tRPCMsw = createTRPCMsw<AppRouter>({
+  links: [
+    httpLink({
+      url: '/api/events'
+    })
+  ],
+  transformer: { input: superjson, output: superjson }
+})
+
+const eventDocument = generateEventDocument({
+  configuration: tennisClubMembershipEvent,
+  actions: [
+    ActionType.CREATE,
+    ActionType.DECLARE,
+    ActionType.VALIDATE,
+    ActionType.REGISTER
+  ]
+})
+
+const eventId = eventDocument.id
+const draft = generateEventDraftDocument(eventId)
+
+export const ReadOnlyViewForUserWithReadPermission: Story = {
+  parameters: {
+    reactRouter: {
+      router: routesConfig,
+      initialPath: ROUTES.V2.EVENTS.VIEW.buildPath({
+        eventId
+      })
+    },
+    msw: {
+      handlers: {
+        drafts: [
+          tRPCMsw.event.draft.list.query(() => {
+            return [draft]
+          })
+        ],
+        events: [
+          tRPCMsw.event.config.get.query(() => {
+            return [tennisClubMembershipEvent]
+          }),
+          tRPCMsw.event.get.query(() => {
+            return eventDocument
+          }),
+          tRPCMsw.event.list.query(() => {
+            return [tennisClubMembershipEventIndex]
+          })
+        ],
+        user: [
+          graphql.query('fetchUser', () => {
+            return HttpResponse.json({
+              data: {
+                getUser: generator.user.localRegistrar()
+              }
+            })
+          })
+        ]
+      }
+    }
+  }
+}
diff --git a/packages/client/src/v2-events/features/events/ReadOnlyView.tsx b/packages/client/src/v2-events/features/events/ReadOnlyView.tsx
new file mode 100644
index 00000000000..317e9efc683
--- /dev/null
+++ b/packages/client/src/v2-events/features/events/ReadOnlyView.tsx
@@ -0,0 +1,102 @@
+/*
+ * 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 { useTypedParams } from 'react-router-typesafe-routes/dom'
+import { noop } from 'lodash'
+import {
+  ActionType,
+  EventConfig,
+  EventDocument,
+  findActiveActionForm,
+  getMetadataForAction
+} from '@opencrvs/commons/client'
+import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration'
+import { useEventFormData } from '@client/v2-events/features/events/useEventFormData'
+import { useEventMetadata } from '@client/v2-events/features/events/useEventMeta'
+import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents'
+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 { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils'
+
+// These are the allowed actions based on which we can read a declarations data
+const READ_ONLY_MODE_ALLOWED_ACTIONS = [
+  ActionType.APPROVE_CORRECTION,
+  ActionType.REGISTER,
+  ActionType.VALIDATE,
+  ActionType.DECLARE
+]
+
+function findLastActionFormConfigForReadOnlyMode(
+  config: EventConfig,
+  event: EventDocument
+) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return findActiveActionForm(config, actionType)
+    }
+  }
+}
+
+function findLastActionMetadata(event: EventDocument) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return getMetadataForAction({ event, actionType, drafts: [] })
+    }
+  }
+}
+
+export function ReadOnlyView() {
+  const { eventId } = useTypedParams(ROUTES.V2.EVENTS.VIEW)
+  const { getEventState, getEvent } = useEvents()
+  const { formatMessage } = useIntlFormatMessageWithFlattenedParams()
+  const currentEventState = getEventState.useSuspenseQuery(eventId)
+  const { eventConfiguration: config } = useEventConfiguration(
+    currentEventState.type
+  )
+  const [fullEvent] = getEvent.useSuspenseQuery(eventId)
+  const formConfig = findLastActionFormConfigForReadOnlyMode(config, fullEvent)
+
+  if (!formConfig) {
+    throw new Error('No active form configuration found for any action')
+  }
+
+  const form = useEventFormData((state) =>
+    state.getFormValues(currentEventState.data)
+  )
+
+  const { setMetadata, getMetadata } = useEventMetadata()
+  const metadata = getMetadata(findLastActionMetadata(fullEvent))
+
+  return (
+    <FormLayout route={ROUTES.V2.EVENTS.DECLARE}>
+      <ReviewComponent.Body
+        readonlyMode
+        eventConfig={config}
+        form={form}
+        formConfig={formConfig}
+        metadata={metadata}
+        title={formatMessage(formConfig.review.title, form)}
+        onEdit={noop}
+        onMetadataChange={(values) => setMetadata(values)}
+      >
+        <></>
+      </ReviewComponent.Body>
+    </FormLayout>
+  )
+}
diff --git a/packages/client/src/v2-events/features/events/components/DocumentViewer.tsx b/packages/client/src/v2-events/features/events/components/DocumentViewer.tsx
index 645abc758e5..4f85282e844 100644
--- a/packages/client/src/v2-events/features/events/components/DocumentViewer.tsx
+++ b/packages/client/src/v2-events/features/events/components/DocumentViewer.tsx
@@ -179,12 +179,14 @@ export function DocumentViewer({
   form,
   formConfig,
   onEdit,
-  showInMobile
+  showInMobile,
+  disabled
 }: {
   formConfig: FormConfig
   form: EventState
   onEdit: () => void
   showInMobile?: boolean
+  disabled?: boolean
 }) {
   const intl = useIntl()
   const fileOptions = getFileOptions(form, formConfig, intl)
@@ -195,15 +197,17 @@ export function DocumentViewer({
         {fileOptions.documentOptions.length === 0 && (
           <ZeroDocument id={`zero_document`}>
             {intl.formatMessage(reviewMessages.zeroDocumentsTextForAnySection)}
-            <Link
-              id="edit-document"
-              onClick={(e) => {
-                e.stopPropagation()
-                onEdit()
-              }}
-            >
-              {intl.formatMessage(reviewMessages.editDocuments)}
-            </Link>
+            {!disabled && (
+              <Link
+                id="edit-document"
+                onClick={(e) => {
+                  e.stopPropagation()
+                  onEdit()
+                }}
+              >
+                {intl.formatMessage(reviewMessages.editDocuments)}
+              </Link>
+            )}
           </ZeroDocument>
         )}
       </DocumentViewerComponent>
diff --git a/packages/client/src/v2-events/features/events/components/Review.tsx b/packages/client/src/v2-events/features/events/components/Review.tsx
index c836318340c..2c8c11cd032 100644
--- a/packages/client/src/v2-events/features/events/components/Review.tsx
+++ b/packages/client/src/v2-events/features/events/components/Review.tsx
@@ -277,13 +277,15 @@ function FormReview({
   form,
   previousForm,
   onEdit,
-  showPreviouslyMissingValuesAsChanged
+  showPreviouslyMissingValuesAsChanged,
+  readonlyMode
 }: {
   formConfig: FormConfig
   form: EventState
   previousForm: EventState
   onEdit: ({ pageId, fieldId }: { pageId: string; fieldId?: string }) => void
   showPreviouslyMissingValuesAsChanged: boolean
+  readonlyMode?: boolean
 }) {
   const intl = useIntl()
   const visiblePages = formConfig.pages.filter((page) =>
@@ -346,14 +348,16 @@ function FormReview({
             >
               <Accordion
                 action={
-                  <Link
-                    onClick={(e) => {
-                      e.stopPropagation()
-                      onEdit({ pageId: page.id })
-                    }}
-                  >
-                    {intl.formatMessage(reviewMessages.changeAllButton)}
-                  </Link>
+                  !readonlyMode && (
+                    <Link
+                      onClick={(e) => {
+                        e.stopPropagation()
+                        onEdit({ pageId: page.id })
+                      }}
+                    >
+                      {intl.formatMessage(reviewMessages.changeAllButton)}
+                    </Link>
+                  )
                 }
                 expand={true}
                 label={intl.formatMessage(page.title)}
@@ -371,19 +375,21 @@ function FormReview({
                       <ListReview.Row
                         key={id}
                         actions={
-                          <Link
-                            data-testid={`change-button-${id}`}
-                            onClick={(e) => {
-                              e.stopPropagation()
-
-                              onEdit({
-                                pageId: page.id,
-                                fieldId: id
-                              })
-                            }}
-                          >
-                            {intl.formatMessage(reviewMessages.changeButton)}
-                          </Link>
+                          !readonlyMode && (
+                            <Link
+                              data-testid={`change-button-${id}`}
+                              onClick={(e) => {
+                                e.stopPropagation()
+
+                                onEdit({
+                                  pageId: page.id,
+                                  fieldId: id
+                                })
+                              }}
+                            >
+                              {intl.formatMessage(reviewMessages.changeButton)}
+                            </Link>
+                          )
                         }
                         id={id}
                         label={intl.formatMessage(label)}
@@ -412,7 +418,8 @@ function ReviewComponent({
   onEdit,
   children,
   title,
-  onMetadataChange
+  onMetadataChange,
+  readonlyMode
 }: {
   children: React.ReactNode
   eventConfig: EventConfig
@@ -431,6 +438,7 @@ function ReviewComponent({
   }) => void
   title: string
   onMetadataChange?: (values: EventState) => void
+  readonlyMode?: boolean
 }) {
   const scopes = useSelector(getScope)
   const showPreviouslyMissingValuesAsChanged = previousFormValues !== undefined
@@ -457,6 +465,7 @@ function ReviewComponent({
             form={form}
             formConfig={formConfig}
             previousForm={previousForm}
+            readonlyMode={readonlyMode}
             showPreviouslyMissingValuesAsChanged={
               showPreviouslyMissingValuesAsChanged
             }
@@ -471,6 +480,7 @@ function ReviewComponent({
                   formData={metadata}
                   id={'review'}
                   initialValues={metadata}
+                  readonlyMode={readonlyMode}
                   setAllFieldsDirty={false}
                   onChange={onMetadataChange}
                 />
@@ -483,6 +493,7 @@ function ReviewComponent({
       {pageIdsWithFile.length > 0 && (
         <RightColumn>
           <DocumentViewer
+            disabled={readonlyMode}
             form={form}
             formConfig={formConfig}
             // @todo: ask about this rule
diff --git a/packages/client/src/v2-events/routes/config.tsx b/packages/client/src/v2-events/routes/config.tsx
index 5cbdbad7aa9..58ab6192a92 100644
--- a/packages/client/src/v2-events/routes/config.tsx
+++ b/packages/client/src/v2-events/routes/config.tsx
@@ -30,6 +30,7 @@ import { TRPCProvider } from '@client/v2-events/trpc'
 import { SearchResultIndex } from '@client/v2-events/features/events/AdvancedSearch/SearchResultIndex'
 import { Action } from '@client/v2-events/features/events/components/Action'
 import { NavigationHistoryProvider } from '@client/v2-events/components/NavigationStack'
+import { ReadOnlyView } from '@client/v2-events/features/events/ReadOnlyView'
 import { ROUTES } from './routes'
 
 /**
@@ -52,6 +53,10 @@ export const routesConfig = {
   ),
   children: [
     workqueueRouter,
+    {
+      path: ROUTES.V2.EVENTS.VIEW.path,
+      element: <ReadOnlyView />
+    },
     {
       path: ROUTES.V2.EVENTS.OVERVIEW.path,
       element: (
diff --git a/packages/client/src/v2-events/routes/routes.ts b/packages/client/src/v2-events/routes/routes.ts
index edcd22d5217..94d74914870 100644
--- a/packages/client/src/v2-events/routes/routes.ts
+++ b/packages/client/src/v2-events/routes/routes.ts
@@ -23,6 +23,9 @@ export const ROUTES = {
         'events',
         {},
         {
+          VIEW: route('view/:eventId', {
+            params: { eventId: string().defined() }
+          }),
           OVERVIEW: route('overview/:eventId', {
             params: { eventId: string().defined() }
           }),


Reviews:
user: login: makelicious
body: Looks good! Could you check the suggestions and create a storybook. If you need to start vacation I can take over
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T13:51:58Z
user: login: tareq89
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T13:58:33Z
user: login: makelicious
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T14:22:53Z
user: login: rikukissa
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T15:52:32Z
user: login: rikukissa
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-26T15:52:53Z

Review comments:
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 { useTypedParams } from 'react-router-typesafe-routes/dom'
+import {
+  ActionType,
+  EventConfig,
+  EventDocument,
+  findActiveActionForm,
+  getMetadataForAction
+} from '@opencrvs/commons/client'
+import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration'
+import { useEventFormData } from '@client/v2-events/features/events/useEventFormData'
+import { useEventMetadata } from '@client/v2-events/features/events/useEventMeta'
+import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents'
+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 { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils'
+
+const READ_ONLY_MODE_ALLOWED_ACTIONS = [
+  ActionType.APPROVE_CORRECTION,
+  ActionType.REGISTER,
+  ActionType.VALIDATE,
+  ActionType.DECLARE
+]
+
+function findLastActionFormConfigForReadOnlyMode(
+  config: EventConfig,
+  event: EventDocument
+) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return findActiveActionForm(config, actionType)
+    }
+  }
+}
+
+function findLastActionMetadata(event: EventDocument) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
path: packages/client/src/v2-events/features/events/ViewDeclaration.tsx
user: login: makelicious
body: Could you add comment specifying why we have something like READ_ONLY_MODE_ALLOWED_ACTIONS
created_at: 2025-03-26T13:43:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 { useTypedParams } from 'react-router-typesafe-routes/dom'
+import {
+  ActionType,
+  EventConfig,
+  EventDocument,
+  findActiveActionForm,
+  getMetadataForAction
+} from '@opencrvs/commons/client'
+import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration'
+import { useEventFormData } from '@client/v2-events/features/events/useEventFormData'
+import { useEventMetadata } from '@client/v2-events/features/events/useEventMeta'
+import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents'
+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 { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils'
+
+const READ_ONLY_MODE_ALLOWED_ACTIONS = [
+  ActionType.APPROVE_CORRECTION,
+  ActionType.REGISTER,
+  ActionType.VALIDATE,
+  ActionType.DECLARE
+]
+
+function findLastActionFormConfigForReadOnlyMode(
+  config: EventConfig,
+  event: EventDocument
+) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return findActiveActionForm(config, actionType)
+    }
+  }
+}
+
+function findLastActionMetadata(event: EventDocument) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return getMetadataForAction({ event, actionType, drafts: [] })
+    }
+  }
+}
+
+function View() {
+  const { eventId } = useTypedParams(ROUTES.V2.EVENTS.VIEW)
+  const { getEventState, getEvent } = useEvents()
+  const { formatMessage } = useIntlFormatMessageWithFlattenedParams()
+  const currentEventState = getEventState.useSuspenseQuery(eventId)
+  const { eventConfiguration: config } = useEventConfiguration(
+    currentEventState.type
+  )
+  const [fullEvent] = getEvent.useSuspenseQuery(eventId)
+  const formConfig = findLastActionFormConfigForReadOnlyMode(config, fullEvent)
+
+  if (!formConfig) {
+    throw new Error('No active form configuration found for declare action')
path: packages/client/src/v2-events/features/events/ViewDeclaration.tsx
user: login: makelicious
body: is this correct? what should it say?
created_at: 2025-03-26T13:48:50Z
author_association: CONTRIBUTOR
path: packages/client/src/v2-events/features/events/ViewDeclaration.tsx
user: login: makelicious
body: we now have 'view', 'overview' and 'review', which might get bit confusing. Could we call this something else, just for clarity? `ReadOnlyView` would make it distinct, even though it is not that snappy. Try that or some better alternative, both for the route name and component name.
created_at: 2025-03-26T13:50:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 { useTypedParams } from 'react-router-typesafe-routes/dom'
+import {
+  ActionType,
+  EventConfig,
+  EventDocument,
+  findActiveActionForm,
+  getMetadataForAction
+} from '@opencrvs/commons/client'
+import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration'
+import { useEventFormData } from '@client/v2-events/features/events/useEventFormData'
+import { useEventMetadata } from '@client/v2-events/features/events/useEventMeta'
+import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents'
+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 { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils'
+
+const READ_ONLY_MODE_ALLOWED_ACTIONS = [
+  ActionType.APPROVE_CORRECTION,
+  ActionType.REGISTER,
+  ActionType.VALIDATE,
+  ActionType.DECLARE
+]
+
+function findLastActionFormConfigForReadOnlyMode(
+  config: EventConfig,
+  event: EventDocument
+) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return findActiveActionForm(config, actionType)
+    }
+  }
+}
+
+function findLastActionMetadata(event: EventDocument) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return getMetadataForAction({ event, actionType, drafts: [] })
+    }
+  }
+}
+
+function View() {
+  const { eventId } = useTypedParams(ROUTES.V2.EVENTS.VIEW)
+  const { getEventState, getEvent } = useEvents()
+  const { formatMessage } = useIntlFormatMessageWithFlattenedParams()
+  const currentEventState = getEventState.useSuspenseQuery(eventId)
+  const { eventConfiguration: config } = useEventConfiguration(
+    currentEventState.type
+  )
+  const [fullEvent] = getEvent.useSuspenseQuery(eventId)
+  const formConfig = findLastActionFormConfigForReadOnlyMode(config, fullEvent)
+
+  if (!formConfig) {
+    throw new Error('No active form configuration found for declare action')
+  }
+
+  const form = useEventFormData((state) =>
+    state.getFormValues(currentEventState.data)
+  )
+
+  const { setMetadata, getMetadata } = useEventMetadata()
+  const metadata = getMetadata(findLastActionMetadata(fullEvent))
+
+  return (
+    <FormLayout route={ROUTES.V2.EVENTS.DECLARE}>
+      <ReviewComponent.Body
+        readonlyMode
+        eventConfig={config}
+        form={form}
+        formConfig={formConfig}
+        metadata={metadata}
+        title={formatMessage(formConfig.review.title, form)}
+        onEdit={() => {}}
path: packages/client/src/v2-events/features/events/ViewDeclaration.tsx
user: login: makelicious
body: prefer noop from lodash
created_at: 2025-03-26T13:51:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 { useTypedParams } from 'react-router-typesafe-routes/dom'
+import {
+  ActionType,
+  EventConfig,
+  EventDocument,
+  findActiveActionForm,
+  getMetadataForAction
+} from '@opencrvs/commons/client'
+import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration'
+import { useEventFormData } from '@client/v2-events/features/events/useEventFormData'
+import { useEventMetadata } from '@client/v2-events/features/events/useEventMeta'
+import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents'
+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 { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils'
+
+const READ_ONLY_MODE_ALLOWED_ACTIONS = [
+  ActionType.APPROVE_CORRECTION,
+  ActionType.REGISTER,
+  ActionType.VALIDATE,
+  ActionType.DECLARE
+]
+
+function findLastActionFormConfigForReadOnlyMode(
+  config: EventConfig,
+  event: EventDocument
+) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return findActiveActionForm(config, actionType)
+    }
+  }
+}
+
+function findLastActionMetadata(event: EventDocument) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
path: packages/client/src/v2-events/features/events/ViewDeclaration.tsx
user: login: tareq89
body: `// These are the allowed actions based on which we can read a declarations data`, is it good?
created_at: 2025-03-26T13:58:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 { useTypedParams } from 'react-router-typesafe-routes/dom'
+import {
+  ActionType,
+  EventConfig,
+  EventDocument,
+  findActiveActionForm,
+  getMetadataForAction
+} from '@opencrvs/commons/client'
+import { useEventConfiguration } from '@client/v2-events/features/events/useEventConfiguration'
+import { useEventFormData } from '@client/v2-events/features/events/useEventFormData'
+import { useEventMetadata } from '@client/v2-events/features/events/useEventMeta'
+import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents'
+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 { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/messages/utils'
+
+const READ_ONLY_MODE_ALLOWED_ACTIONS = [
+  ActionType.APPROVE_CORRECTION,
+  ActionType.REGISTER,
+  ActionType.VALIDATE,
+  ActionType.DECLARE
+]
+
+function findLastActionFormConfigForReadOnlyMode(
+  config: EventConfig,
+  event: EventDocument
+) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
+    const availableAllowedAction = event.actions.find(
+      (a) => a.type === actionType
+    )
+    if (availableAllowedAction) {
+      return findActiveActionForm(config, actionType)
+    }
+  }
+}
+
+function findLastActionMetadata(event: EventDocument) {
+  for (const actionType of READ_ONLY_MODE_ALLOWED_ACTIONS) {
path: packages/client/src/v2-events/features/events/ViewDeclaration.tsx
user: login: rikukissa
body: @makelicious Yet another issue that would require the "big form". For now it's safe to use the form of the `declare` action as "the form" the view a record should show
created_at: 2025-03-26T15:52:32Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-26T13:38:53Z
body: Oops! Looks like you forgot to update the changelog. When updating CHANGELOG.md, please consider the following:
  - Changelog is read by country implementors who might not always be familiar with all technical details of OpenCRVS. Keep language high-level, user friendly and avoid technical references to internals.
  - Answer "What's new?", "Why was the change made?" and "Why should I care?" for each change.
  - If it's a breaking change, include a migration guide answering "What do I need to do to upgrade?".
author_association: NONE
user: login: ocrvs-bot
created_at: 2025-03-26T13:51:17Z
body: Your environment is deployed to https://ocrvs-8979.opencrvs.dev
author_association: CONTRIBUTOR