diff --git a/frontend/src/components/Alerts/AlertsListItem.tsx b/frontend/src/components/Alerts/AlertsListItem.tsx index 8b26e520d..90a0264fa 100644 --- a/frontend/src/components/Alerts/AlertsListItem.tsx +++ b/frontend/src/components/Alerts/AlertsListItem.tsx @@ -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` @@ -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 { @@ -82,24 +82,31 @@ 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 ( <> - {alertTitle} + {alertTitle} - {alertText} + {alertText} - {mission !== undefined && mission.tasks[0]?.type !== TaskType.ReturnHome && mission.tasks[0]?.type !== TaskType.Localization && ( - - )} + {mission !== undefined && + mission.tasks[0]?.type !== TaskType.ReturnHome && + mission.tasks[0]?.type !== TaskType.Localization && ( + + )} @@ -107,9 +114,6 @@ export const AlertListContents = ({ icon, iconColor, alertTitle, alertText, miss ) } - - - export const AlertListItem = ({ children, dismissAlert }: AlertProps) => { return ( <> diff --git a/frontend/src/components/Alerts/BlockedRobotAlert.tsx b/frontend/src/components/Alerts/BlockedRobotAlert.tsx index 679ebfc93..bab3647a1 100644 --- a/frontend/src/components/Alerts/BlockedRobotAlert.tsx +++ b/frontend/src/components/Alerts/BlockedRobotAlert.tsx @@ -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 ( - + ) } diff --git a/frontend/src/components/Alerts/FailedAlertContent.tsx b/frontend/src/components/Alerts/FailedAlertContent.tsx index f2c7e343e..03b09ab20 100644 --- a/frontend/src/components/Alerts/FailedAlertContent.tsx +++ b/frontend/src/components/Alerts/FailedAlertContent.tsx @@ -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 @@ -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 ( - + ) } diff --git a/frontend/src/components/Alerts/FailedMissionAlert.tsx b/frontend/src/components/Alerts/FailedMissionAlert.tsx index f48d492cb..b5868f7dd 100644 --- a/frontend/src/components/Alerts/FailedMissionAlert.tsx +++ b/frontend/src/components/Alerts/FailedMissionAlert.tsx @@ -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 ( - + 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 ? ( + + ) : ( + ) } diff --git a/frontend/src/components/Alerts/FailedRequestAlert.tsx b/frontend/src/components/Alerts/FailedRequestAlert.tsx index e6bc10c5d..8d7983719 100644 --- a/frontend/src/components/Alerts/FailedRequestAlert.tsx +++ b/frontend/src/components/Alerts/FailedRequestAlert.tsx @@ -38,6 +38,11 @@ export const FailedRequestAlertContent = ({ translatedMessage }: { translatedMes export const FailedRequestAlertListContent = ({ translatedMessage }: { translatedMessage: string }) => { const { TranslateText } = useLanguageContext() return ( - + ) } diff --git a/frontend/src/components/Alerts/SafeZoneAlert.tsx b/frontend/src/components/Alerts/SafeZoneAlert.tsx index e3f20216a..c01d1dad1 100644 --- a/frontend/src/components/Alerts/SafeZoneAlert.tsx +++ b/frontend/src/components/Alerts/SafeZoneAlert.tsx @@ -50,7 +50,6 @@ export const SafeZoneAlertContent = ({ alertType, alertCategory }: SafeZoneBanne ) } - export const SafeZoneAlertListContent = ({ alertType, alertCategory }: SafeZoneBannerProps): JSX.Element => { const { TranslateText } = useLanguageContext() let titleMessage = TranslateText('INFO') @@ -58,10 +57,18 @@ export const SafeZoneAlertListContent = ({ alertType, alertCategory }: SafeZoneB 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 ( - - ) + return } diff --git a/frontend/src/components/Contexts/AlertContext.tsx b/frontend/src/components/Contexts/AlertContext.tsx index 34c4648b7..41382ffca 100644 --- a/frontend/src/components/Contexts/AlertContext.tsx +++ b/frontend/src/components/Contexts/AlertContext.tsx @@ -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, @@ -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 { @@ -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(defaultAlertInterface) export const AlertProvider: FC = ({ children }) => { const [alerts, setAlerts] = useState(defaultAlertInterface.alerts) + const [listAlerts, setListAlerts] = useState(defaultAlertInterface.listAlerts) const [recentFailedMissions, setRecentFailedMissions] = useState([]) const [blockedRobotNames, setBlockedRobotNames] = useState([]) const { registerEvent, connectionReady } = useSignalRContext() @@ -94,6 +103,32 @@ export const AlertProvider: FC = ({ 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 === '') { @@ -124,6 +159,13 @@ export const AlertProvider: FC = ({ children }) => { }) .catch((e) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, = ({ 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 @@ -174,12 +216,23 @@ export const AlertProvider: FC = ({ children }) => { if (alertType === AlertType.SafeZoneSuccess) { setAlert( alertType, - , + , AlertCategory.INFO ) clearAlert(AlertType.RequestSafeZone) + setListAlert( + alertType, + , + AlertCategory.INFO + ) + clearListAlert(AlertType.RequestSafeZone) } else { setAlert( + alertType, + , + AlertCategory.ERROR + ) + setListAlert( alertType, , AlertCategory.ERROR @@ -193,6 +246,11 @@ export const AlertProvider: FC = ({ children }) => { useEffect(() => { if (newFailedMissions.length > 0) { setAlert( + AlertType.MissionFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.MissionFail, , AlertCategory.ERROR @@ -207,7 +265,7 @@ export const AlertProvider: FC = ({ children }) => { .filter( (robot) => robot.currentInstallation.installationCode.toLocaleLowerCase() === - installationCode.toLocaleLowerCase() && robot.status === RobotStatus.Blocked + installationCode.toLocaleLowerCase() && robot.status === RobotStatus.Blocked ) .map((robot) => robot.name!) @@ -218,12 +276,18 @@ export const AlertProvider: FC = ({ children }) => { if (isBlockedRobotNamesModifyed) { if (newBlockedRobotNames.length > 0) { setAlert( + AlertType.BlockedRobot, + , + AlertCategory.WARNING + ) + setListAlert( AlertType.BlockedRobot, , AlertCategory.WARNING ) } else { clearAlert(AlertType.BlockedRobot) + clearListAlert(AlertType.BlockedRobot) } } setBlockedRobotNames(newBlockedRobotNames) @@ -237,6 +301,10 @@ export const AlertProvider: FC = ({ children }) => { setAlert, clearAlerts, clearAlert, + listAlerts, + setListAlert, + clearListAlerts, + clearListAlert, }} > {children} diff --git a/frontend/src/components/Contexts/InstallationContext.tsx b/frontend/src/components/Contexts/InstallationContext.tsx index 45ee62cdb..ec95e7c5c 100644 --- a/frontend/src/components/Contexts/InstallationContext.tsx +++ b/frontend/src/components/Contexts/InstallationContext.tsx @@ -6,7 +6,7 @@ import { SignalREventLabels, useSignalRContext } from './SignalRContext' import { Area } from 'models/Area' import { useLanguageContext } from './LanguageContext' import { AlertType, useAlertContext } from './AlertContext' -import { FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' interface IInstallationContext { @@ -34,7 +34,7 @@ const defaultInstallation = { installationName: '', installationDecks: [], installationAreas: [], - switchInstallation: (selectedInstallation: string) => { }, + switchInstallation: (selectedInstallation: string) => {}, } export const InstallationContext = createContext(defaultInstallation) @@ -42,7 +42,7 @@ export const InstallationContext = createContext(defaultIn export const InstallationProvider: FC = ({ children }) => { const { registerEvent, connectionReady } = useSignalRContext() const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const [allPlantsMap, setAllPlantsMap] = useState>(new Map()) const [installationName, setInstallationName] = useState( window.localStorage.getItem('installationName') || '' @@ -60,6 +60,13 @@ export const InstallationProvider: FC = ({ children }) => { }) .catch((e) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, = ({ children }) => { ) .catch((e) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, = ({ children }) => { }) .catch((e) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, { }, + updateRobotMissionState: (newState: MissionStatusRequest, robotId: string) => {}, } export const MissionControlContext = createContext(defaultMissionControlInterface) @@ -33,7 +33,7 @@ const defaultManagementState: IMissionControlState = { export const MissionControlProvider: FC = ({ children }) => { const { TranslateText } = useLanguageContext() const { enabledRobots } = useRobotContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const [missionControlState, setMissionControlState] = useState(defaultManagementState) const setIsWaitingForResponse = (robotId: string, isWaiting: boolean) => { @@ -46,6 +46,13 @@ export const MissionControlProvider: FC = ({ children }) => { const robot = enabledRobots.find((r) => r.id === robotId) if (!robot) { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, = ({ children }) => { setIsWaitingForResponse(robotId, true) BackendAPICaller.pauseMission(robotId) .then((_) => setIsWaitingForResponse(robotId, false)) - .catch((_) => + .catch((_) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, , AlertCategory.ERROR ) - ) + }) break } case MissionStatusRequest.Resume: { setIsWaitingForResponse(robotId, true) BackendAPICaller.resumeMission(robotId) .then((_) => setIsWaitingForResponse(robotId, false)) - .catch((_) => + .catch((_) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, , AlertCategory.ERROR ) - ) + }) break } case MissionStatusRequest.Stop: { setIsWaitingForResponse(robotId, true) BackendAPICaller.stopMission(robotId) .then((_) => setIsWaitingForResponse(robotId, false)) - .catch((_) => + .catch((_) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, , AlertCategory.ERROR ) - ) + }) break } } diff --git a/frontend/src/components/Contexts/MissionDefinitionsContext.tsx b/frontend/src/components/Contexts/MissionDefinitionsContext.tsx index 0bf54ad80..7d57636b8 100644 --- a/frontend/src/components/Contexts/MissionDefinitionsContext.tsx +++ b/frontend/src/components/Contexts/MissionDefinitionsContext.tsx @@ -5,7 +5,7 @@ import { CondensedMissionDefinition } from 'models/MissionDefinition' import { useInstallationContext } from './InstallationContext' import { useLanguageContext } from './LanguageContext' import { AlertType, useAlertContext } from './AlertContext' -import { FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' interface IMissionDefinitionsContext { @@ -48,7 +48,7 @@ export const useMissionDefinitions = (): IMissionDefinitionsContext => { const { registerEvent, connectionReady } = useSignalRContext() const { installationCode } = useInstallationContext() const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() useEffect(() => { if (connectionReady) { @@ -86,6 +86,13 @@ export const useMissionDefinitions = (): IMissionDefinitionsContext => { orderBy: 'InstallationCode installationCode', }).catch((e) => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, | ((mission: Set) => Set)) => { }, + setLoadingMissionSet: (newLoadingMissionSet: Set | ((mission: Set) => Set)) => {}, } export const MissionRunsContext = createContext(defaultMissionRunsContext) @@ -82,7 +82,7 @@ export const useMissionRuns = (): IMissionRunsContext => { const [loadingMissionSet, setLoadingMissionSet] = useState>(new Set()) const { registerEvent, connectionReady } = useSignalRContext() const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const { installationCode } = useInstallationContext() useEffect(() => { @@ -134,7 +134,14 @@ export const useMissionRuns = (): IMissionRunsContext => { }).catch((e) => { setAlert( AlertType.RequestFail, - , + , + AlertCategory.ERROR + ) + setListAlert( + AlertType.RequestFail, + , AlertCategory.ERROR ) }) @@ -148,7 +155,14 @@ export const useMissionRuns = (): IMissionRunsContext => { }).catch((e) => { setAlert( AlertType.RequestFail, - , + , + AlertCategory.ERROR + ) + setListAlert( + AlertType.RequestFail, + , AlertCategory.ERROR ) }) diff --git a/frontend/src/components/Contexts/RobotContext.tsx b/frontend/src/components/Contexts/RobotContext.tsx index 9b0c83c5b..6c3c6da0c 100644 --- a/frontend/src/components/Contexts/RobotContext.tsx +++ b/frontend/src/components/Contexts/RobotContext.tsx @@ -4,7 +4,7 @@ import { Robot } from 'models/Robot' import { SignalREventLabels, useSignalRContext } from './SignalRContext' import { useLanguageContext } from './LanguageContext' import { AlertType, useAlertContext } from './AlertContext' -import { FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' import { useInstallationContext } from './InstallationContext' @@ -34,7 +34,7 @@ export const RobotProvider: FC = ({ children }) => { const [enabledRobots, setEnabledRobots] = useState(defaultRobotState.enabledRobots) const { registerEvent, connectionReady } = useSignalRContext() const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const { installationCode } = useInstallationContext() useEffect(() => { @@ -81,7 +81,14 @@ export const RobotProvider: FC = ({ children }) => { .catch((e) => { setAlert( AlertType.RequestFail, - , + , + AlertCategory.ERROR + ) + setListAlert( + AlertType.RequestFail, + , AlertCategory.ERROR ) }) diff --git a/frontend/src/components/Contexts/SafeZoneContext.tsx b/frontend/src/components/Contexts/SafeZoneContext.tsx index 1ad6a04f9..c9b44f724 100644 --- a/frontend/src/components/Contexts/SafeZoneContext.tsx +++ b/frontend/src/components/Contexts/SafeZoneContext.tsx @@ -1,9 +1,10 @@ import { createContext, FC, useContext, useEffect, useState } from 'react' import { useRobotContext } from './RobotContext' -import { AlertType, useAlertContext } from './AlertContext' -import { SafeZoneAlertListContent } from 'components/Alerts/SafeZoneAlert' +import { AlertType } from './AlertContext' +import { SafeZoneAlertContent, SafeZoneAlertListContent } from 'components/Alerts/SafeZoneAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' import { RobotFlotillaStatus } from 'models/Robot' +import { useAlertContext } from './AlertContext' interface ISafeZoneContext { safeZoneStatus: boolean @@ -22,7 +23,7 @@ export const SafeZoneContext = createContext(defaultSafeZoneIn export const SafeZoneProvider: FC = ({ children }) => { const [safeZoneStatus, setSafeZoneStatus] = useState(defaultSafeZoneInterface.safeZoneStatus) const { enabledRobots } = useRobotContext() - const { setAlert, clearAlert } = useAlertContext() + const { setAlert, clearAlert, setListAlert, clearListAlert } = useAlertContext() useEffect(() => { const missionQueueFozenStatus = enabledRobots.filter( @@ -31,21 +32,37 @@ export const SafeZoneProvider: FC = ({ children }) => { if (missionQueueFozenStatus.length > 0 && safeZoneStatus === false) { setSafeZoneStatus((oldStatus) => !oldStatus) + clearListAlert(AlertType.DismissSafeZone) clearAlert(AlertType.DismissSafeZone) + setListAlert( + AlertType.RequestSafeZone, + , + AlertCategory.WARNING + ) setAlert( AlertType.RequestSafeZone, - , + , AlertCategory.WARNING ) } else if (missionQueueFozenStatus.length === 0 && safeZoneStatus === true) { setSafeZoneStatus((oldStatus) => !oldStatus) + clearListAlert(AlertType.RequestSafeZone) + clearListAlert(AlertType.SafeZoneSuccess) clearAlert(AlertType.RequestSafeZone) clearAlert(AlertType.SafeZoneSuccess) - setAlert( + setListAlert( AlertType.DismissSafeZone, , AlertCategory.INFO ) + setAlert( + AlertType.DismissSafeZone, + , + AlertCategory.INFO + ) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [enabledRobots]) diff --git a/frontend/src/components/Displays/MissionButtons/MissionRestartButton.tsx b/frontend/src/components/Displays/MissionButtons/MissionRestartButton.tsx index 56f6d7ead..82a9c47d5 100644 --- a/frontend/src/components/Displays/MissionButtons/MissionRestartButton.tsx +++ b/frontend/src/components/Displays/MissionButtons/MissionRestartButton.tsx @@ -7,7 +7,7 @@ import { useLanguageContext } from 'components/Contexts/LanguageContext' import styled from 'styled-components' import { useRef, useState } from 'react' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' -import { FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { Mission } from 'models/Mission' import { ScheduleMissionWithConfirmDialogs } from '../ConfirmScheduleDialogs/ConfirmScheduleDialog' import { AlertCategory } from 'components/Alerts/AlertsBanner' @@ -32,7 +32,7 @@ enum ReRunOptions { export const MissionRestartButton = ({ mission, hasFailedTasks, smallButton }: MissionProps) => { const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const [isOpen, setIsOpen] = useState(false) const [isLocationVerificationOpen, setIsLocationVerificationOpen] = useState(false) const [selectedRerunOption, setSelectedRerunOption] = useState() @@ -47,13 +47,18 @@ export const MissionRestartButton = ({ mission, hasFailedTasks, smallButton }: M const startReRun = (option: ReRunOptions) => { BackendAPICaller.reRunMission(mission.id, option === ReRunOptions.ReRunFailed) .then(() => navigateToHome()) - .catch(() => + .catch(() => { setAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + setListAlert( AlertType.RequestFail, , AlertCategory.ERROR ) - ) + }) setIsLocationVerificationOpen(false) } diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index a206d5156..9bd5e25d3 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -64,7 +64,7 @@ const Circle = styled.div` border-radius: 50%; ` export const Header = ({ page }: { page: string }) => { - const { alerts } = useAlertContext() + const { alerts, listAlerts } = useAlertContext() const { installationName } = useInstallationContext() const { TranslateText } = useLanguageContext() @@ -112,7 +112,7 @@ export const Header = ({ page }: { page: string }) => { ref={referenceElementNotifications} > - {Object.entries(alerts).length !== 0 && + {Object.entries(listAlerts).length !== 0 && installationName && page !== 'root' && ( //Alert banners @@ -138,19 +138,19 @@ export const Header = ({ page }: { page: string }) => { > - {TranslateText('Alerts')} + {TranslateText('Alerts')} - {Object.entries(alerts).length === 0 && installationName && page !== 'root' && ( - {TranslateText("No alerts")} + {Object.entries(listAlerts).length === 0 && installationName && page !== 'root' && ( + {TranslateText('No alerts')} )} - {Object.entries(alerts).length > 0 && installationName && page !== 'root' && ( + {Object.entries(listAlerts).length > 0 && installationName && page !== 'root' && ( - {Object.entries(alerts).map(([key, value]) => ( + {Object.entries(listAlerts).map(([key, value]) => ( { const InstallationPicker = () => { const { installationName, switchInstallation } = useInstallationContext() const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const [allPlantsMap, setAllPlantsMap] = useState>(new Map()) const [selectedInstallation, setSelectedInstallation] = useState(installationName) const [showActivePlants, setShowActivePlants] = useState(true) @@ -103,6 +103,13 @@ const InstallationPicker = () => { />, AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) }) } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/components/Pages/FrontPage/MissionOverview/MissionQueueView.tsx b/frontend/src/components/Pages/FrontPage/MissionOverview/MissionQueueView.tsx index c80dd1424..3347ca544 100644 --- a/frontend/src/components/Pages/FrontPage/MissionOverview/MissionQueueView.tsx +++ b/frontend/src/components/Pages/FrontPage/MissionOverview/MissionQueueView.tsx @@ -8,10 +8,9 @@ import { EmptyMissionQueuePlaceholder } from './NoMissionPlaceholder' import { useLanguageContext } from 'components/Contexts/LanguageContext' import { useMissionsContext } from 'components/Contexts/MissionRunsContext' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { FrontPageSectionId } from 'models/FrontPageSectionId' import { AlertCategory } from 'components/Alerts/AlertsBanner' - const StyledMissionView = styled.div` display: grid; grid-column: 1/ -1; @@ -29,16 +28,23 @@ const MissionTable = styled.div` export const MissionQueueView = (): JSX.Element => { const { TranslateText } = useLanguageContext() const { missionQueue, ongoingMissions, loadingMissionSet, setLoadingMissionSet } = useMissionsContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const onDeleteMission = (mission: Mission) => - BackendAPICaller.deleteMission(mission.id).catch((_) => + BackendAPICaller.deleteMission(mission.id).catch((_) => { setAlert( AlertType.RequestFail, , AlertCategory.ERROR ) - ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) + }) useEffect(() => { setLoadingMissionSet((currentLoadingNames) => { diff --git a/frontend/src/components/Pages/FrontPage/MissionOverview/ScheduleMissionDialog/SelectMissionsToScheduleDialog.tsx b/frontend/src/components/Pages/FrontPage/MissionOverview/ScheduleMissionDialog/SelectMissionsToScheduleDialog.tsx index 2f3c6bb68..fc12a6890 100644 --- a/frontend/src/components/Pages/FrontPage/MissionOverview/ScheduleMissionDialog/SelectMissionsToScheduleDialog.tsx +++ b/frontend/src/components/Pages/FrontPage/MissionOverview/ScheduleMissionDialog/SelectMissionsToScheduleDialog.tsx @@ -9,7 +9,7 @@ import { useRobotContext } from 'components/Contexts/RobotContext' import { BackendAPICaller } from 'api/ApiCaller' import { useMissionsContext } from 'components/Contexts/MissionRunsContext' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' const StyledMissionDialog = styled.div` @@ -43,7 +43,7 @@ interface ScheduleDialogProps { export const SelectMissionsToScheduleDialog = ({ echoMissionsList, closeDialog }: ScheduleDialogProps): JSX.Element => { const { TranslateText } = useLanguageContext() const { installationCode } = useInstallationContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const { setLoadingMissionSet } = useMissionsContext() const [selectedEchoMissions, setSelectedEchoMissions] = useState([]) const [selectedRobot, setSelectedRobot] = useState(undefined) @@ -62,6 +62,15 @@ export const SelectMissionsToScheduleDialog = ({ echoMissionsList, closeDialog } />, AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) setLoadingMissionSet((currentSet: Set) => { const updatedSet: Set = new Set(currentSet) updatedSet.delete(String(mission.name)) diff --git a/frontend/src/components/Pages/FrontPage/MissionOverview/StopDialogs.tsx b/frontend/src/components/Pages/FrontPage/MissionOverview/StopDialogs.tsx index 9055a3665..c3f229f78 100644 --- a/frontend/src/components/Pages/FrontPage/MissionOverview/StopDialogs.tsx +++ b/frontend/src/components/Pages/FrontPage/MissionOverview/StopDialogs.tsx @@ -9,7 +9,7 @@ import { BackendAPICaller } from 'api/ApiCaller' import { useInstallationContext } from 'components/Contexts/InstallationContext' import { TaskType } from 'models/Task' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' import { useRobotContext } from 'components/Contexts/RobotContext' import { RobotFlotillaStatus } from 'models/Robot' @@ -139,7 +139,7 @@ export const StopRobotDialog = (): JSX.Element => { const { enabledRobots } = useRobotContext() const { TranslateText } = useLanguageContext() const { installationCode } = useInstallationContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const safeZoneActivated = enabledRobots.find((r) => r.flotillaStatus === RobotFlotillaStatus.SafeZone) !== undefined @@ -158,6 +158,13 @@ export const StopRobotDialog = (): JSX.Element => { , AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) }) closeDialog() return @@ -172,6 +179,13 @@ export const StopRobotDialog = (): JSX.Element => { />, AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) }) closeDialog() } diff --git a/frontend/src/components/Pages/InspectionPage/InspectionOverview.tsx b/frontend/src/components/Pages/InspectionPage/InspectionOverview.tsx index 7dc23ca84..320ef5c31 100644 --- a/frontend/src/components/Pages/InspectionPage/InspectionOverview.tsx +++ b/frontend/src/components/Pages/InspectionPage/InspectionOverview.tsx @@ -11,7 +11,7 @@ import { useInstallationContext } from 'components/Contexts/InstallationContext' import { useRobotContext } from 'components/Contexts/RobotContext' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' import { EchoMissionDefinition } from 'models/MissionDefinition' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { StyledDict } from './InspectionUtilities' import { Icons } from 'utils/icons' import { StyledButton } from 'components/Styles/StyledComponents' @@ -46,7 +46,7 @@ export const InspectionOverviewSection = () => { const { TranslateText } = useLanguageContext() const { installationCode } = useInstallationContext() const { enabledRobots } = useRobotContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const { missionDefinitions } = useMissionDefinitionsContext() const [isFetchingEchoMissions, setIsFetchingEchoMissions] = useState(false) const [isScheduleMissionDialogOpen, setIsScheduleMissionDialogOpen] = useState(false) @@ -79,6 +79,13 @@ export const InspectionOverviewSection = () => { , AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) setIsFetchingEchoMissions(false) }) } diff --git a/frontend/src/components/Pages/InspectionPage/ScheduleMissionDialogs.tsx b/frontend/src/components/Pages/InspectionPage/ScheduleMissionDialogs.tsx index b3862069f..ce32a18a2 100644 --- a/frontend/src/components/Pages/InspectionPage/ScheduleMissionDialogs.tsx +++ b/frontend/src/components/Pages/InspectionPage/ScheduleMissionDialogs.tsx @@ -9,7 +9,7 @@ import { Icons } from 'utils/icons' import { useRobotContext } from 'components/Contexts/RobotContext' import { StyledAutoComplete, StyledButton, StyledDialog } from 'components/Styles/StyledComponents' import { useMissionsContext } from 'components/Contexts/MissionRunsContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' import { ScheduleMissionWithConfirmDialogs } from 'components/Displays/ConfirmScheduleDialogs/ConfirmScheduleDialog' import { FrontPageSectionId } from 'models/FrontPageSectionId' @@ -54,7 +54,7 @@ export const ScheduleMissionDialog = (props: IProps): JSX.Element => { const { TranslateText } = useLanguageContext() const { enabledRobots } = useRobotContext() const { setLoadingMissionSet } = useMissionsContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const [isLocalizationVerificationDialogOpen, setIsLocalizationVerificationDialog] = useState(false) const [selectedRobot, setSelectedRobot] = useState() const [missionsToSchedule, setMissionsToSchedule] = useState() @@ -92,6 +92,15 @@ export const ScheduleMissionDialog = (props: IProps): JSX.Element => { />, AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) setLoadingMissionSet((currentSet: Set) => { const updatedSet: Set = new Set(currentSet) updatedSet.delete(String(mission.name)) diff --git a/frontend/src/components/Pages/MissionDefinitionPage/MissionDefinitionPage.tsx b/frontend/src/components/Pages/MissionDefinitionPage/MissionDefinitionPage.tsx index fd71dd446..a238f298a 100644 --- a/frontend/src/components/Pages/MissionDefinitionPage/MissionDefinitionPage.tsx +++ b/frontend/src/components/Pages/MissionDefinitionPage/MissionDefinitionPage.tsx @@ -16,7 +16,7 @@ import { useMissionDefinitionsContext } from 'components/Contexts/MissionDefinit import { StyledPage } from 'components/Styles/StyledComponents' import styled from 'styled-components' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' const StyledDictCard = styled(StyledDict.Card)` @@ -149,7 +149,7 @@ const MissionDefinitionEditDialog = ({ isDeprecated: false, } const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const navigate = useNavigate() const [form, setForm] = useState(defaultMissionDefinitionForm) @@ -190,6 +190,11 @@ const MissionDefinitionEditDialog = ({ , AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) }) } diff --git a/frontend/src/components/Pages/MissionHistoryPage/MissionHistoryView.tsx b/frontend/src/components/Pages/MissionHistoryPage/MissionHistoryView.tsx index 5ca014703..e615bc44a 100644 --- a/frontend/src/components/Pages/MissionHistoryPage/MissionHistoryView.tsx +++ b/frontend/src/components/Pages/MissionHistoryPage/MissionHistoryView.tsx @@ -13,7 +13,7 @@ import { InspectionType } from 'models/Inspection' import { tokens } from '@equinor/eds-tokens' import { SmallScreenInfoText } from 'utils/InfoText' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' enum InspectionTableColumns { @@ -121,7 +121,7 @@ export const MissionHistoryView = ({ refreshInterval }: RefreshProps) => { const { TranslateText } = useLanguageContext() const { page, switchPage, filterState, filterIsSet, filterFunctions, filterError, clearFilterError } = useMissionFilterContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const [filteredMissions, setFilteredMissions] = useState([]) const [paginationDetails, setPaginationDetails] = useState() const [isLoading, setIsLoading] = useState(true) @@ -201,6 +201,13 @@ export const MissionHistoryView = ({ refreshInterval }: RefreshProps) => { />, AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) }) // eslint-disable-next-line react-hooks/exhaustive-deps }, [page, pageSize, switchPage, filterFunctions]) diff --git a/frontend/src/components/Pages/MissionPage/MissionPage.tsx b/frontend/src/components/Pages/MissionPage/MissionPage.tsx index 2b0a50d80..dc34eb2c8 100644 --- a/frontend/src/components/Pages/MissionPage/MissionPage.tsx +++ b/frontend/src/components/Pages/MissionPage/MissionPage.tsx @@ -13,7 +13,7 @@ import { Header } from 'components/Header/Header' import { SignalREventLabels, useSignalRContext } from 'components/Contexts/SignalRContext' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' import { useLanguageContext } from 'components/Contexts/LanguageContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' const StyledMissionPage = styled.div` @@ -40,7 +40,7 @@ const VideoStreamSection = styled.div` export const MissionPage = () => { const { missionId } = useParams() const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const [videoStreams, setVideoStreams] = useState([]) const [selectedMission, setSelectedMission] = useState() const { registerEvent, connectionReady } = useSignalRContext() @@ -77,6 +77,13 @@ export const MissionPage = () => { />, AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) }) // eslint-disable-next-line react-hooks/exhaustive-deps }, [missionId]) diff --git a/frontend/src/components/Pages/RobotPage/RobotPage.tsx b/frontend/src/components/Pages/RobotPage/RobotPage.tsx index 232257249..cfb4ede8b 100644 --- a/frontend/src/components/Pages/RobotPage/RobotPage.tsx +++ b/frontend/src/components/Pages/RobotPage/RobotPage.tsx @@ -16,7 +16,7 @@ import { useRobotContext } from 'components/Contexts/RobotContext' import { BackendAPICaller } from 'api/ApiCaller' import { StyledButton, StyledPage } from 'components/Styles/StyledComponents' import { AlertType, useAlertContext } from 'components/Contexts/AlertContext' -import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert' +import { FailedRequestAlertContent, FailedRequestAlertListContent } from 'components/Alerts/FailedRequestAlert' import { AlertCategory } from 'components/Alerts/AlertsBanner' const RobotArmMovementSection = styled.div` @@ -56,7 +56,7 @@ const StatusContent = styled.div<{ $alignItems?: string }>` export const RobotPage = () => { const { TranslateText } = useLanguageContext() - const { setAlert } = useAlertContext() + const { setAlert, setListAlert } = useAlertContext() const { robotId } = useParams() const { enabledRobots } = useRobotContext() @@ -72,6 +72,13 @@ export const RobotPage = () => { />, AlertCategory.ERROR ) + setListAlert( + AlertType.RequestFail, + , + AlertCategory.ERROR + ) }) } } diff --git a/frontend/src/language/en.json b/frontend/src/language/en.json index a71944945..3307b68f3 100644 --- a/frontend/src/language/en.json +++ b/frontend/src/language/en.json @@ -265,5 +265,6 @@ "Alert": "Alert", "Alerts": "Alerts", "No alerts": "No alerts", - "minutes ago": "minutes ago" + "minutes ago": "minutes ago", + "Failed to retrieve previous mission runs": "Failed to retrieve previous mission runs" } diff --git a/frontend/src/language/no.json b/frontend/src/language/no.json index a72421866..19779c855 100644 --- a/frontend/src/language/no.json +++ b/frontend/src/language/no.json @@ -265,5 +265,6 @@ "Alert": "Varsling", "Alerts": "Varsler", "No alerts": "Ingen varsler", - "minutes ago": "minutter siden" + "minutes ago": "minutter siden", + "Failed to retrieve previous mission runs": "Kunne ikke hente tildigere oppdragskjøringer" }