Skip to content

Commit

Permalink
Merge pull request #205 from Tauffer-Consulting/feature/display-all-r…
Browse files Browse the repository at this point in the history
…esults

Feature/display-all-results
nathan-vm authored Dec 28, 2023
2 parents bf35832 + 4d5cb80 commit 07adcdc
Showing 26 changed files with 1,286 additions and 421 deletions.
5 changes: 2 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -26,9 +26,7 @@
"axios": "^1.2.1",
"axios-mock-adapter": "^1.21.2",
"cross-env": "^7.0.3",
"date-fns": "^2.30.0",
"dayjs": "^1.11.7",
"dompurify": "^3.0.6",
"dotenv": "^16.3.1",
"elkjs": "^0.8.2",
"localforage": "^1.10.0",
@@ -38,9 +36,10 @@
"react-draggable": "^4.4.5",
"react-hook-form": "^7.45.1",
"react-markdown": "9.0.0",
"react-pdf": "^7.5.1",
"react-pdf": "^7.6.0",
"react-plotly.js": "^2.6.0",
"react-router-dom": "^6.6.0",
"react-to-print": "^2.14.15",
"react-toastify": "^9.1.1",
"reactflow": "^11.4.0",
"remark-gfm": "^4.0.0",
9 changes: 5 additions & 4 deletions frontend/src/@types/utils/index.d.ts
Original file line number Diff line number Diff line change
@@ -4,9 +4,10 @@ declare global {
) => infer R
? R
: T extends (
instance: ReactFlowInstance<NodeData, EdgeData>,
) => Promise<infer R>
? R
: never;
instance: ReactFlowInstance<NodeData, EdgeData>,
) => Promise<infer R>
? R
: never;
}

export {};
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>
);
};
33 changes: 33 additions & 0 deletions frontend/src/components/DownloadPDF/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Button, type ButtonProps } from "@mui/material";
import React, { useEffect, useState } from "react";
import { useReactToPrint } from "react-to-print";

interface Props extends ButtonProps {
contentId: string;
}

export const DownloadAsPDF: React.FC<Props> = ({ contentId, ...props }) => {
const [content, setContent] = useState<HTMLElement | null>(null);
const handlePrint = useReactToPrint({
content: () => content,
});

useEffect(() => {
// Fetch the content element using the contentId
const newContent = document.getElementById(contentId);
setContent(newContent);
}, [contentId]);

const handlePrintWithTimeout = () => {
// Add a short timeout to ensure styles are applied before printing
setTimeout(() => {
handlePrint();
}, 2000); // Adjust the timeout duration as needed
};

return (
<Button variant="outlined" onClick={handlePrintWithTimeout} {...props}>
Generate PDF
</Button>
);
};
45 changes: 45 additions & 0 deletions frontend/src/components/HTMLRender/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useRef, useState } from "react";

interface Props {
html: string;
}

const HtmlRenderer: React.FC<Props> = ({ html }) => {
const iframeRef = useRef<HTMLIFrameElement | null>(null);
const [minHeight, setMinHeight] = useState<number>(0);

const handleLoad = () => {
const iframe = iframeRef.current;
if (iframe) {
const newMinHeight =
iframe.contentWindow?.document.body.scrollHeight ?? 0;
setMinHeight(newMinHeight);
iframe.style.height = `${newMinHeight}px`;
}
};

return (
<div
style={{
minHeight: `${minHeight}px`,
height: "100%",
width: "100%",
overflowY: "hidden",
overflowX: "auto",
}}
>
<iframe
ref={iframeRef}
title="html-renderer"
srcDoc={html}
width="100%"
height="100%"
style={{ border: "none" }}
scrolling="no"
onLoad={handleLoad}
></iframe>
</div>
);
};

export default HtmlRenderer;
92 changes: 92 additions & 0 deletions frontend/src/components/RenderB64/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Typography } from "@mui/material";
import HtmlRenderer from "components/HTMLRender";
import { RenderPDF } from "components/RenderPDF";
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);

return <HtmlRenderer html={decodedHTML} />;
}
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>;
}
};
20 changes: 9 additions & 11 deletions frontend/src/components/RenderPDF/index.tsx
Original file line number Diff line number Diff line change
@@ -35,16 +35,14 @@ export const RenderPDF: React.FC<Props> = (props) => {
}

return (
<div style={{ overflow: "auto", maxWidth: "100%", width: "100%" }}>
<Document
file={file}
onLoadSuccess={onDocumentLoadSuccess}
options={options}
>
{Array.from(new Array(numPages), (el, index) => (
<Page key={`page_${index + 1}`} pageNumber={index + 1} width={650} />
))}
</Document>
</div>
<Document
file={file}
onLoadSuccess={onDocumentLoadSuccess}
options={options}
>
{Array.from(new Array(numPages), (el, index) => (
<Page key={`page_${index + 1}`} pageNumber={index + 1} width={650} />
))}
</Document>
);
};
89 changes: 89 additions & 0 deletions frontend/src/features/myWorkflows/api/runs/getWorkflowRunReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { type AxiosResponse } from "axios";
import { useWorkspaces } from "context/workspaces";
import { type taskState } from "features/myWorkflows/types";
import { dominoApiClient } from "services/clients/domino.client";
import useSWR from "swr";

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

export interface IGetWorkflowRunResultReportResponse {
base64_content: string;
file_type: string;
piece_name: string;
dag_id: string;
duration: number;
start_date: string;
end_date: string;
execution_date: string;
task_id: string;
state: taskState;
}

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: IGetWorkflowRunResultReportResponse[];
}>
| 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),
);
};
Loading

0 comments on commit 07adcdc

Please sign in to comment.