From 9a25a67e664f7791f1cf9cce5f3329c760483a19 Mon Sep 17 00:00:00 2001 From: lwih Date: Tue, 30 Jan 2024 17:08:15 +0100 Subject: [PATCH 1/3] Back & Front - remove isStart for status action --- .../mission/nav/action/ActionStatusEntity.kt | 5 +- .../mission/action/GetStatusForAction.kt | 17 +- .../bff/adapters/action/ActionStatusInput.kt | 2 - .../infrastructure/bff/model/action/Action.kt | 2 - .../bff/model/action/ActionData.kt | 1 - .../model/mission/action/ActionStatusModel.kt | 5 - ....2024.01.30.11.54__status_drop_isstart.sql | 10 + .../main/resources/graphql/action.graphqls | 2 - .../mission/status/GetStatusForActionTests.kt | 54 -- .../mission/actions/action-status-form.tsx | 294 ++++----- .../pam/mission/actions/use-action-by-id.tsx | 1 - frontend/src/pam/mission/mission-content.tsx | 323 +++++---- frontend/src/pam/mission/queries.ts | 623 +++++++++--------- .../mission/status/use-add-update-status.tsx | 25 +- .../item/timeline-item-status.test.tsx | 91 ++- .../timeline/item/timeline-item-status.tsx | 74 +-- .../timeline/item/timeline-item.test.tsx | 88 +-- .../mission/timeline/use-mission-timeline.tsx | 1 - frontend/src/types/action-types.ts | 1 - 19 files changed, 752 insertions(+), 867 deletions(-) create mode 100644 backend/src/main/resources/db/migration/V1.2024.01.30.11.54__status_drop_isstart.sql diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/action/ActionStatusEntity.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/action/ActionStatusEntity.kt index eaf21433b..2cb767a36 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/action/ActionStatusEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/action/ActionStatusEntity.kt @@ -12,7 +12,6 @@ data class ActionStatusEntity( val startDateTimeUtc: ZonedDateTime, val status: ActionStatusType, val reason: ActionStatusReason? = null, - val isStart: Boolean, val observations: String? = null, ) { fun toNavAction(): NavActionEntity { @@ -33,9 +32,7 @@ data class ActionStatusEntity( startDateTimeUtc = startDateTimeUtc, status = status, reason = reason, - isStart = isStart, observations = observations, - - ) + ) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GetStatusForAction.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GetStatusForAction.kt index 022359fe8..d9c30bd67 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GetStatusForAction.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GetStatusForAction.kt @@ -22,23 +22,8 @@ class GetStatusForAction( val lastActionStatus = actions .filter { it.startDateTimeUtc <= actionStartDateTimeUtc } .maxByOrNull { it.startDateTimeUtc } - val lastStartedActionStatus = actions - .filter { it.isStart && it.startDateTimeUtc <= actionStartDateTimeUtc } - .maxByOrNull { it.startDateTimeUtc } - - // case where there are 2 statuses at the same timestamp, one starting, one ending: - if (lastActionStatus != null && lastStartedActionStatus != null && lastStartedActionStatus.startDateTimeUtc == lastActionStatus.startDateTimeUtc) { - return lastStartedActionStatus.status - } - // return unknown if no action or if last action is a status of type finishing and no other starting action at that timestamp - else if (lastActionStatus == null || (!lastActionStatus.isStart && lastStartedActionStatus?.startDateTimeUtc != lastActionStatus.startDateTimeUtc)) { - return ActionStatusType.UNKNOWN - } - // return status of last status, it implies it is a starting status - else { - return lastActionStatus.status - } + return lastActionStatus?.status ?: ActionStatusType.UNKNOWN } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/adapters/action/ActionStatusInput.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/adapters/action/ActionStatusInput.kt index 3c9e142ce..ef52756f4 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/adapters/action/ActionStatusInput.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/adapters/action/ActionStatusInput.kt @@ -14,7 +14,6 @@ data class ActionStatusInput( val startDateTimeUtc: String?, val status: ActionStatusType, val reason: ActionStatusReason?, - val isStart: Boolean, val observations: String? ) { fun toActionStatus(): ActionStatusEntity { @@ -26,7 +25,6 @@ data class ActionStatusInput( } ?: ZonedDateTime.now(ZoneId.of("UTC")), status = status, reason = reason, - isStart = isStart, observations = observations ) } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt index 3cc16cdca..b02b8228b 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt @@ -154,8 +154,6 @@ data class Action( fun sortForTimeline(allActions: List?): List? { return allActions?.sortedWith(compareByDescending { it.startDateTimeUtc } .thenBy { it.data is NavActionStatus } - .thenBy { (it.data as? NavActionStatus)?.isStart == false } - .thenBy { (it.data as? NavActionStatus)?.isStart == true } ) } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/ActionData.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/ActionData.kt index d5e2ff3cd..60f7ea619 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/ActionData.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/ActionData.kt @@ -105,7 +105,6 @@ data class NavActionStatus( val startDateTimeUtc: ZonedDateTime, val status: ActionStatusType, val reason: ActionStatusReason? = null, - val isStart: Boolean, val observations: String? = null ) : ActionData() diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/action/ActionStatusModel.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/action/ActionStatusModel.kt index abb844c98..0c6147de5 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/action/ActionStatusModel.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/action/ActionStatusModel.kt @@ -30,9 +30,6 @@ class ActionStatusModel( @Column(name = "reason", nullable = true) var reason: String?, - @Column(name = "is_start", nullable = false) - var isStart: Boolean, - @Column(name = "observations", nullable = true) var observations: String?, @@ -44,7 +41,6 @@ class ActionStatusModel( startDateTimeUtc = startDateTimeUtc, status = mapStringToActionStatusType(status), reason = mapStringToActionStatusReason(reason), - isStart = isStart, observations = observations, ) } @@ -56,7 +52,6 @@ class ActionStatusModel( startDateTimeUtc = statusAction.startDateTimeUtc, status = statusAction.status.toString(), reason = statusAction.reason.toStringOrNull(), - isStart = statusAction.isStart, observations = statusAction.observations, ) } diff --git a/backend/src/main/resources/db/migration/V1.2024.01.30.11.54__status_drop_isstart.sql b/backend/src/main/resources/db/migration/V1.2024.01.30.11.54__status_drop_isstart.sql new file mode 100644 index 000000000..269533cf4 --- /dev/null +++ b/backend/src/main/resources/db/migration/V1.2024.01.30.11.54__status_drop_isstart.sql @@ -0,0 +1,10 @@ +DO +$$ + BEGIN + + -- Remove the isStart column from the mission_action_status table + ALTER TABLE mission_action_status + DROP COLUMN is_start; + + END +$$; diff --git a/backend/src/main/resources/graphql/action.graphqls b/backend/src/main/resources/graphql/action.graphqls index 9fbced7bb..dd89aa598 100644 --- a/backend/src/main/resources/graphql/action.graphqls +++ b/backend/src/main/resources/graphql/action.graphqls @@ -25,7 +25,6 @@ input ActionStatusInput { startDateTimeUtc: String status: ActionStatusType! reason: ActionStatusReason - isStart: Boolean! observations: String } @@ -63,7 +62,6 @@ type NavActionStatus { startDateTimeUtc: String! status: ActionStatusType! reason: ActionStatusReason - isStart: Boolean! observations: String } diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusForActionTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusForActionTests.kt index 00dc7a168..4106ab3fe 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusForActionTests.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusForActionTests.kt @@ -43,12 +43,6 @@ class GetStatusForActionTests { @Autowired private lateinit var getStatusForAction: GetStatusForAction - // @Test -// fun `execute Should return Unknown when action is null for a mission`() { -// given(this.statusActionsRepository.findAllByMissionId(missionId)).willReturn(null); -// val statusForAction = getStatusForAction.execute(missionId=missionId, actionStartDateTimeUtc=null); -// assertThat(statusForAction).isEqualTo(ActionStatusType.UNKNOWN); -// } @Test fun `execute Should return Unknown when action is empty list for a mission`() { given(this.statusActionsRepository.findAllByMissionId(missionId = 1)).willReturn(listOf()); @@ -56,26 +50,6 @@ class GetStatusForActionTests { assertThat(statusForAction).isEqualTo(ActionStatusType.UNKNOWN); } - @Test - fun `execute Should return Unknown if only finishing actions`() { - val startDatetime = ZonedDateTime.of(2023, 6, 19, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) - val finishingAction = ActionStatusEntity( - id = UUID.randomUUID(), - missionId = missionId, - startDateTimeUtc = startDatetime, - isStart = false, - status = ActionStatusType.UNAVAILABLE, - ) - val actions = listOf(finishingAction) - given(this.statusActionsRepository.findAllByMissionId(missionId = 1)).willReturn(actions.map { - ActionStatusModel.fromActionStatusEntity( - it - ) - }); - val statusForAction = getStatusForAction.execute(missionId = missionId, actionStartDateTimeUtc = startDatetime); - assertThat(statusForAction).isEqualTo(ActionStatusType.UNKNOWN); - } - @Test fun `execute Should return the last action status if the last action is a starting status`() { val startDatetime = ZonedDateTime.of(2023, 6, 19, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) @@ -83,7 +57,6 @@ class GetStatusForActionTests { id = UUID.randomUUID(), missionId = missionId, startDateTimeUtc = startDatetime, - isStart = true, status = ActionStatusType.UNAVAILABLE, ) val actions = listOf(startingAction) @@ -95,31 +68,4 @@ class GetStatusForActionTests { val statusForAction = getStatusForAction.execute(missionId = missionId, actionStartDateTimeUtc = startDatetime); assertThat(statusForAction).isEqualTo(ActionStatusType.UNAVAILABLE); } - - @Test - fun `execute Should return the last started action status if last action is ending but at the same timestamp as a starting status action`() { - val startDatetime = ZonedDateTime.of(2023, 6, 19, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) - val startingAction = ActionStatusEntity( - id = UUID.randomUUID(), - missionId = missionId, - startDateTimeUtc = startDatetime, - isStart = true, - status = ActionStatusType.UNAVAILABLE, - ) - val finishingAction = ActionStatusEntity( - id = UUID.randomUUID(), - missionId = missionId, - startDateTimeUtc = startDatetime, - isStart = false, - status = ActionStatusType.DOCKED, - ) - val actions = listOf(finishingAction, startingAction) - given(this.statusActionsRepository.findAllByMissionId(missionId = 1)).willReturn(actions.map { - ActionStatusModel.fromActionStatusEntity( - it - ) - }); - val statusForAction = getStatusForAction.execute(missionId = missionId, actionStartDateTimeUtc = startDatetime); - assertThat(statusForAction).isEqualTo(startingAction.status); - } } diff --git a/frontend/src/pam/mission/actions/action-status-form.tsx b/frontend/src/pam/mission/actions/action-status-form.tsx index 40bb29477..eb76b3f5e 100644 --- a/frontend/src/pam/mission/actions/action-status-form.tsx +++ b/frontend/src/pam/mission/actions/action-status-form.tsx @@ -1,15 +1,15 @@ import React, { useEffect, useState } from 'react' import { - Accent, - Button, - DatePicker, - Icon, - MultiRadio, - OptionValueType, - Size, - Tag, - Textarea, - THEME + Accent, + Button, + DatePicker, + Icon, + MultiRadio, + OptionValueType, + Size, + Tag, + Textarea, + THEME } from '@mtes-mct/monitor-ui' import { Action, ActionStatus, ActionStatusType, } from '../../../types/action-types' import { Stack } from 'rsuite' @@ -24,171 +24,147 @@ import useAddOrUpdateStatus from "../status/use-add-update-status.tsx"; import useDeleteStatus from "../status/use-delete-status.tsx"; interface ActionStatusFormProps { - action: Action + action: Action } const ActionStatusForm: React.FC = ({action}) => { - const navigate = useNavigate() - const {missionId, actionId} = useParams() + const navigate = useNavigate() + const {missionId, actionId} = useParams() - const {data: navAction, loading, error} = useActionById(actionId, missionId, action.source, action.type) - const [mutateStatus] = useAddOrUpdateStatus() - const [deleteStatus] = useDeleteStatus() + const {data: navAction, loading, error} = useActionById(actionId, missionId, action.source, action.type) + const [mutateStatus] = useAddOrUpdateStatus() + const [deleteStatus] = useDeleteStatus() - const [observationsValue, setObservationsValue] = useState(undefined) + const [observationsValue, setObservationsValue] = useState(undefined) - useEffect(() => { - setObservationsValue(navAction?.data?.observations) - }, [navAction]) + useEffect(() => { + setObservationsValue(navAction?.data?.observations) + }, [navAction]) - if (loading) { - return ( -
Chargement...
- ) - } - if (error) { - return ( -
error
- ) - } - if (navAction) { - const status = navAction?.data as ActionStatus + if (loading) { + return ( +
Chargement...
+ ) + } + if (error) { + return ( +
error
+ ) + } + if (navAction) { + const status = navAction?.data as ActionStatus - const handleObservationsChange = (nextValue?: string) => { - setObservationsValue(nextValue) - } + const handleObservationsChange = (nextValue?: string) => { + setObservationsValue(nextValue) + } - const handleObservationsBlur = async () => { - await onChange('observations', observationsValue) - } + const handleObservationsBlur = async () => { + await onChange('observations', observationsValue) + } - const onChange = async (field: string, value: any) => { - const updatedData = { - missionId: missionId, - ...omit(status, '__typename'), - [field]: value - } - await mutateStatus({variables: {statusAction: updatedData}}) - } + const onChange = async (field: string, value: any) => { + const updatedData = { + missionId: missionId, + ...omit(status, '__typename'), + [field]: value + } + await mutateStatus({variables: {statusAction: updatedData}}) + } - const deleteAction = async () => { - await deleteStatus({ - variables: { - id: action.id! - } - }) - navigate(`/pam/missions/${missionId}`) + const deleteAction = async () => { + await deleteStatus({ + variables: { + id: action.id! } + }) + navigate(`/pam/missions/${missionId}`) + } - return ( -
- - {/* TITLE AND BUTTONS */} - - - - - - - - - - Statut du navire{' '} - {status.startDateTimeUtc && `(${formatDateTimeForFrenchHumans(status.startDateTimeUtc)})`} - - - - - - - - - - - - - - - - - {/* STATUS FIELDS */} - - - - - {mapStatusToText(ActionStatusType[action.status])} - - - - onChange('isStart', nextValue)} - options={[ - { - label: 'Début', - value: true - }, - { - label: 'Fin', - value: false - } - ]} - /> - - - - {/* DATE & REASON FIELDS */} - - - - { - const date = new Date(nextUtcDate) - await onChange('startDateTimeUtc', date.toISOString()) - }} - /> - - - - - - - -