Skip to content

Commit

Permalink
Merge pull request #8313 from opencrvs/feat/events-resolve-ids
Browse files Browse the repository at this point in the history
Feat/events resolve ids
  • Loading branch information
makelicious authored Jan 15, 2025
2 parents 6621237 + 9aace78 commit bdee906
Show file tree
Hide file tree
Showing 63 changed files with 1,458 additions and 471 deletions.
10 changes: 5 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ services:
depends_on:
- base
environment:
- MONGO_URL=mongodb://mongo1/events
- ES_HOST=elasticsearch:9200
- COUNTRY_CONFIG_URL=http://countryconfig:3040/
- EVENTS_MONGO_URL=mongodb://mongo1/events
- USER_MGNT_MONGO_URL=mongodb://mongo1/user-mgnt
- ES_URL=http://elasticsearch:9200
- COUNTRY_CONFIG_URL=http://countryconfig:3040
- DOCUMENTS_URL=http://documents:9050
- USER_MANAGEMENT_URL=http://localhost:3030/

- USER_MANAGEMENT_URL=http://user-mgnt:3030
# User facing services
workflow:
image: opencrvs/ocrvs-workflow:${VERSION}
Expand Down
7 changes: 7 additions & 0 deletions packages/client/src/offline/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/
import { IOfflineDataState, IOfflineData } from '@client/offline/reducer'
import { IStoreState } from '@client/store'
import { createSelector } from '@reduxjs/toolkit'
import { merge } from 'lodash'

const getOfflineState = (store: IStoreState): IOfflineDataState => store.offline
Expand Down Expand Up @@ -46,6 +47,12 @@ export const getOfflineData = (store: IStoreState): IOfflineData => {
return data
}

export const getLocations = createSelector(getOfflineData, (data) => ({
...data.locations,
...data.facilities,
...data.offices
}))

export const selectCountryBackground = (store: IStoreState) => {
const countryBackground = getKey(store, 'offlineData').config
?.LOGIN_BACKGROUND
Expand Down
29 changes: 29 additions & 0 deletions packages/client/src/v2-events/components/withSuspense.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { Spinner } from '@opencrvs/components'

/**
* HOC to wrap a component in a suspense boundary with a spinner fallback.
*/
export function withSuspense<
ComponentProps extends React.JSX.IntrinsicAttributes
>(Component: React.ComponentType<ComponentProps>) {
// eslint-disable-next-line react/display-name
return (props: ComponentProps) => (
<React.Suspense
fallback={<Spinner id={`page-spinner-${new Date().getTime()}`} />}
>
<Component {...props} />
</React.Suspense>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ export function ValidateEvent() {
const validateEvent = events.actions.validate

useEffect(() => {
validateEvent.mutate({ eventId, data: {}, transactionId: uuid() })
validateEvent.mutate({
eventId,
data: {},
transactionId: uuid(),
duplicates: []
})
navigate(ROUTES.V2.path)
}, [validateEvent, eventId, navigate])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { api } from '@client/v2-events/trpc'
* @returns a list of event configurations
*/
export function useEventConfigurations() {
const [config] = api.config.get.useSuspenseQuery()
const [config] = api.event.config.get.useSuspenseQuery()
return config
}

Expand All @@ -28,7 +28,7 @@ export function useEventConfigurations() {
export function useEventConfiguration(eventIdentifier: string): {
eventConfiguration: EventConfig
} {
const [config] = api.config.get.useSuspenseQuery()
const [config] = api.event.config.get.useSuspenseQuery()
const eventConfiguration = config.find(
(event) => event.id === eventIdentifier
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@
*/

import { Mutation as TanstackMutation } from '@tanstack/query-core'
import { hashKey, useMutation } from '@tanstack/react-query'
import { getMutationKey, getQueryKey } from '@trpc/react-query'
import { useMutation } from '@tanstack/react-query'
import { getMutationKey } from '@trpc/react-query'
import {
ActionInput,
EventDocument,
getCurrentEventState
} from '@opencrvs/commons/client'
import { ActionFormData } from '@opencrvs/commons'
import { api, queryClient, utils } from '@client/v2-events/trpc'

async function updateLocalEvent(updatedEvent: EventDocument) {
utils.event.get.setData(updatedEvent.id, updatedEvent)
return utils.events.get.invalidate()
return utils.event.list.invalidate()
}

function waitUntilEventIsCreated<T extends { eventId: string }, R>(
Expand Down Expand Up @@ -127,7 +126,7 @@ function updateEventOptimistically<T extends ActionInput>(
]
}

utils.events.get.setData(undefined, (eventIndices) =>
utils.event.list.setData(undefined, (eventIndices) =>
eventIndices
?.filter((ei) => ei.id !== optimisticEvent.id)
.concat(getCurrentEventState(optimisticEvent))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ utils.event.create.setMutationDefaults(({ canonicalMutationFn }) => ({
}

utils.event.get.setData(newEvent.transactionId, optimisticEvent)
utils.events.get.setData(undefined, (eventIndices) =>
utils.event.list.setData(undefined, (eventIndices) =>
eventIndices?.concat(getCurrentEventState(optimisticEvent))
)
return optimisticEvent
},
onSuccess: async (response) => {
utils.event.get.setData(response.id, response)
utils.event.get.setData(response.transactionId, response)
await utils.events.get.invalidate()
await utils.event.list.invalidate()
}
}))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ utils.event.delete.setMutationDefaults(({ canonicalMutationFn }) => ({
},
retryDelay: 10000,
onSuccess: ({ id }) => {
void utils.events.get.invalidate()
void utils.event.list.invalidate()
},
/*
* This ensures that when the application is reloaded with pending mutations in IndexedDB, the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import { renderHook, RenderHookResult, waitFor } from '@testing-library/react'
import React, { act, PropsWithChildren } from 'react'
import React, { PropsWithChildren } from 'react'

import { http, HttpResponse, HttpResponseResolver } from 'msw'
import { setupServer } from 'msw/node'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function filterOutboxEventsWithMutation<
}

export function useEvents() {
const eventsList = api.events.get.useQuery().data ?? []
const eventsList = api.event.list.useQuery().data ?? []

function getDrafts(): EventDocument[] {
const queries = queryClient.getQueriesData<EventDocument>({
Expand Down Expand Up @@ -98,7 +98,7 @@ export function useEvents() {
return {
createEvent,
getEvent: api.event.get,
getEvents: api.events.get,
getEvents: api.event.list,
deleteEvent: useDeleteEventMutation(),
getOutbox,
getDrafts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import React, { useEffect } from 'react'
import { useSelector } from 'react-redux'
import React from 'react'
import { useTypedParams } from 'react-router-typesafe-routes/dom'
import { useSelector } from 'react-redux'
import {
ActionDocument,
EventIndex,
ActionDocument,
getAllFields,
SummaryConfig
} from '@opencrvs/commons/client'
Expand All @@ -25,47 +25,51 @@ import {
useEventConfiguration,
useEventConfigurations
} from '@client/v2-events/features/events/useEventConfiguration'
// eslint-disable-next-line no-restricted-imports
import { getUserDetails } from '@client/profile/profileSelectors'
// eslint-disable-next-line no-restricted-imports
import { ProfileState } from '@client/profile/profileReducer'
import { getInitialValues } from '@client/v2-events/components/forms/utils'
import { useEvents } from '@client/v2-events/features/events/useEvents/useEvents'
import { useIntlFormatMessageWithFlattenedParams } from '@client/v2-events/features/workqueues/utils'
import { utils } from '@client/v2-events/trpc'
import { useUsers } from '@client/v2-events/hooks/useUsers'
// eslint-disable-next-line no-restricted-imports
import { getLocations } from '@client/offline/selectors'
import { withSuspense } from '@client/v2-events/components/withSuspense'
import { getUserIdsFromActions } from '@client/v2-events/utils'
import { EventHistory } from './components/EventHistory'
import { EventSummary } from './components/EventSummary'

import { ActionMenu } from './components/ActionMenu'
import { EventOverviewProvider } from './EventOverviewContext'

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

export function EventOverviewIndex() {
function EventOverviewContainer() {
const params = useTypedParams(ROUTES.V2.EVENTS.OVERVIEW)
const { getEvents, getEvent } = useEvents()
const user = useSelector(getUserDetails)
const { getUsers } = useUsers()

const [config] = useEventConfigurations()

const { data: fullEvent } = getEvent.useQuery(params.eventId)
const [fullEvent] = getEvent.useSuspenseQuery(params.eventId)
const [events] = getEvents.useSuspenseQuery()
const event = events.find((e) => e.id === params.eventId)

const { data: events } = getEvents.useQuery()
const userIds = getUserIdsFromActions(fullEvent.actions)
const [users] = getUsers.useSuspenseQuery(userIds)
const locations = useSelector(getLocations)

const event = events?.find((e) => e.id === params.eventId)

if (!event || !fullEvent?.actions) {
if (!event) {
return null
}

return (
<EventOverview
event={event}
history={fullEvent.actions.filter((action) => !action.draft)}
summary={config.summary}
user={user}
/>
<EventOverviewProvider locations={locations} users={users}>
<EventOverview
event={event}
history={fullEvent.actions.filter((action) => !action.draft)}
summary={config.summary}
/>
</EventOverviewProvider>
)
}

Expand All @@ -75,13 +79,11 @@ export function EventOverviewIndex() {
function EventOverview({
event,
summary,
history,
user
history
}: {
event: EventIndex
summary: SummaryConfig
history: ActionDocument[]
user: ProfileState['userDetails']
}) {
const { eventConfiguration } = useEventConfiguration(event.type)
const intl = useIntlFormatMessageWithFlattenedParams()
Expand All @@ -98,7 +100,9 @@ function EventOverview({
topActionButtons={[<ActionMenu key={event.id} eventId={event.id} />]}
>
<EventSummary event={event} summary={summary} />
<EventHistory history={history} user={user} />
<EventHistory history={history} />
</Content>
)
}

export const EventOverviewIndex = withSuspense(EventOverviewContainer)
Original file line number Diff line number Diff line change
@@ -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, { createContext, useContext } from 'react'
import { ResolvedUser } from '@opencrvs/commons/client'
// eslint-disable-next-line no-restricted-imports
import { ILocation } from '@client/offline/reducer'

const EventOverviewContext = createContext<{
getUser: (id: string) => ResolvedUser
getLocation: (id: string) => ILocation
} | null>(null)

const MISSING_USER = {
id: 'ID_MISSING',
name: [
{
use: 'en',
given: ['Missing'],
family: 'user'
}
],
systemRole: '-'
}

/**
* Provides methods for resolving users and locations within the event.
*/
export const EventOverviewProvider = ({
children,
locations,
users
}: {
children: React.ReactNode
users: ResolvedUser[]
locations: Record<string, ILocation>
}) => {
const getUser = (id: string) => {
const user = users.find((u) => u.id === id)

if (!user) {
// eslint-disable-next-line no-console
console.error(`User with id ${id} not found.`)

return MISSING_USER
}

return user
}

const getLocation = (id: string) => {
// @TODO: Remove this fallback once developers have had time to update their data.
// eslint-disable-next-line
return locations[id] ?? { id, name: 'Unknown location' }
}

return (
<EventOverviewContext.Provider value={{ getUser, getLocation }}>
{children}
</EventOverviewContext.Provider>
)
}

export const useEventOverviewContext = () => {
const context = useContext(EventOverviewContext)

if (!context) {
throw new Error('EventOverviewContext not found')
}

return context
}
Loading

0 comments on commit bdee906

Please sign in to comment.