Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions frontend/dashboard/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SingleTemplatePage from "./routes/SingleTemplatePage";
import SingleWorkflowPage from "./routes/SingleWorkflowPage";
import ErrorPage from "./ErrorPage";
import { ThemeProvider, DiamondTheme } from "@diamondlightsource/sci-react-ui";
import TriggersListPage from "./routes/TriggersListPage";

const router = createBrowserRouter([
{
Expand Down Expand Up @@ -45,6 +46,10 @@ const router = createBrowserRouter([
path: "workflows/:visitid/:workflowId/:tasknames",
element: <SingleWorkflowPage />,
},
{
path: "triggers",
element: <TriggersListPage />,
},
]);

async function startMockingIfConfigured() {
Expand Down
18 changes: 18 additions & 0 deletions frontend/dashboard/src/routes/TriggersListPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Breadcrumbs } from "@diamondlightsource/sci-react-ui";
import { Box, Container } from "@mui/material";
import { Link } from "react-router-dom";
import { TriggersListView, WorkflowsNavbar } from "relay-workflows-lib";

export default function TriggersListPage() {
return (
<>
<WorkflowsNavbar />
<Breadcrumbs path={window.location.pathname} linkComponent={Link} />
<Container maxWidth="lg">
<Box display="flex" flexDirection="column" alignItems="center" mt={2}>
<TriggersListView />
</Box>
</Container>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { useEffect } from "react";
import { PreloadedQuery, usePreloadedQuery } from "react-relay";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Box,
Typography,
} from "@mui/material";
import { PaginationControls } from "workflows-lib";
import { TriggersListViewQuery } from "../views/TriggersListView";
import type { TriggersListViewQuery as TriggersListViewQueryType } from "../views/__generated__/TriggersListViewQuery.graphql";

interface TriggersListContentProps {
queryRef: PreloadedQuery<TriggersListViewQueryType>;
currentPage: number;
totalPages: number;
selectedLimit: number;
onPageChange: (
page: number,
endCursor?: string | null,
hasNextPage?: boolean,
) => void;
onLimitChange: (limit: number) => void;
updatePageInfo: (hasNextPage: boolean, endCursor: string | null) => void;
}

export default function TriggersListContent({
queryRef,
currentPage,
totalPages,
selectedLimit,
onPageChange,
onLimitChange,
updatePageInfo,
}: TriggersListContentProps) {
const queryData = usePreloadedQuery(TriggersListViewQuery, queryRef);
const { nodes, pageInfo } = queryData.triggers;

useEffect(() => {
updatePageInfo(pageInfo.hasNextPage, pageInfo.endCursor ?? null);
}, [pageInfo.hasNextPage, pageInfo.endCursor, updatePageInfo]);

return (
<Box
sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
<Box sx={{ overflowY: "auto", maxHeight: "80vh", width: "100%" }}>
{nodes.map((node, index) => (
<Accordion key={index}>
<AccordionSummary>
<Box sx={{ display: "flex", flexBasis: 0, flexGrow: 5, gap: 2 }}>
<Typography sx={{ fontWeight: "bold" }}>
{node.beamline}
</Typography>
<Typography>{node.name}</Typography>
</Box>
</AccordionSummary>
<AccordionDetails>
<Typography>
Created from the <i>{node.templateRef}</i> template
</Typography>
</AccordionDetails>
</Accordion>
))}
</Box>
<PaginationControls
currentPage={currentPage}
totalPages={totalPages}
onPageChange={(page) => {
onPageChange(page, pageInfo.endCursor, pageInfo.hasNextPage);
}}
selectedLimit={selectedLimit}
onLimitChange={onLimitChange}
/>
</Box>
);
}
30 changes: 6 additions & 24 deletions frontend/relay-workflows-lib/lib/components/WorkflowsNavbar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Box, Typography } from "@mui/material";
import { Box } from "@mui/material";
import { NavLink as Link } from "react-router-dom";
import {
Navbar,
DiamondTheme,
NavLinks,
NavLink,
User,
Expand Down Expand Up @@ -32,7 +31,7 @@ const handleLogout = () => {
}
};

const WorkflowsNavbar: React.FC<WorkflowsNavbarProps> = ({ sessionInfo }) => {
const WorkflowsNavbar: React.FC<WorkflowsNavbarProps> = () => {
const [user, setUser] = useState<AuthState | null>(null);

useEffect(() => {
Expand Down Expand Up @@ -67,31 +66,14 @@ const WorkflowsNavbar: React.FC<WorkflowsNavbarProps> = ({ sessionInfo }) => {
<NavLink to="/templates" linkComponent={Link}>
Templates
</NavLink>
<NavLink to="/triggers" linkComponent={Link}>
Triggers
</NavLink>
</NavLinks>
</Box>
}
rightSlot={
<>
{sessionInfo && (
<Typography
sx={{
color: DiamondTheme.palette.primary.contrastText,
fontSize: {
xs: "0.75rem",
sm: "0.8rem",
md: "0.8rem",
lg: "1rem",
},
textAlign: "right",
ml: 2,
whiteSpace: "nowrap",
}}
>
{sessionInfo}
</Typography>
)}
<User colour="white" user={user} onLogout={handleLogout}></User>
</>
<User colour="white" user={user} onLogout={handleLogout}></User>
}
/>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/relay-workflows-lib/lib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export {
export { default as TemplateView } from "./views/TemplateView";
export { default as TemplateViewRetrigger } from "./views/TemplateViewRetrigger";
export { default as SingleWorkflowView } from "./views/SingleWorkflowView";
export { default as TriggersListView } from "./views/TriggersListView";
export { default as RepositoryLink } from "./query-components/RepositoryLink";
export { default as WorkflowsNavbar } from "./components/WorkflowsNavbar";
export {
Expand Down
70 changes: 70 additions & 0 deletions frontend/relay-workflows-lib/lib/views/TriggersListView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { graphql } from "relay-runtime";
import { type TriggersListViewQuery as TriggersListViewQueryType } from "./__generated__/TriggersListViewQuery.graphql";
import { useQueryLoader } from "react-relay";
import { useCallback, useEffect } from "react";
import TriggersListContent from "../components/TriggersListContent";
import { Box } from "@mui/material";
import { useServerSidePagination } from "../utils/useServerSidePagination";

export const TriggersListViewQuery = graphql`
query TriggersListViewQuery($limit: Int, $cursor: String) {
triggers(limit: $limit, cursor: $cursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
name
beamline
templateRef
}
}
}
`;

export default function TriggersListView() {
const [queryReference, loadQuery] = useQueryLoader<TriggersListViewQueryType>(
TriggersListViewQuery,
);

const {
cursor,
currentPage,
totalPages,
selectedLimit,
goToPage,
changeLimit,
updatePageInfo,
} = useServerSidePagination();

const load = useCallback(() => {
loadQuery(
{ limit: selectedLimit, cursor },
{ fetchPolicy: "store-and-network" },
);
}, [selectedLimit, cursor, loadQuery]);

useEffect(() => {
load();
const interval = setInterval(load, 5000);
return () => {
clearInterval(interval);
};
}, [load]);

return (
<Box width="100%">
{queryReference && (
<TriggersListContent
queryRef={queryReference}
currentPage={currentPage}
totalPages={totalPages}
selectedLimit={selectedLimit}
onPageChange={goToPage}
onLimitChange={changeLimit}
updatePageInfo={updatePageInfo}
/>
)}
</Box>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,6 @@ describe("WorkflowsNavbar", () => {
vi.restoreAllMocks();
});

it("renders with title and sessionInfo", () => {
const { getByText } = render(
<ThemeProvider theme={DiamondTheme}>
<BrowserRouter>
<WorkflowsNavbar sessionInfo="cm12345-6" />
</BrowserRouter>
</ThemeProvider>,
);
expect(getByText("cm12345-6")).toBeInTheDocument();
});

it("applies the correct styles", () => {
const { getByText } = render(
<ThemeProvider theme={DiamondTheme}>
<BrowserRouter>
<WorkflowsNavbar sessionInfo="cm34567-8" />
</BrowserRouter>
</ThemeProvider>,
);
const sessionElement = getByText("cm34567-8");
expect(sessionElement).toHaveStyle(
`color: ${DiamondTheme.palette.primary.contrastText}`,
);
});

it("displays the logged in user", async () => {
vi.mocked(getUser).mockReturnValue(Promise.resolve(testUser));
render(
Expand Down
Empty file.
Loading