Skip to content

Commit

Permalink
Split alert banner and list context
Browse files Browse the repository at this point in the history
  • Loading branch information
haakonsf committed Aug 21, 2024
1 parent b488a57 commit 15e7781
Show file tree
Hide file tree
Showing 27 changed files with 395 additions and 109 deletions.
34 changes: 19 additions & 15 deletions frontend/src/components/Alerts/AlertsListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const StyledListItem = styled(outline)`
display: flex;
flex-direction: column;
align-items: left;
row-gap: 10px
row-gap: 10px;
`

const VerticalContent = styled.div`
Expand All @@ -55,9 +55,9 @@ const Horizontal = styled.div`
`

const Right = styled.div`
display: flex;
align-items: right;
justify-content: right;
display: flex;
align-items: right;
justify-content: right;
`

export enum AlertCategory {
Expand All @@ -82,34 +82,38 @@ interface AlertProps {

export const AlertListContents = ({ icon, iconColor, alertTitle, alertText, mission }: AlertListInfo) => {
let missionHasFailedTasks = false
if (mission !== undefined) missionHasFailedTasks = mission.tasks.some(
(t) => t.status !== TaskStatus.PartiallySuccessful && t.status !== TaskStatus.Successful
)
if (mission !== undefined)
missionHasFailedTasks = mission.tasks.some(
(t) => t.status !== TaskStatus.PartiallySuccessful && t.status !== TaskStatus.Successful
)
return (
<>
<StyledListContainer>
<StyledListHeading>
<VerticalContent>
<StyledIcon name={icon} style={{ color: iconColor }} />
<Typography variant='h6'>{alertTitle}</Typography>
<Typography variant="h6">{alertTitle}</Typography>
</VerticalContent>
</StyledListHeading>
<StyledListItem>
<Typography variant='caption'>{alertText}</Typography>
<Typography variant="caption">{alertText}</Typography>
<Right>
{mission !== undefined && mission.tasks[0]?.type !== TaskType.ReturnHome && mission.tasks[0]?.type !== TaskType.Localization && (
<MissionRestartButton mission={mission} hasFailedTasks={missionHasFailedTasks} smallButton={false} />
)}
{mission !== undefined &&
mission.tasks[0]?.type !== TaskType.ReturnHome &&
mission.tasks[0]?.type !== TaskType.Localization && (
<MissionRestartButton
mission={mission}
hasFailedTasks={missionHasFailedTasks}
smallButton={false}
/>
)}
</Right>
</StyledListItem>
</StyledListContainer>
</>
)
}




export const AlertListItem = ({ children, dismissAlert }: AlertProps) => {
return (
<>
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/components/Alerts/BlockedRobotAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,16 @@ export const BlockedRobotAlertContent = ({ robotNames }: AlertProps) => {

export const BlockedRobotAlertListContent = ({ robotNames }: AlertProps) => {
const { TranslateText } = useLanguageContext()
let message =
`${TranslateText('The robot')} ${robotNames[0]} ${TranslateText('is blocked and cannot perform tasks')}.`
let message = `${TranslateText('The robot')} ${robotNames[0]} ${TranslateText('is blocked and cannot perform tasks')}.`

if (robotNames.length > 1) message = `${TranslateText('Several robots are blocked and cannot perform tasks')}.`

return (
<AlertListContents icon={Icons.Warning} iconColor={tokens.colors.interactive.danger__resting.rgba} alertTitle={TranslateText('Robot is blocked')} alertText={message} />
<AlertListContents
icon={Icons.Warning}
iconColor={tokens.colors.interactive.danger__resting.rgba}
alertTitle={TranslateText('Robot is blocked')}
alertText={message}
/>
)
}
12 changes: 6 additions & 6 deletions frontend/src/components/Alerts/FailedAlertContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ const Indent = styled.div`
padding: 0px 9px;
`





export const FailedAlertContent = ({ title, message }: { title: string; message: string }) => {
const { TranslateText } = useLanguageContext()
const iconColor = tokens.colors.interactive.danger__resting.rgba
Expand All @@ -44,10 +40,14 @@ export const FailedAlertContent = ({ title, message }: { title: string; message:
)
}


export const FailedAlertListContent = ({ title, message }: { title: string; message: string }) => {
const { TranslateText } = useLanguageContext()
return (
<AlertListContents icon={Icons.Failed} iconColor={tokens.colors.interactive.danger__resting.rgba} alertTitle={TranslateText(title)} alertText={TranslateText(message)} />
<AlertListContents
icon={Icons.Failed}
iconColor={tokens.colors.interactive.danger__resting.rgba}
alertTitle={TranslateText(title)}
alertText={TranslateText(message)}
/>
)
}
23 changes: 19 additions & 4 deletions frontend/src/components/Alerts/FailedMissionAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,24 @@ export const FailedMissionAlertListContent = ({ missions }: MissionsProps) => {
const { TranslateText } = useLanguageContext()
const mission = missions[0]
let message = `${mission.name} ${TranslateText('failed on robot')} ${mission.robot.name}: ${mission.statusReason}`
if (mission.statusReason === null) message = `${mission.name} ${TranslateText('failed on robot')} ${mission.robot.name}`
if (missions.length > 1) message = `${missions.length.toString()} ${TranslateText("missions failed recently. See 'Mission History' for more information.")}.`
return (
<AlertListContents icon={Icons.Failed} alertTitle={TranslateText(MissionStatus.Failed)} alertText={message} iconColor={tokens.colors.interactive.danger__resting.rgba} mission={mission} />
if (mission.statusReason === null)
message = `${mission.name} ${TranslateText('failed on robot')} ${mission.robot.name}`
if (missions.length > 1)
message = `${missions.length.toString()} ${TranslateText("missions failed recently. See 'Mission History' for more information.")}.`
return missions.length === 1 ? (
<AlertListContents
icon={Icons.Failed}
alertTitle={TranslateText(MissionStatus.Failed)}
alertText={message}
iconColor={tokens.colors.interactive.danger__resting.rgba}
mission={mission}
/>
) : (
<AlertListContents
icon={Icons.Failed}
alertTitle={TranslateText(MissionStatus.Failed)}
alertText={message}
iconColor={tokens.colors.interactive.danger__resting.rgba}
/>
)
}
7 changes: 6 additions & 1 deletion frontend/src/components/Alerts/FailedRequestAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export const FailedRequestAlertContent = ({ translatedMessage }: { translatedMes
export const FailedRequestAlertListContent = ({ translatedMessage }: { translatedMessage: string }) => {
const { TranslateText } = useLanguageContext()
return (
<AlertListContents icon={Icons.Failed} alertTitle={translatedMessage} alertText={TranslateText('Request error')} iconColor={tokens.colors.interactive.danger__resting.rgba} />
<AlertListContents
icon={Icons.Failed}
alertTitle={TranslateText('Request error')}
alertText={translatedMessage}
iconColor={tokens.colors.interactive.danger__resting.rgba}
/>
)
}
19 changes: 13 additions & 6 deletions frontend/src/components/Alerts/SafeZoneAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,25 @@ export const SafeZoneAlertContent = ({ alertType, alertCategory }: SafeZoneBanne
)
}


export const SafeZoneAlertListContent = ({ alertType, alertCategory }: SafeZoneBannerProps): JSX.Element => {
const { TranslateText } = useLanguageContext()
let titleMessage = TranslateText('INFO')
let message = TranslateText('Safe zone banner text')
let icon = Icons.Warning
let iconColor = tokens.colors.interactive.danger__resting.rgba
if (alertCategory === AlertCategory.WARNING) titleMessage = TranslateText('WARNING')
if (alertCategory === AlertCategory.INFO && alertType === AlertType.SafeZoneSuccess) [message, icon, iconColor] = [TranslateText('Safe Zone successful text'), Icons.Info, tokens.colors.text.static_icons__default.rgba]
if (alertCategory === AlertCategory.INFO && alertType === AlertType.DismissSafeZone) [message, icon, iconColor] = [TranslateText('Dismiss safe zone banner text'), Icons.Info, tokens.colors.text.static_icons__default.rgba]
if (alertCategory === AlertCategory.INFO && alertType === AlertType.SafeZoneSuccess)
[message, icon, iconColor] = [
TranslateText('Safe Zone successful text'),
Icons.Info,
tokens.colors.text.static_icons__default.rgba,
]
if (alertCategory === AlertCategory.INFO && alertType === AlertType.DismissSafeZone)
[message, icon, iconColor] = [
TranslateText('Dismiss safe zone banner text'),
Icons.Info,
tokens.colors.text.static_icons__default.rgba,
]

return (
<AlertListContents icon={icon} alertTitle={titleMessage} alertText={message} iconColor={iconColor} />
)
return <AlertListContents icon={icon} alertTitle={titleMessage} alertText={message} iconColor={iconColor} />
}
90 changes: 79 additions & 11 deletions frontend/src/components/Contexts/AlertContext.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { createContext, FC, ReactNode, useContext, useEffect, useState } from 'react'
import { addMinutes, max } from 'date-fns'
import { Mission, MissionStatus } from 'models/Mission'
import { FailedMissionAlertListContent } from 'components/Alerts/FailedMissionAlert'
import { FailedMissionAlertContent, FailedMissionAlertListContent } from 'components/Alerts/FailedMissionAlert'
import { BackendAPICaller } from 'api/ApiCaller'
import { SignalREventLabels, useSignalRContext } from './SignalRContext'
import { useInstallationContext } from './InstallationContext'
import { Alert } from 'models/Alert'
import { useRobotContext } from './RobotContext'
import { BlockedRobotAlertListContent } from 'components/Alerts/BlockedRobotAlert'
import { BlockedRobotAlertContent, BlockedRobotAlertListContent } from 'components/Alerts/BlockedRobotAlert'
import { RobotStatus } from 'models/Robot'
import { FailedAlertListContent } from 'components/Alerts/FailedAlertContent'
import { FailedAlertContent, FailedAlertListContent } from 'components/Alerts/FailedAlertContent'
import { convertUTCDateToLocalDate } from 'utils/StringFormatting'
import { AlertCategory } from 'components/Alerts/AlertsBanner'
import { SafeZoneAlertListContent } from 'components/Alerts/SafeZoneAlert'
import { SafeZoneAlertContent, SafeZoneAlertListContent } from 'components/Alerts/SafeZoneAlert'
import { useLanguageContext } from './LanguageContext'
import { FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert'
import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert'

export enum AlertType {
MissionFail,
Expand All @@ -41,6 +41,10 @@ interface IAlertContext {
setAlert: (source: AlertType, alert: ReactNode, category: AlertCategory) => void
clearAlerts: () => void
clearAlert: (source: AlertType) => void
listAlerts: AlertDictionaryType
setListAlert: (source: AlertType, listAlert: ReactNode, category: AlertCategory) => void
clearListAlerts: () => void
clearListAlert: (source: AlertType) => void
}

interface Props {
Expand All @@ -49,15 +53,20 @@ interface Props {

const defaultAlertInterface = {
alerts: {},
setAlert: (source: AlertType, alert: ReactNode, category: AlertCategory) => { },
clearAlerts: () => { },
clearAlert: (source: AlertType) => { },
setAlert: (source: AlertType, alert: ReactNode, category: AlertCategory) => {},
clearAlerts: () => {},
clearAlert: (source: AlertType) => {},
listAlerts: {},
setListAlert: (source: AlertType, alert: ReactNode, category: AlertCategory) => {},
clearListAlerts: () => {},
clearListAlert: (source: AlertType) => {},
}

export const AlertContext = createContext<IAlertContext>(defaultAlertInterface)

export const AlertProvider: FC<Props> = ({ children }) => {
const [alerts, setAlerts] = useState<AlertDictionaryType>(defaultAlertInterface.alerts)
const [listAlerts, setListAlerts] = useState<AlertDictionaryType>(defaultAlertInterface.listAlerts)
const [recentFailedMissions, setRecentFailedMissions] = useState<Mission[]>([])
const [blockedRobotNames, setBlockedRobotNames] = useState<string[]>([])
const { registerEvent, connectionReady } = useSignalRContext()
Expand Down Expand Up @@ -94,6 +103,32 @@ export const AlertProvider: FC<Props> = ({ children }) => {
})
}

const setListAlert = (source: AlertType, listAlert: ReactNode, category: AlertCategory) => {
setListAlerts((oldListAlerts) => {
return {
...oldListAlerts,
[source]: {
content: listAlert,
dismissFunction: () => clearListAlert(source),
alertCategory: category,
},
}
})
}

const clearListAlerts = () => setAlerts({})

const clearListAlert = (source: AlertType) => {
if (source === AlertType.MissionFail)
sessionStorage.setItem(dismissMissionFailTimeKey, JSON.stringify(Date.now()))

setListAlerts((oldListAlerts) => {
let newListAlerts = { ...oldListAlerts }
delete newListAlerts[source]
return newListAlerts
})
}

const getLastDismissalTime = (): Date => {
const sessionValue = sessionStorage.getItem(dismissMissionFailTimeKey)
if (sessionValue === null || sessionValue === '') {
Expand Down Expand Up @@ -124,6 +159,13 @@ export const AlertProvider: FC<Props> = ({ children }) => {
})
.catch((e) => {
setAlert(
AlertType.RequestFail,
<FailedRequestAlertContent
translatedMessage={TranslateText('Failed to retrieve failed missions')}
/>,
AlertCategory.ERROR
)
setListAlert(
AlertType.RequestFail,
<FailedRequestAlertListContent
translatedMessage={TranslateText('Failed to retrieve failed missions')}
Expand All @@ -148,7 +190,7 @@ export const AlertProvider: FC<Props> = ({ children }) => {
installationCode &&
(!newFailedMission.installationCode ||
newFailedMission.installationCode.toLocaleLowerCase() !==
installationCode.toLocaleLowerCase())
installationCode.toLocaleLowerCase())
)
return failedMissions // Ignore missions for other installations
// Ignore missions shortly after the user dismissed the last one
Expand All @@ -174,12 +216,23 @@ export const AlertProvider: FC<Props> = ({ children }) => {
if (alertType === AlertType.SafeZoneSuccess) {
setAlert(
alertType,
<SafeZoneAlertListContent alertType={alertType} alertCategory={AlertCategory.INFO} />,
<SafeZoneAlertContent alertType={alertType} alertCategory={AlertCategory.INFO} />,
AlertCategory.INFO
)
clearAlert(AlertType.RequestSafeZone)
setListAlert(
alertType,
<SafeZoneAlertListContent alertType={alertType} alertCategory={AlertCategory.INFO} />,
AlertCategory.INFO
)
clearListAlert(AlertType.RequestSafeZone)
} else {
setAlert(
alertType,
<FailedAlertContent title={backendAlert.alertTitle} message={backendAlert.alertMessage} />,
AlertCategory.ERROR
)
setListAlert(
alertType,
<FailedAlertListContent title={backendAlert.alertTitle} message={backendAlert.alertMessage} />,
AlertCategory.ERROR
Expand All @@ -193,6 +246,11 @@ export const AlertProvider: FC<Props> = ({ children }) => {
useEffect(() => {
if (newFailedMissions.length > 0) {
setAlert(
AlertType.MissionFail,
<FailedMissionAlertContent missions={newFailedMissions} />,
AlertCategory.ERROR
)
setListAlert(
AlertType.MissionFail,
<FailedMissionAlertListContent missions={newFailedMissions} />,
AlertCategory.ERROR
Expand All @@ -207,7 +265,7 @@ export const AlertProvider: FC<Props> = ({ children }) => {
.filter(
(robot) =>
robot.currentInstallation.installationCode.toLocaleLowerCase() ===
installationCode.toLocaleLowerCase() && robot.status === RobotStatus.Blocked
installationCode.toLocaleLowerCase() && robot.status === RobotStatus.Blocked
)
.map((robot) => robot.name!)

Expand All @@ -218,12 +276,18 @@ export const AlertProvider: FC<Props> = ({ children }) => {
if (isBlockedRobotNamesModifyed) {
if (newBlockedRobotNames.length > 0) {
setAlert(
AlertType.BlockedRobot,
<BlockedRobotAlertContent robotNames={newBlockedRobotNames} />,
AlertCategory.WARNING
)
setListAlert(
AlertType.BlockedRobot,
<BlockedRobotAlertListContent robotNames={newBlockedRobotNames} />,
AlertCategory.WARNING
)
} else {
clearAlert(AlertType.BlockedRobot)
clearListAlert(AlertType.BlockedRobot)
}
}
setBlockedRobotNames(newBlockedRobotNames)
Expand All @@ -237,6 +301,10 @@ export const AlertProvider: FC<Props> = ({ children }) => {
setAlert,
clearAlerts,
clearAlert,
listAlerts,
setListAlert,
clearListAlerts,
clearListAlert,
}}
>
{children}
Expand Down
Loading

0 comments on commit 15e7781

Please sign in to comment.