From 5cb7d704d64d5c196da546ae6505437dc4457a21 Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 17 Mar 2023 00:51:12 +0100 Subject: [PATCH] Fixed file management component breakout to make it more smooth --- frontend/src/components/Files.jsx | 812 ++++++++++++++++++++++++++++++ 1 file changed, 812 insertions(+) create mode 100644 frontend/src/components/Files.jsx diff --git a/frontend/src/components/Files.jsx b/frontend/src/components/Files.jsx new file mode 100644 index 00000000..2a248d65 --- /dev/null +++ b/frontend/src/components/Files.jsx @@ -0,0 +1,812 @@ +import React, { useState, useEffect } from "react"; + +import { + IconButton, + List, + ListItem, + ListItemText, + ListItemAvatar, + ListItemSecondaryAction, + Tooltip, + Button, + FormControl, + InputLabel, + TextField, + Divider, + Select, + MenuItem, +} from "@material-ui/core"; + +import { + OpenInNew as OpenInNewIcon, + Edit as EditIcon, + CloudDownload as CloudDownloadIcon, + Delete as DeleteIcon, + FileCopy as FileCopyIcon, + Cached as CachedIcon, + Publish as PublishIcon, + Clear as ClearIcon, + Add as AddIcon, +} from "@material-ui/icons"; + +import { useAlert } from "react-alert"; +import Dropzone from "../components/Dropzone.jsx"; +import CodeEditor from "../components/ShuffleCodeEditor.jsx"; +import theme from "../theme"; + +const Files = (props) => { + const { globalUrl, userdata, serverside, selectedOrganization, } = props; + + const [files, setFiles] = React.useState([]); + const [selectedNamespace, setSelectedNamespace] = React.useState("default"); + const [openFileId, setOpenFileId] = React.useState(false); + const [fileNamespaces, setFileNamespaces] = React.useState([]); + const [fileContent, setFileContent] = React.useState(""); + const [openEditor, setOpenEditor] = React.useState(false); + const [renderTextBox, setRenderTextBox] = React.useState(false); + + const alert = useAlert(); + const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv",] + var upload = ""; + + const handleKeyDown = (event) => { + if (event.key === 'Enter') { + + console.log('do validate') + console.log("new namespace name->",event.target.value); + fileNamespaces.push(event.target.value); + setSelectedNamespace(event.target.value); + setRenderTextBox(false); + } + + if (event.key === 'Escape'){ // not working for some reasons + console.log('escape pressed') + setRenderTextBox(false); + } + + } + + const runUpdateText = (text) =>{ + fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body:text, + credentials: "include", + }).then((response) => { + if (response.status !== 200) { + console.log("Can't update file"); + } + return response.json(); + }) + //console.log(text); + } + + const getFiles = () => { + fetch(globalUrl + "/api/v1/files", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for apps :O!"); + return; + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.files !== undefined && responseJson.files !== null) { + setFiles(responseJson.files); + } else { + setFiles([]); + } + + console.log("NAMESPACES: ", responseJson.namespaces); + if ( + responseJson.namespaces !== undefined && + responseJson.namespaces !== null + ) { + setFileNamespaces(responseJson.namespaces); + } + }) + .catch((error) => { + alert.error(error.toString()); + }); + }; + + useEffect(() => { + getFiles(); + }, []); + + const deleteFile = (file) => { + fetch(globalUrl + "/api/v1/files/" + file.id, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for file delete :O!"); + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson.success) { + alert.info("Successfully deleted file " + file.name); + } else if ( + responseJson.reason !== undefined && + responseJson.reason !== null + ) { + alert.error("Failed to delete file: " + responseJson.reason); + } + setTimeout(() => { + getFiles(); + }, 1500); + + console.log(responseJson); + }) + .catch((error) => { + alert.error(error.toString()); + }); + }; + + const readFileData = (file) => { + fetch(globalUrl + "/api/v1/files/" + file.id + "/content", { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for file :O!"); + return ""; + } + return response.text(); + }) + .then((respdata) => { + // console.log("respdata ->", respdata); + // console.log("respdata type ->", typeof(respdata)); + + if (respdata.length === 0) { + alert.error("Failed getting file. Is it deleted?"); + return; + } + return respdata + }) + .then((responseData) => { + + setFileContent(responseData); + //console.log("filecontent state ",fileContent); + }) + .catch((error) => { + alert.error(error.toString()); + }); + }; + + const downloadFile = (file) => { + fetch(globalUrl + "/api/v1/files/" + file.id + "/content", { + method: "GET", + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for apps :O!"); + return ""; + } + + console.log("Resp: ", response) + + return response.blob() + }) + .then((respdata) => { + if (respdata.length === 0) { + alert.error("Failed getting file. Is it deleted?"); + return; + } + + var blob = new Blob([respdata], { + type: "application/octet-stream", + }); + + var url = URL.createObjectURL(blob); + var link = document.createElement("a"); + link.setAttribute("href", url); + link.setAttribute("download", `${file.filename}`); + var event = document.createEvent("MouseEvents"); + event.initMouseEvent( + "click", + true, + true, + window, + 1, + 0, + 0, + 0, + 0, + false, + false, + false, + false, + 0, + null + ); + link.dispatchEvent(event); + + //return response.json() + }) + .then((responseJson) => { + //console.log(responseJson) + //setSchedules(responseJson) + }) + .catch((error) => { + alert.error(error.toString()); + }); + }; + + const handleCreateFile = (filename, file) => { + var data = { + filename: filename, + org_id: selectedOrganization.id, + workflow_id: "global", + }; + + if ( + selectedNamespace !== undefined && + selectedNamespace !== null && + selectedNamespace.length > 0 && + selectedNamespace !== "default" + ) { + data.namespace = selectedNamespace; + } + + fetch(globalUrl + "/api/v1/files/create", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + body: JSON.stringify(data), + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for apps :O!"); + return; + } + + return response.json(); + }) + .then((responseJson) => { + //console.log("RESP: ", responseJson) + if (responseJson.success === true) { + handleFileUpload(responseJson.id, file); + } else { + alert.error("Failed to upload file ", filename); + } + }) + .catch((error) => { + alert.error("Failed to upload file ", filename); + console.log(error.toString()); + }); + }; + + + + const handleFileUpload = (file_id, file) => { + //console.log("FILE: ", file_id, file) + fetch(`${globalUrl}/api/v1/files/${file_id}/upload`, { + method: "POST", + credentials: "include", + body: file, + }) + .then((response) => { + if (response.status !== 200 && response.status !== 201) { + console.log("Status not 200 for apps :O!"); + alert.error("File was created, but failed to upload."); + return; + } + + return response.json(); + }) + .then((responseJson) => { + //console.log("RESPONSE: ", responseJson) + //setFiles(responseJson) + }) + .catch((error) => { + alert.error(error.toString()); + }); + }; + + const uploadFiles = (files) => { + for (var key in files) { + try { + const filename = files[key].name; + var filedata = new FormData(); + filedata.append("shuffle_file", files[key]); + + if (typeof files[key] === "object") { + handleCreateFile(filename, filedata); + } + + /* + reader.addEventListener('load', (e) => { + var data = e.target.result; + setIsDropzone(false) + console.log(filename) + console.log(data) + console.log(files[key]) + }) + reader.readAsText(files[key]) + */ + } catch (e) { + console.log("Error in dropzone: ", e); + } + } + + setTimeout(() => { + getFiles(); + }, 2500); + }; + + const uploadFile = (e) => { + const isDropzone = + e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0; + const files = isDropzone ? e.dataTransfer.files : e.target.files; + + //const reader = new FileReader(); + //alert.info("Starting fileupload") + uploadFiles(files); + }; + + return ( + 1366 ? 1366 : 1200, + margin: "auto", + padding: 20, + }} + onDrop={uploadFile} + > +
+
+

Files

+ + Files from Workflows.{" "} + + Learn more + + +
+ + {/* */} + (upload = ref)} + onChange={(event) => { + //const file = event.target.value + //const fileObject = URL.createObjectURL(actualFile) + //setFile(fileObject) + //const files = event.target.files[0] + uploadFiles(event.target.files); + }} + /> + + + {fileNamespaces !== undefined && + fileNamespaces !== null && + fileNamespaces.length > 1 ? ( + + File Category + + + ) : null} +
+ {renderTextBox ? + + + + + : + + + } + {renderTextBox && { + handleKeyDown(event); + }} + InputProps={{ + style: { + color: "white", + }, + }} + color="primary" + placeholder="File category name" + required + margin="dense" + defaultValue={""} + autoFocus + />}
+ + + + + + + + + + + + + + + + {files === undefined || files === null || files.length === 0 ? null : + files.map((file, index) => { + if (file.namespace === "") { + file.namespace = "default"; + } + + if (file.namespace !== selectedNamespace) { + return null; + } + + var bgColor = "#27292d"; + if (index % 2 === 0) { + bgColor = "#1f2023"; + } + + const filenamesplit = file.filename.split(".") + const iseditable = file.filesize < 100000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1]) + + return ( + + + + + + + ) : ( + + + + + + + + + + ) + } + style={{ + minWidth: 100, + maxWidth: 100, + overflow: "hidden", + }} + /> + + + + + + + { + setOpenEditor(true) + setOpenFileId(file.id) + readFileData(file) + }} + > + + + + + + + { + downloadFile(file); + }} + > + + + + + + { + const elementName = "copy_element_shuffle"; + var copyText = + document.getElementById(elementName); + if ( + copyText !== null && + copyText !== undefined + ) { + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + alert.error( + "Can only copy over HTTPS (port 3443)" + ); + return; + } + + navigator.clipboard.writeText(file.id); + copyText.select(); + copyText.setSelectionRange( + 0, + 99999 + ); /* For mobile devices */ + + /* Copy the text inside the text field */ + document.execCommand("copy"); + + alert.info(file.id + " copied to clipboard"); + } + }} + > + + + + + + { + deleteFile(file); + }} + > + + + + + + style={{ + minWidth: 250, + maxWidth: 250, + // overflow: "hidden", + }} + /> + + ); + }) + } + +
+
+ ) +} + +export default Files;