Skip to content
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
628e71d
Save example queue - does not return to robot page for some reason
noemifrisina May 26, 2026
f90ab19
Add daq queue to helm chart
noemifrisina May 27, 2026
1cc9276
Save basic query for interrogating the queue
noemifrisina May 27, 2026
c0a9717
A first displayed queue
noemifrisina May 27, 2026
7d9d847
Tidy up and add colour to chips
noemifrisina May 27, 2026
766ac30
Display plan parameters as a string and add a comment about it
noemifrisina May 27, 2026
ff7585e
Fix typing in test
noemifrisina May 28, 2026
f0bbc56
Actually save the patch queue state
noemifrisina May 28, 2026
8423d9f
Add generated types from queue fastapi
jacob720 May 28, 2026
19f7ee6
Update generated queue types
jacob720 May 28, 2026
a8a0161
Add script to generate queue types
jacob720 May 28, 2026
0e7c48e
Update mock data
jacob720 May 28, 2026
380459a
Add pause queue toggle button
jacob720 May 28, 2026
ef8f904
Use SSE to get event driven updates
jacob720 May 29, 2026
4842f66
Add button to cancel tasks
jacob720 May 29, 2026
c00f0be
Allow re-ordering of tasks
jacob720 May 29, 2026
9165e79
Add toggle to show historic tasks and button to delete history
jacob720 May 29, 2026
1c957fc
Fix move task logic
jacob720 May 29, 2026
70ebbf1
Disable clear history button unless historic tasks shown
jacob720 Jun 1, 2026
6fc8aab
Use more up to date node image for dev container
jacob720 Jun 1, 2026
ad76f2c
Rename pause queue component
jacob720 Jun 1, 2026
b975de0
Only make tasks draggable if status is 'Queued'
jacob720 Jun 2, 2026
eabc095
Don't display task ID
jacob720 Jun 2, 2026
1b56518
PR suggestion
jacob720 Jun 2, 2026
b3b2697
Have a local queue server dev mode
jacob720 Jun 2, 2026
263faa7
Improve docs
jacob720 Jun 2, 2026
62ee74b
Link to issue about displaying params
jacob720 Jun 3, 2026
eaf1247
Display when not connected to queue
jacob720 Jun 4, 2026
89ec400
Small change
jacob720 Jun 4, 2026
d0966f9
Put queue status panel inside table bar
jacob720 Jun 4, 2026
b3f2e4b
Add tests WIP
jacob720 Jun 4, 2026
16922b0
Add tests for queue status panel
jacob720 Jun 4, 2026
6ea2328
Make queue pause button smaller
jacob720 Jun 4, 2026
a8b173a
Add tests for QueueView
jacob720 Jun 8, 2026
e9bbded
Put move logic in a helper function
jacob720 Jun 8, 2026
766aec7
Add test for new position calculation
jacob720 Jun 8, 2026
03c13b8
Fix lint
jacob720 Jun 8, 2026
3b54cb1
Fix bug
jacob720 Jun 8, 2026
bfdf52b
Fix some lint issues
jacob720 Jun 9, 2026
a9b726e
PR comments
jacob720 Jun 10, 2026
b1660b3
Fix lockfile
jacob720 Jun 10, 2026
15b20d1
Merge branch 'main' into 1625-display-queue
jacob720 Jun 10, 2026
fcf191f
Fix lint in tests
jacob720 Jun 10, 2026
9e29162
Move generated folder
jacob720 Jun 10, 2026
ffa221a
Ignore lint for generated code
jacob720 Jun 10, 2026
5f1f547
Try to fix deployed UI
jacob720 Jun 10, 2026
25ce7bc
Revert unneeded fix
jacob720 Jun 10, 2026
14be2f3
Remove .devcontainer/devcontainer-lock.json
jacob720 Jun 11, 2026
198474d
PR comments
jacob720 Jun 11, 2026
47d17f1
Fix the mocks for the history of the queue
DominicOram Jun 12, 2026
92151ca
Fix typo for endpoint
DominicOram Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"features": {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should: Can you remove this and add to the .gitignore?

"ghcr.io/diamondlightsource/devcontainer-features/bash-config:1": {
"version": "1.0.3",
"resolved": "ghcr.io/diamondlightsource/devcontainer-features/bash-config@sha256:5b32dfbc5d02a04b200eba276b5f21b6fce31658e8965f7c4b25c002b46dab4c",
"integrity": "sha256:5b32dfbc5d02a04b200eba276b5f21b6fce31658e8965f7c4b25c002b46dab4c"
}
}
}
8 changes: 5 additions & 3 deletions apps/i15-1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ The initial aim of this app to provide a science facing UI for the waffle projec
To run this locally:

1. Start the devcontainer in VSCode
1. To make sure you have the most up-to-date environment either run `pnpm install` in the container or rebuild the container (via the Ctrl+Shift+P menu in VSCode)
1. Run `turbo dev --filter @atlas/i15-1`
1. Navigate to http://localhost:5173/
2. To make sure you have the most up-to-date environment either run `pnpm install` in the container or rebuild the container (via the Ctrl+Shift+P menu in VSCode)
3. In a terminal in this devcontainer run either:
- `VITE_QUEUE_MODE=local turbo dev --filter @atlas/i15-1` if running a local queue server on http://127.0.0.1:8001, **OR**
- `turbo dev --filter @atlas/i15-1` to use mocked backends
4. Navigate to http://localhost:5173/
9 changes: 8 additions & 1 deletion apps/i15-1/helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ ui-base:
rewriteTarget: /
target:
external:
uri: http://i15-1-blueapi.diamond.ac.uk
uri: https://i15-1-blueapi.diamond.ac.uk

- id: daq-queue
path: /api/daq-queue/
rewriteTarget: /
target:
external:
uri: https://i15-1-daq-queue.diamond.ac.uk/

- id: supergraph
path: /api/graphql
Expand Down
8 changes: 6 additions & 2 deletions apps/i15-1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint ."
"lint": "eslint .",
"generate:queue": "npx @hey-api/openapi-ts -i https://raw.githubusercontent.com/DiamondLightSource/daq-queuing-service/main/docs/reference/rest_api.json -o /workspaces/atlas/apps/i15-1/apps/i15-1/src/queue/generated/",
"test": "vitest run",
"coverage": "vitest run --coverage"
},
"dependencies": {
"@apollo/client": "^4.2.3",
Expand All @@ -24,6 +27,7 @@
"axios": "^1.13.4",
"graphql": "^16.14.2",
"lucide-react": "^1.17.0",
"material-react-table": "^3.2.1",
"react-error-boundary": "^6.0.0"
},
"devDependencies": {
Expand All @@ -40,7 +44,7 @@
"typescript": "~5.9.3",
"vite": "^7.3.1",
"vite-plugin-relay": "^2.1.0",
"vitest": "*"
"vitest": "^4.0.18"
},
"msw": {
"workerDirectory": [
Expand Down
4 changes: 3 additions & 1 deletion apps/i15-1/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { router } from "./router.tsx";
import { createApi } from "@atlas/blueapi";
import { AppProviders } from "./AppProviders.tsx";

const QUEUE_MODE = import.meta.env.VITE_QUEUE_MODE;

async function enableMocking() {
if (import.meta.env.DEV) {
if (import.meta.env.DEV && QUEUE_MODE != "local") {
const { worker } = await import("./mocks/browser");
return worker.start();
}
Expand Down
108 changes: 108 additions & 0 deletions apps/i15-1/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,106 @@ const fakeExperiments = {

const fakePvws = ws.link("wss://pvws.diamond.ac.uk/pvws/pv");

const fakeQueue = [
{
experiment_definition: {
plan_name: "sleep",
sample_id: "1",
params: {
time: 10,
},
instrument_session: "cm12345-1",
},
id: "aaa",
status: "Complete",
blueapi_calls: [
{
task_request: {
name: "sleep",
params: {
time: 10,
},
instrument_session: "cm12345-1",
},
status: "Success",
parent_task_id: "aaa",
result: { outcome: "success" },
errors: [],
time_started: "2026-05-28T15:09:29.507128",
time_completed: "2026-05-28T15:09:39.561053",
blueapi_id: fakeTaskId,
},
],
position: 0,
},
{
experiment_definition: {
plan_name: "robot_load",
sample_id: "1",
params: {
puck: 1,
position: 1,
},
instrument_session: "cm12345-1",
},
id: "bbb",
status: "In progress",
blueapi_calls: [
{
task_request: {
name: "robot_load",
params: {
puck: 1,
position: 1,
},
instrument_session: "cm12345-1",
},
status: "In progress",
parent_task_id: "bbb",
result: null,
errors: [],
time_started: "2026-05-28T15:09:39.812345",
time_completed: null,
blueapi_id: fakeTaskId,
},
],
position: 1,
},
{
experiment_definition: {
plan_name: "static_collection_plan",
sample_id: "1",
params: {
frames: 10,
exposure_time: 0.5,
},
instrument_session: "cm12345-1",
},
id: "ccc",
status: "Queued",
blueapi_calls: [
{
task_request: {
name: "Queued",
params: {
frames: 10,
exposure_time: 0.5,
},
instrument_session: "cm12345-1",
},
status: "Waiting",
parent_task_id: "ccc",
result: null,
errors: [],
time_started: null,
time_completed: null,
blueapi_id: fakeTaskId,
},
],
position: 2,
},
];

export const handlers = [
http.put("/api/blueapi/worker/task", () => {
workerStatus.status = "RUNNING";
Expand Down Expand Up @@ -82,4 +182,12 @@ export const handlers = [
http.get("/api/blueapi/plans", () => {
return HttpResponse.json(plansResponse);
}),

http.get("/api/daq-queue/queue/state", () => {
return HttpResponse.json({ paused: true });
}),

http.get("/api/daq-queue/queue", () => {
return HttpResponse.json(fakeQueue);
}),
];
103 changes: 103 additions & 0 deletions apps/i15-1/src/queue/QueueStatusPanel.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { fireEvent, render, screen } from "@atlas/vitest-conf";
import { describe, it, expect, vi, afterEach } from "vitest";
import { QueueStatusPanel, QueueControlButton } from "./QueueStatusPanel";
import * as queueService from "./queueService";

afterEach(() => {
vi.restoreAllMocks();
});

describe("QueueStatusPanel", () => {
it("shows Queue Running when queue not paused and not empty", () => {
vi.spyOn(queueService, "useConnected").mockReturnValue({
connected: true,
} as any);

vi.spyOn(queueService, "useGetQueuedTasks").mockReturnValue({
data: [{ id: 1 }],
} as any);

vi.spyOn(queueService, "useToggleQueueState").mockReturnValue({
paused: false,
} as any);

render(<QueueStatusPanel />);

expect(screen.getByText(/Queue Running/i)).toBeInTheDocument();
});

it("shows Queue Finished when queue empty", () => {
vi.spyOn(queueService, "useConnected").mockReturnValue({
connected: true,
} as any);

vi.spyOn(queueService, "useGetQueuedTasks").mockReturnValue({
data: [],
} as any);

vi.spyOn(queueService, "useToggleQueueState").mockReturnValue({
paused: false,
} as any);

render(<QueueStatusPanel />);

expect(screen.getByText(/Queue Finished/i)).toBeInTheDocument();
});

it("shows Queue Paused when queue paused", () => {
vi.spyOn(queueService, "useConnected").mockReturnValue({
connected: true,
} as any);

vi.spyOn(queueService, "useGetQueuedTasks").mockReturnValue({
data: [{ id: 1 }],
} as any);

vi.spyOn(queueService, "useToggleQueueState").mockReturnValue({
paused: true,
} as any);

render(<QueueStatusPanel />);

expect(screen.getByText(/Queue Paused/i)).toBeInTheDocument();
});

it("shows Queue Not Connected when queue not connected", () => {
vi.spyOn(queueService, "useConnected").mockReturnValue({
connected: false,
} as any);

vi.spyOn(queueService, "useGetQueuedTasks").mockReturnValue({
data: [{ id: 1 }],
} as any);

vi.spyOn(queueService, "useToggleQueueState").mockReturnValue({
paused: true,
} as any);

render(<QueueStatusPanel />);

expect(screen.getByText(/Queue Not Connected/i)).toBeInTheDocument();
});
});

describe("QueueControlButton", () => {
it("calls toggle on click", () => {
const toggle = vi.fn();

vi.spyOn(queueService, "useConnected").mockReturnValue({
connected: true,
} as any);

vi.spyOn(queueService, "useToggleQueueState").mockReturnValue({
paused: false,
toggle,
isLoading: false,
} as any);

render(<QueueControlButton />);
fireEvent.click(screen.getByRole("button"));

expect(toggle).toHaveBeenCalled();
});
});
96 changes: 96 additions & 0 deletions apps/i15-1/src/queue/QueueStatusPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
Box,
Button,
CircularProgress,
Stack,
Typography,
} from "@mui/material";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import PauseIcon from "@mui/icons-material/Pause";
import {
useConnected,
useGetQueuedTasks,
useToggleQueueState,
} from "./queueService";

export function QueueControlButton() {
const { paused, toggle, isLoading } = useToggleQueueState();
const { connected } = useConnected();

const icon = isLoading ? (
<CircularProgress size={16} />
) : paused ? (
<PlayArrowIcon fontSize="small" />
) : (
<PauseIcon fontSize="small" />
);

return (
<Button
sx={{
height: 28,
width: 100,
alignItems: "center",
"& .MuiButton-startIcon": {
display: "flex",
alignItems: "center",
},
}}
variant="contained"
color={!connected ? "error" : paused ? "warning" : "success"}
onClick={toggle}
disabled={!connected}
startIcon={icon}
>
{paused ? "Resume" : "Pause"}
</Button>
);
}

export function QueueStatusPanel() {
const { paused } = useToggleQueueState();
const queuedTasks = useGetQueuedTasks();
const empty = !queuedTasks.data || queuedTasks.data.length === 0;
const { connected } = useConnected();

return (
<Box
sx={{
display: "flex",
border: "1px solid",
borderColor: !connected
? "error.main"
: paused
? "warning.main"
: "success.main",
borderRadius: 1,
padding: 1,
}}
>
<Stack direction="row" spacing={2} alignItems="center">
<Typography
variant="h6"
sx={{
color: !connected
? "error.main"
: paused
? "warning.main"
: "success.main",
fontWeight: 500,
fontSize: 17,
}}
>
Queue{" "}
{!connected
? "Not Connected"
: empty
? "Finished"
: paused
? "Paused"
: "Running"}
</Typography>
<QueueControlButton />
</Stack>
</Box>
);
}
Loading
Loading