Skip to content

Commit

Permalink
feat(front): component triggering REST call
Browse files Browse the repository at this point in the history
 - create Action control, create action status
 - unwind response + commit after call
  • Loading branch information
xtiannyeto committed Dec 30, 2024
1 parent 7f36dbe commit 609ff13
Show file tree
Hide file tree
Showing 17 changed files with 118 additions and 129 deletions.
25 changes: 15 additions & 10 deletions frontend/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { FC, useEffect, useState } from 'react'
import { ApolloClient, ApolloProvider, NormalizedCacheObject } from '@apollo/client'
import { Notifier } from '@mtes-mct/monitor-ui'
import * as Sentry from '@sentry/react'
import { QueryClientProvider } from '@tanstack/react-query'
import { CachePersistor, LocalStorageWrapper } from 'apollo3-cache-persist'
import { router } from './router/router'
import UIThemeWrapper from './features/common/components/ui/ui-theme-wrapper'
import { FC, useEffect, useState } from 'react'
import apolloClient, { apolloCache } from './apollo-client/apollo-client.ts'
import RouterProvider from './router/router-provider'
import * as Sentry from '@sentry/react'
import UIThemeWrapper from './features/common/components/ui/ui-theme-wrapper'
import ErrorPage from './pages/error-page.tsx'
import { Notifier } from '@mtes-mct/monitor-ui'
import { queryClient } from './query-client/index.ts'
import { router } from './router/router'
import RouterProvider from './router/router-provider'

// import { FlagProvider } from '@unleash/proxy-client-react'
// import { IConfig } from 'unleash-proxy-client'

Expand Down Expand Up @@ -52,10 +55,12 @@ const App: FC = () => {
<Sentry.ErrorBoundary fallback={ErrorPage}>
<ApolloProvider client={client}>
<UIThemeWrapper>
{/*<FlagProvider config={config}>*/}
<Notifier />
<RouterProvider router={router} />
{/*</FlagProvider>*/}
<QueryClientProvider client={queryClient}>
{/*<FlagProvider config={config}>*/}
<Notifier />
<RouterProvider router={router} />
{/*</FlagProvider>*/}
</QueryClientProvider>
</UIThemeWrapper>
</ApolloProvider>
</Sentry.ErrorBoundary>
Expand Down
32 changes: 19 additions & 13 deletions frontend/src/v2/features/common/hooks/use-mission-timeline.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { ActionTypeEnum } from '@common/types/env-mission-types'
import { ActionTypeEnum, MissionSourceEnum } from '@common/types/env-mission-types'
import { MissionNavAction } from '../types/mission-action'

type ActionRegistryInput = { [key in ActionTypeEnum]?: unknown }
type Input = { missionId: number; startDateTimeUtc: Date }

const ACTION_REGISTRY_INPUT: ActionRegistryInput = {
[ActionTypeEnum.NOTE]: { endDateTimeUtc: new Date().toISOString() },
[ActionTypeEnum.RESCUE]: { isPersonRescue: false, isVesselRescue: true }
}

interface TimelineHook<T> {
getBaseInput: () => Input
getActionDataInput: (actionType: ActionTypeEnum) => unknown
getActionInput: (actionType: ActionTypeEnum, moreData?: unknown) => MissionNavAction
}

export function useMissionTimeline<T>(missionId?: string): TimelineHook<T> {
const getActionDataInput = (actionType: ActionTypeEnum) => {
const data = ACTION_REGISTRY_INPUT[actionType] || {}
return {
...data,
...getBaseInput(),
endDateTimeUtc: new Date()
}
export function useMissionTimeline<T>(missionId?: number): TimelineHook<T> {
const getActionInput = (actionType: ActionTypeEnum, moreData?: unknown): MissionNavAction => {
const input = {
missionId: Number(missionId),
actionType,
source: MissionSourceEnum.RAPPORTNAV,
data: {
...(moreData ?? {}),
...(ACTION_REGISTRY_INPUT[actionType] ?? {}),
startDateTimeUtc: new Date().toISOString()
}
} as MissionNavAction

return input
}
const getBaseInput = (): Input => ({ startDateTimeUtc: new Date(), missionId: parseInt(missionId!, 10) })

return { getBaseInput, getActionDataInput }
return { getActionInput }
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { MissionStatusEnum } from '@common/types/mission-types.ts'
import { FC } from 'react'
import { Stack } from 'rsuite'
import { useMissionActionQuery } from '../../../common/services/use-mission-action.tsx'
import useGetActionQuery from '../../../common/services/use-mission-action.tsx'
import MissionActionHeaderCompletenessForStats from './mission-action-header-completeness-for-stats.tsx'
import MissionActionHeaderTitle from './mission-action-header-title.tsx'

export type MissionActionHeaderProps = {
actionId?: string
missionId?: number
missionId: number
missionStatus?: MissionStatusEnum
}

const MissionActionHeader: FC<MissionActionHeaderProps> = ({ actionId, missionId, missionStatus }) => {
const { data: action } = useMissionActionQuery(actionId, missionId)
const { data: action } = useGetActionQuery(missionId, actionId)

return (
<Stack direction="column" spacing={'0.5rem'}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { createElement, FunctionComponent } from 'react'
import { MissionActionOutput } from '../../../common/types/mission-action-output.ts'
import { MissionAction } from '../../../common/types/mission-action.ts'
import MissionActionEmpty from '../ui/mission-action-empty.tsx'
import MissionActionError from '../ui/mission-action-error.tsx'
import MissionActionLoader from '../ui/mission-action-loader.tsx'

type MissionActionWrapperProps = {
isError?: any
missionId?: number
missionId: number
isLoading?: boolean
action?: MissionActionOutput
item: FunctionComponent<{ action: MissionActionOutput; missionId?: number }>
action?: MissionAction
item: FunctionComponent<{ action: MissionAction; missionId: number }>
}

const MissionActionWrapper: React.FC<MissionActionWrapperProps> = ({ item, action, missionId, isError, isLoading }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,24 @@ import { Stack } from 'rsuite'
import MissionActionDropdownWrapper from '../../../common/components/ui/mission-action-dropdown-wrapper'
import MissionControlSelection from '../../../common/components/ui/mission-control-selection'
import { useMissionTimeline } from '../../../common/hooks/use-mission-timeline'
import { useAddOrUpdateActionMutation } from '../../../common/services/use-add-action'
import useAddOrUpdateControlMutation from '../../../common/services/use-add-update-action-control'
import useCreateMissionActionMutation from '../../../common/services/use-create-mission-action'
import { ModuleType } from '../../../common/types/module-type'

type MissionTimelineAddActionProps = {
missionId?: number
missionId: number
moduleType: ModuleType
onSumbit?: (id?: string) => void
}

function MissionTimelineAddAction({ missionId, onSumbit, moduleType }: MissionTimelineAddActionProps): JSX.Element {
const [addControl] = useAddOrUpdateControlMutation()
const [addOrUpdateAction] = useAddOrUpdateActionMutation()
const { getActionInput } = useMissionTimeline(missionId)
const mutation = useCreateMissionActionMutation(missionId)
const [showModal, setShowModal] = useState<boolean>(false)
const { getBaseInput, getActionDataInput } = useMissionTimeline(missionId?.toString())

const handleAddAction = async (actionType: ActionTypeEnum) => {
const data = getActionDataInput(actionType)
const response = await addOrUpdateAction({ variables: { action: { missionId, type: actionType, data } } })
if (onSumbit) onSumbit(response.data?.id)
const handleAddAction = async (actionType: ActionTypeEnum, data?: unknown) => {
const action = getActionInput(actionType, data)
const response = await mutation.mutateAsync(action)
if (onSumbit) onSumbit(response?.id)
}

const handleSelect = async (actionType: ActionTypeEnum) => {
Expand All @@ -38,14 +36,7 @@ function MissionTimelineAddAction({ missionId, onSumbit, moduleType }: MissionTi
}

const handleAddControl = async (controlMethod: string, vesselType: VesselTypeEnum) => {
const controlAction = {
vesselType,
controlMethod,
...getBaseInput(),
endDateTimeUtc: new Date()
}
const response = await addControl({ variables: { controlAction } })
if (onSumbit) onSumbit(response.data?.id)
handleAddAction(ActionTypeEnum.CONTROL, { controlMethod, vesselType })
}

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ActionStatusType } from '@common/types/action-types.ts'
import { ActionTypeEnum } from '@common/types/env-mission-types'
import { getColorForStatus, mapStatusToText } from '@common/utils/status-utils'
import useAddOrUpdateStatus from '@features/pam/mission/hooks/use-add-update-status'
import { Dropdown, Icon } from '@mtes-mct/monitor-ui'
import { FC } from 'react'
import { Stack } from 'rsuite'
import { useMissionTimeline } from '../../../common/hooks/use-mission-timeline'
import useCreateMissionActionMutation from '../../../common/services/use-create-mission-action'

const ACTION_STATUS: ActionStatusType[] = [
ActionStatusType.NAVIGATING,
Expand All @@ -14,7 +15,7 @@ const ACTION_STATUS: ActionStatusType[] = [
]

interface MissionTimelineAddStatusProps {
missionId?: number
missionId: number
onSumbit?: (id?: string) => void
}

Expand All @@ -30,30 +31,25 @@ export const MissionStatusColorTag: FC<{ status: ActionStatusType }> = ({ status
)

const MissionTimelineAddStatus: FC<MissionTimelineAddStatusProps> = ({ missionId, onSumbit }) => {
const [addStatus, { loading }] = useAddOrUpdateStatus()
const { getBaseInput } = useMissionTimeline(missionId?.toString())
const { getActionInput } = useMissionTimeline(missionId)
const mutation = useCreateMissionActionMutation(missionId)

const handleAddStatus = async (status: ActionStatusType) => {
const statusAction = {
status,
reason: null,
observations: null,
...getBaseInput()
}
const response = await addStatus({ variables: { statusAction } })
if (onSumbit) onSumbit(response.data?.id)
const action = getActionInput(ActionTypeEnum.STATUS, { status })
const response = await mutation.mutateAsync(action)
if (onSumbit) onSumbit(response?.id)
}

return (
<Dropdown
Icon={Icon.FleetSegment}
onSelect={handleAddStatus}
disabled={loading}
disabled={mutation.isPending}
title="&#x25BC;"
placement="bottomEnd"
>
{ACTION_STATUS.map(status => (
<Dropdown.Item key={status} eventKey={status} disabled={loading}>
<Dropdown.Item key={status} eventKey={status} disabled={mutation.isPending}>
<Stack spacing="0.5rem" alignItems="center">
<Stack.Item>
<MissionStatusColorTag status={status} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import MissionTimelineAddAction from '../elements/mission-timeline-add-action'
import MissionTimelineAddStatus from '../elements/mission-timeline-add-status'

type MissionTimelineHeaderWrapperProps = {
missionId?: number
missionId: number
hideAction?: boolean
hideStatus?: boolean
moduleType: ModuleType
Expand All @@ -18,7 +18,7 @@ const MissionTimelineHeaderWrapper: React.FC<MissionTimelineHeaderWrapperProps>
moduleType
}) => {
const navigate = useNavigate()
const handleOnSubmit = (id?: string) => navigate(`/${moduleType}/missions/${missionId}/${id}`)
const handleOnSubmit = (id?: string) => navigate(`/v2/${moduleType}/missions/${missionId}/${id}`)

return (
<Stack direction={'row'} justifyContent={'space-between'} spacing={'0.5rem'} wrap={true}>
Expand Down
31 changes: 14 additions & 17 deletions frontend/src/v2/features/mission-timeline/hooks/use-timeline.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { MissionSourceEnum } from '@common/types/env-mission-types'
import { MissionActionOutput } from '../../common/types/mission-action-output'
import { MissionEnvActionOutput } from '../../common/types/mission-env-action-output'
import { MissionFishActionOutput } from '../../common/types/mission-fish-action-output'
import { MissionNavActionOutput } from '../../common/types/mission-nav-action-output'
import { MissionAction, MissionEnvAction, MissionFishAction, MissionNavAction } from '../../common/types/mission-action'
import { MissionTimelineAction } from '../types/mission-timeline-output'

interface TimelineHook {
getTimeLineAction: (actions?: MissionActionOutput[]) => MissionTimelineAction[]
getTimeLineFromNavAction: (output: MissionActionOutput) => MissionTimelineAction
getTimeLineFromEnvAction: (output: MissionActionOutput) => MissionTimelineAction
getTimeLineFromFishAction: (output: MissionActionOutput) => MissionTimelineAction
getTimeLineAction: (actions?: MissionAction[]) => MissionTimelineAction[]
getTimeLineFromNavAction: (output: MissionAction) => MissionTimelineAction
getTimeLineFromEnvAction: (output: MissionAction) => MissionTimelineAction
getTimeLineFromFishAction: (output: MissionAction) => MissionTimelineAction
}

export function useTimeline(): TimelineHook {
const getTimeLineAction = (actions?: MissionActionOutput[]): MissionTimelineAction[] => {
const getTimeLineAction = (actions?: MissionAction[]): MissionTimelineAction[] => {
return (
actions?.map(action => {
switch (action.source) {
Expand All @@ -30,8 +27,8 @@ export function useTimeline(): TimelineHook {
)
}

const getTimeLineFromEnvAction = (output: MissionActionOutput): MissionTimelineAction => {
const action = output as MissionEnvActionOutput
const getTimeLineFromEnvAction = (output: MissionAction): MissionTimelineAction => {
const action = output as MissionEnvAction
return {
id: action.id,
status: action.status,
Expand All @@ -50,10 +47,10 @@ export function useTimeline(): TimelineHook {
}
}

const getTimeLineFromNavAction = (output: MissionActionOutput): MissionTimelineAction => {
const action = output as MissionNavActionOutput
const getTimeLineFromNavAction = (output: MissionAction): MissionTimelineAction => {
const action = output as MissionNavAction
return {
id: action.id.toString(),
id: action.id,
source: action.source,
status: action.status,
missionId: action.missionId,
Expand All @@ -74,10 +71,10 @@ export function useTimeline(): TimelineHook {
}
}

const getTimeLineFromFishAction = (output: MissionActionOutput): MissionTimelineAction => {
const action = output as MissionFishActionOutput
const getTimeLineFromFishAction = (output: MissionAction): MissionTimelineAction => {
const action = output as MissionFishAction
return {
id: action.id.toString(),
id: action.id,
source: action.source,
status: action.status,
type: action.actionType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ModuleType } from '../../../common/types/module-type'
import MissionTimelineHeaderWrapper from '../../../mission-timeline/components/layout/mission-timeline-Header-wrapper'

interface MissionTimelineHeaderPamProps {
missionId?: string
missionId: number
}

const MissionTimelineHeaderPam: FC<MissionTimelineHeaderPamProps> = ({ missionId }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { FC } from 'react'
import { useMissionActionListQuery } from '../../../common/services/use-mission-action-list'
import useGetActionListQuery from '../../../common/services/use-mission-action-list'
import MissionTimelineWrapper from '../../../mission-timeline/components/layout/mission-timeline-wrapper'
import { useTimeline } from '../../../mission-timeline/hooks/use-timeline'
import MissionTimelineItemPam from './mission-timeline-item-pam'

interface MissionTimelineProps {
missionId?: number
missionId: number
}

const MissionTimelinePam: FC<MissionTimelineProps> = ({ missionId }) => {
const { getTimeLineAction } = useTimeline()
const { data: actions, loading, error } = useMissionActionListQuery(missionId)
const query = useGetActionListQuery(missionId)
return (
<MissionTimelineWrapper
isError={error}
isLoading={loading}
isError={query.error}
missionId={missionId}
groupBy="startDateTimeUtc"
isLoading={query.isLoading}
item={MissionTimelineItemPam}
actions={getTimeLineAction(actions)}
actions={getTimeLineAction(query.data)}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,26 @@ import { createElement, FC } from 'react'
import { store } from '../../../store'
import { resetDebounceTime } from '../../../store/slices/delay-query-reducer'
import { useDelay } from '../../common/hooks/use-delay'
import { useMissionActionInput } from '../../common/hooks/use-mission-action-input'
import useUpdateMissionActionMutation from '../../common/services/use-update-mission-action'
import { MissionActionOutput } from '../../common/types/mission-action-output'
import { MissionAction } from '../../common/types/mission-action'
import { usePamActionRegistry } from '../hooks/use-pam-action-registry'

interface MissionActionItemPamProps {
missionId?: number
missionId: number
action: MissionAction
isMissionFinished?: boolean
action: MissionActionOutput
}

const MissionActionItemPam: FC<MissionActionItemPamProps> = ({ action, missionId, isMissionFinished }) => {
const { handleExecuteOnDelay } = useDelay()
const debounceTime = useStore(store, state => state.delayQuery.debounceTime)
const { getMissionActionInput } = useMissionActionInput()
const { actionComponent } = usePamActionRegistry(action.actionType)

const [updateAction] = useUpdateMissionActionMutation(action.id, missionId)

const onChange = async (newAction: MissionActionOutput): Promise<void> => {
const input = getMissionActionInput(newAction)
const mutation = useUpdateMissionActionMutation(missionId, action?.id)

const onChange = async (newAction: MissionAction): Promise<void> => {
handleExecuteOnDelay(async () => {
await updateAction({ variables: { input } })
await mutation.mutateAsync(newAction)
if (debounceTime !== undefined) resetDebounceTime()
}, debounceTime)
}
Expand Down
Loading

0 comments on commit 609ff13

Please sign in to comment.