Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add safe zone banner #1169

Merged
merged 1 commit into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions frontend/src/components/Alerts/AlertsBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,34 @@ const Horizontal = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
`

const Center = styled.div`
align-items: center;
`

export enum AlertCategory {
ERROR,
WARNING,
SUCCESS,
Eddasol marked this conversation as resolved.
Show resolved Hide resolved
}

interface AlertProps {
children: ReactNode
dismissAlert: () => void
alertCategory: AlertCategory
}

export const AlertBanner = ({ children, dismissAlert }: AlertProps) => {
export const AlertBanner = ({ children, dismissAlert, alertCategory }: AlertProps) => {
let bannerColor = tokens.colors.ui.background__danger.hex

if (alertCategory === AlertCategory.WARNING) bannerColor = tokens.colors.interactive.warning__resting.hex
if (alertCategory === AlertCategory.SUCCESS) bannerColor = tokens.colors.interactive.success__resting.hex

return (
<>
<StyledCard variant="danger" style={{ boxShadow: tokens.elevation.raised }}>
<StyledCard style={{ boxShadow: tokens.elevation.raised, backgroundColor: bannerColor }}>
<Horizontal>
<Center>{children}</Center>
<Button variant="ghost_icon" onClick={dismissAlert}>
Expand Down
47 changes: 35 additions & 12 deletions frontend/src/components/Contexts/AlertContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,29 @@ import { BlockedRobotAlertContent } from 'components/Alerts/BlockedRobotAlert'
import { RobotStatus } from 'models/Robot'
import { FailedAlertContent } from 'components/Alerts/FailedAlertContent'
import { convertUTCDateToLocalDate } from 'utils/StringFormatting'

type AlertDictionaryType = { [key in AlertType]?: { content: ReactNode | undefined; dismissFunction: () => void } }
import { AlertCategory } from 'components/Alerts/AlertsBanner'

export enum AlertType {
MissionFail,
RequestFail,
SafeZoneFail,
BlockedRobot,
RequestSafeZone,
DismissSafeZone,
}

const alertTypeEnumMap: { [key: string]: AlertType } = {
safeZoneFailure: AlertType.SafeZoneFail,
scheduleFailure: AlertType.RequestFail,
}

type AlertDictionaryType = {
[key in AlertType]?: { content: ReactNode | undefined; dismissFunction: () => void; alertCategory: AlertCategory }
}

interface IAlertContext {
alerts: AlertDictionaryType
setAlert: (source: AlertType, alert: ReactNode) => void
setAlert: (source: AlertType, alert: ReactNode, category: AlertCategory) => void
clearAlerts: () => void
clearAlert: (source: AlertType) => void
}
Expand All @@ -39,7 +44,7 @@ interface Props {

const defaultAlertInterface = {
alerts: {},
setAlert: (source: AlertType, alert: ReactNode) => {},
setAlert: (source: AlertType, alert: ReactNode, category: AlertCategory) => {},
clearAlerts: () => {},
clearAlert: (source: AlertType) => {},
}
Expand All @@ -61,17 +66,26 @@ export const AlertProvider: FC<Props> = ({ children }) => {
const maxTimeInterval: number = 60
const dismissMissionFailTimeKey: string = 'lastMissionFailDismissalTime'

const setAlert = (source: AlertType, alert: ReactNode) =>
setAlerts({ ...alerts, [source]: { content: alert, dismissFunction: () => clearAlert(source) } })
const setAlert = (source: AlertType, alert: ReactNode, category: AlertCategory) => {
setAlerts((oldAlerts) => {
return {
...oldAlerts,
[source]: { content: alert, dismissFunction: () => clearAlert(source), alertCategory: category },
}
})
}

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

const clearAlert = (source: AlertType) => {
if (source === AlertType.MissionFail)
sessionStorage.setItem(dismissMissionFailTimeKey, JSON.stringify(Date.now()))
let newAlerts = { ...alerts }
delete newAlerts[source]
setAlerts(newAlerts)

setAlerts((oldAlerts) => {
let newAlerts = { ...oldAlerts }
delete newAlerts[source]
return newAlerts
})
}

const getLastDismissalTime = (): Date => {
Expand Down Expand Up @@ -151,7 +165,8 @@ export const AlertProvider: FC<Props> = ({ children }) => {
}
setAlert(
alertType,
<FailedAlertContent title={backendAlert.alertTitle} message={backendAlert.alertMessage} />
<FailedAlertContent title={backendAlert.alertTitle} message={backendAlert.alertMessage} />,
AlertCategory.ERROR
)
})
}
Expand All @@ -160,7 +175,11 @@ export const AlertProvider: FC<Props> = ({ children }) => {

useEffect(() => {
if (newFailedMissions.length > 0) {
setAlert(AlertType.MissionFail, <FailedMissionAlertContent missions={newFailedMissions} />)
setAlert(
AlertType.MissionFail,
<FailedMissionAlertContent missions={newFailedMissions} />,
AlertCategory.ERROR
)
setNewFailedMissions([])
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -181,7 +200,11 @@ export const AlertProvider: FC<Props> = ({ children }) => {

if (isBlockedRobotNamesModifyed) {
if (newBlockedRobotNames.length > 0) {
setAlert(AlertType.BlockedRobot, <BlockedRobotAlertContent robotNames={newBlockedRobotNames} />)
setAlert(
AlertType.BlockedRobot,
<BlockedRobotAlertContent robotNames={newBlockedRobotNames} />,
AlertCategory.WARNING
)
} else {
clearAlert(AlertType.BlockedRobot)
}
Expand Down
48 changes: 39 additions & 9 deletions frontend/src/components/Contexts/SafeZoneContext.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,64 @@
import { createContext, FC, useContext, useState } from 'react'
import { createContext, FC, useContext, useEffect, useState } from 'react'
import { useRobotContext } from './RobotContext'
import { useInstallationContext } from './InstallationContext'
import { AlertType, useAlertContext } from './AlertContext'
import { SafeZoneBanner } from 'components/Pages/FrontPage/MissionOverview/SafeZoneBanner'
import { AlertCategory } from 'components/Alerts/AlertsBanner'

interface ISafeZoneContext {
safeZoneStatus: boolean
switchSafeZoneStatus: (newSafeZoneStatus: boolean) => void
}

interface Props {
children: React.ReactNode
}

const defaultSafeZoneInterface = {
safeZoneStatus: JSON.parse(localStorage.getItem('safeZoneStatus') ?? 'false'),
switchSafeZoneStatus: (newSafeZoneStatus: boolean) => {},
safeZoneStatus: false,
}

export const SafeZoneContext = createContext<ISafeZoneContext>(defaultSafeZoneInterface)

export const SafeZoneProvider: FC<Props> = ({ children }) => {
const [safeZoneStatus, setSafeZoneStatus] = useState<boolean>(defaultSafeZoneInterface.safeZoneStatus)
const { enabledRobots } = useRobotContext()
const { installationCode } = useInstallationContext()
const { setAlert, clearAlert } = useAlertContext()

const switchSafeZoneStatus = (newSafeZoneStatus: boolean) => {
localStorage.setItem('safeZoneStatus', String(newSafeZoneStatus))
setSafeZoneStatus(newSafeZoneStatus)
}
useEffect(() => {
const missionQueueFozenStatus = enabledRobots
.filter(
(robot) =>
robot.currentInstallation.installationCode.toLocaleLowerCase() ===
installationCode.toLocaleLowerCase()
)
.map((robot) => robot.missionQueueFrozen)
.filter((status) => status === true)

if (missionQueueFozenStatus.length > 0 && safeZoneStatus === false) {
setSafeZoneStatus((oldStatus) => !oldStatus)
clearAlert(AlertType.DismissSafeZone)
setAlert(
AlertType.RequestSafeZone,
<SafeZoneBanner alertCategory={AlertCategory.WARNING} />,
AlertCategory.WARNING
)
} else if (missionQueueFozenStatus.length === 0 && safeZoneStatus === true) {
setSafeZoneStatus((oldStatus) => !oldStatus)
clearAlert(AlertType.RequestSafeZone)
setAlert(
AlertType.DismissSafeZone,
<SafeZoneBanner alertCategory={AlertCategory.SUCCESS} />,
AlertCategory.SUCCESS
)
mrica-equinor marked this conversation as resolved.
Show resolved Hide resolved
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [enabledRobots])

return (
<SafeZoneContext.Provider
value={{
safeZoneStatus,
switchSafeZoneStatus,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Robot } from 'models/Robot'
import { useEffect, useState } from 'react'
import { ScheduleMissionWithLocalizationVerificationDialog } from 'components/Displays/ConfirmScheduleDialogs/LocalizationVerification/ScheduleMissionWithLocalizationVerification'
import { useRobotContext } from 'components/Contexts/RobotContext'
import {
InsufficientBatteryDialog,
InsufficientPressureDialog,
} from 'components/Displays/ConfirmScheduleDialogs/InsufficientValueDialogs'
import { ScheduleMissionWithLocalizationVerificationDialog } from './LocalizationVerification/ScheduleMissionWithLocalizationVerification'

interface ConfirmScheduleDialogProps {
scheduleMissions: () => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AlertType, useAlertContext } from 'components/Contexts/AlertContext'
import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert'
import { Mission } from 'models/Mission'
import { ScheduleMissionWithConfirmDialogs } from '../ConfirmScheduleDialogs/ConfirmScheduleDialog'
import { AlertCategory } from 'components/Alerts/AlertsBanner'

const Centered = styled.div`
display: flex;
Expand Down Expand Up @@ -48,7 +49,8 @@ export const MissionRestartButton = ({ mission, hasFailedTasks }: MissionProps)
.catch(() =>
setAlert(
AlertType.RequestFail,
<FailedRequestAlertContent translatedMessage={TranslateText('Failed to rerun mission')} />
<FailedRequestAlertContent translatedMessage={TranslateText('Failed to rerun mission')} />,
AlertCategory.ERROR
)
)
setIsLocationVerificationOpen(false)
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { AlertBanner } from 'components/Alerts/AlertsBanner'
import { FrontPageSectionId } from 'models/FrontPageSectionId'

const StyledTopBar = styled(TopBar)`
margin-bottom: 2rem;
align-items: center;
box-shadow: none;
`
Expand All @@ -35,6 +34,7 @@ const StyledAlertList = styled.div`
export const Header = ({ page }: { page: string }) => {
const { alerts } = useAlertContext()
const { installationName } = useInstallationContext()

return (
<>
<StyledTopBar id={FrontPageSectionId.TopBar}>
Expand Down Expand Up @@ -67,7 +67,7 @@ export const Header = ({ page }: { page: string }) => {
{Object.entries(alerts).length > 0 && installationName && page !== 'root' && (
<StyledAlertList>
{Object.entries(alerts).map(([key, value]) => (
<AlertBanner key={key} dismissAlert={value.dismissFunction}>
<AlertBanner key={key} dismissAlert={value.dismissFunction} alertCategory={value.alertCategory}>
{value.content}
</AlertBanner>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useInstallationContext } from 'components/Contexts/InstallationContext'
import { AlertType, useAlertContext } from 'components/Contexts/AlertContext'
import { FailedRequestAlertContent } from 'components/Alerts/FailedRequestAlert'
import { FrontPageSectionId } from 'models/FrontPageSectionId'
import { AlertCategory } from 'components/Alerts/AlertsBanner'

const StyledMissionView = styled.div`
display: grid;
Expand Down Expand Up @@ -39,7 +40,8 @@ export const MissionQueueView = (): JSX.Element => {
BackendAPICaller.deleteMission(mission.id).catch((_) =>
setAlert(
AlertType.RequestFail,
<FailedRequestAlertContent translatedMessage={TranslateText('Failed to delete mission from queue')} />
<FailedRequestAlertContent translatedMessage={TranslateText('Failed to delete mission from queue')} />,
AlertCategory.ERROR
)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Button, Icon, Typography } from '@equinor/eds-core-react'
import { AlertCategory } from 'components/Alerts/AlertsBanner'
import { useLanguageContext } from 'components/Contexts/LanguageContext'
import styled from 'styled-components'

const StyledDiv = styled.div`
flex-direction: column;
`

const StyledAlertTitle = styled.div`
display: flex;
gap: 0.3em;
align-items: flex-end;
`

const StyledButton = styled(Button)`
text-align: left;
height: auto;
padding: 5px 5px;
`

interface SafeZoneBannerProps {
alertCategory: AlertCategory
}

export const SafeZoneBanner = ({ alertCategory }: SafeZoneBannerProps): JSX.Element => {
mrica-equinor marked this conversation as resolved.
Show resolved Hide resolved
const { TranslateText } = useLanguageContext()

return (
<StyledDiv>
<StyledAlertTitle>
<Icon name="error_outlined" />
<Typography>
{alertCategory === AlertCategory.WARNING ? TranslateText('WARNING') : TranslateText('INFO')}
</Typography>
</StyledAlertTitle>
<StyledButton variant="ghost" color="secondary">
{alertCategory === AlertCategory.WARNING
? TranslateText('Safe zone banner text')
: TranslateText('Dismiss safe zone banner text')}
</StyledButton>
</StyledDiv>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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 { AlertCategory } from 'components/Alerts/AlertsBanner'

const StyledMissionDialog = styled.div`
display: flex;
Expand Down Expand Up @@ -58,7 +59,8 @@ export const SelectMissionsToScheduleDialog = ({ echoMissionsList, closeDialog }
translatedMessage={
TranslateText('Failed to schedule mission') + ` '${mission.name}'. ${e.message}`
}
/>
/>,
AlertCategory.ERROR
)
setLoadingMissionSet((currentSet: Set<string>) => {
const updatedSet: Set<string> = new Set(currentSet)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,24 @@ export const StopRobotDialog = (): JSX.Element => {
<StyledDialog open={isStopRobotDialogOpen} isDismissable>
<Dialog.Header>
<Dialog.Title>
<Typography variant="h5">{TranslateText('Send robots to safe zone') + '?'}</Typography>
<Typography variant="h5">
{!safeZoneStatus
? TranslateText('Send robots to safe zone') + '?'
: TranslateText('Dismiss robots from safe zone') + '?'}
</Typography>
</Dialog.Title>
</Dialog.Header>
<Dialog.CustomContent>
<StyledText>
<Typography variant="body_long">
{!safeZoneStatus
? TranslateText('Send robots to safe zone long text')
: TranslateText('Dismiss robots from safe zone') + '?'}
: TranslateText('Dismiss robots from safe zone long text')}
</Typography>
<Typography variant="body_long">
{!safeZoneStatus
? TranslateText('Send robots to safe confirmation text')
: TranslateText('Dismiss robots from safe zone long text')}
: TranslateText('Dismiss robots from safe confirmation text')}
</Typography>
</StyledText>
</Dialog.CustomContent>
Expand Down
Loading
Loading