Added simple frontend changes
This commit is contained in:
@@ -1,9 +1,5 @@
|
|||||||
# app_sdk.py
|
# app_sdk.py
|
||||||
This is the SDK used for apps to behave like they should.
|
This is the SDK used for apps to behave like they should.
|
||||||
To change it in the backend, upload it to Buckets/shuffler.appspot.com/generated_apps/baseline.
|
|
||||||
|
|
||||||
# static_baseline.py
|
|
||||||
It's used for python code generation and should be under MIT. Has to be located here because it's used by the backend.
|
|
||||||
|
|
||||||
## If you want to update apps.. PS: downloads from docker hub do overrides.. :)
|
## If you want to update apps.. PS: downloads from docker hub do overrides.. :)
|
||||||
1. Write your code & check if runtime works
|
1. Write your code & check if runtime works
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
urllib3
|
urllib3=1.25.9
|
||||||
requests
|
requests=2.25.1
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
|
|
||||||
# Goal here:
|
|
||||||
# * Make an app from WALKOFF able to run without app_base.py from WALKOFF
|
|
||||||
# # How:
|
|
||||||
# * Make it rely 100% on INPUT throug HTTP invocations instead of redis READS
|
|
||||||
# # But really, how?
|
|
||||||
# * Make a WORKER that reads the queue, and reuses a function
|
|
||||||
|
|
||||||
# Here to get it global
|
|
||||||
apikey = ""
|
|
||||||
try:
|
|
||||||
apikey = os.environ["FUNCTION_APIKEY"]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Authorize the execution
|
|
||||||
def authorization(request):
|
|
||||||
# This is basically my issue, but it enforces the use of an internal API key for execution
|
|
||||||
try:
|
|
||||||
apikey = os.environ["FUNCTION_APIKEY"]
|
|
||||||
except KeyError:
|
|
||||||
return f"Internal server error", 500
|
|
||||||
|
|
||||||
|
|
||||||
# Check API key from ENV authentication
|
|
||||||
authentication = request.headers.get("Authorization")
|
|
||||||
if authentication == None: return f"Unauthorized", 401
|
|
||||||
|
|
||||||
apikey_split = authentication.split(" ")
|
|
||||||
if apikey_split[0] != "Bearer" or len(apikey_split) != 2:
|
|
||||||
return f"Apikey error", 401
|
|
||||||
|
|
||||||
if apikey != apikey_split[1]:
|
|
||||||
return f"Unauthorized", 401
|
|
||||||
|
|
||||||
return run(request)
|
|
||||||
|
|
||||||
class AppBase:
|
|
||||||
""" The base class for Python-based Walkoff applications, handles Redis and logging configurations. """
|
|
||||||
__version__ = None
|
|
||||||
app_name = None
|
|
||||||
|
|
||||||
def __init__(self, redis=None, logger=None, console_logger=None):#, docker_client=None):
|
|
||||||
self.logger = logger if logger is not None else logging.getLogger("AppBaseLogger")
|
|
||||||
self.redis=redis
|
|
||||||
self.console_logger=console_logger
|
|
||||||
self.current_execution_id = None
|
|
||||||
self.url = "https://shuffler.io"
|
|
||||||
self.apikey = apikey
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def run(cls, action):
|
|
||||||
""" Connect to Redis and HTTP session, await actions """
|
|
||||||
logging.basicConfig(format="{asctime} - {name} - {levelname}:{message}", style='{')
|
|
||||||
logger = logging.getLogger(f"{cls.__name__}")
|
|
||||||
logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
app = cls(redis=None, logger=logger, console_logger=logger)
|
|
||||||
|
|
||||||
# Authorization for the app/function to control the workflow
|
|
||||||
# Function will crash if its wrong, which it probably should.
|
|
||||||
|
|
||||||
await app.execute_action(action)
|
|
||||||
|
|
||||||
async def execute_action(self, action):
|
|
||||||
# FIXME - add request for the function STARTING here. Use "results stream" or something
|
|
||||||
# PAUSED, AWAITING_DATA, PENDING, COMPLETED, ABORTED, EXECUTING, SUCCESS, FAILURE
|
|
||||||
|
|
||||||
self.authorization = action["authorization"]
|
|
||||||
self.execution_id = action["execution_id"]
|
|
||||||
self.current_execution_id = action["execution_id"]
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
|
import {Typography, } from '@material-ui/core';
|
||||||
|
|
||||||
|
const Workflow = (props) => {
|
||||||
|
const { workflow, appAuthentication, apps } = props
|
||||||
|
const [requiredActions, setRequiredActions] = React.useState([])
|
||||||
|
const [firstLoad, setFirstLoad] = React.useState("")
|
||||||
|
|
||||||
|
// Rofl
|
||||||
|
if (workflow === undefined || workflow === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apps === undefined || apps === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appAuthentication === undefined || appAuthentication === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstLoad.length === 0 || firstLoad !== workflow.id) {
|
||||||
|
setFirstLoad(workflow.id)
|
||||||
|
const newactions = []
|
||||||
|
for (var key in workflow.actions) {
|
||||||
|
var newaction = {
|
||||||
|
"large_image": "",
|
||||||
|
"app_name": "",
|
||||||
|
"app_version": "",
|
||||||
|
"must_activate": false,
|
||||||
|
"must_authenticate": false,
|
||||||
|
"action_ids": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = workflow.actions[key]
|
||||||
|
console.log(action)
|
||||||
|
const app = apps.find(app => app.name === action.app_name && app.app_version === action.app_version)
|
||||||
|
if (app === undefined || app === null) {
|
||||||
|
console.log("COULDNT FIND APP - SEARCH BACKEND")
|
||||||
|
|
||||||
|
newaction.app_name = action.app_name
|
||||||
|
newaction.app_version = action.app_version
|
||||||
|
} else {
|
||||||
|
newaction.app_name = app.name
|
||||||
|
newaction.app_version = app.app_version
|
||||||
|
|
||||||
|
console.log("APP: ", app)
|
||||||
|
if (action.authentication_id === "" && app.authentication.required === true) {
|
||||||
|
console.log("Requires auth!")
|
||||||
|
newaction.must_authenticate = true
|
||||||
|
newaction.action_ids.push(action.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
//newaction.app_name = action.app_name
|
||||||
|
//newaction.app_name = action.app_version
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.errors !== undefined && action.errors !== null && action.errors.length > 0) {
|
||||||
|
console.log("Has errors!")
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("NEWACTION: ", newaction)
|
||||||
|
if (newaction.must_authenticate || newaction.must_activate) {
|
||||||
|
newactions.push(newaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("ACTIONS: ", newactions)
|
||||||
|
setRequiredActions(newactions)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppSection = (props) => {
|
||||||
|
const {action} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Typography variant="body2">Name: {action.app_name}:{action.app_version}. </Typography>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(requiredActions)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Typography variant="h6">Workflow: {workflow.id}</Typography>
|
||||||
|
{requiredActions.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<AppSection key={index} action={data} />
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Workflow
|
||||||
@@ -92,8 +92,7 @@ const AngularWorkflow = (props) => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const [bodyWidth, bodyHeight] = useWindowSize();
|
const [bodyWidth, bodyHeight] = useWindowSize();
|
||||||
const appBarSize = 74
|
const appBarSize = 75
|
||||||
const headerSize = 60
|
|
||||||
|
|
||||||
var to_be_copied = ""
|
var to_be_copied = ""
|
||||||
const [cystyle, ] = useState(cytoscapestyle)
|
const [cystyle, ] = useState(cytoscapestyle)
|
||||||
@@ -124,6 +123,7 @@ const AngularWorkflow = (props) => {
|
|||||||
const [newVariableDescription, setNewVariableDescription] = React.useState("");
|
const [newVariableDescription, setNewVariableDescription] = React.useState("");
|
||||||
const [newVariableValue, setNewVariableValue] = React.useState("");
|
const [newVariableValue, setNewVariableValue] = React.useState("");
|
||||||
const [workflowDone, setWorkflowDone] = React.useState(false)
|
const [workflowDone, setWorkflowDone] = React.useState(false)
|
||||||
|
const [authLoaded, setAuthLoaded] = React.useState(false)
|
||||||
const [localFirstrequest, setLocalFirstrequest] = React.useState(true)
|
const [localFirstrequest, setLocalFirstrequest] = React.useState(true)
|
||||||
const [requiresAuthentication, setRequiresAuthentication] = React.useState(false)
|
const [requiresAuthentication, setRequiresAuthentication] = React.useState(false)
|
||||||
const [rightSideBarOpen, setRightSideBarOpen] = React.useState(false)
|
const [rightSideBarOpen, setRightSideBarOpen] = React.useState(false)
|
||||||
@@ -1002,7 +1002,6 @@ const AngularWorkflow = (props) => {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.log("Status not 200 for apps :O!")
|
console.log("Status not 200 for apps :O!")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
@@ -1018,18 +1017,27 @@ const AngularWorkflow = (props) => {
|
|||||||
newauth.push(responseJson.data[key])
|
newauth.push(responseJson.data[key])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reset === true) {
|
if (cy !== undefined) {
|
||||||
console.log("APP RESET = reset cy")
|
console.log("NEW AUTH = reset cy's onnodeselect")
|
||||||
|
|
||||||
|
// Remove the old listener for select, run with new one
|
||||||
|
cy.removeListener('select')
|
||||||
|
|
||||||
cy.on('select', 'node', (e) => onNodeSelect(e, newauth))
|
cy.on('select', 'node', (e) => onNodeSelect(e, newauth))
|
||||||
|
cy.on('select', 'edge', (e) => onEdgeSelect(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
setAppAuthentication(newauth)
|
setAppAuthentication(newauth)
|
||||||
|
setAuthLoaded(true)
|
||||||
} else {
|
} else {
|
||||||
|
setAuthLoaded(true)
|
||||||
alert.error("Failed getting authentications")
|
alert.error("Failed getting authentications")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
alert.error(error.toString())
|
setAuthLoaded(true)
|
||||||
});
|
alert.error("Auth loading error: ", error.toString())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1187,17 +1195,20 @@ const AngularWorkflow = (props) => {
|
|||||||
setSelectedTrigger({})
|
setSelectedTrigger({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nodeselectbatching:
|
||||||
|
// https://stackoverflow.com/questions/16677856/cy-onselect-callback-only-once
|
||||||
const onNodeSelect = (event, newAppAuth) => {
|
const onNodeSelect = (event, newAppAuth) => {
|
||||||
const data = event.target.data()
|
const data = event.target.data()
|
||||||
setLastSaved(false)
|
setLastSaved(false)
|
||||||
|
|
||||||
const node = cy.getElementById(data.id)
|
//const node = cy.getElementById(data.id)
|
||||||
if (node.length > 0) {
|
//if (node.length > 0) {
|
||||||
node.addClass('shuffle-hover-highlight')
|
// node.addClass('shuffle-hover-highlight')
|
||||||
}
|
//}
|
||||||
|
|
||||||
//const branch = workflow.branches.filter(branch => branch.source_id === data.id || branch.destination_id === data.id)
|
//const branch = workflow.branches.filter(branch => branch.source_id === data.id || branch.destination_id === data.id)
|
||||||
console.log("NODE: ", data)
|
console.log("NODE: ", data)
|
||||||
|
console.log("APPAUTH: ", newAppAuth)
|
||||||
//console.log("BRANCHES: ", branch)
|
//console.log("BRANCHES: ", branch)
|
||||||
|
|
||||||
if (data.type === "ACTION") {
|
if (data.type === "ACTION") {
|
||||||
@@ -1213,13 +1224,14 @@ const AngularWorkflow = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedAction(curaction)
|
|
||||||
|
|
||||||
const curapp = apps.find(a => a.name === curaction.app_name && a.app_version === curaction.app_version)
|
const curapp = apps.find(a => a.name === curaction.app_name && a.app_version === curaction.app_version)
|
||||||
if (!curapp || curapp === undefined) {
|
if (!curapp || curapp === undefined) {
|
||||||
alert.error("App "+curaction.app_name+" not found. Did someone delete it?")
|
alert.error("App "+curaction.app_name+" not found. Is it activated?")
|
||||||
//return
|
//return
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
console.log("AUTHENTICATION: ", curapp.authentication)
|
||||||
setRequiresAuthentication(curapp.authentication.required && curapp.authentication.parameters !== undefined && curapp.authentication.parameters !== null)
|
setRequiresAuthentication(curapp.authentication.required && curapp.authentication.parameters !== undefined && curapp.authentication.parameters !== null)
|
||||||
if (curapp.authentication.required) {
|
if (curapp.authentication.required) {
|
||||||
// Setup auth here :)
|
// Setup auth here :)
|
||||||
@@ -1230,6 +1242,8 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tmpAuth = JSON.parse(JSON.stringify(newAppAuth))
|
var tmpAuth = JSON.parse(JSON.stringify(newAppAuth))
|
||||||
|
console.log("FOUND AUTH: ", tmpAuth)
|
||||||
|
|
||||||
//console.log("Checking authentication: ", tmpAuth)
|
//console.log("Checking authentication: ", tmpAuth)
|
||||||
for (var key in tmpAuth) {
|
for (var key in tmpAuth) {
|
||||||
var item = tmpAuth[key]
|
var item = tmpAuth[key]
|
||||||
@@ -1248,6 +1262,7 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("OPTIONS: ", authenticationOptions)
|
||||||
curaction.authentication = authenticationOptions
|
curaction.authentication = authenticationOptions
|
||||||
//console.log("Authentication: ", authenticationOptions)
|
//console.log("Authentication: ", authenticationOptions)
|
||||||
if (curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || curaction.selectedAuthentication.length === "") {
|
if (curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || curaction.selectedAuthentication.length === "") {
|
||||||
@@ -1260,6 +1275,7 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSelectedApp(curapp)
|
setSelectedApp(curapp)
|
||||||
|
setSelectedAction(curaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (environments !== undefined && environments !== null) {
|
if (environments !== undefined && environments !== null) {
|
||||||
@@ -1271,28 +1287,6 @@ const AngularWorkflow = (props) => {
|
|||||||
setSelectedActionEnvironment(env)
|
setSelectedActionEnvironment(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
var params = []
|
|
||||||
const fixedName = "$"+curaction.label.toLowerCase().replace(" ", "_")
|
|
||||||
for (var actionkey in workflow.actions) {
|
|
||||||
if (workflow.actions[actionkey].id === curaction.id) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var paramkey in workflow.actions[actionkey].parameters) {
|
|
||||||
const param = workflow.actions[actionkey].parameters[paramkey]
|
|
||||||
if (param.value === null || param.value === undefined || !param.value.includes("$")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const innername = param.value.toLowerCase().replace(" ", "_")
|
|
||||||
if (innername.includes(fixedName)) {
|
|
||||||
console.log("FOUND!: ", innername)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} else if (data.type === "TRIGGER") {
|
} else if (data.type === "TRIGGER") {
|
||||||
//console.log("Should handle trigger "+data.triggertype)
|
//console.log("Should handle trigger "+data.triggertype)
|
||||||
//console.log(data)
|
//console.log(data)
|
||||||
@@ -1631,7 +1625,12 @@ const AngularWorkflow = (props) => {
|
|||||||
if (elements.length === 0 && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) {
|
if (elements.length === 0 && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) {
|
||||||
setGraphSetup(true)
|
setGraphSetup(true)
|
||||||
setupGraph()
|
setupGraph()
|
||||||
} else if (!established && cy !== undefined && apps !== null && apps !== undefined && apps.length > 0 && Object.getOwnPropertyNames(workflow).length > 0){
|
} else if (!established && cy !== undefined && apps !== null && apps !== undefined && apps.length > 0 && Object.getOwnPropertyNames(workflow).length > 0 && authLoaded){
|
||||||
|
//This part has to load LAST, as it's kind of not async.
|
||||||
|
//This means we need everything else to happen first.
|
||||||
|
console.log("AUTH IN HERE: ", appAuthentication)
|
||||||
|
|
||||||
|
|
||||||
setEstablished(true)
|
setEstablished(true)
|
||||||
cy.edgehandles({
|
cy.edgehandles({
|
||||||
handleNodes: (el) => el.isNode(),
|
handleNodes: (el) => el.isNode(),
|
||||||
@@ -2648,9 +2647,9 @@ const AngularWorkflow = (props) => {
|
|||||||
const [hover, setHover] = React.useState(false)
|
const [hover, setHover] = React.useState(false)
|
||||||
|
|
||||||
// FIXME - add label to apps, as this might be slow with A LOT of apps
|
// FIXME - add label to apps, as this might be slow with A LOT of apps
|
||||||
var newAppname = app.name
|
|
||||||
newAppname = newAppname.replace("_", " ").charAt(0).toUpperCase()+newAppname.substring(1)
|
|
||||||
const maxlen = 24
|
const maxlen = 24
|
||||||
|
var newAppname = app.name.replace("_", " ", -1)
|
||||||
|
newAppname = newAppname.charAt(0).toUpperCase()+newAppname.substring(1)
|
||||||
if (newAppname.length > maxlen) {
|
if (newAppname.length > maxlen) {
|
||||||
newAppname = newAppname.slice(0, maxlen)+".."
|
newAppname = newAppname.slice(0, maxlen)+".."
|
||||||
}
|
}
|
||||||
@@ -2661,7 +2660,6 @@ const AngularWorkflow = (props) => {
|
|||||||
const pixelSize = !hover ? "2px" : "4px"
|
const pixelSize = !hover ? "2px" : "4px"
|
||||||
newAppStyle.borderLeft = app.is_valid ? `${pixelSize} solid green` : `${pixelSize} solid orange`
|
newAppStyle.borderLeft = app.is_valid ? `${pixelSize} solid green` : `${pixelSize} solid orange`
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Draggable
|
<Draggable
|
||||||
onDrag={(e) => {handleAppDrag(e, app)}}
|
onDrag={(e) => {handleAppDrag(e, app)}}
|
||||||
@@ -3991,7 +3989,7 @@ const AngularWorkflow = (props) => {
|
|||||||
const rightsidebarStyle = {
|
const rightsidebarStyle = {
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
right: 0,
|
right: 0,
|
||||||
top: headerSize+1,
|
top: appBarSize+1,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
minWidth: 350,
|
minWidth: 350,
|
||||||
@@ -6537,9 +6535,11 @@ const AngularWorkflow = (props) => {
|
|||||||
//}}>Execute websocket</Button>
|
//}}>Execute websocket</Button>
|
||||||
//
|
//
|
||||||
|
|
||||||
const leftView = leftViewOpen ? <div style={{minWidth: leftBarSize, maxWidth: leftBarSize, borderRight: "1px solid rgb(91, 96, 100)"}}>
|
const leftView = leftViewOpen ?
|
||||||
|
<div style={{minWidth: leftBarSize, maxWidth: leftBarSize, borderRight: "1px solid rgb(91, 96, 100)"}}>
|
||||||
<HandleLeftView />
|
<HandleLeftView />
|
||||||
</div> :
|
</div>
|
||||||
|
:
|
||||||
<div style={{minWidth: leftBarSize, maxWidth: leftBarSize, borderRight: "1px solid rgb(91, 96, 100)"}}>
|
<div style={{minWidth: leftBarSize, maxWidth: leftBarSize, borderRight: "1px solid rgb(91, 96, 100)"}}>
|
||||||
<div style={{cursor: "pointer", height: 20, marginTop: 10, marginLeft: 10}} onClick={() => {
|
<div style={{cursor: "pointer", height: 20, marginTop: 10, marginLeft: 10}} onClick={() => {
|
||||||
setLeftViewOpen(true)
|
setLeftViewOpen(true)
|
||||||
@@ -7172,7 +7172,7 @@ const AngularWorkflow = (props) => {
|
|||||||
elements={elements}
|
elements={elements}
|
||||||
minZoom={0.35}
|
minZoom={0.35}
|
||||||
maxZoom={2.00}
|
maxZoom={2.00}
|
||||||
style={{width: bodyWidth-leftBarSize-15, height: bodyHeight-appBarSize-1, backgroundColor: surfaceColor}}
|
style={{width: bodyWidth-leftBarSize-15, height: bodyHeight-appBarSize-5, backgroundColor: surfaceColor}}
|
||||||
stylesheet={cystyle}
|
stylesheet={cystyle}
|
||||||
boxSelectionEnabled={true}
|
boxSelectionEnabled={true}
|
||||||
autounselectify={false}
|
autounselectify={false}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import React, { useEffect } from 'react';
|
|||||||
|
|
||||||
import { useInterval } from 'react-powerhooks';
|
import { useInterval } from 'react-powerhooks';
|
||||||
|
|
||||||
import {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 {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 {Apps as AppsIcon, Cached as CachedIcon, Publish as PublishIcon, CloudDownload as CloudDownloadIcon, Edit as EditIcon, Delete as DeleteIcon} from '@material-ui/icons';
|
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 { useTheme } from '@material-ui/core/styles';
|
||||||
|
|
||||||
@@ -607,7 +607,7 @@ const Apps = (props) => {
|
|||||||
|
|
||||||
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"),
|
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"),
|
||||||
var baseInfo = newAppname.length > 0 ?
|
var baseInfo = newAppname.length > 0 ?
|
||||||
<div>
|
<div style={{position: "relative"}}>
|
||||||
<div style={{display: "flex"}}>
|
<div style={{display: "flex"}}>
|
||||||
<div style={{marginRight: 15, marginTop: 10}}>
|
<div style={{marginRight: 15, marginTop: 10}}>
|
||||||
{imageline}
|
{imageline}
|
||||||
@@ -618,6 +618,14 @@ const Apps = (props) => {
|
|||||||
<p style={{marginTop: 5, marginBottom: 0, maxHeight: 150, overflowY: "auto", overflowX: "hidden",}}>{description}</p>
|
<p style={{marginTop: 5, marginBottom: 0, maxHeight: 150, overflowY: "auto", overflowX: "hidden",}}>{description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{isCloud ?
|
||||||
|
<a href={"https://shuffler.io/apps/"+selectedApp.id} style={{textDecoration: "none", color: "#f85a3e"}} target="_blank">
|
||||||
|
<IconButton style={{top: -10, right: 0, position: "absolute", color: "#f85a3e"}} >
|
||||||
|
<OpenInNewIcon style={{}} />
|
||||||
|
</IconButton>
|
||||||
|
</a>
|
||||||
|
: null}
|
||||||
|
|
||||||
{activateButton}
|
{activateButton}
|
||||||
{(props.userdata !== undefined && (props.userdata.role === "admin" || props.userdata.id === selectedApp.owner) || !selectedApp.generated) ?
|
{(props.userdata !== undefined && (props.userdata.role === "admin" || props.userdata.id === selectedApp.owner) || !selectedApp.generated) ?
|
||||||
<div>
|
<div>
|
||||||
@@ -886,7 +894,7 @@ const Apps = (props) => {
|
|||||||
<div style={{flex: 1, marginLeft: 10, marginRight: 10}}>
|
<div style={{flex: 1, marginLeft: 10, marginRight: 10}}>
|
||||||
<div style={{display: "flex", minHeight: 84.81}}>
|
<div style={{display: "flex", minHeight: 84.81}}>
|
||||||
<div style={{flex: 1}}>
|
<div style={{flex: 1}}>
|
||||||
<h2>Your apps ({apps.length+searchableApps.length})</h2>
|
<h2>Activated apps ({apps.length+searchableApps.length})</h2>
|
||||||
</div>
|
</div>
|
||||||
{isCloud ? null :
|
{isCloud ? null :
|
||||||
<span>
|
<span>
|
||||||
@@ -959,9 +967,13 @@ const Apps = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<Paper square style={uploadViewPaperStyle}>
|
<Paper square style={uploadViewPaperStyle}>
|
||||||
<h4 style={{margin: 10, }}>
|
<Typography style={{margin: 10, }}>
|
||||||
Try a broader search term, e.g. http, alert, ticket etc.
|
<span>
|
||||||
</h4>
|
<Link to={"https://shuffler.io/search"} style={{textDecoration: "none", color: "#f85a3e"}}>
|
||||||
|
Click here
|
||||||
|
</Link> to search ALL apps, not just your activated ones.
|
||||||
|
</span>
|
||||||
|
</Typography>
|
||||||
<div/>
|
<div/>
|
||||||
|
|
||||||
{appSearchLoading ?
|
{appSearchLoading ?
|
||||||
|
|||||||
Reference in New Issue
Block a user