import React, { useEffect } from 'react'; import { useInterval } from 'react-powerhooks'; import {IconButton, Typography, Grid, Select, Paper, Divider, ButtonBase, Button, TextField, FormControl, MenuItem, Tooltip, FormControlLabel, Switch, Input, Breadcrumbs, Chip, Dialog, DialogTitle, DialogActions, DialogContent, CircularProgress} from '@material-ui/core'; import {OpenInNew as OpenInNewIcon,Apps as AppsIcon, Cached as CachedIcon, Publish as PublishIcon, CloudDownload as CloudDownloadIcon, Edit as EditIcon, Delete as DeleteIcon} from '@material-ui/icons'; import { useTheme } from '@material-ui/core/styles'; import YAML from 'yaml' import {Link} from 'react-router-dom'; import ReactJson from 'react-json-view' import { useAlert } from "react-alert"; import Dropzone from '../components/Dropzone'; const surfaceColor = "#27292D" const inputColor = "#383B40" const chipStyle = { backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white", } // Parses JSON data into keys that can be used everywhere :) export const GetParsedPaths = (inputdata, basekey) => { const splitkey = " > " var parsedValues = [] if (inputdata === undefined || inputdata === null) { return parsedValues } if (typeof(inputdata) !== "object") { return parsedValues } for (const [key, value] of Object.entries(inputdata)) { // Check if loop or JSON const extra = basekey.length > 0 ? splitkey : "" const basekeyname = `${basekey.slice(1, basekey.length).split(".").join(splitkey)}${extra}${key}` // Handle direct loop! if (!isNaN(key) && basekey === "") { console.log("Handling direct loop.") parsedValues.push({"type": "object", "name": "Node", "autocomplete": `${basekey}`}) parsedValues.push({"type": "list", "name": `${splitkey}list`, "autocomplete": `${basekey}.#`}) const returnValues = GetParsedPaths(value, `${basekey}.#`) for (var subkey in returnValues) { parsedValues.push(returnValues[subkey]) } return parsedValues } //console.log("KEY: ", key, "VALUE: ", value, "BASEKEY: ", basekeyname) if (typeof(value) === 'object') { if (Array.isArray(value)) { // Check if each item is object parsedValues.push({"type": "object", "name": basekeyname, "autocomplete": `${basekey}.${key}`}) parsedValues.push({"type": "list", "name": `${basekeyname}${splitkey}list`, "autocomplete": `${basekey}.${key}.#`}) // Only check the first. This would be probably be dumb otherwise. for (var subkey in value) { if (typeof(value) === 'object') { const returnValues = GetParsedPaths(value[subkey], `${basekey}.${key}.#`) for (var subkey in returnValues) { parsedValues.push(returnValues[subkey]) } } // Don't need else as # (all items) is already defined before the loop break } //console.log(key+" is array") } else { parsedValues.push({"type": "object", "name": basekeyname, "autocomplete": `${basekey}.${key}`}) const returnValues = GetParsedPaths(value, `${basekey}.${key}`) for (var subkey in returnValues) { parsedValues.push(returnValues[subkey]) } } } else { parsedValues.push({"type": "value", "name": basekeyname, "autocomplete": `${basekey}.${key}`, "value": value,}) } } return parsedValues } const Apps = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata } = props; //const [workflows, setWorkflows] = React.useState([]); const theme = useTheme(); const baseRepository = "https://github.com/frikky/shuffle-apps" const alert = useAlert() const [selectedApp, setSelectedApp] = React.useState({}); const [firstrequest, setFirstrequest] = React.useState(true) const [apps, setApps] = React.useState([]) const [filteredApps, setFilteredApps] = React.useState([]) const [validation, setValidation] = React.useState(false) const [isLoading, setIsLoading] = React.useState(true) const [appSearchLoading, setAppSearchLoading] = React.useState(false) const [selectedAction, setSelectedAction] = React.useState({}) const [searchBackend, setSearchBackend] = React.useState(false) const [searchableApps, setSearchableApps] = React.useState([]) const [openApi, setOpenApi] = React.useState("") const [openApiData, setOpenApiData] = React.useState("") const [appValidation, setAppValidation] = React.useState("") const [loadAppsModalOpen, setLoadAppsModalOpen] = React.useState(false); const [deleteModalOpen, setDeleteModalOpen] = React.useState(false); const [openApiModal, setOpenApiModal] = React.useState(false); const [openApiModalType, setOpenApiModalType] = React.useState(""); const [openApiError, setOpenApiError] = React.useState("") const [field1, setField1] = React.useState("") const [field2, setField2] = React.useState("") const [cursearch, setCursearch] = React.useState("") const [sharingConfiguration, setSharingConfiguration] = React.useState("you") const [downloadBranch, setDownloadBranch] = React.useState("master") const [isDropzone, setIsDropzone] = React.useState(false); const upload = React.useRef(null); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" ? true : false const borderRadius = 3 const { start, stop } = useInterval({ duration: 5000, startImmediate: false, callback: () => { getApps() } }); useEffect(() => { if (apps.length <= 0 && firstrequest) { document.title = "Shuffle - Apps" if (!isLoggedIn && isLoaded) { window.location = "/login" } setFirstrequest(false) getApps() } }) function sortByKey(array, key) { if (array === undefined || array === null) { return array } return array.sort(function(a, b) { var x = a[key]; var y = b[key]; if (typeof x == "string") { x = (""+x).toLowerCase(); } if (typeof y == "string") { y = (""+y).toLowerCase(); } return ((x < y) ? 1 : ((x > y) ? -1 : 0)); }); } const appViewStyle = { color: "#ffffff", width: "100%", display: "flex", } const paperAppStyle = { minHeight: 130, maxHeight: 130, minWidth: "100%", maxWidth: 612.5, marginBottom: 5, borderRadius: 5, color: "white", backgroundColor: surfaceColor, cursor: "pointer", display: "flex", } const getApps = () => { fetch(globalUrl+"/api/v1/workflows/apps", { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { setIsLoading(false) if (response.status !== 200) { console.log("Status not 200 for apps :O!") if (isCloud) { window.location.pathname = "/search" } } return response.json() }) .then((responseJson) => { //console.log("Apps: ", responseJson) //responseJson = sortByKey(responseJson, "large_image") //responseJson = sortByKey(responseJson, "is_valid") //setFilteredApps(responseJson.filter(app => !internalIds.includes(app.name) && !(!app.activated && app.generated))) var privateapps = [] var valid = [] var invalid = [] for (var key in responseJson) { const app = responseJson[key] if (app.is_valid && !(!app.activated && app.generated)) { privateapps.push(app) } else if (app.private_id !== undefined && app.private_id.length > 0) { valid.push(app) } else { invalid.push(app) } } //console.log(privateapps) //console.log(valid) //console.log(invalid) //console.log(privateapps) //privateapps.reverse() privateapps.push(...valid) privateapps.push(...invalid) setApps(privateapps) setFilteredApps(privateapps) if (privateapps.length > 0) { if (selectedApp.id === undefined || selectedApp.id === null) { setSelectedApp(privateapps[0]) } if (privateapps[0].actions !== null && privateapps[0].actions.length > 0) { setSelectedAction(privateapps[0].actions[0]) } else { setSelectedAction({}) } } //runAppSearch("") }) .catch(error => { alert.error(error.toString()) setIsLoading(false) }); } const downloadApp = (inputdata) => { const id = inputdata.id alert.info("Downloading..") fetch(globalUrl+"/api/v1/apps/"+id+"/config", { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { window.location.pathname = "/apps" } return response.json() }) .then((responseJson) => { if (!responseJson.success) { alert.error("Failed to download file") } else { const inputdata = YAML.parse(responseJson.body) const newpaths = {} Object.keys(inputdata["paths"]).forEach(function(key) { newpaths[key.split("?")[0]] = inputdata.paths[key] }) inputdata.paths = newpaths console.log("INPUT: ", inputdata) var name = inputdata.info.title name = name.replace(/ /g, "_", -1) name = name.toLowerCase() delete inputdata.id delete inputdata.editing const data = YAML.stringify(inputdata) var blob = new Blob( [ data ], { type: 'application/octet-stream' }) var url = URL.createObjectURL( blob ) var link = document.createElement( 'a' ) link.setAttribute( 'href', url ) link.setAttribute( 'download', `${name}.yaml` ) 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 ) //link.parentNode.removeChild(link) } }) .catch(error => { console.log(error) alert.error(error.toString()) }); } // dropdown with copy etc I guess const appPaper = (data) => { var boxWidth = "2px" if (selectedApp.id === data.id) { boxWidth = "4px" } var boxColor = "orange" if (data.is_valid) { boxColor = "green" } if (!data.activated && data.generated) { boxColor = "orange" } if (data.invalid) { boxColor = "red" } //
ID: {selectedApp.id}
*/} SharingOwner: {selectedApp.owner}
*/} {selectedApp.privateId !== undefined && selectedApp.privateId.length > 0 ?PrivateID: {selectedApp.privateId}
: null}URL: {selectedApp.link}
: null}