Files
Jungfraujoch/frontend/src/components/AzIntSettings.tsx
T
leonarski_fandClaude Opus 4.8 237de5a0fc frontend: convert to function components + TanStack Query
Replace all class components with function components and move server-state
polling to @tanstack/react-query.

- add @tanstack/react-query; generate query helpers via the hey-api
  @tanstack/react-query plugin (src/client/@tanstack)
- QueryClientProvider + one-time client.setConfig({ baseUrl: '' }) in index.tsx
- App polls statistics with useQuery(getStatisticsOptions, refetchInterval),
  DataProcessingPlot uses getPreviewPlotOptions; manual setInterval polling gone
- memo() on presentational children; with TanStack structural sharing this
  re-renders a child only when its own statistics slice changes
- PreviewImage stays imperative (binary JPEG -> object URL) using useEffect +
  a ref for the object-URL lifecycle
- fix AzIntSettings correction checkboxes that mutated state in place (relied on
  the poll re-render); they now use setState
- drop dead code uncovered during the port (unused upload/deactivate handlers
  and imports in ImageFormatSettings, DetectorSettings, ROI)

Build (tsc + vite) passes; dev server transforms all entry modules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:32:36 +02:00

161 lines
6.0 KiB
TypeScript

import {ChangeEvent, memo, useEffect, useState} from 'react';
import Paper from '@mui/material/Paper';
import {FormControlLabel, Checkbox, Stack, Radio, RadioGroup, Typography} from "@mui/material";
import NumberTextField from "./NumberTextField";
import {azim_int_settings} from "../client";
import _ from "lodash";
import ButtonWithSnackbar from "./ButtonWithSnackbar";
import FormControl from "@mui/material/FormControl";
type MyProps = {
s?: azim_int_settings
};
const default_azim_int_settings : azim_int_settings = {
solid_angle_corr: true,
polarization_corr: true,
high_q_recipA: 5,
low_q_recipA: 0.1,
q_spacing: 0.1,
azimuthal_bins: 1,
force_cpu: false
}
function AzIntSettings({s: serverS}: MyProps) {
const [s, setS] = useState<azim_int_settings>(default_azim_int_settings);
const [lastDownloadedS, setLastDownloadedS] = useState<azim_int_settings>(default_azim_int_settings);
const [downloadCounter, setDownloadCounter] = useState(0);
const [lowQError, setLowQError] = useState(false);
const [highQError, setHighQError] = useState(false);
const [qSpacingError, setQSpacingError] = useState(false);
useEffect(() => {
if ((serverS !== undefined) && !_.isEqual(serverS, lastDownloadedS)) {
setS(serverS);
setLastDownloadedS(serverS);
setDownloadCounter(c => c + 1);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [serverS]);
return <Paper style={{textAlign: 'center'}} sx={{ width: '100%'}}>
<br/>
<Stack spacing={3} sx={{
justifyContent: "center",
alignItems: "center",
marginLeft: '8%',
marginRight: '8%',
}}>
<div><strong>Azimuthal integration settings </strong></div>
<Stack spacing={2} direction="row">
<NumberTextField
default={1.0}
start_val={s.q_spacing}
label={"Q spacing"}
min={1e-5}
units={"A-1"}
float={true}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setS(prev => ({...prev, q_spacing: val}));
setQSpacingError(err);
}}
fullWidth/>
<NumberTextField
default={0.1}
start_val={s.low_q_recipA}
label={"Low Q"}
min={1e-5}
max={10.0}
units={"A-1"}
float={true}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setS(prev => ({...prev, low_q_recipA: val}));
setLowQError(err);
}}
fullWidth/>
<NumberTextField
default={5}
start_val={s.high_q_recipA}
label={"High Q"}
min={1e-5}
max={10.0}
units={"A-1"}
float={true}
counter={downloadCounter}
callback={(val: number, err: boolean) => {
setS(prev => ({...prev, high_q_recipA: val}));
setHighQError(err);
}}
fullWidth/>
</Stack>
<FormControl>
<FormControlLabel control={
<Checkbox
checked={s.solid_angle_corr}
onChange={
(event: ChangeEvent<HTMLInputElement>) => {
setS(prev => ({...prev, solid_angle_corr: event.target.checked}));
}}
/>} label="Solid angle correction"/>
<FormControlLabel control={
<Checkbox
checked={s.polarization_corr}
onChange={
(event: ChangeEvent<HTMLInputElement>) => {
setS(prev => ({...prev, polarization_corr: event.target.checked}));
}}
/>} label="Polarization correction"/>
<FormControlLabel control={
<Checkbox
checked={s.force_cpu ?? false}
onChange={
(event: ChangeEvent<HTMLInputElement>) => {
setS(prev => ({...prev, force_cpu: event.target.checked}));
}}
/>} label="Force CPU calculation in FPGA workflow"/>
</FormControl>
<Typography>Azimuthal bins</Typography>
<FormControl component="fieldset">
<RadioGroup
row
value={s.azimuthal_bins}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setS(prev => ({...prev, azimuthal_bins: Number(event.target.value)}));
}}
>
{[1, 2, 4, 8, 16, 32, 64, 128].map((value) => (
<FormControlLabel
key={value}
value={value}
control={<Radio/>}
label={value.toString()}
/>
))}
</RadioGroup>
</FormControl>
<ButtonWithSnackbar
color={"primary"}
path={"/config/azim_int"}
input={JSON.stringify(s)}
method={"PUT"}
text={"Upload"}
disabled={
(s.high_q_recipA <= s.low_q_recipA)
|| highQError
|| lowQError
|| qSpacingError
}
/>
</Stack>
<br/>
</Paper>
}
export default memo(AzIntSettings);