From b2d0be44dad20f5514e144fbab173c3b982da95b Mon Sep 17 00:00:00 2001 From: Jessica V Date: Thu, 4 Jun 2026 17:35:58 +0100 Subject: [PATCH 1/7] Add type definitions --- frontend/i14/src/types/workflowFields.ts | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 frontend/i14/src/types/workflowFields.ts diff --git a/frontend/i14/src/types/workflowFields.ts b/frontend/i14/src/types/workflowFields.ts new file mode 100644 index 00000000..320f60e2 --- /dev/null +++ b/frontend/i14/src/types/workflowFields.ts @@ -0,0 +1,2 @@ +type WorkflowFormData = { visit: string; workflow: string }; +type Option = { label: string; value: string; desc?: string }; From b9e453473d3d97f91d50f5f03b76cc45bde1c91d Mon Sep 17 00:00:00 2001 From: Jessica V Date: Thu, 4 Jun 2026 17:47:24 +0100 Subject: [PATCH 2/7] Add initial form data and workflow data --- frontend/i14/src/data/form.ts | 6 ++++++ frontend/i14/src/data/workflows.ts | 34 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 frontend/i14/src/data/form.ts create mode 100644 frontend/i14/src/data/workflows.ts diff --git a/frontend/i14/src/data/form.ts b/frontend/i14/src/data/form.ts new file mode 100644 index 00000000..ffa5c836 --- /dev/null +++ b/frontend/i14/src/data/form.ts @@ -0,0 +1,6 @@ +import type { WorkflowFormData } from "../types/workflowFormData"; + +export const initialData: WorkflowFormData = { + visit: "mg23967-1", + workflow: "dpc-batch", +}; diff --git a/frontend/i14/src/data/workflows.ts b/frontend/i14/src/data/workflows.ts new file mode 100644 index 00000000..f302faca --- /dev/null +++ b/frontend/i14/src/data/workflows.ts @@ -0,0 +1,34 @@ +import type { Option } from "../types/workflowFormData"; + +export const workflowOptions: Option[] = [ + { + label: "DPC", + value: "dpc-batch", + desc: "DPC imaging produces an image of the phase shifts to the X-ray beam as a result of interaction with the sample by measuring the gradient of the phase and retrieving the phase shifts by a straightforward integration step.", + }, + { + label: "XANES Auto-processing", + value: "xanes", + desc: "XANES is a utility which attempts to stack a sequence of datasets acquired at different energy for a particular line group and perform alignment based on a line group.", + }, + { + label: "XANES Point", + value: "xanes-point", + desc: "XANES-point is a utility which takes in a scan file (inpath) and a line group (edge_element), performs windowing of this line group, and saves a two-column text file (outpath): the first column is the energy in keV, and the second column is the summed windowed MCA intensity across the 4 channels.", + }, + { + label: "XANES Sparse", + value: "xanes-sparse", + desc: "XANES-sparse is a utility which takes the last scan file of a sparse XANES scan (inpath), defines the 2D full grid, inserts the data in the correct rows, stack the images, and completes the missing data by using looped alternating steepest descent (ASD).", + }, + { + label: "XRD 1D", + value: "xrd1d-batch", + desc: "XRD can be used to spatially map changes in crystallographic direction, d-spacing or strain across a sample.", + }, + { + label: "XRD 2D", + value: "xrd2d-batch", + desc: "XRD 2D is a utility which performs Azimuthal integration (ExcaliburXRDIntegration) and saves result to a nxs file", + }, +]; From 6cfcede5e1c389ef0dcbf602b8787130dd357596 Mon Sep 17 00:00:00 2001 From: Jessica V Date: Thu, 4 Jun 2026 17:48:24 +0100 Subject: [PATCH 3/7] Add OptionSelect component --- .../src/components/workflows/OptionSelect.tsx | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 frontend/i14/src/components/workflows/OptionSelect.tsx diff --git a/frontend/i14/src/components/workflows/OptionSelect.tsx b/frontend/i14/src/components/workflows/OptionSelect.tsx new file mode 100644 index 00000000..58dc86b8 --- /dev/null +++ b/frontend/i14/src/components/workflows/OptionSelect.tsx @@ -0,0 +1,51 @@ +import React, { FC } from "react"; +import { + FormControl, + InputLabel, + Select, + IconButton, + Tooltip, + Grid, +} from "@mui/material"; +import InfoIcon from "@mui/icons-material/Info"; +import type { SelectChangeEvent } from "@mui/material/Select"; +import type { Option } from "../../types/workflowFields"; + +type Props = { + label: string; + value: string; + options: Option[]; + onChange: (e: SelectChangeEvent) => void; +}; + +const OptionSelect: FC = ({ label, value, options, onChange }) => { + const selected = options.find((o) => o.value === value); + + return ( + + {label} + + + + + + + + + + ); +}; + +export default OptionSelect; From 7de3c7c76ac595795370e1848a266db092f3fcb0 Mon Sep 17 00:00:00 2001 From: Jessica V Date: Thu, 4 Jun 2026 17:53:05 +0100 Subject: [PATCH 4/7] Update WorkflowForm --- .../src/components/workflows/WorkflowForm.tsx | 160 +++++------------- 1 file changed, 44 insertions(+), 116 deletions(-) diff --git a/frontend/i14/src/components/workflows/WorkflowForm.tsx b/frontend/i14/src/components/workflows/WorkflowForm.tsx index 3b146321..6bf221c4 100644 --- a/frontend/i14/src/components/workflows/WorkflowForm.tsx +++ b/frontend/i14/src/components/workflows/WorkflowForm.tsx @@ -1,26 +1,12 @@ -import React, { FC, ChangeEvent, useState } from "react"; -import { - Button, - MenuItem, - IconButton, - InputLabel, - Grid, - Select, - Stack, - TextField, - Tooltip, - Typography, -} from "@mui/material"; -import InfoIcon from "@mui/icons-material/Info"; -import type { SelectChangeEvent } from "@mui/material/Select"; +import React, { FC, useMemo, useState } from "react"; +import { Button, Grid, Stack, TextField, Typography } from "@mui/material"; import { visitRegex } from "@diamondlightsource/sci-react-ui"; -type FormData = { visit: string; workflow: string }; +import { initialData } from "../../data/form"; +import { workflowOptions } from "../../data/workflows"; +import OptionSelect from "./OptionSelect"; -const initialData: FormData = { - visit: "cm23467-2", - workflow: "dpc-batch", -}; +import type { WorkflowFormData, Option } from "../../types/workflowFields"; export const WorkflowForm: FC = () => { const [data, setData] = useState(initialData); @@ -28,112 +14,54 @@ export const WorkflowForm: FC = () => { const openInNewTab = (url: string) => { const w = window.open(url, "_blank"); - if (w) w.focus(); + w?.focus(); }; - const openLink = () => { - if (!visitMatch) return; - const linkString = `https://workflows.diamond.ac.uk/templates/${data.workflow}/${data.visit}`; - openInNewTab(linkString); + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + const url = `https://workflows.diamond.ac.uk/templates/${data.workflow}/${data.visit}`; + openInNewTab(url); }; - const workflowOptions = [ - { - label: "DPC", - value: "dpc-batch", - desc: "DPC imaging produces an image of the phase shifts to the X-ray beam as a result of interaction with the sample by measuring the gradient of the phase and retrieving the phase shifts by a straightforward integration step.", - }, - { - label: "XANES Auto-processing", - value: "xanes", - desc: "XANES is a utility which attempts to stack a sequence of datasets acquired at different energy for a particular line group and perform alignment based on a line group.", - }, - { - label: "XANES Point", - value: "xanes-point", - desc: "XANES-point is a utility which takes in a scan file (inpath) and a line group (edge_element), performs windowing of this line group, and saves a two-column text file (outpath): the first column is the energy in keV, and the second column is the summed windowed MCA intensity across the 4 channels.", - }, - { - label: "XANES Sparse", - value: "xanes-sparse", - desc: "XANES-sparse is a utility which takes the last scan file of a sparse XANES scan (inpath), defines the 2D full grid, inserts the data in the correct rows, stack the images, and completes the missing data by using looped alternating steepest descent (ASD).", - }, - { - label: "XRD 1D", - value: "xrd1d-batch", - desc: "XRD can be used to spatially map changes in crystallographic direction, d-spacing or strain across a sample.", - }, - { - label: "XRD 2D", - value: "xrd2d-batch", - desc: "XRD 2D is a utility which performs Azimuthal integration (ExcaliburXRDIntegration) and saves result to a nxs file", - }, - ]; - - const option = workflowOptions.find( - (option) => option.value === data.workflow - ); - return ( - - I14 Workflows - -
{ - e.preventDefault(); - openLink(); - }} - > - - - Workflow - - - - - - - - - - ) => { - const value = e.target.value; - setData((prev) => ({ ...prev, visit: value })); - }} - helperText={visitMatch ? "" : "Expected format: xx12345-1"} - error={!visitMatch} - /> + /> + + ) => { + const value = e.target.value; + setData((prev) => ({ ...prev, visit: value })); + }} + helperText={visitMatch ? "" : "Expected format: xx12345-1"} + error={!visitMatch} + /> - - -
-
+ + + + + ); }; From 162c70f61cd1524f75652a61a7ebc86425d84d21 Mon Sep 17 00:00:00 2001 From: Jessica V Date: Tue, 4 Aug 2026 10:59:57 +0100 Subject: [PATCH 5/7] Add Toggle for workflow categories --- .../src/components/workflows/WorkflowForm.tsx | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/frontend/i14/src/components/workflows/WorkflowForm.tsx b/frontend/i14/src/components/workflows/WorkflowForm.tsx index 6bf221c4..6b3935e9 100644 --- a/frontend/i14/src/components/workflows/WorkflowForm.tsx +++ b/frontend/i14/src/components/workflows/WorkflowForm.tsx @@ -1,5 +1,14 @@ import React, { FC, useMemo, useState } from "react"; -import { Button, Grid, Stack, TextField, Typography } from "@mui/material"; +import { + Button, + FormLabel, + TextField, + ToggleButton, + ToggleButtonGroup, + Grid, + Stack, + Typography, +} from "@mui/material"; import { visitRegex } from "@diamondlightsource/sci-react-ui"; import { initialData } from "../../data/form"; @@ -9,9 +18,41 @@ import OptionSelect from "./OptionSelect"; import type { WorkflowFormData, Option } from "../../types/workflowFields"; export const WorkflowForm: FC = () => { - const [data, setData] = useState(initialData); + const workflowGroups = ["dpc", "xanes", "xrd"] as const; + type ToggleGroup = (typeof workflowGroups)[number]; + const getFilteredWorkflows = (toggle: ToggleGroup): Option[] => { + return (workflowOptions ?? []).filter((o) => o.value.includes(toggle)); + }; + + const [data, setData] = useState(() => { + const defaultGroup = + workflowGroups.find((g) => initialData.workflow.includes(g)) ?? + workflowGroups[0]; + return { + ...initialData, + group: defaultGroup, + }; + }); + const visitMatch = visitRegex.exec(data.visit); + const filteredWorkflowOptions: Option[] = getFilteredWorkflows( + data.group as ToggleGroup + ); + + const handleToggleChange = ( + _event: React.MouseEvent, + next: ToggleGroup | null + ) => { + if (!next) return; + const filteredWorkflows = getFilteredWorkflows(next); + setData((prev) => ({ + ...prev, + group: next, + workflow: filteredWorkflows[0].value, + })); + }; + const openInNewTab = (url: string) => { const w = window.open(url, "_blank"); w?.focus(); @@ -30,10 +71,25 @@ export const WorkflowForm: FC = () => {
+ + Technique + + {workflowGroups.map((g) => ( + + {g.toUpperCase()} + + ))} + + setData((prev) => ({ ...prev, workflow: e.target.value })) } From d16ba916498155a03e4290a5669414a9904e2d6a Mon Sep 17 00:00:00 2001 From: Jessica V Date: Wed, 5 Aug 2026 12:30:36 +0100 Subject: [PATCH 6/7] Update name references for template and technique --- .../src/components/workflows/WorkflowForm.tsx | 45 +++++++++---------- frontend/i14/src/data/form.ts | 4 +- .../src/data/{workflows.ts => templates.ts} | 4 +- frontend/i14/src/types/workflowFields.ts | 2 +- 4 files changed, 27 insertions(+), 28 deletions(-) rename frontend/i14/src/data/{workflows.ts => templates.ts} (94%) diff --git a/frontend/i14/src/components/workflows/WorkflowForm.tsx b/frontend/i14/src/components/workflows/WorkflowForm.tsx index 6b3935e9..82ef02fa 100644 --- a/frontend/i14/src/components/workflows/WorkflowForm.tsx +++ b/frontend/i14/src/components/workflows/WorkflowForm.tsx @@ -12,32 +12,31 @@ import { import { visitRegex } from "@diamondlightsource/sci-react-ui"; import { initialData } from "../../data/form"; -import { workflowOptions } from "../../data/workflows"; +import { templateOptions } from "../../data/templates"; import OptionSelect from "./OptionSelect"; import type { WorkflowFormData, Option } from "../../types/workflowFields"; export const WorkflowForm: FC = () => { - const workflowGroups = ["dpc", "xanes", "xrd"] as const; - type ToggleGroup = (typeof workflowGroups)[number]; - const getFilteredWorkflows = (toggle: ToggleGroup): Option[] => { - return (workflowOptions ?? []).filter((o) => o.value.includes(toggle)); + const techniques = ["dpc", "xanes", "xrd"] as const; + type ToggleGroup = (typeof techniques)[number]; + const getFilteredTemplates = (toggle: ToggleGroup): Option[] => { + return (templateOptions ?? []).filter((o) => o.value.includes(toggle)); }; const [data, setData] = useState(() => { - const defaultGroup = - workflowGroups.find((g) => initialData.workflow.includes(g)) ?? - workflowGroups[0]; + const defaultTechnique = + techniques.find((t) => initialData.template.includes(t)) ?? techniques[0]; return { ...initialData, - group: defaultGroup, + technique: defaultTechnique, }; }); const visitMatch = visitRegex.exec(data.visit); - const filteredWorkflowOptions: Option[] = getFilteredWorkflows( - data.group as ToggleGroup + const filteredTemplateOptions: Option[] = getFilteredTemplates( + data.technique ); const handleToggleChange = ( @@ -45,11 +44,11 @@ export const WorkflowForm: FC = () => { next: ToggleGroup | null ) => { if (!next) return; - const filteredWorkflows = getFilteredWorkflows(next); + const filteredTemplates = getFilteredTemplates(next); setData((prev) => ({ ...prev, - group: next, - workflow: filteredWorkflows[0].value, + technique: next, + template: filteredTemplates[0].value, })); }; @@ -60,7 +59,7 @@ export const WorkflowForm: FC = () => { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - const url = `https://workflows.diamond.ac.uk/templates/${data.workflow}/${data.visit}`; + const url = `https://workflows.diamond.ac.uk/templates/${data.template}/${data.visit}`; openInNewTab(url); }; @@ -75,23 +74,23 @@ export const WorkflowForm: FC = () => { Technique - {workflowGroups.map((g) => ( - - {g.toUpperCase()} + {techniques.map((t) => ( + + {t.toUpperCase()} ))} - setData((prev) => ({ ...prev, workflow: e.target.value })) + setData((prev) => ({ ...prev, template: e.target.value })) } /> diff --git a/frontend/i14/src/data/form.ts b/frontend/i14/src/data/form.ts index ffa5c836..ea2744c8 100644 --- a/frontend/i14/src/data/form.ts +++ b/frontend/i14/src/data/form.ts @@ -1,6 +1,6 @@ -import type { WorkflowFormData } from "../types/workflowFormData"; +import type { WorkflowFormData } from "../types/workflowFields"; export const initialData: WorkflowFormData = { visit: "mg23967-1", - workflow: "dpc-batch", + template: "dpc-batch", }; diff --git a/frontend/i14/src/data/workflows.ts b/frontend/i14/src/data/templates.ts similarity index 94% rename from frontend/i14/src/data/workflows.ts rename to frontend/i14/src/data/templates.ts index f302faca..cb3df06a 100644 --- a/frontend/i14/src/data/workflows.ts +++ b/frontend/i14/src/data/templates.ts @@ -1,6 +1,6 @@ -import type { Option } from "../types/workflowFormData"; +import type { Option } from "../types/workflowFields"; -export const workflowOptions: Option[] = [ +export const templateOptions: Option[] = [ { label: "DPC", value: "dpc-batch", diff --git a/frontend/i14/src/types/workflowFields.ts b/frontend/i14/src/types/workflowFields.ts index 320f60e2..66863210 100644 --- a/frontend/i14/src/types/workflowFields.ts +++ b/frontend/i14/src/types/workflowFields.ts @@ -1,2 +1,2 @@ -type WorkflowFormData = { visit: string; workflow: string }; +type WorkflowFormData = { visit: string; template: string }; type Option = { label: string; value: string; desc?: string }; From f8d8b9353cfb7b3f5e82071c045152bada66a1f5 Mon Sep 17 00:00:00 2001 From: Jessica V Date: Thu, 30 Jul 2026 13:34:06 +0100 Subject: [PATCH 7/7] Add outpath to I14 dashboard --- .../i14/src/components/workflows/WorkflowForm.tsx | 15 ++++++++++++++- frontend/i14/src/data/form.ts | 1 + frontend/i14/src/types/workflowFields.ts | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/i14/src/components/workflows/WorkflowForm.tsx b/frontend/i14/src/components/workflows/WorkflowForm.tsx index 82ef02fa..90c20cc5 100644 --- a/frontend/i14/src/components/workflows/WorkflowForm.tsx +++ b/frontend/i14/src/components/workflows/WorkflowForm.tsx @@ -59,7 +59,7 @@ export const WorkflowForm: FC = () => { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - const url = `https://workflows.diamond.ac.uk/templates/${data.template}/${data.visit}`; + const url = `https://workflows.diamond.ac.uk/templates/${data.template}/${data.visit}?outputFolder=${data.outpath}`; openInNewTab(url); }; @@ -110,6 +110,19 @@ export const WorkflowForm: FC = () => { error={!visitMatch} /> + ) => { + setData((prev) => ({ ...prev, outpath: e.target.value })); + }} + /> + diff --git a/frontend/i14/src/data/form.ts b/frontend/i14/src/data/form.ts index ea2744c8..1230af8d 100644 --- a/frontend/i14/src/data/form.ts +++ b/frontend/i14/src/data/form.ts @@ -3,4 +3,5 @@ import type { WorkflowFormData } from "../types/workflowFields"; export const initialData: WorkflowFormData = { visit: "mg23967-1", template: "dpc-batch", + outpath: "/dls/i14/data/", }; diff --git a/frontend/i14/src/types/workflowFields.ts b/frontend/i14/src/types/workflowFields.ts index 66863210..c0a2d1ab 100644 --- a/frontend/i14/src/types/workflowFields.ts +++ b/frontend/i14/src/types/workflowFields.ts @@ -1,2 +1,2 @@ -type WorkflowFormData = { visit: string; template: string }; +type WorkflowFormData = { visit: string; template: string; outpath: string }; type Option = { label: string; value: string; desc?: string };