Skip to content

Commit

Permalink
[Mission] display detached reportings in mission form
Browse files Browse the repository at this point in the history
  • Loading branch information
claire2212 committed Oct 17, 2023
1 parent 6f0c8fa commit f3289ea
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.dtos.ReportingDTO
import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.publicapi.outputs.ControlUnitDataOutput
import org.locationtech.jts.geom.Geometry
import java.time.ZonedDateTime
import java.util.UUID

data class AttachedReportingDataOutput(
val id: Int,
Expand Down Expand Up @@ -39,7 +38,6 @@ data class AttachedReportingDataOutput(
val missionId: Int? = null,
val attachedToMissionAtUtc: ZonedDateTime? = null,
val detachedFromMissionAtUtc: ZonedDateTime? = null,
val attachedEnvActionId: UUID? = null,
) {
companion object {
fun fromReportingDTO(
Expand All @@ -51,7 +49,8 @@ data class AttachedReportingDataOutput(
reportingId = dto.reporting.reportingId,
sourceType = dto.reporting.sourceType,
semaphoreId = dto.reporting.semaphoreId,
semaphore = if (dto.semaphore != null) {
semaphore =
if (dto.semaphore != null) {
SemaphoreDataOutput.fromSemaphoreEntity(
dto.semaphore,
)
Expand All @@ -61,10 +60,9 @@ data class AttachedReportingDataOutput(
controlUnitId = dto.reporting.controlUnitId,
controlUnit =
if (dto.controlUnit != null) {
ControlUnitDataOutput
.fromFullControlUnit(
dto.controlUnit,
)
ControlUnitDataOutput.fromFullControlUnit(
dto.controlUnit,
)
} else {
null
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ data class MissionDetachedReportingDataOutput(
) {
companion object {
fun fromReporting(reporting: ReportingEntity): MissionDetachedReportingDataOutput {
requireNotNull(reporting.id) {
"an attached reporting must have an id"
}
requireNotNull(reporting.id) { "an attached reporting must have an id" }
requireNotNull(reporting.reportingId) {
"an attached reporting must have a reportingId"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ VALUES
'COMPANY',
NULL,
'[{"operatorName": "Ma société", "vesselName": "Mr le gérant" }]',
ST_GeomFromText('MULTIPOINT((-4.18759766312331 47.11281269827924))', 4326),
ST_GeomFromText('MULTIPOINT((0.37083333 49.76777778))', 4326),
'Guadeloupe',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'OBSERVATION',
Expand Down Expand Up @@ -172,7 +172,8 @@ INSERT INTO reportings (
mission_id,
attached_to_mission_at_utc,
detached_from_mission_at_utc,
attached_env_action_id)
attached_env_action_id,
open_by)
VALUES
(6,
2300006,
Expand All @@ -183,7 +184,7 @@ VALUES
'COMPANY',
NULL,
'[{"operatorName": "La sociéter", "vesselName": "Héron" }]',
ST_GeomFromText('MULTIPOINT((-4.18759766312331 47.11281269827924))', 4326),
ST_GeomFromText('MULTIPOINT((-1.59695455 43.6569585))', 4326),
'Guadeloupe',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'OBSERVATION',
Expand All @@ -198,7 +199,8 @@ VALUES
34,
now() - INTERVAL '15 minutes',
null,
'b8007c8a-5135-4bc3-816f-c69c7b75d807'
'b8007c8a-5135-4bc3-816f-c69c7b75d807',
'ABC'
),
(7,
2300007,
Expand All @@ -209,7 +211,7 @@ VALUES
'COMPANY',
NULL,
'[{"operatorName": "Good Company", "vesselName": "Mr le gérant" }]',
ST_GeomFromText('MULTIPOINT((-4.18759766312331 47.11281269827924))', 4326),
ST_GeomFromText('MULTIPOINT((-4.18759766312331 47.11281269827924))', 4326),
'NAMO',
'Lorem LoremLorem ipsum dolor sit amet, consectetur adipiscing elit.',
'OBSERVATION',
Expand All @@ -224,7 +226,8 @@ VALUES
34,
now() - INTERVAL '25 minutes',
null,
null
null,
'DEF'
),
(8,
2300008,
Expand All @@ -235,7 +238,7 @@ VALUES
'COMPANY',
NULL,
'[{"operatorName": "Good Company", "vesselName": "Mr le gérant" }]',
ST_GeomFromText('MULTIPOINT((-4.18759766312331 47.11281269827924))', 4326),
ST_GeomFromText('MULTIPOINT((-1.35943753 46.02911873))', 4326),
'NAMO',
'Lorem LoremLorem ipsum dolor sit amet, consectetur adipiscing elit.',
'OBSERVATION',
Expand All @@ -250,7 +253,8 @@ VALUES
38,
now() - INTERVAL '25 minutes',
null,
null
null,
'GHI'
);

SELECT setval('reportings_id_seq', (SELECT max(id) FROM reportings), true);
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/domain/entities/missions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { SeaFrontEnum } from './seaFrontType'

export enum ActionTypeEnum {
CONTROL = 'CONTROL',
DETACHED_REPORTING = 'DETACHED_REPORTING',
NOTE = 'NOTE',
REPORTING = 'REPORTING',
SURVEILLANCE = 'SURVEILLANCE'
Expand Down Expand Up @@ -256,6 +257,8 @@ export type Mission<EnvAction = EnvActionControl | EnvActionSurveillance | EnvAc
attachedReportings: ReportingDetailed[]
closedBy: string
controlUnits: LegacyControlUnit[]
detachedReportingIds?: number[]
detachedReportings?: []
endDateTimeUtc?: string
envActions: EnvAction[]
facade: SeaFrontEnum
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/domain/entities/reporting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ export type ReportingDetailed = Reporting & {
displayedSource: string
}

export type DetachedReporting = {
attachedToMissionAtUtc?: string
detachedFromMissionAtUtc?: string
missionId?: number
reportingId?: number
}

export enum ControlStatusEnum {
CONTROL_TO_BE_DONE = 'CONTROL_TO_BE_DONE',
CONTROL_DONE = 'CONTROL_DONE',
Expand All @@ -64,6 +71,12 @@ export type ReportingForTimeline = Partial<ReportingDetailed> & {
timelineDate: string
}

export type DetachedReportingForTimeline = DetachedReporting & {
action: string
actionType: ActionTypeEnum.DETACHED_REPORTING
timelineDate: string
}

export enum ReportingSourceEnum {
CONTROL_UNIT = 'CONTROL_UNIT',
OTHER = 'OTHER',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export function ReportingCard({
<StatusTag
controlStatus={controlStatus}
isArchived={timeLeft < 0 || isArchived}
isAttachToMission={!!missionId || (missionId && !detachedFromMissionAtUtc)}
isAttachToMission={missionId && !detachedFromMissionAtUtc}
/>
{!isOnlyHoverable && (
<StyledButton Icon={Icon.Edit} onClick={editReporting} size={Size.SMALL}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Icon, THEME } from '@mtes-mct/monitor-ui'
import styled from 'styled-components'

import type { DetachedReportingForTimeline } from '../../../../domain/entities/reporting'

export function ReportingHistory({ action }: { action: DetachedReportingForTimeline }) {
if (action.action === 'attach') {
return (
<>
<Icon.Link color={THEME.color.charcoal} />
<ReportingHistoryContainer>Signalement {action.reportingId} lié de la mission</ReportingHistoryContainer>
</>
)
}

return (
<>
<Icon.Unlink color={THEME.color.charcoal} />
<ReportingHistoryContainer>Signalement {action.reportingId} désaffecté de la mission</ReportingHistoryContainer>
</>
)
}

const ReportingHistoryContainer = styled.div`
color: ${p => p.theme.color.gunMetal};
font-weight: bold;
margin-left: 10px;
`
75 changes: 40 additions & 35 deletions frontend/src/features/missions/MissionForm/ActionCards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import styled from 'styled-components'
import { ControlCard } from './ControlCard'
import { NoteCard } from './NoteCard'
import { ReportingCard } from './ReportingCard'
import { ReportingHistory } from './ReportingHistory'
import { Action, ActionButtons, ActionSummaryWrapper, ButtonsWrapper, Card, TimeLine } from './style'
import { SurveillanceCard } from './SurveillanceCard'
import { ActionTypeEnum, type EnvActionForTimeline } from '../../../../domain/entities/missions'
import { getDateAsLocalizedStringExpanded } from '../../../../utils/getDateAsLocalizedString'

import type { ReportingForTimeline } from '../../../../domain/entities/reporting'
import type { DetachedReportingForTimeline, ReportingForTimeline } from '../../../../domain/entities/reporting'
import type { MouseEventHandler } from 'react'

type ActionCardProps = {
action: EnvActionForTimeline | ReportingForTimeline
action: EnvActionForTimeline | ReportingForTimeline | DetachedReportingForTimeline
duplicateAction: MouseEventHandler
hasError: boolean
removeAction: MouseEventHandler
Expand All @@ -34,40 +35,44 @@ export function ActionCards({
return (
<Action data-cy="action-card" onClick={selectAction}>
<TimeLine>{getDateAsLocalizedStringExpanded(action.timelineDate)}</TimeLine>
<Card>
<ActionSummaryWrapper $hasError={hasError} $selected={selected} $type={action.actionType}>
{action.actionType === ActionTypeEnum.CONTROL && <ControlCard action={action} />}
{action.actionType === ActionTypeEnum.SURVEILLANCE && <SurveillanceCard action={action} />}
{action.actionType === ActionTypeEnum.NOTE && <NoteCard action={action} />}
{action.actionType === ActionTypeEnum.REPORTING && (
<ReportingCard action={action} setCurrentActionIndex={setCurrentActionIndex} />
)}
{action.actionType === ActionTypeEnum.DETACHED_REPORTING ? (
<ReportingHistory action={action} />
) : (
<Card>
<ActionSummaryWrapper $hasError={hasError} $selected={selected} $type={action.actionType}>
{action.actionType === ActionTypeEnum.CONTROL && <ControlCard action={action} />}
{action.actionType === ActionTypeEnum.SURVEILLANCE && <SurveillanceCard action={action} />}
{action.actionType === ActionTypeEnum.NOTE && <NoteCard action={action} />}
{action.actionType === ActionTypeEnum.REPORTING && (
<ReportingCard action={action} setCurrentActionIndex={setCurrentActionIndex} />
)}

{action.actionType !== ActionTypeEnum.REPORTING && (
<ButtonsWrapper>
<ActionButtons>
<IconButton
accent={Accent.TERTIARY}
Icon={Icon.Duplicate}
onClick={duplicateAction}
title="dupliquer"
/>
<IconButton
accent={Accent.TERTIARY}
color={THEME.color.maximumRed}
Icon={Icon.Delete}
onClick={removeAction}
title="supprimer"
/>
</ActionButtons>
{action.actionType === ActionTypeEnum.CONTROL && action.attachedReportingId && (
<StyledTag Icon={Icon.Link}>{`Signalement ${action.formattedReportingId}`}</StyledTag>
)}
</ButtonsWrapper>
)}
</ActionSummaryWrapper>
{hasError && <FieldError>Veuillez compléter les champs manquants dans cette action</FieldError>}
</Card>
{action.actionType !== ActionTypeEnum.REPORTING && (
<ButtonsWrapper>
<ActionButtons>
<IconButton
accent={Accent.TERTIARY}
Icon={Icon.Duplicate}
onClick={duplicateAction}
title="dupliquer"
/>
<IconButton
accent={Accent.TERTIARY}
color={THEME.color.maximumRed}
Icon={Icon.Delete}
onClick={removeAction}
title="supprimer"
/>
</ActionButtons>
{action.actionType === ActionTypeEnum.CONTROL && action.attachedReportingId && (
<StyledTag Icon={Icon.Link}>{`Signalement ${action.formattedReportingId}`}</StyledTag>
)}
</ButtonsWrapper>
)}
</ActionSummaryWrapper>
{hasError && <FieldError>Veuillez compléter les champs manquants dans cette action</FieldError>}
</Card>
)}
</Action>
)
}
Expand Down
19 changes: 12 additions & 7 deletions frontend/src/features/missions/MissionForm/ActionsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@ import { ReactComponent as SurveillanceSVG } from '../../../uiMonitor/icons/Obse
import { ReactComponent as PlusSVG } from '../../../uiMonitor/icons/Plus.svg'
import { actionFactory, getEnvActionsAndReportingsForTimeline } from '../Missions.helpers'

import type { Reporting } from '../../../domain/entities/reporting'
import type { DetachedReporting, Reporting } from '../../../domain/entities/reporting'

export function ActionsForm({ currentActionIndex, setCurrentActionIndex }) {
const { errors, setFieldValue, values } = useFormikContext<Partial<Mission | NewMission>>()

const envActions = values?.envActions as EnvAction[]
const attachedReportings = values?.attachedReportings as Reporting[]
const detachedReportings = values?.detachedReportings as DetachedReporting[]
const isFirstSurveillanceAction = !envActions?.find(action => action.actionType === ActionTypeEnum.SURVEILLANCE)

const actions = useMemo(
() => getEnvActionsAndReportingsForTimeline(envActions, attachedReportings),
[envActions, attachedReportings]
() => getEnvActionsAndReportingsForTimeline(envActions, attachedReportings, detachedReportings),
[envActions, attachedReportings, detachedReportings]
)

const sortedActions = useMemo(
Expand Down Expand Up @@ -116,14 +117,18 @@ export function ActionsForm({ currentActionIndex, setCurrentActionIndex }) {
</TitleWrapper>
<ActionsTimeline>
{sortedActions ? (
sortedActions.map(action => {
const index = envActions?.findIndex(a => a.id === action.id)
sortedActions.map((action, index) => {
const envActionsIndex = envActions?.findIndex(a => a.id === action.id)
const envActionsErrors =
errors?.envActions && index !== undefined && index >= 0 && errors?.envActions[index]
errors?.envActions &&
envActionsIndex !== undefined &&
envActionsIndex >= 0 &&
errors?.envActions[envActionsIndex]

return (
<ActionCards
key={action.id}
// eslint-disable-next-line react/no-array-index-key
key={index}
action={action}
duplicateAction={() => handleDuplicateAction(action.id)}
hasError={!!envActionsErrors}
Expand Down
Loading

0 comments on commit f3289ea

Please sign in to comment.