Skip to content

Commit

Permalink
Redesign mission queue
Browse files Browse the repository at this point in the history
Note that placeholder mission will appear on all robots queues. This needs a larger rewrite to fix and will be solved later.
  • Loading branch information
Eddasol committed Nov 27, 2024
1 parent 434851a commit 69f0bba
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 213 deletions.
13 changes: 1 addition & 12 deletions frontend/src/components/Pages/FrontPage/FrontPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { MissionQueueView } from 'components/Pages/FrontPage/MissionOverview/MissionQueueView'
import { Header } from 'components/Header/Header'
import styled from 'styled-components'
import { InspectionOverviewSection } from 'components/Pages/InspectionPage/InspectionOverview'
Expand All @@ -14,23 +13,13 @@ const StyledFrontPage = styled.div`
background-color: ${tokens.colors.ui.background__light.hex};
`

const HorizontalContent = styled.div`
display: flex;
flex-wrap: wrap;
grid-template-columns: auto auto;
gap: 2rem;
`

export const FrontPage = () => {
return (
<>
<Header page={'frontPage'} />
<StyledFrontPage>
<StopRobotDialog />
<HorizontalContent>
<MissionControlSection />
<MissionQueueView />
</HorizontalContent>
<MissionControlSection />
<InspectionOverviewSection />
</StyledFrontPage>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { tokens } from '@equinor/eds-tokens'
import { OngoingMissionCard, OngoingMissionPlaceholderCard } from './OngoingMissionCard'
import { useMissionsContext } from 'components/Contexts/MissionRunsContext'
import { MissionHistoryButton } from './MissionHistoryButton'
import { Robot } from 'models/Robot'
import { RobotMissionQueueView } from './MissionQueueView'

const MissionControlStyle = styled.div`
display: flex;
Expand All @@ -25,45 +27,43 @@ const MissionControlHeader = styled.div`
gap: 0.5rem;
`

const MissionControlCard = styled(Card)`
const MissionControlCardStyle = styled(Card)`
display: flex;
gap: 0px;
align-items: flex-start;
flex-direction: column;
@media (min-width: 960px) {
flex-direction: row;
width: 960px;
}
@media (max-width: 960px) {
max-width: 669px;
flex-direction: column;
align-self: stretch;
}
`

const OngoingMissionControlCardStyle = styled.div`
display: flex;
gap: 0px;
align-items: flex-start;
@media (min-width: 960px) {
flex-direction: row;
}
@media (max-width: 960px) {
flex-direction: column;
}
`

export const MissionControlSection = (): JSX.Element => {
const { TranslateText } = useLanguageContext()
const { enabledRobots } = useRobotContext()
const { ongoingMissions } = useMissionsContext()

const missionControlCards = enabledRobots.map((robot, index) => {
const ongoingMission = ongoingMissions.find((mission) => mission.robot.id === robot.id)
return (
<MissionControlCard key={index} style={{ boxShadow: tokens.elevation.raised }}>
<RobotCard robot={robot} />
{ongoingMission ? <OngoingMissionCard mission={ongoingMission} /> : <OngoingMissionPlaceholderCard />}
</MissionControlCard>
)
return <MissionControlCard key={index} robot={robot} />
})

const missionControlPlaceholderCard = (
<MissionControlCard style={{ boxShadow: tokens.elevation.raised }}>
<RobotCardPlaceholder />
<OngoingMissionPlaceholderCard />
</MissionControlCard>
)

return (
<MissionControlStyle>
<MissionControlHeader>
Expand All @@ -73,9 +73,40 @@ export const MissionControlSection = (): JSX.Element => {
</MissionControlHeader>
<MissionControlBody>
{enabledRobots.length > 0 && missionControlCards}
{enabledRobots.length === 0 && missionControlPlaceholderCard}
{enabledRobots.length === 0 && <MissionControlPlaceholderCard />}
</MissionControlBody>
<MissionHistoryButton />
</MissionControlStyle>
)
}

const MissionControlCard = ({ robot }: { robot: Robot }) => {
const { ongoingMissions } = useMissionsContext()
const ongoingMission = ongoingMissions.find((mission) => mission.robot.id === robot.id)
return (
<>
<MissionControlCardStyle style={{ boxShadow: tokens.elevation.raised }}>
<OngoingMissionControlCardStyle>
<RobotCard robot={robot} />
{ongoingMission ? (
<OngoingMissionCard mission={ongoingMission} />
) : (
<OngoingMissionPlaceholderCard />
)}
</OngoingMissionControlCardStyle>
<RobotMissionQueueView robot={robot} />
</MissionControlCardStyle>
</>
)
}

const MissionControlPlaceholderCard = () => {
return (
<MissionControlCardStyle style={{ boxShadow: tokens.elevation.raised }}>
<OngoingMissionControlCardStyle>
<RobotCardPlaceholder />
<OngoingMissionPlaceholderCard />
</OngoingMissionControlCardStyle>
</MissionControlCardStyle>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Card, Dialog, Icon, Typography, DotProgress } from '@equinor/eds-core-react'
import { config } from 'config'
import { Mission, placeholderMission } from 'models/Mission'
import { Mission } from 'models/Mission'
import styled from 'styled-components'
import { useNavigate } from 'react-router-dom'
import { useState } from 'react'
Expand All @@ -15,10 +15,6 @@ interface MissionQueueCardProps {
onDeleteMission: (mission: Mission) => void
}

interface MissionDisplayProps {
mission: Mission
}

interface RemoveMissionDialogProps {
confirmDeleteDialogOpen: boolean
mission: Mission
Expand All @@ -27,50 +23,22 @@ interface RemoveMissionDialogProps {
}

const StyledMissionCard = styled(Card)`
display: grid;
width: calc(100vw - 30px);
max-width: 880px;
min-height: 50px;
`
const HorizontalContent = styled.div`
display: grid;
grid-template-columns: 40px auto 50px;
display: flex;
flex-direction: row;
min-height: 48px;
padding: 4px 16px;
justify-content: space-between;
align-items: center;
padding-left: 10px;
align-self: stretch;
border: 1px solid ${tokens.colors.ui.background__medium.hex};
`
const HorizontalNonButtonContent = styled.div`
@media (min-width: 700px) {
display: grid;
grid-template-columns: auto 120px 90px 180px;
align-items: center;
padding: 4px 0px 4px 10px;
gap: 10px;
}

@media (max-width: 700px) {
display: grid;
grid-template: auto auto / 140px auto;
#missionName {
grid-area: 1 / 1 / auto / span 2;
padding-bottom: 10px;
}
#robotName {
grid-area: 2 / 1 / 140px / span 1;
}
#taskProgress {
grid-area: 2 / 2 / auto / span 1;
padding-left: 15px;
}
#estimatedDuration {
display: none;
}
padding: 4px 0px 10px 4px;
}
const StyledBody = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
flex: 1 0 0;
`

const StyledDialogContent = styled.div`
Expand All @@ -87,10 +55,6 @@ const StyledButtonSection = styled.div`
gap: 8px;
`

const PaddingLeft = styled.div`
padding-left: 10px;
`

const CircularCard = styled(Card)`
height: 24px;
width: 24px;
Expand All @@ -104,70 +68,34 @@ const StyledButton = styled(Button)`
height: auto;
`

const EllipsisTypography = styled(Typography)`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`

export const MissionQueueCard = ({ order, mission, onDeleteMission }: MissionQueueCardProps): JSX.Element => {
const { TranslateText } = useLanguageContext()
let navigate = useNavigate()
const routeChange = () => {
const path = `${config.FRONTEND_BASE_ROUTE}/mission/${mission.id}`
navigate(path)
}
const [confirmDeleteDialogOpen, setConfirmDeleteDialogOpen] = useState<boolean>(false)
const fillColor = tokens.colors.infographic.primary__energy_red_21.hex
const numberOfTasks = mission.tasks.length

return (
<StyledMissionCard key={mission.id} style={{ boxShadow: tokens.elevation.raised }}>
<HorizontalContent>
<CircularCard style={{ background: fillColor }}>
<Typography variant="body_short_bold">{order}</Typography>
</CircularCard>
{mission === placeholderMission ? (
<PaddingLeft>
<DotProgress size={48} color="primary" />
</PaddingLeft>
) : (
<>
<HorizontalNonButtonContent onClick={routeChange}>
<div id="missionName">
<StyledButton variant="ghost">
<Typography variant="body_short_bold">{mission.name}</Typography>
</StyledButton>
</div>
<EllipsisTypography
id="robotName"
variant="caption"
color={tokens.colors.text.static_icons__tertiary.hex}
>
{TranslateText('Robot')}: {mission.robot.name}
</EllipsisTypography>
<Typography
id="taskProgress"
variant="caption"
color={tokens.colors.text.static_icons__tertiary.hex}
>
{TranslateText('Tasks')}: {numberOfTasks}
</Typography>
<div id="estimatedDuration">
<MissionDurationDisplay mission={mission} />
</div>
</HorizontalNonButtonContent>
<Button
variant="ghost_icon"
onClick={() => {
setConfirmDeleteDialogOpen(true)
}}
>
<Icon name={Icons.Remove} size={24} title="more action" />
</Button>
</>
)}
</HorizontalContent>
<StyledMissionCard key={mission.id}>
<CircularCard style={{ background: fillColor }}>
<Typography variant="body_short_bold">{order}</Typography>
</CircularCard>
<StyledBody onClick={routeChange}>
<StyledButton variant="ghost">
<Typography variant="body_short_bold">{mission.name}</Typography>
</StyledButton>
<MissionDetails mission={mission} />
</StyledBody>
<Button
variant="ghost_icon"
onClick={() => {
setConfirmDeleteDialogOpen(true)
}}
>
<Icon name={Icons.Remove} size={24} title="more action" />
</Button>
<RemoveMissionDialog
confirmDeleteDialogOpen={confirmDeleteDialogOpen}
mission={mission}
Expand All @@ -178,6 +106,18 @@ export const MissionQueueCard = ({ order, mission, onDeleteMission }: MissionQue
)
}

export const PlaceholderMissionCard = ({ order }: MissionQueueCardProps) => {
const fillColor = tokens.colors.infographic.primary__energy_red_21.hex
return (
<StyledMissionCard style={{ justifyContent: 'start' }}>
<CircularCard style={{ background: fillColor }}>
<Typography variant="body_short_bold">{order}</Typography>
</CircularCard>
<DotProgress size={48} color="primary" />
</StyledMissionCard>
)
}

const RemoveMissionDialog = ({
confirmDeleteDialogOpen,
mission,
Expand Down Expand Up @@ -221,30 +161,32 @@ const RemoveMissionDialog = ({
)
}

const MissionDurationDisplay = ({ mission }: MissionDisplayProps) => {
const MissionDetails = ({ mission }: { mission: Mission }) => {
const { TranslateText } = useLanguageContext()
const translateEstimatedDuration = TranslateText('Estimated duration')
const translateH = TranslateText('h')
const translateMin = TranslateText('min')
const translateNotAvailable = TranslateText('Estimated duration: not available')

if (mission.estimatedDuration) {
const hours = Math.floor(mission.estimatedDuration / 3600)
const remainingSeconds = mission.estimatedDuration % 3600
const minutes = Math.ceil(remainingSeconds / 60)

return (
<Typography variant="caption" color={tokens.colors.text.static_icons__tertiary.hex}>
{translateEstimatedDuration}: {hours}
{translateH} {minutes}
{translateMin}
</Typography>
)

const estimatedDuration = () => {
const translateEstimatedDuration = TranslateText('Estimated duration')
const translateH = TranslateText('h')
const translateMin = TranslateText('min')
const translateNotAvailable = TranslateText('Estimated duration: not available')
if (mission.estimatedDuration !== undefined) {
const hours = Math.floor(mission.estimatedDuration / 3600)
const remainingSeconds = mission.estimatedDuration % 3600
const minutes = Math.ceil(remainingSeconds / 60)

return `${translateEstimatedDuration}: ${hours} ${translateH} ${minutes} ${translateMin}`
}
console.log(mission.estimatedDuration)
return translateNotAvailable
}

const area = `${TranslateText('Area')}: ${mission.area?.areaName ?? TranslateText('Not available')}`
const tasks = `${TranslateText('Tasks')}: ${mission.tasks.length}`
const missionDetails = `${area} | ${tasks} | ${estimatedDuration()}`

return (
<Typography variant="caption" color={tokens.colors.text.static_icons__tertiary.hex}>
{translateNotAvailable}
{missionDetails}
</Typography>
)
}
Loading

0 comments on commit 69f0bba

Please sign in to comment.