Skip to content

Commit

Permalink
Add new assetselection frontpage
Browse files Browse the repository at this point in the history
  • Loading branch information
OleDrange committed Sep 29, 2023
1 parent c75d138 commit 4cbb18f
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 143 deletions.
1 change: 0 additions & 1 deletion frontend/run_nginx.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ mv /app/tmp.html /app/index.html
# Start Nginx
echo $(date) Starting Nginx…
nginx -g "daemon off;"

4 changes: 2 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AuthenticatedTemplate, UnauthenticatedTemplate } from '@azure/msal-react'
import { SignInPage } from './components/Pages/SignInPage/SignInPage'
import { AssetSelectionPage } from 'components/Pages/AssetSelectionPage/AssetSelectionPage'
import { FlotillaSite } from 'components/Pages/FlotillaSite'
import { LanguageProvider } from 'components/Contexts/LanguageContext'
import { MissionControlProvider } from 'components/Contexts/MissionControlContext'
Expand All @@ -14,7 +14,7 @@ function App() {
<>
<UnauthenticatedTemplate>
<div className="sign-in-page">
<SignInPage></SignInPage>
<AssetSelectionPage></AssetSelectionPage>
</div>
</UnauthenticatedTemplate>
<AuthenticatedTemplate>
Expand Down
40 changes: 33 additions & 7 deletions frontend/src/components/Contexts/InstallationContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { createContext, FC, useContext, useState } from 'react'
import { createContext, FC, useContext, useState, useEffect } from 'react'
import { BackendAPICaller } from 'api/ApiCaller'
import { EchoPlantInfo } from 'models/EchoMission'

interface IInstallationContext {
installationCode: string
switchInstallation: (selectedInstallation: string) => void
installationName: string
switchInstallation: (selectedName: string) => void
}

const mapInstallationCodeToName = (echoPlantInfoArray: EchoPlantInfo[]): Map<string, string> => {
var mapping = new Map<string, string>()
echoPlantInfoArray.forEach((echoPlantInfo: EchoPlantInfo) => {
mapping.set(echoPlantInfo.projectDescription, echoPlantInfo.plantCode)
})
return mapping
}

interface Props {
Expand All @@ -11,24 +22,39 @@ interface Props {

const defaultInstallation = {
installationCode: '',
installationName: '',
switchInstallation: (selectedInstallation: string) => {},
}

export const InstallationContext = createContext<IInstallationContext>(defaultInstallation)

export const InstallationProvider: FC<Props> = ({ children }) => {
const previousInstallation = window.localStorage.getItem('installationString')
const [installationCode, setInstallation] = useState(previousInstallation || defaultInstallation.installationCode)
const [allPlantsMap, setAllPlantsMap] = useState<Map<string, string>>(new Map())
const [installationName, setInstallationName] = useState<string>(
window.localStorage.getItem('installationName') || ''
)

useEffect(() => {
BackendAPICaller.getEchoPlantInfo().then((response: EchoPlantInfo[]) => {
const mapping = mapInstallationCodeToName(response)
setAllPlantsMap(mapping)
})
}, [])

const installationCode = allPlantsMap.get(installationName) || ''

const switchInstallation = (selectedInstallation: string) => {
setInstallation(selectedInstallation.toLowerCase())
window.localStorage.setItem('installationString', selectedInstallation.toLowerCase())
const switchInstallation = (selectedName: string) => {
setInstallationName(selectedName)
window.localStorage.setItem('installationName', selectedName)
const derivedCode = allPlantsMap.get(selectedName) || ''
window.localStorage.setItem('installationCode', derivedCode)
}

return (
<InstallationContext.Provider
value={{
installationCode,
installationName,
switchInstallation,
}}
>
Expand Down
95 changes: 11 additions & 84 deletions frontend/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { config } from 'config'
import { Button, Icon, TopBar, Autocomplete, Typography, Checkbox, Dialog } from '@equinor/eds-core-react'
import { BackendAPICaller } from 'api/ApiCaller'
import { useInstallationContext } from 'components/Contexts/InstallationContext'
import { EchoPlantInfo } from 'models/EchoMission'
import { useEffect, useState } from 'react'
import styled from 'styled-components'
import { useLanguageContext } from 'components/Contexts/LanguageContext'
import { SelectLanguage } from './LanguageSelector'
import { Icons } from 'utils/icons'
import { tokens } from '@equinor/eds-tokens'

const StyledTopBar = styled(TopBar)`
margin-bottom: 2rem;
Expand All @@ -28,32 +23,26 @@ const HandPointer = styled.div`
cursor: pointer;
`

const StyledTopBarContent = styled(TopBar.CustomContent)`
display: grid;
grid-template-columns: minmax(50px, 265px) auto;
gap: 0px 3rem;
align-items: center;
`

const SelectLanguageWrapper = styled.div`
margin-left: 1.5rem;
`

export function Header({ page }: { page: string }) {
const { installationName } = useInstallationContext()
return (
<StyledTopBar>
<HandPointer>
<TopBar.Header
onClick={() => {
window.location.href = `${config.FRONTEND_URL}/`
window.location.href = `${config.FRONTEND_BASE_ROUTE}/FrontPage`
}}
>
<Typography variant="body_long_bold" color="primary">
Flotilla
</Typography>
</TopBar.Header>
</HandPointer>
<StyledTopBarContent>{InstallationPicker(page)}</StyledTopBarContent>
<Typography> {installationName}</Typography>
<TopBar.Actions>
<IconStyle>
<Button variant="ghost_icon" onClick={() => console.log('Clicked account icon')}>
Expand All @@ -65,79 +54,17 @@ export function Header({ page }: { page: string }) {
<Button variant="ghost_icon" onClick={() => console.log('Clicked notification icon')}>
<Icon name={Icons.Notifications} size={24} />
</Button>
<Button
variant="ghost_icon"
onClick={() => {
window.location.href = `${config.FRONTEND_BASE_ROUTE}/`
}}
>
<Icon name={Icons.Platform} size={24} title="Change Asset" />
</Button>
</IconStyle>
<SelectLanguageWrapper>{SelectLanguage()}</SelectLanguageWrapper>
</TopBar.Actions>
</StyledTopBar>
)
}

function InstallationPicker(page: string) {
const { TranslateText } = useLanguageContext()
const [allPlantsMap, setAllPlantsMap] = useState<Map<string, string>>()
const { installationCode, switchInstallation } = useInstallationContext()
const [showActivePlants, setShowActivePlants] = useState<boolean>(true)
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false)
const [updateListOfActivePlants, setUpdateListOfActivePlants] = useState<boolean>(false)

useEffect(() => {
const plantPromise = showActivePlants ? BackendAPICaller.getActivePlants() : BackendAPICaller.getEchoPlantInfo()
plantPromise.then(async (response: EchoPlantInfo[]) => {
const mapping = mapInstallationCodeToName(response)
setAllPlantsMap(mapping)
})
}, [showActivePlants, updateListOfActivePlants])

const mappedOptions = allPlantsMap ? allPlantsMap : new Map<string, string>()
return (
<>
<Autocomplete
options={Array.from(mappedOptions.keys()).sort()}
label=""
disabled={page === 'mission'}
initialSelectedOptions={[installationCode.toUpperCase()]}
placeholder={TranslateText('Select installation')}
onOptionsChange={({ selectedItems }) => {
const mapKey = mappedOptions.get(selectedItems[0])
if (mapKey !== undefined) switchInstallation(mapKey)
else switchInstallation('')
}}
autoWidth={true}
onFocus={(e) => {
e.preventDefault()
setUpdateListOfActivePlants(!updateListOfActivePlants)
}}
/>
<Button variant="ghost_icon" onClick={() => setIsDialogOpen(true)}>
{' '}
<Icon
name={Icons.Settings}
size={24}
aria-haspopup="dialog"
color={tokens.colors.interactive.primary__resting.rgba}
/>{' '}
</Button>
<Dialog open={isDialogOpen} onClose={() => setIsDialogOpen(false)}>
<Dialog.Header>
<Dialog.Title>{TranslateText('Installation settings')}</Dialog.Title>
</Dialog.Header>
<Dialog.Actions>
<Checkbox
label={TranslateText('Only active installations')}
checked={showActivePlants}
onChange={(e) => setShowActivePlants(e.target.checked)}
/>
<Button onClick={() => setIsDialogOpen(false)}>{TranslateText('Close')}</Button>
</Dialog.Actions>
</Dialog>
</>
)
}

const mapInstallationCodeToName = (echoPlantInfoArray: EchoPlantInfo[]): Map<string, string> => {
var mapping = new Map<string, string>()
echoPlantInfoArray.forEach((echoPlantInfo: EchoPlantInfo) => {
mapping.set(echoPlantInfo.projectDescription, echoPlantInfo.plantCode)
})
return mapping
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { useEffect, useState } from 'react'
import { useIsAuthenticated } from '@azure/msal-react'
import { useMsal } from '@azure/msal-react'
import { loginRequest } from '../../../api/AuthConfig'
import { Autocomplete, Button, TopBar, CircularProgress, Typography, Checkbox } from '@equinor/eds-core-react'
import { IPublicClientApplication } from '@azure/msal-browser'
import styled from 'styled-components'
import { useLanguageContext } from 'components/Contexts/LanguageContext'
import { useInstallationContext } from 'components/Contexts/InstallationContext'
import { BackendAPICaller } from 'api/ApiCaller'
import { EchoPlantInfo } from 'models/EchoMission'
import { Header } from 'components/Header/Header'
import { config } from 'config'

const Centered = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`

function handleLogin(instance: IPublicClientApplication) {
instance.loginRedirect(loginRequest).catch((e) => {
console.error(e)
})
}
const StyledTopBarContent = styled(TopBar.CustomContent)`
display: grid;
grid-template-columns: minmax(50px, 265px) auto;
gap: 0px 3rem;
align-items: center;
`
const BlockLevelContainer = styled.div`
& > * {
display: block;
}
`
const StyledCheckbox = styled(Checkbox)`
margin-left: -14px;
`
const RowContainer = styled.div`
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
margin-top: 50px;
`

export const AssetSelectionPage = () => {
const isAuthenticated = useIsAuthenticated()
const { instance } = useMsal()
const { TranslateText } = useLanguageContext()

useEffect(() => {
if (!isAuthenticated) {
handleLogin(instance)
}
}, [isAuthenticated, instance])

return (
<>
{isAuthenticated ? (
<>
<Header page={'root'} />
<Centered>
<RowContainer>
<StyledTopBarContent>{InstallationPicker()}</StyledTopBarContent>

<Button href={`${config.FRONTEND_BASE_ROUTE}/FrontPage`}>
{TranslateText('Confirm plant')}
</Button>
</RowContainer>
{/* TODO! ADD image here*/}
</Centered>
</>
) : (
<Centered>
<Typography variant="body_long_bold" color="primary">
Authentication
</Typography>
<CircularProgress size={48} color="primary" />
</Centered>
)}
</>
)
}

function InstallationPicker() {
const [allPlantsMap, setAllPlantsMap] = useState<Map<string, string>>(new Map())
const { installationName, switchInstallation } = useInstallationContext()
const { TranslateText } = useLanguageContext()
const [showActivePlants, setShowActivePlants] = useState<boolean>(true)
const [updateListOfActivePlants, setUpdateListOfActivePlants] = useState<boolean>(false)

useEffect(() => {
const plantPromise = showActivePlants ? BackendAPICaller.getActivePlants() : BackendAPICaller.getEchoPlantInfo()
plantPromise.then(async (response: EchoPlantInfo[]) => {
const mapping = mapInstallationCodeToName(response)
setAllPlantsMap(mapping)
})
}, [showActivePlants, updateListOfActivePlants])
const mappedOptions = allPlantsMap ? allPlantsMap : new Map<string, string>()
return (
<>
<BlockLevelContainer>
<Autocomplete
options={Array.from(mappedOptions.keys()).sort()}
label=""
initialSelectedOptions={[installationName]}
placeholder={TranslateText('Select installation')}
onOptionsChange={({ selectedItems }) => {
const selectedName = selectedItems[0]
switchInstallation(selectedName)
}}
autoWidth={true}
onFocus={(e) => {
e.preventDefault()
setUpdateListOfActivePlants(!updateListOfActivePlants)
}}
/>

<StyledCheckbox
label={TranslateText('Show only active installations')}
checked={showActivePlants}
onChange={(e) => setShowActivePlants(e.target.checked)}
/>
</BlockLevelContainer>
</>
)
}

const mapInstallationCodeToName = (echoPlantInfoArray: EchoPlantInfo[]): Map<string, string> => {
var mapping = new Map<string, string>()
echoPlantInfoArray.forEach((echoPlantInfo: EchoPlantInfo) => {
mapping.set(echoPlantInfo.projectDescription, echoPlantInfo.plantCode)
})
return mapping
}
4 changes: 3 additions & 1 deletion frontend/src/components/Pages/FlotillaSite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { RobotPage } from './RobotPage/RobotPage'
import { AuthProvider } from 'components/Contexts/AuthProvider'
import { APIUpdater } from 'components/Contexts/APIUpdater'
import { MissionDefinitionPage } from './MissionDefinitionPage/MissionDefinitionPage'
import { AssetSelectionPage } from './AssetSelectionPage/AssetSelectionPage'

export function FlotillaSite() {
return (
Expand All @@ -17,7 +18,8 @@ export function FlotillaSite() {
<APIUpdater>
<BrowserRouter>
<Routes>
<Route path={`${config.FRONTEND_BASE_ROUTE}/`} element={<FrontPage />} />
<Route path={`${config.FRONTEND_BASE_ROUTE}/`} element={<AssetSelectionPage />} />
<Route path={`${config.FRONTEND_BASE_ROUTE}/FrontPage`} element={<FrontPage />} />
<Route
path={`${config.FRONTEND_BASE_ROUTE}/mission/:missionId`}
element={<MissionPage />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function MissionRestartButton({ mission }: MissionProps) {

let navigate = useNavigate()
const navigateToHome = () => {
let path = `${config.FRONTEND_BASE_ROUTE}/`
let path = `${config.FRONTEND_BASE_ROUTE}/FrontPage`
navigate(path)
}

Expand Down
Loading

0 comments on commit 4cbb18f

Please sign in to comment.