feat: click to deploy to AWS test
This commit is contained in:
@@ -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
|
||||
@@ -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'
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
Shuffle Automation
|
||||
|
||||
<<<<<<< HEAD
|
||||
[](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml)
|
||||
[](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml)
|
||||
[](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">
|
||||
|
||||
[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.
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -1,22 +1,2 @@
|
||||
# app_sdk.py
|
||||
This is the SDK used for apps to behave like they should.
|
||||
|
||||
## 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.
|
||||
## CHANGES
|
||||
In November 2024, we moved this to its own repistory: https://github.com/shuffle/app_sdk
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
+7
-24
@@ -35,8 +35,8 @@ import (
|
||||
"github.com/go-git/go-billy/v5/memfs"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
gitProxy "github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
|
||||
// Random
|
||||
xj "github.com/basgys/goxml2json"
|
||||
@@ -257,7 +257,6 @@ type Hook struct {
|
||||
Environment string `json:"environment" datastore:"environment"`
|
||||
}
|
||||
|
||||
|
||||
func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{
|
||||
"id": "12345",
|
||||
@@ -414,13 +413,13 @@ func isGitNoProxy(rawURL string) bool {
|
||||
}
|
||||
host := parsedURL.Hostname()
|
||||
|
||||
for _,value := range noProxyList {
|
||||
for _, value := range noProxyList {
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
if host == value {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(value, "*.") && strings.HasSuffix(host, value[2:]){
|
||||
if strings.HasPrefix(value, "*.") && strings.HasSuffix(host, value[2:]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -428,7 +427,7 @@ func isGitNoProxy(rawURL string) bool {
|
||||
}
|
||||
|
||||
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{
|
||||
URL: os.Getenv("HTTP_PROXY"),
|
||||
}
|
||||
@@ -559,7 +558,6 @@ func createNewUser(username, password, role, apikey string, org shuffle.OrgMini)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -932,7 +930,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
|
||||
log.Printf("[DEBUG] Failed to get org during getinfo: %s", err)
|
||||
}
|
||||
|
||||
|
||||
//if err == nil {
|
||||
if len(org.Id) > 0 {
|
||||
if userInfo.Role == "" {
|
||||
@@ -1092,7 +1089,6 @@ type passwordReset struct {
|
||||
Reference string `json:"reference"`
|
||||
}
|
||||
|
||||
|
||||
func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := shuffle.HandleCors(resp, request)
|
||||
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)
|
||||
if len(user.Id) > 0 {
|
||||
resp.WriteHeader(200)
|
||||
@@ -3379,8 +3374,6 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
|
||||
buildSwaggerApp(resp, body, user, false)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Hotloads new apps from a folder
|
||||
func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error {
|
||||
|
||||
@@ -3727,7 +3720,6 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
||||
|
||||
// Check if it's 1 in 10 (10% chance random)
|
||||
@@ -3777,7 +3769,6 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
||||
backupJobData = []byte{}
|
||||
}
|
||||
|
||||
|
||||
syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl)
|
||||
client := shuffle.GetExternalClient(syncUrl)
|
||||
req, err := http.NewRequest(
|
||||
@@ -4340,7 +4331,6 @@ func runInitEs(ctx context.Context) {
|
||||
log.Printf("[INFO] Skipping download of extra API samples as %d were found", len(workflowapps))
|
||||
}
|
||||
|
||||
|
||||
if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" {
|
||||
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)
|
||||
@@ -4369,7 +4359,6 @@ func runInitEs(ctx context.Context) {
|
||||
log.Printf("[INFO] Finished INIT (ES)")
|
||||
}
|
||||
|
||||
|
||||
func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) {
|
||||
ctx := context.Background()
|
||||
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}`)))
|
||||
}
|
||||
|
||||
|
||||
|
||||
func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := shuffle.HandleCors(resp, request)
|
||||
if cors {
|
||||
@@ -5008,8 +4995,6 @@ func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
|
||||
resp.Write([]byte("OK"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
func initHandlers() {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
@@ -5056,7 +5041,7 @@ func initHandlers() {
|
||||
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/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/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("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/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
|
||||
|
||||
|
||||
// Docker orborus specific - downloads an image
|
||||
r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("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
|
||||
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/{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", shuffle.HandleGetSelectedRules).Methods("GET", "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")
|
||||
|
||||
// This is weird.
|
||||
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")
|
||||
|
||||
|
||||
// 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.HandleGetNotifications).Methods("GET", "OPTIONS")
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@uiw/codemirror-themes": "^4.21.9",
|
||||
"@uiw/react-codemirror": "^4.21.21",
|
||||
"algoliasearch": "^4.8.3",
|
||||
"chart.js": "^3.0.0",
|
||||
"class-transformer": "^0.2.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"cpx": "^1.5.0",
|
||||
@@ -60,7 +61,7 @@
|
||||
"react-alice-carousel": "^2.6.4",
|
||||
"react-avatar-editor": "^11.1.0",
|
||||
"react-beforeunload": "^2.2.1",
|
||||
"react-chartjs-2": "^5.2.0",
|
||||
"react-chartjs-2": "^5.0.0",
|
||||
"react-cookie": "^4.0.1",
|
||||
"react-cytoscapejs": "^2.0.0",
|
||||
"react-device-detect": "^2.2.3",
|
||||
|
||||
+19
-1
@@ -14,6 +14,7 @@ import HealthPage from "./components/HealthPage.jsx";
|
||||
//import Header from "./components/Header.jsx";
|
||||
import theme from "./theme";
|
||||
import Apps from "./views/Apps";
|
||||
import Apps2 from "./views/Apps2.jsx";
|
||||
import AppCreator from "./views/AppCreator";
|
||||
import DetectionDashBoard from "./views/DetectionDashboard.jsx";
|
||||
|
||||
@@ -48,7 +49,7 @@ import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
import Drift from "react-driftjs";
|
||||
|
||||
import { AppContext } from './context/contextApi.jsx';
|
||||
import { AppContext } from './context/ContextApi.jsx';
|
||||
|
||||
// Production - backend proxy forwarding in nginx
|
||||
var globalUrl = window.location.origin;
|
||||
@@ -408,6 +409,23 @@ const App = (message, 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
|
||||
exact
|
||||
|
||||
@@ -1972,14 +1972,14 @@ const AppFramework = (props) => {
|
||||
{data.name}
|
||||
</Typography>
|
||||
<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}
|
||||
{parsedLeftText}
|
||||
</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}
|
||||
</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}
|
||||
{parsedRightText}
|
||||
</div>
|
||||
|
||||
@@ -464,8 +464,17 @@ const AppGrid = (props) => {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
id={`image_${index}`}
|
||||
alt={data.name}
|
||||
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={{
|
||||
width: 80,
|
||||
height: 80,
|
||||
@@ -995,7 +1004,7 @@ const AppGrid = (props) => {
|
||||
}}
|
||||
autoComplete="off"
|
||||
color="primary"
|
||||
placeholder="Search your Activated or Self-built apps"
|
||||
placeholder="Search your Activated or self-built apps"
|
||||
id="shuffle_search_field"
|
||||
onChange={(event) => {
|
||||
setSearchQuery(event.currentTarget.value);
|
||||
|
||||
@@ -47,6 +47,7 @@ const AppSelection = props => {
|
||||
defaultSearch,
|
||||
setDefaultSearch,
|
||||
checkLogin,
|
||||
isAppPage=false
|
||||
|
||||
} = props;
|
||||
const [discoveryData, setDiscoveryData] = React.useState({})
|
||||
@@ -507,11 +508,13 @@ const AppSelection = props => {
|
||||
})}
|
||||
</Grid>
|
||||
</div>
|
||||
{
|
||||
!isAppPage && (
|
||||
<>
|
||||
{!moreButton ? (
|
||||
<div style={{ width: "100%", marginLeft: isMobile ? 80 : 200, marginBottom: 20, textAlign: isMobile ? "center" : null }}>
|
||||
<Link style={{ color: "#FF8444" }} onClick={() => {
|
||||
setMoreButton(true)
|
||||
|
||||
setTimeout(() => {
|
||||
navigate("/welcome?tab=2")
|
||||
}, 250)
|
||||
@@ -527,6 +530,9 @@ const AppSelection = props => {
|
||||
See usecases
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Fade>
|
||||
)
|
||||
|
||||
@@ -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 rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
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 [message, setMessage] = React.useState("");
|
||||
const [formMessage, setFormMessage] = React.useState("");
|
||||
|
||||
@@ -45,21 +45,21 @@ const AuthenticationItem = (props) => {
|
||||
data.fields = [
|
||||
{
|
||||
key: "url",
|
||||
value: "Secret. Replaced during app execution!",
|
||||
value: "URL Secret. Replaced during runtime",
|
||||
},
|
||||
{
|
||||
key: "client_id",
|
||||
value: "Secret. Replaced during app execution!",
|
||||
value: "ClientID Secret. Replaced during runtime.",
|
||||
},
|
||||
{
|
||||
key: "client_secret",
|
||||
value: "Secret. Replaced during app execution!",
|
||||
value: "Client Secret. Replaced during runtime.",
|
||||
},
|
||||
{
|
||||
key: "scope",
|
||||
value: "Secret. Replaced during app execution!",
|
||||
value: "Scope Secret. Replaced during runtime.",
|
||||
},
|
||||
];
|
||||
]
|
||||
}
|
||||
|
||||
const deleteAuthentication = (data) => {
|
||||
@@ -152,7 +152,7 @@ const AuthenticationItem = (props) => {
|
||||
src={data.app.large_image}
|
||||
style={{
|
||||
maxWidth: 50,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
/>
|
||||
style={{ minWidth: 75, maxWidth: 75 }}
|
||||
|
||||
@@ -222,7 +222,7 @@ const AuthenticationData = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -304,7 +304,7 @@ const AuthenticationData = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
|
||||
@@ -294,7 +294,7 @@ const AuthenticationData = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -328,10 +328,10 @@ const AuthenticationData = (props) => {
|
||||
|
||||
const authenticationButtons = <span>
|
||||
<Button
|
||||
style={{ borderRadius: theme.palette.borderRadius, marginTop: authFieldsOnly ? 20 : 0 }}
|
||||
style={{ borderRadius: theme.palette?.borderRadius, marginTop: authFieldsOnly ? 20 : 0 }}
|
||||
onClick={() => {
|
||||
setAuthenticationOptions(authenticationOption);
|
||||
handleSubmitCheck();
|
||||
setAuthenticationOptions(authenticationOption)
|
||||
handleSubmitCheck()
|
||||
}}
|
||||
variant={"contained"}
|
||||
disabled={submitSuccessful}
|
||||
@@ -437,7 +437,7 @@ const AuthenticationData = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
|
||||
@@ -179,7 +179,7 @@ const Billing = (props) => {
|
||||
height: 480,
|
||||
// width: "100%",
|
||||
backgroundColor: theme.palette.platformColor,
|
||||
borderRadius: theme.palette.borderRadius * 2,
|
||||
borderRadius: theme.palette?.borderRadius * 2,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
marginRight: 10,
|
||||
marginTop: 15,
|
||||
@@ -583,7 +583,7 @@ const Billing = (props) => {
|
||||
margin: "auto",
|
||||
width: 100,
|
||||
backgroundColor: "white",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
/>
|
||||
: null}
|
||||
@@ -653,7 +653,7 @@ const Billing = (props) => {
|
||||
value={feature.split("Worker License: ")[1]}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
id={fieldId}
|
||||
onClick={() => { }}
|
||||
@@ -1041,7 +1041,7 @@ const Billing = (props) => {
|
||||
width: 340,
|
||||
height: 480,
|
||||
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)",
|
||||
marginRight: 10,
|
||||
marginTop: 15,
|
||||
@@ -1315,7 +1315,7 @@ const Billing = (props) => {
|
||||
// maxWidth: 400,
|
||||
width: 340,
|
||||
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)",
|
||||
marginRight: 10,
|
||||
marginTop: 15,
|
||||
|
||||
@@ -43,7 +43,7 @@ const LineChartWrapper = ({keys, inputname, height, width}) => {
|
||||
const inputdata = keys.data === undefined ? keys : keys.data
|
||||
|
||||
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, }}>
|
||||
{inputname}
|
||||
</Typography>
|
||||
|
||||
@@ -147,12 +147,12 @@ const ConfigureWorkflow = (props) => {
|
||||
}
|
||||
|
||||
if (apps === undefined || apps === null) {
|
||||
console.log("Apps is undefined or null: ", apps)
|
||||
//console.log("Apps is undefined or null: ", apps)
|
||||
return 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
|
||||
}
|
||||
|
||||
@@ -427,7 +427,6 @@ const ConfigureWorkflow = (props) => {
|
||||
trigger.index = key;
|
||||
|
||||
if (trigger.trigger_type === "WEBHOOK") {
|
||||
console.log("Found webhook: ", trigger)
|
||||
|
||||
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
|
||||
const findapp = trigger.app_association.name.toLowerCase()
|
||||
@@ -599,7 +598,7 @@ const ConfigureWorkflow = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: <InputAdornment position="end"></InputAdornment>,
|
||||
@@ -800,7 +799,7 @@ const ConfigureWorkflow = (props) => {
|
||||
>
|
||||
<div
|
||||
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"
|
||||
>
|
||||
@@ -997,7 +996,7 @@ const ConfigureWorkflow = (props) => {
|
||||
justifyContent: "flex-start",
|
||||
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
|
||||
color: action.auth_done ? "#686a6c" : "#ffffff",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
minWidth: 350,
|
||||
maxHeight: 50,
|
||||
overflow: "hidden",
|
||||
@@ -1037,7 +1036,7 @@ const ConfigureWorkflow = (props) => {
|
||||
>
|
||||
<img
|
||||
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}
|
||||
/>
|
||||
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
|
||||
@@ -1057,7 +1056,7 @@ const ConfigureWorkflow = (props) => {
|
||||
justifyContent: "flex-start",
|
||||
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
|
||||
color: action.auth_done ? "#686a6c" : "#ffffff",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
minWidth: 350,
|
||||
maxHeight: 50,
|
||||
overflow: "hidden",
|
||||
@@ -1088,7 +1087,7 @@ const ConfigureWorkflow = (props) => {
|
||||
>
|
||||
<img
|
||||
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}
|
||||
/>
|
||||
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
|
||||
@@ -1108,7 +1107,7 @@ const ConfigureWorkflow = (props) => {
|
||||
justifyContent: "flex-start",
|
||||
backgroundColor: action.auth_done ? theme.palette.surfaceColor : theme.palette.inputColor,
|
||||
color: action.auth_done ? "#686a6c" : "#ffffff",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
minWidth: 350,
|
||||
maxHeight: 50,
|
||||
overflow: "hidden",
|
||||
@@ -1123,7 +1122,7 @@ const ConfigureWorkflow = (props) => {
|
||||
>
|
||||
<img
|
||||
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}
|
||||
/>
|
||||
<Typography style={{ margin: 0, marginLeft: 10 }} variant="body1">
|
||||
@@ -1275,7 +1274,7 @@ const ConfigureWorkflow = (props) => {
|
||||
const [finishCount, setFinishCount] = useState(0)
|
||||
|
||||
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, }}
|
||||
onClick={() => {
|
||||
|
||||
@@ -100,7 +100,7 @@ const Detection = (props) => {
|
||||
width: "100%",
|
||||
padding: 50,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
||||
@@ -10,10 +10,12 @@ import {
|
||||
Paper,
|
||||
Divider,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
OpenInNew as OpenInNewIcon,
|
||||
FmdGood as FmdGoodIcon,
|
||||
} from "@mui/icons-material"
|
||||
|
||||
import { toast } from "react-toastify";
|
||||
@@ -68,6 +70,7 @@ const DetectionExplorer = (props) => {
|
||||
const [detectionWorkflowId, setDetectionWorkflowId] = useState("")
|
||||
const [isDetectionValid, setIsDetectionValid] = useState(false)
|
||||
const [availableDetection, setAvailableDetection] = React.useState([]);
|
||||
const [environmentList, setEnvironmentList] = React.useState([])
|
||||
|
||||
const loadUsecases = () => {
|
||||
const url = `${globalUrl}/api/v1/workflows/usecases`
|
||||
@@ -146,7 +149,7 @@ const DetectionExplorer = (props) => {
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect`;
|
||||
const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect`
|
||||
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
@@ -177,7 +180,11 @@ const DetectionExplorer = (props) => {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||
toast(responseJson.reason)
|
||||
} 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) {
|
||||
@@ -193,16 +200,52 @@ const DetectionExplorer = (props) => {
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
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(() => {
|
||||
loadUsecases()
|
||||
loadEnvironments()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (detectionInfo === undefined || detectionInfo === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (detectionInfo.category === undefined || detectionInfo.category === null || detectionInfo.category === "") {
|
||||
return
|
||||
}
|
||||
|
||||
handleConnectClick()
|
||||
}, [detectionInfo])
|
||||
|
||||
@@ -211,6 +254,8 @@ const DetectionExplorer = (props) => {
|
||||
rule.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
|
||||
const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.data_lake?.enabled === true).length : 0
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Paper
|
||||
@@ -219,7 +264,7 @@ const DetectionExplorer = (props) => {
|
||||
width: "100%",
|
||||
padding: 50,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -234,6 +279,7 @@ const DetectionExplorer = (props) => {
|
||||
{detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rules)`}
|
||||
</Typography>
|
||||
|
||||
<div style={{display: "flex", }}>
|
||||
{workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
|
||||
<div style={{display: "flex", }}>
|
||||
<div style={{minWidth: 400, maxWidth: 400, }}>
|
||||
@@ -282,6 +328,16 @@ const DetectionExplorer = (props) => {
|
||||
isDetectionValid ? `Connected to ${detectionInfo?.category}` : `Fix ${detectionInfo?.category} connection`}
|
||||
</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>
|
||||
{filteredRules?.length > 0 ?
|
||||
<Box
|
||||
|
||||
@@ -98,7 +98,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
||||
|
||||
return (
|
||||
<Card style={{
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
minHeight: 100,
|
||||
marginBottom: 10,
|
||||
paddingBottom: 0,
|
||||
@@ -145,7 +145,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
height: 40,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
@@ -197,7 +197,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
||||
overflow: 'visible',
|
||||
zIndex: 10,
|
||||
//border: "1px solid rgba(255,255,255,0.3)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginTop: 5,
|
||||
|
||||
minHeight: 40,
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
RadioGroup,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Slider,
|
||||
|
||||
} from "@mui/material";
|
||||
|
||||
@@ -65,7 +66,7 @@ import {
|
||||
} from "@mui/icons-material";
|
||||
|
||||
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
|
||||
|
||||
@@ -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 [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 [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();
|
||||
|
||||
useEffect(() => {
|
||||
if (setBoxWidth !== undefined && boxWidth !== formWidth) {
|
||||
setBoxWidth(formWidth)
|
||||
}
|
||||
}, [formWidth])
|
||||
|
||||
if (scrollTo !== undefined && scrollTo !== null && scrollTo.length > 0 && scrollDone === false) {
|
||||
setTimeout(() => {
|
||||
const foundScroll = document.getElementById(scrollTo)
|
||||
@@ -308,9 +316,14 @@ const EditWorkflow = (props) => {
|
||||
}
|
||||
|
||||
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.description = description
|
||||
@@ -1049,13 +1062,34 @@ const EditWorkflow = (props) => {
|
||||
}
|
||||
|
||||
setInputMarkdown(e.target.value)
|
||||
workflow.input_markdown = e.target.value
|
||||
workflow.form_control.input_markdown = e.target.value
|
||||
setWorkflow(workflow)
|
||||
setUpdate(Math.random())
|
||||
}}
|
||||
/>
|
||||
</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">
|
||||
<Typography variant="h6" style={{marginTop: 50, }}>
|
||||
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">
|
||||
<Button
|
||||
style={{ margin: "auto", marginTop: 50, textAlign: "center", textTransform: "none", }}
|
||||
style={{ margin: "auto", marginTop: 50, marginBottom: 100, textAlign: "center", textTransform: "none", }}
|
||||
variant="outlined"
|
||||
disabled={newWorkflow === true}
|
||||
color="secondary"
|
||||
|
||||
@@ -227,7 +227,7 @@ const ExploreWorkflow = (props) => {
|
||||
<ArrowBackIosNewIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
|
||||
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette?.borderRadius, }}>
|
||||
<AliceCarousel
|
||||
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
|
||||
items={formattedCarousel}
|
||||
@@ -320,7 +320,7 @@ const ExploreWorkflow = (props) => {
|
||||
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<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={{}}>
|
||||
{suggestedUsecases.length === 0 && usecasesSet ?
|
||||
<Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)">
|
||||
|
||||
@@ -325,7 +325,7 @@ const FixWorkflowValidationErrors = (props) => {
|
||||
width: 25,
|
||||
height: 25,
|
||||
marginRight: 10,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
border: `1px solid ${theme.palette.borderColor}`,
|
||||
}}
|
||||
/>
|
||||
@@ -467,7 +467,7 @@ const FixWorkflowValidationErrors = (props) => {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
height: 40,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
@@ -559,7 +559,7 @@ const FixWorkflowValidationErrors = (props) => {
|
||||
justifyContent: !validating ? "flex-start" : "center",
|
||||
textTransform: "none",
|
||||
fontSize: 18,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onClick={() => {
|
||||
toast("Validating app")
|
||||
|
||||
@@ -50,8 +50,6 @@ const hoverOutColor = "#e8eaf6"
|
||||
|
||||
const Header = props => {
|
||||
const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props;
|
||||
//const theme = useTheme();
|
||||
//const alert = useAlert()
|
||||
|
||||
|
||||
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
|
||||
@@ -829,7 +827,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
},
|
||||
}}
|
||||
style={{
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
marginRight: 15,
|
||||
color: "white",
|
||||
@@ -904,7 +902,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
|
||||
<Tooltip color="primary" title={parsedTitle} placement="left">
|
||||
<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>
|
||||
</Tooltip>
|
||||
</MenuItem>
|
||||
@@ -935,7 +933,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
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.`}>
|
||||
<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)
|
||||
if (window.drift !== undefined) {
|
||||
window.drift.api.startInteraction({ interactionId: 326905 })
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import { Chart, registerables } from 'chart.js';
|
||||
|
||||
Chart.register(...registerables);
|
||||
|
||||
const HealthBarChart = (props) => {
|
||||
const { globalUrl, filteredData, options, onBarClick } = props;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -87,7 +87,7 @@ const LicencePopup = (props) => {
|
||||
|
||||
const paperStyle = {
|
||||
padding: 20,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
height: "100%",
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ const LicencePopup = (props) => {
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
style={{ borderRadius: theme.palette.borderRadius, }}
|
||||
style={{ borderRadius: theme.palette?.borderRadius, }}
|
||||
placement="bottom"
|
||||
>
|
||||
<div style={{}}>
|
||||
@@ -319,7 +319,7 @@ const LicencePopup = (props) => {
|
||||
margin: "auto",
|
||||
width: 100,
|
||||
backgroundColor: "white",
|
||||
// borderRadius: theme.palette.borderRadius,
|
||||
// borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
/>
|
||||
: null}
|
||||
@@ -389,7 +389,7 @@ const LicencePopup = (props) => {
|
||||
value={feature.split("Worker License: ")[1]}
|
||||
style={{
|
||||
// backgroundColor: theme.palette.inputColor,
|
||||
// borderRadius: theme.palette.borderRadius,
|
||||
// borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
id={fieldId}
|
||||
onClick={() => { }}
|
||||
@@ -823,7 +823,7 @@ const LicencePopup = (props) => {
|
||||
{errorMessage.length > 0 ? <Typography variant="h4">Error: {errorMessage}</Typography> : null}
|
||||
<Card style={{
|
||||
padding: 20,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
border: "1px solid #f85a3e",
|
||||
}}>
|
||||
<div>
|
||||
|
||||
@@ -65,7 +65,6 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
},
|
||||
dropdownMenu: {
|
||||
marginTop: theme.spacing(1),
|
||||
borderRadius: "12px !important",
|
||||
zIndex: 10,
|
||||
"& .MuiPaper-root": {
|
||||
@@ -88,7 +87,6 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
},
|
||||
dropdownMenuItem: {
|
||||
padding: theme.spacing(2, 3),
|
||||
fontSize: "16px",
|
||||
fontWeight: 400,
|
||||
color: "#fff",
|
||||
@@ -503,7 +501,7 @@ const Header = (props) => {
|
||||
minHeight: 370,
|
||||
padding: 20,
|
||||
backgroundColor: "rgba(0, 0, 0, 1)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -928,7 +926,7 @@ const Header = (props) => {
|
||||
},
|
||||
}}
|
||||
style={{
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
marginRight: 15,
|
||||
color: "white",
|
||||
@@ -1056,7 +1054,7 @@ const Header = (props) => {
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
float: "left",
|
||||
margin: "0 0 0 0",
|
||||
marginRight: 25,
|
||||
@@ -1165,7 +1163,7 @@ const Header = (props) => {
|
||||
padding: 8,
|
||||
textAlign: "center",
|
||||
cursor: "pointer",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginTop: 5,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
minWidth: 60,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {useState} from 'react';
|
||||
import { useTheme } from '@mui/styles';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import ReactGA from 'react-ga4';
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
TextField,
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
const Newsletter = (props) => {
|
||||
const { globalUrl, } = props;
|
||||
|
||||
const theme = useTheme();
|
||||
const [email, setEmail] = useState("");
|
||||
const [msg, setMsg] = useState("");
|
||||
const [buttonActive, setButtonActive] = useState(true);
|
||||
|
||||
@@ -96,7 +96,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
autoAuth,
|
||||
authButtonOnly,
|
||||
isLoggedIn,
|
||||
|
||||
org_id,
|
||||
setFinalized,
|
||||
} = props;
|
||||
|
||||
@@ -148,7 +148,6 @@ const AuthenticationOauth2 = (props) => {
|
||||
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) {
|
||||
startOauth2Request()
|
||||
}
|
||||
@@ -452,6 +451,8 @@ const AuthenticationOauth2 = (props) => {
|
||||
if (orgId !== undefined && orgId !== null && orgId.length > 0) {
|
||||
console.log("Adding org_id from user side")
|
||||
state += `%26org_id%3d${orgId}`;
|
||||
}else{
|
||||
state += `%26org_id%3d${org_id}`
|
||||
}
|
||||
|
||||
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
|
||||
@@ -466,7 +467,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
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}`
|
||||
}
|
||||
|
||||
@@ -696,7 +697,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
justifyContent: "flex-start",
|
||||
backgroundColor: "#ffffff",
|
||||
color: "#2f2f2f",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
minWidth: 300,
|
||||
maxWidth: 300,
|
||||
maxHeight: 50,
|
||||
@@ -721,7 +722,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
<span style={{display: "flex"}}>
|
||||
<img
|
||||
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}
|
||||
/>
|
||||
<Typography style={{ margin: 0, marginLeft: 10, marginTop: 5,}} variant="body1">
|
||||
@@ -803,7 +804,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
</span>
|
||||
: null}
|
||||
{/*<TextField
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}}
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette?.borderRadius,}}
|
||||
InputProps={{
|
||||
style:{
|
||||
},
|
||||
@@ -835,13 +836,14 @@ const AuthenticationOauth2 = (props) => {
|
||||
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 (
|
||||
<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 }} />
|
||||
|
||||
<b>{fieldname}</b>
|
||||
@@ -893,7 +895,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -924,7 +926,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
style={{
|
||||
marginTop: 20,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -942,7 +944,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
InputProps={{
|
||||
@@ -964,7 +966,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -982,7 +984,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
InputProps={{
|
||||
@@ -1061,7 +1063,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
style={{
|
||||
marginBottom: 40,
|
||||
marginTop: 20,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
disabled={
|
||||
clientSecret.length === 0 || clientId.length === 0 || buttonClicked || (allscopes.length !== 0 && selectedScopes.length === 0)
|
||||
@@ -1092,7 +1094,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
<Button
|
||||
style={{
|
||||
marginLeft: 10,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
disabled={clientSecret.length === 0 || clientId.length === 0}
|
||||
variant="text"
|
||||
|
||||
@@ -438,7 +438,7 @@ const OrgHeaderexpanded = (props) => {
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
console.log("Found value: ", newValue)
|
||||
@@ -470,7 +470,7 @@ const OrgHeaderexpanded = (props) => {
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{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}
|
||||
<Typography>
|
||||
Choose {data.name}
|
||||
@@ -498,7 +498,7 @@ const OrgHeaderexpanded = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find a notification workflow"
|
||||
|
||||
@@ -11,7 +11,9 @@ const PaperComponent = (props) => {
|
||||
handle="#draggable-dialog-title"
|
||||
cancel={'[class*="MuiDialogContent-root"]'}
|
||||
>
|
||||
<Paper {...props} />
|
||||
<Paper
|
||||
{...props}
|
||||
/>
|
||||
</Draggable>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,6 +207,53 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
}, [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(() => {
|
||||
if (selectedActionEnvironment === undefined || selectedActionEnvironment === null || Object.keys(selectedActionEnvironment).length === 0) {
|
||||
|
||||
@@ -582,8 +629,9 @@ const ParsedAction = (props) => {
|
||||
}
|
||||
|
||||
let newParameters = selectedAction?.parameters?.map((param) => {
|
||||
let paramvalue = param.value;
|
||||
let paramvalue = param.value === undefined || param.value === null ? "" : param.value;
|
||||
let errorVars = [];
|
||||
|
||||
if(paramvalue.includes("$")){
|
||||
let actions = workflow.actions?.map((action) => {
|
||||
return "$"+action.label?.toLowerCase();
|
||||
@@ -1161,7 +1209,7 @@ const ParsedAction = (props) => {
|
||||
var helperText = ""
|
||||
if (name.includes("url")) {
|
||||
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) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
const oldstartnode = cy.getElementById(selectedAction.id);
|
||||
@@ -1444,12 +1492,12 @@ const ParsedAction = (props) => {
|
||||
|
||||
setSelectedResult(foundResult);
|
||||
if (setCodeModalOpen !== undefined) {
|
||||
setCodeModalOpen(true);
|
||||
setCodeModalOpen(true)
|
||||
|
||||
found = true
|
||||
}
|
||||
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@@ -1497,13 +1545,13 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
disabled={autoCompleting}
|
||||
onClick={() => {
|
||||
//if (setAiQueryModalOpen !== undefined) {
|
||||
// setAiQueryModalOpen(true)
|
||||
//} else {
|
||||
if (setAiQueryModalOpen !== undefined) {
|
||||
setAiQueryModalOpen(true)
|
||||
} else {
|
||||
aiSubmit("Fill based on previous values", undefined, undefined, selectedAction)
|
||||
//}
|
||||
setAutocompleting(true)
|
||||
}
|
||||
|
||||
setAutocompleting(true)
|
||||
setTimeout(() => {
|
||||
setAutocompleting(false)
|
||||
}, 3000)
|
||||
@@ -1511,7 +1559,7 @@ const ParsedAction = (props) => {
|
||||
>
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title={"Autocomplete fields. Uses the name of the current action, the fields and previous actions' results"}
|
||||
title={"Autocomplete the action"}
|
||||
placement="top"
|
||||
>
|
||||
{autoCompleting ?
|
||||
@@ -1568,7 +1616,7 @@ const ParsedAction = (props) => {
|
||||
color: "white",
|
||||
height: 35,
|
||||
marginleft: 10,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
@@ -1855,7 +1903,9 @@ const ParsedAction = (props) => {
|
||||
<span>
|
||||
<Button
|
||||
color="primary"
|
||||
style={{}}
|
||||
style={{
|
||||
textTransform: "none",
|
||||
}}
|
||||
fullWidth
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
@@ -1963,7 +2013,7 @@ const ParsedAction = (props) => {
|
||||
color: "white",
|
||||
height: 50,
|
||||
maxWidth: rightsidebarStyle.maxWidth - 80,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
@@ -2066,7 +2116,7 @@ const ParsedAction = (props) => {
|
||||
: null}
|
||||
|
||||
|
||||
{showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
|
||||
{/*showEnvironment !== undefined && showEnvironment && environments.length > 1 && !isIntegration ? (
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<Typography style={{color: "rgba(255,255,255,0.7)"}}>Environment</Typography>
|
||||
<Select
|
||||
@@ -2097,7 +2147,7 @@ const ParsedAction = (props) => {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
height: "50px",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
{environments.map((data, index) => {
|
||||
@@ -2153,15 +2203,9 @@ const ParsedAction = (props) => {
|
||||
})}
|
||||
</Select>
|
||||
|
||||
{/*selectedActionEnvironment.running_ip === "" && selectedActionEnvironment.Name !== "Cloud" && selectedActionEnvironment.Name !== "cloud" ?
|
||||
<a href={`/admin?tab=environment&env=${selectedActionEnvironment.Name}`} target="_blank" style={{textDecoration: "none", color: "#f85a3e",}}>
|
||||
<Typography style={{}}>
|
||||
Configure the environment
|
||||
</Typography>
|
||||
</a>
|
||||
: null*/}
|
||||
</div>
|
||||
) : null}
|
||||
) : null*/}
|
||||
|
||||
{workflow.execution_variables !== undefined &&
|
||||
workflow.execution_variables !== null &&
|
||||
workflow.execution_variables.length > 0 ? (
|
||||
@@ -2201,7 +2245,7 @@ const ParsedAction = (props) => {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
height: "50px",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
@@ -2294,7 +2338,7 @@ const ParsedAction = (props) => {
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
// Workaround with event lol
|
||||
@@ -2476,7 +2520,7 @@ const ParsedAction = (props) => {
|
||||
variant="body1"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
label={isIntegration ? "Choose a category" : "Find Actions"}
|
||||
variant="outlined"
|
||||
@@ -2496,7 +2540,7 @@ const ParsedAction = (props) => {
|
||||
value={selectedAction.name}
|
||||
fullWidth
|
||||
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={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
@@ -2724,7 +2768,7 @@ const ParsedAction = (props) => {
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
console.log("SELECT: ", event, newValue)
|
||||
@@ -2786,7 +2830,7 @@ const ParsedAction = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find App to Translate"
|
||||
@@ -2801,7 +2845,7 @@ const ParsedAction = (props) => {
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid rgba(255,255,255,0.6)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginTop: 15,
|
||||
marginBottom: 10,
|
||||
maxHeight: 70,
|
||||
@@ -2838,7 +2882,7 @@ const ParsedAction = (props) => {
|
||||
if (selectedAction.parameters === undefined || selectedAction.parameters === null || selectedAction.parameters.length !== selectedActionParameters.length) {
|
||||
|
||||
//selectedAction.parameters = selectedActionParameters
|
||||
console.log("PARAM BUG: ", selectedAction)
|
||||
//console.log("PARAM BUG - length change(?): ", selectedAction)
|
||||
}
|
||||
|
||||
//!selectedAction.auth_not_required &&
|
||||
@@ -3008,7 +3052,7 @@ const ParsedAction = (props) => {
|
||||
marginTop: 50,
|
||||
border: "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",
|
||||
textAlign: "center",
|
||||
}}
|
||||
@@ -3284,11 +3328,13 @@ const ParsedAction = (props) => {
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
autofill="off"
|
||||
autoComplete="off"
|
||||
id={clickedFieldId}
|
||||
disabled={disabled}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
border:
|
||||
selectedActionParameters[count].required ||
|
||||
selectedActionParameters[count].configuration
|
||||
@@ -3323,6 +3369,8 @@ const ParsedAction = (props) => {
|
||||
"field_number": count,
|
||||
"actionlist": actionlist,
|
||||
"field_id": clickedFieldId,
|
||||
|
||||
"example": selectedActionParameters[count].example,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
@@ -3646,7 +3694,7 @@ const ParsedAction = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: hideExtraTypes ? null : (
|
||||
@@ -3730,7 +3778,7 @@ const ParsedAction = (props) => {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
height: "50px",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
{parsedoptions.map(
|
||||
@@ -4307,7 +4355,7 @@ const ParsedAction = (props) => {
|
||||
color: "white",
|
||||
height: 50,
|
||||
marginTop: 2,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onChange={(e) => {
|
||||
console.log("SELECT ONCHANGE DONE")
|
||||
|
||||
@@ -286,7 +286,7 @@ const Priorities = (props) => {
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
marginBottom: 20,
|
||||
border: highlighted ? "2px solid #f85a3e" : null,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
<div style={{display: "flex", }}>
|
||||
|
||||
@@ -121,7 +121,7 @@ const Priority = (props) => {
|
||||
const srcSize = realignedSrc ? 35 : 30
|
||||
const dstSize = realignedDst ? 35 : 30
|
||||
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",}}>
|
||||
<span style={{display: "flex", }}>
|
||||
{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>
|
||||
{priority.type === "usecase" && priority.description.includes("&") ?
|
||||
<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, }}>
|
||||
{newdescription.split("&")[0]}
|
||||
</Typography>
|
||||
@@ -139,7 +139,7 @@ const Priority = (props) => {
|
||||
{newdescription.split("&").length > 3 ?
|
||||
<span style={{display: "flex", }}>
|
||||
<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}}>
|
||||
{newdescription.split("&")[2]}
|
||||
</Typography>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react"
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
@@ -20,7 +21,6 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [hovered, setHovered] = React.useState(false)
|
||||
|
||||
if (workflow === undefined || workflow === null) {
|
||||
console.log("No workflow")
|
||||
return null
|
||||
@@ -39,12 +39,11 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
|
||||
|
||||
// Check if workflow.input_markdown has an image in it
|
||||
// If it does, show it as the main thing
|
||||
//
|
||||
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  markdown
|
||||
// html > markdown
|
||||
const imgTag = workflow.input_markdown.match(/<img[^>]+>/g)
|
||||
const imgTag = workflow?.form_control?.input_markdown.match(/<img[^>]+>/g)
|
||||
|
||||
if (imgTag !== null) {
|
||||
const src = imgTag[0].match(/src="([^"]+)"/)
|
||||
@@ -52,7 +51,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
|
||||
relevantImageUrl = src[1]
|
||||
}
|
||||
} else {
|
||||
const markdownTag = workflow.input_markdown.match(/!\[.*\]\(.*\)/g)
|
||||
const markdownTag = workflow?.form_control?.input_markdown.match(/!\[.*\]\(.*\)/g)
|
||||
|
||||
if (markdownTag !== null) {
|
||||
const src = markdownTag[0].match(/\(([^)]+)\)/)
|
||||
@@ -68,15 +67,13 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
>
|
||||
<Link to={`/workflows/` + workflow?.id} style={{textDecoration: "none"}}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
if (onclickHandler !== undefined) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
onclickHandler()
|
||||
} else {
|
||||
navigate(`/workflows/` + workflow?.id)
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 100)
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
@@ -89,7 +86,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
|
||||
opacity: expandLeftNav ? 1 : 0,
|
||||
transition: "opacity 0.1s",
|
||||
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
backgroundColor: hovered || currentWorkflowId === workflow.id ? "#1f1f1f" : "transparent",
|
||||
}}
|
||||
disableRipple
|
||||
@@ -150,6 +147,7 @@ const RecentWorkflow = ({ workflow, onclickHandler, leftNavOpen, currentWorkflow
|
||||
}
|
||||
</Box>
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
PlayArrow as PlayArrowIcon,
|
||||
Insights as InsightsIcon,
|
||||
Replay as ReplayIcon,
|
||||
EditNote as EditNoteIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'
|
||||
@@ -76,6 +77,10 @@ const RuntimeDebugger = (props) => {
|
||||
{"id": "", "name": "All Workflows",}
|
||||
])
|
||||
|
||||
if (document != undefined) {
|
||||
document.title = "Workflow Run Debugger"
|
||||
}
|
||||
|
||||
// Shitty workflow search on purpose :)
|
||||
const handleWorkflowUsageCount = (workflows) => {
|
||||
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", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -240,6 +245,15 @@ const RuntimeDebugger = (props) => {
|
||||
var foundWorkflows = [{"id": "", "name": "All Workflows",}]
|
||||
foundWorkflows.push(...responseJson)
|
||||
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) => {
|
||||
@@ -248,7 +262,6 @@ const RuntimeDebugger = (props) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getAvailableWorkflows()
|
||||
|
||||
// Find workflow_id in url query
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
@@ -265,6 +278,8 @@ const RuntimeDebugger = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
getAvailableWorkflows(workflowId)
|
||||
|
||||
const foundStatus = urlParams.get('status');
|
||||
if (foundStatus !== undefined && foundStatus !== null && foundStatus !== "") {
|
||||
setStatus(foundStatus)
|
||||
@@ -314,15 +329,17 @@ const RuntimeDebugger = (props) => {
|
||||
|
||||
var source = params.row.execution_source
|
||||
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") {
|
||||
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) {
|
||||
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"
|
||||
} else if (source === "rerun" || source.length === 36) {
|
||||
foundSource = <ReplayIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} />
|
||||
source = "rerun of a previous run"
|
||||
} else if (source === "form") {
|
||||
foundSource = <EditNoteIcon style={{color: theme.palette.primary.secondary, height: imageSize, width: imageSize, }} />
|
||||
} else {
|
||||
source = "manual"
|
||||
}
|
||||
@@ -888,13 +905,13 @@ const RuntimeDebugger = (props) => {
|
||||
{userdata.support === true ?
|
||||
<Button
|
||||
variant={ignoreOrg ? "contained" : "outlined"}
|
||||
color="primary"
|
||||
style={{maxHeight: 40, marginTop: 25, }}
|
||||
color="secondary"
|
||||
style={{marginLeft: 100, maxHeight: 40, marginTop: 25, }}
|
||||
onClick={() => {
|
||||
setIgnoreOrg(!ignoreOrg)
|
||||
}}
|
||||
>
|
||||
{ignoreOrg ? "Ignoring Org" : "Ignore Org"}
|
||||
{ignoreOrg ? "Ignoring Org" : "Ignore Org (Support Only)"}
|
||||
</Button>
|
||||
: null}
|
||||
</div>
|
||||
@@ -942,11 +959,8 @@ const RuntimeDebugger = (props) => {
|
||||
},
|
||||
}}
|
||||
getOptionLabel={(option) => {
|
||||
if (
|
||||
option === undefined ||
|
||||
option === null ||
|
||||
option.name === undefined ||
|
||||
option.name === null
|
||||
if (option === undefined || option === null ||
|
||||
option.name === undefined || option.name === null
|
||||
) {
|
||||
return "No Workflow Selected";
|
||||
}
|
||||
@@ -961,7 +975,7 @@ const RuntimeDebugger = (props) => {
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginTop: 5,
|
||||
marginLeft: 5,
|
||||
}}
|
||||
@@ -995,13 +1009,13 @@ const RuntimeDebugger = (props) => {
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{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}
|
||||
<Typography>
|
||||
Choose {data.name}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
}>
|
||||
<MenuItem
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
@@ -1023,7 +1037,7 @@ const RuntimeDebugger = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Workflow"
|
||||
|
||||
@@ -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 { useNavigate, Link, useParams } from "react-router-dom";
|
||||
@@ -33,6 +33,8 @@ import {
|
||||
AvatarGroup,
|
||||
} 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 algoliasearch from 'algoliasearch/lite';
|
||||
@@ -47,8 +49,9 @@ const chipStyle = {
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const SearchData = props => {
|
||||
const { serverside, globalUrl, userdata, searchBarModalOpen, setSearchBarModalOpen } = props
|
||||
const { serverside, globalUrl, userdata } = props
|
||||
let navigate = useNavigate();
|
||||
const { searchBarModalOpen, setSearchBarModalOpen } = useContext(Context);
|
||||
const borderRadius = 3
|
||||
const node = useRef()
|
||||
const [searchOpen, setSearchOpen] = useState(false)
|
||||
@@ -547,8 +550,6 @@ const SearchData = props => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
console.log("OBJECT CHANGE: ", hit.objectID)
|
||||
|
||||
// This does nothing rofl
|
||||
if (userdata.active_apps === undefined || userdata.active_apps === null) {
|
||||
activateApp(hit.name, hit.objectID, "activate")
|
||||
@@ -873,10 +874,11 @@ const SearchData = props => {
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid style={{ textAlign: "end", width: "100%", textTransform: 'capitalize', }}>
|
||||
<Button style={{ textAlign: "center", textTransform: 'capitalize' }}
|
||||
onClick={() => { window.location = "/search"; }} >
|
||||
<Link to="/search" style={{ textDecoration: "none", color: "#f85a3e" }}>
|
||||
<Button style={{ textAlign: "center", textTransform: 'capitalize' }}>
|
||||
See More
|
||||
</Button>
|
||||
</Link>
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : null
|
||||
|
||||
@@ -4,7 +4,7 @@ import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import SearchBox from "../components/SearchData.jsx";
|
||||
|
||||
import { Context } from '../context/contextApi.jsx';
|
||||
import { Context } from '../context/ContextApi.jsx';
|
||||
|
||||
import {
|
||||
Chip,
|
||||
@@ -101,7 +101,7 @@ const SearchField = props => {
|
||||
</div>
|
||||
: null}
|
||||
<DialogContent className='dialog-content' style={{}}>
|
||||
<SearchBox globalUrl={globalUrl} setSearchBarModalOpen={setSearchBarModalOpen} modalOpen={searchBarModalOpen} serverside={serverside} userdata={userdata} />
|
||||
<SearchBox globalUrl={globalUrl} serverside={serverside} userdata={userdata} />
|
||||
</DialogContent>
|
||||
<Divider style={{overflow: "hidden"}}/>
|
||||
<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, }}>
|
||||
{modalView}
|
||||
<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={{
|
||||
style: {
|
||||
color: "white",
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
|
||||
Close as CloseIcon,
|
||||
DragIndicator as DragIndicatorIcon,
|
||||
RestartAlt as RestartAltIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
@@ -56,6 +57,7 @@ import { tags as t } from '@lezer/highlight';
|
||||
import AceEditor from "react-ace";
|
||||
import ace from "ace-builds";
|
||||
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-solarized_dark';
|
||||
import 'ace-builds/src-noconflict/theme-gruvbox';
|
||||
@@ -109,6 +111,9 @@ const CodeEditor = (props) => {
|
||||
setActiveDialog,
|
||||
fieldname,
|
||||
contentLoading,
|
||||
editorData,
|
||||
|
||||
setAiQueryModalOpen,
|
||||
} = props
|
||||
|
||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||
@@ -163,6 +168,11 @@ const CodeEditor = (props) => {
|
||||
setMenuPosition(null);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
highlight_variables(localcodedata)
|
||||
expectedOutput(localcodedata)
|
||||
}, [localcodedata])
|
||||
|
||||
let navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -811,11 +821,7 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
const handleItemClick = (values) => {
|
||||
if (
|
||||
values === undefined ||
|
||||
values === null ||
|
||||
values.length === 0
|
||||
) {
|
||||
if (values === undefined || values === null || values.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -830,7 +836,11 @@ const CodeEditor = (props) => {
|
||||
toComplete += values[key].autocomplete;
|
||||
}
|
||||
|
||||
setlocalcodedata(localcodedata+toComplete)
|
||||
|
||||
handleClick({
|
||||
"value": toComplete
|
||||
})
|
||||
//setlocalcodedata(localcodedata+toComplete)
|
||||
setMenuPosition(null)
|
||||
}
|
||||
|
||||
@@ -839,11 +849,38 @@ const CodeEditor = (props) => {
|
||||
return
|
||||
}
|
||||
|
||||
// Injects it in the right spot instead of random
|
||||
var edited = false
|
||||
if (currentCharacter !== undefined && currentCharacter !== null && currentCharacter !== -1 && currentLine !== undefined && currentLine !== null && currentLine !== -1) {
|
||||
// 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)
|
||||
setAnchorEl2(null)
|
||||
@@ -946,14 +983,19 @@ const CodeEditor = (props) => {
|
||||
|
||||
|
||||
// Define a custom completer for the Ace Editor
|
||||
const customVariables = availableVariables
|
||||
const customCompleter = {
|
||||
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||
callback(null, customVariables.map((variable) => ({
|
||||
console.log("CUSTOM COMPLETER: ", prefix)
|
||||
|
||||
callback(null, availableVariables.map((variable) => {
|
||||
console.log("CUSTOM VAR: ", variable)
|
||||
|
||||
return ({
|
||||
caption: variable,
|
||||
value: variable,
|
||||
meta: 'custom',
|
||||
})));
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1338,6 +1380,8 @@ const CodeEditor = (props) => {
|
||||
onClick={() => {
|
||||
console.log("CLICKED: ", innerdata);
|
||||
console.log(innerdata.example)
|
||||
|
||||
//const handleClick = (item) => {
|
||||
handleItemClick([innerdata]);
|
||||
}}
|
||||
>
|
||||
@@ -1482,14 +1526,37 @@ const CodeEditor = (props) => {
|
||||
width: 50,
|
||||
marginLeft: 100,
|
||||
}}
|
||||
disabled={isAiLoading}
|
||||
disabled={editorData === undefined || editorData.example === undefined || editorData.example === null || editorData.example.length === 0}
|
||||
onClick={() => {
|
||||
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
|
||||
color="primary"
|
||||
title={"Auto format data"}
|
||||
title={"Format with AI"}
|
||||
placement="top"
|
||||
>
|
||||
{isAiLoading ?
|
||||
@@ -1504,7 +1571,7 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
<div style={{
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
position: "relative",
|
||||
paddingTop: 0,
|
||||
// minHeight: 548,
|
||||
@@ -1512,8 +1579,10 @@ const CodeEditor = (props) => {
|
||||
}}>
|
||||
{(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor ? (
|
||||
<AceEditor
|
||||
id="shuffle-codeeditor"
|
||||
name="shuffle-codeeditor"
|
||||
value={localcodedata}
|
||||
mode={selectedAction === undefined ? "" : selectedAction.name === "execute_python" ? "python" : ""}
|
||||
mode={selectedAction === undefined ? "json" : selectedAction.name === "execute_python" ? "python" : "json"}
|
||||
theme="gruvbox"
|
||||
height={isFileEditor ? 450 : 550}
|
||||
width={isFileEditor ? 650 : "100%"}
|
||||
@@ -1538,12 +1607,10 @@ const CodeEditor = (props) => {
|
||||
highlight_variables(localcodedata)
|
||||
}}
|
||||
onCursorChange={(cursorPosition, editor, value) => {
|
||||
setCurrentCharacter(cursorPosition.column)
|
||||
setCurrentLine(cursorPosition.row)
|
||||
setCurrentCharacter(cursorPosition.cursor.column)
|
||||
setCurrentLine(cursorPosition.cursor.row)
|
||||
findIndex(cursorPosition.row, cursorPosition.column)
|
||||
|
||||
//highlight_variables(localcodedata)
|
||||
//console.log("VALUE CURSOR: ", value)
|
||||
}}
|
||||
onChange={(value, editor) => {
|
||||
// setlocalcodedata(value)
|
||||
@@ -1666,7 +1733,7 @@ const CodeEditor = (props) => {
|
||||
padding: 10,
|
||||
marginTop: -2,
|
||||
border: `2px solid ${theme.palette.inputColor}`,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
maxHeight: 450,
|
||||
minHeight: 450,
|
||||
overflow: "auto",
|
||||
|
||||
@@ -102,7 +102,7 @@ const SuggestedWorkflows = (props) => {
|
||||
placement="top"
|
||||
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)
|
||||
}} onMouseOut={() => {
|
||||
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, }}>
|
||||
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
|
||||
open={usecaseSearch.length > 0 && usecaseSearchType.length > 0}
|
||||
onClose={() => {
|
||||
@@ -193,7 +193,7 @@ const SuggestedWorkflows = (props) => {
|
||||
apps={apps}
|
||||
/>
|
||||
</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"}}>
|
||||
Suggested Workflows ({finishedUsecases.length}/{usecaseSuggestions.length})
|
||||
</Typography>
|
||||
|
||||
@@ -594,7 +594,7 @@ const UsecaseSearch = (props) => {
|
||||
width: 30,
|
||||
height: 30,
|
||||
border: "2px solid rgba(255,255,255,0.6)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
maxHeight: 30,
|
||||
maxWidth: 30,
|
||||
overflow: "hidden",
|
||||
@@ -616,7 +616,7 @@ const UsecaseSearch = (props) => {
|
||||
width: 30,
|
||||
height: 30,
|
||||
border: "2px solid rgba(255,255,255,0.6)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
maxWidth: 30,
|
||||
maxHeight: 30,
|
||||
overflow: "hidden",
|
||||
@@ -1266,9 +1266,11 @@ const UsecaseSearch = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
var msgString = "Failed to activate the app"
|
||||
|
||||
if (responseJson.reason !== undefined) {
|
||||
msgString += ": " + responseJson.reason
|
||||
}
|
||||
|
||||
toast(msgString)
|
||||
} else {
|
||||
//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, }}>
|
||||
{startText}
|
||||
</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 ?
|
||||
<AppsearchPopout
|
||||
@@ -1560,7 +1562,7 @@ const UsecaseSearch = (props) => {
|
||||
// <b>{defaultSearch}: {allusecases[usecaseIndex].name}</b>
|
||||
//console.log("UseCase: ", usecases)
|
||||
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}
|
||||
{authenticationModal}
|
||||
{showTitle !== false && defaultSearch !== undefined ?
|
||||
|
||||
@@ -648,7 +648,7 @@ const WelcomeForm = (props) => {
|
||||
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<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
|
||||
globalUrl={globalUrl}
|
||||
isLoggedIn={isLoggedIn}
|
||||
|
||||
@@ -585,7 +585,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
{/*errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
|
||||
<Tooltip title="Click to explore the workflow" placement="top">
|
||||
<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={() => {
|
||||
// Open in new tab
|
||||
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)"
|
||||
|
||||
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 />
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ const WorkflowValidationTimeline = (props) => {
|
||||
<div
|
||||
style={{
|
||||
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)",
|
||||
cursor: hovering === true && showHoverForClick === true ? "pointer" : "default",
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -13,6 +13,30 @@ const data = [
|
||||
return elementname
|
||||
},
|
||||
"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-weight": "lighter",
|
||||
"font-size": "18px",
|
||||
@@ -23,7 +47,6 @@ const data = [
|
||||
padding: "10px",
|
||||
margin: "5px",
|
||||
"border-width": "1px",
|
||||
"text-margin-x": "10px",
|
||||
"z-index": 5001,
|
||||
},
|
||||
},
|
||||
@@ -263,7 +286,9 @@ const data = [
|
||||
{
|
||||
selector: "node[?isStartNode]",
|
||||
css: {
|
||||
shape: "ellipse",
|
||||
shape: function(element) {
|
||||
return "ellipse"
|
||||
},
|
||||
"border-color": "#80deea",
|
||||
width: "80px",
|
||||
height: "80px",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { createTheme, adaptV4Theme } from "@mui/material/styles";
|
||||
|
||||
//const theme = createTheme({
|
||||
const theme = createTheme(adaptV4Theme({
|
||||
palette: {
|
||||
theme: "dark",
|
||||
|
||||
+459
-281
File diff suppressed because it is too large
Load Diff
+908
-1364
File diff suppressed because it is too large
Load Diff
@@ -4717,7 +4717,7 @@ const AppCreator = (defaultprops) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -5066,7 +5066,7 @@ const AppCreator = (defaultprops) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -5106,7 +5106,7 @@ const AppCreator = (defaultprops) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -6018,7 +6018,7 @@ const AppCreator = (defaultprops) => {
|
||||
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", }}>
|
||||
<FormControl style={{ }} variant="outlined">
|
||||
<Typography variant="h6">Authentication</Typography>
|
||||
|
||||
+51
-20
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useContext, memo } from "react";
|
||||
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import theme from '../theme.jsx';
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
Folder as FolderIcon,
|
||||
LibraryBooks as LibraryBooksIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
|
||||
import {
|
||||
ForkRight as ForkRightIcon,
|
||||
@@ -280,6 +281,7 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
|
||||
const Apps = (props) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded, userdata, serverside, } = props;
|
||||
|
||||
|
||||
//const [workflows, setWorkflows] = React.useState([]);
|
||||
const baseRepository = "https://github.com/frikky/shuffle-apps";
|
||||
//const alert = useAlert();
|
||||
@@ -423,7 +425,7 @@ const Apps = (props) => {
|
||||
minWidth: "100%",
|
||||
maxWidth: 612.5,
|
||||
marginBottom: 5,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
color: "white",
|
||||
backgroundColor: surfaceColor,
|
||||
cursor: "pointer",
|
||||
@@ -875,7 +877,7 @@ const Apps = (props) => {
|
||||
minWidth: viewWidth,
|
||||
maxWidth: viewWidth,
|
||||
color: "white",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
backgroundColor: surfaceColor,
|
||||
//display: "flex",
|
||||
marginBottom: 10,
|
||||
@@ -1551,7 +1553,7 @@ const Apps = (props) => {
|
||||
minHeight: 150,
|
||||
maxHeight: 150,
|
||||
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}
|
||||
|
||||
if (!makeFancy) {
|
||||
@@ -1829,7 +1831,7 @@ const Apps = (props) => {
|
||||
}}>
|
||||
<TextField
|
||||
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={{
|
||||
style: {
|
||||
color: "white",
|
||||
@@ -2185,7 +2187,7 @@ const Apps = (props) => {
|
||||
</div>
|
||||
<div style={{ height: 50 }}>
|
||||
<TextField
|
||||
style={{ backgroundColor: inputColor, borderRadius: theme.palette.borderRadius, }}
|
||||
style={{ backgroundColor: inputColor, borderRadius: theme.palette?.borderRadius, }}
|
||||
InputProps={{
|
||||
style: {
|
||||
},
|
||||
@@ -2236,7 +2238,7 @@ const Apps = (props) => {
|
||||
minWidth: viewWidth,
|
||||
maxWidth: viewWidth,
|
||||
color: "white",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
//display: "flex",
|
||||
marginBottom: 10,
|
||||
overflow: "hidden",
|
||||
@@ -2373,6 +2375,7 @@ const Apps = (props) => {
|
||||
toast("Hotloading apps from location in .env");
|
||||
setIsLoading(true);
|
||||
fetch(globalUrl + "/api/v1/apps/run_hotload", {
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
@@ -2999,7 +3002,7 @@ const Apps = (props) => {
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.platformColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
@@ -3091,7 +3094,7 @@ const Apps = (props) => {
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.platformColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
@@ -3202,22 +3205,50 @@ const Apps = (props) => {
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
const loadedCheck =
|
||||
isLoaded && !firstrequest ? (
|
||||
<div>
|
||||
const loadedCheck = isLoaded && !firstrequest ? (
|
||||
<SidebarAdjustWrapper userdata={userdata}>
|
||||
<AppsWrapper
|
||||
userdata={userdata}
|
||||
appView={appView}
|
||||
modalView={modalView}
|
||||
publishModal={publishModal}
|
||||
generateAppView={generateAppView}
|
||||
appsModalLoad={appsModalLoad}
|
||||
deleteModal={deleteModal}
|
||||
/>
|
||||
</SidebarAdjustWrapper>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
return loadedCheck;
|
||||
};
|
||||
|
||||
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>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
// Maybe use gridview or something, idk
|
||||
return loadedCheck;
|
||||
};
|
||||
|
||||
export default Apps;
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -197,7 +197,7 @@ const LineChartWrapper = ({keys, height, width}) => {
|
||||
const name = data.metadata !== undefined && data.metadata.name !== undefined ? data.metadata.name : "No"
|
||||
|
||||
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">
|
||||
{name}
|
||||
</Typography>
|
||||
@@ -281,7 +281,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
|
||||
}}
|
||||
content={(data, color) => {
|
||||
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">
|
||||
{data.x}
|
||||
</Typography>
|
||||
@@ -795,7 +795,7 @@ const Dashboard = (props) => {
|
||||
color: "white",
|
||||
height: 40,
|
||||
maxWidth: 150,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
{data.available_keys.map((foundKey, index) => {
|
||||
|
||||
+47
-19
@@ -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 Markdown from 'react-markdown'
|
||||
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 { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
import {
|
||||
Grid,
|
||||
TextField,
|
||||
@@ -166,7 +167,7 @@ export const Img = (props) => {
|
||||
|
||||
return(
|
||||
<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}
|
||||
src={props.src}
|
||||
/>
|
||||
@@ -213,7 +214,7 @@ export const CodeHandler = (props) => {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
overflowY: "auto",
|
||||
// Have it inline
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
>
|
||||
{validate.valid === true ?
|
||||
@@ -250,10 +251,8 @@ export const CodeHandler = (props) => {
|
||||
}
|
||||
|
||||
const Docs = (defaultprops) => {
|
||||
const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops;
|
||||
|
||||
const { globalUrl, selectedDoc, serverside, serverMobile, userdata } = defaultprops;
|
||||
let navigate = useNavigate();
|
||||
|
||||
// Quickfix for react router 5 -> 6
|
||||
const params = useParams();
|
||||
//var props = JSON.parse(JSON.stringify(defaultprops))
|
||||
@@ -526,7 +525,7 @@ const Docs = (defaultprops) => {
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
padding: 15,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginBottom: 30,
|
||||
display: "flex",
|
||||
}}
|
||||
@@ -892,7 +891,7 @@ const Docs = (defaultprops) => {
|
||||
target="_blank"
|
||||
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) => {
|
||||
if (link === "" || link === undefined) {
|
||||
event.preventDefault()
|
||||
@@ -932,7 +931,7 @@ const Docs = (defaultprops) => {
|
||||
|
||||
return (
|
||||
<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={() => {
|
||||
setHover(true)
|
||||
}}
|
||||
@@ -968,8 +967,8 @@ const Docs = (defaultprops) => {
|
||||
Documentation
|
||||
</Typography>
|
||||
<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="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="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" />
|
||||
</div>
|
||||
|
||||
<div style={{ textAlign: "left" }}>
|
||||
@@ -1265,15 +1264,44 @@ const Docs = (defaultprops) => {
|
||||
</div>
|
||||
);
|
||||
|
||||
// Padding and zIndex etc set because of footer in cloud.
|
||||
const loadedCheck = (
|
||||
<div style={{ minHeight: 1000, zIndex: 50000, maxWidth: 1920, minWidth: isMobile ? null : 1366, margin: "auto", }}>
|
||||
<BrowserView>{postDataBrowser}</BrowserView>
|
||||
<MobileView>{postDataMobile}</MobileView>
|
||||
</div>
|
||||
);
|
||||
console.log("docs render")
|
||||
|
||||
// Padding and zIndex etc set because of footer in cloud.
|
||||
const loadedCheck = (
|
||||
<DocsWrapper userdata={userdata}>
|
||||
<DocsContent postDataBrowser={postDataBrowser} postDataMobile={postDataMobile}/>
|
||||
</DocsWrapper>
|
||||
);
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
|
||||
return <div style={{}}>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
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>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -2467,7 +2467,7 @@ const GettingStarted = (props) => {
|
||||
maxWidth: 1024,
|
||||
zIndex: 11,
|
||||
border: "1px solid rgba(255,255,255,0.1)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
textAlign: "center",
|
||||
overflow: "auto",
|
||||
}}
|
||||
|
||||
+217
-102
@@ -43,6 +43,7 @@ import {
|
||||
Lock as LockIcon,
|
||||
LockOpen as LockOpenIcon,
|
||||
OpenInNew as OpenInNewIcon,
|
||||
Edit as EditIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
const hrefStyle = {
|
||||
@@ -50,20 +51,13 @@ const hrefStyle = {
|
||||
textDecoration: "none"
|
||||
}
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
width: isMobile? "100%":"500px",
|
||||
position: "relative",
|
||||
|
||||
paddingBottom: 250,
|
||||
}
|
||||
|
||||
|
||||
const RunWorkflow = (defaultprops) => {
|
||||
const { globalUrl, userdata, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
|
||||
const [explorerUi, setExplorerUi] = useState(false)
|
||||
const [message, setMessage] = useState("");
|
||||
const [workflow, setWorkflow] = React.useState({});
|
||||
const [executionRequest, setExecutionRequest] = React.useState({});
|
||||
@@ -80,6 +74,8 @@ const RunWorkflow = (defaultprops) => {
|
||||
const [sharingOpen, setSharingOpen] = React.useState(false)
|
||||
const [realtimeMarkdown, setRealtimeMarkdown] = React.useState("")
|
||||
const [forms, setForms] = React.useState([])
|
||||
const [boxWidth, setBoxWidth] = React.useState(500)
|
||||
const [inputQuestions, setInputQuestions] = React.useState([])
|
||||
|
||||
const IframeWrapper = (props) => {
|
||||
var propsCopy = JSON.parse(JSON.stringify(props))
|
||||
@@ -94,11 +90,21 @@ const RunWorkflow = (defaultprops) => {
|
||||
if (propsCopy.width === undefined || propsCopy.width === null) {
|
||||
propsCopy.width = 400
|
||||
propsCopy.height = "auto"
|
||||
|
||||
propsCopy.margin = "auto"
|
||||
}
|
||||
|
||||
return Img(propsCopy)
|
||||
}
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
width: isMobile? "100%" : boxWidth,
|
||||
position: "relative",
|
||||
|
||||
paddingBottom: 250,
|
||||
}
|
||||
|
||||
const boxStyle = {
|
||||
color: "white",
|
||||
padding: "25px 50px 50px 50px",
|
||||
@@ -112,7 +118,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
props.match = {}
|
||||
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) {
|
||||
document.title = defaultTitle
|
||||
}
|
||||
@@ -136,6 +142,18 @@ const RunWorkflow = (defaultprops) => {
|
||||
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) {
|
||||
if (executionArgument[key] === undefined || executionArgument[key] === null || executionArgument[key] === "") {
|
||||
return false
|
||||
@@ -257,9 +275,12 @@ const RunWorkflow = (defaultprops) => {
|
||||
|
||||
return (
|
||||
<div style={{marginTop: executionMargin, }}>
|
||||
{workflowQuestion !== "" ? null :
|
||||
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
|
||||
}
|
||||
|
||||
{validate.valid === false ?
|
||||
{workflowQuestion !== "" ? null :
|
||||
validate.valid === false ?
|
||||
<div style={{marginTop: 20, }}>
|
||||
<Divider />
|
||||
<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({
|
||||
duration: 1500,
|
||||
startImmediate: true,
|
||||
@@ -520,8 +529,8 @@ const RunWorkflow = (defaultprops) => {
|
||||
if (realtimeMarkdown !== undefined && realtimeMarkdown !== null && realtimeMarkdown.length > 0) {
|
||||
const newmarkdown = realtimeMarkdown.replace(`{{ ${workflow_id} }}`, "", -1)
|
||||
setRealtimeMarkdown(newmarkdown)
|
||||
} else if (inputWorkflow.input_markdown !== undefined && inputWorkflow.input_markdown !== null && inputWorkflow.input_markdown.length > 0) {
|
||||
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
|
||||
} else if (inputWorkflow.form_control.input_markdown !== undefined && inputWorkflow.form_control.input_markdown !== null && inputWorkflow.form_control.input_markdown.length > 0) {
|
||||
const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
|
||||
setRealtimeMarkdown(newmarkdown)
|
||||
}
|
||||
}
|
||||
@@ -531,15 +540,21 @@ const RunWorkflow = (defaultprops) => {
|
||||
console.log("Get workflow error: ", error.toString())
|
||||
|
||||
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)
|
||||
} else if (inputWorkflow.input_markdown !== undefined && inputWorkflow.input_markdown !== null && inputWorkflow.input_markdown.length > 0) {
|
||||
const newmarkdown = inputWorkflow.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
|
||||
} else if (inputWorkflow.form_control.input_markdown !== undefined && inputWorkflow.form_control.input_markdown !== null && inputWorkflow.form_control.input_markdown.length > 0) {
|
||||
const newmarkdown = inputWorkflow.form_control.input_markdown.replace(`{{ ${workflow_id} }}`, "", -1)
|
||||
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) => {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// 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 found = newmarkdown.match(uuidRegex)
|
||||
@@ -697,7 +763,21 @@ const RunWorkflow = (defaultprops) => {
|
||||
handleExecutionLoader()
|
||||
|
||||
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) => {
|
||||
console.log("Get workflow error: ", error.toString());
|
||||
@@ -723,7 +803,6 @@ const RunWorkflow = (defaultprops) => {
|
||||
if (responseJson.result.startsWith("[") && responseJson.result.endsWith("]")) {
|
||||
try {
|
||||
responseJson.result = JSON.parse(responseJson.result).length
|
||||
console.log("Set length to: ", responseJson.result)
|
||||
} catch (e) {
|
||||
console.log("Error parsing length: ", e)
|
||||
}
|
||||
@@ -875,8 +954,8 @@ const RunWorkflow = (defaultprops) => {
|
||||
const newmarkdown = realtimeMarkdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
|
||||
setRealtimeMarkdown(newmarkdown)
|
||||
|
||||
} else if (workflow.input_markdown !== undefined && workflow.input_markdown !== null && workflow.input_markdown.length > 0) {
|
||||
const newmarkdown = workflow.input_markdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
|
||||
} else if (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) {
|
||||
const newmarkdown = workflow.form_control.input_markdown.replace(`{{ ${responseJson.workflow.id} }}`, responseJson.result, -1)
|
||||
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(() => {
|
||||
if (!isLoaded) {
|
||||
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)
|
||||
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
|
||||
console.log("Get execution: ", execution_id)
|
||||
@@ -915,7 +1000,6 @@ const RunWorkflow = (defaultprops) => {
|
||||
}
|
||||
}, [isLoaded])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (executionData === undefined || executionData === null || executionData === {}) {
|
||||
return
|
||||
@@ -947,9 +1031,14 @@ const RunWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
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.user !== undefined && parsedresult.click_info.user !== null && parsedresult.click_info.user.length > 0) {
|
||||
setMessage("Already answered by " + parsedresult.click_info.user)
|
||||
|
||||
}
|
||||
} else {
|
||||
setMessage("Answered.")
|
||||
@@ -965,8 +1054,12 @@ const RunWorkflow = (defaultprops) => {
|
||||
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 disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
|
||||
const disabledButtons = executionLoading || executionRunning
|
||||
// Check if all fields are filled in?
|
||||
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 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
|
||||
const basedata =
|
||||
<div style={bodyDivStyle}>
|
||||
<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", }}>
|
||||
<CircularProgress />
|
||||
<Typography variant="body1" style={{marginTop: 20, }}>
|
||||
Loding Form Details...
|
||||
Loading Form Details...
|
||||
</Typography>
|
||||
</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, }}>
|
||||
<Markdown
|
||||
components={{
|
||||
@@ -1024,13 +1166,13 @@ const RunWorkflow = (defaultprops) => {
|
||||
}}
|
||||
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>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
<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>
|
||||
<img
|
||||
alt={workflow.name}
|
||||
@@ -1066,7 +1208,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
<div style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
padding: 20,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginBottom: 35,
|
||||
marginTop: 30,
|
||||
}}>
|
||||
@@ -1081,7 +1223,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
|
||||
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
||||
<div style={{marginBottom: 5, }}>
|
||||
{workflow.input_questions.map((question, index) => {
|
||||
{inputQuestions.map((question, index) => {
|
||||
|
||||
// Multiple choice checks for semicolon-splits
|
||||
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?
|
||||
</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")
|
||||
setExecutionData({
|
||||
status: "FINISHED",
|
||||
})
|
||||
|
||||
onSubmit(null, execution_id, authorization, true)
|
||||
}}>Continue</Button>
|
||||
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
|
||||
or
|
||||
</Typography>
|
||||
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => {
|
||||
onSubmit(null, execution_id, authorization, false)
|
||||
|
||||
<Button fullWidth id="abort_execution" variant="contained" disabled={!handleValidateForm(executionArgument) || disabledButtons} color="primary" style={{ flex: 1, }} onClick={() => {
|
||||
setButtonClicked("ABORTED")
|
||||
setExecutionData({
|
||||
status: "ABORTED",
|
||||
})
|
||||
|
||||
onSubmit(null, execution_id, authorization, false)
|
||||
}}>Stop</Button>
|
||||
</div>
|
||||
</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, }}>
|
||||
{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) {
|
||||
return null
|
||||
}
|
||||
@@ -1328,15 +1471,18 @@ const RunWorkflow = (defaultprops) => {
|
||||
</div>
|
||||
}
|
||||
</Paper>
|
||||
|
||||
{workflowQuestion !== "" ? null :
|
||||
<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.
|
||||
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>
|
||||
|
||||
|
||||
// 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 ?
|
||||
<div>
|
||||
<div style={{marginTop: 30, }}>
|
||||
{editWorkflowModalOpen === true ?
|
||||
<EditWorkflow
|
||||
saveWorkflow={saveWorkflow}
|
||||
@@ -1350,6 +1496,8 @@ const RunWorkflow = (defaultprops) => {
|
||||
|
||||
expanded={true}
|
||||
setRealtimeMarkdown={setRealtimeMarkdown}
|
||||
boxWidth={boxWidth}
|
||||
setBoxWidth={setBoxWidth}
|
||||
scrollTo={"form_fill"}
|
||||
/>
|
||||
: null}
|
||||
@@ -1367,7 +1515,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
minHeight: 400,
|
||||
maxHeight: 400,
|
||||
padding: 25,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
},
|
||||
@@ -1418,7 +1566,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
</DialogContent>
|
||||
</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, }}>
|
||||
|
||||
<Button
|
||||
@@ -1462,7 +1610,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
setEditWorkflowModalOpen(true)
|
||||
}}
|
||||
>
|
||||
Edit Details
|
||||
<EditIcon style={{marginRight: 5, }} /> Edit Form
|
||||
</Button>
|
||||
</div>
|
||||
: null}
|
||||
@@ -1476,7 +1624,8 @@ const RunWorkflow = (defaultprops) => {
|
||||
|
||||
// Check width
|
||||
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={{
|
||||
minWidth: 215,
|
||||
maxWidth: 215,
|
||||
@@ -1485,10 +1634,10 @@ const RunWorkflow = (defaultprops) => {
|
||||
minHeight: 500,
|
||||
maxHeight: 500,
|
||||
position: "absolute",
|
||||
left: 100,
|
||||
left: 150,
|
||||
top: 0,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
|
||||
padding: 25,
|
||||
|
||||
@@ -1497,7 +1646,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
}}>
|
||||
{selectedOrganization !== undefined && selectedOrganization !== null ?
|
||||
<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, }}>
|
||||
{selectedOrganization.name}
|
||||
</Typography>
|
||||
@@ -1506,41 +1655,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
: null}
|
||||
|
||||
{forms !== undefined && forms !== null && forms.length > 0 ?
|
||||
<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)
|
||||
}}
|
||||
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>
|
||||
<FormList />
|
||||
:
|
||||
<div>
|
||||
No forms loaded
|
||||
@@ -1557,4 +1672,4 @@ const RunWorkflow = (defaultprops) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default RunWorkflow;
|
||||
export default RunWorkflow
|
||||
|
||||
@@ -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 { isMobile } from "react-device-detect";
|
||||
@@ -11,6 +11,7 @@ import { Tabs, Tab, setRef } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import DiscordChat from "../components/DiscordChat.jsx";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
|
||||
import {
|
||||
Apps as AppsIcon,
|
||||
@@ -21,16 +22,14 @@ import {
|
||||
DescriptionOutlined as DescriptionOutlinedIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import {
|
||||
Typography
|
||||
} from "@mui/material"
|
||||
|
||||
import {Typography} from "@mui/material";
|
||||
|
||||
// Should be different if logged in :|
|
||||
const Search = (props) => {
|
||||
const { globalUrl, isLoaded, serverside, userdata, hidemargins, isHeader } =
|
||||
props;
|
||||
let navigate = useNavigate();
|
||||
const { leftSideBarOpenByClick } = useContext(Context);
|
||||
|
||||
const [curTab, setCurTab] = useState(0);
|
||||
const iconStyle = { marginRight: isHeader ? null : 10 };
|
||||
@@ -124,14 +123,15 @@ const Search = (props) => {
|
||||
flex: "1",
|
||||
marginLeft: isHeader ? null : 10,
|
||||
marginRight: isHeader ? null : 10,
|
||||
paddingLeft: isHeader ? null : 30,
|
||||
paddingRight: isHeader ? null : 30,
|
||||
paddingBottom: isHeader ? null : 30,
|
||||
paddingTop: hidemargins === true ? 0 : isHeader ? null : 30,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflowX: "hidden",
|
||||
minHeight: 400
|
||||
minHeight: 400,
|
||||
paddingLeft: leftSideBarOpenByClick ? 250 : 0,
|
||||
transition: "padding-left 0.3s ease",
|
||||
};
|
||||
|
||||
const views = {
|
||||
@@ -223,7 +223,7 @@ const Search = (props) => {
|
||||
margin: isHeader ? null : "auto",
|
||||
marginTop: hidemargins === true ? 0 : isHeader ? null : 25,
|
||||
backgroundColor: "rgba(33, 33, 33, 1)",
|
||||
borderRadius: 8
|
||||
borderRadius: 8,
|
||||
}}
|
||||
value={curTab}
|
||||
indicatorColor="primary"
|
||||
|
||||
@@ -302,7 +302,7 @@ const SetAuthentication = (props) => {
|
||||
}
|
||||
|
||||
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
|
||||
variant="h4"
|
||||
style={{ marginLeft: "auto", marginRight: "auto", marginTop: 50}}
|
||||
|
||||
@@ -627,7 +627,7 @@ const Settings = (props) => {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: "0px 0px 12px 0px",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
};
|
||||
|
||||
const currentOwner = checkOwner(data, userdata);
|
||||
@@ -636,7 +636,7 @@ const Settings = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid item xs={4} style={{ borderRadius: theme.palette.borderRadius }}>
|
||||
<Grid item xs={4} style={{ borderRadius: theme.palette?.borderRadius }}>
|
||||
<Paper style={innerPaperStyle}>
|
||||
<img
|
||||
src={data.image}
|
||||
@@ -692,7 +692,7 @@ const Settings = (props) => {
|
||||
);
|
||||
|
||||
const landingpageData = (
|
||||
<div style={{ display: "flex", marginTop: 120 }}>
|
||||
<div style={{ display: "flex", paddingTop: 120 }}>
|
||||
<Paper style={boxStyle}>
|
||||
{imageInfo}
|
||||
<h2>Settings</h2>
|
||||
@@ -1065,7 +1065,7 @@ const Settings = (props) => {
|
||||
<Paper
|
||||
square
|
||||
style={{
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
padding: 50,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
@@ -1100,7 +1100,7 @@ const Settings = (props) => {
|
||||
<Paper
|
||||
square
|
||||
style={{
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
padding: 50,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
|
||||
@@ -441,7 +441,7 @@ const UsecaseListComponent = (props) => {
|
||||
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 ?
|
||||
<div style={{textAlign: "left", position: "relative",}}>
|
||||
@@ -799,7 +799,7 @@ const UsecaseListComponent = (props) => {
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
|
||||
@@ -856,7 +856,7 @@ const UsecaseListComponent = (props) => {
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{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}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
@@ -881,7 +881,7 @@ const UsecaseListComponent = (props) => {
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find your workflows"
|
||||
@@ -952,8 +952,8 @@ const UsecaseListComponent = (props) => {
|
||||
target="_blank"
|
||||
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",}}>
|
||||
<img src={subdata.image} style={{width: 40, height: 40, borderRadius: theme.palette.borderRadius, marginTop: 15, }} />
|
||||
<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, }} />
|
||||
<Typography variant="body1" style={{lineHeight: "95%", marginLeft: 12, marginTop: marginTop === 0 ? 19 : 25, maxHeight: 34, }}>
|
||||
{subdata.name}
|
||||
</Typography>
|
||||
@@ -983,7 +983,7 @@ const UsecaseListComponent = (props) => {
|
||||
height: 350,
|
||||
width: isMobile? null:350,
|
||||
marginTop: isMobile ? 50:null,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
padding: 5,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
@@ -1113,7 +1113,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
|
||||
}}
|
||||
content={(data, color) => {
|
||||
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">
|
||||
{data.x}
|
||||
</Typography>
|
||||
|
||||
@@ -304,14 +304,14 @@ const Welcome = (props) => {
|
||||
minWidth: isMobile ? null : 275,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}
|
||||
|
||||
const actionObject = {
|
||||
padding: "25px",
|
||||
maxHeight: 280,
|
||||
minHeight: 280,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
}
|
||||
|
||||
const imageStyle = {
|
||||
@@ -340,7 +340,7 @@ const Welcome = (props) => {
|
||||
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
|
||||
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", }}>
|
||||
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
|
||||
@@ -354,7 +354,7 @@ const Welcome = (props) => {
|
||||
color="primary"
|
||||
style={{
|
||||
backgroundColor: theme.palette.platformColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
padding: 12,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
maxWidth: 500,
|
||||
@@ -434,7 +434,7 @@ const Welcome = (props) => {
|
||||
We will use this information to personalize your automation
|
||||
</Typography> */}
|
||||
<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={() => {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
@@ -478,7 +478,7 @@ const Welcome = (props) => {
|
||||
navigate("/workflows?message=Skipped intro")
|
||||
}}>
|
||||
<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"}}>
|
||||
Experienced
|
||||
</Typography>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useContext } from "react";
|
||||
import React, { useEffect, useContext, memo } from "react";
|
||||
import ReactDOM from "react-dom"
|
||||
|
||||
import { makeStyles } from "@mui/styles";
|
||||
@@ -6,7 +6,7 @@ import { Navigate } from "react-router-dom";
|
||||
import SecurityFramework from '../components/SecurityFramework.jsx';
|
||||
import EditWorkflow from "../components/EditWorkflow.jsx"
|
||||
import Priority from "../components/Priority.jsx";
|
||||
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
import { isMobile } from "react-device-detect"
|
||||
|
||||
import {
|
||||
@@ -92,29 +92,31 @@ import theme from "../theme.jsx";
|
||||
const svgSize = 24;
|
||||
const imagesize = 22;
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
const useStyles = makeStyles(() => {
|
||||
|
||||
return {
|
||||
datagrid: {
|
||||
border: 0,
|
||||
"& .MuiDataGrid-columnsContainer": {
|
||||
backgroundColor:
|
||||
theme.palette.type === "light" ? "#fafafa" : theme.palette.inputColor,
|
||||
theme?.palette?.type === "light" ? "#fafafa" : theme?.palette?.inputColor,
|
||||
},
|
||||
"& .MuiDataGrid-iconSeparator": {
|
||||
display: "none",
|
||||
},
|
||||
"& .MuiDataGrid-colCell, .MuiDataGrid-cell": {
|
||||
borderRight: `1px solid ${
|
||||
theme.palette.type === "light" ? "white" : "#303030"
|
||||
theme?.palette?.type === "light" ? "white" : "#303030"
|
||||
}`,
|
||||
},
|
||||
"& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell": {
|
||||
borderBottom: `1px solid ${
|
||||
theme.palette.type === "light" ? "#f0f0f0" : "#303030"
|
||||
theme?.palette?.type === "light" ? "#f0f0f0" : "#303030"
|
||||
}`,
|
||||
},
|
||||
"& .MuiDataGrid-cell": {
|
||||
color:
|
||||
theme.palette.type === "light" ? "white" : "rgba(255,255,255,0.65)",
|
||||
theme?.palette?.type === "light" ? "white" : "rgba(255,255,255,0.65)",
|
||||
},
|
||||
"& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption":
|
||||
{
|
||||
@@ -122,7 +124,8 @@ const useStyles = makeStyles((theme) => ({
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
})
|
||||
|
||||
// Takes an action in Shuffle and
|
||||
// Returns information about the icon, the color etc to be used
|
||||
@@ -481,9 +484,9 @@ export const validateJson = (showResult) => {
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
showResult = showResult.split("'").join('"');
|
||||
|
||||
try {
|
||||
showResult = showResult.split("'").join('"');
|
||||
if (!showResult.includes("{") && !showResult.includes("[")) {
|
||||
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 { globalUrl, isLoggedIn, isLoaded, userdata, checkLogin } = props;
|
||||
|
||||
document.title = "Shuffle - Workflows";
|
||||
let navigate = useNavigate();
|
||||
|
||||
const classes = useStyles(theme);
|
||||
const classes = useStyles(theme)
|
||||
const imgSize = 60;
|
||||
|
||||
const referenceUrl = globalUrl + "/api/v1/hooks/";
|
||||
@@ -1194,8 +1221,8 @@ const Workflows = (props) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson !== undefined) {
|
||||
|
||||
if (responseJson !== undefined) {
|
||||
var newarray = []
|
||||
for (var wfkey in responseJson) {
|
||||
const wf = responseJson[wfkey]
|
||||
@@ -1228,13 +1255,11 @@ const Workflows = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (newarray.length > 0) {
|
||||
try {
|
||||
localStorage.setItem("workflows", JSON.stringify(newarray))
|
||||
} catch (e) {
|
||||
console.log("Failed to set workflows in localstorage: ", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures the zooming happens only once per load
|
||||
setTimeout(() => {
|
||||
@@ -1285,20 +1310,10 @@ const Workflows = (props) => {
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const handleKeysetting = (categorydata, workflows) => {
|
||||
if (workflows !== undefined && workflows !== null) {
|
||||
var newcategories = []
|
||||
for (var key in categorydata) {
|
||||
var category = categorydata[key]
|
||||
// Check if category is bool
|
||||
if (typeof category === "boolean") {
|
||||
continue
|
||||
}
|
||||
|
||||
const findMatches = (category, workflows) => {
|
||||
category.matches = []
|
||||
|
||||
for (var subcategorykey in category.list) {
|
||||
var subcategory = category.list[subcategorykey]
|
||||
subcategory.matches = []
|
||||
@@ -1328,6 +1343,20 @@ const Workflows = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
return category
|
||||
}
|
||||
|
||||
const handleKeysetting = (categorydata, workflows) => {
|
||||
if (workflows !== undefined && workflows !== null) {
|
||||
var newcategories = []
|
||||
for (var key in categorydata) {
|
||||
var category = categorydata[key]
|
||||
// Check if category is bool
|
||||
if (typeof category === "boolean") {
|
||||
continue
|
||||
}
|
||||
|
||||
category = findMatches(category, workflows)
|
||||
newcategories.push(category)
|
||||
}
|
||||
|
||||
@@ -1440,7 +1469,7 @@ const Workflows = (props) => {
|
||||
display: "flex",
|
||||
boxSizing: "border-box",
|
||||
position: "relative",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
};
|
||||
|
||||
@@ -2229,7 +2258,7 @@ const Workflows = (props) => {
|
||||
}
|
||||
|
||||
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}>
|
||||
{selectedCategory !== "" ?
|
||||
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
|
||||
@@ -2277,7 +2306,7 @@ const Workflows = (props) => {
|
||||
title={
|
||||
<div style={{width: "100%", minWidth: 250, maxWidth: 310, }}>
|
||||
{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}
|
||||
<Typography>
|
||||
Edit '{data.name}'
|
||||
@@ -2493,7 +2522,7 @@ const Workflows = (props) => {
|
||||
{workflowMenuButtons}
|
||||
</div>
|
||||
) : 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">
|
||||
<div style={{position: "absolute", top: 45, right: 8, }}>
|
||||
<IconButton
|
||||
@@ -3061,9 +3090,6 @@ const Workflows = (props) => {
|
||||
autoHeight
|
||||
density="standard"
|
||||
onSelectionModelChange={(newSelection) => {
|
||||
//setSelectedWorkflows(newSelection.selectionModel);
|
||||
console.log(newSelection)
|
||||
|
||||
setSelectedWorkflowIndexes(newSelection)
|
||||
}}
|
||||
selectionModel={selectedWorkflowIndexes}
|
||||
@@ -3210,7 +3236,6 @@ const Workflows = (props) => {
|
||||
<em>None</em>
|
||||
</MenuItem>
|
||||
{usecases.map((usecase, index) => {
|
||||
//console.log(usecase)
|
||||
return (
|
||||
<span key={index}>
|
||||
<ListSubheader
|
||||
@@ -3586,7 +3611,7 @@ const Workflows = (props) => {
|
||||
// );
|
||||
// }
|
||||
|
||||
const WorkflowView = () => {
|
||||
const WorkflowView = memo(() => {
|
||||
if (workflows.length === 0) {
|
||||
}
|
||||
|
||||
@@ -3660,9 +3685,14 @@ const Workflows = (props) => {
|
||||
{!isMobile && !hasWorkflows && usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
<div style={{ display: "flex", }}>
|
||||
{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
|
||||
//console.log("Usecase Matches: ", usecase.matches, ", Percent: ", percentDone)
|
||||
if (percentDone === 0) {
|
||||
usecase = findMatches(usecase, workflows)
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper
|
||||
@@ -3671,7 +3701,7 @@ const Workflows = (props) => {
|
||||
flex: 1,
|
||||
backgroundImage: `linear-gradient(to right, ${usecase.color}, ${usecase.color} ${percentDone}%, transparent ${percentDone}%, transparent 100%)`,
|
||||
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,
|
||||
cursor: "pointer",
|
||||
border: `2px solid ${usecase.color}`,
|
||||
@@ -3692,7 +3722,7 @@ const Workflows = (props) => {
|
||||
<Typography variant="body1" color="textPrimary" style={{flex: 4, }}>
|
||||
{usecase.name}
|
||||
</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}
|
||||
</Typography>
|
||||
</span>
|
||||
@@ -3715,7 +3745,7 @@ const Workflows = (props) => {
|
||||
minWidth: isMobile ? "100%" : 1024,
|
||||
zIndex: 11,
|
||||
border: "1px solid rgba(255,255,255,0.1)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
textAlign: "center",
|
||||
overflow: "auto",
|
||||
}}
|
||||
@@ -3734,7 +3764,6 @@ const Workflows = (props) => {
|
||||
}
|
||||
|
||||
if (data.app_name.toLowerCase() === "integration framework") {
|
||||
console.log("Skipping: ", data.app_name)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -3813,7 +3842,7 @@ const Workflows = (props) => {
|
||||
) : null}
|
||||
|
||||
{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:
|
||||
theme.palette.surfaceColor, display: "flex" , maxHeight: "105px", minHeight: "110px"}}
|
||||
>
|
||||
@@ -3905,7 +3934,7 @@ const Workflows = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
const importWorkflowsFromUrl = (url) => {
|
||||
console.log("IMPORT WORKFLOWS FROM ", downloadUrl);
|
||||
@@ -4173,7 +4202,7 @@ const Workflows = (props) => {
|
||||
Setup progress: <b>{isNaN(percentDone) ? 0 : percentDone}%</b>
|
||||
</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, }}>
|
||||
Follow these steps to get you up and running!
|
||||
@@ -4251,17 +4280,7 @@ const Workflows = (props) => {
|
||||
<TourButton />
|
||||
</ShepherdTour>
|
||||
*/}
|
||||
<Dropzone
|
||||
style={{
|
||||
maxWidth: window.innerWidth > 1366 ? 1366 : isMobile ? "100%" : 1200,
|
||||
margin: "auto",
|
||||
padding: 20,
|
||||
paddingLeft: userdata?.support ? 80 : 0
|
||||
}}
|
||||
onDrop={uploadFile}
|
||||
>
|
||||
<WorkflowView />
|
||||
</Dropzone>
|
||||
<DropzoneWrapper onDrop={uploadFile} WorkflowView={WorkflowView}/>
|
||||
{/*modalView*/}
|
||||
{deleteModal}
|
||||
{exportVerifyModal}
|
||||
@@ -4269,7 +4288,7 @@ const Workflows = (props) => {
|
||||
{workflowDownloadModalOpen}
|
||||
|
||||
{/*!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">
|
||||
<IconButton onClick={() => {
|
||||
setDrawerOpen(true)
|
||||
|
||||
@@ -782,7 +782,6 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
|
||||
// image on every Orborus/new worker restart.
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
|
||||
-83
@@ -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
|
||||
Reference in New Issue
Block a user