Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f82d756
updating the logging component to allow users to choose which workflo…
fallars Sep 15, 2026
aaf8230
Remove unnecessary wrapper function
yousefmoazzam Sep 15, 2026
c8fefc6
Pass setter function through to `JobsTable`
yousefmoazzam Sep 15, 2026
df85f6c
Add type to setter function
yousefmoazzam Sep 15, 2026
7045aa2
Remove unnecessary suspense
yousefmoazzam Sep 15, 2026
ab1bb0b
Add types to log component props
yousefmoazzam Sep 15, 2026
f1e6c57
Add type for log component props
yousefmoazzam Sep 15, 2026
23ccad5
Remove unnecessary `any` type for workflow object
yousefmoazzam Sep 15, 2026
c88e911
Remove unnecessary `any` type for task and artifact objects
yousefmoazzam Sep 15, 2026
78d761d
Replace `any` type with string array type
yousefmoazzam Sep 15, 2026
37e5684
Rename variable for clarity
yousefmoazzam Sep 15, 2026
22c9cde
Add type to artifact URL + logfile name variable
yousefmoazzam Sep 15, 2026
bf60513
Refactor button array generator function
yousefmoazzam Sep 15, 2026
b6716a2
Fix incorrect comparison
yousefmoazzam Sep 15, 2026
3c22b00
Remove unused variable
yousefmoazzam Sep 15, 2026
7f499a7
Remove unused import
yousefmoazzam Sep 15, 2026
cc03786
Remove unnecessary `null` check of query response
yousefmoazzam Sep 15, 2026
8de9173
Remove commented-out code
yousefmoazzam Sep 15, 2026
3dc389a
Remove unnecessary check if workflow is `undefined`
yousefmoazzam Sep 15, 2026
48cf4bd
Add key to buttons rendered from array
yousefmoazzam Sep 15, 2026
225a1fa
Remove reference to non-existent ptyrex technique
yousefmoazzam Sep 15, 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: 7 additions & 2 deletions frontend/unified/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SessionQueryQuery,
SessionQueryQueryVariables,
} from "./__generated__/App.generated";
import { WorkflowsQueryQuery } from "./components/JobsViewer/__generated__/JobsTable.generated";

const VERTICAL_SPACING = 2;
const HORIZONTAL_SPACING = 2;
Expand Down Expand Up @@ -93,6 +94,9 @@ export const App: React.FC = () => {
const [customSession, setCustomSession] = useState<InstrumentSession | null>(
null
);
const [TableInfo, setTableInfo] = useState<WorkflowsQueryQuery | undefined>(
undefined
);
const { loading, error, data } = useQuery(SESSION_QUERY, { variables: {} });

if (loading) return <p>Loading...</p>;
Expand Down Expand Up @@ -292,6 +296,7 @@ export const App: React.FC = () => {
availableTemplates={filterTemplates(
Technique[currentTechnique as keyof typeof Technique]
)}
visit={selectedVisit}
/>
</Stack>
<Stack spacing={VERTICAL_SPACING} width="500px">
Expand All @@ -304,7 +309,7 @@ export const App: React.FC = () => {
/>
<Divider sx={{ width: "100%" }} />
<Typography variant="h5">Log</Typography>
<DisplayLogMeta visit={selectedVisit} />
<DisplayLogMeta visit={selectedVisit} TableInfo={TableInfo} />

<PlaceholderComponent
placeholderText="Log component placeholder"
Expand All @@ -314,7 +319,7 @@ export const App: React.FC = () => {

<Divider sx={{ width: "100%" }} />
<Typography variant="h5">Jobs</Typography>
<JobsViewer visit={selectedVisit} />
<JobsViewer visit={selectedVisit} setInfo={setTableInfo} />
</Stack>
</Grid>
</ApolloProvider>
Expand Down
168 changes: 146 additions & 22 deletions frontend/unified/src/components/InspectLogMeta.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import React, { FC } from "react";
import { Button, Stack } from "@mui/material";
import React, { FC, useState } from "react";
import {
Button,
Stack,
Menu,
MenuItem,
List,
ListItemButton,
ListItemText,
Paper,
Typography,
Divider,
} from "@mui/material";

import { useQuery } from "@apollo/client/react";
import { gql, type TypedDocumentNode } from "@apollo/client";
Expand All @@ -8,6 +19,8 @@ import {
LogQueryQuery,
LogQueryQueryVariables,
} from "./__generated__/InspectLogMeta.generated";
import { Visit } from "@diamondlightsource/sci-react-ui";
import { WorkflowsQueryQuery } from "./JobsViewer/__generated__/JobsTable.generated";

export const InspectLog_Query: TypedDocumentNode<
LogQueryQuery,
Expand All @@ -31,31 +44,79 @@ export const InspectLog_Query: TypedDocumentNode<
}
}
}
... on WorkflowErroredStatus {
message
tasks {
name
artifacts {
name
url
mimeType
}
}
}
... on WorkflowFailedStatus {
message
tasks {
name
artifacts {
name
url
mimeType
}
}
}
}
}
}
`;

export const DisplayLogMeta: FC = (props: { visit: any }) => {
type DisplayLogMetaProps = {
visit: Visit;
TableInfo: WorkflowsQueryQuery;
};

export const DisplayLogMeta: FC<DisplayLogMetaProps> = (props: {
visit: Visit;
TableInfo: WorkflowsQueryQuery;
}) => {
let artifactUrlAndLogFileTuples: [string, string][] = [];
let workflownames: string[] = [];
let y: any = [];

//ToDO maybe need to Consider what to display if there is no workflow as then workflowsnames is empty
if (props.TableInfo !== undefined) {
props.TableInfo.workflows?.nodes.forEach((workflow) => {
if (workflow.status?.__typename == "WorkflowSucceededStatus") {
workflownames.push(workflow.name);
}
});
}

const [selectedWorkflow, setSelectedWorkflow] = useState(0);

const { loading, error, data } = useQuery(InspectLog_Query, {
variables: { visitobj: props.visit, name: "example-template-599zg" },
variables: { visitobj: props.visit, name: workflownames[0] },
});
let x: any = [];

if (data !== undefined && data !== null) {
if (data.workflow !== undefined && data.workflow !== null) {
data.workflow.status?.tasks.forEach((element: any) => {
element.artifacts.forEach((subElement: any) => {
if (subElement !== undefined) {
if (subElement.url !== undefined) {
if (subElement.mimeType == "text/plain") {
x.push([subElement.url, element.name + ".log"]);
}
if (data !== undefined) {
if (data.workflow !== null) {
if (data.workflow.status?.__typename == "WorkflowSucceededStatus") {
data.workflow.status.tasks.forEach((task) => {
task.artifacts.forEach((artifact) => {
if (artifact.mimeType == "text/plain") {
artifactUrlAndLogFileTuples.push([
artifact.url,
task.name + ".log",
]);
}
}
});
});
});
} else
y[0] = ["http://localhost:5173/unified", "Error-No-logs-found.log"];
}
} else {
y = ["http://localhost:5173/unified", "Error-No-logs-found.log"];
}

const openInNewTab = (url: string) => {
Expand All @@ -65,27 +126,90 @@ export const DisplayLogMeta: FC = (props: { visit: any }) => {
}
};

function makeButtonArray(Arr: any) {
function makeButtonArray(artifactUrlsAndLogFilenames: [string, string][]) {
return (
<Stack direction="row" spacing={1}>
{" "}
{Arr.map((subArr: any) => {
{artifactUrlsAndLogFilenames.map(([artifactUrl, logFilename]) => {
return (
<Button
key={subArr[1]}
key={logFilename}
variant="contained"
onClick={() => openInNewTab(subArr[0])}
onClick={() => openInNewTab(artifactUrl)}
>
{" "}
{subArr[1]}{" "}
{logFilename}{" "}
</Button>
);
})}{" "}
</Stack>
);
}

return <div>{makeButtonArray(x)}</div>;
//Menu handling
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClose = () => {
setAnchorEl(null);
};
const handleClickListItem = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleMenuListItem = (
event: React.MouseEvent<HTMLElement>,
index: number
) => {
setSelectedWorkflow(index);
setAnchorEl(null);
};

return (
<div>
<Paper sx={{ width: 400 }}>
<Divider
flexItem={true}
sx={{ width: "100%", Color: "rgba(2, 2, 1, 0.5)" }}
variant="fullWidth"
/>
<List>
<ListItemButton onClick={handleClickListItem}>
<ListItemText
primary="Select a previous workflow by clicking here"
secondary={`Workflow: ${workflownames[selectedWorkflow]}`}
/>
</ListItemButton>
</List>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
{workflownames.map((option: string, index: number) => (
<MenuItem
key={option}
role="menuitemradio"
selected={workflownames[selectedWorkflow] === option}
onClick={(event) => handleMenuListItem(event, index)}
>
<ListItemText>{option}</ListItemText>
<Divider
flexItem={true}
variant="fullWidth"
sx={{ width: "100%", Color: "rgba(2, 2, 1, 0.5)" }}
/>
</MenuItem>
))}
</Menu>
</Paper>
<p />
<Divider
flexItem={true}
variant="fullWidth"
sx={{ mb: 2, width: "100%", Color: "rgba(2, 2, 1, 0.5)" }}
/>
<Typography>
Choose a log from {workflownames[selectedWorkflow]}:
</Typography>
<p />
{makeButtonArray(artifactUrlAndLogFileTuples)}
</div>
);
};

export default DisplayLogMeta;
14 changes: 12 additions & 2 deletions frontend/unified/src/components/JobsViewer/JobsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
WorkflowsQueryQuery,
WorkflowsQueryQueryVariables,
} from "./__generated__/JobsTable.generated";
import { Suspense, useCallback, useState } from "react";
import { Suspense, useCallback, useState, useEffect } from "react";

import { Visit } from "./JobsViewer";
import TableContent, { TABLECONTENT_FRAGMENT } from "./TableContent";
Expand All @@ -22,7 +22,13 @@ export const JOBSTABLE_QUERY: TypedDocumentNode<
${TABLECONTENT_FRAGMENT}
`;

const JobsTable = ({ visit }: { visit: Visit }) => {
const JobsTable = ({
visit,
setInfo,
}: {
visit: Visit;
setInfo: (_: WorkflowsQueryQuery | undefined) => void;
}) => {
const [selectedLimit, setSelectedLimit] = useState<number>(5);
const [currentPage, setCurrentPage] = useState<number>(0);
const [cursor, setCursor] = useState<string | null>(null);
Expand All @@ -45,6 +51,10 @@ const JobsTable = ({ visit }: { visit: Visit }) => {
fetchPolicy: "cache-and-network",
});

useEffect(() => {
setInfo(data);
});

return (
<Box width="600px" height="600px">
<Suspense>
Expand Down
11 changes: 9 additions & 2 deletions frontend/unified/src/components/JobsViewer/JobsViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { Suspense } from "react";
import JobsTable from "./JobsTable";
import { WorkflowsQueryQuery } from "./__generated__/JobsTable.generated";

export type Visit = {
proposalCode: string;
proposalNumber: number;
number: number;
};

const JobsViewer = ({ visit }: { visit: Visit }) => {
const JobsViewer = ({
visit,
setInfo,
}: {
visit: Visit;
setInfo: (_: WorkflowsQueryQuery | undefined) => void;
}) => {
return (
<Suspense>
<JobsTable visit={visit} />
<JobsTable visit={visit} setInfo={setInfo} />
</Suspense>
);
};
Expand Down
Loading