feat: click to deploy to AWS test

This commit is contained in:
Aditya
2024-11-20 17:42:37 +05:30
80 changed files with 5537 additions and 7738 deletions
-13
View File
@@ -1,13 +0,0 @@
on:
push:
branches:
- launch
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
with:
release-type: node
package-name: release-please-action
-51
View File
@@ -1,51 +0,0 @@
# This is a basic workflow to help you get started with Actions
name: App SDK upload
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ master, launch ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v0'
with:
credentials_json: '${{ secrets.SANDBOX_CREDENTIALS }}'
- id: 'upload_sdk'
name: Cloud Storage Uploader
uses: google-github-actions/upload-cloud-storage@v0.9.0
with:
path: 'backend/app_sdk/app_base.py'
destination: 'shuffle-sandbox-337810.appspot.com/generated_apps/baseline'
- id: 'upload_requirement'
name: Cloud Storage Uploader
uses: google-github-actions/upload-cloud-storage@v0.9.0
with:
path: 'backend/app_sdk/requirements.txt'
destination: 'shuffle-sandbox-337810.appspot.com/generated_apps/baseline'
- id: 'upload_Dockerfile'
name: Cloud Storage Uploader
uses: google-github-actions/upload-cloud-storage@v0.9.0
with:
path: 'backend/app_sdk/Dockerfile'
destination: 'shuffle-sandbox-337810.appspot.com/generated_apps/baseline'
+3
View File
@@ -4,10 +4,13 @@
Shuffle Automation Shuffle Automation
<<<<<<< HEAD
[![CodeQL](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml) [![CodeQL](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml)
[![Autobuild](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml) [![Autobuild](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml)
[![Deploy to AWS](https://d1.awsstatic.com/cloudformation-deploy-to-aws-button.png)](https://console.aws.amazon.com/cloudformation/home?#/stacks/new?stackName=Shuffle-Instance&templateURL=https://raw.githubusercontent.com/Shuffle/Shuffle/refs/heads/2.0.0/template.yaml) [![Deploy to AWS](https://d1.awsstatic.com/cloudformation-deploy-to-aws-button.png)](https://console.aws.amazon.com/cloudformation/home?#/stacks/new?stackName=Shuffle-Instance&templateURL=https://raw.githubusercontent.com/Shuffle/Shuffle/refs/heads/2.0.0/template.yaml)
=======
>>>>>>> 8883de54d10855428866f598b70e16674df6d7c1
</h1><h4 align="center"> </h1><h4 align="center">
[Shuffle](https://shuffler.io) is an automation platform for and by the community, focusing on accessibility for anyone to automate. Security operations is complex, but it doesn't have to be. [Shuffle](https://shuffler.io) is an automation platform for and by the community, focusing on accessibility for anyone to automate. Security operations is complex, but it doesn't have to be.
-9
View File
@@ -1,9 +0,0 @@
{
"name": "Shuffle",
"description": "Security Automation Platform",
"repository": "https://github.com/0x0elliot/Shuffle",
"ref": "2.0.0",
"scripts": {
"postclone": "chmod +x startup.sh && ./startup.sh"
}
}
-21
View File
@@ -1,21 +0,0 @@
#FROM python:3.9.1-alpine as base
FROM python:3.10.0-alpine as base
#FROM python:3.11.3-alpine as base
FROM base as builder
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev tzdata coreutils
RUN mkdir /install
WORKDIR /install
FROM base
#--no-cache
RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev coreutils
COPY --from=builder /install /usr/local
COPY requirements.txt /requirements.txt
RUN pip3 install -r /requirements.txt
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-42
View File
@@ -1,42 +0,0 @@
FROM python:3.10.0-alpine as base
FROM base as builder
RUN apk --no-cache add --update \
alpine-sdk \
build-base \
g++ \
gcc \
libffi \
libffi-dev \
libstdc++ \
linux-headers \
musl-dev \
openssl-dev \
tzdata \
coreutils
RUN pip install --upgrade pip && \
pip install --prefix="/install" --no-cache-dir grpcio grpcio-tools && \
apk del --purge \
g++ \
gcc \
musl-dev \
libffi-dev \
libstdc++ \
build-base \
linux-headers
RUN mkdir -p /install
WORKDIR /install
FROM base
#--no-cache
RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev coreutils
COPY --from=builder /install /usr/local
COPY requirements.txt /requirements.txt
RUN pip3 install -r /requirements.txt
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-19
View File
@@ -1,19 +0,0 @@
FROM blackarchlinux/blackarch as base
FROM base as builder
RUN /bin/pacman -Syu --noconfirm
RUN /bin/pacman -Sy --noconfirm base-devel libffi musl openssl python python-pip -y
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN pip install --prefix="/install" -r /requirements.txt
FROM base
COPY --from=builder /install /usr/local
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-19
View File
@@ -1,19 +0,0 @@
FROM kalilinux/kali-rolling as base
FROM base as builder
RUN apt-get update
RUN apt-get dist-upgrade -y
RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN pip install --prefix="/install" -r /requirements.txt
FROM base
COPY --from=builder /install /usr/local
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-22
View File
@@ -1,22 +0,0 @@
FROM ubuntu as base
FROM base as builder
RUN apt-get update
RUN apt-get dist-upgrade -y
RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN pip install --prefix="/install" -r /requirements.txt
FROM base
RUN apt-get update
RUN apt-get dist-upgrade -y
RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y
COPY --from=builder /install /usr/local
COPY __init__.py /app/walkoff_app_sdk/__init__.py
COPY app_base.py /app/walkoff_app_sdk/app_base.py
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Frikkylikeme
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Executable → Regular
+2 -22
View File
@@ -1,22 +1,2 @@
# app_sdk.py ## CHANGES
This is the SDK used for apps to behave like they should. In November 2024, we moved this to its own repistory: https://github.com/shuffle/app_sdk
## If you want to update apps.. PS: downloads from docker hub do overrides.. :)
1. Write your code & check if runtime works
2. Build app_base image
3. docker rm $(docker ps -aq) # Remove all stopped containers
4. Delete the specific app's Docker image (docker rmi frikky/shuffle:...)
5. Rebuild the Docker image (click load in GUI?)
## Cloud updates
1. Go to shuffle cloud on GCP
2. Go to Cloud Storage
3. Find shuffler.appspot.com
4. Navigate to generated_apps/baseline
5. Update SDK there. This will make all new apps run with the new SDK
## Cloud app force-updates
1. Run the "stitcher.go" program in the public shuffle-shared repository.
# LICENSE
Everything in here is MIT, not AGPLv3 as indicated by the license.
View File
File diff suppressed because it is too large Load Diff
-187
View File
@@ -1,187 +0,0 @@
import re
import json
input_data = """{
"test4": $test,
"test5": ,
"test6": "what"
}
"""
input_data = """{
"test0": {{ '' | default: [] }},
"test": {{ | default: [] }},
"test2": {{ $test.asd | default: [] }},
"test3": {{ {"key": "val} | default: [] }},
"test4": $test,
"test5": ,
"test6": "what"
}
"""
liquiddata = "{{ $test.asd | some other stuff {{ $test.xyz | more stuff"
pattern = r'\{\{\s*\$[^|}]+\s*\|'
replaced_data = re.sub(pattern, "{{ '' |", liquiddata)
print(replaced_data)
def patternfix_string(liquiddata, patterns, regex_patterns, inputtype="liquid"):
if not inputtype or inputtype == "liquid":
if "{{" not in liquiddata or "}}" not in liquiddata:
return liquiddata
elif inputtype == "json":
liquiddata = liquiddata.strip()
# Validating if it looks like json or not
if liquiddata[0] == "{" and liquiddata[len(liquiddata)-1] == "}":
pass
else:
if liquiddata[0] == "[" and liquiddata[len(liquiddata)-1] == "]":
pass
else:
return liquiddata
# If it's already json, don't touch it
try:
json.loads(liquiddata)
return liquiddata
except Exception as e:
pass
else:
print("No replace handler for %s" % inputtype)
return liquiddata
skipkeys = [" "]
newoutput = liquiddata[:]
for pattern in patterns:
keylocations = []
parsedvalue = ""
record = False
index = -1
for key in liquiddata:
# Return instant if possible
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
pass
index += 1
if not key:
if record:
keylocations.append(index)
parsedvalue += key
continue
if key in skipkeys:
if record:
keylocations.append(index)
parsedvalue += key
continue
if key == pattern[0] and not record:
record = True
if key not in pattern:
keylocations = []
parsedvalue = ""
record = False
if record:
keylocations.append(index)
parsedvalue += key
if len(parsedvalue) == 0:
continue
evaluated_value = parsedvalue[:]
for skipkey in skipkeys:
evaluated_value = "".join(evaluated_value.split(skipkey))
if evaluated_value == pattern:
#print("Found matching: %s (%s)" % (parsedvalue, keylocations))
#print("Should replace with: %s" % patterns[pattern])
newoutput = newoutput.replace(parsedvalue, patterns[pattern], -1)
# Return instant if possible
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
pass
for pattern in regex_patterns:
newlines = []
for line in newoutput.split("\n"):
replaced_line = re.sub(pattern, regex_patterns[pattern], line)
newlines.append(replaced_line)
newoutput = "\n".join(newlines)
# Return instant if possible
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
pass
# Dont return json properly unless actually json
if inputtype == "json":
try:
json.loads(newoutput)
return newoutput
except:
# Returns original if json fixing didn't work
return liquiddata
return newoutput
print("Start:\n%s" % input_data)
try:
newinput = patternfix_string(input_data,
{
"{{|": '{{ "" |',
},
{
#r'\{\{\s*|': "{{ '' |",
r'\{\{\s*\$[^|}]+\s*\|': '{{ "" |',
}
,
inputtype="liquid"
)
except Exception as e:
print("[ERROR} Failed liquid parsing fix: %s" % e)
newinput = input_data
try:
newinput = patternfix_string(newinput,
{
},
{
r'\"\s*\:\s*,': '\": "",',
r'\"\s*\:\s*\$[^,]+\w*\,': '\": "",',
}
,
inputtype="json"
)
try:
json.loads(newinput)
print("It's json! Override.")
except Exception as e:
print("Bad json. DONT use the value at all: %s" % e)
except Exception as e:
print("[ERROR} Failed json parsing fix: %s" % e)
print("\nEnd:\n%s" % newinput)
-51
View File
@@ -1,51 +0,0 @@
#!/bin/bash
### DEFAULT
NAME=shuffle-app_sdk
VERSION=1.2.0
docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force
docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION -t ghcr.io/frikky/$NAME:nightly -t shuffle/shuffle:app_sdk -t shuffle/$NAME:$VERSION -t docker.pkg.github.com/shuffle/shuffle/$NAME:$VERSION -t ghcr.io/shuffle/$NAME:$VERSION -t ghcr.io/shuffle/$NAME:nightly
docker push frikky/shuffle:app_sdk
docker push ghcr.io/frikky/$NAME:$VERSION
docker push ghcr.io/frikky/$NAME:nightly
docker push ghcr.io/frikky/$NAME:latest
docker push shuffle/shuffle:app_sdk
docker push ghcr.io/shuffle/$NAME:$VERSION
docker push ghcr.io/shuffle/$NAME:nightly
docker push ghcr.io/shuffle/$NAME:latest
#### UBUNTU
NAME=shuffle-app_sdk_ubuntu
docker build . -f Dockerfile_ubuntu -t frikky/shuffle:app_sdk_ubuntu -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
docker push frikky/shuffle:app_sdk_ubuntu
docker push ghcr.io/frikky/$NAME:$VERSION
#### Alpine GRPC
NAME=shuffle-app_sdk_grpc
docker build . -f Dockerfile_alpine_grpc -t frikky/shuffle:app_sdk_grpc -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
docker push frikky/shuffle:app_sdk_grpc
docker push ghcr.io/frikky/$NAME:$VERSION
#### KALI ###
#NAME=shuffle-app_sdk_kali
#docker build . -f Dockerfile_kali -t frikky/shuffle:app_sdk_kali -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
#
#docker push frikky/shuffle:app_sdk_kali
#docker push ghcr.io/frikky/$NAME:$VERSION
#docker push ghcr.io/frikky/$NAME:nightly
### BLACKARCH ###
#NAME=shuffle-app_sdk_blackarch
#docker build . -f Dockerfile_blackarch -t frikky/shuffle:app_sdk_blackarch -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
#
#docker push frikky/shuffle:app_sdk_blackarch
#docker push ghcr.io/frikky/$NAME:$VERSION
#docker push ghcr.io/frikky/$NAME:nightly
-326
View File
@@ -1,326 +0,0 @@
## A test script of the recurse_json function
## to validate that it can handle the different types of data
## and follow the dot formation format
import re
import json
def recurse_json(basejson, parsersplit):
match = "#([0-9a-z]+):?-?([0-9a-z]+)?#?"
try:
outercnt = 0
# Loops over split values
splitcnt = -1
for value in parsersplit:
splitcnt += 1
#if " " in value:
# value = value.replace(" ", "_", -1)
actualitem = re.findall(match, value, re.MULTILINE)
# Goes here if loop
if value == "#":
newvalue = []
if basejson == None:
return "", False
for innervalue in basejson:
# 1. Check the next item (message)
# 2. Call this function again
try:
ret, is_loop = recurse_json(innervalue, parsersplit[outercnt+1:])
except IndexError:
# Only in here if it's the last loop without anything in it?
ret, is_loop = recurse_json(innervalue, parsersplit[outercnt:])
newvalue.append(ret)
# Magical way of returning which makes app sdk identify
# it as multi execution
return newvalue, True
# Checks specific regex like #1-2 for index 1-2 in a loop
elif len(actualitem) > 0:
is_loop = True
newvalue = []
firstitem = actualitem[0][0]
seconditem = actualitem[0][1]
if isinstance(firstitem, int):
firstitem = str(firstitem)
if isinstance(seconditem, int):
seconditem = str(seconditem)
#print("[DEBUG] ACTUAL PARSED: %s" % actualitem)
# Means it's a single item -> continue
if seconditem == "":
#print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson)))
if str(firstitem).lower() == "max" or str(firstitem).lower() == "last" or str(firstitem).lower() == "end":
firstitem = len(basejson)-1
elif str(firstitem).lower() == "min" or str(firstitem).lower() == "first":
firstitem = 0
else:
firstitem = int(firstitem)
#print(f"[DEBUG] Post lower checks with item {firstitem}")
tmpitem = basejson[int(firstitem)]
try:
newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:])
except IndexError:
newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:])
else:
#print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem))
if isinstance(firstitem, str):
if firstitem.lower() == "max" or firstitem.lower() == "last" or firstitem.lower() == "end":
firstitem = len(basejson)-1
elif firstitem.lower() == "min" or firstitem.lower() == "first":
firstitem = 0
else:
firstitem = int(firstitem)
else:
firstitem = int(firstitem)
if isinstance(seconditem, str):
if str(seconditem).lower() == "max" or str(seconditem).lower() == "last" or str(firstitem).lower() == "end":
seconditem = len(basejson)-1
elif str(seconditem).lower() == "min" or str(seconditem).lower() == "first":
seconditem = 0
else:
seconditem = int(seconditem)
else:
seconditem = int(seconditem)
#print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}")
newvalue = []
if int(seconditem) > len(basejson):
seconditem = len(basejson)
for i in range(int(firstitem), int(seconditem)+1):
# 1. Check the next item (message)
# 2. Call this function again
try:
ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:])
except IndexError:
#print("[DEBUG] INDEXERROR (1): ", parsersplit[outercnt])
#ret = innervalue
ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt:])
newvalue.append(ret)
return newvalue, is_loop
else:
if len(value) == 0:
return basejson, False
try:
if isinstance(basejson, list):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
try:
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
# Should we sanitize here?
#print("[DEBUG] VALUE TO SANITIZE FOR KEY '%s'?: %s" % (value, basejson[value]))
# Check if we are on the last item?
if outercnt == len(parsersplit)-1:
#print("[DEBUG] LAST KEY")
return str(basejson[value]), False
else:
#print("[DEBUG] NOT LAST KEY")
pass
except json.decoder.JSONDecodeError as e:
return str(basejson[value]), False
else:
basejson = basejson[value]
except KeyError as e:
print("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e))
if "_" in value:
value = value.replace("_", " ", -1)
elif " " in value:
value = value.replace(" ", "_", -1)
try:
if isinstance(basejson, list):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value)
return basejson, False
elif isinstance(basejson, bool):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (bool): %s" % value)
return basejson, False
elif isinstance(basejson, int):
#print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (int): %s" % value)
return basejson, False
elif isinstance(basejson[value], str):
#print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value])
try:
#print("[DEBUG] BASEJSON: %s" % basejson)
if (basejson[value].endswith("}") and basejson[value].endswith("}")) or (basejson[value].startswith("[") and basejson[value].endswith("]")):
basejson = json.loads(basejson[value])
else:
if outercnt == len(parsersplit)-1:
#print("LAST KEY (2)")
return str(basejson[value]), False
else:
#print("NOT LAST KEY (2)")
pass
except json.decoder.JSONDecodeError as e:
#print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value])
return str(basejson[value]), False
else:
basejson = basejson[value]
except KeyError as e:
# Check if previous key was handled or not
previouskey = parsersplit[outercnt-1]
#print("[DEBUG] PREVIOUS KEY: ", previouskey)
tmpval = previouskey + "." + value
#print("\n\n[WARNING] Running third dot notation fix '%s' on data %s: %s" % (value, basejson, e))
if tmpval in basejson:
return basejson[tmpval], False
try:
currentsplitcnt = splitcnt
recursed_value = value
handled = False
#tmpbase = basejson
previouskey = value
while True:
#print("\n\n[DEBUG] CURRENTSPLITCNT: ", currentsplitcnt)
newvalue = parsersplit[currentsplitcnt+1]
if newvalue == "#" or newvalue == "":
break
recursed_value += "." + newvalue
#print("\n\nRECURSED: ", recursed_value)
found = False
for key, value in basejson.items():
if recursed_value.lower() in key.lower():
found = True
if found == False:
#print("[INFO] DIDN'T FIND similar VALUE: ", recursed_value)
# Check if we are on the last key or not
return "", False
#if outercnt == len(parsersplit)-1:
# print("[DEBUG] LAST KEY (3)")
# break
#else:
# print("[DEBUG] NOT LAST KEY (3)")
# return "", False
if recursed_value in basejson:
#print("[INFO] FOUND RECURSED VALUE: ", recursed_value)
basejson = basejson[recursed_value]
# Whether to dig deeper or not
if isinstance(basejson, bool) or isinstance(basejson, int) or isinstance(basejson, str):
handled = False
else:
handled = True
break
currentsplitcnt += 1
if handled:
continue
break
except IndexError as e:
print("[DEBUG] INDEXERROR (2):", parsersplit[outercnt])
return "", False
outercnt += 1
except KeyError as e:
print("[INFO] Lower keyerror: %s" % e)
return "", False
except Exception as e:
print("[WARNING] Exception: %s" % e)
return "", False
return basejson, False
print("[INFO] Starting")
#input_data = "test"
#input_data = "test2.data"
# Matchwith
basejson = {
"test": "hello",
"test2": {
"test3": "hello2",
"test3.data": "hello3",
"test4.data.testing": {
"value": "hello4"
},
"test5.data.hello": "wut",
},
"test3": ["hello", "hello2", "hello3"],
"test4": [{
"id": "1",
}]
}
# Inputexamples (ALL should be True)
inputs = {
#"": "",
"badkey": "",
"test": "hello",
"test2.badkey": "",
"test2.test3": "hello2",
"test2.test3.data": "hello3",
"test2.test4.data.testing": "{'value': 'hello4'}", # FIXME: Doesn't work due to break vs return "", False in last exception
"test2.test4.data.testing.value": "hello4", # FIXME: Doesn't work due to break vs return "", False in last exception. Not fixed as we didn't find one of these yet.
"test2.test5.data.hello": "wut",
"test2.test5.data.badkey": "",
"test3.#1": "hello2",
"test4.#0.id": "1",
"test4.#1.id": "",
}
outputs = []
for key, value in inputs.items():
parsersplit = key.split(".")
ret, is_loop = recurse_json(basejson, parsersplit)
print("\n\nOUTPUT RET (%s): %s" % (key, ret))
outputs.append("[%s]: %s = '%s' vs '%s'" % (str(ret) == str(value), key, ret, value))
print("\n\n%s" % "\n".join(outputs))
#input_data = ""
#input_data = "badkey"
#input_data = "test"
#input_data = "test2.data"
#input_data = "test2.test3.data"
#input_data = "test2.test4.data.testing.value.as"
#input_data = "test2.test5.data.hello"
-9
View File
@@ -1,9 +0,0 @@
urllib3==1.26.18
requests==2.31.0
MarkupSafe==2.0.1
liquidpy==0.8.1
flask[async]==2.0.2
waitress==2.1.0
#flask==1.1.2
python-dateutil==2.8.1
PyJWT==2.9.0
+52 -69
View File
@@ -35,8 +35,8 @@ import (
"github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
gitProxy "github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/storage/memory" "github.com/go-git/go-git/v5/storage/memory"
gitProxy "github.com/go-git/go-git/v5/plumbing/transport"
// Random // Random
xj "github.com/basgys/goxml2json" xj "github.com/basgys/goxml2json"
@@ -257,7 +257,6 @@ type Hook struct {
Environment string `json:"environment" datastore:"environment"` Environment string `json:"environment" datastore:"environment"`
} }
func GetUsersHandler(w http.ResponseWriter, r *http.Request) { func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{ data := map[string]interface{}{
"id": "12345", "id": "12345",
@@ -398,49 +397,49 @@ func checkUsername(Username string) error {
} }
func isGitNoProxy(rawURL string) bool { func isGitNoProxy(rawURL string) bool {
noProxy := os.Getenv("NO_PROXY") noProxy := os.Getenv("NO_PROXY")
if noProxy == "" { if noProxy == "" {
return false return false
} }
if noProxy == "*" {
return true
}
noProxyList := strings.Split(noProxy, ",") if noProxy == "*" {
parsedURL, err := url.Parse(rawURL) return true
if err != nil { }
return false
}
host := parsedURL.Hostname()
for _,value := range noProxyList { noProxyList := strings.Split(noProxy, ",")
value = strings.TrimSpace(value) parsedURL, err := url.Parse(rawURL)
if err != nil {
return false
}
host := parsedURL.Hostname()
if host == value { for _, value := range noProxyList {
return true value = strings.TrimSpace(value)
}
if strings.HasPrefix(value, "*.") && strings.HasSuffix(host, value[2:]){ if host == value {
return true return true
} }
} if strings.HasPrefix(value, "*.") && strings.HasSuffix(host, value[2:]) {
return false return true
}
}
return false
} }
func checkGitProxy(cloneOptions *git.CloneOptions) *git.CloneOptions { func checkGitProxy(cloneOptions *git.CloneOptions) *git.CloneOptions {
if os.Getenv("HTTP_PROXY") != "" && !isGitNoProxy(cloneOptions.URL){ if os.Getenv("HTTP_PROXY") != "" && !isGitNoProxy(cloneOptions.URL) {
cloneOptions.ProxyOptions = gitProxy.ProxyOptions{ cloneOptions.ProxyOptions = gitProxy.ProxyOptions{
URL: os.Getenv("HTTP_PROXY"), URL: os.Getenv("HTTP_PROXY"),
} }
} }
if os.Getenv("HTTPS_PROXY") != "" && !isGitNoProxy(cloneOptions.URL) { if os.Getenv("HTTPS_PROXY") != "" && !isGitNoProxy(cloneOptions.URL) {
cloneOptions.ProxyOptions = gitProxy.ProxyOptions{ cloneOptions.ProxyOptions = gitProxy.ProxyOptions{
URL: os.Getenv("HTTPS_PROXY"), URL: os.Getenv("HTTPS_PROXY"),
} }
} }
return cloneOptions return cloneOptions
} }
func createNewUser(username, password, role, apikey string, org shuffle.OrgMini) error { func createNewUser(username, password, role, apikey string, org shuffle.OrgMini) error {
@@ -559,7 +558,6 @@ func createNewUser(username, password, role, apikey string, org shuffle.OrgMini)
} }
} }
return nil return nil
} }
@@ -663,7 +661,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) {
Name: newOrg.Name, Name: newOrg.Name,
} }
user.ActiveOrg = currentOrg user.ActiveOrg = currentOrg
} }
} }
} }
@@ -932,7 +930,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
log.Printf("[DEBUG] Failed to get org during getinfo: %s", err) log.Printf("[DEBUG] Failed to get org during getinfo: %s", err)
} }
//if err == nil { //if err == nil {
if len(org.Id) > 0 { if len(org.Id) > 0 {
if userInfo.Role == "" { if userInfo.Role == "" {
@@ -1069,9 +1066,9 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
ChatDisabled: chatDisabled, ChatDisabled: chatDisabled,
Tutorials: tutorialsFinished, Tutorials: tutorialsFinished,
Interests: orgInterests, Interests: orgInterests,
Priorities: orgPriorities, Priorities: orgPriorities,
Licensed: licensed, Licensed: licensed,
} }
returnData, err := json.Marshal(returnValue) returnData, err := json.Marshal(returnValue)
@@ -1092,7 +1089,6 @@ type passwordReset struct {
Reference string `json:"reference"` Reference string `json:"reference"`
} }
func checkAdminLogin(resp http.ResponseWriter, request *http.Request) { func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request) cors := shuffle.HandleCors(resp, request)
if cors { if cors {
@@ -3338,7 +3334,6 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s
} }
} }
log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID) log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID)
if len(user.Id) > 0 { if len(user.Id) > 0 {
resp.WriteHeader(200) resp.WriteHeader(200)
@@ -3379,8 +3374,6 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
buildSwaggerApp(resp, body, user, false) buildSwaggerApp(resp, body, user, false)
} }
// Hotloads new apps from a folder // Hotloads new apps from a folder
func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error { func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error {
@@ -3727,11 +3720,10 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error {
return nil return nil
} }
func remoteOrgJobHandler(org shuffle.Org, interval int) error { func remoteOrgJobHandler(org shuffle.Org, interval int) error {
// Check if it's 1 in 10 (10% chance random) // Check if it's 1 in 10 (10% chance random)
backupJob := shuffle.BackupJob{} backupJob := shuffle.BackupJob{}
// Check if workflow backup is active // Check if workflow backup is active
// Check if app backup is active // Check if app backup is active
@@ -3777,7 +3769,6 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
backupJobData = []byte{} backupJobData = []byte{}
} }
syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl) syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl)
client := shuffle.GetExternalClient(syncUrl) client := shuffle.GetExternalClient(syncUrl)
req, err := http.NewRequest( req, err := http.NewRequest(
@@ -3983,7 +3974,7 @@ func runInitEs(ctx context.Context) {
} }
// FIXME: Add a randomized timer to avoid all schedules running at the same time // FIXME: Add a randomized timer to avoid all schedules running at the same time
// Many are at 5 minutes / 1 hour. The point is to spread these out // Many are at 5 minutes / 1 hour. The point is to spread these out
// a bit instead of all of them starting at the exact same time // a bit instead of all of them starting at the exact same time
//log.Printf("Schedule: %#v", schedule) //log.Printf("Schedule: %#v", schedule)
@@ -4275,7 +4266,7 @@ func runInitEs(ctx context.Context) {
} }
} }
cloneOptions = checkGitProxy(cloneOptions) cloneOptions = checkGitProxy(cloneOptions)
branch := os.Getenv("SHUFFLE_DOWNLOAD_AUTH_BRANCH") branch := os.Getenv("SHUFFLE_DOWNLOAD_AUTH_BRANCH")
if len(branch) > 0 && branch != "master" && branch != "main" { if len(branch) > 0 && branch != "master" && branch != "main" {
@@ -4322,7 +4313,7 @@ func runInitEs(ctx context.Context) {
URL: apis, URL: apis,
} }
cloneOptions = checkGitProxy(cloneOptions) cloneOptions = checkGitProxy(cloneOptions)
_, err = git.Clone(storer, fs, cloneOptions) _, err = git.Clone(storer, fs, cloneOptions)
if err != nil { if err != nil {
@@ -4340,17 +4331,16 @@ func runInitEs(ctx context.Context) {
log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps)) log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps))
} }
if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" { if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" {
healthcheckInterval := 30 healthcheckInterval := 30
log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval) log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval)
job := func() { job := func() {
// Prepare a fake http.responsewriter // Prepare a fake http.responsewriter
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
request := http.Request{} request := http.Request{}
// Add the "force=true" query to the fake request // Add the "force=true" query to the fake request
request.URL, err = url.Parse("/api/v1/health/stats?force=true") request.URL, err = url.Parse("/api/v1/health/stats?force=true")
if err != nil { if err != nil {
log.Printf("[ERROR] Failed to parse test url for healthstats: %s", err) log.Printf("[ERROR] Failed to parse test url for healthstats: %s", err)
} }
@@ -4369,7 +4359,6 @@ func runInitEs(ctx context.Context) {
log.Printf("[INFO] Finished INIT (ES)") log.Printf("[INFO] Finished INIT (ES)")
} }
func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) { func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) {
ctx := context.Background() ctx := context.Background()
org, err := shuffle.GetOrg(ctx, orgId) org, err := shuffle.GetOrg(ctx, orgId)
@@ -4948,8 +4937,6 @@ func makeWorkflowPublic(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
} }
func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) { func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request) cors := shuffle.HandleCors(resp, request)
if cors { if cors {
@@ -5008,8 +4995,6 @@ func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte("OK")) resp.Write([]byte("OK"))
} }
func initHandlers() { func initHandlers() {
var err error var err error
ctx := context.Background() ctx := context.Background()
@@ -5056,7 +5041,7 @@ func initHandlers() {
r.HandleFunc("/api/v1/users/register", handleRegister).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/register", handleRegister).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/{userId}/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/users/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS")
@@ -5192,7 +5177,7 @@ func initHandlers() {
r.HandleFunc("/api/v1/triggers/gmail/register", shuffle.HandleNewGmailRegister).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/register", shuffle.HandleNewGmailRegister).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/triggers/pipeline", shuffle.HandleNewPipelineRegister).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/triggers/pipeline", shuffle.HandleNewPipelineRegister).Methods("POST", "OPTIONS")
//r.HandleFunc("/api/v1/triggers/pipeline/save", shuffle.HandleSavePipelineInfo).Methods("PUT", "OPTIONS") //r.HandleFunc("/api/v1/triggers/pipeline/save", shuffle.HandleSavePipelineInfo).Methods("PUT", "OPTIONS")
r.HandleFunc("/api/v1/pipelines/{key}", handlePipelineCallback).Methods("POST", "GET", "PATCH", "PUT", "DELETE", "OPTIONS") r.HandleFunc("/api/v1/pipelines/{key}", handlePipelineCallback).Methods("POST", "GET", "PATCH", "PUT", "DELETE", "OPTIONS")
r.HandleFunc("/api/v1/triggers", shuffle.HandleGetTriggers).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers", shuffle.HandleGetTriggers).Methods("GET", "OPTIONS")
//r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS") //r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS")
@@ -5220,7 +5205,7 @@ func initHandlers() {
r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS")
r.HandleFunc("/api/v1/orgs/{orgId}/suborgs", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/suborgs", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS")
// This is a new API that validates if a key has been seen before. // This is a new API that validates if a key has been seen before.
// Not sure what the best course of action is for it. // Not sure what the best course of action is for it.
r.HandleFunc("/api/v1/environments/{key}/stop", shuffle.HandleStopExecutions).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/environments/{key}/stop", shuffle.HandleStopExecutions).Methods("GET", "POST", "OPTIONS")
@@ -5244,7 +5229,6 @@ func initHandlers() {
r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/orgs/{orgId}/datastore/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/datastore/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
// Docker orborus specific - downloads an image // Docker orborus specific - downloads an image
r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS")
@@ -5266,15 +5250,14 @@ func initHandlers() {
// This structure is horrendous. Needs fixing after we got the prototype up // This structure is horrendous. Needs fixing after we got the prototype up
r.HandleFunc("/api/v1/detections/{detectionType}/connect", shuffle.HandleDetectionAutoConnect).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/detections/{detectionType}/connect", shuffle.HandleDetectionAutoConnect).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/detections/{detection_type}", shuffle.HandleGetDetectionRules).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/detections/{detection_type}", shuffle.HandleGetDetectionRules).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET","OPTIONS") r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST","OPTIONS") r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/detections/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS") r.HandleFunc("/api/v1/detections/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS")
// This is weird. // This is weird.
r.HandleFunc("/api/v1/detections/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS") r.HandleFunc("/api/v1/detections/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS")
//r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS") //r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS")
// Introduced in 0.9.21 to handle notifications for e.g. failed Workflow // Introduced in 0.9.21 to handle notifications for e.g. failed Workflow
r.HandleFunc("/api/v1/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS")
+2 -1
View File
@@ -21,6 +21,7 @@
"@uiw/codemirror-themes": "^4.21.9", "@uiw/codemirror-themes": "^4.21.9",
"@uiw/react-codemirror": "^4.21.21", "@uiw/react-codemirror": "^4.21.21",
"algoliasearch": "^4.8.3", "algoliasearch": "^4.8.3",
"chart.js": "^3.0.0",
"class-transformer": "^0.2.0", "class-transformer": "^0.2.0",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"cpx": "^1.5.0", "cpx": "^1.5.0",
@@ -60,7 +61,7 @@
"react-alice-carousel": "^2.6.4", "react-alice-carousel": "^2.6.4",
"react-avatar-editor": "^11.1.0", "react-avatar-editor": "^11.1.0",
"react-beforeunload": "^2.2.1", "react-beforeunload": "^2.2.1",
"react-chartjs-2": "^5.2.0", "react-chartjs-2": "^5.0.0",
"react-cookie": "^4.0.1", "react-cookie": "^4.0.1",
"react-cytoscapejs": "^2.0.0", "react-cytoscapejs": "^2.0.0",
"react-device-detect": "^2.2.3", "react-device-detect": "^2.2.3",
+19 -1
View File
@@ -14,6 +14,7 @@ import HealthPage from "./components/HealthPage.jsx";
//import Header from "./components/Header.jsx"; //import Header from "./components/Header.jsx";
import theme from "./theme"; import theme from "./theme";
import Apps from "./views/Apps"; import Apps from "./views/Apps";
import Apps2 from "./views/Apps2.jsx";
import AppCreator from "./views/AppCreator"; import AppCreator from "./views/AppCreator";
import DetectionDashBoard from "./views/DetectionDashboard.jsx"; import DetectionDashBoard from "./views/DetectionDashboard.jsx";
@@ -48,7 +49,7 @@ import 'react-toastify/dist/ReactToastify.css';
import Drift from "react-driftjs"; import Drift from "react-driftjs";
import { AppContext } from './context/contextApi.jsx'; import { AppContext } from './context/ContextApi.jsx';
// Production - backend proxy forwarding in nginx // Production - backend proxy forwarding in nginx
var globalUrl = window.location.origin; var globalUrl = window.location.origin;
@@ -408,6 +409,23 @@ const App = (message, props) => {
{...props} {...props}
/> />
} }
/>
<Route
exact
path="/apps2"
element={
<Apps2
serverside={false}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
checkLogin={checkLogin}
userdata={userdata}
globalUrl={globalUrl}
surfaceColor={theme.palette.surfaceColor}
inputColor={theme.palette.inputColor}
{...props}
/>
}
/> />
<Route <Route
exact exact
+3 -3
View File
@@ -1972,14 +1972,14 @@ const AppFramework = (props) => {
{data.name} {data.name}
</Typography> </Typography>
<div style={{display: "flex", width: 200, margin: "auto", marginTop: 15, }}> <div style={{display: "flex", width: 200, margin: "auto", marginTop: 15, }}>
<div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginRight: 15, position: "relative",}}> <div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginRight: 15, position: "relative",}}>
{parsedLeftImage} {parsedLeftImage}
{parsedLeftText} {parsedLeftText}
</div> </div>
<div style={{backgroundColor: theme.palette.inputColor, maxHeight: 30, maxWidth: 30, height: 30, width: 30, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginTop: 22, padding: "10px 0px 0px 9px",}}> <div style={{backgroundColor: theme.palette.inputColor, maxHeight: 30, maxWidth: 30, height: 30, width: 30, borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginTop: 22, padding: "10px 0px 0px 9px",}}>
{svgIcon} {svgIcon}
</div> </div>
<div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginLeft: 15, position: "relative",}}> <div style={{backgroundColor: theme.palette.inputColor, height: 75, width: 75, borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.7)", marginLeft: 15, position: "relative",}}>
{parsedRightImage} {parsedRightImage}
{parsedRightText} {parsedRightText}
</div> </div>
+10 -1
View File
@@ -464,8 +464,17 @@ const AppGrid = (props) => {
}} }}
> >
<img <img
id={`image_${index}`}
alt={data.name} alt={data.name}
src={data.image_url ? data.image_url : "/images/no_image.png"} src={data.image_url ? data.image_url : "/images/no_image.png"}
onError={(e) => {
// Replace the image with the default image
const foundImage = document.getElementById(`image_${index}`)
if (foundImage !== undefined && foundImage !== null) {
foundImage.src = theme.palette.defaultImage
data.image_url = theme.palette.defaultImage
}
}}
style={{ style={{
width: 80, width: 80,
height: 80, height: 80,
@@ -995,7 +1004,7 @@ const AppGrid = (props) => {
}} }}
autoComplete="off" autoComplete="off"
color="primary" color="primary"
placeholder="Search your Activated or Self-built apps" placeholder="Search your Activated or self-built apps"
id="shuffle_search_field" id="shuffle_search_field"
onChange={(event) => { onChange={(event) => {
setSearchQuery(event.currentTarget.value); setSearchQuery(event.currentTarget.value);
+19 -13
View File
@@ -47,6 +47,7 @@ const AppSelection = props => {
defaultSearch, defaultSearch,
setDefaultSearch, setDefaultSearch,
checkLogin, checkLogin,
isAppPage=false
} = props; } = props;
const [discoveryData, setDiscoveryData] = React.useState({}) const [discoveryData, setDiscoveryData] = React.useState({})
@@ -507,26 +508,31 @@ const AppSelection = props => {
})} })}
</Grid> </Grid>
</div> </div>
{!moreButton ? ( {
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}> !isAppPage && (
<Link style={{ color: "#FF8444" }} onClick={() => { <>
{!moreButton ? (
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}>
<Link style={{ color: "#FF8444" }} onClick={() => {
setMoreButton(true) setMoreButton(true)
setTimeout(() => { setTimeout(() => {
navigate("/welcome?tab=2") navigate("/welcome?tab=2")
}, 250) }, 250)
}} }}
>See More Apps</Link> >See More Apps</Link>
</div>) : ""} </div>) : ""}
<div style={{ flexDirection: "row", width: isMobile ? 340 : null, textAlign: isMobile ? "center" : null }}> <div style={{ flexDirection: "row", width: isMobile ? 340 : null, textAlign: isMobile ? "center" : null }}>
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => { <Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => {
navigate("/usecases2") navigate("/usecases2")
setActiveStep(2) setActiveStep(2)
}}> }}>
See usecases See usecases
</Button> </Button>
</div> </div>
</>
)
}
</div> </div>
</Fade> </Fade>
) )
-3
View File
@@ -27,9 +27,6 @@ const Appsearch = props => {
const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true"); const isCloud = (window.location.host === "localhost:3002" || window.location.host === "shuffler.io") ? true : (process.env.IS_SSR === "true");
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs
//const theme = useTheme();
//const [apps, setApps] = React.useState([]);
//const [filteredApps, setFilteredApps] = React.useState([]);
const [formMail, setFormMail] = React.useState(""); const [formMail, setFormMail] = React.useState("");
const [message, setMessage] = React.useState(""); const [message, setMessage] = React.useState("");
const [formMessage, setFormMessage] = React.useState(""); const [formMessage, setFormMessage] = React.useState("");
@@ -45,21 +45,21 @@ const AuthenticationItem = (props) => {
data.fields = [ data.fields = [
{ {
key: "url", key: "url",
value: "Secret. Replaced during app execution!", value: "URL Secret. Replaced during runtime",
}, },
{ {
key: "client_id", key: "client_id",
value: "Secret. Replaced during app execution!", value: "ClientID Secret. Replaced during runtime.",
}, },
{ {
key: "client_secret", key: "client_secret",
value: "Secret. Replaced during app execution!", value: "Client Secret. Replaced during runtime.",
}, },
{ {
key: "scope", key: "scope",
value: "Secret. Replaced during app execution!", value: "Scope Secret. Replaced during runtime.",
}, },
]; ]
} }
const deleteAuthentication = (data) => { const deleteAuthentication = (data) => {
@@ -152,7 +152,7 @@ const AuthenticationItem = (props) => {
src={data.app.large_image} src={data.app.large_image}
style={{ style={{
maxWidth: 50, maxWidth: 50,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
/> />
style={{ minWidth: 75, maxWidth: 75 }} style={{ minWidth: 75, maxWidth: 75 }}
@@ -222,7 +222,7 @@ const AuthenticationData = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -304,7 +304,7 @@ const AuthenticationData = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -294,7 +294,7 @@ const AuthenticationData = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -328,10 +328,10 @@ const AuthenticationData = (props) => {
const authenticationButtons = <span> const authenticationButtons = <span>
<Button <Button
style={{ borderRadius: theme.palette.borderRadius, marginTop: authFieldsOnly ? 20 : 0 }} style={{ borderRadius: theme.palette?.borderRadius, marginTop: authFieldsOnly ? 20 : 0 }}
onClick={() => { onClick={() => {
setAuthenticationOptions(authenticationOption); setAuthenticationOptions(authenticationOption)
handleSubmitCheck(); handleSubmitCheck()
}} }}
variant={"contained"} variant={"contained"}
disabled={submitSuccessful} disabled={submitSuccessful}
@@ -437,7 +437,7 @@ const AuthenticationData = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
+5 -5
View File
@@ -179,7 +179,7 @@ const Billing = (props) => {
height: 480, height: 480,
// width: "100%", // width: "100%",
backgroundColor: theme.palette.platformColor, backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius * 2, borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10, marginRight: 10,
marginTop: 15, marginTop: 15,
@@ -583,7 +583,7 @@ const Billing = (props) => {
margin: "auto", margin: "auto",
width: 100, width: 100,
backgroundColor: "white", backgroundColor: "white",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
/> />
: null} : null}
@@ -653,7 +653,7 @@ const Billing = (props) => {
value={feature.split("Worker License: ")[1]} value={feature.split("Worker License: ")[1]}
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
id={fieldId} id={fieldId}
onClick={() => { }} onClick={() => { }}
@@ -1041,7 +1041,7 @@ const Billing = (props) => {
width: 340, width: 340,
height: 480, height: 480,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor, backgroundColor: hovered ? "#232427" : theme.palette.platformColor,
borderRadius: theme.palette.borderRadius * 2, borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10, marginRight: 10,
marginTop: 15, marginTop: 15,
@@ -1315,7 +1315,7 @@ const Billing = (props) => {
// maxWidth: 400, // maxWidth: 400,
width: 340, width: 340,
backgroundColor: hovered ? "#232427" : theme.palette.platformColor, backgroundColor: hovered ? "#232427" : theme.palette.platformColor,
borderRadius: theme.palette.borderRadius * 2, borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10, marginRight: 10,
marginTop: 15, marginTop: 15,
+1 -1
View File
@@ -43,7 +43,7 @@ const LineChartWrapper = ({keys, inputname, height, width}) => {
const inputdata = keys.data === undefined ? keys : keys.data const inputdata = keys.data === undefined ? keys : keys.data
return ( return (
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}> <div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, padding: 30, marginTop: 15, backgroundColor: theme.palette.platformColor, overflow: "hidden", }}>
<Typography variant="h6" style={{marginBotton: 15, }}> <Typography variant="h6" style={{marginBotton: 15, }}>
{inputname} {inputname}
</Typography> </Typography>
+11 -12
View File
@@ -147,12 +147,12 @@ const ConfigureWorkflow = (props) => {
} }
if (apps === undefined || apps === null) { if (apps === undefined || apps === null) {
console.log("Apps is undefined or null: ", apps) //console.log("Apps is undefined or null: ", apps)
return null return null
} }
if (appAuthentication === undefined || appAuthentication === null) { if (appAuthentication === undefined || appAuthentication === null) {
console.log("App authentication is undefined or null: ", appAuthentication) //console.log("App authentication is undefined or null: ", appAuthentication)
return null return null
} }
@@ -427,7 +427,6 @@ const ConfigureWorkflow = (props) => {
trigger.index = key; trigger.index = key;
if (trigger.trigger_type === "WEBHOOK") { if (trigger.trigger_type === "WEBHOOK") {
console.log("Found webhook: ", trigger)
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") { if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
const findapp = trigger.app_association.name.toLowerCase() const findapp = trigger.app_association.name.toLowerCase()
@@ -599,7 +598,7 @@ const ConfigureWorkflow = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
endAdornment: <InputAdornment position="end"></InputAdornment>, endAdornment: <InputAdornment position="end"></InputAdornment>,
@@ -800,7 +799,7 @@ const ConfigureWorkflow = (props) => {
> >
<div <div
style={{ style={{
border: filled ? `1px solid ${theme.palette.green}` : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, width: "100%", padding: 12, cursor: "pointer", border: filled ? `1px solid ${theme.palette.green}` : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: "100%", padding: 12, cursor: "pointer",
}} }}
id="app-config" id="app-config"
> >
@@ -997,7 +996,7 @@ const ConfigureWorkflow = (props) => {
justifyContent: "flex-start", justifyContent: "flex-start",
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor, backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
color: action.auth_done ? "#686a6c" : "#ffffff", color: action.auth_done ? "#686a6c" : "#ffffff",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
minWidth: 350, minWidth: 350,
maxHeight: 50, maxHeight: 50,
overflow: "hidden", overflow: "hidden",
@@ -1037,7 +1036,7 @@ const ConfigureWorkflow = (props) => {
> >
<img <img
alt={action.app_name} alt={action.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }} style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={action.large_image} src={action.large_image}
/> />
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1"> <Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
@@ -1057,7 +1056,7 @@ const ConfigureWorkflow = (props) => {
justifyContent: "flex-start", justifyContent: "flex-start",
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor, backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
color: action.auth_done ? "#686a6c" : "#ffffff", color: action.auth_done ? "#686a6c" : "#ffffff",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
minWidth: 350, minWidth: 350,
maxHeight: 50, maxHeight: 50,
overflow: "hidden", overflow: "hidden",
@@ -1088,7 +1087,7 @@ const ConfigureWorkflow = (props) => {
> >
<img <img
alt={action.app_name} alt={action.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }} style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={action.large_image} src={action.large_image}
/> />
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1"> <Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
@@ -1108,7 +1107,7 @@ const ConfigureWorkflow = (props) => {
justifyContent: "flex-start", justifyContent: "flex-start",
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor, backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
color: action.auth_done ? "#686a6c" : "#ffffff", color: action.auth_done ? "#686a6c" : "#ffffff",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
minWidth: 350, minWidth: 350,
maxHeight: 50, maxHeight: 50,
overflow: "hidden", overflow: "hidden",
@@ -1123,7 +1122,7 @@ const ConfigureWorkflow = (props) => {
> >
<img <img
alt={action.app_name} alt={action.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }} style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={action.large_image} src={action.large_image}
/> />
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1"> <Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
@@ -1275,7 +1274,7 @@ const ConfigureWorkflow = (props) => {
const [finishCount, setFinishCount] = useState(0) const [finishCount, setFinishCount] = useState(0)
return ( return (
<div style={{backgroundColor: hovered ? theme.palette.inputColor : "inherit", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, cursor: "pointer", }} <div style={{backgroundColor: hovered ? theme.palette.inputColor : "inherit", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, cursor: "pointer", }}
> >
<div style={{display: "flex", marginLeft: 15, marginTop: 15, marginBottom: 15, }} <div style={{display: "flex", marginLeft: 15, marginTop: 15, marginBottom: 15, }}
onClick={() => { onClick={() => {
+1 -1
View File
@@ -100,7 +100,7 @@ const Detection = (props) => {
width: "100%", width: "100%",
padding: 50, padding: 50,
backgroundColor: theme.palette.backgroundColor, backgroundColor: theme.palette.backgroundColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
<Box <Box
+60 -4
View File
@@ -10,10 +10,12 @@ import {
Paper, Paper,
Divider, Divider,
IconButton, IconButton,
Tooltip,
} from "@mui/material"; } from "@mui/material";
import { import {
OpenInNew as OpenInNewIcon, OpenInNew as OpenInNewIcon,
FmdGood as FmdGoodIcon,
} from "@mui/icons-material" } from "@mui/icons-material"
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -68,6 +70,7 @@ const DetectionExplorer = (props) => {
const [detectionWorkflowId, setDetectionWorkflowId] = useState("") const [detectionWorkflowId, setDetectionWorkflowId] = useState("")
const [isDetectionValid, setIsDetectionValid] = useState(false) const [isDetectionValid, setIsDetectionValid] = useState(false)
const [availableDetection, setAvailableDetection] = React.useState([]); const [availableDetection, setAvailableDetection] = React.useState([]);
const [environmentList, setEnvironmentList] = React.useState([])
const loadUsecases = () => { const loadUsecases = () => {
const url = `${globalUrl}/api/v1/workflows/usecases` const url = `${globalUrl}/api/v1/workflows/usecases`
@@ -146,7 +149,7 @@ const DetectionExplorer = (props) => {
} }
setLoading(true); setLoading(true);
const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect`; const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect`
fetch(url, { fetch(url, {
method: "GET", method: "GET",
@@ -177,7 +180,11 @@ const DetectionExplorer = (props) => {
if (responseJson.reason !== undefined && responseJson.reason !== null) { if (responseJson.reason !== undefined && responseJson.reason !== null) {
toast(responseJson.reason) toast(responseJson.reason)
} else { } else {
toast(`Failed to connect to ${detectionInfo?.category}`); if (responseJson.workflow_id === "" && responseJson.workflow_valid === false) {
toast.info(`Sent job to generate a Detection Workflow and enable ${detectionInfo?.category}. Please wait a minute and reload this UI.`);
} else {
toast.error(`Failed to connect to ${detectionInfo?.category}`);
}
} }
if (responseJson.action !== undefined && responseJson.actio !== null && responseJson.action.length > 0) { if (responseJson.action !== undefined && responseJson.actio !== null && responseJson.action.length > 0) {
@@ -193,16 +200,52 @@ const DetectionExplorer = (props) => {
.catch((error) => { .catch((error) => {
setLoading(false); setLoading(false);
console.log(`Error in connecting to ${detectionInfo?.category}: `, error); console.log(`Error in connecting to ${detectionInfo?.category}: `, error);
toast(`An error occurred while connecting to ${detectionInfo?.category}`); toast.error(`An error occurred while connecting to ${detectionInfo?.category}`);
}); });
} }
const loadEnvironments = () => {
const url = `${globalUrl}/api/v1/getenvironments`
fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
return response.json()
})
.then((responseJson) => {
if (responseJson.success === false) {
return
}
if (responseJson.length == 0) {
return
}
setEnvironmentList(responseJson)
})
.catch((error) => {
console.log(`Error in loading environments: `, error);
})
}
useEffect(() => { useEffect(() => {
loadUsecases() loadUsecases()
loadEnvironments()
}, []) }, [])
useEffect(() => { useEffect(() => {
if (detectionInfo === undefined || detectionInfo === null) {
return
}
if (detectionInfo.category === undefined || detectionInfo.category === null || detectionInfo.category === "") {
return
}
handleConnectClick() handleConnectClick()
}, [detectionInfo]) }, [detectionInfo])
@@ -211,6 +254,8 @@ const DetectionExplorer = (props) => {
rule.description.toLowerCase().includes(searchQuery.toLowerCase()) rule.description.toLowerCase().includes(searchQuery.toLowerCase())
) )
const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.data_lake?.enabled === true).length : 0
return ( return (
<Container> <Container>
<Paper <Paper
@@ -219,7 +264,7 @@ const DetectionExplorer = (props) => {
width: "100%", width: "100%",
padding: 50, padding: 50,
backgroundColor: theme.palette.backgroundColor, backgroundColor: theme.palette.backgroundColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
<Box <Box
@@ -234,6 +279,7 @@ const DetectionExplorer = (props) => {
{detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rules)`} {detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rules)`}
</Typography> </Typography>
<div style={{display: "flex", }}>
{workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ? {workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
<div style={{display: "flex", }}> <div style={{display: "flex", }}>
<div style={{minWidth: 400, maxWidth: 400, }}> <div style={{minWidth: 400, maxWidth: 400, }}>
@@ -282,6 +328,16 @@ const DetectionExplorer = (props) => {
isDetectionValid ? `Connected to ${detectionInfo?.category}` : `Fix ${detectionInfo?.category} connection`} isDetectionValid ? `Connected to ${detectionInfo?.category}` : `Fix ${detectionInfo?.category} connection`}
</Button> </Button>
} }
{detectionInfo?.category === "SIGMA" || detectionInfo?.category === "SIEM" ?
<Tooltip title={`You have ${lakeNodes} available Data Lake node(s)`}>
<a href="/admin?tab=environments" style={{textDecoration: "none", color: "inherit", }} target="_blank" rel="noreferrer">
<FmdGoodIcon style={{marginLeft: 15, marginTop: 5, color: lakeNodes > 0 ? green : red}} />
</a>
</Tooltip>
: null}
</div>
</Box> </Box>
{filteredRules?.length > 0 ? {filteredRules?.length > 0 ?
<Box <Box
@@ -98,7 +98,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
return ( return (
<Card style={{ <Card style={{
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
minHeight: 100, minHeight: 100,
marginBottom: 10, marginBottom: 10,
paddingBottom: 0, paddingBottom: 0,
@@ -145,7 +145,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
color: "white", color: "white",
height: 40, height: 40,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
<MenuItem <MenuItem
@@ -197,7 +197,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
overflow: 'visible', overflow: 'visible',
zIndex: 10, zIndex: 10,
//border: "1px solid rgba(255,255,255,0.3)", //border: "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginTop: 5, marginTop: 5,
minHeight: 40, minHeight: 40,
+41 -7
View File
@@ -43,6 +43,7 @@ import {
RadioGroup, RadioGroup,
FormControl, FormControl,
FormLabel, FormLabel,
Slider,
} from "@mui/material"; } from "@mui/material";
@@ -65,7 +66,7 @@ import {
} from "@mui/icons-material"; } from "@mui/icons-material";
const EditWorkflow = (props) => { const EditWorkflow = (props) => {
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, scrollTo, setRealtimeMarkdown, } = props const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, saveWorkflow, expanded, scrollTo, setRealtimeMarkdown, boxWidth, setBoxWidth, } = props
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
@@ -82,12 +83,19 @@ const EditWorkflow = (props) => {
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day')) const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : []) const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
const [inputMarkdown, setInputMarkdown] = React.useState(workflow.input_markdown !== undefined && workflow.input_markdown !== null ? workflow.input_markdown : "") const [inputMarkdown, setInputMarkdown] = React.useState(workflow?.form_control?.input_markdown !== undefined && workflow?.form_control?.input_markdown !== null ? workflow?.form_control?.input_markdown : "")
const [scrollDone, setScrollDone] = React.useState(false) const [scrollDone, setScrollDone] = React.useState(false)
const [selectedYieldActions, setSelectedYieldActions] = React.useState(workflow.output_yields !== undefined && workflow.output_yields !== null ? JSON.parse(JSON.stringify(workflow.output_yields)) : []) const [selectedYieldActions, setSelectedYieldActions] = React.useState(workflow?.form_control?.output_yields !== undefined && workflow?.form_control?.output_yields !== null ? JSON.parse(JSON.stringify(workflow?.form_control?.output_yields)) : [])
const [formWidth, setFormWidth] = React.useState(boxWidth === undefined || boxWidth === null ? 500 : boxWidth)
const classes = useStyles(); const classes = useStyles();
useEffect(() => {
if (setBoxWidth !== undefined && boxWidth !== formWidth) {
setBoxWidth(formWidth)
}
}, [formWidth])
if (scrollTo !== undefined && scrollTo !== null && scrollTo.length > 0 && scrollDone === false) { if (scrollTo !== undefined && scrollTo !== null && scrollTo.length > 0 && scrollDone === false) {
setTimeout(() => { setTimeout(() => {
const foundScroll = document.getElementById(scrollTo) const foundScroll = document.getElementById(scrollTo)
@@ -308,9 +316,14 @@ const EditWorkflow = (props) => {
} }
innerWorkflow.input_questions = validfields innerWorkflow.input_questions = validfields
innerWorkflow.input_markdown = inputMarkdown
innerWorkflow.output_yields = selectedYieldActions if (innerWorkflow.form_control === undefined || innerWorkflow.form_control === null) {
innerWorkflow.form_control = {}
}
innerWorkflow.form_control.input_markdown = inputMarkdown
innerWorkflow.form_control.output_yields = selectedYieldActions
innerWorkflow.form_control.form_width = formWidth
innerWorkflow.name = name innerWorkflow.name = name
innerWorkflow.description = description innerWorkflow.description = description
@@ -1049,13 +1062,34 @@ const EditWorkflow = (props) => {
} }
setInputMarkdown(e.target.value) setInputMarkdown(e.target.value)
workflow.input_markdown = e.target.value workflow.form_control.input_markdown = e.target.value
setWorkflow(workflow) setWorkflow(workflow)
setUpdate(Math.random()) setUpdate(Math.random())
}} }}
/> />
</div> </div>
<div id="form_size">
<Typography variant="h6" style={{marginTop: 50, }}>
Form Size
</Typography>
<Typography variant="body2" color="textSecondary" style={{marginBottom: 20, }}>
Control the width of the form. It will grow vertically as needed.
</Typography>
<Slider
defaultValue={formWidth}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={10}
marks
min={300}
max={1000}
onChange={(e, value) => {
setFormWidth(value)
}}
/>
</div>
<div id="output_control"> <div id="output_control">
<Typography variant="h6" style={{marginTop: 50, }}> <Typography variant="h6" style={{marginTop: 50, }}>
Output Control ({selectedYieldActions.length === 0 ? "No Returns" : selectedYieldActions.length === 1 ? "Returning 1 node" : `Returning ${selectedYieldActions.length} nodes`}) Output Control ({selectedYieldActions.length === 0 ? "No Returns" : selectedYieldActions.length === 1 ? "Returning 1 node" : `Returning ${selectedYieldActions.length} nodes`})
@@ -1117,7 +1151,7 @@ const EditWorkflow = (props) => {
<Tooltip color="primary" title={"Add more details"} placement="top"> <Tooltip color="primary" title={"Add more details"} placement="top">
<Button <Button
style={{ margin: "auto", marginTop: 50, textAlign: "center", textTransform: "none", }} style={{ margin: "auto", marginTop: 50, marginBottom: 100, textAlign: "center", textTransform: "none", }}
variant="outlined" variant="outlined"
disabled={newWorkflow === true} disabled={newWorkflow === true}
color="secondary" color="secondary"
+2 -2
View File
@@ -227,7 +227,7 @@ const ExploreWorkflow = (props) => {
<ArrowBackIosNewIcon /> <ArrowBackIosNewIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}> <div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette?.borderRadius, }}>
<AliceCarousel <AliceCarousel
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }} style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
items={formattedCarousel} items={formattedCarousel}
@@ -320,7 +320,7 @@ const ExploreWorkflow = (props) => {
<div style={{ marginTop: 0, }}> <div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}> <div className="thumbs" style={{ display: "flex" }}>
<div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette.borderRadius, }}> <div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette?.borderRadius, }}>
<Grid item xs={11} style={{}}> <Grid item xs={11} style={{}}>
{suggestedUsecases.length === 0 && usecasesSet ? {suggestedUsecases.length === 0 && usecasesSet ?
<Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)"> <Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)">
@@ -325,7 +325,7 @@ const FixWorkflowValidationErrors = (props) => {
width: 25, width: 25,
height: 25, height: 25,
marginRight: 10, marginRight: 10,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
border: `1px solid ${theme.palette.borderColor}`, border: `1px solid ${theme.palette.borderColor}`,
}} }}
/> />
@@ -467,7 +467,7 @@ const FixWorkflowValidationErrors = (props) => {
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
color: "white", color: "white",
height: 40, height: 40,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
<MenuItem <MenuItem
@@ -559,7 +559,7 @@ const FixWorkflowValidationErrors = (props) => {
justifyContent: !validating ? "flex-start" : "center", justifyContent: !validating ? "flex-start" : "center",
textTransform: "none", textTransform: "none",
fontSize: 18, fontSize: 18,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onClick={() => { onClick={() => {
toast("Validating app") toast("Validating app")
+3 -5
View File
@@ -50,8 +50,6 @@ const hoverOutColor = "#e8eaf6"
const Header = props => { const Header = props => {
const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props; const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props;
//const theme = useTheme();
//const alert = useAlert()
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
@@ -829,7 +827,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
}, },
}} }}
style={{ style={{
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
marginRight: 15, marginRight: 15,
color: "white", color: "white",
@@ -904,7 +902,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
<Tooltip color="primary" title={parsedTitle} placement="left"> <Tooltip color="primary" title={parsedTitle} placement="left">
<div style={{display: "flex"}}> <div style={{display: "flex"}}>
{isCloud?<Typography variant="body2" style={{borderRadius: theme.palette.borderRadius, float: "left", margin: "0 0 0 0", marginRight: 25, }}>{regiontag}</Typography>:null} {image} <span style={{marginLeft: 8}}>{data.name}</span> {isCloud?<Typography variant="body2" style={{borderRadius: theme.palette?.borderRadius, float: "left", margin: "0 0 0 0", marginRight: 25, }}>{regiontag}</Typography>:null} {image} <span style={{marginLeft: 8}}>{data.name}</span>
</div> </div>
</Tooltip> </Tooltip>
</MenuItem> </MenuItem>
@@ -935,7 +933,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
null null
: :
<Tooltip title={`Amount of executions left: ${userdata.app_execution_usage} / ${userdata.app_execution_limit}. When the limit is reached, you can still use Shuffle normally, but your Workflow triggers may stop working. Reach out to support@shuffler.io to extend this limit.`}> <Tooltip title={`Amount of executions left: ${userdata.app_execution_usage} / ${userdata.app_execution_limit}. When the limit is reached, you can still use Shuffle normally, but your Workflow triggers may stop working. Reach out to support@shuffler.io to extend this limit.`}>
<div style={{maxHeight: 30, minHeight: 30, padding: 8, textAlign: "center", cursor: "pointer", borderRadius: theme.palette.borderRadius, marginRight: 10, marginTop: 12, backgroundColor: theme.palette.surfaceColor, minWidth: 60, maxWidth: 60, border: userdata.app_execution_usage/userdata.app_execution_limit >= 0.9 ? "#f86a3e" : null, }} onClick={() => { <div style={{maxHeight: 30, minHeight: 30, padding: 8, textAlign: "center", cursor: "pointer", borderRadius: theme.palette?.borderRadius, marginRight: 10, marginTop: 12, backgroundColor: theme.palette.surfaceColor, minWidth: 60, maxWidth: 60, border: userdata.app_execution_usage/userdata.app_execution_limit >= 0.9 ? "#f86a3e" : null, }} onClick={() => {
console.log(userdata.appe_execution_usage/userdata.app_execution_limit) console.log(userdata.appe_execution_usage/userdata.app_execution_limit)
if (window.drift !== undefined) { if (window.drift !== undefined) {
window.drift.api.startInteraction({ interactionId: 326905 }) window.drift.api.startInteraction({ interactionId: 326905 })
@@ -1,5 +1,8 @@
import React from 'react'; import React from 'react';
import { Bar } from 'react-chartjs-2'; import { Bar } from 'react-chartjs-2';
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
const HealthBarChart = (props) => { const HealthBarChart = (props) => {
const { globalUrl, filteredData, options, onBarClick } = props; const { globalUrl, filteredData, options, onBarClick } = props;
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -87,7 +87,7 @@ const LicencePopup = (props) => {
const paperStyle = { const paperStyle = {
padding: 20, padding: 20,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
height: "100%", height: "100%",
} }
@@ -193,7 +193,7 @@ const LicencePopup = (props) => {
return ( return (
<Tooltip <Tooltip
style={{ borderRadius: theme.palette.borderRadius, }} style={{ borderRadius: theme.palette?.borderRadius, }}
placement="bottom" placement="bottom"
> >
<div style={{}}> <div style={{}}>
@@ -319,7 +319,7 @@ const LicencePopup = (props) => {
margin: "auto", margin: "auto",
width: 100, width: 100,
backgroundColor: "white", backgroundColor: "white",
// borderRadius: theme.palette.borderRadius, // borderRadius: theme.palette?.borderRadius,
}} }}
/> />
: null} : null}
@@ -389,7 +389,7 @@ const LicencePopup = (props) => {
value={feature.split("Worker License: ")[1]} value={feature.split("Worker License: ")[1]}
style={{ style={{
// backgroundColor: theme.palette.inputColor, // backgroundColor: theme.palette.inputColor,
// borderRadius: theme.palette.borderRadius, // borderRadius: theme.palette?.borderRadius,
}} }}
id={fieldId} id={fieldId}
onClick={() => { }} onClick={() => { }}
@@ -823,7 +823,7 @@ const LicencePopup = (props) => {
{errorMessage.length > 0 ? <Typography variant="h4">Error: {errorMessage}</Typography> : null} {errorMessage.length > 0 ? <Typography variant="h4">Error: {errorMessage}</Typography> : null}
<Card style={{ <Card style={{
padding: 20, padding: 20,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
border: "1px solid #f85a3e", border: "1px solid #f85a3e",
}}> }}>
<div> <div>
+4 -6
View File
@@ -65,7 +65,6 @@ const useStyles = makeStyles((theme) => ({
}, },
}, },
dropdownMenu: { dropdownMenu: {
marginTop: theme.spacing(1),
borderRadius: "12px !important", borderRadius: "12px !important",
zIndex: 10, zIndex: 10,
"& .MuiPaper-root": { "& .MuiPaper-root": {
@@ -88,7 +87,6 @@ const useStyles = makeStyles((theme) => ({
}, },
}, },
dropdownMenuItem: { dropdownMenuItem: {
padding: theme.spacing(2, 3),
fontSize: "16px", fontSize: "16px",
fontWeight: 400, fontWeight: 400,
color: "#fff", color: "#fff",
@@ -503,7 +501,7 @@ const Header = (props) => {
minHeight: 370, minHeight: 370,
padding: 20, padding: 20,
backgroundColor: "rgba(0, 0, 0, 1)", backgroundColor: "rgba(0, 0, 0, 1)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}, },
}} }}
> >
@@ -928,7 +926,7 @@ const Header = (props) => {
}, },
}} }}
style={{ style={{
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
marginRight: 15, marginRight: 15,
color: "white", color: "white",
@@ -1056,7 +1054,7 @@ const Header = (props) => {
<Typography <Typography
variant="body2" variant="body2"
style={{ style={{
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
float: "left", float: "left",
margin: "0 0 0 0", margin: "0 0 0 0",
marginRight: 25, marginRight: 25,
@@ -1165,7 +1163,7 @@ const Header = (props) => {
padding: 8, padding: 8,
textAlign: "center", textAlign: "center",
cursor: "pointer", cursor: "pointer",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginTop: 5, marginTop: 5,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
minWidth: 60, minWidth: 60,
+1 -2
View File
@@ -1,7 +1,7 @@
import React, {useState} from 'react'; import React, {useState} from 'react';
import { useTheme } from '@mui/styles';
import {isMobile} from "react-device-detect"; import {isMobile} from "react-device-detect";
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import theme from '../theme.jsx';
import { import {
TextField, TextField,
@@ -12,7 +12,6 @@ import {
const Newsletter = (props) => { const Newsletter = (props) => {
const { globalUrl, } = props; const { globalUrl, } = props;
const theme = useTheme();
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [msg, setMsg] = useState(""); const [msg, setMsg] = useState("");
const [buttonActive, setButtonActive] = useState(true); const [buttonActive, setButtonActive] = useState(true);
+18 -16
View File
@@ -96,7 +96,7 @@ const AuthenticationOauth2 = (props) => {
autoAuth, autoAuth,
authButtonOnly, authButtonOnly,
isLoggedIn, isLoggedIn,
org_id,
setFinalized, setFinalized,
} = props; } = props;
@@ -148,7 +148,6 @@ const AuthenticationOauth2 = (props) => {
navigate(`/login?view=${window.location.pathname}&message=Log in to authenticate this app`) navigate(`/login?view=${window.location.pathname}&message=Log in to authenticate this app`)
} }
console.log("Should automatically click the auto-auth button?: ", autoAuth)
if (autoAuth === true && selectedApp !== undefined) { if (autoAuth === true && selectedApp !== undefined) {
startOauth2Request() startOauth2Request()
} }
@@ -452,6 +451,8 @@ const AuthenticationOauth2 = (props) => {
if (orgId !== undefined && orgId !== null && orgId.length > 0) { if (orgId !== undefined && orgId !== null && orgId.length > 0) {
console.log("Adding org_id from user side") console.log("Adding org_id from user side")
state += `%26org_id%3d${orgId}`; state += `%26org_id%3d${orgId}`;
}else{
state += `%26org_id%3d${org_id}`
} }
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) { if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
@@ -466,7 +467,7 @@ const AuthenticationOauth2 = (props) => {
state += `%26refresh_uri%3d${authentication_url}` state += `%26refresh_uri%3d${authentication_url}`
} }
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) { if (workflow?.org_id !== undefined && workflow?.org_id !== null && workflow?.org_id.length > 0) {
state += `%26org_id%3d${workflow.org_id}` state += `%26org_id%3d${workflow.org_id}`
} }
@@ -696,7 +697,7 @@ const AuthenticationOauth2 = (props) => {
justifyContent: "flex-start", justifyContent: "flex-start",
backgroundColor: "#ffffff", backgroundColor: "#ffffff",
color: "#2f2f2f", color: "#2f2f2f",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
minWidth: 300, minWidth: 300,
maxWidth: 300, maxWidth: 300,
maxHeight: 50, maxHeight: 50,
@@ -721,7 +722,7 @@ const AuthenticationOauth2 = (props) => {
<span style={{display: "flex"}}> <span style={{display: "flex"}}>
<img <img
alt={selectedAction.app_name} alt={selectedAction.app_name}
style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette.borderRadius, }} style={{ margin: 4, minHeight: 30, maxHeight: 30, borderRadius: theme.palette?.borderRadius, }}
src={selectedAction.large_image} src={selectedAction.large_image}
/> />
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 5,}} variant="body1"> <Typography style={{ margin: 0, marginLeft: 10, marginTop: 5,}} variant="body1">
@@ -803,7 +804,7 @@ const AuthenticationOauth2 = (props) => {
</span> </span>
: null} : null}
{/*<TextField {/*<TextField
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}} style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius,}}
InputProps={{ InputProps={{
style:{ style:{
}, },
@@ -835,13 +836,14 @@ const AuthenticationOauth2 = (props) => {
setOauthUrl(data.value); setOauthUrl(data.value);
} }
const defaultValue = data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) && authenticationType.type === "oauth2-app" ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value const isNormalOauth = authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null && authenticationType.redirect_uri.length > 0
const defaultValue = !isNormalOauth && data.name === "url" && authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0 && (authenticationType.authorizationUrl === undefined || authenticationType.authorizationUrl === null || authenticationType.authorizationUrl.length === 0) && authenticationType.type === "oauth2-app" ? authenticationType.token_uri : data.value === undefined || data.value === null ? "" : data.value
const fieldname = data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name const fieldname = !isNormalOauth && data.name === "url" && authenticationType.grant_type !== undefined && authenticationType.grant_type !== null && authenticationType.grant_type.length > 0 && authenticationType.type === "oauth2-app" ? "Token URL" : data.name
return ( return (
<div key={index} style={{ marginTop: authenticationType.type === "oauth2-app" ? 10 : 0, }}> <div key={index} style={{ marginTop: !isNormalOauth && authenticationType.type === "oauth2-app" ? 10 : 0, }}>
<LockOpenIcon style={{ marginRight: 10 }} /> <LockOpenIcon style={{ marginRight: 10 }} />
<b>{fieldname}</b> <b>{fieldname}</b>
@@ -893,7 +895,7 @@ const AuthenticationOauth2 = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -924,7 +926,7 @@ const AuthenticationOauth2 = (props) => {
style={{ style={{
marginTop: 20, marginTop: 20,
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -942,7 +944,7 @@ const AuthenticationOauth2 = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginBottom: 10, marginBottom: 10,
}} }}
InputProps={{ InputProps={{
@@ -964,7 +966,7 @@ const AuthenticationOauth2 = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -982,7 +984,7 @@ const AuthenticationOauth2 = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginBottom: 10, marginBottom: 10,
}} }}
InputProps={{ InputProps={{
@@ -1061,7 +1063,7 @@ const AuthenticationOauth2 = (props) => {
style={{ style={{
marginBottom: 40, marginBottom: 40,
marginTop: 20, marginTop: 20,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
disabled={ disabled={
clientSecret.length === 0 || clientId.length === 0 || buttonClicked || (allscopes.length !== 0 && selectedScopes.length === 0) clientSecret.length === 0 || clientId.length === 0 || buttonClicked || (allscopes.length !== 0 && selectedScopes.length === 0)
@@ -1092,7 +1094,7 @@ const AuthenticationOauth2 = (props) => {
<Button <Button
style={{ style={{
marginLeft: 10, marginLeft: 10,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
disabled={clientSecret.length === 0 || clientId.length === 0} disabled={clientSecret.length === 0 || clientId.length === 0}
variant="text" variant="text"
@@ -438,7 +438,7 @@ const OrgHeaderexpanded = (props) => {
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50, height: 50,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
console.log("Found value: ", newValue) console.log("Found value: ", newValue)
@@ -470,7 +470,7 @@ const OrgHeaderexpanded = (props) => {
<Tooltip arrow placement="left" title={ <Tooltip arrow placement="left" title={
<span style={{}}> <span style={{}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} /> <img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
: null} : null}
<Typography> <Typography>
Choose {data.name} Choose {data.name}
@@ -498,7 +498,7 @@ const OrgHeaderexpanded = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
{...params} {...params}
label="Find a notification workflow" label="Find a notification workflow"
+4 -2
View File
@@ -2,7 +2,7 @@ import React, {useState, useEffect, useLayoutEffect} from 'react';
import Draggable from "react-draggable"; import Draggable from "react-draggable";
import { import {
Paper Paper
} from "@mui/material"; } from "@mui/material";
const PaperComponent = (props) => { const PaperComponent = (props) => {
@@ -11,7 +11,9 @@ const PaperComponent = (props) => {
handle="#draggable-dialog-title" handle="#draggable-dialog-title"
cancel={'[class*="MuiDialogContent-root"]'} cancel={'[class*="MuiDialogContent-root"]'}
> >
<Paper {...props} /> <Paper
{...props}
/>
</Draggable> </Draggable>
) )
} }
+95 -47
View File
@@ -192,13 +192,13 @@ const ParsedAction = (props) => {
const [selectedActionParameters, setSelectedActionParameters] = React.useState(selectedAction?.parameters || []); const [selectedActionParameters, setSelectedActionParameters] = React.useState(selectedAction?.parameters || []);
const [selectedVariableParameter, setSelectedVariableParameter] = React.useState(""); const [selectedVariableParameter, setSelectedVariableParameter] = React.useState("");
const [paramUpdate, setParamUpdate] = React.useState(""); const [paramUpdate, setParamUpdate] = React.useState("");
const [actionlist, setActionlist] = React.useState([]); const [actionlist, setActionlist] = React.useState([]);
const [jsonList, setJsonList] = React.useState([]); const [jsonList, setJsonList] = React.useState([]);
const [showDropdown, setShowDropdown] = React.useState(false); const [showDropdown, setShowDropdown] = React.useState(false);
const [showDropdownNumber, setShowDropdownNumber] = React.useState(0); const [showDropdownNumber, setShowDropdownNumber] = React.useState(0);
const [showAutocomplete, setShowAutocomplete] = React.useState(false); const [showAutocomplete, setShowAutocomplete] = React.useState(false);
const [menuPosition, setMenuPosition] = useState(null); const [menuPosition, setMenuPosition] = useState(null);
const [uiBox, setUiBox] = useState(null); const [uiBox, setUiBox] = useState(null);
const isIntegration = selectedAction.app_id === "integration" const isIntegration = selectedAction.app_id === "integration"
useEffect(() => { useEffect(() => {
@@ -207,6 +207,53 @@ const ParsedAction = (props) => {
} }
}, [expansionModalOpen]) }, [expansionModalOpen])
useEffect(() => {
// Changes the order of params to show in order:
// auth, required, optional
var changed = false
if (selectedActionParameters === undefined || selectedActionParameters === null || selectedActionParameters.length === 0) {
return
}
var auth = []
var required = []
var optional = []
var keyorder = []
for (let paramkey in selectedActionParameters) {
const param = selectedActionParameters[paramkey]
keyorder.push(param.name)
if (param.configuration) {
auth.push(param)
continue
}
if (param.required) {
required.push(param)
continue
}
optional.push(param)
}
// Check new keyorder
const newparams = auth.concat(required).concat(optional)
var newkeyorder = []
for (let paramkey in newparams) {
newkeyorder.push(newparams[paramkey].name)
}
if (keyorder.join(",") !== newkeyorder.join(",")) {
//toast("Changed order of params")
setSelectedActionParameters(newparams)
selectedAction.parameters = newparams
setSelectedAction(selectedAction)
}
}, [selectedActionParameters])
useEffect(() => { useEffect(() => {
if (selectedActionEnvironment === undefined || selectedActionEnvironment === null || Object.keys(selectedActionEnvironment).length === 0) { if (selectedActionEnvironment === undefined || selectedActionEnvironment === null || Object.keys(selectedActionEnvironment).length === 0) {
@@ -582,8 +629,9 @@ const ParsedAction = (props) => {
} }
let newParameters = selectedAction?.parameters?.map((param) => { let newParameters = selectedAction?.parameters?.map((param) => {
let paramvalue = param.value; let paramvalue = param.value === undefined || param.value === null ? "" : param.value;
let errorVars = []; let errorVars = [];
if(paramvalue.includes("$")){ if(paramvalue.includes("$")){
let actions = workflow.actions?.map((action) => { let actions = workflow.actions?.map((action) => {
return "$"+action.label?.toLowerCase(); return "$"+action.label?.toLowerCase();
@@ -1161,7 +1209,7 @@ const ParsedAction = (props) => {
var helperText = "" var helperText = ""
if (name.includes("url")) { if (name.includes("url")) {
if (value.includes("localhost") || value.includes("127.0.0.1")) { if (value.includes("localhost") || value.includes("127.0.0.1")) {
helperText = "Can't use localhost. Please change to your external IP." helperText = "Can't use localhost in Shuffle. Please change to server's IP."
} }
} }
@@ -1431,7 +1479,7 @@ const ParsedAction = (props) => {
) )
if (foundResult === undefined || foundResult === null) { if (foundResult === undefined || foundResult === null) {
continue; continue
} }
const oldstartnode = cy.getElementById(selectedAction.id); const oldstartnode = cy.getElementById(selectedAction.id);
@@ -1444,12 +1492,12 @@ const ParsedAction = (props) => {
setSelectedResult(foundResult); setSelectedResult(foundResult);
if (setCodeModalOpen !== undefined) { if (setCodeModalOpen !== undefined) {
setCodeModalOpen(true); setCodeModalOpen(true)
found = true found = true
} }
break; break
} }
if (!found) { if (!found) {
@@ -1497,13 +1545,13 @@ const ParsedAction = (props) => {
}} }}
disabled={autoCompleting} disabled={autoCompleting}
onClick={() => { onClick={() => {
//if (setAiQueryModalOpen !== undefined) { if (setAiQueryModalOpen !== undefined) {
// setAiQueryModalOpen(true) setAiQueryModalOpen(true)
//} else { } else {
aiSubmit("Fill based on previous values", undefined, undefined, selectedAction) aiSubmit("Fill based on previous values", undefined, undefined, selectedAction)
//} }
setAutocompleting(true)
setAutocompleting(true)
setTimeout(() => { setTimeout(() => {
setAutocompleting(false) setAutocompleting(false)
}, 3000) }, 3000)
@@ -1511,7 +1559,7 @@ const ParsedAction = (props) => {
> >
<Tooltip <Tooltip
color="primary" color="primary"
title={"Autocomplete fields. Uses the name of the current action, the fields and previous actions' results"} title={"Autocomplete the action"}
placement="top" placement="top"
> >
{autoCompleting ? {autoCompleting ?
@@ -1568,7 +1616,7 @@ const ParsedAction = (props) => {
color: "white", color: "white",
height: 35, height: 35,
marginleft: 10, marginleft: 10,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
SelectDisplayProps={{ SelectDisplayProps={{
style: { style: {
@@ -1855,7 +1903,9 @@ const ParsedAction = (props) => {
<span> <span>
<Button <Button
color="primary" color="primary"
style={{}} style={{
textTransform: "none",
}}
fullWidth fullWidth
variant="contained" variant="contained"
onClick={() => { onClick={() => {
@@ -1963,7 +2013,7 @@ const ParsedAction = (props) => {
color: "white", color: "white",
height: 50, height: 50,
maxWidth: rightsidebarStyle.maxWidth - 80, maxWidth: rightsidebarStyle.maxWidth - 80,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
<MenuItem <MenuItem
@@ -2066,7 +2116,7 @@ const ParsedAction = (props) => {
: null} : null}
{showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? ( {/*showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Environment</Typography> <Typography style={{color: "rgba(255,255,255,0.7)"}}>Environment</Typography>
<Select <Select
@@ -2097,7 +2147,7 @@ const ParsedAction = (props) => {
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
color: "white", color: "white",
height: "50px", height: "50px",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
{environments.map((data, index) => { {environments.map((data, index) => {
@@ -2153,15 +2203,9 @@ const ParsedAction = (props) => {
})} })}
</Select> </Select>
{/*selectedActionEnvironment.running_ip === "" && selectedActionEnvironment.Name !== "Cloud" && selectedActionEnvironment.Name !== "cloud" ? </div>
<a href={`/admin?tab=environment&env=${selectedActionEnvironment.Name}`} target="_blank" style={{textDecoration: "none", color: "#f85a3e",}}> ) : null*/}
<Typography style={{}}>
Configure the environment
</Typography>
</a>
: null*/}
</div>
) : null}
{workflow.execution_variables !== undefined && {workflow.execution_variables !== undefined &&
workflow.execution_variables !== null && workflow.execution_variables !== null &&
workflow.execution_variables.length > 0 ? ( workflow.execution_variables.length > 0 ? (
@@ -2201,7 +2245,7 @@ const ParsedAction = (props) => {
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
color: "white", color: "white",
height: "50px", height: "50px",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
<MenuItem <MenuItem
@@ -2294,7 +2338,7 @@ const ParsedAction = (props) => {
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50, height: 50,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
// Workaround with event lol // Workaround with event lol
@@ -2476,7 +2520,7 @@ const ParsedAction = (props) => {
variant="body1" variant="body1"
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
label={isIntegration ? "Choose a category" : "Find Actions"} label={isIntegration ? "Choose a category" : "Find Actions"}
variant="outlined" variant="outlined"
@@ -2496,7 +2540,7 @@ const ParsedAction = (props) => {
value={selectedAction.name} value={selectedAction.name}
fullWidth fullWidth
onChange={setNewSelectedAction} onChange={setNewSelectedAction}
style={{backgroundColor: theme.palette.inputColor, color: "white", height: 50, borderRadius: theme.palette.borderRadius,}} style={{backgroundColor: theme.palette.inputColor, color: "white", height: 50, borderRadius: theme.palette?.borderRadius,}}
SelectDisplayProps={{ SelectDisplayProps={{
style: { style: {
marginLeft: 10, marginLeft: 10,
@@ -2724,7 +2768,7 @@ const ParsedAction = (props) => {
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50, height: 50,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
console.log("SELECT: ", event, newValue) console.log("SELECT: ", event, newValue)
@@ -2786,7 +2830,7 @@ const ParsedAction = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
{...params} {...params}
label="Find App to Translate" label="Find App to Translate"
@@ -2801,7 +2845,7 @@ const ParsedAction = (props) => {
<div <div
style={{ style={{
border: "1px solid rgba(255,255,255,0.6)", border: "1px solid rgba(255,255,255,0.6)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginTop: 15, marginTop: 15,
marginBottom: 10, marginBottom: 10,
maxHeight: 70, maxHeight: 70,
@@ -2838,7 +2882,7 @@ const ParsedAction = (props) => {
if (selectedAction.parameters === undefined || selectedAction.parameters === null || selectedAction.parameters.length !== selectedActionParameters.length) { if (selectedAction.parameters === undefined || selectedAction.parameters === null || selectedAction.parameters.length !== selectedActionParameters.length) {
//selectedAction.parameters = selectedActionParameters //selectedAction.parameters = selectedActionParameters
console.log("PARAM BUG: ", selectedAction) //console.log("PARAM BUG - length change(?): ", selectedAction)
} }
//!selectedAction.auth_not_required && //!selectedAction.auth_not_required &&
@@ -3008,7 +3052,7 @@ const ParsedAction = (props) => {
marginTop: 50, marginTop: 50,
border: "1px solid rgba(255,255,255,0.7)", border: "1px solid rgba(255,255,255,0.7)",
borderTop: "1px solid rgba(255,255,255,0.7)", borderTop: "1px solid rgba(255,255,255,0.7)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
alignItems: "center", alignItems: "center",
textAlign: "center", textAlign: "center",
}} }}
@@ -3284,11 +3328,13 @@ const ParsedAction = (props) => {
}} }}
> >
<TextField <TextField
autofill="off"
autoComplete="off"
id={clickedFieldId} id={clickedFieldId}
disabled={disabled} disabled={disabled}
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
border: border:
selectedActionParameters[count].required || selectedActionParameters[count].required ||
selectedActionParameters[count].configuration selectedActionParameters[count].configuration
@@ -3323,6 +3369,8 @@ const ParsedAction = (props) => {
"field_number": count, "field_number": count,
"actionlist": actionlist, "actionlist": actionlist,
"field_id": clickedFieldId, "field_id": clickedFieldId,
"example": selectedActionParameters[count].example,
}) })
}} }}
/> />
@@ -3646,7 +3694,7 @@ const ParsedAction = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
endAdornment: hideExtraTypes ? null : ( endAdornment: hideExtraTypes ? null : (
@@ -3730,7 +3778,7 @@ const ParsedAction = (props) => {
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
color: "white", color: "white",
height: "50px", height: "50px",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
{parsedoptions.map( {parsedoptions.map(
@@ -4307,7 +4355,7 @@ const ParsedAction = (props) => {
color: "white", color: "white",
height: 50, height: 50,
marginTop: 2, marginTop: 2,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(e) => { onChange={(e) => {
console.log("SELECT ONCHANGE DONE") console.log("SELECT ONCHANGE DONE")
+1 -1
View File
@@ -286,7 +286,7 @@ const Priorities = (props) => {
borderBottom: "1px solid rgba(255,255,255,0.4)", borderBottom: "1px solid rgba(255,255,255,0.4)",
marginBottom: 20, marginBottom: 20,
border: highlighted ? "2px solid #f85a3e" : null, border: highlighted ? "2px solid #f85a3e" : null,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
<div style={{display: "flex", }}> <div style={{display: "flex", }}>
+3 -3
View File
@@ -121,7 +121,7 @@ const Priority = (props) => {
const srcSize = realignedSrc ? 35 : 30 const srcSize = realignedSrc ? 35 : 30
const dstSize = realignedDst ? 35 : 30 const dstSize = realignedDst ? 35 : 30
return ( return (
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : clickedFromOrgTab ?null:"1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: clickedFromOrgTab ? 24:15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: clickedFromOrgTab ? "#1A1A1A": theme.palette.surfaceColor, display: "flex", }}> <div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : clickedFromOrgTab ?null:"1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, marginTop: 10, marginBottom: 10, padding: clickedFromOrgTab ? 24:15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: clickedFromOrgTab ? "#1A1A1A": theme.palette.surfaceColor, display: "flex", }}>
<div style={{flex: 2, overflow: "hidden",}}> <div style={{flex: 2, overflow: "hidden",}}>
<span style={{display: "flex", }}> <span style={{display: "flex", }}>
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null} {priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null}
@@ -131,7 +131,7 @@ const Priority = (props) => {
</span> </span>
{priority.type === "usecase" && priority.description.includes("&") ? {priority.type === "usecase" && priority.description.includes("&") ?
<span style={{display: "flex", marginTop: 10, }}> <span style={{display: "flex", marginTop: 10, }}>
<img src={newdescription.split("&")[1]} alt={priority.name} style={{height: srcSize, width: srcSize, marginRight: realignedSrc ? isCloud ? 0 : -10 : 10, borderRadius: theme.palette.borderRadius-3, marginTop: realignedSrc ? 5 : 0 }} /> <img src={newdescription.split("&")[1]} alt={priority.name} style={{height: srcSize, width: srcSize, marginRight: realignedSrc ? isCloud ? 0 : -10 : 10, borderRadius: theme.palette?.borderRadius-3, marginTop: realignedSrc ? 5 : 0 }} />
<Typography variant="body2" color="textSecondary" style={{marginTop: 3, }}> <Typography variant="body2" color="textSecondary" style={{marginTop: 3, }}>
{newdescription.split("&")[0]} {newdescription.split("&")[0]}
</Typography> </Typography>
@@ -139,7 +139,7 @@ const Priority = (props) => {
{newdescription.split("&").length > 3 ? {newdescription.split("&").length > 3 ?
<span style={{display: "flex", }}> <span style={{display: "flex", }}>
<ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, }}/> <ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, }}/>
<img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: dstSize, width: dstSize, marginRight: realignedDst ? -5 : 10, borderRadius: theme.palette.borderRadius-3, marginTop: realignedDst ? 5 : 0 }} /> <img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: dstSize, width: dstSize, marginRight: realignedDst ? -5 : 10, borderRadius: theme.palette?.borderRadius-3, marginTop: realignedDst ? 5 : 0 }} />
<Typography variant="body2" color="textSecondary" style={{marginTop: 3}}> <Typography variant="body2" color="textSecondary" style={{marginTop: 3}}>
{newdescription.split("&")[2]} {newdescription.split("&")[2]}
</Typography> </Typography>
+10 -12
View File
@@ -1,5 +1,6 @@
import React from "react" import React from "react"
import { Link } from "react-router-dom";
import { import {
Avatar, Avatar,
Box, Box,
@@ -20,7 +21,6 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
const navigate = useNavigate(); const navigate = useNavigate();
const [hovered, setHovered] = React.useState(false) const [hovered, setHovered] = React.useState(false)
if (workflow === undefined || workflow === null) { if (workflow === undefined || workflow === null) {
console.log("No workflow") console.log("No workflow")
return null return null
@@ -39,12 +39,11 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
// Check if workflow.input_markdown has an image in it // Check if workflow.input_markdown has an image in it
// If it does, show it as the main thing // If it does, show it as the main thing
//
var relevantImageUrl = "" var relevantImageUrl = ""
if (workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown !== "") { if (workflow?.form_control?.input_markdown !== undefined && workflow?.form_control?.input_markdown !== null && workflow?.form_control?.input_markdown !== "") {
// Look for <img> tag or ![alt](src) markdown // Look for <img> tag or ![alt](src) markdown
// html > markdown // html > markdown
const imgTag = workflow.input_markdown.match(/<img[^>]+>/g) const imgTag = workflow?.form_control?.input_markdown.match(/<img[^>]+>/g)
if (imgTag !== null) { if (imgTag !== null) {
const src = imgTag[0].match(/src="([^"]+)"/) const src = imgTag[0].match(/src="([^"]+)"/)
@@ -52,7 +51,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
relevantImageUrl = src[1] relevantImageUrl = src[1]
} }
} else { } else {
const markdownTag = workflow.input_markdown.match(/!\[.*\]\(.*\)/g) const markdownTag = workflow?.form_control?.input_markdown.match(/!\[.*\]\(.*\)/g)
if (markdownTag !== null) { if (markdownTag !== null) {
const src = markdownTag[0].match(/\(([^)]+)\)/) const src = markdownTag[0].match(/\(([^)]+)\)/)
@@ -68,15 +67,13 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
onMouseEnter={() => setHovered(true)} onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)} onMouseLeave={() => setHovered(false)}
> >
<Link to={`/workflows/` + workflow?.id} style={{textDecoration: "none"}}>
<Button <Button
onClick={() => { onClick={(e) => {
if (onclickHandler !== undefined) { if (onclickHandler !== undefined) {
e.preventDefault()
e.stopPropagation()
onclickHandler() onclickHandler()
} else {
navigate(`/workflows/` + workflow?.id)
setTimeout(() => {
window.location.reload()
}, 100)
} }
}} }}
style={{ style={{
@@ -89,7 +86,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
opacity: expandLeftNav ? 1 : 0, opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.1s", transition: "opacity 0.1s",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
backgroundColor: hovered || currentWorkflowId === workflow.id ? "#1f1f1f" : "transparent", backgroundColor: hovered || currentWorkflowId === workflow.id ? "#1f1f1f" : "transparent",
}} }}
disableRipple disableRipple
@@ -150,6 +147,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
} }
</Box> </Box>
</Button> </Button>
</Link>
</div> </div>
) )
} }
+31 -17
View File
@@ -35,6 +35,7 @@ import {
PlayArrow as PlayArrowIcon, PlayArrow as PlayArrowIcon,
Insights as InsightsIcon, Insights as InsightsIcon,
Replay as ReplayIcon, Replay as ReplayIcon,
EditNote as EditNoteIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid' import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
@@ -76,6 +77,10 @@ const RuntimeDebugger = (props) => {
{"id": "", "name": "All Workflows",} {"id": "", "name": "All Workflows",}
]) ])
if (document != undefined) {
document.title = "Workflow Run Debugger"
}
// Shitty workflow search on purpose :) // Shitty workflow search on purpose :)
const handleWorkflowUsageCount = (workflows) => { const handleWorkflowUsageCount = (workflows) => {
if (workflows === undefined || workflows === null || workflows.length === 0) { if (workflows === undefined || workflows === null || workflows.length === 0) {
@@ -219,7 +224,7 @@ const RuntimeDebugger = (props) => {
} }
const getAvailableWorkflows = () => { const getAvailableWorkflows = (workflowId) => {
fetch(globalUrl + "/api/v1/workflows", { fetch(globalUrl + "/api/v1/workflows", {
method: "GET", method: "GET",
headers: { headers: {
@@ -240,6 +245,15 @@ const RuntimeDebugger = (props) => {
var foundWorkflows = [{"id": "", "name": "All Workflows",}] var foundWorkflows = [{"id": "", "name": "All Workflows",}]
foundWorkflows.push(...responseJson) foundWorkflows.push(...responseJson)
setWorkflows(foundWorkflows) setWorkflows(foundWorkflows)
if (workflowId !== undefined && workflowId !== null && workflowId !== "" && workflowId.length === 36) {
for (var key in responseJson) {
if (responseJson[key].id === workflowId) {
setWorkflow(responseJson[key])
break
}
}
}
} }
}) })
.catch((error) => { .catch((error) => {
@@ -248,7 +262,6 @@ const RuntimeDebugger = (props) => {
} }
useEffect(() => { useEffect(() => {
getAvailableWorkflows()
// Find workflow_id in url query // Find workflow_id in url query
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
@@ -265,6 +278,8 @@ const RuntimeDebugger = (props) => {
} }
} }
getAvailableWorkflows(workflowId)
const foundStatus = urlParams.get('status'); const foundStatus = urlParams.get('status');
if (foundStatus !== undefined && foundStatus !== null && foundStatus !== "") { if (foundStatus !== undefined && foundStatus !== null && foundStatus !== "") {
setStatus(foundStatus) setStatus(foundStatus)
@@ -314,15 +329,17 @@ const RuntimeDebugger = (props) => {
var source = params.row.execution_source var source = params.row.execution_source
if (source === "schedule") { if (source === "schedule") {
foundSource = <img src={alltriggers[1].large_image} alt="schedule" style={{borderRadius: theme.palette.borderRadius, height: imageSize, width: imageSize, }} /> foundSource = <img src={alltriggers[1].large_image} alt="schedule" style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
} else if (source === "webhook") { } else if (source === "webhook") {
foundSource = <img src={alltriggers[0].large_image} alt="webhook" style={{borderRadius: theme.palette.borderRadius, height: imageSize, width: imageSize, }} /> foundSource = <img src={alltriggers[0].large_image} alt="webhook" style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
} else if (source === "subflow" || source.length === 36) { } else if (source === "subflow" || source.length === 36) {
foundSource = <img src={alltriggers[4].large_image} alt="subflow" style={{borderRadius: theme.palette.borderRadius, height: imageSize, width: imageSize, }} /> foundSource = <img src={alltriggers[3].large_image} alt="subflow" style={{borderRadius: theme.palette?.borderRadius, height: imageSize, width: imageSize, }} />
source = "subflow" source = "subflow"
} else if (source === "rerun" || source.length === 36) { } else if (source === "rerun" || source.length === 36) {
foundSource = <ReplayIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} /> foundSource = <ReplayIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} />
source = "rerun of a previous run" source = "rerun of a previous run"
} else if (source === "form") {
foundSource = <EditNoteIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} />
} else { } else {
source = "manual" source = "manual"
} }
@@ -888,13 +905,13 @@ const RuntimeDebugger = (props) => {
{userdata.support === true ? {userdata.support === true ?
<Button <Button
variant={ignoreOrg ? "contained" : "outlined"} variant={ignoreOrg ? "contained" : "outlined"}
color="primary" color="secondary"
style={{maxHeight: 40, marginTop: 25, }} style={{marginLeft: 100, maxHeight: 40, marginTop: 25, }}
onClick={() => { onClick={() => {
setIgnoreOrg(!ignoreOrg) setIgnoreOrg(!ignoreOrg)
}} }}
> >
{ignoreOrg ? "Ignoring Org" : "Ignore Org"} {ignoreOrg ? "Ignoring Org" : "Ignore Org (Support Only)"}
</Button> </Button>
: null} : null}
</div> </div>
@@ -942,11 +959,8 @@ const RuntimeDebugger = (props) => {
}, },
}} }}
getOptionLabel={(option) => { getOptionLabel={(option) => {
if ( if (option === undefined || option === null ||
option === undefined || option.name === undefined || option.name === null
option === null ||
option.name === undefined ||
option.name === null
) { ) {
return "No Workflow Selected"; return "No Workflow Selected";
} }
@@ -961,7 +975,7 @@ const RuntimeDebugger = (props) => {
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50, height: 50,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginTop: 5, marginTop: 5,
marginLeft: 5, marginLeft: 5,
}} }}
@@ -995,13 +1009,13 @@ const RuntimeDebugger = (props) => {
<Tooltip arrow placement="left" title={ <Tooltip arrow placement="left" title={
<span style={{}}> <span style={{}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} /> <img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
: null} : null}
<Typography> <Typography>
Choose {data.name} Choose {data.name}
</Typography> </Typography>
</span> </span>
} placement="bottom"> }>
<MenuItem <MenuItem
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
@@ -1023,7 +1037,7 @@ const RuntimeDebugger = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
{...params} {...params}
label="Workflow" label="Workflow"
+10 -8
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef, useContext } from 'react';
import theme from '../theme.jsx'; import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
@@ -33,6 +33,8 @@ import {
AvatarGroup, AvatarGroup,
} from "@mui/material" } from "@mui/material"
import { Context } from '../context/ContextApi.jsx';
import { Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material' import { Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material'
import algoliasearch from 'algoliasearch/lite'; import algoliasearch from 'algoliasearch/lite';
@@ -47,8 +49,9 @@ const chipStyle = {
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const SearchData = props => { const SearchData = props => {
const { serverside, globalUrl, userdata, searchBarModalOpen, setSearchBarModalOpen } = props const { serverside, globalUrl, userdata } = props
let navigate = useNavigate(); let navigate = useNavigate();
const { searchBarModalOpen, setSearchBarModalOpen } = useContext(Context);
const borderRadius = 3 const borderRadius = 3
const node = useRef() const node = useRef()
const [searchOpen, setSearchOpen] = useState(false) const [searchOpen, setSearchOpen] = useState(false)
@@ -547,8 +550,6 @@ const SearchData = props => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
console.log("OBJECT CHANGE: ", hit.objectID)
// This does nothing rofl // This does nothing rofl
if (userdata.active_apps === undefined || userdata.active_apps === null) { if (userdata.active_apps === undefined || userdata.active_apps === null) {
activateApp(hit.name, hit.objectID, "activate") activateApp(hit.name, hit.objectID, "activate")
@@ -873,10 +874,11 @@ const SearchData = props => {
</List> </List>
</Grid> </Grid>
<Grid style={{ textAlign: "end", width: "100%", textTransform: 'capitalize', }}> <Grid style={{ textAlign: "end", width: "100%", textTransform: 'capitalize', }}>
<Button style={{ textAlign: "center", textTransform: 'capitalize' }} <Link to="/search" style={{ textDecoration: "none", color: "#f85a3e" }}>
onClick={() => { window.location = "/search"; }} > <Button style={{ textAlign: "center", textTransform: 'capitalize' }}>
See More See More
</Button> </Button>
</Link>
</Grid> </Grid>
</Grid> </Grid>
) : null ) : null
+3 -3
View File
@@ -4,7 +4,7 @@ import theme from '../theme.jsx';
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import SearchBox from "../components/SearchData.jsx"; import SearchBox from "../components/SearchData.jsx";
import { Context } from '../context/contextApi.jsx'; import { Context } from '../context/ContextApi.jsx';
import { import {
Chip, Chip,
@@ -101,7 +101,7 @@ const SearchField = props => {
</div> </div>
: null} : null}
<DialogContent className='dialog-content' style={{}}> <DialogContent className='dialog-content' style={{}}>
<SearchBox globalUrl={globalUrl} setSearchBarModalOpen={setSearchBarModalOpen} modalOpen={searchBarModalOpen} serverside={serverside} userdata={userdata} /> <SearchBox globalUrl={globalUrl} serverside={serverside} userdata={userdata} />
</DialogContent> </DialogContent>
<Divider style={{overflow: "hidden"}}/> <Divider style={{overflow: "hidden"}}/>
<span style={{display:"flex", width:"100%", height:30}}> <span style={{display:"flex", width:"100%", height:30}}>
@@ -131,7 +131,7 @@ const SearchField = props => {
<div style={{ marginTop: "auto", marginLeft: !isLoggedIn ? 0: "auto", marginRight: !isLoggedIn ? 0 : "auto", width: !isLoggedIn ? "auto" : 410, }}> <div style={{ marginTop: "auto", marginLeft: !isLoggedIn ? 0: "auto", marginRight: !isLoggedIn ? 0 : "auto", width: !isLoggedIn ? "auto" : 410, }}>
{modalView} {modalView}
<TextField <TextField
style={{ backgroundColor: "#212121", height: 48, borderRadius: rounded === true ? 25 : theme.palette.borderRadius, minWidth: fieldWidth, maxWidth: fieldWidth, }} style={{ backgroundColor: "#212121", height: 48, borderRadius: rounded === true ? 25 : theme.palette?.borderRadius, minWidth: fieldWidth, maxWidth: fieldWidth, }}
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
+93 -26
View File
@@ -40,6 +40,7 @@ import {
Close as CloseIcon, Close as CloseIcon,
DragIndicator as DragIndicatorIcon, DragIndicator as DragIndicatorIcon,
RestartAlt as RestartAltIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
@@ -56,6 +57,7 @@ import { tags as t } from '@lezer/highlight';
import AceEditor from "react-ace"; import AceEditor from "react-ace";
import ace from "ace-builds"; import ace from "ace-builds";
import 'ace-builds/src-noconflict/mode-python'; import 'ace-builds/src-noconflict/mode-python';
import 'ace-builds/src-noconflict/mode-json';
//import 'ace-builds/src-noconflict/theme-twilight'; //import 'ace-builds/src-noconflict/theme-twilight';
//import 'ace-builds/src-noconflict/theme-solarized_dark'; //import 'ace-builds/src-noconflict/theme-solarized_dark';
import 'ace-builds/src-noconflict/theme-gruvbox'; import 'ace-builds/src-noconflict/theme-gruvbox';
@@ -109,6 +111,9 @@ const CodeEditor = (props) => {
setActiveDialog, setActiveDialog,
fieldname, fieldname,
contentLoading, contentLoading,
editorData,
setAiQueryModalOpen,
} = props } = props
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
@@ -163,6 +168,11 @@ const CodeEditor = (props) => {
setMenuPosition(null); setMenuPosition(null);
} }
useEffect(() => {
highlight_variables(localcodedata)
expectedOutput(localcodedata)
}, [localcodedata])
let navigate = useNavigate(); let navigate = useNavigate();
useEffect(() => { useEffect(() => {
@@ -811,11 +821,7 @@ const CodeEditor = (props) => {
} }
const handleItemClick = (values) => { const handleItemClick = (values) => {
if ( if (values === undefined || values === null || values.length === 0) {
values === undefined ||
values === null ||
values.length === 0
) {
return; return;
} }
@@ -830,7 +836,11 @@ const CodeEditor = (props) => {
toComplete += values[key].autocomplete; toComplete += values[key].autocomplete;
} }
setlocalcodedata(localcodedata+toComplete)
handleClick({
"value": toComplete
})
//setlocalcodedata(localcodedata+toComplete)
setMenuPosition(null) setMenuPosition(null)
} }
@@ -839,10 +849,37 @@ const CodeEditor = (props) => {
return return
} }
if (!item.value.includes("{%") && !item.value.includes("{{")) { // Injects it in the right spot instead of random
setlocalcodedata(localcodedata+" | "+item.value+" }}") var edited = false
} else { if (currentCharacter !== undefined && currentCharacter !== null && currentCharacter !== -1 && currentLine !== undefined && currentLine !== null && currentLine !== -1) {
setlocalcodedata(localcodedata+item.value) // Input at the right spot
var codedatasplit = localcodedata.split('\n')
if (codedatasplit.length > currentLine) {
var currentLineData = codedatasplit[currentLine]
// Remove newlines from item.value
if (item.value.includes("% python %")) {
item.value = item.value.replaceAll("\n", ";")
item.value = item.value.replaceAll("python %};", "python %}")
} else {
item.value = item.value.replaceAll("\n", "")
}
currentLineData = currentLineData.slice(0, currentCharacter) + item.value + currentLineData.slice(currentCharacter)
codedatasplit[currentLine] = currentLineData
setlocalcodedata(codedatasplit.join('\n'))
edited = true
}
}
if (edited === false) {
if (!item.value.includes("{%") && !item.value.includes("{{")) {
setlocalcodedata(localcodedata+" | "+item.value+" }}")
} else {
setlocalcodedata(localcodedata+item.value)
}
} }
setAnchorEl(null) setAnchorEl(null)
@@ -946,14 +983,19 @@ const CodeEditor = (props) => {
// Define a custom completer for the Ace Editor // Define a custom completer for the Ace Editor
const customVariables = availableVariables
const customCompleter = { const customCompleter = {
getCompletions: function(editor, session, pos, prefix, callback) { getCompletions: function(editor, session, pos, prefix, callback) {
callback(null, customVariables.map((variable) => ({ console.log("CUSTOM COMPLETER: ", prefix)
caption: variable,
value: variable, callback(null, availableVariables.map((variable) => {
meta: 'custom', console.log("CUSTOM VAR: ", variable)
})));
return ({
caption: variable,
value: variable,
meta: 'custom',
})
}))
} }
} }
@@ -1338,6 +1380,8 @@ const CodeEditor = (props) => {
onClick={() => { onClick={() => {
console.log("CLICKED: ", innerdata); console.log("CLICKED: ", innerdata);
console.log(innerdata.example) console.log(innerdata.example)
//const handleClick = (item) => {
handleItemClick([innerdata]); handleItemClick([innerdata]);
}} }}
> >
@@ -1482,14 +1526,37 @@ const CodeEditor = (props) => {
width: 50, width: 50,
marginLeft: 100, marginLeft: 100,
}} }}
disabled={isAiLoading} disabled={editorData === undefined || editorData.example === undefined || editorData.example === null || editorData.example.length === 0}
onClick={() => { onClick={() => {
autoFormat(localcodedata) setlocalcodedata(editorData.example)
}}
color="secondary"
>
<Tooltip
title={"Reset to example body"}
placement="top"
>
<RestartAltIcon />
</Tooltip>
</IconButton>
<IconButton
style={{
height: 50,
width: 50,
marginLeft: 0,
}}
disabled={isAiLoading || editorData.name !== "body"}
onClick={() => {
if (setAiQueryModalOpen !== undefined) {
setAiQueryModalOpen(true)
} else {
autoFormat(localcodedata)
}
}} }}
> >
<Tooltip <Tooltip
color="primary" color="primary"
title={"Auto format data"} title={"Format with AI"}
placement="top" placement="top"
> >
{isAiLoading ? {isAiLoading ?
@@ -1504,7 +1571,7 @@ const CodeEditor = (props) => {
} }
<div style={{ <div style={{
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
position: "relative", position: "relative",
paddingTop: 0, paddingTop: 0,
// minHeight: 548, // minHeight: 548,
@@ -1512,8 +1579,10 @@ const CodeEditor = (props) => {
}}> }}>
{(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor ? ( {(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor ? (
<AceEditor <AceEditor
id="shuffle-codeeditor"
name="shuffle-codeeditor"
value={localcodedata} value={localcodedata}
mode={selectedAction === undefined ? "" : selectedAction.name === "execute_python" ? "python" : ""} mode={selectedAction === undefined ? "json" : selectedAction.name === "execute_python" ? "python" : "json"}
theme="gruvbox" theme="gruvbox"
height={isFileEditor ? 450 : 550} height={isFileEditor ? 450 : 550}
width={isFileEditor ? 650 : "100%"} width={isFileEditor ? 650 : "100%"}
@@ -1538,12 +1607,10 @@ const CodeEditor = (props) => {
highlight_variables(localcodedata) highlight_variables(localcodedata)
}} }}
onCursorChange={(cursorPosition, editor, value) => { onCursorChange={(cursorPosition, editor, value) => {
setCurrentCharacter(cursorPosition.column) setCurrentCharacter(cursorPosition.cursor.column)
setCurrentLine(cursorPosition.row) setCurrentLine(cursorPosition.cursor.row)
findIndex(cursorPosition.row, cursorPosition.column) findIndex(cursorPosition.row, cursorPosition.column)
//highlight_variables(localcodedata)
//console.log("VALUE CURSOR: ", value)
}} }}
onChange={(value, editor) => { onChange={(value, editor) => {
// setlocalcodedata(value) // setlocalcodedata(value)
@@ -1666,7 +1733,7 @@ const CodeEditor = (props) => {
padding: 10, padding: 10,
marginTop: -2, marginTop: -2,
border: `2px solid ${theme.palette.inputColor}`, border: `2px solid ${theme.palette.inputColor}`,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
maxHeight: 450, maxHeight: 450,
minHeight: 450, minHeight: 450,
overflow: "auto", overflow: "auto",
@@ -102,7 +102,7 @@ const SuggestedWorkflows = (props) => {
placement="top" placement="top"
style={{ zIndex: 10011 }} style={{ zIndex: 10011 }}
> >
<div key={index} style={{cursor: finished ? "auto" : "pointer", marginTop: 10, padding: 10, borderRadius: theme.palette.borderRadius, border: `1px solid ${bordercolor}`, display: "flex", backgroundColor: hovering === true ? theme.palette.inputColor : theme.palette.surfaceColor, }} onMouseOver={() => { <div key={index} style={{cursor: finished ? "auto" : "pointer", marginTop: 10, padding: 10, borderRadius: theme.palette?.borderRadius, border: `1px solid ${bordercolor}`, display: "flex", backgroundColor: hovering === true ? theme.palette.inputColor : theme.palette.surfaceColor, }} onMouseOver={() => {
setHovering(true) setHovering(true)
}} onMouseOut={() => { }} onMouseOut={() => {
setHovering(false) setHovering(false)
@@ -140,7 +140,7 @@ const SuggestedWorkflows = (props) => {
//<Paper style={{width: 275, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}> //<Paper style={{width: 275, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}>
return ( return (
<Paper style={{margin: "auto", position: "relative", backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette.borderRadius, zIndex: foundZindex, border: "1px solid rgba(255,255,255,0.2)", top: 100, left: 85,}}> <Paper style={{margin: "auto", position: "relative", backgroundColor: theme.palette.surfaceColor, borderRadius: theme.palette?.borderRadius, zIndex: foundZindex, border: "1px solid rgba(255,255,255,0.2)", top: 100, left: 85,}}>
<Dialog <Dialog
open={usecaseSearch.length > 0 && usecaseSearchType.length > 0} open={usecaseSearch.length > 0 && usecaseSearchType.length > 0}
onClose={() => { onClose={() => {
@@ -193,7 +193,7 @@ const SuggestedWorkflows = (props) => {
apps={apps} apps={apps}
/> />
</Dialog> </Dialog>
<div style={{minWidth: 250, maxWidth: 250, padding: 15, borderRadius: theme.palette.borderRadius, position: "relative", }}> <div style={{minWidth: 250, maxWidth: 250, padding: 15, borderRadius: theme.palette?.borderRadius, position: "relative", }}>
<Typography variant="body1" style={{textAlign: "center"}}> <Typography variant="body1" style={{textAlign: "center"}}>
Suggested Workflows ({finishedUsecases.length}/{usecaseSuggestions.length}) Suggested Workflows ({finishedUsecases.length}/{usecaseSuggestions.length})
</Typography> </Typography>
+6 -4
View File
@@ -594,7 +594,7 @@ const UsecaseSearch = (props) => {
width: 30, width: 30,
height: 30, height: 30,
border: "2px solid rgba(255,255,255,0.6)", border: "2px solid rgba(255,255,255,0.6)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
maxHeight: 30, maxHeight: 30,
maxWidth: 30, maxWidth: 30,
overflow: "hidden", overflow: "hidden",
@@ -616,7 +616,7 @@ const UsecaseSearch = (props) => {
width: 30, width: 30,
height: 30, height: 30,
border: "2px solid rgba(255,255,255,0.6)", border: "2px solid rgba(255,255,255,0.6)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
maxWidth: 30, maxWidth: 30,
maxHeight: 30, maxHeight: 30,
overflow: "hidden", overflow: "hidden",
@@ -1266,9 +1266,11 @@ const UsecaseSearch = (props) => {
.then((responseJson) => { .then((responseJson) => {
if (responseJson.success === false) { if (responseJson.success === false) {
var msgString = "Failed to activate the app" var msgString = "Failed to activate the app"
if (responseJson.reason !== undefined) { if (responseJson.reason !== undefined) {
msgString += ": " + responseJson.reason msgString += ": " + responseJson.reason
} }
toast(msgString) toast(msgString)
} else { } else {
//toast("App activated for your organization! Refresh the page to use the app.") //toast("App activated for your organization! Refresh the page to use the app.")
@@ -1388,7 +1390,7 @@ const UsecaseSearch = (props) => {
<Typography variant="body1" style={{color: "rgba(255,255,255,0.5)", marginRight: 20, marginTop: 13, }}> <Typography variant="body1" style={{color: "rgba(255,255,255,0.5)", marginRight: 20, marginTop: 13, }}>
{startText} {startText}
</Typography> </Typography>
<div style={{border: `1px solid ${borderColor}`, backgroundColor: theme.palette.surfaceColor, width: miditem === true ? "65%" : "85%", marginLeft: miditem === true ? 125 : 0, borderRadius: expanded ? theme.palette.borderRadius : 50, maxHeight: expanded || hasError ? 500 : 50, minHeight: 50, }}> <div style={{border: `1px solid ${borderColor}`, backgroundColor: theme.palette.surfaceColor, width: miditem === true ? "65%" : "85%", marginLeft: miditem === true ? 125 : 0, borderRadius: expanded ? theme.palette?.borderRadius : 50, maxHeight: expanded || hasError ? 500 : 50, minHeight: 50, }}>
{selectionOpen === true ? {selectionOpen === true ?
<AppsearchPopout <AppsearchPopout
@@ -1560,7 +1562,7 @@ const UsecaseSearch = (props) => {
// <b>{defaultSearch}: {allusecases[usecaseIndex].name}</b> // <b>{defaultSearch}: {allusecases[usecaseIndex].name}</b>
//console.log("UseCase: ", usecases) //console.log("UseCase: ", usecases)
return ( return (
<div style={{maxWidth: "100%", minWidth: "100%", border: "1px solid rgba(255,255,255,0)", borderRadius: theme.palette.borderRadius,}}> <div style={{maxWidth: "100%", minWidth: "100%", border: "1px solid rgba(255,255,255,0)", borderRadius: theme.palette?.borderRadius,}}>
{configureWorkflowModal} {configureWorkflowModal}
{authenticationModal} {authenticationModal}
{showTitle !== false && defaultSearch !== undefined ? {showTitle !== false && defaultSearch !== undefined ?
+1 -1
View File
@@ -648,7 +648,7 @@ const WelcomeForm = (props) => {
<div style={{ marginTop: 0, }}> <div style={{ marginTop: 0, }}>
<div className="thumbs" style={{ display: "flex" }}> <div className="thumbs" style={{ display: "flex" }}>
<div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette.borderRadius, }}> <div style={{ minWidth: isMobile ? 300 : 554, maxWidth: isMobile ? 300 : 554, borderRadius: theme.palette?.borderRadius, }}>
<ExploreWorkflow <ExploreWorkflow
globalUrl={globalUrl} globalUrl={globalUrl}
isLoggedIn={isLoggedIn} isLoggedIn={isLoggedIn}
@@ -585,7 +585,7 @@ const WorkflowTemplatePopup = (props) => {
{/*errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ? {/*errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
<Tooltip title="Click to explore the workflow" placement="top"> <Tooltip title="Click to explore the workflow" placement="top">
<span <span
style={{position: "fixed", display: "flex", right: "10%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: "15px 30px 15px 30px", backgroundColor: theme.palette.platformColor, cursor: "pointer", }} style={{position: "fixed", display: "flex", right: "10%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, padding: "15px 30px 15px 30px", backgroundColor: theme.palette.platformColor, cursor: "pointer", }}
onClick={() => { onClick={() => {
// Open in new tab // Open in new tab
window.open("/workflows/" + workflow.id, "_blank") window.open("/workflows/" + workflow.id, "_blank")
@@ -759,7 +759,7 @@ const WorkflowTemplatePopup = (props) => {
const borderStyle = isHomePage ? null : isHovered && isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? `1px solid ${highlightColor}` : "1px solid rgba(33, 33, 33, 1)" const borderStyle = isHomePage ? null : isHovered && isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? `1px solid ${highlightColor}` : "1px solid rgba(33, 33, 33, 1)"
return ( return (
<div style={{ display: "flex", height: boxHeight, borderRadius: theme.palette.borderRadius, justifyContent: isMobile ? null : "center" }} <div style={{ display: "flex", height: boxHeight, borderRadius: theme.palette?.borderRadius, justifyContent: isMobile ? null : "center" }}
> >
<ModalView /> <ModalView />
@@ -319,7 +319,7 @@ const WorkflowValidationTimeline = (props) => {
<div <div
style={{ style={{
padding: "10px 5px 10px 5px", padding: "10px 5px 10px 5px",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
border: hovering === true && showHoverForClick === true ? `1px solid ${decidedColor}` : "1px solid rgba(255,255,255,0.0)", border: hovering === true && showHoverForClick === true ? `1px solid ${decidedColor}` : "1px solid rgba(255,255,255,0.0)",
cursor: hovering === true && showHoverForClick === true ? "pointer" : "default", cursor: hovering === true && showHoverForClick === true ? "pointer" : "default",
+37
View File
@@ -0,0 +1,37 @@
import { createContext, useState, useEffect } from 'react';
export const Context = createContext();
export const AppContext =(props) => {
// Left side bar global states
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(false);
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
//Calculate window width
useEffect(() => {
const handleResize = () => {
setWindowWidth(window?.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<Context.Provider value={{
searchBarModalOpen,
setSearchBarModalOpen,
leftSideBarOpenByClick,
setLeftSideBarOpenByClick,
windowWidth
}}>
{props.children}
</Context.Provider>
)
}
-23
View File
@@ -1,23 +0,0 @@
import { createContext, useState } from 'react';
export const Context = createContext();
export const AppContext =(props) => {
// Left side bar global states
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(false);
return (
<Context.Provider value={{
searchBarModalOpen,
setSearchBarModalOpen,
leftSideBarOpenByClick,
setLeftSideBarOpenByClick
}}>
{props.children}
</Context.Provider>
)
}
+27 -2
View File
@@ -13,6 +13,30 @@ const data = [
return elementname return elementname
}, },
"text-valign": "center", "text-valign": "center",
"text-margin-x": function(element) {
// Attempt at bottom-positioning
// Required text-valign: bottom
// FIXME: Disabled for now.
return "15px"
const name = element.data("label")
console.log("Name: ", name)
if (name === null || name === undefined || name == "" || document=== undefined || document === null) {
return "0px"
}
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d')
context.font = '18px Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif'
const textWidth = context.measureText(name).width
return textWidth + "px"
//return -1*(textWidth) + "px"
},
"font-family": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif", "font-family": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif",
"font-weight": "lighter", "font-weight": "lighter",
"font-size": "18px", "font-size": "18px",
@@ -23,7 +47,6 @@ const data = [
padding: "10px", padding: "10px",
margin: "5px", margin: "5px",
"border-width": "1px", "border-width": "1px",
"text-margin-x": "10px",
"z-index": 5001, "z-index": 5001,
}, },
}, },
@@ -263,7 +286,9 @@ const data = [
{ {
selector: "node[?isStartNode]", selector: "node[?isStartNode]",
css: { css: {
shape: "ellipse", shape: function(element) {
return "ellipse"
},
"border-color": "#80deea", "border-color": "#80deea",
width: "80px", width: "80px",
height: "80px", height: "80px",
-1
View File
@@ -1,7 +1,6 @@
import React from "react"; import React from "react";
import { createTheme, adaptV4Theme } from "@mui/material/styles"; import { createTheme, adaptV4Theme } from "@mui/material/styles";
//const theme = createTheme({
const theme = createTheme(adaptV4Theme({ const theme = createTheme(adaptV4Theme({
palette: { palette: {
theme: "dark", theme: "dark",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -4717,7 +4717,7 @@ const AppCreator = (defaultprops) => {
<TextField <TextField
style={{ style={{
backgroundColor: inputColor, backgroundColor: inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -5066,7 +5066,7 @@ const AppCreator = (defaultprops) => {
<TextField <TextField
style={{ style={{
backgroundColor: inputColor, backgroundColor: inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -5106,7 +5106,7 @@ const AppCreator = (defaultprops) => {
<TextField <TextField
style={{ style={{
backgroundColor: inputColor, backgroundColor: inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
InputProps={{ InputProps={{
style: { style: {
@@ -6018,7 +6018,7 @@ const AppCreator = (defaultprops) => {
setBaseUrl(tmpstring); setBaseUrl(tmpstring);
}} }}
/> />
<div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette.borderRadius, }}> <div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette?.borderRadius, }}>
<span style={{display: "flex", }}> <span style={{display: "flex", }}>
<FormControl style={{ }} variant="outlined"> <FormControl style={{ }} variant="outlined">
<Typography variant="h6">Authentication</Typography> <Typography variant="h6">Authentication</Typography>
+54 -23
View File
@@ -1,4 +1,4 @@
import React, { useEffect } from "react"; import React, { useEffect, useContext, memo } from "react";
import { useInterval } from "react-powerhooks"; import { useInterval } from "react-powerhooks";
import theme from '../theme.jsx'; import theme from '../theme.jsx';
@@ -49,6 +49,7 @@ import {
Folder as FolderIcon, Folder as FolderIcon,
LibraryBooks as LibraryBooksIcon, LibraryBooks as LibraryBooksIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { Context } from "../context/ContextApi.jsx";
import { import {
ForkRight as ForkRightIcon, ForkRight as ForkRightIcon,
@@ -280,6 +281,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
const Apps = (props) => { const Apps = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, serverside, } = props; const { globalUrl, isLoggedIn, isLoaded, userdata, serverside, } = props;
//const [workflows, setWorkflows] = React.useState([]); //const [workflows, setWorkflows] = React.useState([]);
const baseRepository = "https://github.com/frikky/shuffle-apps"; const baseRepository = "https://github.com/frikky/shuffle-apps";
//const alert = useAlert(); //const alert = useAlert();
@@ -423,7 +425,7 @@ const Apps = (props) => {
minWidth: "100%", minWidth: "100%",
maxWidth: 612.5, maxWidth: 612.5,
marginBottom: 5, marginBottom: 5,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
color: "white", color: "white",
backgroundColor: surfaceColor, backgroundColor: surfaceColor,
cursor: "pointer", cursor: "pointer",
@@ -875,7 +877,7 @@ const Apps = (props) => {
minWidth: viewWidth, minWidth: viewWidth,
maxWidth: viewWidth, maxWidth: viewWidth,
color: "white", color: "white",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
backgroundColor: surfaceColor, backgroundColor: surfaceColor,
//display: "flex", //display: "flex",
marginBottom: 10, marginBottom: 10,
@@ -1551,7 +1553,7 @@ const Apps = (props) => {
minHeight: 150, minHeight: 150,
maxHeight: 150, maxHeight: 150,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
} }
if (!makeFancy) { if (!makeFancy) {
@@ -1829,7 +1831,7 @@ const Apps = (props) => {
}}> }}>
<TextField <TextField
fullWidth fullWidth
style={{ backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, maxWidth: leftBarSize - 20, }} style={{ backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius, maxWidth: leftBarSize - 20, }}
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
@@ -2185,7 +2187,7 @@ const Apps = (props) => {
</div> </div>
<div style={{ height: 50 }}> <div style={{ height: 50 }}>
<TextField <TextField
style={{ backgroundColor: inputColor, borderRadius: theme.palette.borderRadius, }} style={{ backgroundColor: inputColor, borderRadius: theme.palette?.borderRadius, }}
InputProps={{ InputProps={{
style: { style: {
}, },
@@ -2236,7 +2238,7 @@ const Apps = (props) => {
minWidth: viewWidth, minWidth: viewWidth,
maxWidth: viewWidth, maxWidth: viewWidth,
color: "white", color: "white",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
//display: "flex", //display: "flex",
marginBottom: 10, marginBottom: 10,
overflow: "hidden", overflow: "hidden",
@@ -2373,6 +2375,7 @@ const Apps = (props) => {
toast("Hotloading apps from location in .env"); toast("Hotloading apps from location in .env");
setIsLoading(true); setIsLoading(true);
fetch(globalUrl + "/api/v1/apps/run_hotload", { fetch(globalUrl + "/api/v1/apps/run_hotload", {
method: "POST",
mode: "cors", mode: "cors",
headers: { headers: {
Accept: "application/json", Accept: "application/json",
@@ -2999,7 +3002,7 @@ const Apps = (props) => {
PaperProps={{ PaperProps={{
style: { style: {
backgroundColor: theme.palette.platformColor, backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
color: "white", color: "white",
minWidth: "800px", minWidth: "800px",
minHeight: "320px", minHeight: "320px",
@@ -3091,7 +3094,7 @@ const Apps = (props) => {
PaperProps={{ PaperProps={{
style: { style: {
backgroundColor: theme.palette.platformColor, backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
color: "white", color: "white",
minWidth: "800px", minWidth: "800px",
minHeight: "320px", minHeight: "320px",
@@ -3202,22 +3205,50 @@ const Apps = (props) => {
</Dialog> </Dialog>
) : null; ) : null;
const loadedCheck = const loadedCheck = isLoaded && !firstrequest ? (
isLoaded && !firstrequest ? ( <SidebarAdjustWrapper userdata={userdata}>
<div> <AppsWrapper
{appView} userdata={userdata}
{modalView} appView={appView}
{publishModal} modalView={modalView}
{generateAppView} publishModal={publishModal}
{appsModalLoad} generateAppView={generateAppView}
{deleteModal} appsModalLoad={appsModalLoad}
</div> deleteModal={deleteModal}
) : ( />
<div></div> </SidebarAdjustWrapper>
); ) : (
<div></div>
);
// Maybe use gridview or something, idk
return loadedCheck; return loadedCheck;
}; };
export default Apps; export default Apps;
const AppsWrapper = memo(({ appView, modalView, userdata, publishModal, generateAppView, appsModalLoad, deleteModal }) => (
<>
{appView}
{modalView}
{publishModal}
{generateAppView}
{appsModalLoad}
{deleteModal}
</>
));
const SidebarAdjustWrapper = memo(({ userdata, children }) => {
const {leftSideBarOpenByClick } = useContext(Context)
const marginLeft = userdata?.support
? leftSideBarOpenByClick ? 250 : 80
: 0;
return (
<div style={{ marginLeft, transition: 'margin-left 0.3s ease' }}>
{children}
</div>
);
});
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -197,7 +197,7 @@ const LineChartWrapper = ({keys, height, width}) => {
const name = data.metadata !== undefined && data.metadata.name !== undefined ? data.metadata.name : "No" const name = data.metadata !== undefined && data.metadata.name !== undefined ? data.metadata.name : "No"
return ( return (
<div style={{borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}> <div style={{borderRadius: theme.palette?.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<Typography variant="body1"> <Typography variant="body1">
{name} {name}
</Typography> </Typography>
@@ -281,7 +281,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
}} }}
content={(data, color) => { content={(data, color) => {
return ( return (
<div style={{borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}> <div style={{borderRadius: theme.palette?.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<Typography variant="body1"> <Typography variant="body1">
{data.x} {data.x}
</Typography> </Typography>
@@ -795,7 +795,7 @@ const Dashboard = (props) => {
color: "white", color: "white",
height: 40, height: 40,
maxWidth: 150, maxWidth: 150,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
{data.available_keys.map((foundKey, index) => { {data.available_keys.map((foundKey, index) => {
+47 -19
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useLayoutEffect, useRef, useState } from "react" import React, { useEffect, useLayoutEffect, useRef, useState, useContext, memo } from "react"
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import theme from '../theme.jsx'; import theme from '../theme.jsx';
@@ -8,6 +8,7 @@ import { BrowserView, MobileView } from "react-device-detect";
import { useParams, useNavigate, Link } from "react-router-dom"; import { useParams, useNavigate, Link } from "react-router-dom";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
import { Context } from "../context/ContextApi.jsx";
import { import {
Grid, Grid,
TextField, TextField,
@@ -166,7 +167,7 @@ export const Img = (props) => {
return( return(
<img <img
style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, width: width, maxWidth: width, margin: "auto", marginTop: 10, marginBottom: 10, }} style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: width, maxWidth: width, margin: "auto", marginTop: 10, marginBottom: 10, }}
alt={props.alt} alt={props.alt}
src={props.src} src={props.src}
/> />
@@ -213,7 +214,7 @@ export const CodeHandler = (props) => {
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
overflowY: "auto", overflowY: "auto",
// Have it inline // Have it inline
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
> >
{validate.valid === true ? {validate.valid === true ?
@@ -250,10 +251,8 @@ export const CodeHandler = (props) => {
} }
const Docs = (defaultprops) => { const Docs = (defaultprops) => {
const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; const { globalUrl, selectedDoc, serverside, serverMobile, userdata } = defaultprops;
let navigate = useNavigate(); let navigate = useNavigate();
// Quickfix for react router 5 -> 6 // Quickfix for react router 5 -> 6
const params = useParams(); const params = useParams();
//var props = JSON.parse(JSON.stringify(defaultprops)) //var props = JSON.parse(JSON.stringify(defaultprops))
@@ -526,7 +525,7 @@ const Docs = (defaultprops) => {
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
padding: 15, padding: 15,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginBottom: 30, marginBottom: 30,
display: "flex", display: "flex",
}} }}
@@ -892,7 +891,7 @@ const Docs = (defaultprops) => {
target="_blank" target="_blank"
style={{ textDecoration: "none", color: "inherit", flex: 1, margin: 10, }} style={{ textDecoration: "none", color: "inherit", flex: 1, margin: 10, }}
> >
<div style={{ cursor: hover ? "pointer" : "default", borderRadius: theme.palette.borderRadius, flex: 1, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, padding: 25, }} <div style={{ cursor: hover ? "pointer" : "default", borderRadius: theme.palette?.borderRadius, flex: 1, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, padding: 25, }}
onClick={(event) => { onClick={(event) => {
if (link === "" || link === undefined) { if (link === "" || link === undefined) {
event.preventDefault() event.preventDefault()
@@ -932,7 +931,7 @@ const Docs = (defaultprops) => {
return ( return (
<Link to={link} style={hrefStyle}> <Link to={link} style={hrefStyle}>
<div style={{ width: "100%", height: 80, cursor: hover ? "pointer" : "default", borderRadius: theme.palette.borderRadius, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, }} <div style={{ width: "100%", height: 80, cursor: hover ? "pointer" : "default", borderRadius: theme.palette?.borderRadius, border: "1px solid rgba(255,255,255,0.3)", backgroundColor: hover ? theme.palette.surfaceColor : theme.palette.inputColor, }}
onMouseOver={() => { onMouseOver={() => {
setHover(true) setHover(true)
}} }}
@@ -968,8 +967,8 @@ const Docs = (defaultprops) => {
Documentation Documentation
</Typography> </Typography>
<div style={{ display: "flex", marginTop: 25, }}> <div style={{ display: "flex", marginTop: 25, }}>
<CustomButton title="Talk to Support" icon=<img src="/images/Shuffle_logo_new.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette.borderRadius, }} /> /> <CustomButton title="Talk to Support" icon=<img src="/images/Shuffle_logo_new.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette?.borderRadius, }} /> />
<CustomButton title="Ask the community" icon=<img src="/images/social/discord.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette.borderRadius, }} /> link="https://discord.gg/B2CBzUm" /> <CustomButton title="Ask the community" icon=<img src="/images/social/discord.png" style={{ height: 35, width: 35, border: "", borderRadius: theme.palette?.borderRadius, }} /> link="https://discord.gg/B2CBzUm" />
</div> </div>
<div style={{ textAlign: "left" }}> <div style={{ textAlign: "left" }}>
@@ -1265,15 +1264,44 @@ const Docs = (defaultprops) => {
</div> </div>
); );
// Padding and zIndex etc set because of footer in cloud. console.log("docs render")
const loadedCheck = (
<div style={{ minHeight: 1000, zIndex: 50000, maxWidth: 1920, minWidth: isMobile ? null : 1366, margin: "auto", }}> // Padding and zIndex etc set because of footer in cloud.
<BrowserView>{postDataBrowser}</BrowserView> const loadedCheck = (
<MobileView>{postDataMobile}</MobileView> <DocsWrapper userdata={userdata}>
</div> <DocsContent postDataBrowser={postDataBrowser} postDataMobile={postDataMobile}/>
); </DocsWrapper>
);
return <div>{loadedCheck}</div>;
return <div style={{}}>{loadedCheck}</div>;
}; };
export default Docs; export default Docs;
const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
return(
<div>
<BrowserView>{postDataBrowser}</BrowserView>
<MobileView>{postDataMobile}</MobileView>
</div>
)})
const DocsWrapper = memo(({userdata, children })=>{
const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
return (
<div style={{
minHeight: 1000, zIndex: 1,
maxWidth: Math.min(!userdata?.support ? 1920 : leftSideBarOpenByClick ? windowWidth - 300 : windowWidth - 200, 1920),
minWidth: isMobile ? null : userdata?.support ? leftSideBarOpenByClick ? 800 : 900 : null, margin: "auto",
position: userdata?.support && leftSideBarOpenByClick ? "relative" : "static",
left: userdata?.support && leftSideBarOpenByClick ? 120 : userdata?.support && !leftSideBarOpenByClick ? 80 : 0,
marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick && userdata?.support ? 160 : userdata?.support && !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
transition: "left 0.3s ease-in-out, min-width 0.3s ease-in-out, max-width 0.3s ease-in-out, position 0.3s ease-in-out, margin 0.3s ease-in-out, margin-left 0.3s ease"
}}>
{children}
</div>
)
})
+1 -1
View File
@@ -2467,7 +2467,7 @@ const GettingStarted = (props) => {
maxWidth: 1024, maxWidth: 1024,
zIndex: 11, zIndex: 11,
border: "1px solid rgba(255,255,255,0.1)", border: "1px solid rgba(255,255,255,0.1)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
textAlign: "center", textAlign: "center",
overflow: "auto", overflow: "auto",
}} }}
+221 -106
View File
@@ -43,6 +43,7 @@ import {
Lock as LockIcon, Lock as LockIcon,
LockOpen as LockOpenIcon, LockOpen as LockOpenIcon,
OpenInNew as OpenInNewIcon, OpenInNew as OpenInNewIcon,
Edit as EditIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
const hrefStyle = { const hrefStyle = {
@@ -50,20 +51,13 @@ const hrefStyle = {
textDecoration: "none" textDecoration: "none"
} }
const bodyDivStyle = {
margin: "auto",
width: isMobile? "100%":"500px",
position: "relative",
paddingBottom: 250,
}
const RunWorkflow = (defaultprops) => { const RunWorkflow = (defaultprops) => {
const { globalUrl, userdata, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops; const { globalUrl, userdata, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
let navigate = useNavigate(); let navigate = useNavigate();
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
const [explorerUi, setExplorerUi] = useState(false)
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
const [workflow, setWorkflow] = React.useState({}); const [workflow, setWorkflow] = React.useState({});
const [executionRequest, setExecutionRequest] = React.useState({}); const [executionRequest, setExecutionRequest] = React.useState({});
@@ -80,6 +74,8 @@ const RunWorkflow = (defaultprops) => {
const [sharingOpen, setSharingOpen] = React.useState(false) const [sharingOpen, setSharingOpen] = React.useState(false)
const [realtimeMarkdown, setRealtimeMarkdown] = React.useState("") const [realtimeMarkdown, setRealtimeMarkdown] = React.useState("")
const [forms, setForms] = React.useState([]) const [forms, setForms] = React.useState([])
const [boxWidth, setBoxWidth] = React.useState(500)
const [inputQuestions, setInputQuestions] = React.useState([])
const IframeWrapper = (props) => { const IframeWrapper = (props) => {
var propsCopy = JSON.parse(JSON.stringify(props)) var propsCopy = JSON.parse(JSON.stringify(props))
@@ -94,11 +90,21 @@ const RunWorkflow = (defaultprops) => {
if (propsCopy.width === undefined || propsCopy.width === null) { if (propsCopy.width === undefined || propsCopy.width === null) {
propsCopy.width = 400 propsCopy.width = 400
propsCopy.height = "auto" propsCopy.height = "auto"
propsCopy.margin = "auto"
} }
return Img(propsCopy) return Img(propsCopy)
} }
const bodyDivStyle = {
margin: "auto",
width: isMobile? "100%" : boxWidth,
position: "relative",
paddingBottom: 250,
}
const boxStyle = { const boxStyle = {
color: "white", color: "white",
padding: "25px 50px 50px 50px", padding: "25px 50px 50px 50px",
@@ -112,7 +118,7 @@ const RunWorkflow = (defaultprops) => {
props.match = {} props.match = {}
props.match.params = params props.match.params = params
const defaultTitle = workflow.name !== undefined ? workflow.name : "Run Workflow" const defaultTitle = workflow.name !== undefined ? "Shuffle - Form for " + workflow.name : "Shuffle - Form to Run Workflows"
if (document != undefined && document.title != defaultTitle) { if (document != undefined && document.title != defaultTitle) {
document.title = defaultTitle document.title = defaultTitle
} }
@@ -136,6 +142,18 @@ const RunWorkflow = (defaultprops) => {
return true return true
} }
// Check if it's an object or not
if (typeof executionArgument === "string") {
// Make it an object
try {
executionArgument = JSON.parse(executionArgument)
} catch (e) {
console.log("Error parsing execution argument: ", e)
executionArgument = {}
}
}
console.log("EXEC: ", executionArgument)
for (var key in executionArgument) { for (var key in executionArgument) {
if (executionArgument[key] === undefined || executionArgument[key] === null || executionArgument[key] === "") { if (executionArgument[key] === undefined || executionArgument[key] === null || executionArgument[key] === "") {
return false return false
@@ -257,9 +275,12 @@ const RunWorkflow = (defaultprops) => {
return ( return (
<div style={{marginTop: executionMargin, }}> <div style={{marginTop: executionMargin, }}>
<Divider style={{marginTop: 20, marginBottom: 20, }}/> {workflowQuestion !== "" ? null :
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
}
{validate.valid === false ? {workflowQuestion !== "" ? null :
validate.valid === false ?
<div style={{marginTop: 20, }}> <div style={{marginTop: 20, }}>
<Divider /> <Divider />
<Markdown <Markdown
@@ -446,18 +467,6 @@ const RunWorkflow = (defaultprops) => {
}) })
} }
/*
useEffect(() => {
if (executionRequest === undefined || executionRequest === null || executionRequest.execution_id === undefined || executionRequest.execution_id === null || executionRequest.execution_id.length === 0) {
return
}
if (executionRequest.start === true) {
start()
}
}, [executionRequest])
*/
const { start, stop } = useInterval({ const { start, stop } = useInterval({
duration: 1500, duration: 1500,
startImmediate: true, startImmediate: true,
@@ -520,8 +529,8 @@ const RunWorkflow = (defaultprops) => {
if (realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0) { if (realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0) {
const newmarkdown = realtimeMarkdown.replace(`{{ ${workflow_id} }}`, "", -1) const newmarkdown = realtimeMarkdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown) setRealtimeMarkdown(newmarkdown)
} else if (inputWorkflow.input_markdown !== undefined && inputWorkflow.input_markdown !== null && inputWorkflow.input_markdown.length > 0) { } else if (inputWorkflow.form_control.input_markdown !== undefined && inputWorkflow.form_control.input_markdown !== null && inputWorkflow.form_control.input_markdown.length > 0) {
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1) const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown) setRealtimeMarkdown(newmarkdown)
} }
} }
@@ -531,15 +540,21 @@ const RunWorkflow = (defaultprops) => {
console.log("Get workflow error: ", error.toString()) console.log("Get workflow error: ", error.toString())
if (realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0) { if (realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0) {
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1) const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown) setRealtimeMarkdown(newmarkdown)
} else if (inputWorkflow.input_markdown !== undefined && inputWorkflow.input_markdown !== null && inputWorkflow.input_markdown.length > 0) { } else if (inputWorkflow.form_control.input_markdown !== undefined && inputWorkflow.form_control.input_markdown !== null && inputWorkflow.form_control.input_markdown.length > 0) {
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1) const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
setRealtimeMarkdown(newmarkdown) setRealtimeMarkdown(newmarkdown)
} }
}) })
} }
const searchParams = new URLSearchParams(window.location.search)
const answer = searchParams.get("answer")
const execution_id = searchParams.get("reference_execution")
const authorization = searchParams.get("authorization")
const sourceNode = searchParams.get("source_node")
const getWorkflow = (workflow_id, selectedNode) => { const getWorkflow = (workflow_id, selectedNode) => {
setRealtimeMarkdown("") setRealtimeMarkdown("")
@@ -595,6 +610,56 @@ const RunWorkflow = (defaultprops) => {
} }
} }
// Override with just relevant fields
if (sourceNode !== undefined && sourceNode !== null && sourceNode.length > 0) {
for (var triggerkey in responseJson.triggers) {
const trig = responseJson.triggers[triggerkey]
if (trig.id !== sourceNode) {
continue
}
console.log("TRIG: ", trig)
if (trig.parameters === undefined || trig.parameters === null) {
trig.parameters = []
}
for (var paramkey in trig.parameters) {
const param = trig.parameters[paramkey]
if (param.name !== "input_questions") {
continue
}
// Parse as json
var keepfields = []
try {
const parsed = JSON.parse(param.value)
// Find this in the workflow.input_questions
for (var questionkey in responseJson.input_questions) {
var question = JSON.parse(JSON.stringify(responseJson.input_questions[questionkey]))
question.value = question.value.split(";")[0]
if (parsed.includes(question.name)) {
keepfields.push(question.value)
}
}
//newexec = {}
} catch (e) {
console.log("Error parsing input questions: ", e)
}
// Remapping it to exec
if (keepfields.length > 0) {
newexec = {}
for (var key in keepfields) {
newexec[keepfields[key]] = ""
}
}
}
}
}
setExecutionArgument(newexec) setExecutionArgument(newexec)
} }
@@ -631,7 +696,8 @@ const RunWorkflow = (defaultprops) => {
} }
} }
responseJson.input_questions = relevantquestions setInputQuestions(relevantquestions)
//responseJson.input_questions = relevantquestions
} }
} }
} }
@@ -642,10 +708,10 @@ const RunWorkflow = (defaultprops) => {
} }
} }
if (responseJson.input_markdown !== undefined && responseJson.input_markdown !== null && responseJson.input_markdown.length > 0) { if (responseJson.form_control.input_markdown !== undefined && responseJson.form_control.input_markdown !== null && responseJson.form_control.input_markdown.length > 0) {
// Look for {{ uuid }} format, and try to run that workflow with their account // Look for {{ uuid }} format, and try to run that workflow with their account
// This is a hack, but a fun one. // This is a hack, but a fun one.
var newmarkdown = responseJson.input_markdown.replace("", "") var newmarkdown = responseJson.form_control.input_markdown.replace("", "")
const uuidRegex = /{{\s[a-f0-9-]+\s}}/g const uuidRegex = /{{\s[a-f0-9-]+\s}}/g
const found = newmarkdown.match(uuidRegex) const found = newmarkdown.match(uuidRegex)
@@ -697,7 +763,21 @@ const RunWorkflow = (defaultprops) => {
handleExecutionLoader() handleExecutionLoader()
handleGetOrg(responseJson.org_id) handleGetOrg(responseJson.org_id)
setWorkflow(responseJson);
if (responseJson.form_control === undefined || responseJson.form_control === null) {
responseJson.form_control = {
"input_markdown": "",
"output_yields": [],
"form_width": 500,
}
}
if (responseJson.form_control.form_width !== undefined && responseJson.form_control.form_width !== null && responseJson.form_control.form_width > 300) {
setBoxWidth(responseJson.form_control.form_width)
}
setWorkflow(responseJson)
}) })
.catch((error) => { .catch((error) => {
console.log("Get workflow error: ", error.toString()); console.log("Get workflow error: ", error.toString());
@@ -723,7 +803,6 @@ const RunWorkflow = (defaultprops) => {
if (responseJson.result.startsWith("[") && responseJson.result.endsWith("]")) { if (responseJson.result.startsWith("[") && responseJson.result.endsWith("]")) {
try { try {
responseJson.result = JSON.parse(responseJson.result).length responseJson.result = JSON.parse(responseJson.result).length
console.log("Set length to: ", responseJson.result)
} catch (e) { } catch (e) {
console.log("Error parsing length: ", e) console.log("Error parsing length: ", e)
} }
@@ -875,8 +954,8 @@ const RunWorkflow = (defaultprops) => {
const newmarkdown = realtimeMarkdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1) const newmarkdown = realtimeMarkdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
setRealtimeMarkdown(newmarkdown) setRealtimeMarkdown(newmarkdown)
} else if (workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0) { } else if (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) {
const newmarkdown = workflow.input_markdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1) const newmarkdown = workflow.form_control.input_markdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
setRealtimeMarkdown(newmarkdown) setRealtimeMarkdown(newmarkdown)
} }
@@ -893,17 +972,23 @@ const RunWorkflow = (defaultprops) => {
}); });
}; };
const searchParams = new URLSearchParams(window.location.search)
const answer = searchParams.get("answer")
const execution_id = searchParams.get("reference_execution")
const authorization = searchParams.get("authorization")
const sourceNode = searchParams.get("source_node")
useEffect(() => { useEffect(() => {
if (!isLoaded) { if (!isLoaded) {
return return
} }
if (props.match.params.key === undefined) {
setExplorerUi(true)
if (isLoggedIn) {
loadForms(userdata.active_org.id)
handleGetOrg(userdata.active_org.id)
}
return
}
getWorkflow(props.match.params.key, sourceNode) getWorkflow(props.match.params.key, sourceNode)
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) { if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
console.log("Get execution: ", execution_id) console.log("Get execution: ", execution_id)
@@ -915,7 +1000,6 @@ const RunWorkflow = (defaultprops) => {
} }
}, [isLoaded]) }, [isLoaded])
useEffect(() => { useEffect(() => {
if (executionData === undefined || executionData === null || executionData === {}) { if (executionData === undefined || executionData === null || executionData === {}) {
return return
@@ -947,9 +1031,14 @@ const RunWorkflow = (defaultprops) => {
} }
if (result.status !== "WAITING") { if (result.status !== "WAITING") {
if (parsedresult.information !== undefined && parsedresult.information !== null && parsedresult.information.length > 0) {
setWorkflowQuestion(parsedresult.information)
}
if (parsedresult.click_info !== undefined && parsedresult.click_info !== null) { if (parsedresult.click_info !== undefined && parsedresult.click_info !== null) {
if (parsedresult.click_info.user !== undefined && parsedresult.click_info.user !== null && parsedresult.click_info.user.length > 0) { if (parsedresult.click_info.user !== undefined && parsedresult.click_info.user !== null && parsedresult.click_info.user.length > 0) {
setMessage("Already answered by " + parsedresult.click_info.user) setMessage("Already answered by " + parsedresult.click_info.user)
} }
} else { } else {
setMessage("Answered.") setMessage("Answered.")
@@ -965,8 +1054,12 @@ const RunWorkflow = (defaultprops) => {
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)" const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"} const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
//const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED" // Check if all fields are filled in?
const disabledButtons = executionLoading || executionRunning var disabledButtons = executionLoading || executionRunning || message.length > 0
if (disabledButtons === false && workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0) {
// Check field values
//disabledButtons = handleValidateForm(executionArgument)
}
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown" const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io" const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io"
@@ -995,20 +1088,69 @@ const RunWorkflow = (defaultprops) => {
} }
} }
const FormList = () => {
return (
<div>
{forms.map((form, formIndex) => {
if (form.id === undefined || form.id === null) {
return null
}
return (
<div key={formIndex} style={{marginBottom: 10, }}>
<RecentWorkflow
workflow={form}
onclickHandler={() => {
navigate(`/forms/${form.id}`)
getWorkflow(form.id, sourceNode)
setExplorerUi(false)
}}
currentWorkflowId={workflow.id}
/>
</div>
)
})}
</div>
)
}
const ExplorerUi = () => {
return (
<div style={{paddingTop: 50, marginTop: 50, width: 250, itemAlign: "center", textAlign: "center", margin: "auto", }}>
{forms !== undefined && forms !== null && forms.length > 0 ?
<div>
<Typography variant="h6" style={{marginBottom: 20, }}>
Available forms
</Typography>
<FormList />
</div>
:
<Typography variant="h6" style={{marginTop: 100, marginBottom: 20, }}>
No Form Found
</Typography>
}
</div>
)
}
var validResults = 0 var validResults = 0
const basedata = const basedata =
<div style={bodyDivStyle}> <div style={bodyDivStyle}>
<Paper style={boxStyle}> <Paper style={boxStyle}>
{workflow.id === undefined || workflow.id === null ? {explorerUi === true ?
<ExplorerUi />
:
workflow.id === undefined || workflow.id === null ?
<div style={{paddingTop: 150, marginTop: 150, width: 250, itemAlign: "center", textAlign: "center", margin: "auto", }}> <div style={{paddingTop: 150, marginTop: 150, width: 250, itemAlign: "center", textAlign: "center", margin: "auto", }}>
<CircularProgress /> <CircularProgress />
<Typography variant="body1" style={{marginTop: 20, }}> <Typography variant="body1" style={{marginTop: 20, }}>
Loding Form Details... Loading Form Details...
</Typography> </Typography>
</div> </div>
: :
<div> <div>
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ? {workflowQuestion !== "" || (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) ?
<div style={{marginBottom: 20, }}> <div style={{marginBottom: 20, }}>
<Markdown <Markdown
components={{ components={{
@@ -1024,13 +1166,13 @@ const RunWorkflow = (defaultprops) => {
}} }}
rehypePlugins={[rehypeRaw]} rehypePlugins={[rehypeRaw]}
> >
{realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0 ? realtimeMarkdown : workflow.input_markdown} {workflowQuestion !== "" ? workflowQuestion : realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0 ? realtimeMarkdown : workflow.form_control.input_markdown}
</Markdown> </Markdown>
</div> </div>
: null} : null}
<form onSubmit={(e) => {onSubmit(e)}} style={{margin: "25px 0px 15px 0px",}}> <form onSubmit={(e) => {onSubmit(e)}} style={{margin: "25px 0px 15px 0px",}}>
{workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0 ? null : {workflowQuestion !== "" || (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) ? null :
<div> <div>
<img <img
alt={workflow.name} alt={workflow.name}
@@ -1066,7 +1208,7 @@ const RunWorkflow = (defaultprops) => {
<div style={{ <div style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
padding: 20, padding: 20,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginBottom: 35, marginBottom: 35,
marginTop: 30, marginTop: 30,
}}> }}>
@@ -1081,7 +1223,7 @@ const RunWorkflow = (defaultprops) => {
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ? {workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
<div style={{marginBottom: 5, }}> <div style={{marginBottom: 5, }}>
{workflow.input_questions.map((question, index) => { {inputQuestions.map((question, index) => {
// Multiple choice checks for semicolon-splits // Multiple choice checks for semicolon-splits
var multiChoiceOptions = question.value !== undefined && question.value !== null && question.value.length > 0 && question.value.includes(";") ? question.value.split(";") : [] var multiChoiceOptions = question.value !== undefined && question.value !== null && question.value.length > 0 && question.value.includes(";") ? question.value.split(";") : []
@@ -1215,25 +1357,26 @@ const RunWorkflow = (defaultprops) => {
What do you want to do? What do you want to do?
</Typography> </Typography>
} }
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
onSubmit(null, execution_id, authorization, true)
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
<Button fullWidth id="continue_execution" variant="contained" disabled={!handleValidateForm(executionArgument) || disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
setButtonClicked("FINISHED") setButtonClicked("FINISHED")
setExecutionData({ setExecutionData({
status: "FINISHED", status: "FINISHED",
}) })
onSubmit(null, execution_id, authorization, true)
}}>Continue</Button> }}>Continue</Button>
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}> <Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
&nbsp;or&nbsp; &nbsp;or&nbsp;
</Typography> </Typography>
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => { <Button fullWidth id="abort_execution" variant="contained" disabled={!handleValidateForm(executionArgument) || disabledButtons} color="primary" style={{ flex: 1, }} onClick={() => {
onSubmit(null, execution_id, authorization, false)
setButtonClicked("ABORTED") setButtonClicked("ABORTED")
setExecutionData({ setExecutionData({
status: "ABORTED", status: "ABORTED",
}) })
onSubmit(null, execution_id, authorization, false)
}}>Stop</Button> }}>Stop</Button>
</div> </div>
</span> </span>
@@ -1258,9 +1401,9 @@ const RunWorkflow = (defaultprops) => {
} }
{workflow.output_yields !== undefined && workflow.output_yields !== null && workflow.output_yields.length > 0 ? {workflow.form_control.output_yields !== undefined && workflow.form_control.output_yields !== null && workflow.form_control.output_yields.length > 0 ?
<div style={{marginTop: 20, }}> <div style={{marginTop: 20, }}>
{workflow.output_yields.map((yieldItem, index) => { {workflow.form_control.output_yields.map((yieldItem, index) => {
if (executionData.results === undefined || executionData.results === null || executionData.results.length === 0) { if (executionData.results === undefined || executionData.results === null || executionData.results.length === 0) {
return null return null
} }
@@ -1328,15 +1471,18 @@ const RunWorkflow = (defaultprops) => {
</div> </div>
} }
</Paper> </Paper>
<Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 10, }} >
Forms are in Beta. Form submissions data includes your Organization's unique ID while logged in, or a unique identifier for your browser otherwise. Your input will be automatically sanitized. {workflowQuestion !== "" ? null :
</Typography> <Typography variant="body2" color="textSecondary" align="center" style={{marginTop: 10, }} >
Forms are in late Beta. Form submission data includes your Organization's unique ID while logged in, or a unique identifier for your browser otherwise. Your input will be automatically sanitized.
</Typography>
}
</div> </div>
// const isCorrectOrg = userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id // const isCorrectOrg = userdata.active_org.id === undefined || userdata.active_org.id === null || workflow.org_id === null || workflow.org_id === undefined || workflow.org_id.length === 0 || userdata.active_org.id === workflow.org_id
const loadedCheck = isLoaded ? const loadedCheck = isLoaded ?
<div> <div style={{marginTop: 30, }}>
{editWorkflowModalOpen === true ? {editWorkflowModalOpen === true ?
<EditWorkflow <EditWorkflow
saveWorkflow={saveWorkflow} saveWorkflow={saveWorkflow}
@@ -1350,6 +1496,8 @@ const RunWorkflow = (defaultprops) => {
expanded={true} expanded={true}
setRealtimeMarkdown={setRealtimeMarkdown} setRealtimeMarkdown={setRealtimeMarkdown}
boxWidth={boxWidth}
setBoxWidth={setBoxWidth}
scrollTo={"form_fill"} scrollTo={"form_fill"}
/> />
: null} : null}
@@ -1367,7 +1515,7 @@ const RunWorkflow = (defaultprops) => {
minHeight: 400, minHeight: 400,
maxHeight: 400, maxHeight: 400,
padding: 25, padding: 25,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550, //maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
}, },
@@ -1407,7 +1555,7 @@ const RunWorkflow = (defaultprops) => {
> >
Organization only Organization only
</MenuItem> </MenuItem>
<Divider /> <Divider />
<MenuItem <MenuItem
value={"form"} value={"form"}
> >
@@ -1418,7 +1566,7 @@ const RunWorkflow = (defaultprops) => {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{isLoggedIn && userdata?.active_org?.id === workflow?.org_id ? {isLoggedIn && userdata?.active_org?.id === workflow?.org_id || userdata?.support === true ?
<div style={{position: "fixed", top: 10, right: 20, }}> <div style={{position: "fixed", top: 10, right: 20, }}>
<Button <Button
@@ -1462,7 +1610,7 @@ const RunWorkflow = (defaultprops) => {
setEditWorkflowModalOpen(true) setEditWorkflowModalOpen(true)
}} }}
> >
Edit Details <EditIcon style={{marginRight: 5, }} /> Edit Form
</Button> </Button>
</div> </div>
: null} : null}
@@ -1476,7 +1624,8 @@ const RunWorkflow = (defaultprops) => {
// Check width // Check width
const overlap = window !== undefined && window.innerWidth !== undefined && window.innerWidth < 1300 const overlap = window !== undefined && window.innerWidth !== undefined && window.innerWidth < 1300
const formSidebar = !isLoaded || overlap || !(forms !== undefined && forms !== null && forms.length > 1) ? null :
const formSidebar = explorerUi === true || !isLoaded || overlap || !(forms !== undefined && forms !== null && forms.length > 1) ? null :
<div style={{ <div style={{
minWidth: 215, minWidth: 215,
maxWidth: 215, maxWidth: 215,
@@ -1485,10 +1634,10 @@ const RunWorkflow = (defaultprops) => {
minHeight: 500, minHeight: 500,
maxHeight: 500, maxHeight: 500,
position: "absolute", position: "absolute",
left: 100, left: 150,
top: 0, top: 0,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
padding: 25, padding: 25,
@@ -1497,7 +1646,7 @@ const RunWorkflow = (defaultprops) => {
}}> }}>
{selectedOrganization !== undefined && selectedOrganization !== null ? {selectedOrganization !== undefined && selectedOrganization !== null ?
<div style={{display: "flex", marginBottom: 20, }}> <div style={{display: "flex", marginBottom: 20, }}>
<img src={selectedOrganization.image} style={{width: 40, height: 40, borderRadius: theme.palette.borderRadius, }} /> <img src={selectedOrganization.image} style={{width: 40, height: 40, borderRadius: theme.palette?.borderRadius, }} />
<Typography variant="body1" style={{marginTop: 7, marginLeft: 10, }}> <Typography variant="body1" style={{marginTop: 7, marginLeft: 10, }}>
{selectedOrganization.name} {selectedOrganization.name}
</Typography> </Typography>
@@ -1506,41 +1655,7 @@ const RunWorkflow = (defaultprops) => {
: null} : null}
{forms !== undefined && forms !== null && forms.length > 0 ? {forms !== undefined && forms !== null && forms.length > 0 ?
<div> <FormList />
{forms.map((form, formIndex) => {
if (form.id === undefined || form.id === null) {
return null
}
return (
<div key={formIndex} style={{marginBottom: 10, }}>
<RecentWorkflow
workflow={form}
onclickHandler={() => {
navigate(`/forms/${form.id}`)
getWorkflow(form.id, sourceNode)
}}
currentWorkflowId={workflow.id}
/>
{/*
<Button
variant="outlined"
color="primary"
fullWidth
style={{textTransform: "none", }}
onClick={() => {
navigate(`/forms/${form.id}`)
getWorkflow(form.id, sourceNode)
}}
>
{form.name}
</Button>
*/}
</div>
)
})}
</div>
: :
<div> <div>
No forms loaded No forms loaded
@@ -1557,4 +1672,4 @@ const RunWorkflow = (defaultprops) => {
) )
} }
export default RunWorkflow; export default RunWorkflow
+8 -8
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from "react"; import React, { useState, useEffect, useMemo, useContext } from "react";
import theme from "../theme.jsx"; import theme from "../theme.jsx";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
@@ -11,6 +11,7 @@ import { Tabs, Tab, setRef } from "@mui/material";
import { styled } from "@mui/material/styles"; import { styled } from "@mui/material/styles";
import { makeStyles } from '@mui/styles'; import { makeStyles } from '@mui/styles';
import DiscordChat from "../components/DiscordChat.jsx"; import DiscordChat from "../components/DiscordChat.jsx";
import { Context } from "../context/ContextApi.jsx";
import { import {
Apps as AppsIcon, Apps as AppsIcon,
@@ -21,16 +22,14 @@ import {
DescriptionOutlined as DescriptionOutlinedIcon, DescriptionOutlined as DescriptionOutlinedIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { import {Typography} from "@mui/material";
Typography
} from "@mui/material"
// Should be different if logged in :| // Should be different if logged in :|
const Search = (props) => { const Search = (props) => {
const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader } = const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader } =
props; props;
let navigate = useNavigate(); let navigate = useNavigate();
const { leftSideBarOpenByClick } = useContext(Context);
const [curTab, setCurTab] = useState(0); const [curTab, setCurTab] = useState(0);
const iconStyle = { marginRight: isHeader ? null : 10 }; const iconStyle = { marginRight: isHeader ? null : 10 };
@@ -124,14 +123,15 @@ const Search = (props) => {
flex: "1", flex: "1",
marginLeft: isHeader ? null : 10, marginLeft: isHeader ? null : 10,
marginRight: isHeader ? null : 10, marginRight: isHeader ? null : 10,
paddingLeft: isHeader ? null : 30,
paddingRight: isHeader ? null : 30, paddingRight: isHeader ? null : 30,
paddingBottom: isHeader ? null : 30, paddingBottom: isHeader ? null : 30,
paddingTop: hidemargins === true ? 0 : isHeader ? null : 30, paddingTop: hidemargins === true ? 0 : isHeader ? null : 30,
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
overflowX: "hidden", overflowX: "hidden",
minHeight: 400 minHeight: 400,
paddingLeft: leftSideBarOpenByClick ? 250 : 0,
transition: "padding-left 0.3s ease",
}; };
const views = { const views = {
@@ -223,7 +223,7 @@ const Search = (props) => {
margin: isHeader ? null : "auto", margin: isHeader ? null : "auto",
marginTop: hidemargins === true ? 0 : isHeader ? null : 25, marginTop: hidemargins === true ? 0 : isHeader ? null : 25,
backgroundColor: "rgba(33, 33, 33, 1)", backgroundColor: "rgba(33, 33, 33, 1)",
borderRadius: 8 borderRadius: 8,
}} }}
value={curTab} value={curTab}
indicatorColor="primary" indicatorColor="primary"
+1 -1
View File
@@ -302,7 +302,7 @@ const SetAuthentication = (props) => {
} }
return ( return (
<div style={{ padding: 50, border: failed === true ? `1px solid ${red}` : "1px solid rgba(255,255,255,0.6)", borderRadius: theme.palette.borderRadius, width: 500, margin: "auto", marginTop: 50, itemAlign: "center", textAlign: "center",}}> <div style={{ padding: 50, border: failed === true ? `1px solid ${red}` : "1px solid rgba(255,255,255,0.6)", borderRadius: theme.palette?.borderRadius, width: 500, margin: "auto", marginTop: 50, itemAlign: "center", textAlign: "center",}}>
<Typography <Typography
variant="h4" variant="h4"
style={{ marginLeft: "auto", marginRight: "auto", marginTop: 50}} style={{ marginLeft: "auto", marginRight: "auto", marginTop: 50}}
+5 -5
View File
@@ -627,7 +627,7 @@ const Settings = (props) => {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
padding: "0px 0px 12px 0px", padding: "0px 0px 12px 0px",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}; };
const currentOwner = checkOwner(data, userdata); const currentOwner = checkOwner(data, userdata);
@@ -636,7 +636,7 @@ const Settings = (props) => {
} }
return ( return (
<Grid item xs={4} style={{ borderRadius: theme.palette.borderRadius }}> <Grid item xs={4} style={{ borderRadius: theme.palette?.borderRadius }}>
<Paper style={innerPaperStyle}> <Paper style={innerPaperStyle}>
<img <img
src={data.image} src={data.image}
@@ -692,7 +692,7 @@ const Settings = (props) => {
); );
const landingpageData = ( const landingpageData = (
<div style={{ display: "flex", marginTop: 120 }}> <div style={{ display: "flex", paddingTop: 120 }}>
<Paper style={boxStyle}> <Paper style={boxStyle}>
{imageInfo} {imageInfo}
<h2>Settings</h2> <h2>Settings</h2>
@@ -1065,7 +1065,7 @@ const Settings = (props) => {
<Paper <Paper
square square
style={{ style={{
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
padding: 50, padding: 50,
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
}} }}
@@ -1100,7 +1100,7 @@ const Settings = (props) => {
<Paper <Paper
square square
style={{ style={{
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
padding: 50, padding: 50,
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
}} }}
+8 -8
View File
@@ -441,7 +441,7 @@ const UsecaseListComponent = (props) => {
navigate(`/usecases?selected_object=${fixedName}`) navigate(`/usecases?selected_object=${fixedName}`)
} }
}}> }}>
<Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor, borderRadius: theme.palette.borderRadius, }} onClick={() => { <Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor, borderRadius: theme.palette?.borderRadius, }} onClick={() => {
}}> }}>
{!selectedItem ? {!selectedItem ?
<div style={{textAlign: "left", position: "relative",}}> <div style={{textAlign: "left", position: "relative",}}>
@@ -799,7 +799,7 @@ const UsecaseListComponent = (props) => {
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
height: 50, height: 50,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
onChange={(event, newValue) => { onChange={(event, newValue) => {
//handleWorkflowSelectionUpdate({ target: { value: newValue} }) //handleWorkflowSelectionUpdate({ target: { value: newValue} })
@@ -856,7 +856,7 @@ const UsecaseListComponent = (props) => {
<Tooltip arrow placement="left" title={ <Tooltip arrow placement="left" title={
<span style={{}}> <span style={{}}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} /> <img src={data.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
: null} : null}
<Typography> <Typography>
Choose {newname} Choose {newname}
@@ -881,7 +881,7 @@ const UsecaseListComponent = (props) => {
<TextField <TextField
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
}} }}
{...params} {...params}
label="Find your workflows" label="Find your workflows"
@@ -952,8 +952,8 @@ const UsecaseListComponent = (props) => {
target="_blank" target="_blank"
style={{ textDecoration: "none", color: "rgba(255,255,255,0.7)", marginRight: 5, }} style={{ textDecoration: "none", color: "rgba(255,255,255,0.7)", marginRight: 5, }}
> >
<div style={{width: 160, display: "flex", borderRadius: theme.palette.borderRadius, cursor: "pointer", border: highlight ? "2px solid #f86a3e" : "1px solid rgba(255,255,255,0.7)", backgroundColor: theme.palette.inputColor, padding: "0px 0px 15px 15px", overflow: "hidden",}}> <div style={{width: 160, display: "flex", borderRadius: theme.palette?.borderRadius, cursor: "pointer", border: highlight ? "2px solid #f86a3e" : "1px solid rgba(255,255,255,0.7)", backgroundColor: theme.palette.inputColor, padding: "0px 0px 15px 15px", overflow: "hidden",}}>
<img src={subdata.image} style={{width: 40, height: 40, borderRadius: theme.palette.borderRadius, marginTop: 15, }} /> <img src={subdata.image} style={{width: 40, height: 40, borderRadius: theme.palette?.borderRadius, marginTop: 15, }} />
<Typography variant="body1" style={{lineHeight: "95%", marginLeft: 12, marginTop: marginTop === 0 ? 19 : 25, maxHeight: 34, }}> <Typography variant="body1" style={{lineHeight: "95%", marginLeft: 12, marginTop: marginTop === 0 ? 19 : 25, maxHeight: 34, }}>
{subdata.name} {subdata.name}
</Typography> </Typography>
@@ -983,7 +983,7 @@ const UsecaseListComponent = (props) => {
height: 350, height: 350,
width: isMobile? null:350, width: isMobile? null:350,
marginTop: isMobile ? 50:null, marginTop: isMobile ? 50:null,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
padding: 5, padding: 5,
backgroundColor: theme.palette.backgroundColor, backgroundColor: theme.palette.backgroundColor,
@@ -1113,7 +1113,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
}} }}
content={(data, color) => { content={(data, color) => {
return ( return (
<div style={{borderRadius: theme.palette.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}> <div style={{borderRadius: theme.palette?.borderRadius, backgroundColor: theme.palette.inputColor, border: "1px solid rgba(255,255,255,0.3)", color: "white", padding: 5, cursor: "pointer",}}>
<Typography variant="body1"> <Typography variant="body1">
{data.x} {data.x}
</Typography> </Typography>
+6 -6
View File
@@ -304,14 +304,14 @@ const Welcome = (props) => {
minWidth: isMobile ? null : 275, minWidth: isMobile ? null : 275,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
color: "white", color: "white",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
} }
const actionObject = { const actionObject = {
padding: "25px", padding: "25px",
maxHeight: 280, maxHeight: 280,
minHeight: 280, minHeight: 280,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
} }
const imageStyle = { const imageStyle = {
@@ -340,7 +340,7 @@ const Welcome = (props) => {
const defaultImage = "/images/experienced.png" const defaultImage = "/images/experienced.png"
const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage
return ( return (
<div style={{ margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}> <div style={{ margin: "auto", paddingBottom: 150, minHeight: 1500, paddingTop: 50, }}>
{/* {/*
<div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}> <div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}>
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} /> <img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
@@ -354,7 +354,7 @@ const Welcome = (props) => {
color="primary" color="primary"
style={{ style={{
backgroundColor: theme.palette.platformColor, backgroundColor: theme.palette.platformColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
padding: 12, padding: 12,
border: "1px solid rgba(255,255,255,0.3)", border: "1px solid rgba(255,255,255,0.3)",
maxWidth: 500, maxWidth: 500,
@@ -434,7 +434,7 @@ const Welcome = (props) => {
We will use this information to personalize your automation We will use this information to personalize your automation
</Typography> */} </Typography> */}
<div style={{display: isMobile ? null : "flex", marginTop: 70, width: isMobile ? 280 : 700, margin: "auto",}}> <div style={{display: isMobile ? null : "flex", marginTop: 70, width: isMobile ? 280 : 700, margin: "auto",}}>
<div style={{border: "2px solid #806BFF", borderRadius: theme.palette.borderRadius, }}> <div style={{border: "2px solid #806BFF", borderRadius: theme.palette?.borderRadius, }}>
<Card style={paperObject} onClick={() => { <Card style={paperObject} onClick={() => {
if (isCloud) { if (isCloud) {
ReactGA.event({ ReactGA.event({
@@ -478,7 +478,7 @@ const Welcome = (props) => {
navigate("/workflows?message=Skipped intro") navigate("/workflows?message=Skipped intro")
}}> }}>
<CardActionArea style={actionObject}> <CardActionArea style={actionObject}>
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius*2, marginBottom: experienced_image === defaultImage ? 24 : 2 }} /> <img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette?.borderRadius*2, marginBottom: experienced_image === defaultImage ? 24 : 2 }} />
<Typography variant="h4" style={{color: "#F1F1F1"}}> <Typography variant="h4" style={{color: "#F1F1F1"}}>
Experienced Experienced
</Typography> </Typography>
+126 -107
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useContext } from "react"; import React, { useEffect, useContext, memo } from "react";
import ReactDOM from "react-dom" import ReactDOM from "react-dom"
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";
@@ -6,7 +6,7 @@ import { Navigate } from "react-router-dom";
import SecurityFramework from '../components/SecurityFramework.jsx'; import SecurityFramework from '../components/SecurityFramework.jsx';
import EditWorkflow from "../components/EditWorkflow.jsx" import EditWorkflow from "../components/EditWorkflow.jsx"
import Priority from "../components/Priority.jsx"; import Priority from "../components/Priority.jsx";
import { Context } from "../context/ContextApi.jsx";
import { isMobile } from "react-device-detect" import { isMobile } from "react-device-detect"
import { import {
@@ -92,37 +92,40 @@ import theme from "../theme.jsx";
const svgSize = 24; const svgSize = 24;
const imagesize = 22; const imagesize = 22;
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles(() => {
datagrid: {
border: 0, return {
"& .MuiDataGrid-columnsContainer": { datagrid: {
backgroundColor: border: 0,
theme.palette.type === "light" ? "#fafafa" : theme.palette.inputColor, "& .MuiDataGrid-columnsContainer": {
}, backgroundColor:
"& .MuiDataGrid-iconSeparator": { theme?.palette?.type === "light" ? "#fafafa" : theme?.palette?.inputColor,
display: "none", },
}, "& .MuiDataGrid-iconSeparator": {
"& .MuiDataGrid-colCell, .MuiDataGrid-cell": { display: "none",
borderRight: `1px solid ${ },
theme.palette.type === "light" ? "white" : "#303030" "& .MuiDataGrid-colCell, .MuiDataGrid-cell": {
}`, borderRight: `1px solid ${
}, theme?.palette?.type === "light" ? "white" : "#303030"
"& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": { }`,
borderBottom: `1px solid ${ },
theme.palette.type === "light" ? "#f0f0f0" : "#303030" "& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": {
}`, borderBottom: `1px solid ${
}, theme?.palette?.type === "light" ? "#f0f0f0" : "#303030"
"& .MuiDataGrid-cell": { }`,
color: },
theme.palette.type === "light" ? "white" : "rgba(255,255,255,0.65)", "& .MuiDataGrid-cell": {
}, color:
"& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption": theme?.palette?.type === "light" ? "white" : "rgba(255,255,255,0.65)",
{ },
borderRadius: 0, "& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption":
color: "white", {
}, borderRadius: 0,
}, color: "white",
})); },
},
}
})
// Takes an action in Shuffle and // Takes an action in Shuffle and
// Returns information about the icon, the color etc to be used // Returns information about the icon, the color etc to be used
@@ -481,9 +484,9 @@ export const validateJson = (showResult) => {
}; };
} }
} catch (e) { } catch (e) {
showResult = showResult.split("'").join('"');
try { try {
showResult = showResult.split("'").join('"');
if (!showResult.includes("{") && !showResult.includes("[")) { if (!showResult.includes("{") && !showResult.includes("[")) {
jsonvalid = false; jsonvalid = false;
} }
@@ -590,13 +593,37 @@ export const validateJson = (showResult) => {
}; };
}; };
//Custom hook for handling styling of the dropzone
const useDropzoneStyles = () => {
const { leftSideBarOpenByClick } = useContext(Context);
return {
maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
margin: "auto",
padding: 20,
paddingLeft: leftSideBarOpenByClick ? 200 : 0,
transition: "padding-left 0.3s ease",
};
};
//Wrapper for the dropzone component
const DropzoneWrapper = memo(({ onDrop, WorkflowView }) => {
const dropzoneStyles = useDropzoneStyles();
return (
<Dropzone style={dropzoneStyles} onDrop={onDrop}>
<WorkflowView />
</Dropzone>
);
});
const Workflows = (props) => { const Workflows = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props; const { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
document.title = "Shuffle - Workflows"; document.title = "Shuffle - Workflows";
let navigate = useNavigate(); let navigate = useNavigate();
const classes = useStyles(theme); const classes = useStyles(theme)
const imgSize = 60; const imgSize = 60;
const referenceUrl = globalUrl + "/api/v1/hooks/"; const referenceUrl = globalUrl + "/api/v1/hooks/";
@@ -702,7 +729,7 @@ const Workflows = (props) => {
console.log("Using filters: ", filters) console.log("Using filters: ", filters)
if (filters.length === 0) { if (filters.length === 0) {
setFilteredWorkflows(workflows); setFilteredWorkflows(workflows);
handleKeysetting(allUsecases, workflows) handleKeysetting(allUsecases, workflows)
return; return;
} }
@@ -778,9 +805,9 @@ const Workflows = (props) => {
} }
} }
console.log("Changing workflow filter, and finding new usecase mappings!") console.log("Changing workflow filter, and finding new usecase mappings!")
if (newWorkflows.length !== workflows.length) { if (newWorkflows.length !== workflows.length) {
handleKeysetting(allUsecases, newWorkflows) handleKeysetting(allUsecases, newWorkflows)
setFilteredWorkflows(newWorkflows); setFilteredWorkflows(newWorkflows);
} }
@@ -1194,8 +1221,8 @@ const Workflows = (props) => {
return response.json(); return response.json();
}) })
.then((responseJson) => { .then((responseJson) => {
if (responseJson !== undefined) {
if (responseJson !== undefined) {
var newarray = [] var newarray = []
for (var wfkey in responseJson) { for (var wfkey in responseJson) {
const wf = responseJson[wfkey] const wf = responseJson[wfkey]
@@ -1228,12 +1255,10 @@ const Workflows = (props) => {
} }
} }
if (newarray.length > 0) { try {
try { localStorage.setItem("workflows", JSON.stringify(newarray))
localStorage.setItem("workflows", JSON.stringify(newarray)) } catch (e) {
} catch (e) { console.log("Failed to set workflows in localstorage: ", e)
console.log("Failed to set workflows in localstorage: ", e)
}
} }
// Ensures the zooming happens only once per load // Ensures the zooming happens only once per load
@@ -1285,7 +1310,41 @@ const Workflows = (props) => {
.catch((error) => { .catch((error) => {
toast(error.toString()); toast(error.toString());
}); });
}; }
const findMatches = (category, workflows) => {
category.matches = []
for (var subcategorykey in category.list) {
var subcategory = category.list[subcategorykey]
subcategory.matches = []
for (var workflowkey in workflows) {
const workflow = workflows[workflowkey]
if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) {
for (var usecasekey in workflow.usecase_ids) {
if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory.name.toLowerCase()) {
//console.log("Got match: ", workflow.usecase_ids[usecasekey])
category.matches.push({
"workflow": workflow.id,
"category": subcategory.name,
})
subcategory.matches.push(workflow.id)
break
}
}
}
if (subcategory.matches.length > 0) {
break
}
}
}
return category
}
const handleKeysetting = (categorydata, workflows) => { const handleKeysetting = (categorydata, workflows) => {
if (workflows !== undefined && workflows !== null) { if (workflows !== undefined && workflows !== null) {
@@ -1297,37 +1356,7 @@ const Workflows = (props) => {
continue continue
} }
category.matches = [] category = findMatches(category, workflows)
for (var subcategorykey in category.list) {
var subcategory = category.list[subcategorykey]
subcategory.matches = []
for (var workflowkey in workflows) {
const workflow = workflows[workflowkey]
if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) {
for (var usecasekey in workflow.usecase_ids) {
if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory.name.toLowerCase()) {
//console.log("Got match: ", workflow.usecase_ids[usecasekey])
category.matches.push({
"workflow": workflow.id,
"category": subcategory.name,
})
subcategory.matches.push(workflow.id)
break
}
}
}
if (subcategory.matches.length > 0) {
break
}
}
}
newcategories.push(category) newcategories.push(category)
} }
@@ -1440,7 +1469,7 @@ const Workflows = (props) => {
display: "flex", display: "flex",
boxSizing: "border-box", boxSizing: "border-box",
position: "relative", position: "relative",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
backgroundColor: theme.palette.surfaceColor, backgroundColor: theme.palette.surfaceColor,
}; };
@@ -2229,7 +2258,7 @@ const Workflows = (props) => {
} }
return ( return (
<div style={{width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "1px solid #40E0D0" : "inherit", borderRadius: theme.palette.borderRadius, }}> <div style={{width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "1px solid #40E0D0" : "inherit", borderRadius: theme.palette?.borderRadius, }}>
<Paper square style={paperAppStyle}> <Paper square style={paperAppStyle}>
{selectedCategory !== "" ? {selectedCategory !== "" ?
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom"> <Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
@@ -2277,7 +2306,7 @@ const Workflows = (props) => {
title={ title={
<div style={{width: "100%", minWidth: 250, maxWidth: 310, }}> <div style={{width: "100%", minWidth: 250, maxWidth: 310, }}>
{data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.image !== undefined && data.image !== null && data.image.length > 0 ?
<img src={data.image} alt={data.name} style={{backgroundColor: theme.palette.surfaceColor, maxWidth: 300, minWidth: 250, borderRadius: theme.palette.borderRadius, }} /> <img src={data.image} alt={data.name} style={{backgroundColor: theme.palette.surfaceColor, maxWidth: 300, minWidth: 250, borderRadius: theme.palette?.borderRadius, }} />
: null} : null}
<Typography> <Typography>
Edit '{data.name}' Edit '{data.name}'
@@ -2493,7 +2522,7 @@ const Workflows = (props) => {
{workflowMenuButtons} {workflowMenuButtons}
</div> </div>
) : null} ) : null}
{(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data.input_markdown !== undefined && data.input_markdown !== null && data.input_markdown !== "") ? {(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") ?
<Tooltip title="Edit Form" placement="top"> <Tooltip title="Edit Form" placement="top">
<div style={{position: "absolute", top: 45, right: 8, }}> <div style={{position: "absolute", top: 45, right: 8, }}>
<IconButton <IconButton
@@ -3061,9 +3090,6 @@ const Workflows = (props) => {
autoHeight autoHeight
density="standard" density="standard"
onSelectionModelChange={(newSelection) => { onSelectionModelChange={(newSelection) => {
//setSelectedWorkflows(newSelection.selectionModel);
console.log(newSelection)
setSelectedWorkflowIndexes(newSelection) setSelectedWorkflowIndexes(newSelection)
}} }}
selectionModel={selectedWorkflowIndexes} selectionModel={selectedWorkflowIndexes}
@@ -3210,7 +3236,6 @@ const Workflows = (props) => {
<em>None</em> <em>None</em>
</MenuItem> </MenuItem>
{usecases.map((usecase, index) => { {usecases.map((usecase, index) => {
//console.log(usecase)
return ( return (
<span key={index}> <span key={index}>
<ListSubheader <ListSubheader
@@ -3586,7 +3611,7 @@ const Workflows = (props) => {
// ); // );
// } // }
const WorkflowView = () => { const WorkflowView = memo(() => {
if (workflows.length === 0) { if (workflows.length === 0) {
} }
@@ -3660,9 +3685,14 @@ const Workflows = (props) => {
{!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ? {!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ?
<div style={{ display: "flex", }}> <div style={{ display: "flex", }}>
{usecases.map((usecase, index) => { {usecases.map((usecase, index) => {
//console.log(usecase) if (usecase.name === "5. Verify") {
return null
}
const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length/usecase.list.length*100) : 0 const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length/usecase.list.length*100) : 0
//console.log("Usecase Matches: ", usecase.matches, ", Percent: ", percentDone) if (percentDone === 0) {
usecase = findMatches(usecase, workflows)
}
return ( return (
<Paper <Paper
@@ -3671,7 +3701,7 @@ const Workflows = (props) => {
flex: 1, flex: 1,
backgroundImage: `linear-gradient(to right, ${usecase.color}, ${usecase.color} ${percentDone}%, transparent ${percentDone}%, transparent 100%)`, backgroundImage: `linear-gradient(to right, ${usecase.color}, ${usecase.color} ${percentDone}%, transparent ${percentDone}%, transparent 100%)`,
backgroundColor: filters.includes(usecase.name.toLowerCase()) ? null : theme.palette.surfaceColor, backgroundColor: filters.includes(usecase.name.toLowerCase()) ? null : theme.palette.surfaceColor,
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
marginRight: index === usecases.length-1 ? 0 : 10, marginRight: index === usecases.length-1 ? 0 : 10,
cursor: "pointer", cursor: "pointer",
border: `2px solid ${usecase.color}`, border: `2px solid ${usecase.color}`,
@@ -3692,7 +3722,7 @@ const Workflows = (props) => {
<Typography variant="body1" color="textPrimary" style={{flex: 4, }}> <Typography variant="body1" color="textPrimary" style={{flex: 4, }}>
{usecase.name} {usecase.name}
</Typography> </Typography>
<Typography variant="body2" color="textSecondary" style={{flex: 1, marginTop: 5,}}> <Typography variant="body2" color="textSecondary" style={{flex: 1, marginTop: 0,}}>
{usecase.matches.length}/{usecase.list.length} {usecase.matches.length}/{usecase.list.length}
</Typography> </Typography>
</span> </span>
@@ -3715,7 +3745,7 @@ const Workflows = (props) => {
minWidth: isMobile ? "100%" : 1024, minWidth: isMobile ? "100%" : 1024,
zIndex: 11, zIndex: 11,
border: "1px solid rgba(255,255,255,0.1)", border: "1px solid rgba(255,255,255,0.1)",
borderRadius: theme.palette.borderRadius, borderRadius: theme.palette?.borderRadius,
textAlign: "center", textAlign: "center",
overflow: "auto", overflow: "auto",
}} }}
@@ -3734,7 +3764,6 @@ const Workflows = (props) => {
} }
if (data.app_name.toLowerCase() === "integration framework") { if (data.app_name.toLowerCase() === "integration framework") {
console.log("Skipping: ", data.app_name)
return null return null
} }
@@ -3813,7 +3842,7 @@ const Workflows = (props) => {
) : null} ) : null}
{userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ? {userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && userdata.priorities[0].name.includes("CPU") && userdata.priorities[0].active === true ?
<div style={{border: "1px solid rgba(255,255,255,0.1)" , borderRadius: theme.palette.borderRadius, marginTop: 10, <div style={{border: "1px solid rgba(255,255,255,0.1)" , borderRadius: theme.palette?.borderRadius, marginTop: 10,
marginBottom: 10, padding: 15, textAlign: "center" , height: 70, textAlign: "left" , backgroundColor: marginBottom: 10, padding: 15, textAlign: "center" , height: 70, textAlign: "left" , backgroundColor:
theme.palette.surfaceColor, display: "flex" , maxHeight: "105px", minHeight: "110px"}} theme.palette.surfaceColor, display: "flex" , maxHeight: "105px", minHeight: "110px"}}
> >
@@ -3905,7 +3934,7 @@ const Workflows = (props) => {
</div> </div>
</div> </div>
); );
}; });
const importWorkflowsFromUrl = (url) => { const importWorkflowsFromUrl = (url) => {
console.log("IMPORT WORKFLOWS FROM ", downloadUrl); console.log("IMPORT WORKFLOWS FROM ", downloadUrl);
@@ -4173,7 +4202,7 @@ const Workflows = (props) => {
Setup progress: <b>{isNaN(percentDone) ? 0 : percentDone}%</b> Setup progress: <b>{isNaN(percentDone) ? 0 : percentDone}%</b>
</Typography> </Typography>
<LinearProgress color="primary" variant="determinate" value={percentDone} style={{marginTop: 5, height: 7, borderRadius: theme.palette.borderRadius, }} /> <LinearProgress color="primary" variant="determinate" value={percentDone} style={{marginTop: 5, height: 7, borderRadius: theme.palette?.borderRadius, }} />
<Typography variant="body2" style={{marginTop: 20, }}> <Typography variant="body2" style={{marginTop: 20, }}>
Follow these steps to get you up and running! Follow these steps to get you up and running!
@@ -4251,17 +4280,7 @@ const Workflows = (props) => {
<TourButton /> <TourButton />
</ShepherdTour> </ShepherdTour>
*/} */}
<Dropzone <DropzoneWrapper onDrop={uploadFile} WorkflowView={WorkflowView}/>
style={{
maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
margin: "auto",
padding: 20,
paddingLeft: userdata?.support ? 80 : 0
}}
onDrop={uploadFile}
>
<WorkflowView />
</Dropzone>
{/*modalView*/} {/*modalView*/}
{deleteModal} {deleteModal}
{exportVerifyModal} {exportVerifyModal}
@@ -4269,7 +4288,7 @@ const Workflows = (props) => {
{workflowDownloadModalOpen} {workflowDownloadModalOpen}
{/*!drawerOpen ? {/*!drawerOpen ?
<div style={{ position: "fixed", top: 64, right: -5, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius, }}> <div style={{ position: "fixed", top: 64, right: -5, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius, }}>
<Tooltip title={`Getting Started`} placement="bottom"> <Tooltip title={`Getting Started`} placement="bottom">
<IconButton onClick={() => { <IconButton onClick={() => {
setDrawerOpen(true) setDrawerOpen(true)
-1
View File
@@ -782,7 +782,6 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
// image on every Orborus/new worker restart. // image on every Orborus/new worker restart.
// Running as coroutine for eventual completeness // Running as coroutine for eventual completeness
//go shuffle.DownloadDockerImageBackend(&http.Client{}, image)
// FIXME: With goroutines it got too much trouble of deploying with an older version // FIXME: With goroutines it got too much trouble of deploying with an older version
// Allowing slow startups, as long as it's eventually fast, and uses the same registry as on host. // Allowing slow startups, as long as it's eventually fast, and uses the same registry as on host.
shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image) shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
-83
View File
@@ -1,83 +0,0 @@
#!/bin/bash
# Update and install dependencies
apt-get update
apt-get install -y docker.io docker-compose curl git
# Start and enable Docker
systemctl start docker
systemctl enable docker
# Clone Shuffle repository
git clone --branch 2.0.0 https://github.com/Shuffle/Shuffle.git
cd Shuffle
# Setup directories and permissions
mkdir shuffle-database && chmod -R 777 shuffle-database
# Start services
docker-compose up -d
# Wait for initial startup
echo "Waiting 30 seconds for initial startup..."
sleep 30
# Check for restarting containers
echo "Checking for restarting containers..."
ATTEMPTS=30
for i in $(seq 1 $ATTEMPTS); do
RESTARTING_CONTAINERS=$(docker ps --filter "status=restarting" --format "{{.Names}}")
if [ -n "$RESTARTING_CONTAINERS" ]; then
echo "The following containers are restarting:"
echo "$RESTARTING_CONTAINERS"
exit 1
fi
echo "No containers are restarting. Attempt $i/$ATTEMPTS."
sleep 1
done
echo "No containers were found in a restarting state after $ATTEMPTS checks."
# Check frontend response
echo "Checking frontend response..."
RESPONSE=$(curl -s http://localhost:3001)
if echo "$RESPONSE" | grep -q "Shuffle"; then
echo "The word 'Shuffle' was found in the response."
else
echo "The word 'Shuffle' was not found in the response."
exit 1
fi
# Register user
echo "Attempting to register user..."
MAX_RETRIES=30
RETRY_INTERVAL=10
CONTAINER_NAME="shuffle-backend"
for (( i=1; i<=$MAX_RETRIES; i++ ))
do
STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" 'http://localhost:3001/api/v1/register' \
-H 'Accept: */*' \
-H 'Accept-Language: en-US,en;q=0.9' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json' \
--data-raw '{"username":"demo@demo.io","password":"supercoolpassword"}')
if [ "$STATUS_CODE" -eq 200 ]; then
echo "User registration was successful with status code 200."
exit 0
elif [ "$STATUS_CODE" -ne 502 ]; then
echo "User registration failed with status code $STATUS_CODE."
exit 1
fi
echo "Received status code $STATUS_CODE. Retrying in $RETRY_INTERVAL seconds... ($i/$MAX_RETRIES)"
echo "Fetching last 30 lines of logs from container $CONTAINER_NAME..."
docker logs --tail 30 "$CONTAINER_NAME"
echo "Fetching last 30 lines of logs from container shuffle-opensearch..."
docker logs --tail 30 shuffle-opensearch
sleep $RETRY_INTERVAL
done
echo "User registration failed after $MAX_RETRIES attempts."
exit 1