frontend: PSI logo in footer + wider, shorter settings pages

Add PSI branding and use the horizontal space on the expert pages,
which previously stacked panels into one tall narrow column.

- Footer carries the official PSI logo (one of four variants picked at
  page load), standalone with clear space, linking to psi.ch. Top bar is
  a clean "Jungfraujoch" wordmark.
- PanelColumns masonry lays the processing/output/streaming/masks panels
  into responsive columns; ROI stays full width.
- DetectorSettings regrouped from a fixed-height single column into a
  responsive two-column form (layout only; controls unchanged).
- Softer corners (theme borderRadius 4 -> 8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 14:17:57 +02:00
co-authored by Claude Opus 4.8
parent fd7e2ae6f9
commit b474264a3f
7 changed files with 293 additions and 300 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+48 -12
View File
@@ -66,13 +66,40 @@ const drawerWidth = 240;
const drawerBg = indigo[50];
const drawerSelectedBg = indigo[100];
// PSI branding sits alone in the footer credit bar with clear space (never merged
// with the app wordmark or menu icon). The four official logos differ only in the
// diffused spots; pick one at module load so it is fixed per page load and never
// re-randomises on render. BASE_URL keeps the path correct under the /frontend/ base.
// The assets are the white "negative" logos; `brightness(0)` renders them as the
// approved solid-black logo for the light footer.
const PSI_LOGOS = [1, 2, 3, 4].map(i => `${import.meta.env.BASE_URL}psi_0${i}_sn.png`);
const PSI_LOGO = PSI_LOGOS[Math.floor(Math.random() * PSI_LOGOS.length)];
const jfjoch_theme = createTheme({
palette: {
primary: indigo,
secondary: lime,
},
// Slightly rounder corners than the MUI default (4px) give panels, inputs and
// buttons a softer, more modern feel without changing any component's behaviour.
shape: { borderRadius: 8 },
});
/** Flow a page's settings panels into responsive columns (masonry) so a page of
* small panels reads wide-and-short instead of as one tall single-file column.
* Panels never split across a column break. */
function PanelColumns({ columns = 2, children }: { columns?: number; children: ReactNode }) {
return (
<Box sx={{
columnCount: { xs: 1, md: columns },
columnGap: 2,
'& > *': { breakInside: 'avoid', mb: 2, display: 'block' },
}}>
{children}
</Box>
);
}
// An item either renders a panel inside the app (render) or links out to an
// external page in a new tab (href), e.g. the source repository.
type NavItem = { id: string; label: string; icon: ReactNode; disabled?: boolean;
@@ -173,31 +200,34 @@ function App() {
<DetectorSelection s={s.detector_list}/>
)},
{ id: 'masks', label: 'Masks', icon: <GridOnIcon/>, render: () => (
<Stack spacing={2}>
<PanelColumns>
<PixelMask s={s.pixel_mask}/>
<DarkMaskSettings s={s.dark_mask}/>
</Stack>
</PanelColumns>
)},
{ id: 'processing', label: 'On-the-fly processing', icon: <BoltIcon/>, render: () => (
<Stack spacing={2}>
<IndexingSettings s={s.indexing} status={s.broker}/>
<BraggIntegrationSettings s={s.bragg_integration}/>
<AzIntSettings s={s.az_int}/>
<PanelColumns>
<IndexingSettings s={s.indexing} status={s.broker}/>
<BraggIntegrationSettings s={s.bragg_integration}/>
<AzIntSettings s={s.az_int}/>
</PanelColumns>
{/* ROI holds three wide side-by-side tables — keep it full width. */}
<ROI s={s.roi}/>
</Stack>
)},
{ id: 'output', label: 'Output', icon: <SaveIcon/>, render: () => (
<Stack spacing={2}>
<PanelColumns>
<FileWriterSettings s={s.file_writer_settings}/>
<ImageFormatSettings s={s.image_format_settings}/>
<InstrumentMetadata s={s.instrument_metadata}/>
</Stack>
</PanelColumns>
)},
{ id: 'streaming', label: 'Streaming', icon: <WindPowerIcon/>, render: () => (
<Stack spacing={2}>
<PanelColumns>
<ZeroMQPreview s={s.zeromq_preview}/>
<ImagePusherStatus s={s.image_pusher}/>
</Stack>
</PanelColumns>
)},
{ id: 'calibration', label: 'JUNGFRAU calibration', icon: <StraightenIcon/>,
disabled: _.isEmpty(s.calibration), render: () => (
@@ -238,7 +268,7 @@ function App() {
'& .MuiDrawer-paper': { width: drawerWidth, boxSizing: 'border-box',
bgcolor: drawerBg, borderRight: 'none' } }}>
<Toolbar/>
<Box sx={{ overflow: 'auto', pb: 6 }}>
<Box sx={{ overflow: 'auto', pb: 9 }}>
{sections.map((sec, i) => (
<List key={i} subheader={sec.subheader
? <ListSubheader sx={{ bgcolor: 'transparent', color: 'primary.dark',
@@ -265,7 +295,7 @@ function App() {
</Box>
</Drawer>
)}
<Box component="main" sx={{ flexGrow: 1, p: 3, pb: '64px', minWidth: 0 }}>
<Box component="main" sx={{ flexGrow: 1, p: 3, pb: '84px', minWidth: 0 }}>
<Toolbar/>
<ErrorMessage s={s.broker}/>
<Box sx={{ maxWidth: 1300, mx: 'auto', mt: 2 }}>
@@ -277,8 +307,14 @@ function App() {
position: 'fixed', bottom: 0, left: 0, right: 0,
zIndex: (t) => t.zIndex.drawer + 1,
bgcolor: 'background.paper', borderTop: '1px solid', borderColor: 'divider',
py: 0.5, px: 2, textAlign: 'center', fontSize: '0.8rem',
py: 1.5, px: 2, textAlign: 'center', fontSize: '0.8rem',
}}>
<Link href="https://www.psi.ch" target="_blank" rel="noopener" aria-label="Paul Scherrer Institute"
sx={{ position: 'absolute', left: 24, top: '50%', transform: 'translateY(-50%)',
display: 'inline-flex' }}>
<Box component="img" src={PSI_LOGO} alt="PSI"
sx={{ height: 34, width: 'auto', filter: 'brightness(0)', opacity: 0.8 }}/>
</Link>
Developed at{' '}
<Link href="https://www.psi.ch" target="_blank" rel="noopener">Paul Scherrer Institute</Link>
{' '}(20192026). Main author:{' '}
+239 -283
View File
@@ -1,6 +1,6 @@
import {ChangeEvent, memo, useEffect, useState} from 'react';
import {Box, Checkbox, FormControlLabel, FormGroup, Grid, List, ListItem, Switch, Tooltip} from "@mui/material";
import {Box, Checkbox, FormControlLabel, FormGroup, Grid, List, ListItem, Stack, Switch, Tooltip} from "@mui/material";
import Paper from "@mui/material/Paper";
import FormControl from "@mui/material/FormControl";
import InputLabel from "@mui/material/InputLabel";
@@ -146,295 +146,251 @@ function DetectorSettings({s: serverS}: MyProps) {
const dirty = !_.isEqual(s, lastDownloadedS);
return <Paper style={{textAlign: 'center'}} sx={{ height: 1150, width: '100%' }}>
// Two responsive columns keep this large form wide-and-short instead of one
// very tall centred strip: acquisition/timing on the left, JUNGFRAU + EIGER on
// the right, with the title on top and the action buttons spanning the bottom.
return <Paper style={{textAlign: 'center'}} sx={{ p: 3, width: '100%' }}>
<PanelTitle title="Detector settings" dirty={dirty}/>
<Grid container>
<Grid item xs={1}/>
<Grid item xs={10}>
<br/><PanelTitle title="Detector settings" dirty={dirty}/>
<br/><br/><br/>
<NumberTextField start_val={s.frame_time_us}
label={"Internal frame time"}
units={"us"}
min={450}
fullWidth
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setFrameTimeErr(err);
setS(prev => ({...prev, frame_time_us: val}));
}}/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<Box sx={{display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 1, my: 2}}>
<Switch onChange={countTimeToggle}
checked={s.count_time_us !== undefined}/>
<NumberTextField
start_val={s.count_time_us}
disabled={s.count_time_us === undefined}
label={"Count time"}
units={"us"}
min={0}
max={1980}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setCountTimeErr(err);
setS(prev => ({...prev, count_time_us: val}));
}}/>
</Box>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<NumberTextField start_val={s.detector_trigger_delay_ns}
label={"Detector delay"}
units={"ns"}
default={0}
min={0}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setDetectorTriggerDelayErr(err);
setS(prev => ({...prev, detector_trigger_delay_ns: val}));
}}
sx={{width: 120}}
/>&nbsp;&nbsp;
<FormControl>
<InputLabel id="timing-select-label">Timing mode</InputLabel>
<Select
sx={{width: 120}}
labelId="timing-select-label"
id="timing-select"
value={timingModeListValue}
label="Storage cell count"
onChange={handleTimingChange}
>
<MenuItem value={"trigger"}>Trigger</MenuItem>
<MenuItem value={"auto"}>Auto</MenuItem>
<MenuItem value={"burst"}>Burst (EIGER)</MenuItem>
<MenuItem value={"gated"}>Gated (EIGER)</MenuItem>
</Select>
</FormControl>
<br/><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<b>Internal image generator (FPGA) </b><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={3}/>
<Grid item xs={6}>
<br/>
<List>
<Tooltip title="Internal generator allows to test Jungfraujoch without having detector connected.">
<ListItem>
<Checkbox onChange={internalFrameGeneratorToggle}
checked={s.internal_frame_generator === true}/>
<FormControl fullWidth>
<NumberTextField start_val={s.internal_frame_generator_images}
label={"Images"}
min={1}
default={1}
max={128}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setInternalFrameGeneratorImagesErr(err);
setS(prev => ({...prev, internal_frame_generator_images: val}));
}}
disabled={!s.internal_frame_generator}
fullWidth/>
</FormControl>
</ListItem>
</Tooltip>
</List>
</Grid>
<Grid item xs={3}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<b>JUNGFRAU settings </b><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={3}/>
<Grid item xs={6}>
<FormGroup>
<FormControlLabel control={
<Checkbox onChange={useGainHG0Toggle} checked={s.jungfrau_use_gain_hg0 === true}/>
} label="HG0"/>
<FormControlLabel control={
<Checkbox onChange={fixedGainG1Toggle} checked={s.jungfrau_fixed_gain_g1 === true}/>
} label="Fixed G1"/>
</FormGroup><br/>
</Grid>
<Grid item xs={3}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<b>JUNGFRAU storage cells </b><br/><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<FormControl>
<InputLabel id="demo-simple-select-label">Count</InputLabel>
<Select
sx={{width: 120}}
labelId="demo-simple-select-label"
id="demo-simple-select"
value={storageCellListValue}
label="Storage cell count"
onChange={handleChange}
>
<MenuItem value={"1"}>1</MenuItem>
<MenuItem value={"2"}>2</MenuItem>
<MenuItem value={"4"}>4</MenuItem>
<MenuItem value={"8"}>8</MenuItem>
<MenuItem value={"15"}>15</MenuItem>
<MenuItem value={"16"}>16</MenuItem>
</Select>
</FormControl>&nbsp;&nbsp;
<NumberTextField start_val={s.jungfrau_storage_cell_delay_ns}
label={"Delay"}
units={"ns"}
min={2100}
default={2100}
counter={downloadCounter}
sx={{ width: "140px" }}
callback={(val: number, err: boolean) => {
setStorageCellDelayErr(err);
setS(prev => ({...prev, jungfrau_storage_cell_delay_ns: val}));
}}
disabled={storageCellListValue === "1"}
fullWidth/>
<br/><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<b>JUNGFRAU Pedestal</b><br/><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<NumberTextField start_val={s.jungfrau_pedestal_g0_frames}
label={"G0"}
min={0}
default={2000}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalG0FramesErr(err);
setS(prev => ({...prev, jungfrau_pedestal_g0_frames: val}));
}}
sx={{width: "80px"}}/>&nbsp;
<NumberTextField start_val={s.jungfrau_pedestal_g1_frames}
label={"G1"}
min={0}
default={300}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalG1FramesErr(err);
setS(prev => ({...prev, jungfrau_pedestal_g1_frames: val}));
}}
sx={{width: "80px"}}/>&nbsp;
<NumberTextField start_val={s.jungfrau_pedestal_g2_frames}
label={"G2"}
min={0}
default={300}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalG2FramesErr(err);
setS(prev => ({...prev, jungfrau_pedestal_g2_frames: val}));
}}
sx={{width: "80px"}}/>
&nbsp;<br/><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<NumberTextField start_val={s.jungfrau_pedestal_min_image_count}
label={"Min. image count"}
min={0}
default={128}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalMinImageCountErr(err);
setS(prev => ({...prev, jungfrau_pedestal_min_image_count: val}));
}}
sx={{width: "120px"}}/><br/><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<b>EIGER settings </b><br/><br/>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<ListItem>
<Checkbox onChange={eigerThresholdToggle}
checked={s.eiger_threshold_keV !== undefined}/>
<FormControl fullWidth>
<Grid container spacing={4} sx={{ mt: 1 }}>
<Grid item xs={12} md={6}>
<Stack spacing={3} alignItems="center">
<NumberTextField start_val={s.frame_time_us}
label={"Internal frame time"}
units={"us"}
min={450}
fullWidth
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setFrameTimeErr(err);
setS(prev => ({...prev, frame_time_us: val}));
}}/>
<Box sx={{display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 1}}>
<Switch onChange={countTimeToggle}
checked={s.count_time_us !== undefined}/>
<NumberTextField
default={eigerThresholdKeVOld}
start_val={s.eiger_threshold_keV}
label={"Energy threshold"}
min={1.0}
max={100.0}
units={"keV"}
float={true}
start_val={s.count_time_us}
disabled={s.count_time_us === undefined}
label={"Count time"}
units={"us"}
min={0}
max={1980}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
if (!err)
setEigerThresholdKeVOld(val);
setEigerThresholdErr(err);
setS(prev => ({...prev, eiger_threshold_keV: val}));
}}
disabled={s.eiger_threshold_keV === undefined}
/>
</FormControl>&nbsp;
<FormControl>
<InputLabel id="demo-simple-select-label">Read-out</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
label="Detector"
value={bitWidthSelection}
onChange={handleBitWidthChange}
sx={{minWidth:106}}
>
<MenuItem value={"a"}>Auto</MenuItem>
<MenuItem value={"8"}>8-bit</MenuItem>
<MenuItem value={"16"}>16-bit</MenuItem>
<MenuItem value={"32"}>32-bit</MenuItem>
</Select>
</FormControl><br/><br/>
</ListItem><br/>
setCountTimeErr(err);
setS(prev => ({...prev, count_time_us: val}));
}}/>
</Box>
<Box sx={{display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 1}}>
<NumberTextField start_val={s.detector_trigger_delay_ns}
label={"Detector delay"}
units={"ns"}
default={0}
min={0}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setDetectorTriggerDelayErr(err);
setS(prev => ({...prev, detector_trigger_delay_ns: val}));
}}
sx={{width: 120}}
/>
<FormControl>
<InputLabel id="timing-select-label">Timing mode</InputLabel>
<Select
sx={{width: 120}}
labelId="timing-select-label"
id="timing-select"
value={timingModeListValue}
label="Storage cell count"
onChange={handleTimingChange}
>
<MenuItem value={"trigger"}>Trigger</MenuItem>
<MenuItem value={"auto"}>Auto</MenuItem>
<MenuItem value={"burst"}>Burst (EIGER)</MenuItem>
<MenuItem value={"gated"}>Gated (EIGER)</MenuItem>
</Select>
</FormControl>
</Box>
<Box sx={{ width: '100%' }}>
<b>Internal image generator (FPGA)</b>
<List>
<Tooltip title="Internal generator allows to test Jungfraujoch without having detector connected.">
<ListItem>
<Checkbox onChange={internalFrameGeneratorToggle}
checked={s.internal_frame_generator === true}/>
<FormControl fullWidth>
<NumberTextField start_val={s.internal_frame_generator_images}
label={"Images"}
min={1}
default={1}
max={128}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setInternalFrameGeneratorImagesErr(err);
setS(prev => ({...prev, internal_frame_generator_images: val}));
}}
disabled={!s.internal_frame_generator}
fullWidth/>
</FormControl>
</ListItem>
</Tooltip>
</List>
</Box>
<b>EIGER settings</b>
<ListItem>
<Checkbox onChange={eigerThresholdToggle}
checked={s.eiger_threshold_keV !== undefined}/>
<FormControl fullWidth>
<NumberTextField
default={eigerThresholdKeVOld}
start_val={s.eiger_threshold_keV}
label={"Energy threshold"}
min={1.0}
max={100.0}
units={"keV"}
float={true}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
if (!err)
setEigerThresholdKeVOld(val);
setEigerThresholdErr(err);
setS(prev => ({...prev, eiger_threshold_keV: val}));
}}
disabled={s.eiger_threshold_keV === undefined}
/>
</FormControl>&nbsp;
<FormControl>
<InputLabel id="demo-simple-select-label">Read-out</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
label="Detector"
value={bitWidthSelection}
onChange={handleBitWidthChange}
sx={{minWidth:106}}
>
<MenuItem value={"a"}>Auto</MenuItem>
<MenuItem value={"8"}>8-bit</MenuItem>
<MenuItem value={"16"}>16-bit</MenuItem>
<MenuItem value={"32"}>32-bit</MenuItem>
</Select>
</FormControl>
</ListItem>
</Stack>
</Grid>
<Grid item xs={1}/>
<Grid item xs={1}/>
<Grid item xs={10}>
<Button color={dirty ? "warning" : "primary"} variant="contained" disableElevation
onClick={() => submit(s)}
disabled={pending
|| countTimeErr
|| frameTimeErr
|| internalFrameGeneratorImagesErr
|| detectorTriggerDelayErr
|| storageCellDelayErr
|| pedestalG0FramesErr
|| pedestalG1FramesErr
|| pedestalG2FramesErr
|| pedestalMinImageCountErr}>Upload</Button>&nbsp;&nbsp;
<ButtonWithSnackbar
color={"primary"}
path={"/deactivate"}
text={"Deactivate"}
/>
<br/><br/>
<Grid item xs={12} md={6}>
<Stack spacing={3} alignItems="center">
<Box>
<b>JUNGFRAU settings</b>
<FormGroup>
<FormControlLabel control={
<Checkbox onChange={useGainHG0Toggle} checked={s.jungfrau_use_gain_hg0 === true}/>
} label="HG0"/>
<FormControlLabel control={
<Checkbox onChange={fixedGainG1Toggle} checked={s.jungfrau_fixed_gain_g1 === true}/>
} label="Fixed G1"/>
</FormGroup>
</Box>
<Box sx={{display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 1}}>
<FormControl>
<InputLabel id="demo-simple-select-label">Count</InputLabel>
<Select
sx={{width: 120}}
labelId="demo-simple-select-label"
id="demo-simple-select"
value={storageCellListValue}
label="Storage cell count"
onChange={handleChange}
>
<MenuItem value={"1"}>1</MenuItem>
<MenuItem value={"2"}>2</MenuItem>
<MenuItem value={"4"}>4</MenuItem>
<MenuItem value={"8"}>8</MenuItem>
<MenuItem value={"15"}>15</MenuItem>
<MenuItem value={"16"}>16</MenuItem>
</Select>
</FormControl>
<NumberTextField start_val={s.jungfrau_storage_cell_delay_ns}
label={"Delay"}
units={"ns"}
min={2100}
default={2100}
counter={downloadCounter}
sx={{ width: "140px" }}
callback={(val: number, err: boolean) => {
setStorageCellDelayErr(err);
setS(prev => ({...prev, jungfrau_storage_cell_delay_ns: val}));
}}
disabled={storageCellListValue === "1"}
fullWidth/>
</Box>
<b>JUNGFRAU Pedestal</b>
<Stack direction="row" spacing={1} justifyContent="center">
<NumberTextField start_val={s.jungfrau_pedestal_g0_frames}
label={"G0"}
min={0}
default={2000}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalG0FramesErr(err);
setS(prev => ({...prev, jungfrau_pedestal_g0_frames: val}));
}}
sx={{width: "80px"}}/>
<NumberTextField start_val={s.jungfrau_pedestal_g1_frames}
label={"G1"}
min={0}
default={300}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalG1FramesErr(err);
setS(prev => ({...prev, jungfrau_pedestal_g1_frames: val}));
}}
sx={{width: "80px"}}/>
<NumberTextField start_val={s.jungfrau_pedestal_g2_frames}
label={"G2"}
min={0}
default={300}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalG2FramesErr(err);
setS(prev => ({...prev, jungfrau_pedestal_g2_frames: val}));
}}
sx={{width: "80px"}}/>
</Stack>
<NumberTextField start_val={s.jungfrau_pedestal_min_image_count}
label={"Min. image count"}
min={0}
default={128}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setPedestalMinImageCountErr(err);
setS(prev => ({...prev, jungfrau_pedestal_min_image_count: val}));
}}
sx={{width: "120px"}}/>
</Stack>
</Grid>
<Grid item xs={1}/>
</Grid>
<Box sx={{ mt: 4 }}>
<Button color={dirty ? "warning" : "primary"} variant="contained" disableElevation
onClick={() => submit(s)}
disabled={pending
|| countTimeErr
|| frameTimeErr
|| internalFrameGeneratorImagesErr
|| detectorTriggerDelayErr
|| storageCellDelayErr
|| pedestalG0FramesErr
|| pedestalG1FramesErr
|| pedestalG2FramesErr
|| pedestalMinImageCountErr}>Upload</Button>&nbsp;&nbsp;
<ButtonWithSnackbar
color={"primary"}
path={"/deactivate"}
text={"Deactivate"}
/>
</Box>
<UploadSnackbarView snackbar={snackbar}/>
</Paper>
}
+6 -5
View File
@@ -4,6 +4,7 @@ import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import Box from '@mui/material/Box';
import MenuIcon from '@mui/icons-material/Menu';
import {broker_status} from "../client";
import ButtonWithSnackbar from "./ButtonWithSnackbar";
@@ -40,16 +41,16 @@ function StatusBar({s, onMenuClick}: MyProps) {
<MenuIcon/>
</IconButton>
)}
<Typography variant="h6" style={{flexGrow: 0.5}}>
PSI Jungfraujoch
<Typography variant="h6" component="div" noWrap
sx={{flexGrow: 0.5, fontWeight: 500, letterSpacing: '.02em'}}>
Jungfraujoch
</Typography>
<Typography variant="h6" style={{flexGrow: 2.0}}>
<Typography variant="h6" component="div" sx={{flexGrow: 2.0, textAlign: 'center'}}>
{statusDescription()}
</Typography>
<Typography variant="h6" style={{flexGrow: 2.0}}>
</Typography>
<Box sx={{flexGrow: 2.0}}/>
<ButtonWithSnackbar
text={"Pedestal"}
path={"/pedestal"}