diff --git a/frontend/public/psi_01.png b/frontend/public/psi_01.png new file mode 100644 index 00000000..49bbde89 Binary files /dev/null and b/frontend/public/psi_01.png differ diff --git a/frontend/public/psi_01_sn.png b/frontend/public/psi_01_sn.png deleted file mode 100644 index d5ad912f..00000000 Binary files a/frontend/public/psi_01_sn.png and /dev/null differ diff --git a/frontend/public/psi_02.png b/frontend/public/psi_02.png new file mode 100644 index 00000000..c2af10a1 Binary files /dev/null and b/frontend/public/psi_02.png differ diff --git a/frontend/public/psi_02_sn.png b/frontend/public/psi_02_sn.png deleted file mode 100644 index f7183b7f..00000000 Binary files a/frontend/public/psi_02_sn.png and /dev/null differ diff --git a/frontend/public/psi_03.png b/frontend/public/psi_03.png new file mode 100644 index 00000000..d6c7269b Binary files /dev/null and b/frontend/public/psi_03.png differ diff --git a/frontend/public/psi_03_sn.png b/frontend/public/psi_03_sn.png deleted file mode 100644 index a21c6e4f..00000000 Binary files a/frontend/public/psi_03_sn.png and /dev/null differ diff --git a/frontend/public/psi_04.png b/frontend/public/psi_04.png new file mode 100644 index 00000000..d988f56f Binary files /dev/null and b/frontend/public/psi_04.png differ diff --git a/frontend/public/psi_04_sn.png b/frontend/public/psi_04_sn.png deleted file mode 100644 index 597e61eb..00000000 Binary files a/frontend/public/psi_04_sn.png and /dev/null differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 0ac7c7ea..f7829836 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -70,9 +70,8 @@ const drawerSelectedBg = indigo[100]; // 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`); +// These are the positive (dark) logos, used as-is on the light footer. +const PSI_LOGOS = [1, 2, 3, 4].map(i => `${import.meta.env.BASE_URL}psi_0${i}.png`); const PSI_LOGO = PSI_LOGOS[Math.floor(Math.random() * PSI_LOGOS.length)]; const jfjoch_theme = createTheme({ @@ -313,7 +312,7 @@ function App() { sx={{ position: 'absolute', left: 24, top: '50%', transform: 'translateY(-50%)', display: 'inline-flex' }}> + sx={{ height: 34, width: 'auto', opacity: 0.85 }}/> Developed at{' '} Paul Scherrer Institute diff --git a/frontend/src/components/ButtonWithSnackbar.tsx b/frontend/src/components/ButtonWithSnackbar.tsx index 7e000430..ebe00c9a 100644 --- a/frontend/src/components/ButtonWithSnackbar.tsx +++ b/frontend/src/components/ButtonWithSnackbar.tsx @@ -5,17 +5,20 @@ import { } from "@mui/material"; import Button from "@mui/material/Button"; import Snackbar from "@mui/material/Snackbar"; +import { SxProps, Theme } from "@mui/material/styles"; type MyProps = { input?: string, disabled?: boolean, path: string, text: string, - color?: "primary" | "secondary", + color?: "primary" | "secondary" | "inherit", + variant?: "text" | "outlined" | "contained", + sx?: SxProps, method?: "GET" | "POST" | "PUT" } -function ButtonWithSnackbar({input, disabled, path, text, color, method}: MyProps) { +function ButtonWithSnackbar({input, disabled, path, text, color, variant, sx, method}: MyProps) { const [snackbarOpen, setSnackbarOpen] = useState(false); const [startSuccess, setStartSuccess] = useState(true); const [startError, setStartError] = useState(); @@ -65,15 +68,17 @@ function ButtonWithSnackbar({input, disabled, path, text, color, method}: MyProp + onClose={handleClose} + sx={{ bottom: { xs: 80 } }}> No plots available; + return } text="No plots available"/>; let data: PlotlyData = []; if ((plots.plot[0].z !== undefined) diff --git a/frontend/src/components/DetectorSettings.tsx b/frontend/src/components/DetectorSettings.tsx index cfad2112..ee4888bc 100644 --- a/frontend/src/components/DetectorSettings.tsx +++ b/frontend/src/components/DetectorSettings.tsx @@ -1,6 +1,6 @@ import {ChangeEvent, memo, useEffect, useState} from 'react'; -import {Box, Checkbox, FormControlLabel, FormGroup, Grid, List, ListItem, Stack, Switch, Tooltip} from "@mui/material"; +import {Box, Checkbox, Divider, 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"; @@ -12,7 +12,7 @@ import NumberTextField from "./NumberTextField"; import _ from "lodash"; import ButtonWithSnackbar from "./ButtonWithSnackbar"; import Button from "@mui/material/Button"; -import {PanelTitle, UploadSnackbarView} from "./SettingsPanel"; +import {PanelTitle, UploadSnackbarView, sectionHeadingSx, panelHeaderSx} from "./SettingsPanel"; import {useUpload} from "./useUpload"; type MyProps = { @@ -149,10 +149,13 @@ function DetectorSettings({s: serverS}: MyProps) { // 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 - - - + return + + + + + + + } diff --git a/frontend/src/components/DetectorStatus.tsx b/frontend/src/components/DetectorStatus.tsx index f01622d3..63ca42bb 100644 --- a/frontend/src/components/DetectorStatus.tsx +++ b/frontend/src/components/DetectorStatus.tsx @@ -5,6 +5,20 @@ import {Table, TableBody, TableCell, TableContainer, TableRow,} from "@mui/mater import {detector_status, detector_state, detector_power_state} from "../client"; import SettingsPanel from "./SettingsPanel"; +// Colour the state/power values so a healthy detector reads green at a glance and +// a disconnected or partially-powered one stands out. +function stateColor(state: detector_state): string { + return state === detector_state.NOT_CONNECTED ? "text.secondary" : "success.main"; +} + +function powerColor(p: detector_power_state): string { + switch (p) { + case detector_power_state.POWER_ON: return "success.main"; + case detector_power_state.PARTIAL: return "warning.main"; + default: return "text.secondary"; + } +} + type MyProps = { s?: detector_status } @@ -54,11 +68,13 @@ function DetectorStatus({s}: MyProps) { Detector state: - {(det.state.toString())} + + {det.state.toString()} Detector ASIC power: - {powerchipToString(det)} + + {powerchipToString(det)} Triggers remaining: diff --git a/frontend/src/components/EmptyState.tsx b/frontend/src/components/EmptyState.tsx new file mode 100644 index 00000000..e53bf1ae --- /dev/null +++ b/frontend/src/components/EmptyState.tsx @@ -0,0 +1,17 @@ +import { ReactNode } from "react"; +import { Box, Typography } from "@mui/material"; + +/** A calm placeholder for "no data yet" areas (empty plots, previews, tables): + * a muted icon over a short line of helper text, centred in its container. */ +function EmptyState({ icon, text }: { icon: ReactNode; text: string }) { + return ( + + {icon} + {text} + + ); +} + +export default EmptyState; diff --git a/frontend/src/components/SettingsPanel.tsx b/frontend/src/components/SettingsPanel.tsx index 620527ec..2b243da2 100644 --- a/frontend/src/components/SettingsPanel.tsx +++ b/frontend/src/components/SettingsPanel.tsx @@ -1,14 +1,35 @@ import { ReactNode } from "react"; -import { Alert, Box, Button, Paper, Snackbar, Stack, Tooltip, Typography } from "@mui/material"; +import { Alert, Box, Button, Divider, Paper, Snackbar, Stack, Tooltip, Typography } from "@mui/material"; +import { indigo } from "@mui/material/colors"; import { SxProps, Theme } from "@mui/material/styles"; +import { SystemStyleObject } from "@mui/system"; import { TypographyProps } from "@mui/material/Typography"; import { UploadSnackbar } from "./useUpload"; +/** The panel title row: a light-blue (sidebar) band with a lime left accent, echoing + * the selected sidebar entry so the cards feel part of the same system. Exported so + * DetectorSettings (which doesn't use SettingsPanel) can match. */ +export const panelHeaderSx = { + px: 3, py: 1.75, bgcolor: indigo[50], borderLeft: "4px solid", borderColor: "secondary.main", +}; + +/** Uniform look for the inline section labels used inside the config panels: + * small uppercase captions in the brand colour, instead of ad-hoc bold text. + * Exported so panels that don't use SettingsPanel (e.g. DetectorSettings) match. + * Typed as a plain style object so it composes inside an sx={[...]} array. */ +export const sectionHeadingSx: SystemStyleObject = { + "& b": { + display: "block", mt: 1, color: "primary.dark", fontSize: "0.78rem", + fontWeight: 700, letterSpacing: ".05em", textTransform: "uppercase", + }, +}; + /** The upload-result snackbar, usable on its own by panels that don't use SettingsPanel. */ export function UploadSnackbarView({ snackbar }: { snackbar?: UploadSnackbar }) { if (!snackbar) return null; return ( - + {snackbar.success ? "Ok!" : snackbar.message} @@ -62,9 +83,16 @@ function SettingsPanel({ children, }: SettingsPanelProps) { return ( - - - {title !== undefined && } + + {title !== undefined && ( + <> + + + + + + )} + {children} {onUpload && (