Skip to content

Commit

Permalink
feat: create a results report page
Browse files Browse the repository at this point in the history
  • Loading branch information
nathan-vm committed Dec 13, 2023
1 parent 891cb1e commit f0ff5be
Show file tree
Hide file tree
Showing 10 changed files with 439 additions and 141 deletions.
73 changes: 73 additions & 0 deletions frontend/src/components/DownloadB64Button/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Tooltip, type ButtonProps, Button } from "@mui/material";
import React, { useCallback } from "react";

interface Props extends ButtonProps {
base64_content: string;
file_type: string;
}

export const DownloadB64Button: React.FC<Props> = ({
base64_content,
file_type,
...props
}) => {
const downloadContent = useCallback(() => {
let href = "";
switch (file_type) {
case "txt":
href = `data:text/plain;base64,${base64_content}`;
break;
case "plotly_json":
case "json":
href = `data:application/json;base64,${base64_content}`;
break;
case "jpeg":
case "jpg":
case "png":
case "bmp":
case "gif":
case "tiff":
href = `data:image/${file_type};base64,${base64_content}`;
break;
case "svg":
href = `data:image/svg+xml;base64,${base64_content}`;
break;
case "md":
href = `data:text/markdown;base64,${base64_content}`;
break;
case "pdf":
href = `data:application/pdf;base64,${base64_content}`;
break;
case "html":
href = `data:text/html;base64,${base64_content}`;
break;
default:
href = `data:text/plain;base64,${base64_content}`;
break;
}

const a = document.createElement("a"); // Create <a>
a.href = href; // Image Base64 Goes here
a.download = `download.${file_type}`; // File name Here
a.click(); // Downloaded file
}, [base64_content, file_type]);

return (
<Tooltip
title={
!base64_content || !file_type
? "Missing base64_content of file_type"
: "Will download the raw result content "
}
>
<Button
variant="contained"
onClick={downloadContent}
disabled={!base64_content || !file_type}
{...props}
>
Download content
</Button>
</Tooltip>
);
};
93 changes: 93 additions & 0 deletions frontend/src/components/RenderB64/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Typography } from "@mui/material";
import { RenderPDF } from "components/RenderPDF";
import DOMPurify from "dompurify";
import React, { type CSSProperties } from "react";
import ReactMarkdown from "react-markdown";
import Plot from "react-plotly.js";
import remarkGfm from "remark-gfm";

interface Props {
base64_content: string;
file_type: string;
style?: CSSProperties;
}

export const RenderB64: React.FC<Props> = ({
base64_content,
file_type,
style,
}) => {
if (!base64_content || !file_type) {
return <Typography variant="h2">No content</Typography>;
}
switch (file_type) {
case "txt":
return <pre style={style}>{window.atob(base64_content)}</pre>;
case "json":
return (
<pre style={style}>
{JSON.stringify(JSON.parse(window.atob(base64_content)), null, 2)}
</pre>
);
case "jpeg":
case "jpg":
case "png":
case "bmp":
case "gif":
case "tiff":
return (
<img
src={`data:image/${file_type};base64,${base64_content}`}
alt="Content"
style={{ maxWidth: "100%", maxHeight: "100%", ...style }}
/>
);
case "svg":
return (
<object
type="image/svg+xml"
data={`data:image/svg+xml;base64,${base64_content}`}
style={{ maxWidth: "100%", maxHeight: "100%", ...style }}
>
Your browser does not support SVG
</object>
);
case "md":
return (
<div
style={{ overflow: "auto", maxWidth: "100%", width: "100%" }}
className="markdown-container"
>
<ReactMarkdown
className="react-markdown-component"
remarkPlugins={[remarkGfm]}
>
{window.atob(base64_content)}
</ReactMarkdown>
;
</div>
);

case "pdf":
return <RenderPDF base64Content={base64_content} />;
case "html": {
const decodedHTML = atob(base64_content);
const sanitizedHTML = DOMPurify.sanitize(decodedHTML);

return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
}
case "plotly_json": {
const utf8String = atob(base64_content);
const decodedJSON = JSON.parse(utf8String);
return (
<Plot
data={decodedJSON.data}
layout={decodedJSON.layout}
style={{ width: "100%", height: "100%" }}
/>
);
}
default:
return <div>Unsupported file type</div>;
}
};
75 changes: 75 additions & 0 deletions frontend/src/features/myWorkflows/api/runs/getWorkflowRunReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { type AxiosResponse } from "axios";
import { useWorkspaces } from "context/workspaces";
import { dominoApiClient } from "services/clients/domino.client";
import useSWR from "swr";

export interface IGetWorkflowRunResultReportParams {
workflowId: string;
runId: string;
}

const getWorkflowRunResultReportUrl = ({
workspace,
workflowId,
runId,
}: Partial<IGetWorkflowRunResultReportParams & { workspace: string }>) => {
if (workspace && workflowId && runId) {
return `/workspaces/${workspace}/workflows/${workflowId}/runs/${runId}/tasks/report`;
} else {
return null;
}
};

/**
* Get workflows using GET /workflows
* @returns workflow
*/
const getWorkflowRunResultReport: ({
workspace,
workflowId,
runId,
}: Partial<
IGetWorkflowRunResultReportParams & { workspace: string }
>) => Promise<
| AxiosResponse<{
data: Array<{ base64_content: string; file_type: string }>;
}>
| undefined
> = async ({ workspace, workflowId, runId }) => {
if (workspace && workflowId && runId) {
const url = getWorkflowRunResultReportUrl({
workspace,
workflowId,
runId,
});
if (url) return await dominoApiClient.get(url);
}
};

/**
* Get workflow runs
* @returns runs as swr response
*/
export const useAuthenticatedGetWorkflowRunResultReport = (
params: Partial<IGetWorkflowRunResultReportParams>,
) => {
const { workspace } = useWorkspaces();
if (!workspace)
throw new Error(
"Impossible to fetch workflows without specifying a workspace",
);

const url = getWorkflowRunResultReportUrl({
workspace: workspace.id,
...params,
});

return useSWR(
url,
async () =>
await getWorkflowRunResultReport({
workspace: workspace.id,
...params,
}).then((data) => data?.data),
);
};
1 change: 1 addition & 0 deletions frontend/src/features/myWorkflows/api/runs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./getWorkflowRuns";
export * from "./getWorkflowRunTasks";
export * from "./getWorkflowRunTaskLogs";
export * from "./getWorkflowRunTaskResult";
export * from "./getWorkflowRunReport";
105 changes: 105 additions & 0 deletions frontend/src/features/myWorkflows/components/ResultsReport/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Grid, Paper } from "@mui/material";
import { RenderPDF } from "components/RenderPDF";
import DOMPurify from "dompurify";
import { useAuthenticatedGetWorkflowRunResultReport } from "features/myWorkflows/api";
import React from "react";
import ReactMarkdown from "react-markdown";
import Plot from "react-plotly.js";
import { useParams } from "react-router-dom";
import remarkGfm from "remark-gfm";

export const ResultsReport: React.FC = () => {
const { id, runId } = useParams<{ id: string; runId: string }>();
const { data } = useAuthenticatedGetWorkflowRunResultReport({
workflowId: id,
runId,
});
return (
<Paper sx={{ height: "88vh", overflowX: "scroll" }}>
<Grid container>
{data?.data?.map((d) => {
switch (d.file_type) {
case "txt":
return <pre>{window.atob(d.base64_content)}</pre>;
case "json":
return (
<pre>
{JSON.stringify(
JSON.parse(window.atob(d.base64_content)),
null,
2,
)}
</pre>
);
case "jpeg":
case "jpg":
case "png":
case "bmp":
case "gif":
case "tiff":
return (
<img
src={`data:image/${d.file_type};base64,${d.base64_content}`}
alt="Content"
style={{ maxWidth: "100%", maxHeight: "100%" }}
/>
);
case "svg":
return (
<object
type="image/svg+xml"
data={`data:image/svg+xml;base64,${d.base64_content}`}
style={{ maxWidth: "100%", maxHeight: "100%" }}
>
Your browser does not support SVG
</object>
);
case "md":
return (
<div
style={{
overflow: "auto",
maxWidth: "100%",
width: "100%",
}}
className="markdown-container"
>
<ReactMarkdown
className="react-markdown-component"
remarkPlugins={[remarkGfm]}
>
{window.atob(d.base64_content)}
</ReactMarkdown>
;
</div>
);

case "pdf":
return <RenderPDF base64Content={d.base64_content} />;
case "html": {
const decodedHTML = atob(d.base64_content);
const sanitizedHTML = DOMPurify.sanitize(decodedHTML);

return (
<div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />
);
}
case "plotly_json": {
const utf8String = atob(d.base64_content);
const decodedJSON = JSON.parse(utf8String);
return (
<Plot
data={decodedJSON.data}
layout={decodedJSON.layout}
style={{ width: "100%", height: "100%" }}
/>
);
}
default:
return <div>Unsupported file type: {d.file_type}</div>;
}
})}
</Grid>
</Paper>
);
};
Loading

0 comments on commit f0ff5be

Please sign in to comment.