Merge branch '1.3.0'
This commit is contained in:
@@ -55,29 +55,28 @@ SHUFFLE_PASS_APP_PROXY=FALSE
|
||||
TZ=Europe/Amsterdam # Timezone-handler in Orborus, Worker and Apps
|
||||
ORBORUS_CONTAINER_NAME= # Used to FIND the containername. cgroup v2: issue 501
|
||||
SHUFFLE_ORBORUS_STARTUP_DELAY= # Used for setting up a startup delay for Orborus
|
||||
IS_KUBERNETES=false # Used for controlling if the environment should run in kubernetes or not
|
||||
|
||||
SHUFFLE_BASE_IMAGE_NAME=shuffle
|
||||
SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io
|
||||
SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-1.1.0"
|
||||
|
||||
## shuffle_memcached (for distributed caching)
|
||||
## shuffle_SWARM_CONFIG (run vs not run)
|
||||
## shuffle_Scale_Replicas (workers/node)
|
||||
## shuffle_App_Replicas (apps/node)
|
||||
|
||||
SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500 # 1500 by default
|
||||
# The eth0 interface inside a container corresponds
|
||||
# to the virtual Ethernet interface that connects
|
||||
# the container to the docker0
|
||||
SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE=eth0
|
||||
SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500 # 1500 by default
|
||||
|
||||
# Used for auto-cleanup of containers. REALLY important at scale.
|
||||
SHUFFLE_CONTAINER_AUTO_CLEANUP=false
|
||||
# Used for auto-cleanup of containers. REALLY important at scale. Set to false to see all container info.
|
||||
SHUFFLE_MEMCACHED=
|
||||
SHUFFLE_CONTAINER_AUTO_CLEANUP=true
|
||||
SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY=3 # The amount of concurrent executions Orborus can handle. This is a soft limit, but it's recommended to keep it low.
|
||||
SHUFFLE_HEALTHCHECK_DISABLED=false
|
||||
SHUFFLE_ELASTIC=true
|
||||
SHUFFLE_LOGS_DISABLED=false
|
||||
SHUFFLE_CHAT_DISABLED=false # Controls support chat
|
||||
SHUFFLE_RERUN_SCHEDULE=300
|
||||
SHUFFLE_DISABLE_RERUN_AND_ABORT=false
|
||||
SHUFFLE_RERUN_SCHEDULE=300
|
||||
SHUFFLE_WORKER_SERVER_URL= # Definition in case Worker & Orborus is talking to the wrong server
|
||||
SHUFFLE_ORBORUS_PULL_TIME= # Definition in case Orborus is pulling too often/not often enough
|
||||
|
||||
@@ -93,3 +92,5 @@ SHUFFLE_OPENSEARCH_CLOUDID=
|
||||
SHUFFLE_OPENSEARCH_PROXY=
|
||||
SHUFFLE_OPENSEARCH_INDEX_PREFIX=
|
||||
SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY=true
|
||||
|
||||
DEBUG_MODE=false
|
||||
@@ -8,7 +8,7 @@ The Docker setup is done with docker-compose
|
||||
|
||||
**PS: if you're setting up Shuffle on Windows, go to the next step (Windows Docker setup)**
|
||||
|
||||
1. Make sure you have [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed.
|
||||
1. Make sure you have [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed, and that you have a minimum of **2Gb of RAM** available.
|
||||
2. Download Shuffle
|
||||
```bash
|
||||
git clone https://github.com/Shuffle/Shuffle
|
||||
|
||||
@@ -33,22 +33,24 @@ jobs:
|
||||
experimental: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: 'amd64,arm64,arm'
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Ghcr
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -56,7 +58,7 @@ jobs:
|
||||
|
||||
- name: Ghcr Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
env:
|
||||
BUILDX_NO_DEFAULT_LOAD: true
|
||||
with:
|
||||
@@ -68,8 +70,8 @@ jobs:
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
tags: |
|
||||
ghcr.io/shuffle/shuffle-${{ matrix.app }}:nightly
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:nightly
|
||||
ghcr.io/shuffle/shuffle-${{ matrix.app }}:${{ matrix.version }}
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:${{ matrix.version }}
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
+1
-1
@@ -41,4 +41,4 @@ COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certifica
|
||||
|
||||
WORKDIR /app
|
||||
EXPOSE 5001
|
||||
CMD ["./webapp"]
|
||||
CMD ["./webapp"]
|
||||
+58
-29
@@ -1,11 +1,12 @@
|
||||
import os
|
||||
import ast
|
||||
import copy
|
||||
import sys
|
||||
import re
|
||||
import copy
|
||||
import time
|
||||
import base64
|
||||
import json
|
||||
import random
|
||||
import liquid
|
||||
import logging
|
||||
import urllib3
|
||||
@@ -22,8 +23,7 @@ import dateutil
|
||||
import threading
|
||||
import concurrent.futures
|
||||
|
||||
from io import StringIO as StringBuffer
|
||||
from io import BytesIO
|
||||
from io import StringIO as StringBuffer, BytesIO
|
||||
from liquid import Liquid, defaults
|
||||
|
||||
runtime = os.getenv("SHUFFLE_SWARM_CONFIG", "")
|
||||
@@ -105,9 +105,12 @@ def base64_encode(a):
|
||||
def base64_decode(a):
|
||||
a = str(a)
|
||||
try:
|
||||
return base64.b64decode(a).decode()
|
||||
return base64.b64decode(a).decode("unicode_escape")
|
||||
except:
|
||||
return base64.b64decode(a)
|
||||
try:
|
||||
return base64.b64decode(a).decode()
|
||||
except:
|
||||
return base64.b64decode(a)
|
||||
|
||||
@shuffle_filters.register
|
||||
def json_parse(a):
|
||||
@@ -502,10 +505,12 @@ class AppBase:
|
||||
except Exception as e:
|
||||
print(f"[WARNING] Failed adding parameter for logs: {e}")
|
||||
|
||||
# FIXME: Adding retries here.
|
||||
try:
|
||||
finished = False
|
||||
for i in range (0, 10):
|
||||
# Random sleeptime between 0 and 1 second, with 0.1 increments
|
||||
sleeptime = float(random.randint(0, 10) / 10)
|
||||
|
||||
try:
|
||||
ret = requests.post(url, headers=headers, json=action_result, timeout=10, verify=False)
|
||||
|
||||
@@ -514,29 +519,29 @@ class AppBase:
|
||||
finished = True
|
||||
break
|
||||
else:
|
||||
self.logger.info(f"[ERROR] RESP: {ret.text}")
|
||||
self.logger.info(f"[ERROR] Bad resp {ret.status_code}: {ret.text}")
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.logger.info(f"[DEBUG] Request problem: {e}")
|
||||
time.sleep(0.1)
|
||||
time.sleep(sleeptime)
|
||||
|
||||
#time.sleep(5)
|
||||
continue
|
||||
except TimeoutError as e:
|
||||
self.logger.info(f"[DEBUG] Timeout or request: {e}")
|
||||
time.sleep(0.1)
|
||||
time.sleep(sleeptime)
|
||||
|
||||
#time.sleep(5)
|
||||
continue
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
self.logger.info(f"[DEBUG] Connectionerror: {e}")
|
||||
time.sleep(0.1)
|
||||
time.sleep(sleeptime)
|
||||
|
||||
#time.sleep(5)
|
||||
continue
|
||||
except http.client.RemoteDisconnected as e:
|
||||
self.logger.info(f"[DEBUG] Remote: {e}")
|
||||
time.sleep(0.1)
|
||||
time.sleep(sleeptime)
|
||||
|
||||
#time.sleep(5)
|
||||
continue
|
||||
@@ -553,8 +558,11 @@ class AppBase:
|
||||
# Not sure why this would work tho :)
|
||||
action_result["status"] = "FAILURE"
|
||||
action_result["result"] = json.dumps({"success": False, "reason": "POST error: Failed connecting to %s over 10 retries to the backend" % url})
|
||||
self.logger.info(f"[DEBUG] Before typeerror stream result - NOT finished after 10 requests")
|
||||
ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result, verify=False)
|
||||
self.logger.info(f"[ERROR] Before typeerror stream result - NOT finished after 10 requests")
|
||||
|
||||
#ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result, verify=False)
|
||||
self.send_result(action_result, {"Content-Type": "application/json", "Authorization": "Bearer %s" % self.authorization}, "/api/v1/streams")
|
||||
return
|
||||
|
||||
self.logger.info(f"""[DEBUG] Successful request result request: Status= {ret.status_code} & Response= {ret.text}. Action status: {action_result["status"]}""")
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
@@ -1291,6 +1299,27 @@ class AppBase:
|
||||
else:
|
||||
return returns
|
||||
|
||||
def delete_cache(self, key):
|
||||
org_id = self.full_execution["workflow"]["execution_org"]["id"]
|
||||
url = "%s/api/v1/orgs/%s/delete_cache" % (self.url, org_id)
|
||||
|
||||
data = {
|
||||
"workflow_id": self.full_execution["workflow"]["id"],
|
||||
"execution_id": self.current_execution_id,
|
||||
"authorization": self.authorization,
|
||||
"org_id": org_id,
|
||||
"key": key,
|
||||
}
|
||||
|
||||
response = requests.post(url, json=data, verify=False)
|
||||
try:
|
||||
allvalues = response.json()
|
||||
return json.dumps(allvalues)
|
||||
except Exception as e:
|
||||
self.logger.info("[ERROR} Failed to parse response from delete_cache: %s" % e)
|
||||
#return response.json()
|
||||
return json.dumps({"success": False, "reason": f"Failed to delete cache for key '{key}'"})
|
||||
|
||||
def set_cache(self, key, value):
|
||||
org_id = self.full_execution["workflow"]["execution_org"]["id"]
|
||||
url = "%s/api/v1/orgs/%s/set_cache" % (self.url, org_id)
|
||||
@@ -1309,8 +1338,8 @@ class AppBase:
|
||||
allvalues["key"] = key
|
||||
allvalues["value"] = str(value)
|
||||
return allvalues
|
||||
except:
|
||||
self.logger.info("Value couldn't be parsed")
|
||||
except Exception as e:
|
||||
self.logger.info("[ERROR} Failed to parse response from set cache: %s" % e)
|
||||
#return response.json()
|
||||
return {"success": False}
|
||||
|
||||
@@ -2277,10 +2306,13 @@ class AppBase:
|
||||
#if len(template) > 100:
|
||||
# self.logger.info("[DEBUG] Running liquid with data of length %d" % len(template))
|
||||
#self.logger.info(f"[DEBUG] Data: {template}")
|
||||
run = Liquid(template, mode="wild", from_file=False, filters=shuffle_filters.filters)
|
||||
|
||||
# Can't handle self yet (?)
|
||||
ret = run.render(**globals())
|
||||
all_globals = globals()
|
||||
all_globals["self"] = self
|
||||
run = Liquid(template, mode="wild", from_file=False, filters=shuffle_filters.filters, globals=all_globals)
|
||||
|
||||
# Add locals that are missing to globals
|
||||
ret = run.render()
|
||||
return ret
|
||||
except jinja2.exceptions.TemplateNotFound as e:
|
||||
self.logger.info(f"[ERROR] Liquid Template error: {e}")
|
||||
@@ -3487,7 +3519,7 @@ class AppBase:
|
||||
if self.action["app_name"].lower() == "shuffle tools":
|
||||
timeout = 55
|
||||
|
||||
timeout = 30
|
||||
#timeout = 30
|
||||
|
||||
try:
|
||||
executor = concurrent.futures.ThreadPoolExecutor()
|
||||
@@ -3556,15 +3588,11 @@ class AppBase:
|
||||
|
||||
if "the JSON object must be" in errorstring:
|
||||
self.logger.info("[ERROR] Something is wrong with the input for this function. Are lists and JSON data handled parsed properly (0)? the JSON object must be in...")
|
||||
try:
|
||||
e = json.loads(f"{e}")
|
||||
except:
|
||||
e = f"{e}"
|
||||
|
||||
newres = json.dumps({
|
||||
"success": False,
|
||||
"reason": "An exception occurred while running this function (1). See exception for more details and contact support if this persists (support@shuffler.io)",
|
||||
"exception": e,
|
||||
"exception": f"{type(e).__name__} - {e}",
|
||||
})
|
||||
break
|
||||
elif "got an unexpected keyword argument" in errorstring:
|
||||
@@ -3587,15 +3615,16 @@ class AppBase:
|
||||
except Exception as e:
|
||||
self.logger.info(f"[ERROR] Something is wrong with the input for this function. Are lists and JSON data handled parsed properly (1)? err: {e}")
|
||||
|
||||
try:
|
||||
e = json.loads(f"{e}")
|
||||
except:
|
||||
e = f"{e}"
|
||||
#try:
|
||||
# e = json.loads(f"{e}")
|
||||
#except:
|
||||
# e = f"{e}"
|
||||
|
||||
newres = json.dumps({
|
||||
"success": False,
|
||||
"reason": "An exception occurred while running this function (2). See exception for more details and contact support if this persists (support@shuffler.io)",
|
||||
"exception": e,
|
||||
"exception": f"{type(e).__name__} - {e}",
|
||||
|
||||
})
|
||||
break
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
urllib3==1.26.18
|
||||
requests==2.31.0
|
||||
MarkupSafe==2.0.1
|
||||
liquidpy==0.7.6
|
||||
liquidpy==0.8.1
|
||||
flask[async]==2.0.2
|
||||
waitress==2.1.0
|
||||
#flask==1.1.2
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Run
|
||||
go run main.go walkoff.go docker.go
|
||||
|
||||
## Modify
|
||||
- Make sure it's connected with the latest version of the shuffle-shared library, which is used to get resources from Shuffle
|
||||
|
||||
## Database
|
||||
- The database is Opensearch and can be modified with the SHUFFLE_OPENSEARCH_URL environment variable. See .env in the root directory for more. This requires Opensearch to be running (typically started from docker-compose.yml)
|
||||
```
|
||||
docker-compose up -d
|
||||
docker stop shuffle-backend
|
||||
docker stop shuffle-frontend
|
||||
docker stop shuffle-orborus
|
||||
```
|
||||
|
||||
## Caching
|
||||
- To handle caching, it by default runs it in memory of the application itself. If you want to offload this, it can be done using the SHUFFLE_MEMCACHED environment variable, connecting to a memcached instance.
|
||||
```
|
||||
docker run --name shuffle-cache -p 11211:11211 -d memcached -m 1024
|
||||
```
|
||||
+217
-58
@@ -33,6 +33,15 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"time"
|
||||
// "k8s.io/client-go/tools/clientcmd"
|
||||
// "k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
// Parses a directory with a Dockerfile into a tar for Docker images..
|
||||
@@ -318,70 +327,220 @@ func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func getK8sClient() (*kubernetes.Clientset, error) {
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[ERROR] failed to get in-cluster config: %v", err)
|
||||
}
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[ERROR] failed to create Kubernetes client: %v", err)
|
||||
}
|
||||
|
||||
return clientset, nil
|
||||
}
|
||||
|
||||
func deleteJob(client *kubernetes.Clientset, jobName, namespace string) error {
|
||||
deletePolicy := metav1.DeletePropagationForeground
|
||||
return client.BatchV1().Jobs(namespace).Delete(context.TODO(), jobName, metav1.DeleteOptions{
|
||||
PropagationPolicy: &deletePolicy,
|
||||
})
|
||||
}
|
||||
|
||||
func buildImage(tags []string, dockerfileFolder string) error {
|
||||
ctx := context.Background()
|
||||
client, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
return err
|
||||
|
||||
isKubernetes := false
|
||||
if os.Getenv("IS_KUBERNETES") == "true" {
|
||||
isKubernetes = true
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Docker Tags: %s", tags)
|
||||
dockerfileSplit := strings.Split(dockerfileFolder, "/")
|
||||
if isKubernetes {
|
||||
// log.Printf("K8S ###################")
|
||||
// log.Print("dockerfileFolder: ", dockerfileFolder)
|
||||
// log.Print("tags: ", tags)
|
||||
// log.Print("only tag: ", tags[1])
|
||||
|
||||
// Create a buffer
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
defer tw.Close()
|
||||
baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/")
|
||||
|
||||
// Builds the entire folder into buf
|
||||
err = getParsedTar(tw, baseDir, "")
|
||||
if err != nil {
|
||||
log.Printf("Tar issue: %s", err)
|
||||
}
|
||||
|
||||
dockerFileTarReader := bytes.NewReader(buf.Bytes())
|
||||
buildOptions := types.ImageBuildOptions{
|
||||
Remove: true,
|
||||
Tags: tags,
|
||||
BuildArgs: map[string]*string{},
|
||||
}
|
||||
//NetworkMode: "host",
|
||||
|
||||
httpProxy := os.Getenv("HTTP_PROXY")
|
||||
if len(httpProxy) > 0 {
|
||||
buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy
|
||||
}
|
||||
httpsProxy := os.Getenv("HTTPS_PROXY")
|
||||
if len(httpProxy) > 0 {
|
||||
buildOptions.BuildArgs["https_proxy"] = &httpsProxy
|
||||
}
|
||||
|
||||
// Build the actual image
|
||||
imageBuildResponse, err := client.ImageBuild(
|
||||
ctx,
|
||||
dockerFileTarReader,
|
||||
buildOptions,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the STDOUT from the build process
|
||||
defer imageBuildResponse.Body.Close()
|
||||
buildBuf := new(strings.Builder)
|
||||
_, err = io.Copy(buildBuf, imageBuildResponse.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if strings.Contains(buildBuf.String(), "errorDetail") {
|
||||
log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n"))
|
||||
return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ",")))
|
||||
registryName := ""
|
||||
if len(os.Getenv("REGISTRY_URL")) > 0 {
|
||||
registryName = os.Getenv("REGISTRY_URL")
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[INFO] registry name: %s", registryName)
|
||||
|
||||
contextDir := strings.Replace(dockerfileFolder, "Dockerfile", "", -1)
|
||||
contextDir = "/app/" + contextDir
|
||||
log.Print("contextDir: ", contextDir)
|
||||
dockerFile := "./Dockerfile"
|
||||
|
||||
client, err := getK8sClient()
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to authencticate : %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
BackendPodLabel := "io.kompose.service=backend"
|
||||
|
||||
backendPodList, podListErr := client.CoreV1().Pods("shuffle").List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: BackendPodLabel,
|
||||
})
|
||||
|
||||
if podListErr != nil || len(backendPodList.Items) == 0 {
|
||||
fmt.Println("Error getting backend pod or no pod found:", podListErr)
|
||||
return podListErr
|
||||
}
|
||||
|
||||
backendNodeName := backendPodList.Items[0].Spec.NodeName
|
||||
log.Printf("[INFO] Backend running on: %s", backendNodeName)
|
||||
|
||||
job := &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "shuffle-app-builder",
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "kaniko",
|
||||
Image: "gcr.io/kaniko-project/executor:latest",
|
||||
Args: []string{
|
||||
"--verbosity=debug",
|
||||
"--dockerfile=" + dockerFile,
|
||||
"--context=dir://" + contextDir,
|
||||
"--skip-tls-verify",
|
||||
"--destination=" + registryName + "/" + tags[1],
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
Name: "kaniko-workspace",
|
||||
MountPath: "/app/generated",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NodeSelector: map[string]string{
|
||||
"node": backendNodeName,
|
||||
},
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Volumes: []corev1.Volume{
|
||||
{
|
||||
Name: "kaniko-workspace",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: "backend-apps-claim",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
createdJob, err := client.BatchV1().Jobs("shuffle").Create(context.TODO(), job, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
log.Printf("Failed to start image builder job: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
timeout := time.After(5 * time.Minute)
|
||||
tick := time.Tick(5 * time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return fmt.Errorf("job didn't complete within the expected time")
|
||||
case <-tick:
|
||||
currentJob, err := client.BatchV1().Jobs("shuffle").Get(context.TODO(), createdJob.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("[ERROR] failed to fetch %s status: %v", createdJob.Name, err)
|
||||
}
|
||||
|
||||
if currentJob.Status.Succeeded > 0 {
|
||||
log.Printf("[INFO] Job %s completed successfully!", createdJob.Name)
|
||||
log.Printf("[INFO] Cleaning up the job %s", createdJob.Name)
|
||||
err := deleteJob(client, createdJob.Name, "shuffle")
|
||||
if err != nil {
|
||||
return fmt.Errorf("[ERROR] failed deleting job %s with error: %s", createdJob.Name, err)
|
||||
}
|
||||
log.Println("Job deleted successfully!")
|
||||
return nil
|
||||
} else if currentJob.Status.Failed > 0 {
|
||||
log.Printf("[ERROR] %s job failed with error: %s", createdJob.Name, err)
|
||||
err := deleteJob(client, createdJob.Name, "shuffle")
|
||||
if err != nil {
|
||||
return fmt.Errorf("[ERROR] failed deleting job %s with error: %s", createdJob.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
ctx := context.Background()
|
||||
client, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Docker Tags: %s", tags)
|
||||
dockerfileSplit := strings.Split(dockerfileFolder, "/")
|
||||
|
||||
// Create a buffer
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
defer tw.Close()
|
||||
baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/")
|
||||
|
||||
// Builds the entire folder into buf
|
||||
err = getParsedTar(tw, baseDir, "")
|
||||
if err != nil {
|
||||
log.Printf("Tar issue: %s", err)
|
||||
}
|
||||
|
||||
dockerFileTarReader := bytes.NewReader(buf.Bytes())
|
||||
buildOptions := types.ImageBuildOptions{
|
||||
Remove: true,
|
||||
Tags: tags,
|
||||
BuildArgs: map[string]*string{},
|
||||
}
|
||||
//NetworkMode: "host",
|
||||
|
||||
httpProxy := os.Getenv("HTTP_PROXY")
|
||||
if len(httpProxy) > 0 {
|
||||
buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy
|
||||
}
|
||||
httpsProxy := os.Getenv("HTTPS_PROXY")
|
||||
if len(httpProxy) > 0 {
|
||||
buildOptions.BuildArgs["https_proxy"] = &httpsProxy
|
||||
}
|
||||
|
||||
// Build the actual image
|
||||
imageBuildResponse, err := client.ImageBuild(
|
||||
ctx,
|
||||
dockerFileTarReader,
|
||||
buildOptions,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the STDOUT from the build process
|
||||
defer imageBuildResponse.Body.Close()
|
||||
buildBuf := new(strings.Builder)
|
||||
_, err = io.Copy(buildBuf, imageBuildResponse.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if strings.Contains(buildBuf.String(), "errorDetail") {
|
||||
log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n"))
|
||||
return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ",")))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+22
-5
@@ -1,6 +1,6 @@
|
||||
module shuffle-shared
|
||||
|
||||
replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared
|
||||
//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
|
||||
|
||||
go 1.19
|
||||
|
||||
@@ -19,13 +19,16 @@ require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.4.19
|
||||
golang.org/x/crypto v0.14.0
|
||||
github.com/shuffle/shuffle-shared v0.4.66
|
||||
golang.org/x/crypto v0.9.0
|
||||
google.golang.org/api v0.125.0
|
||||
google.golang.org/appengine v1.6.7
|
||||
google.golang.org/grpc v1.55.0
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.22.5
|
||||
k8s.io/apimachinery v0.22.5
|
||||
k8s.io/client-go v0.22.5
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -45,11 +48,13 @@ require (
|
||||
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect
|
||||
github.com/cloudflare/circl v1.3.3 // indirect
|
||||
github.com/containerd/containerd v1.6.18 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-logr/logr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.5 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
@@ -58,18 +63,23 @@ require (
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-github/v28 v28.1.1 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.11.13 // indirect
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect
|
||||
github.com/moby/patternmatcher v0.5.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
@@ -91,15 +101,22 @@ require (
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.8.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/term v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.30.0 // indirect
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
|
||||
)
|
||||
|
||||
@@ -0,0 +1,887 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
|
||||
cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds=
|
||||
cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0=
|
||||
cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774=
|
||||
cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c=
|
||||
cloud.google.com/go/iam v1.0.1 h1:lyeCAU6jpnVNrE9zGQkTl3WgNgK/X+uWwaw0kynZJMU=
|
||||
cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/pubsub v1.31.0 h1:aXdyyJz90kA+bor9+6+xHAciMD5mj8v15WqFZ5E0sek=
|
||||
cloud.google.com/go/pubsub v1.31.0/go.mod h1:dYmJ3K97NCQ/e4OwZ20rD4Ym3Bu8Gu9m/aJdWQjdcks=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho=
|
||||
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
|
||||
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
|
||||
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ=
|
||||
github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ=
|
||||
github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
|
||||
github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
|
||||
github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y=
|
||||
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w=
|
||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82 h1:9bAydALqAjBfPHd/eAiJBHnMZUYov8m2PkXVr+YGQeI=
|
||||
github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA14J0sA3Hph4dt+AfCjPrYR13+vVodshQSM7km9qw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns=
|
||||
github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg=
|
||||
github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ=
|
||||
github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
|
||||
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw=
|
||||
github.com/fsouza/go-dockerclient v1.9.7/go.mod h1:vx9C32kE2D15yDSOMCDaAEIARZpDQDFBHeqL3MgQy/U=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
|
||||
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
||||
github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
|
||||
github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
|
||||
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg=
|
||||
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
|
||||
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4=
|
||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
|
||||
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
|
||||
github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M=
|
||||
github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo=
|
||||
github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ=
|
||||
github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shuffle/shuffle-shared v0.4.56 h1:Z29GZB/eipt2aA4i+6heO1V2mrUmHPdw4QN4X4b1HdU=
|
||||
github.com/shuffle/shuffle-shared v0.4.56/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
|
||||
github.com/shuffle/shuffle-shared v0.4.59 h1:5Sv8aorgQJFZr3cCKltfycdXzp9v5zlF2l3GZXjrTEo=
|
||||
github.com/shuffle/shuffle-shared v0.4.59/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE=
|
||||
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8=
|
||||
go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
|
||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo=
|
||||
google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.125.0 h1:7xGvEY4fyWbhWMHf3R2/4w7L4fXyfpRGE9g6lp8+DCk=
|
||||
google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
|
||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
|
||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.22.5 h1:xk7C+rMjF/EGELiD560jdmwzrB788mfcHiNbMQLIVI8=
|
||||
k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=
|
||||
k8s.io/apimachinery v0.22.5 h1:cIPwldOYm1Slq9VLBRPtEYpyhjIm1C6aAMAoENuvN9s=
|
||||
k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U=
|
||||
k8s.io/client-go v0.22.5 h1:I8Zn/UqIdi2r02aZmhaJ1hqMxcpfJ3t5VqvHtctHYFo=
|
||||
k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=
|
||||
k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs=
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
+135
-1279
File diff suppressed because it is too large
Load Diff
+8
-160
@@ -561,7 +561,7 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
if len(workflowExecution.ExecutionOrg) > 0 && user.ActiveOrg.Id == workflowExecution.ExecutionOrg && user.Role == "admin" {
|
||||
log.Printf("[DEBUG] User %s is in correct org. Allowing org continuation for execution!", user.Username)
|
||||
//log.Printf("[DEBUG] User %s is in correct org. Allowing org continuation for execution!", user.Username)
|
||||
} else {
|
||||
log.Printf("[WARNING] Bad authorization key when getting stream results %s.", actionResult.ExecutionId)
|
||||
resp.WriteHeader(401)
|
||||
@@ -700,64 +700,6 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Removed as UserInput is now handled as an app
|
||||
if actionResult.Status == "WAITING" && actionResult.Action.AppName == "User Input" {
|
||||
log.Printf("[INFO] SHOULD WAIT A BIT AND RUN USER INPUT! WAITING!")
|
||||
|
||||
var trigger shuffle.Trigger
|
||||
err = json.Unmarshal([]byte(actionResult.Result), &trigger)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed unmarshaling actionresult for user input: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
orgId := workflowExecution.ExecutionOrg
|
||||
if len(workflowExecution.OrgId) == 0 && len(workflowExecution.Workflow.OrgId) > 0 {
|
||||
orgId = workflowExecution.Workflow.OrgId
|
||||
}
|
||||
|
||||
err := handleUserInput(trigger, orgId, workflowExecution.Workflow.ID, workflowExecution.ExecutionId)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed userinput handler: %s", err)
|
||||
|
||||
actionResult.Result = fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)
|
||||
|
||||
workflowExecution.Results = append(workflowExecution.Results, actionResult)
|
||||
workflowExecution.Status = "ABORTED"
|
||||
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed to set execution during wait: %s", err)
|
||||
} else {
|
||||
log.Printf("[INFO] Successfully set the execution %s to waiting.", workflowExecution.ExecutionId)
|
||||
}
|
||||
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Error: %s"}`, err)))
|
||||
return
|
||||
} else {
|
||||
log.Printf("[INFO] Successful userinput handler")
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "CLOUD IS DONE"}`)))
|
||||
|
||||
actionResult.Result = `{"success": True, "reason": "Waiting for user feedback based on configuration"}`
|
||||
|
||||
workflowExecution.Results = append(workflowExecution.Results, actionResult)
|
||||
workflowExecution.Status = actionResult.Status
|
||||
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed setting userinput: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Successfully set the execution to waiting.")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
|
||||
}
|
||||
|
||||
@@ -992,6 +934,7 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
cacheKey := fmt.Sprintf("%s_workflows", user.Id)
|
||||
shuffle.DeleteCache(ctx, cacheKey)
|
||||
shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.ActiveOrg.Id))
|
||||
log.Printf("[DEBUG] Cleared workflow cache for %s (%s)", user.Username, user.Id)
|
||||
|
||||
resp.WriteHeader(200)
|
||||
@@ -1187,7 +1130,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
var execution shuffle.ExecutionRequest
|
||||
err = json.Unmarshal(body, &execution)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed execution POST unmarshaling - continuing anyway: %s", err)
|
||||
log.Printf("[WARNING] Failed execution POST unmarshalling for execution %s - continuing anyway: %s", execution.ExecutionId, err)
|
||||
//return shuffle.WorkflowExecution{}, "", err
|
||||
}
|
||||
|
||||
@@ -1390,13 +1333,10 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
|
||||
childNodes := shuffle.FindChildNodes(workflowExecution, workflowExecution.Start, []string{}, []string{})
|
||||
|
||||
//topic := "workflows"
|
||||
startFound := false
|
||||
// FIXME - remove this?
|
||||
newActions := []shuffle.Action{}
|
||||
defaultResults := []shuffle.ActionResult{}
|
||||
|
||||
allAuths := []shuffle.AppAuthenticationStorage{}
|
||||
for _, action := range workflowExecution.Workflow.Actions {
|
||||
//action.LargeImage = ""
|
||||
if action.ID == workflowExecution.Start {
|
||||
@@ -1408,98 +1348,6 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
return shuffle.WorkflowExecution{}, fmt.Sprintf("Environment is not defined for %s", action.Name), errors.New("Environment not defined!")
|
||||
}
|
||||
|
||||
// FIXME: Authentication parameters
|
||||
if len(action.AuthenticationId) > 0 {
|
||||
if len(allAuths) == 0 {
|
||||
allAuths, err = shuffle.GetAllWorkflowAppAuth(ctx, workflow.ExecutingOrg.Id)
|
||||
if err != nil {
|
||||
log.Printf("Api authentication failed in get all app auth: %s", err)
|
||||
return shuffle.WorkflowExecution{}, fmt.Sprintf("Api authentication failed in get all app auth: %s", err), err
|
||||
}
|
||||
}
|
||||
|
||||
curAuth := shuffle.AppAuthenticationStorage{Id: ""}
|
||||
for _, auth := range allAuths {
|
||||
if auth.Id == action.AuthenticationId {
|
||||
curAuth = auth
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(curAuth.Id) == 0 {
|
||||
return shuffle.WorkflowExecution{}, fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId), errors.New(fmt.Sprintf("Auth ID %s doesn't exist", action.AuthenticationId))
|
||||
}
|
||||
|
||||
if curAuth.Encrypted {
|
||||
setField := true
|
||||
newFields := []shuffle.AuthenticationStore{}
|
||||
for _, field := range curAuth.Fields {
|
||||
parsedKey := fmt.Sprintf("%s_%d_%s_%s", curAuth.OrgId, curAuth.Created, curAuth.Label, field.Key)
|
||||
newValue, err := shuffle.HandleKeyDecryption([]byte(field.Value), parsedKey)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed decryption for %s: %s", field.Key, err)
|
||||
setField = false
|
||||
break
|
||||
}
|
||||
|
||||
field.Value = string(newValue)
|
||||
newFields = append(newFields, field)
|
||||
}
|
||||
|
||||
if setField {
|
||||
curAuth.Fields = newFields
|
||||
}
|
||||
} else {
|
||||
log.Printf("[INFO] AUTH IS NOT ENCRYPTED - attempting encrypting!")
|
||||
err = shuffle.SetWorkflowAppAuthDatastore(ctx, curAuth, curAuth.Id)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed running encryption during execution: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
newParams := []shuffle.WorkflowAppActionParameter{}
|
||||
if strings.ToLower(curAuth.Type) == "oauth2" {
|
||||
log.Printf("[DEBUG] Should replace auth parameters (Oauth2)")
|
||||
|
||||
for _, param := range curAuth.Fields {
|
||||
if param.Key == "expiration" {
|
||||
continue
|
||||
}
|
||||
|
||||
newParams = append(newParams, shuffle.WorkflowAppActionParameter{
|
||||
Name: param.Key,
|
||||
Value: param.Value,
|
||||
})
|
||||
}
|
||||
|
||||
for _, param := range action.Parameters {
|
||||
//log.Printf("Param: %#v", param)
|
||||
if param.Configuration {
|
||||
continue
|
||||
}
|
||||
|
||||
newParams = append(newParams, param)
|
||||
}
|
||||
} else {
|
||||
// Rebuild params with the right data. This is to prevent issues on the frontend
|
||||
for _, param := range action.Parameters {
|
||||
|
||||
for _, authparam := range curAuth.Fields {
|
||||
if param.Name == authparam.Key {
|
||||
param.Value = authparam.Value
|
||||
//log.Printf("Name: %s - value: %s", param.Name, param.Value)
|
||||
//log.Printf("Name: %s - value: %s\n", param.Name, param.Value)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
newParams = append(newParams, param)
|
||||
}
|
||||
}
|
||||
|
||||
action.Parameters = newParams
|
||||
}
|
||||
|
||||
action.LargeImage = ""
|
||||
if len(action.Label) == 0 {
|
||||
action.Label = action.ID
|
||||
@@ -1844,6 +1692,8 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
return shuffle.WorkflowExecution{}, "Cloud not implemented yet", errors.New("Cloud not implemented yet")
|
||||
}
|
||||
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_cloud")
|
||||
|
||||
// What it needs to know:
|
||||
// 1. Parameters
|
||||
if len(workflowExecution.Workflow.Actions) == 1 {
|
||||
@@ -1857,13 +1707,11 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
// If worker, should this backend be a proxy? I think so.
|
||||
return shuffle.WorkflowExecution{}, "Cloud not implemented yet (2)", errors.New("Cloud not implemented yet")
|
||||
}
|
||||
} else {
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_onprem")
|
||||
}
|
||||
|
||||
//err = increaseStatisticsField(ctx, "workflow_executions", workflow.ID, 1, workflowExecution.ExecutionOrg)
|
||||
//if err != nil {
|
||||
// log.Printf("Failed to increase stats execution stats: %s", err)
|
||||
//}
|
||||
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions")
|
||||
return workflowExecution, "", nil
|
||||
}
|
||||
|
||||
|
||||
+16
-6
@@ -1,7 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
frontend:
|
||||
image: ghcr.io/shuffle/shuffle-frontend:latest
|
||||
image: ghcr.io/shuffle/shuffle-frontend:nightly
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
depends_on:
|
||||
- backend
|
||||
backend:
|
||||
image: ghcr.io/shuffle/shuffle-backend:latest
|
||||
image: ghcr.io/shuffle/shuffle-backend:nightly
|
||||
container_name: shuffle-backend
|
||||
hostname: ${BACKEND_HOSTNAME}
|
||||
# Here for debugging:
|
||||
@@ -34,7 +34,7 @@ services:
|
||||
- SHUFFLE_FILE_LOCATION=/shuffle-files
|
||||
restart: unless-stopped
|
||||
orborus:
|
||||
image: ghcr.io/shuffle/shuffle-orborus:latest
|
||||
image: ghcr.io/shuffle/shuffle-orborus:nightly
|
||||
container_name: shuffle-orborus
|
||||
hostname: shuffle-orborus
|
||||
networks:
|
||||
@@ -57,12 +57,13 @@ services:
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
opensearch:
|
||||
image: opensearchproject/opensearch:2.5.0
|
||||
image: opensearchproject/opensearch:2.11.0
|
||||
hostname: shuffle-opensearch
|
||||
container_name: shuffle-opensearch
|
||||
environment:
|
||||
- bootstrap.memory_lock=true
|
||||
- "OPENSEARCH_JAVA_OPTS=-Xms2048m -Xmx2048m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
|
||||
- bootstrap.memory_lock=true
|
||||
- DISABLE_PERFORMANCE_ANALYZER_AGENT_CLI=true
|
||||
- cluster.initial_master_nodes=shuffle-opensearch
|
||||
- cluster.routing.allocation.disk.threshold_enabled=false
|
||||
- cluster.name=shuffle-cluster
|
||||
@@ -83,9 +84,17 @@ services:
|
||||
networks:
|
||||
- shuffle
|
||||
restart: unless-stopped
|
||||
|
||||
#memcached:
|
||||
# image: docker.io/bitnami/memcached:1
|
||||
# container_name: shuffle-cache
|
||||
# hostname: shuffle-cache
|
||||
# ports:
|
||||
# - 11211:11211
|
||||
|
||||
#docker-socket-proxy:
|
||||
# image: tecnativa/docker-socket-proxy
|
||||
# container_name: shuffle-frontend
|
||||
# container_name: docker-socket-proxy
|
||||
# hostname: docker-socket-proxy
|
||||
# privileged: true
|
||||
# environment:
|
||||
@@ -110,6 +119,7 @@ services:
|
||||
# - /var/run/docker.sock:/var/run/docker.sock
|
||||
# networks:
|
||||
# - shuffle
|
||||
#
|
||||
networks:
|
||||
shuffle:
|
||||
driver: bridge
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
# Build environment
|
||||
FROM node:14 as builder
|
||||
FROM node:18 as builder
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
|
||||
@@ -8,9 +8,12 @@ ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
|
||||
COPY package.json /usr/src/app/package.json
|
||||
|
||||
# Nocache yarn install
|
||||
RUN yarn config set "strict-ssl" false -g
|
||||
RUN yarn install --network-timeout 1000000
|
||||
|
||||
#RUN npm install
|
||||
|
||||
# copy only required files to not trigger rebuilding every time
|
||||
COPY ./certs /usr/src/app/certs/
|
||||
COPY ./public /usr/src/app/public/
|
||||
@@ -18,7 +21,7 @@ COPY ./src /usr/src/app/src/
|
||||
COPY ./*.sh /usr/src/app/
|
||||
COPY ./*.json /usr/src/app/
|
||||
|
||||
RUN rm -rf /usr/src/app/node_modules/webpack
|
||||
#RUN rm -rf /usr/src/app/node_modules/webpack
|
||||
RUN yarn build
|
||||
|
||||
# Production environment
|
||||
|
||||
@@ -70,11 +70,11 @@ http {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location /api/v1 {
|
||||
location ~ /api/v(1|2) {
|
||||
proxy_pass http://{{ getenv "BACKEND_HOSTNAME" "shuffle-backend" }}:5001;
|
||||
proxy_buffering off;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
|
||||
proxy_connect_timeout 900;
|
||||
proxy_send_timeout 900;
|
||||
proxy_read_timeout 900;
|
||||
|
||||
+45
-43
@@ -1,88 +1,88 @@
|
||||
{
|
||||
"name": "shuffler",
|
||||
"homepage": "https://shuffler.io",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.2.2",
|
||||
"@codemirror/commands": "^6.2.4",
|
||||
"@emotion/is-prop-valid": "^1.1.1",
|
||||
"@emotion/react": "^11.7.0",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@material-ui/core": "^4.5.2",
|
||||
"@material-ui/icons": "^4.5.1",
|
||||
"@material-ui/lab": "^4.0.0-alpha.58",
|
||||
"@material-ui/styles": "^4.5.2",
|
||||
"@material-ui/utils": "^4.11.2",
|
||||
"@metamask/detect-provider": "^1.2.0",
|
||||
"@mui/icons-material": "^5.2.1",
|
||||
"@mui/material": "^5.2.3",
|
||||
"@mui/icons-material": "^5.14.0",
|
||||
"@mui/material": "^5.14.0",
|
||||
"@mui/styles": "^5.14.0",
|
||||
"@mui/x-data-grid": "^5.17.11",
|
||||
"@uiw/codemirror-themes": "^4.19.9",
|
||||
"@uiw/react-codemirror": "^3.2.1",
|
||||
"@mui/x-date-pickers": "^6.11.1",
|
||||
"@uiw/codemirror-themes": "^4.21.9",
|
||||
"@uiw/react-codemirror": "^4.21.9",
|
||||
"@use-it/interval": "^1.0.0",
|
||||
"algoliasearch": "^4.13.1",
|
||||
"calculate-size": "^1.1.1",
|
||||
"class-transformer": "^0.4.0",
|
||||
"create-react-app": "^4.0.3",
|
||||
"create-react-app": "^5.0.1",
|
||||
"cytoscape": "^3.15.1",
|
||||
"cytoscape-clipboard": "^2.2.1",
|
||||
"cytoscape-cxtmenu": "^3.1.1",
|
||||
"cytoscape-edgehandles": "^3.6.0",
|
||||
"cytoscape-grid-guide": "~2.3.3",
|
||||
"cytoscape-node-html-label": "^1.1.5",
|
||||
"cytoscape-panzoom": "^2.5.2",
|
||||
"cytoscape-undo-redo": "^1.3.2",
|
||||
"d3": "^7.1.1",
|
||||
"dayjs": "^1.11.9",
|
||||
"dotenv": "^6.1.0",
|
||||
"downshift": "^3.3.5",
|
||||
"github-markdown-css": "^3.0.1",
|
||||
"import": "0.0.6",
|
||||
"interweave": "^11.2.0",
|
||||
"material-icons": "^0.7.7",
|
||||
"material-icons-react": "^1.0.4",
|
||||
"material-ui-chip-input": "^2.0.0-beta.2",
|
||||
"material-ui-nested-menu-item": "^1.0.2",
|
||||
"jss": "^10.10.0",
|
||||
"jss-camel-case": "^6.1.0",
|
||||
"jss-default-unit": "^8.0.2",
|
||||
"jss-global": "^3.0.0",
|
||||
"jss-nested": "^6.0.1",
|
||||
"jss-props-sort": "^6.0.0",
|
||||
"jss-vendor-prefixer": "^8.0.1",
|
||||
"md5-file": "^4.0.0",
|
||||
"mdbreact": "^4.21.1",
|
||||
"mime": "^3.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"react": "^16.14.0",
|
||||
"react-alert": "^5.5.0",
|
||||
"mui-chips-input": "^2.1.3",
|
||||
"mui-nested-menu": "^3.2.1",
|
||||
"process": "^0.11.10",
|
||||
"react": "^18.2.0",
|
||||
"react-alert": "^7.0.3",
|
||||
"react-alert-template-basic": "^1.0.0",
|
||||
"react-alice-carousel": "^2.6.4",
|
||||
"react-avatar-editor": "^11.1.0",
|
||||
"react-beforeunload": "^2.2.1",
|
||||
"react-chartjs-2": "^2.11.1",
|
||||
"react-cookie": "^4.0.1",
|
||||
"react-cytoscapejs": "^1.2.0",
|
||||
"react-device-detect": "^1.9.10",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-cytoscapejs": "^2.0.0",
|
||||
"react-device-detect": "^2.2.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-draggable": "^3.3.2",
|
||||
"react-driftjs": "^1.2.2",
|
||||
"react-dropzone": "^10.1.10",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-ga4": "^2.0.0",
|
||||
"react-iframe": "^1.8.0",
|
||||
"react-instantsearch-dom": "^6.28.0",
|
||||
"react-json-pretty": "^2.2.0",
|
||||
"react-json-view": "^1.19.1",
|
||||
"react-markdown": "^4.2.2",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-markdown-github": "^3.3.1",
|
||||
"react-powerhooks": "0.0.7",
|
||||
"react-router": "6.2.1",
|
||||
"react-router-dom": "6.2.1",
|
||||
"react-scripts": "^4.0.1",
|
||||
"react-shepherd": "^3.3.6",
|
||||
"reactstrap": "^7.1.0",
|
||||
"reaviz": "^12.1.0",
|
||||
"react-powerhooks": "^0.0.7",
|
||||
"react-router": "^6.14.1",
|
||||
"react-router-dom": "^6.14.1",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-toastify": "^9.1.3",
|
||||
"reaviz": "^14.9.4",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"search-insights": "^2.2.1",
|
||||
"shellwords": "^0.1.1",
|
||||
"simplebar": "^4.2.3",
|
||||
"styled-components": "^4.4.0",
|
||||
"yaml": "^1.7.2",
|
||||
"yamljs": "^0.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
"zone.js": "^0.13.1",
|
||||
"webpack": "^5.88.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "HTTPS=false&&PORT=3000 react-scripts --openssl-legacy-provider start",
|
||||
"start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
@@ -109,6 +109,8 @@
|
||||
"babel-eslint": "^10.1.0",
|
||||
"prettier": "2.4.1",
|
||||
"promise-window": "^1.2.1",
|
||||
"webpack": "^4.44.2"
|
||||
"react-16": "npm:react@16.13.1",
|
||||
"react-dom-16": "npm:react-dom@16.13.1",
|
||||
"react-error-overlay": "6.0.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="m23.82.13l32.97.16,23.2,23.42-.16,32.97-23.42,23.2-32.97-.16L.24,56.31l.16-32.97L23.82.13Z"/>
|
||||
<g>
|
||||
<path class="cls-2" d="m52.34,43.3c0-.05-.02-.1-.03-.15,0-.05.03-.09.03-.14,0-1.03-.36-1.86-.96-2.43-.59-.56-1.36-.84-2.12-.84-.59,0-1.19.17-1.7.51-.1-.8-.42-1.46-.91-1.94-.59-.56-1.36-.84-2.12-.84-.66,0-1.32.22-1.87.64-.16-.46-.41-.86-.74-1.18-.59-.56-1.36-.84-2.12-.84-.57,0-1.15.16-1.66.48v-3.12c0-1.67-1.42-2.95-3.08-2.95s-3.08,1.28-3.08,2.95v11.68s-.03.04-.05.08c-.11.17-.27.4-.46.68-.36.56-.82,1.31-1.2,2.05-.35.71-.71,1.49-.77,2.33-.06.89.23,1.76.99,2.63.65.75,1.81,2.14,2.81,3.35.5.6.96,1.16,1.3,1.57l.55.66s.04.05.06.07c.74.69,1.76,1.07,2.81,1.07h7.29c4.12,0,7.02-2.98,7.02-6.13h-.71.71v-10.2Zm-1.42,0v10.2c0,2.27-2.15,4.71-5.6,4.71h-7.29c-.7,0-1.35-.25-1.81-.66l-.52-.63c-.34-.41-.8-.97-1.3-1.57-1-1.21-2.18-2.61-2.83-3.37-.55-.63-.67-1.14-.64-1.61.03-.52.26-1.07.62-1.79.13-.27.29-.53.44-.8v2.11c0,.39.32.71.71.71s.71-.32.71-.71v-16.44c0-.81.7-1.53,1.66-1.53s1.66.72,1.66,1.53v5.91s0,0,0,0v5.38c0,.39.32.71.71.71,0,0,0,0,0,0h0s0,0,0,0c.1,0,.19-.02.27-.06.06-.02.11-.07.15-.1.02-.02.05-.03.07-.05.05-.05.08-.1.11-.16.01-.02.03-.04.04-.06.04-.09.06-.18.06-.28v-5.38c0-.67.23-1.12.52-1.4.3-.29.71-.44,1.14-.44s.84.15,1.13.44c.29.28.52.73.52,1.41v1.37s0,0,0,0v1.7s0,0,0,0v2.3c0,.39.32.71.71.71s.71-.32.71-.71v-4.01c0-.67.23-1.12.52-1.4.3-.29.71-.44,1.14-.44s.84.15,1.14.44c.29.28.52.73.52,1.41v2.26s0,0,0,0v.85s0,0,0,0v1.07c0,.39.32.71.71.71s.71-.32.71-.71v-1.93c0-.67.23-1.12.52-1.4.3-.29.71-.44,1.14-.44s.84.15,1.14.44c.29.28.52.73.52,1.41,0,.05.02.1.03.15,0,.05-.03.09-.03.14Z"/>
|
||||
<path class="cls-2" d="m51.35,22.81c-2.53,0-4.6,1.88-4.95,4.31h-4.84c-.82-3.03-3.18-5.57-6.42-6.44-4.85-1.3-9.84,1.59-11.14,6.43-1.29,4.83.86,9.61,5.72,10.91.41.11.83-.14.94-.54.11-.41-.14-.83-.54-.94-4.01-1.07-5.72-4.99-4.63-9.03,1.08-4.03,5.23-6.43,9.26-5.35,4.03,1.08,6.43,5.23,5.35,9.26-.11.41.14.83.54.94.41.11.83-.14.94-.54.29-1.1.36-2.2.25-3.27h4.58c.35,2.43,2.42,4.31,4.95,4.31,2.78,0,5.03-2.25,5.03-5.03s-2.25-5.03-5.03-5.03Zm-26.02,4.66c-1.1,4.1.63,8.11,4.74,9.21.33.09.53.44.44.77-.07.25-.28.42-.52.46.24-.03.45-.21.52-.46.09-.33-.11-.68-.44-.77-4.1-1.1-5.83-5.1-4.74-9.21.76-2.82,2.99-4.86,5.65-5.5-2.66.64-4.89,2.68-5.65,5.5Zm9.76-6.65c-1.39-.37-2.8-.39-4.12-.11,1.32-.27,2.73-.26,4.12.11,0,0,0,0,.02,0,0,0-.01,0-.02,0Zm6.31,6.3h0s0,0,0-.01c0,0,0,0,0,.01Zm.02,4.67c-.07.25-.28.42-.52.46.24-.03.45-.21.52-.46.08-.28.13-.56.18-.85-.05.28-.11.56-.18.85Zm.25-3.23h0s0,.01,0,.02c0,0,0-.01,0-.02Zm9.68,2.87c-1.98,0-3.59-1.61-3.59-3.59s1.61-3.59,3.59-3.59,3.59,1.61,3.59,3.59-1.61,3.59-3.59,3.59Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="80px" height="80px" viewBox="0 0 80 80" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 23.820312 0.128906 L 56.789062 0.289062 L 79.988281 23.710938 L 79.828125 56.679688 L 56.410156 79.878906 L 23.441406 79.71875 L 0.238281 56.308594 L 0.398438 23.339844 Z M 23.820312 0.128906 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 52.339844 43.300781 C 52.339844 43.25 52.320312 43.199219 52.308594 43.148438 C 52.308594 43.101562 52.339844 43.058594 52.339844 43.011719 C 52.339844 41.980469 51.980469 41.148438 51.378906 40.578125 C 50.789062 40.019531 50.019531 39.738281 49.261719 39.738281 C 48.671875 39.738281 48.070312 39.910156 47.558594 40.25 C 47.460938 39.449219 47.140625 38.789062 46.648438 38.308594 C 46.058594 37.75 45.289062 37.46875 44.53125 37.46875 C 43.871094 37.46875 43.210938 37.691406 42.660156 38.109375 C 42.5 37.648438 42.25 37.25 41.921875 36.929688 C 41.328125 36.371094 40.558594 36.089844 39.800781 36.089844 C 39.230469 36.089844 38.648438 36.25 38.140625 36.570312 L 38.140625 33.449219 C 38.140625 31.78125 36.71875 30.5 35.058594 30.5 C 33.398438 30.5 31.980469 31.78125 31.980469 33.449219 L 31.980469 45.128906 C 31.980469 45.128906 31.949219 45.171875 31.929688 45.210938 C 31.820312 45.378906 31.660156 45.609375 31.46875 45.890625 C 31.109375 46.449219 30.648438 47.199219 30.269531 47.941406 C 29.921875 48.648438 29.558594 49.429688 29.5 50.269531 C 29.441406 51.160156 29.730469 52.03125 30.488281 52.898438 C 31.140625 53.648438 32.300781 55.039062 33.300781 56.25 C 33.800781 56.851562 34.261719 57.410156 34.601562 57.820312 L 35.148438 58.480469 C 35.148438 58.480469 35.191406 58.53125 35.210938 58.550781 C 35.949219 59.238281 36.96875 59.621094 38.019531 59.621094 L 45.308594 59.621094 C 49.429688 59.621094 52.328125 56.640625 52.328125 53.488281 L 51.621094 53.488281 L 52.328125 53.488281 L 52.328125 43.289062 Z M 50.921875 43.300781 L 50.921875 53.5 C 50.921875 55.769531 48.769531 58.210938 45.320312 58.210938 L 38.03125 58.210938 C 37.328125 58.210938 36.679688 57.960938 36.21875 57.550781 L 35.699219 56.921875 C 35.359375 56.511719 34.898438 55.949219 34.398438 55.351562 C 33.398438 54.140625 32.21875 52.738281 31.570312 51.980469 C 31.019531 51.351562 30.898438 50.839844 30.929688 50.371094 C 30.960938 49.851562 31.191406 49.300781 31.550781 48.578125 C 31.679688 48.308594 31.839844 48.050781 31.988281 47.78125 L 31.988281 49.890625 C 31.988281 50.28125 32.308594 50.601562 32.699219 50.601562 C 33.089844 50.601562 33.410156 50.28125 33.410156 49.890625 L 33.410156 33.449219 C 33.410156 32.640625 34.109375 31.921875 35.070312 31.921875 C 36.03125 31.921875 36.730469 32.640625 36.730469 33.449219 L 36.730469 44.738281 C 36.730469 45.128906 37.050781 45.449219 37.441406 45.449219 C 37.539062 45.449219 37.628906 45.429688 37.710938 45.390625 C 37.769531 45.371094 37.820312 45.320312 37.859375 45.289062 C 37.878906 45.269531 37.910156 45.261719 37.929688 45.238281 C 37.980469 45.191406 38.011719 45.140625 38.039062 45.078125 C 38.050781 45.058594 38.070312 45.039062 38.078125 45.019531 C 38.121094 44.929688 38.140625 44.839844 38.140625 44.738281 L 38.140625 39.359375 C 38.140625 38.691406 38.371094 38.238281 38.660156 37.960938 C 38.960938 37.671875 39.371094 37.519531 39.800781 37.519531 C 40.230469 37.519531 40.640625 37.671875 40.929688 37.960938 C 41.21875 38.238281 41.449219 38.691406 41.449219 39.371094 L 41.449219 44.738281 C 41.449219 45.128906 41.769531 45.449219 42.160156 45.449219 C 42.550781 45.449219 42.871094 45.128906 42.871094 44.738281 L 42.871094 40.730469 C 42.871094 40.058594 43.101562 39.609375 43.390625 39.328125 C 43.691406 39.039062 44.101562 38.890625 44.53125 38.890625 C 44.960938 38.890625 45.371094 39.039062 45.671875 39.328125 C 45.960938 39.609375 46.191406 40.058594 46.191406 40.738281 L 46.191406 44.921875 C 46.191406 45.308594 46.511719 45.628906 46.898438 45.628906 C 47.289062 45.628906 47.609375 45.308594 47.609375 44.921875 L 47.609375 42.988281 C 47.609375 42.320312 47.839844 41.871094 48.128906 41.589844 C 48.429688 41.300781 48.839844 41.148438 49.269531 41.148438 C 49.699219 41.148438 50.109375 41.300781 50.410156 41.589844 C 50.699219 41.871094 50.929688 42.320312 50.929688 43 C 50.929688 43.050781 50.949219 43.101562 50.960938 43.148438 C 50.960938 43.199219 50.929688 43.238281 50.929688 43.289062 Z M 50.921875 43.300781 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 51.351562 22.808594 C 48.820312 22.808594 46.75 24.691406 46.398438 27.121094 L 41.558594 27.121094 C 40.738281 24.089844 38.378906 21.550781 35.140625 20.679688 C 30.289062 19.378906 25.300781 22.269531 24 27.109375 C 22.710938 31.941406 24.859375 36.71875 29.71875 38.019531 C 30.128906 38.128906 30.550781 37.878906 30.660156 37.480469 C 30.769531 37.070312 30.519531 36.648438 30.121094 36.539062 C 26.109375 35.46875 24.398438 31.550781 25.488281 27.511719 C 26.570312 23.480469 30.71875 21.078125 34.75 22.160156 C 38.78125 23.238281 41.179688 27.390625 40.101562 31.421875 C 39.988281 31.828125 40.238281 32.25 40.640625 32.359375 C 41.050781 32.46875 41.46875 32.21875 41.578125 31.820312 C 41.871094 30.71875 41.941406 29.621094 41.828125 28.550781 L 46.410156 28.550781 C 46.761719 30.980469 48.828125 32.859375 51.359375 32.859375 C 54.140625 32.859375 56.390625 30.609375 56.390625 27.828125 C 56.390625 25.050781 54.140625 22.800781 51.359375 22.800781 Z M 25.328125 27.46875 C 24.230469 31.570312 25.960938 35.578125 30.070312 36.679688 C 30.398438 36.769531 30.601562 37.121094 30.511719 37.449219 C 30.441406 37.699219 30.230469 37.871094 29.988281 37.910156 C 30.230469 37.878906 30.441406 37.699219 30.511719 37.449219 C 30.601562 37.121094 30.398438 36.769531 30.070312 36.679688 C 25.96875 35.578125 24.238281 31.578125 25.328125 27.46875 C 26.089844 24.648438 28.320312 22.609375 30.980469 21.96875 C 28.320312 22.609375 26.089844 24.648438 25.328125 27.46875 Z M 35.089844 20.820312 C 33.699219 20.449219 32.289062 20.429688 30.96875 20.710938 C 32.289062 20.441406 33.699219 20.449219 35.089844 20.820312 C 35.089844 20.820312 35.089844 20.820312 35.109375 20.820312 C 35.109375 20.820312 35.101562 20.820312 35.089844 20.820312 Z M 41.398438 27.121094 C 41.398438 27.121094 41.398438 27.121094 41.398438 27.109375 C 41.398438 27.109375 41.398438 27.109375 41.398438 27.121094 Z M 41.421875 31.789062 C 41.351562 32.039062 41.140625 32.210938 40.898438 32.25 C 41.140625 32.21875 41.351562 32.039062 41.421875 31.789062 C 41.5 31.511719 41.550781 31.230469 41.601562 30.941406 C 41.550781 31.21875 41.488281 31.5 41.421875 31.789062 Z M 41.671875 28.558594 C 41.671875 28.558594 41.671875 28.570312 41.671875 28.578125 C 41.671875 28.578125 41.671875 28.570312 41.671875 28.558594 Z M 51.351562 31.429688 C 49.371094 31.429688 47.761719 29.820312 47.761719 27.839844 C 47.761719 25.859375 49.371094 24.25 51.351562 24.25 C 53.328125 24.25 54.941406 25.859375 54.941406 27.839844 C 54.941406 29.820312 53.328125 31.429688 51.351562 31.429688 Z M 51.351562 31.429688 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
+6
-6
@@ -10,9 +10,9 @@ docker tag ghcr.io/frikky/shuffle-frontend:nightly ghcr.io/shuffle/shuffle-front
|
||||
|
||||
echo "Starting server"
|
||||
# Rerun build locally for it to update :)
|
||||
#docker run -it \
|
||||
# -p 3001:80 \
|
||||
# -p 3002:443 \
|
||||
# -v $(pwd)/build:/usr/share/nginx/html:ro \
|
||||
# --rm \
|
||||
# nginx
|
||||
docker run -it \
|
||||
-p 3001:80 \
|
||||
-p 3002:443 \
|
||||
-v $(pwd)/build:/usr/share/nginx/html:ro \
|
||||
--rm \
|
||||
ghcr.io/frikky/shuffle-frontend:nightly
|
||||
|
||||
+46
-195
@@ -6,8 +6,7 @@ import { removeCookies, useCookies } from "react-cookie";
|
||||
|
||||
import Workflows from "./views/Workflows";
|
||||
import GettingStarted from "./views/GettingStarted";
|
||||
import EditWebhook from "./views/EditWebhook";
|
||||
import AngularWorkflow from "./views/AngularWorkflow";
|
||||
import AngularWorkflow from "./views/AngularWorkflow.jsx";
|
||||
|
||||
import Header from "./components/Header.jsx";
|
||||
import theme from "./theme";
|
||||
@@ -19,31 +18,31 @@ import Dashboard from "./views/Dashboard.jsx";
|
||||
import DashboardView from "./views/DashboardViews.jsx";
|
||||
import AdminSetup from "./views/AdminSetup";
|
||||
import Admin from "./views/Admin";
|
||||
import Docs from "./views/Docs";
|
||||
import Introduction from "./views/Introduction";
|
||||
import Docs from "./views/Docs.jsx";
|
||||
//import Introduction from "./views/Introduction";
|
||||
import SetAuthentication from "./views/SetAuthentication";
|
||||
import SetAuthenticationSSO from "./views/SetAuthenticationSSO";
|
||||
import Search from "./views/Search.jsx";
|
||||
import RunWorkflow from "./views/RunWorkflow.jsx";
|
||||
|
||||
import LandingPageNew from "./views/LandingpageNew";
|
||||
import LoginPage from "./views/LoginPage";
|
||||
import SettingsPage from "./views/SettingsPage";
|
||||
import KeepAlive from "./views/KeepAlive.jsx";
|
||||
|
||||
import MyView from "./views/MyView";
|
||||
|
||||
import { createMuiTheme, MuiThemeProvider } from "@material-ui/core/styles";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
|
||||
import UpdateAuthentication from "./views/UpdateAuthentication.jsx";
|
||||
import FrameworkWrapper from "./views/FrameworkWrapper.jsx";
|
||||
import ScrollToTop from "./components/ScrollToTop";
|
||||
import AlertTemplate from "./components/AlertTemplate";
|
||||
import { useAlert, positions, Provider } from "react-alert";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import detectEthereumProvider from "@metamask/detect-provider";
|
||||
import { ToastContainer, toast } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
import Drift from "react-driftjs";
|
||||
import DashboardPage from "./views/TempDashboard.jsx";
|
||||
|
||||
// Production - backend proxy forwarding in nginx
|
||||
var globalUrl = window.location.origin;
|
||||
@@ -147,120 +146,6 @@ const App = (message, props) => {
|
||||
}
|
||||
|
||||
// Handling Ethereum update
|
||||
{/*
|
||||
detectEthereumProvider().then((provider) => {
|
||||
if (
|
||||
provider &&
|
||||
userInfo.eth_info !== undefined &&
|
||||
userInfo.eth_info !== null
|
||||
) {
|
||||
if (
|
||||
userInfo.eth_info.account !== undefined &&
|
||||
userInfo.eth_info.account !== null &&
|
||||
userInfo.eth_info.account.length === 0
|
||||
) {
|
||||
userInfo.eth_info = {};
|
||||
var method = "eth_requestAccounts";
|
||||
var params = [];
|
||||
provider
|
||||
.request({
|
||||
method: method,
|
||||
params,
|
||||
})
|
||||
.then((result) => {
|
||||
if (
|
||||
result !== undefined &&
|
||||
result !== null &&
|
||||
result.length > 0
|
||||
) {
|
||||
userInfo.eth_info.account = result[0];
|
||||
|
||||
// Getting and setting balance for the current user
|
||||
method = "eth_getBalance";
|
||||
params = [userInfo.eth_info.account, "latest"];
|
||||
provider
|
||||
.request({
|
||||
method: method,
|
||||
params,
|
||||
})
|
||||
.then((result) => {
|
||||
if (
|
||||
result !== undefined &&
|
||||
result !== null &&
|
||||
result.length > 0
|
||||
) {
|
||||
userInfo.parsed_balance =
|
||||
result / 1000000000000000000;
|
||||
} else {
|
||||
alert.error("Couldn't find balance: ", result);
|
||||
}
|
||||
// The result varies by RPC method.
|
||||
// For example, this method will return a transaction hash hexadecimal string on success.
|
||||
})
|
||||
.catch((error) => {
|
||||
// If the request fails, the Promise will reject with an error.
|
||||
alert.error(
|
||||
"Failed getting info from ethereum API: " + error
|
||||
);
|
||||
});
|
||||
} else {
|
||||
alert.error("Couldn't find any user: ", result);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// If the request fails, the Promise will reject with an error.
|
||||
alert.error(
|
||||
"Failed getting info from ethereum API: " + error
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Register hooks here
|
||||
provider.on("message", (event) => {
|
||||
alert.info("Message from MetaMask: ", event);
|
||||
});
|
||||
|
||||
provider.on("chainChanged", (chainId) => {
|
||||
console.log("Changed chain to: ", chainId);
|
||||
|
||||
method = "eth_getBalance";
|
||||
params = [userInfo.eth_info.account, "latest"];
|
||||
provider
|
||||
.request({
|
||||
method: method,
|
||||
params,
|
||||
})
|
||||
.then((result) => {
|
||||
console.log("Got result: ", result);
|
||||
if (result !== undefined && result !== null) {
|
||||
userInfo.eth_info.balance = result;
|
||||
userInfo.eth_info.parsed_balance =
|
||||
result / 1000000000000000000;
|
||||
console.log("INFO: ", userInfo);
|
||||
setUserData(userInfo);
|
||||
} else {
|
||||
alert.error("Couldn't find balance: ", result);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// If the request fails, the Promise will reject with an error.
|
||||
alert.error(
|
||||
"Failed getting info from ethereum API: " + error
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
userInfo.eth_info !== undefined &&
|
||||
userInfo.eth_info.balance !== undefined
|
||||
) {
|
||||
//console.log(userInfo.eth_info.balance)
|
||||
userInfo.eth_info.parsed_balance =
|
||||
userInfo.eth_info.balance / 1000000000000000000;
|
||||
}
|
||||
*/}
|
||||
|
||||
//console.log("USER: ", userInfo)
|
||||
setUserData(userInfo);
|
||||
@@ -283,21 +168,9 @@ const App = (message, props) => {
|
||||
}
|
||||
|
||||
const includedData =
|
||||
window.location.pathname === "/home" ||
|
||||
window.location.pathname === "/features" ? (
|
||||
<div>
|
||||
<Routes>
|
||||
<Route
|
||||
exact
|
||||
path="/home"
|
||||
render={(props) => <LandingPageNew isLoaded={isLoaded} {...props} />}
|
||||
/>
|
||||
</Routes>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#1F2023",
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
color: "rgba(255, 255, 255, 0.65)",
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
@@ -453,6 +326,7 @@ const App = (message, props) => {
|
||||
path="/usecases"
|
||||
element={
|
||||
<Dashboard
|
||||
userdata={userdata}
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
@@ -472,7 +346,7 @@ const App = (message, props) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route exact path="/apps/authentication" element={<UpdateAuthentication serverside={serverside} userdata={userdata} isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
|
||||
<Route exact path="/apps/authentication" element={<UpdateAuthentication serverside={false} userdata={userdata} isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
|
||||
<Route
|
||||
exact
|
||||
path="/apps"
|
||||
@@ -503,7 +377,7 @@ const App = (message, props) => {
|
||||
path="/workflows"
|
||||
element={
|
||||
<Workflows
|
||||
checkLogin={checkLogin}
|
||||
checkLogin={checkLogin}
|
||||
cookies={cookies}
|
||||
removeCookie={removeCookie}
|
||||
isLoaded={isLoaded}
|
||||
@@ -545,8 +419,8 @@ const App = (message, props) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route exact path="/workflows/:key/run" element={<RunWorkflow userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} /> } />
|
||||
<Route exact path="/workflows/:key/execute" element={<RunWorkflow userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} /> } />
|
||||
<Route exact path="/workflows/:key/run" element={<RunWorkflow userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} /> } />
|
||||
<Route exact path="/workflows/:key/execute" element={<RunWorkflow userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} /> } />
|
||||
<Route
|
||||
exact
|
||||
path="/docs/:key"
|
||||
@@ -585,28 +459,6 @@ const App = (message, props) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/introduction"
|
||||
element={
|
||||
<Introduction
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/introduction/:key"
|
||||
element={
|
||||
<Introduction
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/set_authentication"
|
||||
@@ -656,17 +508,6 @@ const App = (message, props) => {
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/testdashboard"
|
||||
element={
|
||||
<DashboardPage
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
@@ -680,22 +521,23 @@ const App = (message, props) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/welcome"
|
||||
element={
|
||||
<Welcome
|
||||
cookies={cookies}
|
||||
removeCookie={removeCookie}
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
<Route
|
||||
exact
|
||||
path="/welcome"
|
||||
element={
|
||||
<Welcome
|
||||
cookies={cookies}
|
||||
removeCookie={removeCookie}
|
||||
isLoaded={isLoaded}
|
||||
isLoggedIn={isLoggedIn}
|
||||
globalUrl={globalUrl}
|
||||
cookies={cookies}
|
||||
userdata={userdata}
|
||||
checkLogin={checkLogin}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
@@ -714,21 +556,30 @@ const App = (message, props) => {
|
||||
/>
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
|
||||
// <div style={{backgroundColor: "rgba(21, 32, 43, 1)", color: "#fffff", minHeight: "100vh"}}>
|
||||
// backgroundColor: "#213243",
|
||||
// This is a mess hahahah
|
||||
return (
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<CookiesProvider>
|
||||
<BrowserRouter>
|
||||
<Provider template={AlertTemplate} {...options}>
|
||||
{includedData}
|
||||
</Provider>
|
||||
</BrowserRouter>
|
||||
<ToastContainer
|
||||
position="bottom-center"
|
||||
autoClose={5000}
|
||||
hideProgressBar={false}
|
||||
newestOnTop={false}
|
||||
closeOnClick
|
||||
rtl={false}
|
||||
pauseOnFocusLoss
|
||||
draggable
|
||||
pauseOnHover
|
||||
theme="dark"
|
||||
/>
|
||||
</CookiesProvider>
|
||||
</MuiThemeProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import React from "react";
|
||||
import InfoIcon from "@material-ui/icons/Info";
|
||||
import CheckIcon from "@material-ui/icons/Check";
|
||||
import ErrorOutlineIcon from "@material-ui/icons/ErrorOutline";
|
||||
import CloseIcon from "@material-ui/icons/Close";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import {
|
||||
Info as InfoIcon,
|
||||
Check as CheckIcon,
|
||||
ErrorOutline as ErrorOutlineIcon,
|
||||
Close as CloseIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import {
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
|
||||
const alertStyle = {
|
||||
backgroundColor: "rgba(0,0,0,0.9)",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import CytoscapeComponent from 'react-cytoscapejs';
|
||||
import frameworkStyle from '../frameworkStyle.jsx';
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
|
||||
import AppSearch from '../components/Appsearch.jsx';
|
||||
import PaperComponent from "../components/PaperComponent.jsx"
|
||||
@@ -18,30 +17,159 @@ import {
|
||||
Divider,
|
||||
IconButton,
|
||||
Badge,
|
||||
CircularProgress,
|
||||
CircularProgress,
|
||||
Tooltip,
|
||||
Dialog,
|
||||
Chip,
|
||||
Avatar,
|
||||
} from "@material-ui/core";
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Button
|
||||
} from '@material-ui/core';
|
||||
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
Delete as DeleteIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import * as edgehandles from "cytoscape-edgehandles";
|
||||
import * as cytoscape from "cytoscape";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
cytoscape.use(edgehandles);
|
||||
|
||||
export const findSpecificApp = (framework, inputcategory) => {
|
||||
// Get the frameworkinfo for the org and fill in
|
||||
//
|
||||
if (framework === undefined || framework === null) {
|
||||
console.log("findSpecificApp: framework is null")
|
||||
return null
|
||||
}
|
||||
|
||||
if (inputcategory === undefined || inputcategory === null) {
|
||||
console.log("findSpecificApp: category is null")
|
||||
return null
|
||||
}
|
||||
|
||||
const category = inputcategory.toLowerCase().split(":")[0].trim()
|
||||
|
||||
console.log("findSpecificApp: ", category, framework)
|
||||
if (category === "edr" || category === "eradication" || category === "edr & av") {
|
||||
if (framework["EDR & AV"] !== undefined && framework["EDR & AV"].name !== undefined) {
|
||||
return framework["EDR & AV"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "EDR :default",
|
||||
large_image: parsedDatatypeImages["EDR & AV"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "communication") {
|
||||
if (framework["Comms"] !== undefined && framework["Comms"].name !== undefined) {
|
||||
return framework["Comms"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "COMMS :default",
|
||||
large_image: parsedDatatypeImages["COMMS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "email") {
|
||||
if (framework["Email"] !== undefined && framework["Email"].name !== undefined) {
|
||||
return framework["Email"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "COMMS :default",
|
||||
large_image: parsedDatatypeImages["COMMS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "assets") {
|
||||
if (framework["Assets"] !== undefined && framework["Assets"].name !== undefined) {
|
||||
return framework["Assets"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "ASSETS :default",
|
||||
large_image: parsedDatatypeImages["ASSETS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "cases") {
|
||||
if (framework["Cases"] !== undefined && framework["Cases"].name !== undefined) {
|
||||
return framework["Cases"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "CASES :default",
|
||||
large_image: parsedDatatypeImages["CASES"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "iam") {
|
||||
if (framework["IAM"] !== undefined && framework["IAM"].name !== undefined) {
|
||||
return framework["IAM"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "EDR :default",
|
||||
large_image: parsedDatatypeImages["EDR & AV"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "network") {
|
||||
if (framework["Network"] !== undefined && framework["Network"].name !== undefined) {
|
||||
return framework["Network"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "Network :default",
|
||||
large_image: parsedDatatypeImages["NETWORK"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "intel") {
|
||||
if (framework["Intel"] !== undefined && framework["Intel"].name !== undefined) {
|
||||
return framework["Intel"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "INTEL :default",
|
||||
large_image: parsedDatatypeImages["INTEL"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "siem") {
|
||||
if (framework["SIEM"] !== undefined && framework["SIEM"].name !== undefined) {
|
||||
return framework["SIEM"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "SIEM :default",
|
||||
large_image: parsedDatatypeImages["SIEM"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else {
|
||||
console.log("findSpecificApp: unknown category: ", category)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const svgSize = "40px"
|
||||
const parsedDatatypeImages = {
|
||||
export const parsedDatatypeImages = {
|
||||
"SIEM": encodeURI(`data:image/svg+xml;utf-8,<svg fill="rgb(248,90,62)" width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M6.93767 0C8.71083 0 10.4114 0.704386 11.6652 1.9582C12.919 3.21202 13.6234 4.91255 13.6234 6.68571C13.6234 8.34171 13.0165 9.864 12.0188 11.0366L12.2965 11.3143H13.1091L18.252 16.4571L16.7091 18L11.5662 12.8571V12.0446L11.2885 11.7669C10.116 12.7646 8.59367 13.3714 6.93767 13.3714C5.16451 13.3714 3.46397 12.667 2.21015 11.4132C0.956339 10.1594 0.251953 8.45888 0.251953 6.68571C0.251953 4.91255 0.956339 3.21202 2.21015 1.9582C3.46397 0.704386 5.16451 0 6.93767 0ZM6.93767 2.05714C4.36624 2.05714 2.3091 4.11429 2.3091 6.68571C2.3091 9.25714 4.36624 11.3143 6.93767 11.3143C9.5091 11.3143 11.5662 9.25714 11.5662 6.68571C11.5662 4.11429 9.5091 2.05714 6.93767 2.05714Z" /></svg>`),
|
||||
|
||||
"CASES": encodeURI(`data:image/svg+xml;utf-8,<svg fill="rgb(248,90,62)" width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M15.6408 8.39233H18.0922V10.0287H15.6408V8.39233ZM0.115234 8.39233H2.56663V10.0287H0.115234V8.39233ZM9.92083 0.21051V2.66506H8.28656V0.21051H9.92083ZM3.31839 2.25596L5.05889 4.00687L3.89856 5.16051L2.15807 3.42596L3.31839 2.25596ZM13.1485 3.99869L14.8808 2.25596L16.0493 3.42596L14.3088 5.16051L13.1485 3.99869ZM9.10369 4.30142C10.404 4.30142 11.651 4.81863 12.5705 5.73926C13.4899 6.65989 14.0065 7.90854 14.0065 9.21051C14.0065 11.0269 13.0178 12.6141 11.5551 13.4651V14.9378C11.5551 15.1548 11.469 15.3629 11.3158 15.5163C11.1625 15.6698 10.9547 15.756 10.738 15.756H7.46943C7.25271 15.756 7.04487 15.6698 6.89163 15.5163C6.73839 15.3629 6.6523 15.1548 6.6523 14.9378V13.4651C5.18963 12.6141 4.2009 11.0269 4.2009 9.21051C4.2009 7.90854 4.71744 6.65989 5.63689 5.73926C6.55635 4.81863 7.80339 4.30142 9.10369 4.30142ZM10.738 16.5741V17.3923C10.738 17.6093 10.6519 17.8174 10.4986 17.9709C10.3454 18.1243 10.1375 18.2105 9.92083 18.2105H8.28656C8.06984 18.2105 7.862 18.1243 7.70876 17.9709C7.55552 17.8174 7.46943 17.6093 7.46943 17.3923V16.5741H10.738ZM8.28656 14.1196H9.92083V12.3769C11.3345 12.0169 12.3722 10.7323 12.3722 9.21051C12.3722 8.34253 12.0279 7.5101 11.4149 6.89634C10.8019 6.28259 9.97056 5.93778 9.10369 5.93778C8.23683 5.93778 7.40546 6.28259 6.79249 6.89634C6.17953 7.5101 5.83516 8.34253 5.83516 9.21051C5.83516 10.7323 6.87292 12.0169 8.28656 12.3769V14.1196Z" /></svg>`),
|
||||
@@ -523,7 +651,7 @@ const AppFramework = (props) => {
|
||||
|
||||
const scale = size === undefined ? 1 : size > 5 ? 3 : size
|
||||
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
|
||||
|
||||
const handleLoadNextSuggestion = (frameworkData) => {
|
||||
@@ -716,7 +844,7 @@ const AppFramework = (props) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
||||
//console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
||||
if (discoveryWrapper === undefined || discoveryWrapper.id === "SHUFFLE" || discoveryWrapper.id === undefined || cy === undefined) {
|
||||
setDiscoveryData({})
|
||||
|
||||
@@ -786,16 +914,16 @@ const AppFramework = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating: " + responseJson.reason)
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to update framework for your org.")
|
||||
toast("Failed to update framework for your org.")
|
||||
}
|
||||
} else {
|
||||
alert.info("Updated usecase.")
|
||||
toast("Updated usecase.")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
@@ -818,13 +946,13 @@ const AppFramework = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed to activate the app")
|
||||
toast("Failed to activate the app")
|
||||
} else {
|
||||
//alert.success("App activated for your organization! Refresh the page to use the app.")
|
||||
//toast("App activated for your organization! Refresh the page to use the app.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error(error.toString())
|
||||
//toast(error.toString())
|
||||
console.log("Activate app error: ", error.toString())
|
||||
});
|
||||
}
|
||||
@@ -854,9 +982,9 @@ const AppFramework = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating: " + responseJson.reason)
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to update framework for your org.")
|
||||
toast("Failed to update framework for your org.")
|
||||
|
||||
}
|
||||
}
|
||||
@@ -865,7 +993,7 @@ const AppFramework = (props) => {
|
||||
//setFrameworkData(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
@@ -877,7 +1005,7 @@ const AppFramework = (props) => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
console.log("New selected app: ", newSelectedApp, discoveryData)
|
||||
//console.log("New selected app: ", newSelectedApp, discoveryData)
|
||||
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||
return
|
||||
}
|
||||
@@ -1859,7 +1987,6 @@ const AppFramework = (props) => {
|
||||
//autounselectify={true}
|
||||
var usecasediff = -100
|
||||
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
|
||||
<div style={{position: "absolute"}}>
|
||||
@@ -1995,11 +2122,11 @@ const AppFramework = (props) => {
|
||||
|
||||
{
|
||||
Object.getOwnPropertyNames(discoveryData).length > 0 ?
|
||||
<Paper style={{width: 275, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 35, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}>
|
||||
<Paper style={{width: 300, maxHeight: 400, overflow: "hidden", zIndex: 12500, padding: 25, paddingRight: 25, backgroundColor: theme.palette.surfaceColor, border: "1px solid rgba(255,255,255,0.2)", position: "absolute", top: -50, left: 50, }}>
|
||||
{paperTitle.length > 0 ?
|
||||
<span>
|
||||
<Typography variant="h6" style={{textAlign: "center"}}>
|
||||
{paperTitle}
|
||||
{paperTitle.replace("_", " ", -1)}
|
||||
</Typography>
|
||||
<Divider style={{marginTop: 5, marginBottom: 5 }} />
|
||||
</span>
|
||||
@@ -2069,8 +2196,8 @@ const AppFramework = (props) => {
|
||||
|
||||
const foundelement = cy.getElementById(discoveryData.id)
|
||||
if (foundelement !== undefined && foundelement !== null) {
|
||||
console.log("element: ", foundelement)
|
||||
console.log("DISC: ", discoveryData)
|
||||
//console.log("element: ", foundelement)
|
||||
//console.log("DISC: ", discoveryData)
|
||||
foundelement.data("large_image", parsedDatatypeImages[discoveryData.id.toUpperCase()])
|
||||
foundelement.data("text_margin_y", "14px")
|
||||
foundelement.data("margin_x", "32px")
|
||||
@@ -2129,7 +2256,7 @@ const AppFramework = (props) => {
|
||||
?
|
||||
<span>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 10}}>
|
||||
Click an app below to select it
|
||||
Search to find your app
|
||||
</Typography>
|
||||
</span>
|
||||
:
|
||||
@@ -2164,7 +2291,7 @@ const AppFramework = (props) => {
|
||||
elements={elements}
|
||||
minZoom={0.35}
|
||||
maxZoom={2.00}
|
||||
style={{width: 560*scale, height: 560*scale, backgroundColor: "transparent", margin: "auto",}}
|
||||
style={{width: 560*scale, height: 560*scale, backgroundColor: theme.palette.backgroundColor, margin: "auto",}}
|
||||
stylesheet={frameworkStyle}
|
||||
boxSelectionEnabled={false}
|
||||
panningEnabled={false}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import {Link} from 'react-router-dom';
|
||||
import { removeQuery } from '../components/ScrollToTop.jsx';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons';
|
||||
import {
|
||||
Search as SearchIcon,
|
||||
CloudQueue as CloudQueueIcon,
|
||||
Code as CodeIcon
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import { InstantSearch, Configure, connectSearchBox, connectHits, connectHitInsights } from 'react-instantsearch-dom';
|
||||
@@ -21,7 +26,7 @@ import {
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip
|
||||
} from '@material-ui/core';
|
||||
} from '@mui/material';
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6")
|
||||
@@ -34,7 +39,6 @@ const AppGrid = props => {
|
||||
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs
|
||||
const theme = useTheme();
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
//const [filteredApps, setFilteredApps] = React.useState([]);
|
||||
const [formMail, setFormMail] = React.useState("");
|
||||
@@ -71,7 +75,7 @@ const AppGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
@@ -86,21 +90,24 @@ const AppGrid = props => {
|
||||
}
|
||||
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
useEffect(() => {
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
console.log("Got query: ", foundQuery)
|
||||
refine(foundQuery)
|
||||
}
|
||||
var defaultSearch = ""
|
||||
//useEffect(() => {
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
console.log("Got query: ", foundQuery)
|
||||
refine(foundQuery)
|
||||
defaultSearch = foundQuery
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
//}, [])
|
||||
|
||||
return (
|
||||
<form noValidate action="" role="search">
|
||||
<TextField
|
||||
defaultValue={defaultSearch}
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||
InputProps={{
|
||||
@@ -118,10 +125,12 @@ const AppGrid = props => {
|
||||
autoComplete='off'
|
||||
type="search"
|
||||
color="primary"
|
||||
defaultValue={currentRefinement}
|
||||
placeholder="Find Apps..."
|
||||
id="shuffle_search_field"
|
||||
onChange={(event) => {
|
||||
// Remove "q" from URL
|
||||
removeQuery("q")
|
||||
|
||||
refine(event.currentTarget.value)
|
||||
}}
|
||||
limit={5}
|
||||
@@ -148,8 +157,6 @@ const AppGrid = props => {
|
||||
// setInnerHits(hits)
|
||||
//}
|
||||
|
||||
console.log("In appgrid")
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
{hits.map((data, index) => {
|
||||
|
||||
@@ -1,377 +0,0 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons';
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import { InstantSearch, Configure, connectSearchBox, connectHits, connectHitInsights } from 'react-instantsearch-dom';
|
||||
|
||||
import aa from 'search-insights'
|
||||
|
||||
import {
|
||||
Zoom,
|
||||
Grid,
|
||||
Paper,
|
||||
TextField,
|
||||
ButtonBase,
|
||||
InputAdornment,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip
|
||||
} from '@material-ui/core';
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6")
|
||||
const AppGrid1 = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, searchValue } = props
|
||||
|
||||
const isCloud =
|
||||
window.location.host === "localhost:3000" ||
|
||||
window.location.host === "shuffler.io";
|
||||
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : 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("");
|
||||
|
||||
const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,}
|
||||
|
||||
const innerColor = "rgba(255,255,255,0.65)"
|
||||
const borderRadius = 3
|
||||
window.title = "Shuffle | Apps | Find and integrate any app"
|
||||
|
||||
const submitContact = (email, message) => {
|
||||
const data = {
|
||||
"firstname": "",
|
||||
"lastname": "",
|
||||
"title": "",
|
||||
"companyname": "",
|
||||
"email": email,
|
||||
"phone": "",
|
||||
"message": message,
|
||||
}
|
||||
|
||||
const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly."
|
||||
|
||||
fetch(globalUrl+"/api/v1/contact", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
setFormMail("")
|
||||
setMessage("")
|
||||
})
|
||||
.catch(error => {
|
||||
setFormMessage(errorMessage)
|
||||
console.log(error)
|
||||
});
|
||||
}
|
||||
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = {searchValue}
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
console.log("Got query: ", foundQuery)
|
||||
refine(foundQuery)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<form noValidate action="" role="search">
|
||||
<input
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
height: 50,
|
||||
},
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon style={{marginLeft: 5}}/>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
autoComplete='off'
|
||||
type="hidden"
|
||||
color="primary"
|
||||
defaultValue={currentRefinement}
|
||||
placeholder="Find Apps..."
|
||||
id="shuffle_search_field"
|
||||
onChange={(event) => {
|
||||
refine(event.currentTarget.value)
|
||||
}}
|
||||
limit={5}
|
||||
/>
|
||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
var workflowDelay = -50
|
||||
const Hits = ({ hits, insights }) => {
|
||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1)
|
||||
var counted = 0
|
||||
|
||||
//console.log(hits)
|
||||
//var curhits = hits
|
||||
//if (hits.length > 0 && defaultApps.length === 0) {
|
||||
// setDefaultApps(hits)
|
||||
//}
|
||||
|
||||
//const [defaultApps, setDefaultApps] = React.useState([])
|
||||
//console.log(hits)
|
||||
//if (hits.length > 0 && hits.length !== innerHits.length) {
|
||||
// setInnerHits(hits)
|
||||
//}
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
{hits.map((data, index) => {
|
||||
|
||||
workflowDelay += 50
|
||||
|
||||
const paperStyle = {
|
||||
backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor,
|
||||
color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)",
|
||||
border: `1px solid ${innerColor}`,
|
||||
padding: 15,
|
||||
cursor: "pointer",
|
||||
position: "relative",
|
||||
minHeight: 116,
|
||||
}
|
||||
|
||||
if (counted === 12/xs*rowHandler) {
|
||||
return null
|
||||
}
|
||||
|
||||
counted += 1
|
||||
var parsedname = ""
|
||||
for (var key = 0; key < data.name.length; key++) {
|
||||
var character = data.name.charAt(key)
|
||||
if (character === character.toUpperCase()) {
|
||||
//console.log(data.name[key], data.name[key+1])
|
||||
if (data.name.charAt(key+1) !== undefined && data.name.charAt(key+1) === data.name.charAt(key+1).toUpperCase()) {
|
||||
} else {
|
||||
parsedname += " "
|
||||
}
|
||||
}
|
||||
|
||||
parsedname += character
|
||||
}
|
||||
|
||||
parsedname = (parsedname.charAt(0).toUpperCase()+parsedname.substring(1)).replaceAll("_", " ")
|
||||
|
||||
return (
|
||||
<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>
|
||||
<Grid item xs={xs} key={index}>
|
||||
<Link to={`/apps/${data.objectID}?queryID=${data.__queryID}`} style={{textDecoration: "none", color: "#f85a3e"}}>
|
||||
<Paper elevation={0} style={paperStyle} onMouseOver={() => {
|
||||
setMouseHoverIndex(index)
|
||||
/*
|
||||
ReactGA.event({
|
||||
category: "app_grid_view",
|
||||
action: `search_bar_click`,
|
||||
label: "",
|
||||
})
|
||||
*/
|
||||
}} onMouseOut={() => {
|
||||
setMouseHoverIndex(-1)
|
||||
}} onClick={() => {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "app_grid_view",
|
||||
action: `app_${parsedname}_${data.id}_click`,
|
||||
label: "",
|
||||
})
|
||||
}
|
||||
|
||||
//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6")
|
||||
console.log(searchClient)
|
||||
aa('init', {
|
||||
appId: searchClient.appId,
|
||||
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||
})
|
||||
|
||||
const timestamp = new Date().getTime()
|
||||
aa('sendEvents', [
|
||||
{
|
||||
eventType: 'click',
|
||||
eventName: 'Product Clicked',
|
||||
index: 'appsearch',
|
||||
objectIDs: [data.objectID],
|
||||
timestamp: timestamp,
|
||||
queryID: data.__queryID,
|
||||
positions: [data.__position],
|
||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||
}
|
||||
])
|
||||
|
||||
}}>
|
||||
<ButtonBase style={{padding: 5, borderRadius: 3, minHeight: 100, minWidth: 100,}}>
|
||||
<img alt={data.name} src={data.image_url} style={{width: "100%", maxWidth: 100, minWidth: 100, minHeight: 100, maxHeight: 100, display: "block", margin: "0 auto"}} />
|
||||
</ButtonBase>
|
||||
<div/>
|
||||
{index === mouseHoverIndex || showName === true ?
|
||||
parsedname
|
||||
:
|
||||
null
|
||||
}
|
||||
{data.generated ?
|
||||
<Tooltip title={"Created with App editor"} style={{marginTop: "28px", width: "100%"}} aria-label={data.name}>
|
||||
{data.invalid ?
|
||||
<CloudQueueIcon style={{position: "absolute", top: 1, left: 3, height: 16, width: 16, color: theme.palette.primary.main }}/>
|
||||
:
|
||||
<CloudQueueIcon style={{position: "absolute", top: 1, left: 3, height: 16, width: 16, color: "rgba(255,255,255,0.95)",}}/>
|
||||
}
|
||||
</Tooltip>
|
||||
:
|
||||
<Tooltip title={"Created with python (custom app)"} style={{marginTop: "28px", width: "100%"}} aria-label={data.name}>
|
||||
<CodeIcon style={{position: "absolute", top: 1, left: 3, height: 16, width: 16, color: "rgba(255,255,255,0.95)",}}/>
|
||||
</Tooltip>
|
||||
}
|
||||
</Paper>
|
||||
</Link>
|
||||
</Grid>
|
||||
</Zoom>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomSearchBox = connectSearchBox(SearchBox)
|
||||
const CustomHits = connectHits(Hits)
|
||||
//const CustomHits = connectHitInsights(aa)(Hits)
|
||||
const selectButtonStyle = {
|
||||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
minHeight: 50,
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{width: "100%", textAlign: "center", position: "relative", height: "100%", display: "flex"}}>
|
||||
{/*
|
||||
<div style={{padding: 10, }}>
|
||||
<Button
|
||||
style={selectButtonStyle}
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
const searchField = document.createElement("shuffle_search_field")
|
||||
console.log("Field: ", searchField)
|
||||
if (searchField !== null & searchField !== undefined) {
|
||||
console.log("Set field.")
|
||||
searchField.value = "WHAT WABALABA"
|
||||
searchField.setAttribute("value", "WHAT WABALABA")
|
||||
}
|
||||
}}
|
||||
>
|
||||
Cases
|
||||
</Button>
|
||||
</div>
|
||||
*/}
|
||||
<div style={{width: "100%", position: "relative", height: "100%",}}>
|
||||
<InstantSearch searchClient={searchClient} indexName="appsearch">
|
||||
<div style={{maxWidth: 450, margin: "auto", marginTop: 15, marginBottom: 15, }}>
|
||||
<CustomSearchBox />
|
||||
</div>
|
||||
<CustomHits hitsPerPage={5}/>
|
||||
<Configure clickAnalytics />
|
||||
</InstantSearch>
|
||||
{showSuggestion === true ?
|
||||
<div style={{paddingTop: 0, maxWidth: isMobile ? "100%" : "60%", margin: "auto"}}>
|
||||
<Typography variant="h6" style={{color: "white", marginTop: 50,}}>
|
||||
Can't find what you're looking for?
|
||||
</Typography>
|
||||
<div style={{flex: "1", display: "flex", flexDirection: "row", textAlign: "center",}}>
|
||||
<TextField
|
||||
required
|
||||
style={{flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "#ffffff",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Email (optional)"
|
||||
type="email"
|
||||
id="email-handler"
|
||||
autoComplete="email"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setFormMail(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
style={{flex: "1", backgroundColor: theme.palette.inputColor}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "#ffffff",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="What apps do you want to see?"
|
||||
type=""
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
autoComplete="off"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={buttonStyle}
|
||||
disabled={message.length === 0}
|
||||
onClick={() => {
|
||||
submitContact(formMail, message)
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<Typography style={{color: "white"}} variant="body2">{formMessage}</Typography>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
|
||||
<span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppGrid1;
|
||||
@@ -0,0 +1,403 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import theme from '../theme.jsx';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material';
|
||||
import aa from 'search-insights'
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import ShowChartIcon from '@mui/icons-material/ShowChart';
|
||||
import ExploreIcon from '@mui/icons-material/Explore';
|
||||
import LightbulbIcon from "@mui/icons-material/Lightbulb";
|
||||
import NewReleasesIcon from "@mui/icons-material/NewReleases";
|
||||
import ExtensionIcon from "@mui/icons-material/Extension";
|
||||
import EmailIcon from "@mui/icons-material/Email";
|
||||
import FingerprintIcon from '@mui/icons-material/Fingerprint';
|
||||
import AppSearch from "../components/Appsearch.jsx";
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
Zoom,
|
||||
Grid,
|
||||
Paper,
|
||||
TextField,
|
||||
Collapse,
|
||||
IconButton,
|
||||
Avatar,
|
||||
ButtonBase,
|
||||
InputAdornment,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
} from '@mui/material';
|
||||
|
||||
const AppSearchButtons = (props) => {
|
||||
const { userdata, globalUrl, appFramework, defaultSearch, finishedApps, onNodeSelect, setDiscoveryData, appName, AppImage, setDefaultSearch, discoveryData } = props
|
||||
const ref = useRef()
|
||||
const [moreButton, setMoreButton] = useState(false);
|
||||
let navigate = useNavigate();
|
||||
|
||||
const sizing = moreButton ? 510 : 480;
|
||||
const buttonWidth = 450;
|
||||
const buttonMargin = 10;
|
||||
const bottomButtonStyle = {
|
||||
borderRadius: 200,
|
||||
marginTop: moreButton ? 44 : "",
|
||||
height: 51,
|
||||
width: 510,
|
||||
fontSize: 16,
|
||||
// background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
|
||||
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
|
||||
padding: "16px 24px",
|
||||
// top: 20,
|
||||
// margin: "auto",
|
||||
textTransform: 'capitalize',
|
||||
itemAlign: "center",
|
||||
// marginTop: 25
|
||||
// marginLeft: "65px",
|
||||
};
|
||||
const buttonStyle = {
|
||||
flex: 1,
|
||||
width: 224,
|
||||
padding: 25,
|
||||
margin: buttonMargin,
|
||||
color: "var(--White-text, #F1F1F1)",
|
||||
fontWeight: 400,
|
||||
fontSize: 17,
|
||||
background: "rgba(33, 33, 33, 1)",
|
||||
textTransform: 'capitalize',
|
||||
border: "1px solid rgba(33, 33, 33, 1)",
|
||||
borderRadius: 8,
|
||||
marginRight: 8,
|
||||
};
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
const mouseOver = (e) => {
|
||||
e.target.style.border = "1px solid #f85a3e";
|
||||
}
|
||||
const mouseOut = (e) => {
|
||||
e.target.style.border = "1px solid rgb(33, 33, 33)";
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid item xs={11} style={{ display: "flex" }}>
|
||||
|
||||
{/*<FormLabel style={{ color: "#B9B9BA" }}>Find your integrations!</FormLabel>*/}
|
||||
{/* <div style={{ display: "flex", width: 510, height:100 }}>
|
||||
<Button
|
||||
disabled={finishedApps.includes("CASES")}
|
||||
variant={
|
||||
defaultSearch === "CASES" ? "contained" : "outlined"
|
||||
}
|
||||
color="secondary"
|
||||
style={{
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
padding: 25,
|
||||
margin: buttonMargin,
|
||||
fontSize: 18,
|
||||
color: "var(--White-text, #F1F1F1)",
|
||||
fontWeight: 400,
|
||||
background: "rgba(33, 33, 33, 1)",
|
||||
borderRadius: 8,
|
||||
textTransform: 'capitalize',
|
||||
border: "1px solid rgba(33, 33, 33, 1)" ,
|
||||
}}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
// startIcon = {defaultSearch === "CASES" ? newSelectedApp.image_url : <LightbulbIcon/>}
|
||||
onClick={(event) => {
|
||||
onNodeSelect("CASES");
|
||||
setDefaultSearch(discoveryData.label)
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.cases === undefined || appFramework.cases.large_image === undefined ||
|
||||
appFramework === null || appFramework.cases === null || appFramework.cases.large_image === null || appFramework.cases.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<LightbulbIcon style={{ marginTop: 6 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={AppImage} />}
|
||||
<div style={{marginLeft: 8, }}>
|
||||
<Typography style={{display:"flex",border:"none"}} >Case Management</Typography>
|
||||
{appFramework === undefined || appFramework.cases === undefined || appFramework.cases.name === undefined ||
|
||||
appFramework === null || appFramework.cases === null || appFramework.cases.name === null || appFramework.cases.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{AppName}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: "flex", width: 510, height: 100 }}>
|
||||
<Button
|
||||
disabled={finishedApps.includes("SIEM")}
|
||||
variant={
|
||||
defaultSearch === "SIEM" ? "contained" : "outlined"
|
||||
}
|
||||
style={buttonStyle}
|
||||
// startIcon={<SearchIcon />}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
color="secondary"
|
||||
onClick={(event) => {
|
||||
onNodeSelect("SIEM");
|
||||
setDefaultSearch(discoveryData.label)
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.siem === undefined || appFramework.siem.large_image === undefined ||
|
||||
appFramework === null || appFramework.siem === null || appFramework.siem.large_image === null || appFramework.siem.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<SearchIcon style={{ marginTop: 6 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.siem.large_image} />}
|
||||
<div style={{marginLeft: 8,}}>
|
||||
<Typography style={{display:"flex",}}>SIEM</Typography>
|
||||
{appFramework === undefined || appFramework.siem === undefined || appFramework.siem.name === undefined ||
|
||||
appFramework === null || appFramework.siem === null || appFramework.siem.name === null || appFramework.siem.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.siem.name.split('_').join(' ')}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
finishedApps.includes("EDR & AV") ||
|
||||
finishedApps.includes("ERADICATION")
|
||||
}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
variant={
|
||||
defaultSearch === "Eradication" ? "contained" : "outlined"
|
||||
}
|
||||
style={buttonStyle}
|
||||
// startIcon={<NewReleasesIcon />}
|
||||
color="secondary"
|
||||
onClick={(event) => {
|
||||
onNodeSelect("ERADICATION");
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.edr === undefined || appFramework.edr.large_image === undefined ||
|
||||
appFramework === null || appFramework.edr === null || appFramework.edr.large_image === null || appFramework.edr.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<NewReleasesIcon style={{marginTop: 8 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.edr.large_image} />}
|
||||
<div style={{marginLeft: 8,}}>
|
||||
<Typography style={{display:"flex",}}>Endpoint</Typography>
|
||||
{appFramework === undefined || appFramework.edr === undefined || appFramework.edr.name === undefined ||
|
||||
appFramework === null || appFramework.edr === null || appFramework.edr.name === null || appFramework.edr.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.edr.name.split('_').join(' ')}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: "flex", width: 510, height: 100 }}>
|
||||
<Button
|
||||
disabled={finishedApps.includes("INTEL")}
|
||||
variant={
|
||||
defaultSearch === "INTEL" ? "contained" : "outlined"
|
||||
}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
style={buttonStyle}
|
||||
// startIcon={<ExtensionIcon />}
|
||||
color="secondary"
|
||||
onClick={(event) => {
|
||||
onNodeSelect("INTEL");
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.intel === undefined || appFramework.intel.large_image === undefined ||
|
||||
appFramework === null || appFramework.intel === null || appFramework.intel.large_image === null || appFramework.intel.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<ExtensionIcon style={{ marginTop: 8 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.intel.large_image} />}
|
||||
<div style={{marginLeft: 8,}}>
|
||||
<Typography style={{display:"flex",}}>Intel</Typography>
|
||||
{appFramework === undefined || appFramework.intel === undefined || appFramework.intel.name === undefined ||
|
||||
appFramework === null || appFramework.intel === null || appFramework.intel.name === null || appFramework.intel.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.intel.name.split('_').join(' ')}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
finishedApps.includes("COMMS") ||
|
||||
finishedApps.includes("EMAIL")
|
||||
}
|
||||
variant={
|
||||
defaultSearch === "EMAIL" ? "contained" : "outlined"
|
||||
}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
style={buttonStyle}
|
||||
// startIcon={<EmailIcon />}
|
||||
color="secondary"
|
||||
onClick={(event) => {
|
||||
onNodeSelect("EMAIL");
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.communication === undefined || appFramework.communication.large_image === undefined ||
|
||||
appFramework === null || appFramework.communication === null || appFramework.communication.large_image === null || appFramework.communication.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<EmailIcon style={{ marginTop: 8 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.communication.large_image} />}
|
||||
<div style={{marginLeft: 8,}}>
|
||||
<Typography style={{display:"flex",}}>Email</Typography>
|
||||
{appFramework === undefined || appFramework.communication === undefined || appFramework.communication.name === undefined ||
|
||||
appFramework === null || appFramework.communication === null || appFramework.communication.name === null || appFramework.communication.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 12, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.communication.name.split('_').join(' ')}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
{moreButton ? (
|
||||
<div style={{ display: "flex", width: 510, height: 100, marginBottom: 20 }}>
|
||||
<Button
|
||||
disabled={finishedApps.includes("NETWORK")}
|
||||
variant={
|
||||
defaultSearch === "NETWORK" ? "contained" : "outlined"
|
||||
}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
style={buttonStyle}
|
||||
// startIcon={<ExtensionIcon />}
|
||||
color="secondary"
|
||||
onClick={(event) => {
|
||||
onNodeSelect("NETWORK");
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.network === undefined || appFramework.network.large_image === undefined ||
|
||||
appFramework === null || appFramework.network === null || appFramework.network.large_image === null || appFramework.network.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<ShowChartIcon style={{ marginTop: 8 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.network.large_image} />}
|
||||
<div style={{marginLeft: 8,}}>
|
||||
<Typography style={{display:"flex",}}>Network</Typography>
|
||||
{appFramework === undefined || appFramework.network === undefined || appFramework.network.name === undefined ||
|
||||
appFramework === null || appFramework.network === null || appFramework.network.name === null || appFramework.network.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 10, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.network.name}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
finishedApps.includes("ASSETS")
|
||||
}
|
||||
variant={
|
||||
defaultSearch === "ASSETS" ? "contained" : "outlined"
|
||||
}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
style={buttonStyle}
|
||||
// startIcon={<EmailIcon />}
|
||||
color="secondary"
|
||||
onClick={(event) => {
|
||||
onNodeSelect("ASSETS");
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.assets === undefined || appFramework.assets.large_image === undefined ||
|
||||
appFramework === null || appFramework.assets === null || appFramework.assets.large_image === null || appFramework.assets.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<ExploreIcon style={{ marginTop: 8 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.assets.large_image} />}
|
||||
<div style={{marginLeft: 8,}}>
|
||||
<Typography style={{display:"flex",}}>Assets</Typography>
|
||||
{appFramework === undefined || appFramework.assets === undefined || appFramework.assets.name === undefined ||
|
||||
appFramework === null || appFramework.assets === null || appFramework.assets.name === null || appFramework.assets.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 10, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.assets.name}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
finishedApps.includes("IAM")
|
||||
}
|
||||
variant={
|
||||
defaultSearch === "IAM" ? "contained" : "outlined"
|
||||
}
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
style={buttonStyle}
|
||||
// startIcon={<EmailIcon />}
|
||||
color="secondary"
|
||||
onClick={(event) => {
|
||||
onNodeSelect("IAM");
|
||||
}}
|
||||
>
|
||||
{appFramework === undefined || appFramework.iam === undefined || appFramework.iam.large_image === undefined ||
|
||||
appFramework === null || appFramework.iam === null || appFramework.iam.large_image === null || appFramework.iam.large_image.length === 0 ?
|
||||
<div style={{width: 40, border: "1px solid rgba(33, 33, 33, 1) !importent", height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign:"center"}}>
|
||||
<FingerprintIcon style={{ marginTop: 8 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={appFramework.iam.large_image} />}
|
||||
<div style={{marginLeft: 8,}}>
|
||||
<Typography style={{display:"flex",}}>IAM</Typography>
|
||||
{appFramework === undefined || appFramework.iam === undefined || appFramework.iam.name === undefined ||
|
||||
appFramework === null || appFramework.iam === null || appFramework.iam.name === null || appFramework.iam.name.length === 0 ?
|
||||
"":<Typography style={{fontSize: 8, textAlign:"left", color:"var(--label-grey-text, #9E9E9E)" }} >{appFramework.iam.name}</Typography>}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
:
|
||||
<div style={{ display: "flex", width: 510, paddingLeft: 165, }}>
|
||||
<Button
|
||||
style={{ color: "#f86a3e", textTransform: 'capitalize', border: 2, backgroundColor: "var(--Background-color, #1A1A1A)" }}
|
||||
className="btn btn-primary"
|
||||
onClick={(event) => {
|
||||
setMoreButton(true);
|
||||
}}
|
||||
>
|
||||
<Typography style={{ textDecorationLine: 'underline', }}>
|
||||
See more Categories
|
||||
</Typography>
|
||||
</Button>
|
||||
</div>} */}
|
||||
<div style={{ display: "flex", width: 510, height: 64, borderRadius: 8, background: "var(--Container, #212121)" }}
|
||||
>
|
||||
<div
|
||||
onMouseOver={mouseOver}
|
||||
onMouseOut={mouseOut}
|
||||
disabled={finishedApps.includes("CASES")}
|
||||
variant={
|
||||
defaultSearch === "CASES" ? "contained" : "outlined"
|
||||
}
|
||||
color="secondary"
|
||||
style={{
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
margin: buttonMargin,
|
||||
fontSize: 18,
|
||||
color: "var(--White-text, #F1F1F1)",
|
||||
fontWeight: 400,
|
||||
background: "rgba(33, 33, 33, 1)",
|
||||
borderRadius: 8,
|
||||
textTransform: 'capitalize',
|
||||
}}
|
||||
// startIcon = {defaultSearch === "CASES" ? newSelectedApp.image_url : <LightbulbIcon/>}
|
||||
onClick={(event) => {
|
||||
onNodeSelect("CASES");
|
||||
setDefaultSearch(discoveryData.label)
|
||||
}}
|
||||
>
|
||||
<div style={{marginLeft: 20, display:"flex", textAlign:"center", alignItems:"center", marginLeft: 100, width: 320, marginRight: "auto" }}>
|
||||
{AppImage === undefined || AppImage === undefined ||
|
||||
AppImage === null || AppImage === null || AppImage.length === 0 ?
|
||||
<div style={{ width: 40, height: 40, borderRadius: 9999, backgroundColor: "#2F2F2F", textAlign: "center" }}>
|
||||
<LightbulbIcon style={{ marginTop: 6 }} />
|
||||
</div>
|
||||
: <img style={{ marginRight: 8, width: 40, height: 40, flexShrink: 0, borderRadius: 40, }} src={AppImage} />}
|
||||
<div style={{ marginLeft: 8, }}>
|
||||
<Typography style={{ display: "flex", border: "none" }} >Case Management</Typography>
|
||||
{appName === undefined || appName === undefined ||
|
||||
appName === null || appName === null || appName.length === 0 ?
|
||||
""
|
||||
:
|
||||
<Typography style={{ fontSize: 12, textAlign: "left", color: "var(--label-grey-text, #9E9E9E)" }} >{appName}</Typography>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
export default AppSearchButtons
|
||||
@@ -0,0 +1,433 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import theme from '../theme.jsx';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material';
|
||||
import aa from 'search-insights'
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import ShowChartIcon from '@mui/icons-material/ShowChart';
|
||||
import ExploreIcon from '@mui/icons-material/Explore';
|
||||
import LightbulbIcon from "@mui/icons-material/Lightbulb";
|
||||
import NewReleasesIcon from "@mui/icons-material/NewReleases";
|
||||
import ExtensionIcon from "@mui/icons-material/Extension";
|
||||
import EmailIcon from "@mui/icons-material/Email";
|
||||
import FingerprintIcon from '@mui/icons-material/Fingerprint';
|
||||
import AppSearch from "../components/Appsearch.jsx";
|
||||
import AppSearchButtons from "../components/AppSearchButtons.jsx";
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
Zoom,
|
||||
Grid,
|
||||
Paper,
|
||||
TextField,
|
||||
Collapse,
|
||||
IconButton,
|
||||
Avatar,
|
||||
ButtonBase,
|
||||
InputAdornment,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
} from '@mui/material';
|
||||
|
||||
const AppSelection = props => {
|
||||
const {
|
||||
userdata,
|
||||
globalUrl,
|
||||
appFramework,
|
||||
setActiveStep,
|
||||
defaultSearch,
|
||||
setDefaultSearch,
|
||||
checkLogin,
|
||||
} = props;
|
||||
const [discoveryData, setDiscoveryData] = React.useState({})
|
||||
const [selectionOpen, setSelectionOpen] = React.useState(false)
|
||||
const [newSelectedApp, setNewSelectedApp] = React.useState({})
|
||||
const [finishedApps, setFinishedApps] = React.useState([])
|
||||
const [appButtons, setAppButtons] = useState([])
|
||||
const [apps, setApps] = useState([])
|
||||
const [appName, setAppName] = React.useState();
|
||||
const [moreButton, setMoreButton] = useState(false);
|
||||
|
||||
// const [mouseHoverIndex, setMouseHoverIndex] = useState(-1)
|
||||
const ref = useRef()
|
||||
let navigate = useNavigate();
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
|
||||
const setFrameworkItem = (data) => {
|
||||
console.log("Setting framework item: ", data, isCloud)
|
||||
// if (!isCloud) {
|
||||
// activateApp(data.id)
|
||||
// }
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
if (checkLogin !== undefined) {
|
||||
checkLogin()
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
toast("Failed to update framework for your org.")
|
||||
|
||||
}
|
||||
}
|
||||
//setFrameworkLoaded(true)
|
||||
//setFrameworkData(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
if (checkLogin !== undefined) {
|
||||
checkLogin()
|
||||
}
|
||||
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
const GetApps = (data) => {
|
||||
console.log("Setting framework item: ", data, isCloud)
|
||||
// if (!isCloud) {
|
||||
// activateApp(data.id)
|
||||
// }
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson === null) {
|
||||
console.log("null-response from server")
|
||||
const pretend_apps = [{
|
||||
"description": "TBD",
|
||||
"id": "TBD",
|
||||
"large_image": "",
|
||||
"name": "TBD",
|
||||
"type": "TBD"
|
||||
}]
|
||||
|
||||
setApps(pretend_apps)
|
||||
return
|
||||
}
|
||||
|
||||
if (responseJson.success === false) {
|
||||
console.log("error loading apps: ", responseJson)
|
||||
return
|
||||
}
|
||||
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
const onNodeSelect = (label) => {
|
||||
// if (setDiscoveryWrapper !== undefined) {
|
||||
// setDiscoveryWrapper({ id: label });
|
||||
// }
|
||||
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "welcome",
|
||||
action: `click_${label}`,
|
||||
label: "",
|
||||
});
|
||||
}
|
||||
setDiscoveryData(label)
|
||||
setSelectionOpen(true)
|
||||
setNewSelectedApp({})
|
||||
setDefaultSearch(label.charAt(0).toUpperCase() + (label.substring(1)).toLowerCase())
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
var tempApps = []
|
||||
if (tempApps.length === 0) {
|
||||
const tempApps =
|
||||
[{
|
||||
"description": newSelectedApp.description,
|
||||
"id": newSelectedApp.objectID,
|
||||
"large_image": newSelectedApp.image_url,
|
||||
"name": newSelectedApp.name,
|
||||
"type": discoveryData
|
||||
},
|
||||
//{
|
||||
// // description: newSelectedApp.siem.description,
|
||||
// id: newSelectedApp.siem.objectID,
|
||||
// large_image: newSelectedApp.siem.image_url,
|
||||
// name: newSelectedApp.siem.name,
|
||||
// type: discoveryData.siem
|
||||
// },{
|
||||
// // description: newSelectedApp.edr.description,
|
||||
// id: newSelectedApp.edr.objectID,
|
||||
// large_image: newSelectedApp.edr.image_url,
|
||||
// name: newSelectedApp.edr.name,
|
||||
// type: discoveryData.edr
|
||||
// }
|
||||
]
|
||||
setAppButtons(tempApps)
|
||||
GetApps()
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||
return
|
||||
}
|
||||
const submitNewApp = {
|
||||
description: newSelectedApp.description,
|
||||
id: newSelectedApp.objectID,
|
||||
large_image: newSelectedApp.image_url,
|
||||
name: newSelectedApp.name,
|
||||
type: discoveryData
|
||||
}
|
||||
if (discoveryData === "CASES") {
|
||||
appFramework.cases = submitNewApp
|
||||
}
|
||||
else if (discoveryData === "SIEM") {
|
||||
appFramework.siem = submitNewApp
|
||||
}
|
||||
else if (discoveryData === "ERADICATION") {
|
||||
appFramework.edr = submitNewApp
|
||||
}
|
||||
else if (discoveryData === "INTEL") {
|
||||
appFramework.intel = submitNewApp
|
||||
}
|
||||
else if (discoveryData === "EMAIL") {
|
||||
appFramework.communication = submitNewApp
|
||||
}
|
||||
else if (discoveryData === "NETWORK") {
|
||||
appFramework.network = submitNewApp
|
||||
}
|
||||
else if (discoveryData === "ASSETS") {
|
||||
appFramework.assets = submitNewApp
|
||||
}
|
||||
else if (discoveryData === "IAM") {
|
||||
appFramework.iam = submitNewApp
|
||||
}
|
||||
setFrameworkItem(submitNewApp);
|
||||
setSelectionOpen(false);
|
||||
console.log("Selected app changed (effect)");
|
||||
}, [newSelectedApp]);
|
||||
|
||||
const sizing = moreButton ? 510 : 480;
|
||||
const buttonWidth = 450;
|
||||
const buttonMargin = 10;
|
||||
const bottomButtonStyle = {
|
||||
borderRadius: 200,
|
||||
marginTop: moreButton ? 44 : "",
|
||||
height: 51,
|
||||
width: 510,
|
||||
fontSize: 16,
|
||||
// background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
|
||||
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
|
||||
padding: "16px 24px",
|
||||
// top: 20,
|
||||
// margin: "auto",
|
||||
textTransform: 'capitalize',
|
||||
itemAlign: "center",
|
||||
// marginTop: 25
|
||||
// marginLeft: "65px",
|
||||
};
|
||||
const buttonStyle = {
|
||||
flex: 1,
|
||||
width: 224,
|
||||
padding: 25,
|
||||
margin: buttonMargin,
|
||||
color: "var(--White-text, #F1F1F1)",
|
||||
fontWeight: 400,
|
||||
fontSize: 17,
|
||||
background: "rgba(33, 33, 33, 1)",
|
||||
textTransform: 'capitalize',
|
||||
border: "1px solid rgba(33, 33, 33, 1)",
|
||||
borderRadius: 8,
|
||||
marginRight: 8,
|
||||
};
|
||||
// console.log("appFramework",appFramework.cases.name)
|
||||
return (
|
||||
<Collapse in={true}>
|
||||
<div
|
||||
style={{
|
||||
minHeight: sizing,
|
||||
maxHeight: sizing,
|
||||
marginTop: 10,
|
||||
width: 500,
|
||||
}}
|
||||
>
|
||||
{selectionOpen ? (
|
||||
<div
|
||||
style={{
|
||||
width: 319,
|
||||
height: 395,
|
||||
flexShrink: 0,
|
||||
marginLeft: 70,
|
||||
marginTop: 68,
|
||||
position: "absolute",
|
||||
zIndex: 100,
|
||||
borderRadius: 6,
|
||||
border: "1px solid var(--Container-Stroke, #494949)",
|
||||
background: "var(--Container, #212121)",
|
||||
boxShadow: "8px 8px 32px 24px rgba(0, 0, 0, 0.16)",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{ display: "flex", textAlign: "center", textTransform: "capitalize" }}>
|
||||
<Typography style={{ padding: 16, color: "#FFFFFF", textTransform: "capitalize" }}> {discoveryData} </Typography>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Tooltip
|
||||
title="Close"
|
||||
placement="top"
|
||||
style={{ zIndex: 10011 }}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
flex: 1,
|
||||
// width: 224,
|
||||
marginLeft: discoveryData === ('ERADICATION') ? 120 : 177,
|
||||
width: "100%",
|
||||
marginBottom: 23,
|
||||
fontSize: 16,
|
||||
background: "rgba(33, 33, 33, 1)",
|
||||
borderColor: "rgba(33, 33, 33, 1)",
|
||||
borderRadius: 8,
|
||||
}}
|
||||
onClick={() => {
|
||||
setSelectionOpen(false)
|
||||
}}
|
||||
>
|
||||
<CloseIcon style={{ width: 16 }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title="Delete app"
|
||||
placement="bottom"
|
||||
style={{ zIndex: 10011 }}
|
||||
>
|
||||
<IconButton
|
||||
style={{ zIndex: 12501, position: "absolute", top: 32, right: 16 }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setSelectionOpen(false)
|
||||
setDefaultSearch("")
|
||||
const submitDeletedApp = {
|
||||
"description": "",
|
||||
"id": "remove",
|
||||
"name": "",
|
||||
"type": discoveryData
|
||||
}
|
||||
setFrameworkItem(submitDeletedApp)
|
||||
setNewSelectedApp({})
|
||||
setTimeout(() => {
|
||||
setDiscoveryData({})
|
||||
setFrameworkItem(submitDeletedApp)
|
||||
setNewSelectedApp({})
|
||||
}, 1000)
|
||||
//setAppName(discoveryData.cases.name)
|
||||
}}
|
||||
>
|
||||
<DeleteIcon style={{ color: "white", height: 15, width: 15, }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{ width: "100%", border: "1px #494949 solid" }}
|
||||
/>
|
||||
<AppSearch
|
||||
defaultSearch={defaultSearch}
|
||||
newSelectedApp={newSelectedApp}
|
||||
setNewSelectedApp={setNewSelectedApp}
|
||||
userdata={userdata}
|
||||
// cy={cy}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<Typography
|
||||
variant="h4"
|
||||
style={{
|
||||
marginLeft: 8,
|
||||
marginTop: 40,
|
||||
marginRight: 30,
|
||||
marginBottom: 0,
|
||||
}}
|
||||
color="rgba(241, 241, 241, 1)"
|
||||
>
|
||||
Find your apps
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{
|
||||
marginLeft: 8,
|
||||
marginTop: 10,
|
||||
marginRight: 30,
|
||||
marginBottom: 40,
|
||||
}}
|
||||
color="rgba(158, 158, 158, 1)"
|
||||
>
|
||||
Select the apps you work with and we will connect the for you.
|
||||
</Typography>
|
||||
{appButtons.map((appData, index) => {
|
||||
|
||||
const appName = appData.name
|
||||
const AppImage = appData.large_image
|
||||
const appType = appData.type
|
||||
|
||||
return (
|
||||
|
||||
<AppSearchButtons
|
||||
appFramework={appFramework}
|
||||
appName={appName}
|
||||
appType = {appType}
|
||||
AppImage={AppImage}
|
||||
defaultSearch={defaultSearch}
|
||||
finishedApps={finishedApps}
|
||||
onNodeSelect={onNodeSelect}
|
||||
discoveryData={discoveryData}
|
||||
setDiscoveryData={setDiscoveryData}
|
||||
setDefaultSearch={setDefaultSearch}
|
||||
apps={apps}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div style={{ flexDirection: "row", }}>
|
||||
<Button variant="contained" type="submit" fullWidth style={bottomButtonStyle} onClick={() => {
|
||||
navigate("/welcome?tab=3")
|
||||
setActiveStep(2)
|
||||
}}>
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
</Collapse>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppSelection;
|
||||
@@ -1,24 +1,33 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import theme from '../theme.jsx';
|
||||
import {Link} from 'react-router-dom';
|
||||
import { useAlert } from "react-alert";
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons';
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
//import algoliasearch from 'algoliasearch/lite';
|
||||
import algoliasearch from 'algoliasearch';
|
||||
import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom';
|
||||
import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@material-ui/core';
|
||||
import {
|
||||
Grid,
|
||||
Paper,
|
||||
TextField,
|
||||
ButtonBase,
|
||||
InputAdornment,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip
|
||||
} from '@mui/material';
|
||||
|
||||
import aa from 'search-insights'
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const Appsearch = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList} = props
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const alert = useAlert();
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs
|
||||
const theme = useTheme();
|
||||
//const theme = useTheme();
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
//const [filteredApps, setFilteredApps] = React.useState([]);
|
||||
const [formMail, setFormMail] = React.useState("");
|
||||
@@ -31,82 +40,6 @@ const Appsearch = props => {
|
||||
const borderRadius = 3
|
||||
window.title = "Shuffle | Apps | Find and integration any app"
|
||||
|
||||
const setUserSpecialzedApp = (user, data) => {
|
||||
// var data = newfields]
|
||||
console.log("data value", data)
|
||||
const appData = {"user_id":user,"specialized_apps":[{}]}
|
||||
console.log("User Check for appdata:", user)
|
||||
appData["specialized_apps"][0]["name"] = data["name"]
|
||||
appData["specialized_apps"][0]["image"] = data["image_url"]
|
||||
appData["specialized_apps"][0]["category"] = data["categories"].toString()
|
||||
console.log("AppData:",appData)
|
||||
console.log("setActionImageList",setActionImageList)
|
||||
console.log("actionImageList",actionImageList)
|
||||
|
||||
const finalData = actionImageList.concat(appData["specialized_apps"])
|
||||
appData["specialized_apps"]=finalData
|
||||
fetch(globalUrl + "/api/v1/users/updateuser", {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(appData),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for set creator :O!");
|
||||
}
|
||||
alert.success("Sucessfully updated specialzed app.")
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success && responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating user: " + responseJson.reason);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
const submitContact = (email, message) => {
|
||||
const data = {
|
||||
"firstname": "",
|
||||
"lastname": "",
|
||||
"title": "",
|
||||
"companyname": "",
|
||||
"email": email,
|
||||
"phone": "",
|
||||
"message": message,
|
||||
}
|
||||
|
||||
const errorMessage = "Something went wrong. Please contact frikky@shuffler.io directly."
|
||||
|
||||
fetch(globalUrl+"/api/v1/contact", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
setFormMail("")
|
||||
setMessage("")
|
||||
})
|
||||
.catch(error => {
|
||||
setFormMessage(errorMessage)
|
||||
console.log(error)
|
||||
});
|
||||
}
|
||||
|
||||
// value={currentRefinement}
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
@@ -121,14 +54,14 @@ const Appsearch = props => {
|
||||
<form noValidate action="" role="search">
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: "100%",}}
|
||||
style={{backgroundColor: "#2F2F2F", borderRadius: borderRadius, width: "100%",}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
height: 50,
|
||||
},
|
||||
startAdornment: (
|
||||
endAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon style={{marginLeft: 5}}/>
|
||||
</InputAdornment>
|
||||
@@ -138,7 +71,8 @@ const Appsearch = props => {
|
||||
type="search"
|
||||
color="primary"
|
||||
defaultValue={defaultSearch}
|
||||
placeholder={`Find ${defaultSearch} Apps...`}
|
||||
// placeholder={`Find ${defaultSearch} Apps...`}
|
||||
placeholder= {defaultSearch ? `${defaultSearch}` : "Search Cases "}
|
||||
id="shuffle_workflow_search_field"
|
||||
onChange={(event) => {
|
||||
refine(event.currentTarget.value)
|
||||
@@ -159,9 +93,9 @@ const Appsearch = props => {
|
||||
<Grid container spacing={0} style={{border: "1px solid rgba(255,255,255,0.2)", maxHeight: 250, minHeight: 250, overflowY: "auto", overflowX: "hidden", }}>
|
||||
{hits.map((data, index) => {
|
||||
const paperStyle = {
|
||||
backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : theme.palette.inputColor,
|
||||
backgroundColor: index === mouseHoverIndex ? "rgba(255,255,255,0.8)" : "#2F2F2F",
|
||||
color: index === mouseHoverIndex ? theme.palette.inputColor : "rgba(255,255,255,0.8)",
|
||||
border: newSelectedApp.objectID !== data.objectID ? `1px solid rgba(255,255,255,0.2)` : "2px solid #f86a3e",
|
||||
// border: newSelectedApp.objectID !== data.objectID ? `1px solid rgba(255,255,255,0.2)` : "2px solid #f86a3e",
|
||||
textAlign: "left",
|
||||
padding: 10,
|
||||
cursor: "pointer",
|
||||
@@ -207,13 +141,8 @@ const Appsearch = props => {
|
||||
setMouseHoverIndex(-1)
|
||||
}} onClick={() => {
|
||||
if(isCreatorPage === true){
|
||||
console.log("data:",data)
|
||||
console.log("userdata.id",userdata.id)
|
||||
console.log("is creator", isCreatorPage)
|
||||
if (setNewSelectedApp !== undefined) {
|
||||
// setUserSpecialzedApp = data
|
||||
if (setNewSelectedApp !== undefined && setUserSpecialzedApp !== undefined) {
|
||||
setUserSpecialzedApp(userdata.id, data)
|
||||
//setActionImageList(userdata.id, data)
|
||||
}
|
||||
}
|
||||
if (setNewSelectedApp !== undefined) {
|
||||
@@ -255,7 +184,7 @@ const Appsearch = props => {
|
||||
}
|
||||
}}>
|
||||
<div style={{display: "flex"}}>
|
||||
<img alt={data.name} src={data.image_url} style={{width: "100%", maxWidth: 30, minWidth: 30, minHeight: 30, maxHeight: 30, display: "block", }} />
|
||||
<img alt={data.name} src={data.image_url} style={{width: "100%", maxWidth: 30, minWidth: 30, minHeight: 30, borderRadius: 40, maxHeight: 30, display: "block", }} />
|
||||
<Typography variant="body1" style={{marginTop: 2, marginLeft: 10, }}>
|
||||
{parsedname}
|
||||
</Typography>
|
||||
@@ -272,7 +201,7 @@ const Appsearch = props => {
|
||||
const CustomHits = connectHits(InputHits)
|
||||
|
||||
return (
|
||||
<div style={{width: "100%", textAlign: "center", position: "relative", height: "100%",}}>
|
||||
<div style={{width: 287, height: 295, padding: "16px 16px 267px 16px", alignItems: "center", gap: 138,}}>
|
||||
<InstantSearch searchClient={searchClient} indexName="appsearch">
|
||||
{/* showSearch === false ? null :
|
||||
<div style={{maxWidth: 450, margin: "auto", }}>
|
||||
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
CircularProgress,
|
||||
Tooltip,
|
||||
Button,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
Delete as DeleteIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const AppSearchPopout = (props) => {
|
||||
const {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
IconButton,
|
||||
@@ -17,15 +18,14 @@ import {
|
||||
Grid,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField,
|
||||
Zoom,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Edit as EditIcon,
|
||||
Delete as DeleteIcon,
|
||||
SelectAll as SelectAllIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const AuthenticationItem = (props) => {
|
||||
const { data, index, globalUrl, getAppAuthentication } = props
|
||||
@@ -34,7 +34,7 @@ const AuthenticationItem = (props) => {
|
||||
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false);
|
||||
const [authenticationFields, setAuthenticationFields] = React.useState([]);
|
||||
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
var bgColor = "#27292d";
|
||||
if (index % 2 === 0) {
|
||||
bgColor = "#1f2023";
|
||||
@@ -63,7 +63,7 @@ const AuthenticationItem = (props) => {
|
||||
}
|
||||
|
||||
const deleteAuthentication = (data) => {
|
||||
alert.info("Deleting auth " + data.label);
|
||||
toast("Deleting auth " + data.label);
|
||||
|
||||
// Just use this one?
|
||||
const url = globalUrl + "/api/v1/apps/authentication/" + data.id;
|
||||
@@ -79,13 +79,13 @@ const AuthenticationItem = (props) => {
|
||||
response.json().then((responseJson) => {
|
||||
console.log("RESP: ", responseJson);
|
||||
if (responseJson["success"] === false) {
|
||||
alert.error("Failed deleting auth");
|
||||
toast("Failed deleting auth");
|
||||
} else {
|
||||
// Need to wait because query in ES is too fast
|
||||
setTimeout(() => {
|
||||
getAppAuthentication();
|
||||
}, 1000);
|
||||
//alert.success("Successfully deleted authentication!")
|
||||
//toast("Successfully deleted authentication!")
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -115,9 +115,9 @@ const AuthenticationItem = (props) => {
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
alert.error("Failed overwriting appauth in workflows");
|
||||
toast("Failed overwriting appauth in workflows");
|
||||
} else {
|
||||
alert.success("Successfully updated auth everywhere!");
|
||||
toast("Successfully updated auth everywhere!");
|
||||
//setSelectedUserModalOpen(false);
|
||||
setTimeout(() => {
|
||||
getAppAuthentication();
|
||||
@@ -126,7 +126,7 @@ const AuthenticationItem = (props) => {
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
alert.error("Err: " + error.toString());
|
||||
toast("Err: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import theme from '../theme.jsx';
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
LockOpen as LockOpenIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const AuthenticationData = (props) => {
|
||||
const {
|
||||
@@ -54,7 +54,7 @@ const AuthenticationData = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to set app auth: " + responseJson.reason);
|
||||
toast("Failed to set app auth: " + responseJson.reason);
|
||||
} else {
|
||||
if (getAppAuthentication !== undefined) {
|
||||
getAppAuthentication()
|
||||
@@ -65,11 +65,11 @@ const AuthenticationData = (props) => {
|
||||
}
|
||||
|
||||
// Needs a refresh with the new authentication..
|
||||
//alert.success("Successfully saved new app auth")
|
||||
//toast("Successfully saved new app auth")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("New auth error: ", error.toString());
|
||||
});
|
||||
}
|
||||
@@ -146,7 +146,7 @@ const AuthenticationData = (props) => {
|
||||
selectedApp.authentication.parameters[key].name
|
||||
] = "false";
|
||||
} else {
|
||||
alert.info(
|
||||
toast(
|
||||
"Field " +
|
||||
selectedApp.authentication.parameters[key].name +
|
||||
" can't be empty"
|
||||
|
||||
+469
@@ -0,0 +1,469 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Divider,
|
||||
MenuItem,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Textfield,
|
||||
TextField,
|
||||
Typography,
|
||||
Select,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
LockOpen as LockOpenIcon,
|
||||
Close as CloseIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import PaperComponent from "../components/PaperComponent.jsx"
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
|
||||
const AuthenticationData = (props) => {
|
||||
const {
|
||||
globalUrl,
|
||||
selectedApp,
|
||||
getAppAuthentication,
|
||||
authenticationModalOpen,
|
||||
setAuthenticationModalOpen,
|
||||
|
||||
configureWorkflowModalOpen,
|
||||
workflow,
|
||||
setUpdate,
|
||||
selectedAction,
|
||||
setSelectedAction,
|
||||
isLoggedIn,
|
||||
authFieldsOnly,
|
||||
} = props
|
||||
|
||||
//const alert = useAlert()
|
||||
let navigate = useNavigate();
|
||||
const [submitSuccessful, setSubmitSuccessful] = useState(false)
|
||||
const [authenticationOption, setAuthenticationOptions] = React.useState({
|
||||
app: JSON.parse(JSON.stringify(selectedApp)),
|
||||
fields: {},
|
||||
label: "",
|
||||
usage: [
|
||||
{
|
||||
workflow_id: workflow === undefined ? "" : workflow.id,
|
||||
},
|
||||
],
|
||||
id: uuidv4(),
|
||||
active: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoggedIn === false && authFieldsOnly !== true) {
|
||||
navigate(`/login?view=${window.location.pathname}&message=Log in to authenticate this app`)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setNewAppAuth = (appAuthData) => {
|
||||
var headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
// Find org_id and authorization from queries and add to headers
|
||||
if (window.location.search !== "") {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const org_id = params.get("org_id")
|
||||
const authorization = params.get("authorization")
|
||||
if (org_id !== null && authorization !== null) {
|
||||
headers["Org-Id"] = org_id
|
||||
headers["Authorization"] = "Bearer " + authorization
|
||||
}
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/authentication", {
|
||||
method: "PUT",
|
||||
headers: headers,
|
||||
body: JSON.stringify(appAuthData),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting app auth :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason === undefined) {
|
||||
toast("Failed to set app auth. Are you logged in?")
|
||||
} else {
|
||||
toast("Failed to set app auth: " + responseJson.reason);
|
||||
}
|
||||
} else {
|
||||
setSubmitSuccessful(true)
|
||||
if (getAppAuthentication !== undefined) {
|
||||
getAppAuthentication(true, false);
|
||||
}
|
||||
|
||||
if (setAuthenticationModalOpen !== undefined) {
|
||||
setAuthenticationModalOpen(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
console.log("New auth error: ", error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
if (selectedApp.authentication === undefined || selectedApp.authentication.parameters === null ||
|
||||
selectedApp.authentication.parameters === undefined || selectedApp.authentication.parameters.length === 0) {
|
||||
|
||||
return (
|
||||
<DialogContent style={{ textAlign: "center", marginTop: 50 }}>
|
||||
<Typography variant="h4" id="draggable-dialog-title" style={{ cursor: "move", }}>
|
||||
{selectedApp.name} does not require authentication
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
authenticationOption.app.actions = [];
|
||||
|
||||
for (let paramkey in selectedApp.authentication.parameters) {
|
||||
if (
|
||||
authenticationOption.fields[
|
||||
selectedApp.authentication.parameters[paramkey].name
|
||||
] === undefined
|
||||
) {
|
||||
authenticationOption.fields[
|
||||
selectedApp.authentication.parameters[paramkey].name
|
||||
] = "";
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmitCheck = () => {
|
||||
if (authenticationOption.label.length === 0) {
|
||||
authenticationOption.label = `Auth for ${selectedApp.name}`;
|
||||
}
|
||||
|
||||
// Automatically mapping fields that already exist (predefined).
|
||||
// Warning if fields are NOT filled
|
||||
for (let paramkey in selectedApp.authentication.parameters) {
|
||||
if (
|
||||
authenticationOption.fields[
|
||||
selectedApp.authentication.parameters[paramkey].name
|
||||
].length === 0
|
||||
) {
|
||||
if (
|
||||
selectedApp.authentication.parameters[paramkey].value !== undefined &&
|
||||
selectedApp.authentication.parameters[paramkey].value !== null &&
|
||||
selectedApp.authentication.parameters[paramkey].value.length > 0
|
||||
) {
|
||||
authenticationOption.fields[
|
||||
selectedApp.authentication.parameters[paramkey].name
|
||||
] = selectedApp.authentication.parameters[paramkey].value;
|
||||
} else {
|
||||
if (
|
||||
selectedApp.authentication.parameters[paramkey].schema.type === "bool"
|
||||
) {
|
||||
authenticationOption.fields[
|
||||
selectedApp.authentication.parameters[paramkey].name
|
||||
] = "false";
|
||||
} else {
|
||||
toast(
|
||||
"Field " +
|
||||
selectedApp.authentication.parameters[paramkey].name +
|
||||
" can't be empty"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Action: ", selectedAction);
|
||||
|
||||
if (selectedAction !== undefined) {
|
||||
selectedAction.authentication_id = authenticationOption.id;
|
||||
selectedAction.selectedAuthentication = authenticationOption;
|
||||
if (
|
||||
selectedAction.authentication === undefined ||
|
||||
selectedAction.authentication === null
|
||||
) {
|
||||
selectedAction.authentication = [authenticationOption];
|
||||
} else {
|
||||
selectedAction.authentication.push(authenticationOption);
|
||||
}
|
||||
|
||||
setSelectedAction(selectedAction);
|
||||
}
|
||||
|
||||
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption));
|
||||
var newFields = [];
|
||||
console.log("Fields: ", newAuthOption.fields)
|
||||
for (let authkey in newAuthOption.fields) {
|
||||
const value = newAuthOption.fields[authkey];
|
||||
newFields.push({
|
||||
"key": authkey,
|
||||
"value": value,
|
||||
});
|
||||
}
|
||||
|
||||
newAuthOption.fields = newFields;
|
||||
setNewAppAuth(newAuthOption);
|
||||
|
||||
if (configureWorkflowModalOpen === true) {
|
||||
setSelectedAction({});
|
||||
}
|
||||
|
||||
if (setUpdate !== undefined) {
|
||||
setUpdate(authenticationOption.id);
|
||||
}
|
||||
};
|
||||
|
||||
if (authenticationOption.label === null || authenticationOption.label === undefined) {
|
||||
authenticationOption.label = selectedApp.name + " authentication";
|
||||
}
|
||||
|
||||
const authenticationParameters = selectedApp.authentication.parameters.map((data, index) => {
|
||||
return (
|
||||
<div key={index} style={{ marginTop: 10 }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<LockOpenIcon style={{ marginRight: 10, }} />
|
||||
<Typography variant="body1" style={{}}>
|
||||
{data.name.replace("_basic", "", -1).replace("_", " ", -1)}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
{data.schema !== undefined &&
|
||||
data.schema !== null &&
|
||||
data.schema.type === "bool" ? (
|
||||
<Select
|
||||
MenuProps={{
|
||||
disableScrollLock: true,
|
||||
}}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
}}
|
||||
defaultValue={"false"}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
console.log("Value: ", e.target.value);
|
||||
authenticationOption.fields[data.name] = e.target.value;
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
height: 50,
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
key={"false"}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
}}
|
||||
value={"false"}
|
||||
>
|
||||
false
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
key={"true"}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
}}
|
||||
value={"true"}
|
||||
>
|
||||
true
|
||||
</MenuItem>
|
||||
</Select>
|
||||
) : (
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
disableUnderline: true,
|
||||
}}
|
||||
fullWidth
|
||||
type={
|
||||
data.example !== undefined && data.example.includes("***")
|
||||
? "password"
|
||||
: "text"
|
||||
}
|
||||
color="primary"
|
||||
defaultValue={
|
||||
data.value !== undefined && data.value !== null
|
||||
? data.value
|
||||
: ""
|
||||
}
|
||||
placeholder={data.example}
|
||||
onChange={(event) => {
|
||||
authenticationOption.fields[data.name] =
|
||||
event.target.value;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
||||
const authenticationButtons = <span>
|
||||
<Button
|
||||
style={{ borderRadius: theme.palette.borderRadius, marginTop: authFieldsOnly ? 20 : 0 }}
|
||||
onClick={() => {
|
||||
setAuthenticationOptions(authenticationOption);
|
||||
handleSubmitCheck();
|
||||
}}
|
||||
variant={"contained"}
|
||||
disabled={submitSuccessful}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
{authFieldsOnly === true ? null :
|
||||
<Button
|
||||
style={{ borderRadius: 0 }}
|
||||
onClick={() => {
|
||||
setAuthenticationModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
}
|
||||
</span>
|
||||
|
||||
// Check if only the auth items should show
|
||||
if (authFieldsOnly === true) {
|
||||
return (
|
||||
<div>
|
||||
{submitSuccessful === true ?
|
||||
<Typography variant="h6" style={{ marginTop: 10 }}>
|
||||
App succesfully configured! You may close this window.
|
||||
</Typography>
|
||||
:
|
||||
<span>
|
||||
{authenticationParameters}
|
||||
{authenticationButtons}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
PaperComponent={PaperComponent}
|
||||
aria-labelledby="draggable-dialog-title"
|
||||
hideBackdrop={true}
|
||||
disableEnforceFocus={true}
|
||||
disableBackdropClick={true}
|
||||
style={{ pointerEvents: "none" }}
|
||||
open={authenticationModalOpen}
|
||||
onClose={() => {
|
||||
//if (configureWorkflowModalOpen) {
|
||||
// setSelectedAction({});
|
||||
//}
|
||||
setAuthenticationModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
pointerEvents: "auto",
|
||||
color: "white",
|
||||
minWidth: 600,
|
||||
minHeight: 600,
|
||||
maxHeight: 600,
|
||||
padding: 15,
|
||||
overflow: "hidden",
|
||||
zIndex: 10012,
|
||||
border: theme.palette.defaultBorder,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
zIndex: 5000,
|
||||
position: "absolute",
|
||||
top: 14,
|
||||
right: 18,
|
||||
color: "grey",
|
||||
}}
|
||||
onClick={() => {
|
||||
setAuthenticationModalOpen(false);
|
||||
if (configureWorkflowModalOpen === true) {
|
||||
setSelectedAction({});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogTitle id="draggable-dialog-title" style={{ cursor: "move", }}>
|
||||
<div style={{ color: "white" }}>
|
||||
Authentication for {selectedApp.name}
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://shuffler.io/docs/apps#authentication"
|
||||
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
>
|
||||
What is app authentication?
|
||||
</a>
|
||||
<div />
|
||||
These are required fields for authenticating with {selectedApp.name}
|
||||
<div style={{ marginTop: 15 }} />
|
||||
<b>Name - what is this used for?</b>
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
color="primary"
|
||||
placeholder={"Auth july 2020"}
|
||||
defaultValue={`Auth for ${selectedApp.name}`}
|
||||
onChange={(event) => {
|
||||
authenticationOption.label = event.target.value;
|
||||
}}
|
||||
/>
|
||||
<Divider
|
||||
style={{
|
||||
marginTop: 15,
|
||||
marginBottom: 15,
|
||||
backgroundColor: "rgb(91, 96, 100)",
|
||||
}}
|
||||
/>
|
||||
<div />
|
||||
{authenticationParameters}
|
||||
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{authenticationButtons}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthenticationData;
|
||||
+1011
-317
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,280 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
TextField,
|
||||
IconButton,
|
||||
Button,
|
||||
Typography,
|
||||
Grid,
|
||||
Paper,
|
||||
Chip,
|
||||
Checkbox,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
BarChart,
|
||||
RadialBarChart,
|
||||
RadialAreaChart,
|
||||
RadialAxis,
|
||||
StackedBarSeries,
|
||||
TooltipArea,
|
||||
ChartTooltip,
|
||||
TooltipTemplate,
|
||||
RadialAreaSeries,
|
||||
RadialPointSeries,
|
||||
RadialArea,
|
||||
RadialLine,
|
||||
TreeMap,
|
||||
TreeMapSeries,
|
||||
TreeMapLabel,
|
||||
TreeMapRect,
|
||||
Line,
|
||||
LineChart,
|
||||
LineSeries,
|
||||
LinearYAxis,
|
||||
LinearXAxis,
|
||||
LinearYAxisTickSeries,
|
||||
LinearXAxisTickSeries,
|
||||
Area,
|
||||
AreaChart,
|
||||
AreaSeries,
|
||||
AreaSparklineChart,
|
||||
PointSeries,
|
||||
GridlineSeries,
|
||||
Gridline,
|
||||
Stripes,
|
||||
Gradient,
|
||||
GradientStop,
|
||||
LinearXAxisTickLabel,
|
||||
} from 'reaviz';
|
||||
|
||||
const LineChartWrapper = ({keys, inputname, height, width}) => {
|
||||
const [hovered, setHovered] = useState("");
|
||||
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, }}>
|
||||
<Typography variant="h6" style={{marginBotton: 15, }}>
|
||||
{inputname}
|
||||
</Typography>
|
||||
<BarChart
|
||||
width={"100%"}
|
||||
height={height}
|
||||
data={inputdata}
|
||||
gridlines={
|
||||
<GridlineSeries line={<Gridline direction="all" />} />
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const AppStats = (defaultprops) => {
|
||||
const { globalUrl, selectedOrganization, userdata, } = defaultprops;
|
||||
const [keys, setKeys] = useState([])
|
||||
const [searches, setSearches] = useState([]);
|
||||
const [clickData, setClickData] = useState(undefined);
|
||||
const [conversionData, setConversionData] = useState(undefined);
|
||||
const [statistics, setStatistics] = useState(undefined);
|
||||
const [appRuns, setAppruns] = useState(undefined);
|
||||
const [workflowRuns, setWorkflowRuns] = useState(undefined);
|
||||
const [subflowRuns, setSubflowRuns] = useState(undefined);
|
||||
|
||||
const handleDataSetting = (inputdata, grouping) => {
|
||||
if (inputdata === undefined || inputdata === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const dailyStats = inputdata.daily_statistics
|
||||
if (dailyStats === undefined || dailyStats === null) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Looking at daily data: ", inputdata)
|
||||
|
||||
var appRuns = {
|
||||
"key": "App Runs",
|
||||
"data": []
|
||||
}
|
||||
|
||||
var workflowRuns = {
|
||||
"key": "Workflow Runs (includes subflows)",
|
||||
"data": []
|
||||
}
|
||||
|
||||
var subflowRuns = {
|
||||
"key": "Subflow Runs",
|
||||
"data": []
|
||||
}
|
||||
|
||||
for (let key in dailyStats) {
|
||||
// Always skips first one as it has accumulated data in it
|
||||
if (key === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const item = dailyStats[key]
|
||||
|
||||
if (item["date"] === undefined) {
|
||||
console.log("No date: ", item)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if app_executions key in item
|
||||
if (item["app_executions"] !== undefined && item["app_executions"] !== null) {
|
||||
appRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["app_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
// Check if workflow_executions key in item
|
||||
if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) {
|
||||
workflowRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["workflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (item["subflow_executions"] !== undefined && item["subflow_executions"] !== null) {
|
||||
subflowRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["subflow_executions"]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Adds data for today
|
||||
console.log("Inputdata: ", inputdata)
|
||||
if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
|
||||
appRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_app_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
|
||||
workflowRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_workflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (inputdata["daily_subflow_executions"] !== undefined && inputdata["daily_subflow_executions"] !== null) {
|
||||
subflowRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_subflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
setSubflowRuns(subflowRuns)
|
||||
setWorkflowRuns(workflowRuns)
|
||||
setAppruns(appRuns)
|
||||
}
|
||||
|
||||
const getStats = () => {
|
||||
fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflows :O!: ", response.status);
|
||||
return;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
return
|
||||
}
|
||||
|
||||
setStatistics(responseJson)
|
||||
handleDataSetting(responseJson, "day")
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error: ", error)
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getStats()
|
||||
}, [])
|
||||
|
||||
const paperStyle = {
|
||||
textAlign: "center",
|
||||
padding: 40,
|
||||
margin: 5,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
maxWidth: 300,
|
||||
}
|
||||
|
||||
const data = (
|
||||
<div className="content" style={{width: "100%", margin: "auto", }}>
|
||||
<Typography variant="body1" style={{margin: "auto", marginLeft: 10, marginBottom: 20, }}>
|
||||
All Stat widgets are monthly and gathered from <a
|
||||
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
|
||||
target="_blank"
|
||||
style={{ textDecoration: "none", color: "#f85a3e",}}
|
||||
>Your Organization Statistics. </a>
|
||||
This is a feature to help give you more insight into Shuffle, and will be populating over time.
|
||||
</Typography>
|
||||
{statistics !== undefined ?
|
||||
<div style={{display: "flex", textAlign: "center",}}>
|
||||
<Paper style={paperStyle}>
|
||||
<Typography variant="h4">
|
||||
{statistics.monthly_workflow_executions}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
Workflow Runs
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper style={paperStyle}>
|
||||
<Typography variant="h4">
|
||||
{statistics.monthly_app_executions}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
App Runs
|
||||
</Typography>
|
||||
</Paper>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
{appRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={appRuns} height={300} width={"100%"} inputname={"Daily App Runs"}/>
|
||||
}
|
||||
|
||||
{workflowRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={workflowRuns} height={300} width={"100%"} inputname={"Daily Workflow Runs (including subflows)"}/>
|
||||
}
|
||||
|
||||
{subflowRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={subflowRuns} height={300} width={"100%"} inputname={"Subflow Runs"}/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
const dataWrapper = (
|
||||
<div style={{ maxWidth: 1366, margin: "auto" }}>{data}</div>
|
||||
);
|
||||
|
||||
return dataWrapper;
|
||||
}
|
||||
|
||||
export default AppStats;
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ReactGA from 'react-ga4';
|
||||
import theme from "../theme.jsx";
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import {
|
||||
Paper,
|
||||
Typography,
|
||||
@@ -10,27 +10,92 @@ import {
|
||||
Button,
|
||||
Grid,
|
||||
Card,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
const Branding = (props) => {
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props;
|
||||
const alert = useAlert();
|
||||
const [publishingInfo, setPublishingInfo] = useState("");
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props;
|
||||
//const alert = useAlert();
|
||||
const [publishingInfo, setPublishingInfo] = useState("");
|
||||
const [publishRequirements, setPublishRequirements] = useState([])
|
||||
|
||||
// Should enable / disable org branding
|
||||
const handleChangePublishing = () => {
|
||||
console.log("Handle change publishing");
|
||||
}
|
||||
|
||||
const handleEditOrg = (joinStatus) => {
|
||||
const data = {
|
||||
"org_id": selectedOrganization.id,
|
||||
"creator_config": joinStatus,
|
||||
};
|
||||
|
||||
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
|
||||
fetch(url, {
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
toast("Failed updating org: ", responseJson.reason);
|
||||
} else {
|
||||
if (joinStatus == "join") {
|
||||
setPublishingInfo("Your organization is now part of the Creator Incentive Program. You can now create and publish content to your organization's page. You can also create a creator account to manage your organization's content.")
|
||||
} else {
|
||||
setPublishingInfo("Your organization is no longer part of the Creator Incentive Program. You can still create a creator account to manage your organization's content.")
|
||||
}
|
||||
handleGetOrg(selectedOrganization.id);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
toast("Err: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
// Should enable / disable org branding
|
||||
const handleChangePublishing = () => {
|
||||
console.log("Handle change publishing");
|
||||
|
||||
if (selectedOrganization.creator_id == "") {
|
||||
handleEditOrg("join")
|
||||
} else {
|
||||
handleEditOrg("leave")
|
||||
}
|
||||
}
|
||||
|
||||
const isOrganizationReady = () => {
|
||||
console.log("Is organization ready?")
|
||||
|
||||
// A simple checklist to ensure the button shows up properly
|
||||
if (selectedOrganization.name === selectedOrganization.org) {
|
||||
const comment = "Change the name of your organization"
|
||||
if (!publishRequirements.includes(comment)) {
|
||||
setPublishRequirements([...publishRequirements, comment])
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it's a suborg
|
||||
if (selectedOrganization.creator_org !== "") {
|
||||
const comment = "Child orgs can't become creators"
|
||||
if (!publishRequirements.includes(comment)) {
|
||||
setPublishRequirements([...publishRequirements, comment])
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedOrganization.large_image === "" || selectedOrganization.large_image === theme.palette.defaultImage) {
|
||||
const comment = "Add a logo for your organization"
|
||||
if (!publishRequirements.includes(comment)) {
|
||||
setPublishRequirements([...publishRequirements, comment])
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,40 +104,58 @@ const Branding = (props) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography variant="h6" style={{ marginTop: 20, marginBottom: 10 }}>
|
||||
Branding
|
||||
</Typography>
|
||||
<h2>
|
||||
Branding
|
||||
</h2>
|
||||
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
|
||||
You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more.
|
||||
</Typography>
|
||||
|
||||
<Divider style={{marginTop: 50, marginBottom: 50, }} />
|
||||
<h2>
|
||||
Creator Network
|
||||
Creator Incentive Program
|
||||
</h2>
|
||||
<div style={{ display: "flex", width: 700, }}>
|
||||
<div style={{ display: "flex", width: 900, }}>
|
||||
<div>
|
||||
<span>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
By changing publishing settings, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your organization's non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. Support: support@shuffler.io
|
||||
|
||||
By changing publishing settings, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your organization's non-sensitive data will be added as a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. None of your existing workflows, apps, or other stored data will be published. Any admin in your organization can manage the creator configuration. Becoming a creator organization is reversible.<div/>Support: <a href="mailto:support@shuffler.io"target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>support@shuffler.io</a>
|
||||
</Typography>
|
||||
{selectedOrganization.creator_id == "" ?
|
||||
<Typography variant="h6" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "grey", }}>
|
||||
|
||||
</Typography>
|
||||
:
|
||||
<Typography variant="h6" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "grey", }}>
|
||||
|
||||
<a href={`/creators/${selectedOrganization.creator_id}`} target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Modify your creator organization</a>
|
||||
</Typography>
|
||||
}
|
||||
|
||||
<Button
|
||||
style={{ height: 40, marginTop: 10, width: 300, }}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
disabled={() => {
|
||||
return isOrganizationReady()
|
||||
}}
|
||||
variant={selectedOrganization.creator_id == "" ? "contained" : "outlined"}
|
||||
color={selectedOrganization.creator_id == "" ? "primary" : "secondary"}
|
||||
disabled={!isOrganizationReady()}
|
||||
onClick={() => {
|
||||
handleChangePublishing();
|
||||
}}
|
||||
>
|
||||
Join Creator Network
|
||||
{selectedOrganization.creator_id == "" ? "Join" : "Leave"} Creators
|
||||
|
||||
</Button>
|
||||
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
|
||||
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "white", }}>
|
||||
{publishingInfo}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "grey", }}>
|
||||
{publishRequirements.map((item) => {
|
||||
return (
|
||||
<div>
|
||||
Required: {item}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Typography>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import theme from "../theme.jsx";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
Divider,
|
||||
@@ -15,8 +17,7 @@ import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
} from "@material-ui/core";
|
||||
import { useAlert } from "react-alert";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Edit as EditIcon,
|
||||
@@ -40,8 +41,7 @@ import {
|
||||
Business as BusinessIcon,
|
||||
Visibility as VisibilityIcon,
|
||||
VisibilityOff as VisibilityOffIcon,
|
||||
} from "@material-ui/icons";
|
||||
import data from "./frameworkStyle.jsx";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const scrollStyle1 = {
|
||||
height: 100,
|
||||
@@ -73,7 +73,7 @@ const CacheView = (props) => {
|
||||
const [dataValue, setDataValue] = React.useState({});
|
||||
const [editCache, setEditCache] = React.useState(false);
|
||||
const [show, setShow] = useState({});
|
||||
const alert = useAlert();
|
||||
|
||||
useEffect(() => {
|
||||
listOrgCache(orgId);
|
||||
console.log("orgid", orgId);
|
||||
@@ -106,7 +106,7 @@ const CacheView = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -149,7 +149,7 @@ const CacheView = (props) => {
|
||||
|
||||
|
||||
const deleteCache = (orgId, key) => {
|
||||
alert.info("Attempting to delete Cache");
|
||||
toast("Attempting to delete Cache");
|
||||
fetch(globalUrl + `/api/v1/orgs/${orgId}/cache/${key}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
@@ -159,16 +159,16 @@ const CacheView = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Successfully deleted Cache");
|
||||
toast("Successfully deleted Cache");
|
||||
setTimeout(() => {
|
||||
listOrgCache(orgId);
|
||||
}, 1000);
|
||||
} else {
|
||||
alert.error("Failed deleting Cache. Does it still exist?");
|
||||
toast("Failed deleting Cache. Does it still exist?");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -198,12 +198,12 @@ const CacheView = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setAddCache(responseJson);
|
||||
alert.success("Cache Edited Successfully!");
|
||||
toast("Cache Edited Successfully!");
|
||||
listOrgCache(orgId);
|
||||
setModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -232,12 +232,12 @@ const CacheView = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setAddCache(responseJson);
|
||||
alert.success("New Cache Added Successfully!");
|
||||
toast("New Cache Added Successfully!");
|
||||
listOrgCache(orgId);
|
||||
setModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,16 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import {Link} from 'react-router-dom';
|
||||
import theme from '../theme.jsx';
|
||||
import { removeQuery } from '../components/ScrollToTop.jsx';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons';
|
||||
import {
|
||||
SkipNext as SkipNextIcon,
|
||||
SkipPrevious as SkipPreviousIcon,
|
||||
PlayArrow as PlayArrowIcon,
|
||||
VerifiedUser as VerifiedUserIcon,
|
||||
Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material';
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import { InstantSearch, Configure, connectSearchBox, connectHits } from 'react-instantsearch-dom';
|
||||
@@ -24,27 +30,18 @@ import {
|
||||
Zoom,
|
||||
CardMedia,
|
||||
CardActionArea,
|
||||
} from '@material-ui/core';
|
||||
} from '@mui/material';
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
AvatarGroup,
|
||||
} from "@mui/material"
|
||||
|
||||
import {
|
||||
SkipNext as SkipNextIcon,
|
||||
SkipPrevious as SkipPreviousIcon,
|
||||
PlayArrow as PlayArrowIcon,
|
||||
VerifiedUser as VerifiedUserIcon,
|
||||
} from "@material-ui/icons";
|
||||
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const CreatorGrid = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs } = props
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : parsedXs
|
||||
const theme = useTheme();
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
//const [filteredApps, setFilteredApps] = React.useState([]);
|
||||
const [formMail, setFormMail] = React.useState("");
|
||||
@@ -85,7 +82,7 @@ const CreatorGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
@@ -101,20 +98,21 @@ const CreatorGrid = props => {
|
||||
|
||||
// value={currentRefinement}
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
useEffect(() => {
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
refine(foundQuery)
|
||||
}
|
||||
var defaultSearch = ""
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
refine(foundQuery)
|
||||
defaultSearch = foundQuery
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
return (
|
||||
<form noValidate action="" role="search">
|
||||
<TextField
|
||||
defaultValue={defaultSearch}
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||
InputProps={{
|
||||
@@ -132,10 +130,10 @@ const CreatorGrid = props => {
|
||||
autoComplete='off'
|
||||
type="search"
|
||||
color="primary"
|
||||
value={currentRefinement}
|
||||
placeholder="Find Creators..."
|
||||
id="shuffle_search_field"
|
||||
onChange={(event) => {
|
||||
removeQuery("q")
|
||||
refine(event.currentTarget.value)
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import {Link} from 'react-router-dom';
|
||||
import { removeQuery } from '../components/ScrollToTop.jsx';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons';
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon, Close as CloseIcon, Folder as FolderIcon, LibraryBooks as LibraryBooksIcon } from '@mui/icons-material';
|
||||
import aa from 'search-insights'
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
@@ -24,16 +25,15 @@ import {
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
} from '@material-ui/core';
|
||||
} from '@mui/material';
|
||||
|
||||
import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons'
|
||||
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const DocsGrid = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, userdata, } = props
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 2 : parsedXs
|
||||
const theme = useTheme();
|
||||
//const [apps, setApps] = React.useState([]);
|
||||
//const [filteredApps, setFilteredApps] = React.useState([]);
|
||||
const [formMail, setFormMail] = React.useState("");
|
||||
@@ -70,7 +70,7 @@ const DocsGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
@@ -85,21 +85,22 @@ const DocsGrid = props => {
|
||||
}
|
||||
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
useEffect(() => {
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
console.log("Got query: ", foundQuery)
|
||||
refine(foundQuery)
|
||||
}
|
||||
var defaultSearch = ""
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
console.log("Got query: ", foundQuery)
|
||||
refine(foundQuery)
|
||||
defaultSearch = foundQuery
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
return (
|
||||
<form noValidate action="" role="search">
|
||||
<TextField
|
||||
defaultValue={defaultSearch}
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||
InputProps={{
|
||||
@@ -117,10 +118,10 @@ const DocsGrid = props => {
|
||||
autoComplete='off'
|
||||
type="search"
|
||||
color="primary"
|
||||
defaultValue={currentRefinement}
|
||||
placeholder="Search our Documentation..."
|
||||
id="shuffle_search_field"
|
||||
onChange={(event) => {
|
||||
removeQuery("q")
|
||||
refine(event.currentTarget.value)
|
||||
}}
|
||||
limit={5}
|
||||
@@ -182,7 +183,7 @@ const DocsGrid = props => {
|
||||
|
||||
//const secondaryText = data.data !== undefined ? data.data.slice(0, 100)+"..." : ""
|
||||
const secondaryText = data.data !== undefined ? data.data.slice(0, 100)+"..." : ""
|
||||
const baseImage = <PolymerIcon />
|
||||
const baseImage = <CodeIcon/>
|
||||
const avatar = data.image_url === undefined ?
|
||||
baseImage
|
||||
:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useEffect } from "react";
|
||||
import BackupIcon from "@material-ui/icons/Backup";
|
||||
import {
|
||||
Backup as BackupIcon
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const dragOverStyle = {
|
||||
backgroundColor: "rgba(0,0,0,0.8)",
|
||||
@@ -54,7 +56,11 @@ const Dropzone = ({ children, style, onDrop }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!dropzoneRef.current) return;
|
||||
if (dropzoneRef === null || dropzoneRef === undefined || dropzoneRef.current === null || dropzoneRef.current === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if event listene exists for dropzoneRef.current
|
||||
|
||||
dropzoneRef.current.addEventListener("dragover", handleDragOver);
|
||||
dropzoneRef.current.addEventListener("dragenter", handleDragEnter);
|
||||
@@ -62,6 +68,10 @@ const Dropzone = ({ children, style, onDrop }) => {
|
||||
dropzoneRef.current.addEventListener("drop", handleDrop);
|
||||
|
||||
return () => {
|
||||
if (dropzoneRef.current === null || dropzoneRef.current === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
dropzoneRef.current.removeEventListener("dragover", handleDragOver);
|
||||
dropzoneRef.current.removeEventListener("dragenter", handleDragEnter);
|
||||
dropzoneRef.current.removeEventListener("dragleave", handleDragLeave);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React, { useEffect, useContext } from "react";
|
||||
import theme from '../theme.jsx';
|
||||
import { isMobile } from "react-device-detect"
|
||||
import ChipInput from "material-ui-chip-input";
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
import UsecaseSearch from "../components/UsecaseSearch.jsx"
|
||||
import WorkflowGrid from "../components/WorkflowGrid.jsx"
|
||||
import dayjs from 'dayjs';
|
||||
import WorkflowTemplatePopup from "./WorkflowTemplatePopup.jsx";
|
||||
|
||||
import {
|
||||
Badge,
|
||||
@@ -25,6 +28,7 @@ import {
|
||||
Typography,
|
||||
Zoom,
|
||||
CircularProgress,
|
||||
Drawer,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
@@ -37,28 +41,38 @@ import {
|
||||
FormControl,
|
||||
FormLabel,
|
||||
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
DatePicker,
|
||||
LocalizationProvider,
|
||||
} from '@mui/x-date-pickers'
|
||||
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
|
||||
|
||||
import {
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Publish as PublishIcon,
|
||||
OpenInNew as OpenInNewIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const EditWorkflow = (props) => {
|
||||
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props
|
||||
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, } = props
|
||||
|
||||
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
||||
|
||||
const [submitLoading, setSubmitLoading] = React.useState(false);
|
||||
const [showMoreClicked, setShowMoreClicked] = React.useState(false);
|
||||
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
|
||||
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
||||
const [innerWorkflow, setInnerWorkflow] = React.useState(workflow)
|
||||
|
||||
const [newWorkflowTags, setNewWorkflowTags] = React.useState(workflow.tags !== undefined && workflow.tags !== null ? JSON.parse(JSON.stringify(workflow.tags)) : [])
|
||||
const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "")
|
||||
|
||||
const [selectedUsecases, setSelectedUsecases] = React.useState(workflow.usecase_ids !== undefined && workflow.usecase_ids !== null ? JSON.parse(JSON.stringify(workflow.usecase_ids)) : []);
|
||||
const [foundWorkflowId, setFoundWorkflowId] = React.useState("")
|
||||
const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "")
|
||||
const [description, setDescription] = React.useState(workflow.description !== undefined ? workflow.description : "")
|
||||
|
||||
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'))
|
||||
|
||||
// Gets the generated workflow
|
||||
const getGeneratedWorkflow = (workflow_id) => {
|
||||
@@ -114,7 +128,7 @@ const EditWorkflow = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("Get workflow error: ", error.toString());
|
||||
})
|
||||
}
|
||||
@@ -130,73 +144,76 @@ const EditWorkflow = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const newWorkflow = isEditing === true ? false : true
|
||||
const newWorkflow = isEditing === true ? false : true
|
||||
const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
console.log("PRIO: ", priority)
|
||||
|
||||
var upload = "";
|
||||
var total_count = 0
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
<Drawer
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: isMobile ? "90%" : 550,
|
||||
maxWidth: isMobile ? "90%" : 550,
|
||||
minHeight: 400,
|
||||
minWidth: isMobile ? "90%" : 650,
|
||||
maxWidth: isMobile ? "90%" : 650,
|
||||
minHeight: 400,
|
||||
paddingTop: 25,
|
||||
paddingLeft: 50,
|
||||
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{padding: 30, paddingBottom: 0, zIndex: 1000,}}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 1, color: "rgba(255,255,255,0.9)" }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<Typography variant="h6" style={{flex: 9, }}>
|
||||
{newWorkflow ? "New" : "Editing"} workflow
|
||||
</Typography>
|
||||
{newWorkflow === true ? null :
|
||||
<div style={{ marginLeft: 5, flex: 1 }}>
|
||||
<Tooltip title="Open Workflow Form for 'normal' users">
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
href={`/workflows/${workflow.id}/run`}
|
||||
target="_blank"
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
color: "#f85a3e",
|
||||
marginLeft: 5,
|
||||
marginTop: 10,
|
||||
}}
|
||||
>
|
||||
<OpenInNewIcon />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
}
|
||||
<div style={{display: "flex"}}>
|
||||
<Typography variant="h4" style={{flex: 9, }}>
|
||||
{newWorkflow ? "New" : "Editing"} workflow
|
||||
</Typography>
|
||||
{newWorkflow === true ? null :
|
||||
<div style={{ marginLeft: 5, flex: 1 }}>
|
||||
<Tooltip title="Open Workflow Form for 'normal' users">
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
href={`/workflows/${workflow.id}/run`}
|
||||
target="_blank"
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
color: "#f85a3e",
|
||||
marginLeft: 5,
|
||||
marginTop: 10,
|
||||
}}
|
||||
>
|
||||
<OpenInNewIcon />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}>
|
||||
Workflows can be built from scratch, or from templates. <a href="/usecases" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
|
||||
</Typography>
|
||||
{showUpload === true ?
|
||||
<div style={{ float: "right" }}>
|
||||
<Tooltip color="primary" title={"Import manually"} placement="top">
|
||||
<Button
|
||||
color="primary"
|
||||
style={{}}
|
||||
variant="text"
|
||||
onClick={() => upload.click()}
|
||||
>
|
||||
<PublishIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
: null}
|
||||
}
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 20, maxWidth: 440,}}>
|
||||
Workflows can be built from scratch, or from templates. <a href="/usecases" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
|
||||
</Typography>
|
||||
{showUpload === true ?
|
||||
<div style={{ float: "right" }}>
|
||||
<Tooltip color="primary" title={"Import manually"} placement="top">
|
||||
<Button
|
||||
color="primary"
|
||||
style={{}}
|
||||
variant="text"
|
||||
onClick={() => upload.click()}
|
||||
>
|
||||
<PublishIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
: null}
|
||||
</div>
|
||||
{/*newWorkflow === true ?
|
||||
<div style={{flex: 1, marginLeft: 45, }}>
|
||||
@@ -211,12 +228,12 @@ const EditWorkflow = (props) => {
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<FormControl>
|
||||
<DialogContent style={{paddingTop: 10, display: "flex", minHeight: 350, zIndex: 1001, }}>
|
||||
<div style={{minWidth: newWorkflow ? 450 : 500, maxWidth: newWorkflow ? 450 : 500, }}>
|
||||
<DialogContent style={{paddingTop: 10, display: "flex", minHeight: 300, zIndex: 1001, }}>
|
||||
<div style={{minWidth: newWorkflow ? 500 : 550, maxWidth: newWorkflow ? 450 : 500, }}>
|
||||
<TextField
|
||||
onBlur={(event) => {
|
||||
setName(event.target.value)
|
||||
}}
|
||||
onChange={(event) => {
|
||||
setName(event.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
@@ -231,27 +248,29 @@ const EditWorkflow = (props) => {
|
||||
autoFocus
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
onBlur={(event) => {
|
||||
setDescription(event.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
maxRows={4}
|
||||
color="primary"
|
||||
defaultValue={innerWorkflow.description}
|
||||
placeholder="Description"
|
||||
multiline
|
||||
label="Description"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
<div style={{display: "flex", }}>
|
||||
<TextField
|
||||
onBlur={(event) => {
|
||||
setDescription(event.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
maxRows={4}
|
||||
color="primary"
|
||||
defaultValue={innerWorkflow.description}
|
||||
placeholder="Description"
|
||||
multiline
|
||||
label="Description"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<div style={{display: "flex", marginTop: 10, }}>
|
||||
<ChipInput
|
||||
style={{ flex: 1, maxHeight: 40, marginTop: 12, overflow: "auto", }}
|
||||
<MuiChipsInput
|
||||
style={{ flex: 1, maxHeight: 40, }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
@@ -261,13 +280,20 @@ const EditWorkflow = (props) => {
|
||||
color="primary"
|
||||
fullWidth
|
||||
value={newWorkflowTags}
|
||||
onChange={(chip) => {
|
||||
console.log("Chip: ", chip)
|
||||
//newWorkflowTags.push(chip);
|
||||
setNewWorkflowTags(chip);
|
||||
}}
|
||||
onAdd={(chip) => {
|
||||
newWorkflowTags.push(chip);
|
||||
setNewWorkflowTags(newWorkflowTags);
|
||||
}}
|
||||
onDelete={(chip, index) => {
|
||||
console.log("Deleting: ", chip, index)
|
||||
newWorkflowTags.splice(index, 1);
|
||||
setNewWorkflowTags(newWorkflowTags);
|
||||
setUpdate(Math.random());
|
||||
}}
|
||||
/>
|
||||
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
@@ -328,26 +354,41 @@ const EditWorkflow = (props) => {
|
||||
|
||||
{showMoreClicked === true ?
|
||||
<span style={{marginTop: 25, }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<FormControl style={{marginTop: 15, }}>
|
||||
<FormLabel id="demo-row-radio-buttons-group-label">Status</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
aria-labelledby="demo-row-radio-buttons-group-label"
|
||||
name="row-radio-buttons-group"
|
||||
defaultValue={innerWorkflow.status}
|
||||
onChange={(e) => {
|
||||
console.log("Data: ", e.target.value)
|
||||
|
||||
innerWorkflow.workflow_type = e.target.value
|
||||
setInnerWorkflow(innerWorkflow)
|
||||
}}
|
||||
>
|
||||
<FormControlLabel value="test" control={<Radio />} label="Test" />
|
||||
<FormControlLabel value="production" control={<Radio />} label="Production" />
|
||||
|
||||
<FormControl style={{marginTop: 15, }}>
|
||||
<FormLabel id="demo-row-radio-buttons-group-label">Status</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
aria-labelledby="demo-row-radio-buttons-group-label"
|
||||
name="row-radio-buttons-group"
|
||||
defaultValue={innerWorkflow.status}
|
||||
onChange={(e) => {
|
||||
console.log("Data: ", e.target.value)
|
||||
|
||||
innerWorkflow.workflow_type = e.target.value
|
||||
setInnerWorkflow(innerWorkflow)
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<DatePicker
|
||||
sx={{
|
||||
marginTop: 3,
|
||||
marginLeft: 3,
|
||||
}}
|
||||
>
|
||||
<FormControlLabel value="test" control={<Radio />} label="Test" />
|
||||
<FormControlLabel value="production" control={<Radio />} label="Production" />
|
||||
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
value={dueDate}
|
||||
label="Due Date"
|
||||
format="YYYY-MM-DD"
|
||||
onChange={(newValue) => {
|
||||
setDueDate(newValue)
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</div>
|
||||
<div />
|
||||
|
||||
<FormControl style={{marginTop: 15, }}>
|
||||
@@ -430,38 +471,28 @@ const EditWorkflow = (props) => {
|
||||
</span>
|
||||
: null}
|
||||
<Tooltip color="primary" title={"Add more details"} placement="top">
|
||||
<IconButton
|
||||
style={{ color: "white", margin: "auto", marginTop: 10, textAlign: "center", width: 50,}}
|
||||
onClick={() => {
|
||||
setShowMoreClicked(!showMoreClicked);
|
||||
}}
|
||||
>
|
||||
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/*newWorkflow === true ?
|
||||
<div style={{marginLeft: 50, maxWidth: 400, minWidth: 400, position: "relative",}}>
|
||||
<UsecaseSearch
|
||||
globalUrl={globalUrl}
|
||||
appFramework={appFramework}
|
||||
defaultSearch={undefined}
|
||||
apps={undefined}
|
||||
setFoundWorkflowId={setFoundWorkflowId}
|
||||
userdata={userdata}
|
||||
/>
|
||||
</div>
|
||||
: null*/}
|
||||
<IconButton
|
||||
style={{ color: "white", margin: "auto", marginTop: 10, textAlign: "center", width: 50,}}
|
||||
onClick={() => {
|
||||
setShowMoreClicked(!showMoreClicked);
|
||||
}}
|
||||
>
|
||||
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
<DialogActions style={{paddingRight: 100, }}>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
if (setNewWorkflow !== undefined) {
|
||||
setWorkflow({})
|
||||
}
|
||||
if (setNewWorkflow !== undefined) {
|
||||
setWorkflow({})
|
||||
}
|
||||
|
||||
setModalOpen(false)
|
||||
setModalOpen(false)
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
@@ -470,46 +501,100 @@ const EditWorkflow = (props) => {
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{}}
|
||||
disabled={name.length === 0}
|
||||
disabled={name.length === 0 || submitLoading === true}
|
||||
onClick={() => {
|
||||
innerWorkflow.name = name
|
||||
innerWorkflow.description = description
|
||||
if (newWorkflowTags.length > 0) {
|
||||
innerWorkflow.tags = newWorkflowTags
|
||||
}
|
||||
setSubmitLoading(true)
|
||||
|
||||
if (selectedUsecases.length > 0) {
|
||||
innerWorkflow.usecase_ids = selectedUsecases
|
||||
}
|
||||
innerWorkflow.name = name
|
||||
innerWorkflow.description = description
|
||||
if (newWorkflowTags.length > 0) {
|
||||
innerWorkflow.tags = newWorkflowTags
|
||||
}
|
||||
|
||||
if (selectedUsecases.length > 0) {
|
||||
innerWorkflow.usecase_ids = selectedUsecases
|
||||
}
|
||||
|
||||
if (setNewWorkflow !== undefined) {
|
||||
setNewWorkflow(
|
||||
innerWorkflow.name,
|
||||
innerWorkflow.description,
|
||||
innerWorkflow.tags,
|
||||
innerWorkflow.default_return_value,
|
||||
innerWorkflow,
|
||||
newWorkflow,
|
||||
innerWorkflow.usecase_ids,
|
||||
innerWorkflow.blogpost,
|
||||
innerWorkflow.status,
|
||||
)
|
||||
setWorkflow({})
|
||||
} else {
|
||||
setWorkflow(innerWorkflow)
|
||||
console.log("editing workflow: ", innerWorkflow)
|
||||
}
|
||||
|
||||
setModalOpen(false)
|
||||
if (dueDate > 0) {
|
||||
innerWorkflow.due_date = new Date(`${dueDate["$y"]}-${dueDate["$M"]+1}-${dueDate["$D"]}`).getTime()/1000
|
||||
}
|
||||
|
||||
if (setNewWorkflow !== undefined) {
|
||||
setNewWorkflow(
|
||||
innerWorkflow.name,
|
||||
innerWorkflow.description,
|
||||
innerWorkflow.tags,
|
||||
innerWorkflow.default_return_value,
|
||||
innerWorkflow,
|
||||
newWorkflow,
|
||||
innerWorkflow.usecase_ids,
|
||||
innerWorkflow.blogpost,
|
||||
innerWorkflow.status,
|
||||
)
|
||||
setWorkflow({})
|
||||
} else {
|
||||
setWorkflow(innerWorkflow)
|
||||
console.log("editing workflow: ", innerWorkflow)
|
||||
}
|
||||
|
||||
setSubmitLoading(true)
|
||||
|
||||
// If new workflow, don't close it
|
||||
if (isEditing) {
|
||||
setModalOpen(false)
|
||||
}
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Submit"}
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Done"}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
{newWorkflow === true ?
|
||||
<span style={{marginTop: 30, }}>
|
||||
<Typography variant="h6" style={{marginLeft: 30, paddingBottom: 0, }}>
|
||||
Relevant Workflows
|
||||
</Typography>
|
||||
|
||||
{priority === null || priority === undefined ? null :
|
||||
<div style={{marginLeft: 30, }}>
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
globalUrl={globalUrl}
|
||||
|
||||
srcapp={priority.description.split("&").length > 2 ? priority.description.split("&")[0] : ""}
|
||||
img1={priority.description.split("&").length > 2 ? priority.description.split("&")[1] : ""}
|
||||
|
||||
dstapp={priority.description.split("&").length > 3 ? priority.description.split("&")[2] : ""}
|
||||
img2={priority.description.split("&").length > 3 ? priority.description.split("&")[3] : ""}
|
||||
title={priority.name}
|
||||
description={priority.description.split("&").length > 4 ? priority.description.split("&")[4] : ""}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
</span>
|
||||
: null}
|
||||
|
||||
{newWorkflow === true && name.length > 2 ?
|
||||
<div style={{marginLeft: 30, }}>
|
||||
<WorkflowGrid
|
||||
maxRows={1}
|
||||
globalUrl={globalUrl}
|
||||
showSuggestions={false}
|
||||
isMobile={isMobile}
|
||||
userdata={userdata}
|
||||
inputsearch={name+description+newWorkflowTags.join(" ")}
|
||||
|
||||
parsedXs={6}
|
||||
alternativeView={false}
|
||||
onlyResults={true}
|
||||
/>
|
||||
</div>
|
||||
: null}
|
||||
</FormControl>
|
||||
</Dialog>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import 'react-alice-carousel/lib/alice-carousel.css';
|
||||
import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
|
||||
import theme from '../theme.jsx';
|
||||
import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp';
|
||||
import { findSpecificApp } from "../components/AppFramework.jsx"
|
||||
import {
|
||||
Checkbox,
|
||||
Button,
|
||||
Collapse,
|
||||
IconButton,
|
||||
FormGroup,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
FormLabel,
|
||||
FormControlLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Grid,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField,
|
||||
Zoom,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
Divider,
|
||||
Tooltip,
|
||||
Chip,
|
||||
ButtonGroup,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
|
||||
const ExploreWorkflow = (props) => {
|
||||
const { userdata, globalUrl, appFramework } = props
|
||||
const [activeUsecases, setActiveUsecases] = useState(0);
|
||||
const [modalOpen, setModalOpen] = React.useState(false);
|
||||
const [suggestedUsecases, setSuggestedUsecases] = useState([])
|
||||
const [usecasesSet, setUsecasesSet] = useState(false)
|
||||
const [apps, setApps] = useState([])
|
||||
const sizing = 475
|
||||
|
||||
let navigate = useNavigate();
|
||||
|
||||
const imagestyle = {
|
||||
height: 40,
|
||||
borderRadius: 40,
|
||||
//border: "2px solid rgba(255,255,255,0.3)",
|
||||
}
|
||||
|
||||
const loadApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson === null) {
|
||||
console.log("null-response from server")
|
||||
const pretend_apps = [{
|
||||
"name": "TBD",
|
||||
"app_name": "TBD",
|
||||
"app_version": "TBD",
|
||||
"description": "TBD",
|
||||
"version": "TBD",
|
||||
"large_image": "",
|
||||
}]
|
||||
|
||||
setApps(pretend_apps)
|
||||
return
|
||||
}
|
||||
|
||||
if (responseJson.success === false) {
|
||||
console.log("error loading apps: ", responseJson)
|
||||
return
|
||||
}
|
||||
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
// Find priorities in userdata.priorities and check if the item.type === "usecase"
|
||||
// If so, set the item.isActive to true
|
||||
if (usecasesSet === false && userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && suggestedUsecases.length === 0) {
|
||||
|
||||
var tmpUsecases = []
|
||||
for (let i = 0; i < userdata.priorities.length; i++) {
|
||||
if (userdata.priorities[i].type !== "usecase" || userdata.priorities[i].active === false) {
|
||||
continue
|
||||
}
|
||||
|
||||
const descsplit = userdata.priorities[i].description.split("&")
|
||||
if (descsplit.length === 5) {
|
||||
console.log("descsplit: ", descsplit)
|
||||
if (descsplit[1] === "") {
|
||||
const item = findSpecificApp(appFramework, descsplit[0])
|
||||
console.log("item: ", item)
|
||||
if (item !== null) {
|
||||
descsplit[1] = item.large_image
|
||||
}
|
||||
}
|
||||
|
||||
if (descsplit[3] === "") {
|
||||
const item = findSpecificApp(appFramework, descsplit[2])
|
||||
console.log("item: ", item)
|
||||
if (item !== null) {
|
||||
descsplit[3] = item.large_image
|
||||
}
|
||||
}
|
||||
|
||||
console.log("descsplit: ", descsplit)
|
||||
userdata.priorities[i].description = descsplit.join("&")
|
||||
}
|
||||
|
||||
tmpUsecases.push(userdata.priorities[i])
|
||||
}
|
||||
|
||||
console.log("USECASES: ", tmpUsecases)
|
||||
if (tmpUsecases.length === 0) {
|
||||
console.log("Add some random ones, as everything is done")
|
||||
|
||||
const comms = findSpecificApp(appFramework, "communication")
|
||||
const cases = findSpecificApp(appFramework, "cases")
|
||||
const edr = findSpecificApp(appFramework, "edr")
|
||||
const siem = findSpecificApp(appFramework, "siem")
|
||||
|
||||
tmpUsecases = [{
|
||||
"name": "Suggested Usecase: Email management",
|
||||
"description": comms.name+"&"+comms.large_image+"&"+cases.name+"&"+cases.large_image,
|
||||
"type": "usecase",
|
||||
"url": "/usecases?selected_object=Email management",
|
||||
"severity": 0,
|
||||
"active": false,
|
||||
},{
|
||||
"name": "Suggested Usecase: EDR to ticket",
|
||||
"description": edr.name+"&"+edr.large_image+"&"+cases.name+"&"+cases.large_image,
|
||||
"type": "usecase",
|
||||
"url": "/usecases?selected_object=EDR to ticket",
|
||||
"severity": 0,
|
||||
"active": false,
|
||||
},{
|
||||
"name": "Suggested Usecase: SIEM to ticket",
|
||||
"description": siem.name+"&"+siem.large_image+"&"+cases.name+"&"+cases.large_image,
|
||||
"type": "usecase",
|
||||
"url": "/usecases?selected_object=SIEM to ticket",
|
||||
"severity": 0,
|
||||
"active": false,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
setSuggestedUsecases(tmpUsecases)
|
||||
setUsecasesSet(true)
|
||||
loadApps()
|
||||
}
|
||||
|
||||
const modalView = (
|
||||
// console.log("key:", dataValue.key),
|
||||
//console.log("value:",dataValue.value),
|
||||
<Dialog
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{}}>
|
||||
<div style={{ color: "white", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Sign Up</span>
|
||||
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginLeft: 8, marginRight: 8 }} />
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Setup</span>
|
||||
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginRight: 8 }} />
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Explore</span>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<Typography style={{ fontSize: 16, width: 252, marginLeft: 167 }}>
|
||||
Here’s a recommended workflow:
|
||||
</Typography>
|
||||
{/* <div style={{ marginTop: 0, maxWidth: 700, minWidth: 700, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<div className="thumbs" style={{ display: "flex" }}>
|
||||
<Tooltip title={"Previous usecase"}>
|
||||
<IconButton
|
||||
style={{
|
||||
// backgroundColor: thumbIndex === 0 ? "inherit" : "white",
|
||||
zIndex: 5000,
|
||||
minHeight: 50,
|
||||
maxHeight: 50,
|
||||
color: "grey",
|
||||
marginTop: 150,
|
||||
borderRadius: 50,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}
|
||||
onClick={() => {
|
||||
slidePrev()
|
||||
}}
|
||||
>
|
||||
<ArrowBackIosNewIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
|
||||
<AliceCarousel
|
||||
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
|
||||
items={formattedCarousel}
|
||||
activeIndex={thumbIndex}
|
||||
infiniteLoop
|
||||
mouseTracking={false}
|
||||
responsive={responsive}
|
||||
// activeIndex={activeIndex}
|
||||
controlsStrategy="responsive"
|
||||
autoPlay={false}
|
||||
infinite={true}
|
||||
animationType="fadeout"
|
||||
animationDuration={800}
|
||||
disableButtonsControls
|
||||
|
||||
/>
|
||||
</div>
|
||||
<Tooltip title={"Next usecase"}>
|
||||
<IconButton
|
||||
style={{
|
||||
backgroundColor: thumbIndex === usecaseButtons.length - 1 ? "inherit" : "white",
|
||||
zIndex: 5000,
|
||||
minHeight: 50,
|
||||
maxHeight: 50,
|
||||
color: "grey",
|
||||
marginTop: 150,
|
||||
borderRadius: 50,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}
|
||||
onClick={() => {
|
||||
slideNext()
|
||||
}}
|
||||
>
|
||||
<ArrowForwardIosIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}>
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => setModalOpen(false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => {
|
||||
console.log("hello")
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||
{modalView}
|
||||
<Typography variant="h4" style={{ marginLeft: 8, marginTop: 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)">
|
||||
Start using workflows
|
||||
</Typography>
|
||||
<Typography variant="body2" style={{ marginLeft: 8, marginTop: 10, marginRight: 30, marginBottom: 40, }} color="rgba(158, 158, 158, 1)">
|
||||
Based on what you selected workflows, here are our recommendations! You will see more of these later.
|
||||
</Typography>
|
||||
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<div className="thumbs" style={{ display: "flex" }}>
|
||||
<div style={{ minWidth: 554, maxWidth: 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)">
|
||||
All Workflows are already added for your current apps!
|
||||
</Typography>
|
||||
:
|
||||
suggestedUsecases.map((priority, index) => {
|
||||
|
||||
const srcapp = priority.description.split("&")[0]
|
||||
var image1 = priority.description.split("&")[1]
|
||||
var image2 = ""
|
||||
var dstapp = ""
|
||||
if (priority.description.split("&").length > 3) {
|
||||
dstapp = priority.description.split("&")[2]
|
||||
image2 = priority.description.split("&")[3]
|
||||
}
|
||||
|
||||
const name = priority.name.replace("Suggested Usecase: ", "")
|
||||
|
||||
var description = ""
|
||||
if (priority.description.split("&").length > 4) {
|
||||
description = priority.description[4]
|
||||
}
|
||||
|
||||
// FIXME: Should have a proper description
|
||||
description = ""
|
||||
|
||||
return (
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
globalUrl={globalUrl}
|
||||
img1={image1}
|
||||
srcapp={srcapp}
|
||||
img2={image2}
|
||||
dstapp={dstapp}
|
||||
title={name}
|
||||
description={description}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
<div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
|
||||
<Button variant="contained" type="submit"
|
||||
fullWidth style={{
|
||||
borderRadius: 200,
|
||||
height: 51,
|
||||
width: 464,
|
||||
fontSize: 16,
|
||||
padding: "16px 24px",
|
||||
margin: "auto",
|
||||
itemAlign: "center",
|
||||
background: activeUsecases === 0 ? "rgba(47, 47, 47, 1)" : "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
|
||||
color: activeUsecases === 0? "rgba(158, 158, 158, 1)" : "rgba(241, 241, 241, 1)",
|
||||
border: activeUsecases === 0 ? "1px solid rgba(158, 158, 158, 1)" : "none",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/workflows?message="+activeUsecases+" workflows added")
|
||||
}}>
|
||||
Continue to workflows
|
||||
</Button>
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
|
||||
<Link style={{ color: "#f86a3e", marginLeft: 145 }} to="/usecases" className="btn btn-primary">
|
||||
Explore usecases
|
||||
</Link>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ExploreWorkflow
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import {
|
||||
IconButton,
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
Divider,
|
||||
Select,
|
||||
MenuItem,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
OpenInNew as OpenInNewIcon,
|
||||
@@ -27,9 +28,9 @@ import {
|
||||
Publish as PublishIcon,
|
||||
Clear as ClearIcon,
|
||||
Add as AddIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
import CodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import theme from "../theme.jsx";
|
||||
@@ -45,7 +46,7 @@ const Files = (props) => {
|
||||
const [openEditor, setOpenEditor] = React.useState(false);
|
||||
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
||||
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"]
|
||||
var upload = "";
|
||||
|
||||
@@ -113,7 +114,7 @@ const Files = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -139,12 +140,12 @@ const Files = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success) {
|
||||
alert.info("Successfully deleted file " + file.name);
|
||||
toast("Successfully deleted file " + file.name);
|
||||
} else if (
|
||||
responseJson.reason !== undefined &&
|
||||
responseJson.reason !== null
|
||||
) {
|
||||
alert.error("Failed to delete file: " + responseJson.reason);
|
||||
toast("Failed to delete file: " + responseJson.reason);
|
||||
}
|
||||
setTimeout(() => {
|
||||
getFiles();
|
||||
@@ -153,7 +154,7 @@ const Files = (props) => {
|
||||
console.log(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -178,7 +179,7 @@ const Files = (props) => {
|
||||
// console.log("respdata type ->", typeof(respdata));
|
||||
|
||||
if (respdata.length === 0) {
|
||||
alert.error("Failed getting file. Is it deleted?");
|
||||
toast("Failed getting file. Is it deleted?");
|
||||
return;
|
||||
}
|
||||
return respdata
|
||||
@@ -189,7 +190,7 @@ const Files = (props) => {
|
||||
//console.log("filecontent state ",fileContent);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -210,7 +211,7 @@ const Files = (props) => {
|
||||
})
|
||||
.then((respdata) => {
|
||||
if (respdata.length === 0) {
|
||||
alert.error("Failed getting file. Is it deleted?");
|
||||
toast("Failed getting file. Is it deleted?");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,7 +250,7 @@ const Files = (props) => {
|
||||
//setSchedules(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -291,11 +292,11 @@ const Files = (props) => {
|
||||
if (responseJson.success === true) {
|
||||
handleFileUpload(responseJson.id, file);
|
||||
} else {
|
||||
alert.error("Failed to upload file ", filename);
|
||||
toast("Failed to upload file ", filename);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Failed to upload file ", filename);
|
||||
toast("Failed to upload file ", filename);
|
||||
console.log(error.toString());
|
||||
});
|
||||
};
|
||||
@@ -312,7 +313,7 @@ const Files = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200 && response.status !== 201) {
|
||||
console.log("Status not 200 for apps :O!");
|
||||
alert.error("File was created, but failed to upload.");
|
||||
toast("File was created, but failed to upload.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -323,7 +324,7 @@ const Files = (props) => {
|
||||
//setFiles(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -364,7 +365,7 @@ const Files = (props) => {
|
||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||
|
||||
//const reader = new FileReader();
|
||||
//alert.info("Starting fileupload")
|
||||
//toast("Starting fileupload")
|
||||
uploadFiles(files);
|
||||
};
|
||||
|
||||
@@ -681,7 +682,7 @@ const Files = (props) => {
|
||||
<ListItemText
|
||||
primary=<span style={{ display:"inline"}}>
|
||||
<Tooltip
|
||||
title={`Edit File (${allowedFileTypes.join(", ")})`}
|
||||
title={`Edit File (${allowedFileTypes.join(", ")}). Max size 2MB`}
|
||||
style={{}}
|
||||
aria-label={"Edit"}
|
||||
>
|
||||
@@ -742,7 +743,7 @@ const Files = (props) => {
|
||||
) {
|
||||
const clipboard = navigator.clipboard;
|
||||
if (clipboard === undefined) {
|
||||
alert.error(
|
||||
toast(
|
||||
"Can only copy over HTTPS (port 3443)"
|
||||
);
|
||||
return;
|
||||
@@ -758,7 +759,7 @@ const Files = (props) => {
|
||||
/* Copy the text inside the text field */
|
||||
document.execCommand("copy");
|
||||
|
||||
alert.info(file.id + " copied to clipboard");
|
||||
toast(file.id + " copied to clipboard");
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
//import List from '@material-ui/core/List';
|
||||
//import ListItem from '@material-ui/core/ListItem';
|
||||
|
||||
//borderTop: "1px solid #385F71"
|
||||
const FooterStyle = {
|
||||
right: "0",
|
||||
left: "0",
|
||||
bottom: "0",
|
||||
height: "130px",
|
||||
backgroundColor: "rgba(15, 14, 31, 1)",
|
||||
};
|
||||
|
||||
const FooterInfo = {
|
||||
maxWidth: "1150px",
|
||||
minWidth: "768px",
|
||||
textAlign: "center",
|
||||
margin: "auto",
|
||||
};
|
||||
|
||||
const hrefStyle = {
|
||||
color: "#bdbdbd",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
const Footer = (props) => {
|
||||
return (
|
||||
<div style={FooterStyle}>
|
||||
<div style={FooterInfo}>
|
||||
<Box />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Box = (props) => {
|
||||
return (
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{ flex: "1" }}>
|
||||
<a style={hrefStyle} href="/about">
|
||||
<h1>About</h1>
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ flex: "1" }}>
|
||||
<a style={hrefStyle} href="/privacy-policy">
|
||||
<h1>Privacy Policy</h1>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -1,883 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
|
||||
import {
|
||||
Chip,
|
||||
Badge,
|
||||
Typography,
|
||||
Paper,
|
||||
Tooltip,
|
||||
List,
|
||||
Avatar,
|
||||
Menu,
|
||||
ListItem,
|
||||
MenuItem,
|
||||
Select,
|
||||
Button,
|
||||
IconButton,
|
||||
Grid,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
MeetingRoom as MeetingRoomIcon,
|
||||
Settings as SettingsIcon,
|
||||
Notifications as NotificationsIcon,
|
||||
Home as HomeIcon,
|
||||
Polymer as PolymerIcon,
|
||||
Apps as AppsIcon,
|
||||
Description as DescriptionIcon,
|
||||
HelpOutline as HelpOutlineIcon,
|
||||
} from "@material-ui/icons";
|
||||
|
||||
import {
|
||||
Analytics as AnalyticsIcon,
|
||||
Lightbulb as LightbulbIcon,
|
||||
} from "@mui/icons-material";
|
||||
//import LogoutIcon from '@mui/icons-material/Logout';
|
||||
import { useAlert } from "react-alert";
|
||||
import SearchField from '../components/Searchfield'
|
||||
|
||||
const hoverColor = "#f85a3e";
|
||||
const hoverOutColor = "#e8eaf6";
|
||||
|
||||
const Header = (props) => {
|
||||
const {
|
||||
globalUrl,
|
||||
setNotifications,
|
||||
notifications,
|
||||
isLoggedIn,
|
||||
removeCookie,
|
||||
homePage,
|
||||
isLoaded,
|
||||
userdata,
|
||||
cookies,
|
||||
} = props;
|
||||
const theme = useTheme();
|
||||
|
||||
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
|
||||
const [SoarHoverColor, setSoarHoverColor] = useState(hoverOutColor);
|
||||
const [LoginHoverColor, setLoginHoverColor] = useState(hoverOutColor);
|
||||
const [DocsHoverColor, setDocsHoverColor] = useState(hoverOutColor);
|
||||
const [HelpHoverColor, setHelpHoverColor] = useState(hoverOutColor);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const [anchorElAvatar, setAnchorElAvatar] = React.useState(null);
|
||||
const alert = useAlert();
|
||||
|
||||
const hrefStyle = {
|
||||
color: hoverOutColor,
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
setAnchorElAvatar(null);
|
||||
};
|
||||
|
||||
const clearNotifications = () => {
|
||||
// Don't really care about the logout
|
||||
fetch(`${globalUrl}/api/v1/notifications/clear`, {
|
||||
credentials: "include",
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.status !== 200) {
|
||||
console.log("Error in response");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then(function (responseJson) {
|
||||
if (responseJson.success === true) {
|
||||
setNotifications([]);
|
||||
handleClose();
|
||||
} else {
|
||||
alert.error(
|
||||
"Failed dismissing notifications. Please try again later."
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error in notification dismissal: ", error);
|
||||
//removeCookie("session_token", {path: "/"})
|
||||
});
|
||||
};
|
||||
|
||||
const dismissNotification = (alert_id) => {
|
||||
// Don't really care about the logout
|
||||
fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, {
|
||||
credentials: "include",
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.status !== 200) {
|
||||
console.log("Error in response");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then(function (responseJson) {
|
||||
if (responseJson.success === true) {
|
||||
const newNotifications = notifications.filter(
|
||||
(data) => data.id !== alert_id
|
||||
);
|
||||
console.log("NEW NOTIFICATIONS: ", newNotifications);
|
||||
setNotifications(newNotifications);
|
||||
} else {
|
||||
alert.error(
|
||||
"Failed dismissing notification. Please try again later."
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error in notification dismissal: ", error);
|
||||
//removeCookie("session_token", {path: "/"})
|
||||
});
|
||||
};
|
||||
|
||||
// DEBUG HERE
|
||||
const handleClickLogout = () => {
|
||||
console.log("COOKIES: ", cookies, "Remover: ", removeCookie);
|
||||
// Don't really care about the logout
|
||||
fetch(globalUrl + "/api/v1/logout", {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
// Log out anyway
|
||||
//cookies.remove("session_token")
|
||||
//window.location.pathname = "/"
|
||||
console.log("Should've logged out");
|
||||
removeCookie("session_token", { path: "/" });
|
||||
removeCookie("session_token", { path: "/workflows" });
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error in logout: ", error);
|
||||
removeCookie("session_token", { path: "/" });
|
||||
window.location.reload();
|
||||
//removeCookie("session_token", {path: "/"})
|
||||
});
|
||||
};
|
||||
|
||||
const handleClickChangeOrg = (orgId) => {
|
||||
// Don't really care about the logout
|
||||
//name: org.name,
|
||||
//orgId = "asd"
|
||||
const data = {
|
||||
org_id: orgId,
|
||||
};
|
||||
|
||||
localStorage.setItem("getting_started_sidebar", "open");
|
||||
|
||||
fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, {
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.status !== 200) {
|
||||
console.log("Error in response");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then(function (responseJson) {
|
||||
if (responseJson.success !== undefined && responseJson.success) {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
alert.success(
|
||||
"Successfully changed active organization - refreshing!"
|
||||
);
|
||||
} else {
|
||||
alert.error("Failed changing org: ", responseJson.reason);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error changing: ", error);
|
||||
//removeCookie("session_token", {path: "/"})
|
||||
});
|
||||
};
|
||||
|
||||
// Rofl this is weird
|
||||
const handleDocsHover = () => {
|
||||
setDocsHoverColor(hoverColor);
|
||||
};
|
||||
|
||||
const handleDocsHoverOut = () => {
|
||||
setDocsHoverColor(hoverOutColor);
|
||||
};
|
||||
|
||||
const handleHomeHover = () => {
|
||||
setHomeHoverColor(hoverColor);
|
||||
};
|
||||
|
||||
const handleHelpHover = () => {
|
||||
setHelpHoverColor(hoverColor);
|
||||
};
|
||||
|
||||
const handleHelpHoverOut = () => {
|
||||
setHelpHoverColor(hoverOutColor);
|
||||
};
|
||||
|
||||
const handleSoarHover = () => {
|
||||
setSoarHoverColor(hoverColor);
|
||||
};
|
||||
|
||||
const handleSoarHoverOut = () => {
|
||||
setSoarHoverColor(hoverOutColor);
|
||||
};
|
||||
|
||||
const handleHomeHoverOut = () => {
|
||||
setHomeHoverColor(hoverOutColor);
|
||||
};
|
||||
|
||||
const handleLoginHover = () => {
|
||||
setLoginHoverColor(hoverColor);
|
||||
};
|
||||
|
||||
const handleLoginHoverOut = () => {
|
||||
setLoginHoverColor(hoverOutColor);
|
||||
};
|
||||
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const chipStyle = {
|
||||
backgroundColor: "#3d3f43",
|
||||
height: 30,
|
||||
marginRight: 5,
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
height: 28,
|
||||
cursor: "pointer",
|
||||
borderColor: "#3d3f43",
|
||||
color: "white",
|
||||
};
|
||||
|
||||
const notificationWidth = 350;
|
||||
const NotificationItem = (props) => {
|
||||
const { data } = props;
|
||||
|
||||
return (
|
||||
<Paper
|
||||
style={{
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
width: notificationWidth,
|
||||
padding: 25,
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
}}
|
||||
>
|
||||
{/*<Typography variant="h6">
|
||||
{new Date(data.updated_at).toISOString()}
|
||||
</Typography >*/}
|
||||
{data.reference_url !== undefined &&
|
||||
data.reference_url !== null &&
|
||||
data.reference_url.length > 0 ? (
|
||||
<Link
|
||||
to={data.reference_url}
|
||||
style={{ color: "#f86a3e", textDecoration: "none" }}
|
||||
>
|
||||
<Typography variant="h6">{data.title}</Typography>
|
||||
</Link>
|
||||
) : (
|
||||
<Typography variant="h6">{data.title}</Typography>
|
||||
)}
|
||||
|
||||
{data.image !== undefined &&
|
||||
data.image !== null &&
|
||||
data.image.length > 0 ? (
|
||||
<img
|
||||
alt={data.title}
|
||||
src={data.image}
|
||||
style={{ height: 100, width: 100 }}
|
||||
/>
|
||||
) : null}
|
||||
<Typography variant="body1">{data.description}</Typography>
|
||||
{/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ?
|
||||
data.tags.map((tag, index) => {
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
style={chipStyle}
|
||||
label={tag}
|
||||
onClick={() => {
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null */}
|
||||
{data.read === false ? (
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
style={{ marginTop: 15 }}
|
||||
onClick={() => {
|
||||
dismissNotification(data.id);
|
||||
}}
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
) : null}
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
const notificationMenu = (
|
||||
<span style={{ zIndex: 10001 }}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
style={{ zIndex: 10001, marginRight: 15 }}
|
||||
aria-controls="simple-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={(event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
}}
|
||||
>
|
||||
<Badge badgeContent={notifications.length} color="primary">
|
||||
<NotificationsIcon
|
||||
color="secondary"
|
||||
style={{ height: 30, width: 30 }}
|
||||
alt="Your username here"
|
||||
src=""
|
||||
/>
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="simple-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
style={{
|
||||
zIndex: 10002,
|
||||
maxHeight: "90vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
},
|
||||
}}
|
||||
onClose={() => {
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
style={{
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
width: notificationWidth,
|
||||
padding: 25,
|
||||
borderBottom: "3px solid rgba(255,255,255,0.4)",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", marginBottom: 5 }}>
|
||||
<Typography variant="h6">
|
||||
Your Notifications ({notifications.length})
|
||||
</Typography>
|
||||
{notifications.length > 1 ? (
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
style={{ marginLeft: 30 }}
|
||||
onClick={() => {
|
||||
clearNotifications();
|
||||
}}
|
||||
>
|
||||
Flush
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
<Typography variant="body2">
|
||||
Notifications are made by Shuffle to help you discover issues or
|
||||
improvements.
|
||||
</Typography>
|
||||
</Paper>
|
||||
{notifications.map((data, index) => {
|
||||
return <NotificationItem data={data} key={index} />;
|
||||
})}
|
||||
</Menu>
|
||||
</span>
|
||||
);
|
||||
|
||||
// Should be based on some path
|
||||
const avatarMenu = (
|
||||
<span style={{ zIndex: 10001 }}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
style={{ zIndex: 10001, marginRight: 15 }}
|
||||
aria-controls="simple-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={(event) => {
|
||||
setAnchorElAvatar(event.currentTarget);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
style={{ height: 30, width: 30 }}
|
||||
alt="Your username here"
|
||||
src=""
|
||||
/>
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="simple-menu"
|
||||
anchorEl={anchorElAvatar}
|
||||
keepMounted
|
||||
open={Boolean(anchorElAvatar)}
|
||||
style={{ zIndex: 10012 }}
|
||||
onClose={() => {
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<Link to="/docs" style={hrefStyle}>
|
||||
<HelpOutlineIcon style={{marginRight: 5 }}/> About
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<Link to="/getting-started" style={hrefStyle}>
|
||||
<AnalyticsIcon style={{marginRight: 5 }}/> Get Started
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<Link to="/usecases" style={hrefStyle}>
|
||||
<LightbulbIcon style={{marginRight: 5 }}/> Use Cases
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<Link to="/settings" style={hrefStyle}>
|
||||
<SettingsIcon style={{marginRight: 5 }}/> Settings
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
style={{ color: "white" }}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleClose();
|
||||
handleClickLogout();
|
||||
}}
|
||||
>
|
||||
<MeetingRoomIcon style={{marginRight: 5 }}/> Logout
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</span>
|
||||
);
|
||||
|
||||
// Handle top bar or something
|
||||
const logoCheck = !homePage ? null : null;
|
||||
//<div style={{position: "fixed", top: 0, left: 0, display: "flex"}}>
|
||||
const loginTextBrowser = !isLoggedIn ? (
|
||||
<div style={{ display: "flex" }}>
|
||||
<List style={{ flex: 1, display: "flex", flexDirect: "row" }} component="nav">
|
||||
<ListItem style={{ textAlign: "center", marginLeft: "0px" }}>
|
||||
<Link to="/docs" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleSoarHover}
|
||||
onMouseOut={handleSoarHoverOut}
|
||||
style={{ color: SoarHoverColor, cursor: "pointer" }}
|
||||
>
|
||||
About
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
</List>
|
||||
{!isLoaded ? null :
|
||||
userdata.chat_disabled === true ? null :
|
||||
<div style={{flex: 1, }}>
|
||||
<SearchField serverside={false} userdata={userdata} />
|
||||
</div>
|
||||
}
|
||||
<div style={{ flex: 1, display: "flex", flexDirection: "row-reverse" }}>
|
||||
<List
|
||||
style={{ display: "flex", flexDirection: "row-reverse" }}
|
||||
component="nav"
|
||||
>
|
||||
<ListItem style={{ flex: "1", textAlign: "center" }}>
|
||||
<Link to="/login" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleLoginHover}
|
||||
onMouseOut={handleLoginHoverOut}
|
||||
style={{ color: LoginHoverColor, cursor: "pointer" }}
|
||||
>
|
||||
Login
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{ flex: 1, flexDirection: "row" }}>
|
||||
<List
|
||||
style={{ display: "flex", flexDirect: "row", flex: "1" }}
|
||||
component="nav"
|
||||
>
|
||||
<ListItem style={{ textAlign: "center", maxWidth: 140, }}>
|
||||
<Link to="/workflows" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleSoarHover}
|
||||
onMouseOut={handleSoarHoverOut}
|
||||
style={{
|
||||
color: SoarHoverColor,
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<PolymerIcon style={{ marginRight: "5px" }} />
|
||||
<span style={{ marginTop: 2 }}>Workflows</span>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
<ListItem style={{ textAlign: "center", maxWidth: 100, }}>
|
||||
<Link to="/apps" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleHelpHover}
|
||||
onMouseOut={handleHelpHoverOut}
|
||||
style={{
|
||||
color: HelpHoverColor,
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<AppsIcon style={{ marginRight: "5px" }} />
|
||||
<span style={{ marginTop: 2 }}>Apps</span>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
{/*
|
||||
<ListItem style={{textAlign: "center"}}>
|
||||
<Link to="/dashboard" style={hrefStyle}>
|
||||
<div onMouseOver={handleDocsHover} onMouseOut={handleDocsHoverOut} style={{color: DocsHoverColor, cursor: "pointer"}}>Dashboard</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
*/}
|
||||
<ListItem style={{ textAlign: "center", maxWidth: 120, }}>
|
||||
<Link to="/docs" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleDocsHover}
|
||||
onMouseOut={handleDocsHoverOut}
|
||||
style={{
|
||||
color: DocsHoverColor,
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<DescriptionIcon style={{ marginRight: "5px" }} />
|
||||
<span style={{ marginTop: 2 }}>Docs</span>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
{/*
|
||||
<ListItem style={{textAlign: "center"}}>
|
||||
<Link to="/pricing" style={hrefStyle}>
|
||||
<div onMouseOver={handleDocsHover} onMouseOut={handleDocsHoverOut} style={{color: DocsHoverColor, cursor: "pointer", display: "flex"}}>
|
||||
<DescriptionIcon style={{marginRight: "5px"}} />
|
||||
<span style={{marginTop: 2}}>Pricing</span>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
*/}
|
||||
{/*
|
||||
<ListItem style={{textAlign: "center"}}>
|
||||
<Link to="/configurations" style={hrefStyle}>
|
||||
<div onMouseOver={handleCredentialHover} onMouseOut={handleCredentialHoverOut} style={{color: CredentialHoverColor, cursor: "pointer"}}>Configure</div>
|
||||
</a>
|
||||
</ListItem>
|
||||
*/}
|
||||
</List>
|
||||
</div>
|
||||
{!isLoaded ? null :
|
||||
userdata.chat_disabled === true ? null :
|
||||
<div style={{flex: 1, }}>
|
||||
<SearchField serverside={false} userdata={userdata} />
|
||||
</div>
|
||||
}
|
||||
<div
|
||||
style={{ flex: 1, display: "flex", flexDirection: "row-reverse" }}
|
||||
>
|
||||
{avatarMenu}
|
||||
{notificationMenu}
|
||||
{userdata === undefined ||
|
||||
userdata.admin === undefined ||
|
||||
userdata.admin === null ||
|
||||
!userdata.admin ? null : (
|
||||
<Link to="/admin" style={hrefStyle}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
style={{ marginRight: 15, marginTop: 12 }}
|
||||
>
|
||||
Admin
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{userdata === undefined ||
|
||||
userdata.orgs === undefined ||
|
||||
userdata.orgs === null ||
|
||||
userdata.orgs.length <= 1 ? null : (
|
||||
<Select
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
maxWidth: 200,
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
value={userdata.active_org.id}
|
||||
fullWidth
|
||||
style={{
|
||||
zIndex: 10012,
|
||||
marginTop: 5,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
marginRight: 15,
|
||||
color: "white",
|
||||
height: 50,
|
||||
width: 200,
|
||||
}}
|
||||
MenuProps={{
|
||||
style: { zIndex: 10012 },
|
||||
}}
|
||||
onChange={(e) => {
|
||||
handleClickChangeOrg(e.target.value);
|
||||
}}
|
||||
>
|
||||
{userdata.orgs.map((data, index) => {
|
||||
if (
|
||||
data.name === undefined ||
|
||||
data.name === null ||
|
||||
data.name.length === 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const imagesize = 22
|
||||
|
||||
//if (data.creator_org !== undefined && data.creator_org !== null && data.creator_org.length > 0 && data.fixed !== true) {
|
||||
var skipOrg = false
|
||||
if (data.creator_org !== undefined && data.creator_org !== null && data.creator_org.length > 0) {
|
||||
// Finds the parent org
|
||||
for (var key in userdata.child_orgs) {
|
||||
if (data.child_orgs[key].id === data.creator_org) {
|
||||
skipOrg = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (skipOrg) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Reordering to have suborgs with access under original org
|
||||
if (data.child_orgs !== undefined && data.child_orgs !== null) {
|
||||
var cnt = 0
|
||||
for (var key in data.child_orgs) {
|
||||
const childorg = data.child_orgs[key]
|
||||
const foundIndex = userdata.orgs.findIndex(item => item.id === childorg.id)
|
||||
if (foundIndex !== -1) {
|
||||
const newindex = parseInt(index)+parseInt(cnt)
|
||||
|
||||
var newitem = userdata.orgs[foundIndex]
|
||||
newitem.fixed = true
|
||||
userdata.orgs.splice(newindex+1, 0, newitem)
|
||||
userdata.orgs.splice(foundIndex+1, 1)
|
||||
} else {
|
||||
console.log("ORG NOT FOUND IN LIST: ", childorg)
|
||||
}
|
||||
|
||||
// This is stupid :)
|
||||
cnt += 1
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("ORG: ", data)
|
||||
const imageStyle = {
|
||||
width: imagesize,
|
||||
height: imagesize,
|
||||
pointerEvents: "none",
|
||||
marginRight: 10,
|
||||
marginLeft: data.creator_org !== undefined && data.creator_org !== null && data.creator_org.length > 0 ? 20 : 0,
|
||||
}
|
||||
|
||||
const parsedTitle = data.creator_org !== undefined && data.creator_org !== null && data.creator_org.length > 0 ? `Suborg of ${data.creator_org}` : ""
|
||||
|
||||
const image = data.image === "" ?
|
||||
<img alt={data.name} src={theme.palette.defaultImage} style={imageStyle} />
|
||||
:
|
||||
<img alt={data.name} src={data.image} style={imageStyle} />
|
||||
|
||||
return (
|
||||
<MenuItem key={index} disabled={data.id === userdata.active_org.id} style={{backgroundColor: theme.palette.inputColor, color: "white", height: 40,}} value={data.id}>
|
||||
|
||||
<Tooltip color="primary" title={parsedTitle} placement="left">
|
||||
<div style={{display: "flex"}}>
|
||||
{image} {data.name}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
//console.log("USR: ", userdata.orgs)
|
||||
|
||||
const loginTextMobile = !isLoggedIn ? (
|
||||
<div style={{ display: "flex" }}>
|
||||
<List style={{ display: "flex", flexDirection: "row" }} component="nav">
|
||||
<ListItem style={{ textAlign: "center" }}>
|
||||
<Link to="/" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleHomeHover}
|
||||
onMouseOut={handleHomeHoverOut}
|
||||
style={{ color: HomeHoverColor, cursor: "pointer" }}
|
||||
>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item>
|
||||
<HomeIcon style={{ marginTop: "3px", marginRight: "5px" }} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
<ListItem style={{ textAlign: "center" }}>
|
||||
<Link to="/docs" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleSoarHover}
|
||||
onMouseOut={handleSoarHoverOut}
|
||||
style={{ color: SoarHoverColor, cursor: "pointer" }}
|
||||
>
|
||||
About
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={{ flex: "1", flexDirection: "row" }}>
|
||||
<List
|
||||
style={{ display: "flex", flexDirect: "row", flex: "1" }}
|
||||
component="nav"
|
||||
>
|
||||
<ListItem style={{ textAlign: "center" }}>
|
||||
<Link to="/" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleHomeHover}
|
||||
onMouseOut={handleHomeHoverOut}
|
||||
style={{ color: HomeHoverColor, cursor: "pointer" }}
|
||||
>
|
||||
Shuffle
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
<ListItem style={{ textAlign: "center" }}>
|
||||
<Link to="/workflows" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleSoarHover}
|
||||
onMouseOut={handleSoarHoverOut}
|
||||
style={{ color: SoarHoverColor, cursor: "pointer" }}
|
||||
>
|
||||
Workflows
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
<ListItem style={{ textAlign: "center" }}>
|
||||
<Link to="/apps" style={hrefStyle}>
|
||||
<div
|
||||
onMouseOver={handleHelpHover}
|
||||
onMouseOut={handleHelpHoverOut}
|
||||
style={{ color: HelpHoverColor, cursor: "pointer" }}
|
||||
>
|
||||
Apps
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
{/*
|
||||
<ListItem style={{textAlign: "center"}}>
|
||||
<Link to="/configurations" style={hrefStyle}>
|
||||
<div onMouseOver={handleCredentialHover} onMouseOut={handleCredentialHoverOut} style={{color: CredentialHoverColor, cursor: "pointer"}}>Configure</div>
|
||||
</a>
|
||||
</ListItem>
|
||||
*/}
|
||||
</List>
|
||||
</div>
|
||||
<div
|
||||
style={{ flex: "10", display: "flex", flexDirection: "row-reverse" }}
|
||||
>
|
||||
{avatarMenu}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// <Divider style={{height: "1px", width: "100%", backgroundColor: "rgb(91, 96, 100)"}}/>
|
||||
const loadedCheck = (
|
||||
<div style={{ minHeight: 60 }}>
|
||||
<BrowserView>{loginTextBrowser}</BrowserView>
|
||||
<MobileView>{loginTextMobile}</MobileView>
|
||||
</div>
|
||||
);
|
||||
// <div style={{backgroundImage: "linear-gradient(-90deg,#342f78 0,#29255e 50%,#1b1947 100%"}}>
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
position: "fixed",
|
||||
minHeight: 60,
|
||||
maxHeight: 60,
|
||||
top: 0,
|
||||
zIndex: 10000,
|
||||
backgroundColor: "inherit",
|
||||
}}
|
||||
>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, {useState} from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import theme from '../theme.jsx';
|
||||
import {BrowserView, MobileView} from "react-device-detect";
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
|
||||
import {Link} from 'react-router-dom';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import {
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
IconButton,
|
||||
Divider,
|
||||
LinearProgress,
|
||||
} from '@material-ui/core'
|
||||
} from '@mui/material'
|
||||
|
||||
import {
|
||||
MeetingRoom as MeetingRoomIcon,
|
||||
@@ -29,19 +30,20 @@ import {
|
||||
Settings as SettingsIcon,
|
||||
Notifications as NotificationsIcon,
|
||||
Home as HomeIcon,
|
||||
Polymer as PolymerIcon,
|
||||
Apps as AppsIcon,
|
||||
Description as DescriptionIcon,
|
||||
EmojiObjects as EmojiObjectsIcon,
|
||||
Business as BusinessIcon,
|
||||
} from '@material-ui/icons';
|
||||
|
||||
Polyline as PolylineIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import {
|
||||
Analytics as AnalyticsIcon,
|
||||
Lightbulb as LightbulbIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
import SearchField from '../components/Searchfield.jsx'
|
||||
const hoverColor = "#f85a3e"
|
||||
@@ -49,8 +51,8 @@ const hoverOutColor = "#e8eaf6"
|
||||
|
||||
const Header = props => {
|
||||
const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, userdata, serverside, } = props;
|
||||
const theme = useTheme();
|
||||
const alert = useAlert()
|
||||
//const theme = useTheme();
|
||||
//const alert = useAlert()
|
||||
|
||||
|
||||
const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor);
|
||||
@@ -61,6 +63,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const [anchorElAvatar, setAnchorElAvatar] = React.useState(null);
|
||||
const [subAnchorEl, setSubAnchorEl] = React.useState(null);
|
||||
let navigate = useNavigate();
|
||||
|
||||
|
||||
const handleClick = (event) => {
|
||||
@@ -99,7 +102,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
setNotifications([])
|
||||
handleClose()
|
||||
} else {
|
||||
alert.error("Failed dismissing notifications. Please try again later.")
|
||||
toast("Failed dismissing notifications. Please try again later.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -129,7 +132,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
console.log("NEW NOTIFICATIONS: ", newNotifications)
|
||||
setNotifications(newNotifications)
|
||||
} else {
|
||||
alert.error("Failed dismissing notification. Please try again later.")
|
||||
toast("Failed dismissing notification. Please try again later.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -209,7 +212,8 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
|
||||
const notificationWidth = 300
|
||||
const imagesize = 22;
|
||||
const boxColor = "#86c142";
|
||||
const boxColor = "#86c142";
|
||||
|
||||
const NotificationItem = (props) => {
|
||||
const {data} = props
|
||||
|
||||
@@ -260,7 +264,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
{data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ?
|
||||
<Link to={data.reference_url} style={{color: "#f86a3e", textDecoration: "none",}}>
|
||||
<Typography variant="body1">
|
||||
{data.title}
|
||||
{data.title} ({data.amount})
|
||||
</Typography >
|
||||
</Link>
|
||||
:
|
||||
@@ -274,7 +278,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
:
|
||||
null
|
||||
}
|
||||
<Typography variant="body2">
|
||||
<Typography variant="body2" style={{maxHeight: 200, overflowX: "hidden", overflowY: "auto", }}>
|
||||
{data.description}
|
||||
</Typography >
|
||||
{/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ?
|
||||
@@ -404,9 +408,9 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 2000)
|
||||
alert.success("Successfully changed active organization - refreshing!")
|
||||
toast("Successfully changed active organization - refreshing!")
|
||||
} else {
|
||||
alert.error("Failed changing org: ", responseJson.reason)
|
||||
toast("Failed changing org: ", responseJson.reason)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -518,6 +522,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
}
|
||||
|
||||
// Handle top bar or something
|
||||
const defaultTop = isCloud ? 0 : 7
|
||||
const loginTextBrowser = !isLoggedIn ?
|
||||
<div style={{display: "flex", minWidth: 1250, maxWidth: 1250, margin: "auto", textAlign: "center",}}>
|
||||
<div style={{display: "flex", flex: 1, }}>
|
||||
@@ -668,7 +673,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div style={{display: "flex", backgroundColor: "#1f2023",}}>
|
||||
<div style={{display: "flex", }}>
|
||||
<div style={{minWidth: 1250, maxWidth: 1250, display: "flex", margin: "auto", }}>
|
||||
<div style={{flex: 1, flexDirection: "row"}}>
|
||||
<List style={{height: 56, marginTop: "auto", marginBottom: "auto", display: "flex", flexDirect: "row", alignItems: "baseline", maxWidth: 340, }} component="nav">
|
||||
@@ -690,9 +695,9 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
<Link to="/workflows" style={hrefStyle}>
|
||||
<div onMouseOver={handleSoarHover} onMouseOut={handleSoarHoverOut} style={{color: SoarHoverColor, cursor: "pointer", display: "flex"}}>
|
||||
{/*
|
||||
<PolymerIcon style={{marginRight: "5px"}} />
|
||||
<PolylineIcon style={{marginRight: "5px"}} />
|
||||
*/}
|
||||
<span style={{marginTop: 0, marginRight: 8, }}>Workflows</span>
|
||||
<Typography style={{marginTop: defaultTop, marginRight: 8, }}>Workflows</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
@@ -702,7 +707,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
{/*
|
||||
<AppsIcon style={{marginRight: "5px"}} />
|
||||
*/}
|
||||
<span style={{marginTop: 0, marginRight: 5, }}>Apps</span>
|
||||
<Typography style={{marginTop: defaultTop, marginRight: 5, }}>Apps</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
@@ -719,7 +724,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
{/*
|
||||
<DescriptionIcon style={{marginRight: "5px"}} />
|
||||
*/}
|
||||
<span style={{marginTop: 0,}}>Docs</span>
|
||||
<Typography style={{marginTop: defaultTop,}}>Docs</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
</ListItem>
|
||||
@@ -793,8 +798,6 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
</ListItem>
|
||||
: null*/}
|
||||
|
||||
|
||||
|
||||
{userdata === undefined || userdata.orgs === undefined || userdata.orgs === null || userdata.orgs.length <= 1 ?
|
||||
null
|
||||
:
|
||||
@@ -811,7 +814,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
}}
|
||||
MenuProps={{
|
||||
style: {
|
||||
zIndex: 10002,
|
||||
zIndex: 15000,
|
||||
},
|
||||
}}
|
||||
style={{
|
||||
@@ -885,7 +888,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
|
||||
<Tooltip color="primary" title={parsedTitle} placement="left">
|
||||
<div style={{display: "flex"}}>
|
||||
<Typography variant="body2" style={{borderRadius: theme.palette.borderRadius, float: "left", margin: "0 0 0 0", marginRight: 25, }}>{regiontag}</Typography> {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>
|
||||
@@ -895,13 +898,32 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
</span>
|
||||
}
|
||||
|
||||
{/* Show on cloud, if not suborg and if not customer/pov/internal */}
|
||||
{isCloud && (userdata.org_status === undefined || userdata.org_status === null || userdata.org_status.length === 0) ?
|
||||
<ListItem style={{textAlign: "center", marginLeft: 0, marginRight: 7, marginTop: 3, }}>
|
||||
<Link to ="/pricing?tab=cloud&highlight=true" style={hrefStyle}>
|
||||
<Button variant="contained" color="primary" style={{textTransform: "none"}} onClick={() => {
|
||||
ReactGA.event({
|
||||
category: "header",
|
||||
action: "pricing_upgrade_click",
|
||||
label: "",
|
||||
})
|
||||
}}>
|
||||
Upgrade
|
||||
</Button>
|
||||
</Link>
|
||||
</ListItem>
|
||||
: null}
|
||||
|
||||
{userdata === undefined || userdata.app_execution_limit === undefined || userdata.app_execution_usage === undefined || userdata.app_execution_usage < 1000 ?
|
||||
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, }} 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 })
|
||||
navigate("/pricing")
|
||||
} else {
|
||||
console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
||||
}
|
||||
@@ -1011,16 +1033,16 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
const loadedCheck =
|
||||
<div style={{minHeight: 68}}>
|
||||
<BrowserView>
|
||||
{loginTextBrowser}
|
||||
{loginTextBrowser}
|
||||
</BrowserView>
|
||||
<MobileView>
|
||||
{loginTextMobile}
|
||||
{loginTextMobile}
|
||||
</MobileView>
|
||||
</div>
|
||||
// <div style={{backgroundImage: "linear-gradient(-90deg,#342f78 0,#29255e 50%,#1b1947 100%"}}>
|
||||
return (
|
||||
<div style={{backgroundColor: props.color === "undefined" ? "inherit" : props.color}}>
|
||||
{loadedCheck}
|
||||
<div style={{backgroundColor: theme.palette.backgroundColor, }}>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import AppFramework, { usecases } from "../components/AppFramework.jsx";
|
||||
import {Link} from 'react-router-dom';
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import { Button, LinearProgress, Typography } from '@material-ui/core';
|
||||
import { Button, LinearProgress, Typography } from '@mui/material';
|
||||
|
||||
export const securityFramework = [
|
||||
{
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState } from "react";
|
||||
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
const LoginDialog = (props) => {
|
||||
const {
|
||||
classes,
|
||||
onClose,
|
||||
open,
|
||||
globalUrl,
|
||||
isLoggedIn,
|
||||
setIsLoggedIn,
|
||||
...other
|
||||
} = props;
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
//const [selectedValue, setSelectedValue] = useState(false);
|
||||
|
||||
// Used to swap from login to register. True = login, false = register
|
||||
const [loginCheck, setLoginCheck] = useState(true);
|
||||
|
||||
// Error messages etc
|
||||
const [loginInfo, setLoginInfo] = useState("");
|
||||
|
||||
const handleValidateForm = () => {
|
||||
return username.length > 1 && password.length > 8;
|
||||
};
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Just use this one?
|
||||
var data =
|
||||
'{"username": "' + username + '", "password": "' + password + '"}';
|
||||
var baseurl = globalUrl;
|
||||
if (loginCheck) {
|
||||
var url = baseurl + "/login";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
//console.log(e)
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful login :)");
|
||||
onClose();
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in userdata");
|
||||
});
|
||||
} else {
|
||||
url = baseurl + "/register";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful register :)");
|
||||
onClose();
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in userdata");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeUser = (e) => {
|
||||
setUsername(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass = (e) => {
|
||||
setPassword(e.target.value);
|
||||
};
|
||||
|
||||
const onClickRegister = () => {
|
||||
setLoginCheck(!loginCheck);
|
||||
};
|
||||
|
||||
//var loginChange = loginCheck ? (<div><p onClick={setLoginCheck(false)}>Want to register? Click here.</p></div>) : (<div><p onClick={setLoginCheck(true)}>Go back to login? Click here.</p></div>);
|
||||
var formtitle = loginCheck ? <div>Login</div> : <div>Register</div>;
|
||||
var formButton = loginCheck ? (
|
||||
<div>Click to Register</div>
|
||||
) : (
|
||||
<div>Click to Login</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog modal open={open} onClose={onClose} {...other}>
|
||||
<DialogTitle>{formtitle}</DialogTitle>
|
||||
<form onSubmit={onSubmit} style={{ margin: "15px 15px 15px 15px" }}>
|
||||
Username
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
id="standard-required"
|
||||
autoComplete="username"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangeUser}
|
||||
/>
|
||||
</div>
|
||||
Password
|
||||
<div>
|
||||
<TextField
|
||||
id="outlined-password-input"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: "15px" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
style={{ flex: "1", marginRight: "5px" }}
|
||||
disabled={!handleValidateForm()}
|
||||
>
|
||||
SUBMIT
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
{loginInfo}
|
||||
</form>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
onClick={onClickRegister}
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
>
|
||||
{formButton}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginDialog;
|
||||
@@ -1,213 +0,0 @@
|
||||
import React, { useState, useRef, useImperativeHandle } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Menu, { MenuProps } from "@material-ui/core/Menu";
|
||||
import MenuItem, { MenuItemProps } from "@material-ui/core/MenuItem";
|
||||
import ArrowRight from "@material-ui/icons/ArrowRight";
|
||||
import clsx from "clsx";
|
||||
|
||||
//<MenuItemProps, 'button'>
|
||||
|
||||
//export interface NestedMenuItemProps {
|
||||
// /**
|
||||
// * Open state of parent `<Menu />`, used to close decendent menus when the
|
||||
// * root menu is closed.
|
||||
// */
|
||||
// parentMenuOpen: boolean;
|
||||
// /**
|
||||
// * Component for the container element.
|
||||
// * @default 'div'
|
||||
// */
|
||||
// component: React.ElementType;
|
||||
// /**
|
||||
// * Effectively becomes the `children` prop passed to the `<MenuItem/>`
|
||||
// * element.
|
||||
// */
|
||||
// label: React.ReactNode;
|
||||
// /**
|
||||
// * @default <ArrowRight />
|
||||
// */
|
||||
// rightIcon: React.ReactNode;
|
||||
// /**
|
||||
// * Props passed to container element.
|
||||
// */
|
||||
// ContainerProps: React.HTMLAttributes;
|
||||
// //<HTMLElement> &React.RefAttributes<HTMLElement | null>
|
||||
// /**
|
||||
// * Props passed to sub `<Menu/>` element
|
||||
// */
|
||||
// MenuProps: Omit<MenuProps, 'children'>;
|
||||
// /**
|
||||
// * @see https://material-ui.com/api/list-item/
|
||||
// */
|
||||
// button: true;
|
||||
//}
|
||||
|
||||
const TRANSPARENT = "rgba(0,0,0,0)";
|
||||
const useMenuItemStyles = makeStyles((theme) => ({
|
||||
root: (props: any) => ({
|
||||
backgroundColor: props.open ? theme.palette.action.hover : TRANSPARENT,
|
||||
}),
|
||||
}));
|
||||
|
||||
/**
|
||||
* Use as a drop-in replacement for `<MenuItem>` when you need to add cascading
|
||||
* menu elements as children to this component.
|
||||
*/
|
||||
//const NestedMenuItem = React.forwardRef<NestedMenuItemProps>(
|
||||
const NestedMenuItem = (props, ref) => {
|
||||
console.log(props, ref);
|
||||
//function NestedMenuItem(props, ref) {
|
||||
const {
|
||||
parentMenuOpen,
|
||||
component = "div",
|
||||
label,
|
||||
rightIcon = <ArrowRight />,
|
||||
children,
|
||||
className,
|
||||
tabIndex: tabIndexProp,
|
||||
MenuProps = {},
|
||||
ContainerProps: ContainerPropsProp = {},
|
||||
...MenuItemProps
|
||||
} = props;
|
||||
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false);
|
||||
|
||||
const { ref: containerRefProp, ...ContainerProps } = ContainerPropsProp;
|
||||
|
||||
const menuItemRef = useRef < HTMLLIElement > null;
|
||||
useImperativeHandle(ref, () => menuItemRef.current);
|
||||
const containerRef = useRef < HTMLDivElement > null;
|
||||
useImperativeHandle(containerRefProp, () => containerRef.current);
|
||||
const menuContainerRef = useRef < HTMLDivElement > null;
|
||||
|
||||
console.log(
|
||||
"PAST THIS: ",
|
||||
containerRefProp,
|
||||
menuItemRef,
|
||||
containerRef,
|
||||
menuContainerRef,
|
||||
ContainerProps
|
||||
);
|
||||
|
||||
const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(true);
|
||||
|
||||
if (ContainerProps?.onMouseEnter) {
|
||||
ContainerProps.onMouseEnter(event);
|
||||
}
|
||||
};
|
||||
const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(false);
|
||||
|
||||
if (ContainerProps?.onMouseLeave) {
|
||||
ContainerProps.onMouseLeave(event);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if any immediate children are active
|
||||
const isSubmenuFocused = () => {
|
||||
const active = containerRef.current?.ownerDocument?.activeElement;
|
||||
for (const child of menuContainerRef.current?.children ?? []) {
|
||||
if (child === active) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
|
||||
if (event.target === containerRef.current) {
|
||||
setIsSubMenuOpen(true);
|
||||
}
|
||||
|
||||
if (ContainerProps?.onFocus) {
|
||||
ContainerProps.onFocus(event);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === "Escape") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSubmenuFocused()) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
const active = containerRef.current?.ownerDocument?.activeElement;
|
||||
|
||||
if (event.key === "ArrowLeft" && isSubmenuFocused()) {
|
||||
containerRef.current?.focus();
|
||||
}
|
||||
|
||||
if (
|
||||
event.key === "ArrowRight" &&
|
||||
event.target === containerRef.current &&
|
||||
event.target === active
|
||||
) {
|
||||
console.log("MENU: ", menuContainerRef);
|
||||
const firstChild = menuContainerRef.current.children[0];
|
||||
console.log("FIRST: ", firstChild);
|
||||
firstChild.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const open = isSubMenuOpen && parentMenuOpen;
|
||||
const menuItemClasses = useMenuItemStyles({ open });
|
||||
|
||||
// Root element must have a `tabIndex` attribute for keyboard navigation
|
||||
let tabIndex;
|
||||
if (!props.disabled) {
|
||||
tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1;
|
||||
}
|
||||
|
||||
console.log("PAST 2! ", tabIndex);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...ContainerProps}
|
||||
ref={containerRef}
|
||||
onFocus={handleFocus}
|
||||
tabIndex={tabIndex}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<MenuItem
|
||||
{...MenuItemProps}
|
||||
className={clsx(menuItemClasses.root, className)}
|
||||
ref={menuItemRef}
|
||||
>
|
||||
{label}
|
||||
{rightIcon}
|
||||
</MenuItem>
|
||||
<Menu
|
||||
// Set pointer events to 'none' to prevent the invisible Popover div
|
||||
// from capturing events for clicks and hovers
|
||||
style={{ pointerEvents: "none" }}
|
||||
anchorEl={menuItemRef.current}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
open={open}
|
||||
autoFocus={false}
|
||||
disableAutoFocus
|
||||
disableEnforceFocus
|
||||
onClose={() => {
|
||||
setIsSubMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<div ref={menuContainerRef} style={{ pointerEvents: "auto" }}>
|
||||
{children}
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NestedMenuItem;
|
||||
@@ -1,202 +0,0 @@
|
||||
import React, {useState, useRef, useImperativeHandle} from 'react'
|
||||
import {makeStyles} from '@material-ui/core/styles'
|
||||
import Menu, {MenuProps} from '@material-ui/core/Menu'
|
||||
import MenuItem, {MenuItemProps} from '@material-ui/core/MenuItem'
|
||||
import ArrowRight from '@material-ui/icons/ArrowRight'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export interface NestedMenuItemProps extends Omit<MenuItemProps, 'button'> {
|
||||
/**
|
||||
* Open state of parent `<Menu />`, used to close decendent menus when the
|
||||
* root menu is closed.
|
||||
*/
|
||||
parentMenuOpen: boolean
|
||||
/**
|
||||
* Component for the container element.
|
||||
* @default 'div'
|
||||
*/
|
||||
component?: React.ElementType
|
||||
/**
|
||||
* Effectively becomes the `children` prop passed to the `<MenuItem/>`
|
||||
* element.
|
||||
*/
|
||||
label?: React.ReactNode
|
||||
/**
|
||||
* @default <ArrowRight />
|
||||
*/
|
||||
rightIcon?: React.ReactNode
|
||||
/**
|
||||
* Props passed to container element.
|
||||
*/
|
||||
ContainerProps?: React.HTMLAttributes<HTMLElement> &
|
||||
React.RefAttributes<HTMLElement | null>
|
||||
/**
|
||||
* Props passed to sub `<Menu/>` element
|
||||
*/
|
||||
MenuProps?: Omit<MenuProps, 'children'>
|
||||
/**
|
||||
* @see https://material-ui.com/api/list-item/
|
||||
*/
|
||||
button?: true | undefined
|
||||
}
|
||||
|
||||
const TRANSPARENT = 'rgba(0,0,0,0)'
|
||||
const useMenuItemStyles = makeStyles((theme) => ({
|
||||
root: (props: any) => ({
|
||||
backgroundColor: props.open ? theme.palette.action.hover : TRANSPARENT
|
||||
})
|
||||
}))
|
||||
|
||||
/**
|
||||
* Use as a drop-in replacement for `<MenuItem>` when you need to add cascading
|
||||
* menu elements as children to this component.
|
||||
*/
|
||||
const NestedMenuItem = React.forwardRef<
|
||||
HTMLLIElement | null,
|
||||
NestedMenuItemProps
|
||||
>(function NestedMenuItem(props, ref) {
|
||||
const {
|
||||
parentMenuOpen,
|
||||
component = 'div',
|
||||
label,
|
||||
rightIcon = <ArrowRight />,
|
||||
children,
|
||||
className,
|
||||
tabIndex: tabIndexProp,
|
||||
MenuProps = {},
|
||||
ContainerProps: ContainerPropsProp = {},
|
||||
...MenuItemProps
|
||||
} = props
|
||||
|
||||
const {ref: containerRefProp, ...ContainerProps} = ContainerPropsProp
|
||||
|
||||
const menuItemRef = useRef<HTMLLIElement>(null)
|
||||
useImperativeHandle(ref, () => menuItemRef.current)
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
useImperativeHandle(containerRefProp, () => containerRef.current)
|
||||
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
|
||||
|
||||
const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(true)
|
||||
|
||||
if (ContainerProps?.onMouseEnter) {
|
||||
ContainerProps.onMouseEnter(event)
|
||||
}
|
||||
}
|
||||
const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setIsSubMenuOpen(false)
|
||||
|
||||
if (ContainerProps?.onMouseLeave) {
|
||||
ContainerProps.onMouseLeave(event)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any immediate children are active
|
||||
const isSubmenuFocused = () => {
|
||||
const active = containerRef.current?.ownerDocument?.activeElement
|
||||
for (const child of menuContainerRef.current?.children ?? []) {
|
||||
if (child === active) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
|
||||
if (event.target === containerRef.current) {
|
||||
setIsSubMenuOpen(true)
|
||||
}
|
||||
|
||||
if (ContainerProps?.onFocus) {
|
||||
ContainerProps.onFocus(event)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === 'Escape') {
|
||||
return
|
||||
}
|
||||
|
||||
if (isSubmenuFocused()) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
const active = containerRef.current?.ownerDocument?.activeElement
|
||||
|
||||
if (event.key === 'ArrowLeft' && isSubmenuFocused()) {
|
||||
containerRef.current?.focus()
|
||||
}
|
||||
|
||||
if (
|
||||
event.key === 'ArrowRight' &&
|
||||
event.target === containerRef.current &&
|
||||
event.target === active
|
||||
) {
|
||||
const firstChild = menuContainerRef.current?.children[0] as
|
||||
| HTMLElement
|
||||
| undefined
|
||||
firstChild?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
const open = isSubMenuOpen && parentMenuOpen
|
||||
const menuItemClasses = useMenuItemStyles({open})
|
||||
|
||||
// Root element must have a `tabIndex` attribute for keyboard navigation
|
||||
let tabIndex
|
||||
if (!props.disabled) {
|
||||
tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
{...ContainerProps}
|
||||
ref={containerRef}
|
||||
onFocus={handleFocus}
|
||||
tabIndex={tabIndex}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<MenuItem
|
||||
{...MenuItemProps}
|
||||
className={clsx(menuItemClasses.root, className)}
|
||||
ref={menuItemRef}
|
||||
>
|
||||
{label}
|
||||
{rightIcon}
|
||||
</MenuItem>
|
||||
<Menu
|
||||
// Set pointer events to 'none' to prevent the invisible Popover div
|
||||
// from capturing events for clicks and hovers
|
||||
style={{pointerEvents: 'none'}}
|
||||
anchorEl={menuItemRef.current}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right'
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left'
|
||||
}}
|
||||
open={open}
|
||||
autoFocus={false}
|
||||
disableAutoFocus
|
||||
disableEnforceFocus
|
||||
onClose={() => {
|
||||
setIsSubMenuOpen(false)
|
||||
}}
|
||||
>
|
||||
<div ref={menuContainerRef} style={{pointerEvents: 'auto'}}>
|
||||
{children}
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default NestedMenuItem
|
||||
@@ -1,102 +1,106 @@
|
||||
import React, {useState} from 'react';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import {TextField, Typography, Button} from '@material-ui/core';
|
||||
|
||||
const Newsletter = (props) => {
|
||||
const { globalUrl, } = props;
|
||||
|
||||
const theme = useTheme();
|
||||
const [email, setEmail] = useState("");
|
||||
const [msg, setMsg] = useState("");
|
||||
const [buttonActive, setButtonActive] = useState(true);
|
||||
const buttonStyle = {minWidth: 300, borderRadius: 30, height: 60, width: 140, margin: isMobile ? "15px auto 15px auto" : "20px 20px 20px 10px", fontSize: 18,}
|
||||
|
||||
const newsletterSignup = (inemail) => {
|
||||
if (inemail.length < 4) {
|
||||
setMsg("Invalid email")
|
||||
setButtonActive(true)
|
||||
return
|
||||
}
|
||||
|
||||
setButtonActive(false)
|
||||
const data = {"email": inemail}
|
||||
const url = globalUrl+'/api/v1/functions/newsletter_signup'
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(responseJson => {
|
||||
setButtonActive(true)
|
||||
setMsg(responseJson["reason"])
|
||||
if (responseJson["success"] === false) {
|
||||
} else {
|
||||
setEmail("")
|
||||
}
|
||||
}),
|
||||
)
|
||||
.catch(error => {
|
||||
setMsg("Something went wrong: ", error.toString())
|
||||
setButtonActive(true)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", color: "white", textAlign: "center",}}>
|
||||
<Typography variant="h4" style={{marginTop: 35,}}>
|
||||
Security Automation Newsletter
|
||||
</Typography>
|
||||
<Typography variant="h6" style={{color: "#7d7f82", marginTop: 20, }}>
|
||||
Defensive security is 99% noise. Join us to sift through it.
|
||||
</Typography>
|
||||
<div style={{}}>
|
||||
<TextField
|
||||
style={{minWidth: isMobile ? "90%" : 450, backgroundColor: theme.palette.inputColor, marginTop: 20, borderRadius: 10, }}
|
||||
InputProps={{
|
||||
style:{
|
||||
borderRadius: 10,
|
||||
height: 60,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value)
|
||||
}}
|
||||
placeholder="Your email"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={buttonStyle}
|
||||
disabled={!buttonActive}
|
||||
onClick={() => {
|
||||
newsletterSignup(email)
|
||||
ReactGA.event({
|
||||
category: "newsletter",
|
||||
action: `signup_click`,
|
||||
label: "",
|
||||
})
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</Button>
|
||||
<div/>
|
||||
{msg}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default Newsletter;
|
||||
import React, {useState} from 'react';
|
||||
import { useTheme } from '@mui/styles';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import {
|
||||
TextField,
|
||||
Typography,
|
||||
Button
|
||||
} from '@mui/material';
|
||||
|
||||
const Newsletter = (props) => {
|
||||
const { globalUrl, } = props;
|
||||
|
||||
const theme = useTheme();
|
||||
const [email, setEmail] = useState("");
|
||||
const [msg, setMsg] = useState("");
|
||||
const [buttonActive, setButtonActive] = useState(true);
|
||||
const buttonStyle = {minWidth: 300, borderRadius: 30, height: 60, width: 140, margin: isMobile ? "15px auto 15px auto" : "20px 20px 20px 10px", fontSize: 18,}
|
||||
|
||||
const newsletterSignup = (inemail) => {
|
||||
if (inemail.length < 4) {
|
||||
setMsg("Invalid email")
|
||||
setButtonActive(true)
|
||||
return
|
||||
}
|
||||
|
||||
setButtonActive(false)
|
||||
const data = {"email": inemail}
|
||||
const url = globalUrl+'/api/v1/functions/newsletter_signup'
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(responseJson => {
|
||||
setButtonActive(true)
|
||||
setMsg(responseJson["reason"])
|
||||
if (responseJson["success"] === false) {
|
||||
} else {
|
||||
setEmail("")
|
||||
}
|
||||
}),
|
||||
)
|
||||
.catch(error => {
|
||||
setMsg("Something went wrong: ", error.toString())
|
||||
setButtonActive(true)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", color: "white", textAlign: "center",}}>
|
||||
<Typography variant="h4" style={{marginTop: 35,}}>
|
||||
Security Automation Newsletter
|
||||
</Typography>
|
||||
<Typography variant="h6" style={{color: "#7d7f82", marginTop: 20, }}>
|
||||
Defensive security is 99% noise. Join us to sift through it.
|
||||
</Typography>
|
||||
<div style={{}}>
|
||||
<TextField
|
||||
style={{minWidth: isMobile ? "90%" : 450, backgroundColor: theme.palette.inputColor, marginTop: 20, borderRadius: 10, }}
|
||||
InputProps={{
|
||||
style:{
|
||||
borderRadius: 10,
|
||||
height: 60,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value)
|
||||
}}
|
||||
placeholder="Your email"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={buttonStyle}
|
||||
disabled={!buttonActive}
|
||||
onClick={() => {
|
||||
newsletterSignup(email)
|
||||
ReactGA.event({
|
||||
category: "newsletter",
|
||||
action: `signup_click`,
|
||||
label: "",
|
||||
})
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</Button>
|
||||
<div/>
|
||||
{msg}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default Newsletter;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import theme from '../theme.jsx';
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
@@ -38,7 +38,8 @@ import {
|
||||
CircularProgress,
|
||||
Switch,
|
||||
Fade,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
LockOpen as LockOpenIcon,
|
||||
SupervisorAccount as SupervisorAccountIcon,
|
||||
@@ -71,6 +72,7 @@ const registeredApps = [
|
||||
"todoist",
|
||||
"microsoft_sentinel",
|
||||
"microsoft_365_defender",
|
||||
"google_chat",
|
||||
"google_sheets",
|
||||
"google_drive",
|
||||
"google_disk",
|
||||
@@ -98,7 +100,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
} = props;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
|
||||
//const [update, setUpdate] = React.useState("|")
|
||||
const [defaultConfigSet, setDefaultConfigSet] = React.useState(
|
||||
@@ -216,7 +218,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
"31cb4c84-658e-43d5-ae84-22c9142e967a",
|
||||
"",
|
||||
"https://graph.microsoft.com",
|
||||
["ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "Chat.Create", "Chat.ReadWrite", "Chat.Read", "offline_access"],
|
||||
["ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "Chat.Create", "Chat.ReadWrite", "Chat.Read", "offline_access", "Team.ReadBasic.All"],
|
||||
admin_consent,
|
||||
)
|
||||
} else if (selectedApp.name.toLowerCase().includes("todoist")) {
|
||||
@@ -261,6 +263,16 @@ const AuthenticationOauth2 = (props) => {
|
||||
admin_consent,
|
||||
"consent",
|
||||
)
|
||||
} else if (selectedApp.name.toLowerCase().includes("google_chat") || selectedApp.name.toLowerCase().includes("google_hangout")) {
|
||||
handleOauth2Request(
|
||||
"253565968129-6pij4g6ojim4gpum0h9m9u3bc357qsq7.apps.googleusercontent.com",
|
||||
"",
|
||||
"https://www.googleapis.com",
|
||||
["https://www.googleapis.com/auth/chat.messages",],
|
||||
admin_consent,
|
||||
"consent",
|
||||
)
|
||||
|
||||
} else if (selectedApp.name.toLowerCase().includes("jira_service_desk") || selectedApp.name.toLowerCase().includes("jira") || selectedApp.name.toLowerCase().includes("jira_service_management")) {
|
||||
handleOauth2Request(
|
||||
"AI02egeCQh1Zskm1QAJaaR6dzjR97V2F",
|
||||
@@ -411,7 +423,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
//}
|
||||
//while(open === true)
|
||||
} catch (e) {
|
||||
alert.error(
|
||||
toast(
|
||||
"Failed authentication - probably bad credentials. Try again"
|
||||
);
|
||||
setButtonClicked(false);
|
||||
@@ -440,7 +452,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
console.log("NEW AUTH: ", authenticationOption);
|
||||
if (authenticationOption.label.length === 0) {
|
||||
authenticationOption.label = `Auth for ${selectedApp.name}`;
|
||||
//alert.info("Label can't be empty")
|
||||
//toast("Label can't be empty")
|
||||
//return
|
||||
}
|
||||
|
||||
@@ -468,7 +480,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
selectedApp.authentication.parameters[key].name
|
||||
] = "false";
|
||||
} else {
|
||||
alert.info(
|
||||
toast(
|
||||
"Field " + selectedApp.authentication.parameters[key].name.replace("_basic", "", -1).replace("_", " ", -1) + " can't be empty"
|
||||
|
||||
);
|
||||
@@ -595,7 +607,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
<div>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "white" }}>
|
||||
Authentication for {selectedApp.name}
|
||||
Authenticate {selectedApp.name.replaceAll("_", " ")}
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
@@ -662,11 +674,6 @@ const AuthenticationOauth2 = (props) => {
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
height: 50,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
@@ -751,11 +758,6 @@ const AuthenticationOauth2 = (props) => {
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
height: 50,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
@@ -792,11 +794,6 @@ const AuthenticationOauth2 = (props) => {
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
height: "50px",
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
@@ -815,11 +812,6 @@ const AuthenticationOauth2 = (props) => {
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
height: "50px",
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
@@ -836,6 +828,7 @@ const AuthenticationOauth2 = (props) => {
|
||||
Scopes
|
||||
<Select
|
||||
multiple
|
||||
underline={false}
|
||||
value={selectedScopes}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,17 +1,22 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import theme from "../theme.jsx";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import Tooltip from "@material-ui/core/Tooltip";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { useAlert } from "react-alert";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import ExpandLessIcon from "@material-ui/icons/ExpandLess";
|
||||
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
||||
import SaveIcon from "@material-ui/icons/Save";
|
||||
import {
|
||||
Tooltip,
|
||||
Grid,
|
||||
Button,
|
||||
TextField,
|
||||
Typography,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Save as SaveIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
@@ -33,8 +38,6 @@ const OrgHeader = (props) => {
|
||||
handleEditOrg,
|
||||
} = props;
|
||||
|
||||
const theme = useTheme();
|
||||
const alert = useAlert();
|
||||
const classes = useStyles();
|
||||
|
||||
var upload = "";
|
||||
@@ -98,7 +101,7 @@ const OrgHeader = (props) => {
|
||||
<Tooltip title="Save any unsaved data" placement="bottom">
|
||||
<Button
|
||||
style={{ width: 150, height: 55, flex: 1 }}
|
||||
variant="contained"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
disabled={
|
||||
userdata === undefined || userdata === null || userdata.admin !== "true"
|
||||
@@ -205,13 +208,13 @@ const OrgHeader = (props) => {
|
||||
const invalid = ["#", ":", "."];
|
||||
for (var key in invalid) {
|
||||
if (e.target.value.includes(invalid[key])) {
|
||||
alert.error("Can't use " + invalid[key] + " in name");
|
||||
toast("Can't use " + invalid[key] + " in name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.target.value.length > 100) {
|
||||
alert.error("Choose a shorter name.");
|
||||
toast("Choose a shorter name.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import { useAlert } from "react-alert";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import theme from '../theme.jsx';
|
||||
import { toast } from "react-toastify"
|
||||
|
||||
import {
|
||||
FormControl,
|
||||
@@ -23,12 +23,15 @@ import {
|
||||
Tabs,
|
||||
Tab,
|
||||
Grid,
|
||||
} from "@material-ui/core";
|
||||
IconButton,
|
||||
Autocomplete,
|
||||
} from "@mui/material";
|
||||
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import ExpandLessIcon from "@material-ui/icons/ExpandLess";
|
||||
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
||||
import SaveIcon from "@material-ui/icons/Save";
|
||||
import {
|
||||
ExpandLess as ExpandLessIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
Save as SaveIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
@@ -46,8 +49,6 @@ const OrgHeaderexpanded = (props) => {
|
||||
adminTab,
|
||||
} = props;
|
||||
|
||||
const theme = useTheme();
|
||||
const alert = useAlert();
|
||||
const classes = useStyles();
|
||||
const defaultBranch = "master";
|
||||
|
||||
@@ -154,6 +155,47 @@ const OrgHeaderexpanded = (props) => {
|
||||
: selectedOrganization.sso_config.openid_token
|
||||
)
|
||||
|
||||
const [workflows, setWorkflows] = React.useState([])
|
||||
const [workflow, setWorkflow] = React.useState({})
|
||||
|
||||
const getAvailableWorkflows = (trigger_index) => {
|
||||
fetch(globalUrl + "/api/v1/workflows", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflows :O!");
|
||||
return;
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson !== undefined) {
|
||||
setWorkflows(responseJson)
|
||||
|
||||
if (selectedOrganization.defaults !== undefined && selectedOrganization.defaults.notification_workflow !== undefined) {
|
||||
|
||||
const workflow = responseJson.find((workflow) => workflow.id === selectedOrganization.defaults.notification_workflow)
|
||||
if (workflow !== undefined && workflow !== null) {
|
||||
setWorkflow(workflow)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error getting workflows: " + error);
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getAvailableWorkflows()
|
||||
}, [])
|
||||
|
||||
const handleEditOrg = (
|
||||
name,
|
||||
description,
|
||||
@@ -187,19 +229,32 @@ const OrgHeaderexpanded = (props) => {
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
alert.error("Failed updating org: ", responseJson.reason);
|
||||
toast("Failed updating org: ", responseJson.reason);
|
||||
} else {
|
||||
alert.success("Successfully edited org!");
|
||||
toast("Successfully edited org!");
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
alert.error("Err: " + error.toString());
|
||||
toast("Err: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const handleWorkflowSelectionUpdate = (e, isUserinput) => {
|
||||
if (e.target.value === undefined || e.target.value === null || e.target.value.id === undefined) {
|
||||
console.log("Returning as there's no id")
|
||||
return null
|
||||
}
|
||||
|
||||
setWorkflow(e.target.value)
|
||||
setNotificationWorkflow(e.target.value.id)
|
||||
toast("Updated notification workflow. Don't forget to save!")
|
||||
}
|
||||
|
||||
const orgSaveButton = (
|
||||
<Tooltip title="Save any unsaved data" placement="bottom">
|
||||
<div>
|
||||
<Button
|
||||
style={{ width: 150, height: 55, flex: 1 }}
|
||||
variant="contained"
|
||||
@@ -236,6 +291,7 @@ const OrgHeaderexpanded = (props) => {
|
||||
>
|
||||
<SaveIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -244,34 +300,149 @@ const OrgHeaderexpanded = (props) => {
|
||||
<Grid container spacing={3} style={{ textAlign: "left" }}>
|
||||
<Grid item xs={12} style={{}}>
|
||||
<span>
|
||||
<Typography>Notification Workflow ID</Typography>
|
||||
<TextField
|
||||
required
|
||||
style={{
|
||||
flex: "1",
|
||||
marginTop: "5px",
|
||||
marginRight: "15px",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
placeholder="ID of the workflow to receive notifications"
|
||||
value={notificationWorkflow}
|
||||
onChange={(e) => {
|
||||
setNotificationWorkflow(e.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Typography>Notification Workflow</Typography>
|
||||
{/*
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Add a Workflow that receives notifications from Shuffle when an error occurs in one of your workflows
|
||||
</Typography>
|
||||
*/}
|
||||
<div style={{display: "flex", flexDirection: "row", alignItems: "center"}}>
|
||||
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
|
||||
<Autocomplete
|
||||
id="notification_workflow_search"
|
||||
autoHighlight
|
||||
freeSolo
|
||||
//autoSelect
|
||||
value={workflow}
|
||||
classes={{ inputRoot: classes.inputRoot }}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
getOptionLabel={(option) => {
|
||||
if (
|
||||
option === undefined ||
|
||||
option === null ||
|
||||
option.name === undefined ||
|
||||
option.name === null
|
||||
) {
|
||||
return "No Workflow Selected";
|
||||
}
|
||||
|
||||
const newname = (
|
||||
option.name.charAt(0).toUpperCase() + option.name.substring(1)
|
||||
).replaceAll("_", " ");
|
||||
return newname;
|
||||
}}
|
||||
options={workflows}
|
||||
fullWidth
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
height: 50,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
console.log("Found value: ", newValue)
|
||||
|
||||
var parsedinput = { target: { value: newValue } }
|
||||
|
||||
// For variables
|
||||
if (typeof newValue === 'string' && newValue.startsWith("$")) {
|
||||
parsedinput = {
|
||||
target: {
|
||||
value: {
|
||||
"name": newValue,
|
||||
"id": newValue,
|
||||
"actions": [],
|
||||
"triggers": [],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleWorkflowSelectionUpdate(parsedinput)
|
||||
}}
|
||||
renderOption={(props, data, state) => {
|
||||
if (data.id === workflow.id) {
|
||||
data = workflow;
|
||||
}
|
||||
|
||||
return (
|
||||
<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, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {data.name}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<MenuItem
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
color: data.id === workflow.id ? "red" : "white",
|
||||
}}
|
||||
value={data}
|
||||
onClick={(e) => {
|
||||
var parsedinput = { target: { value: data } }
|
||||
handleWorkflowSelectionUpdate(parsedinput)
|
||||
}}
|
||||
>
|
||||
{data.name}
|
||||
</MenuItem>
|
||||
</Tooltip>
|
||||
)
|
||||
}}
|
||||
renderInput={(params) => {
|
||||
return (
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find a notification workflow"
|
||||
variant="outlined"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
:
|
||||
<TextField
|
||||
required
|
||||
style={{
|
||||
flex: "1",
|
||||
marginTop: "5px",
|
||||
marginRight: "15px",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
placeholder="ID of the workflow to receive notifications"
|
||||
value={notificationWorkflow}
|
||||
onChange={(e) => {
|
||||
setNotificationWorkflow(e.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}
|
||||
<div style={{minWidth: 150, maxWidth: 150, marginTop: 5, marginLeft: 10, }}>
|
||||
{orgSaveButton}
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</Grid>
|
||||
<Grid item xs={12} style={{}}>
|
||||
@@ -699,4 +870,4 @@ const OrgHeaderexpanded = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default OrgHeaderexpanded;
|
||||
export default OrgHeaderexpanded;
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, {useState, useEffect, useLayoutEffect} from 'react';
|
||||
import Draggable from "react-draggable";
|
||||
import {
|
||||
Paper
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
const PaperComponent = (props) => {
|
||||
return (
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,13 @@ import {
|
||||
Grid,
|
||||
Card,
|
||||
Switch,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import Priority from "../components/Priority.jsx";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
const Priorities = (props) => {
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, } = props;
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, checkLogin, setAdminTab, setCurTab, } = props;
|
||||
const [showDismissed, setShowDismissed] = React.useState(false);
|
||||
const [showRead, setShowRead] = React.useState(false);
|
||||
|
||||
@@ -60,6 +60,8 @@ const Priorities = (props) => {
|
||||
globalUrl={globalUrl}
|
||||
priority={priority}
|
||||
checkLogin={checkLogin}
|
||||
setAdminTab={setAdminTab}
|
||||
setCurTab={setCurTab}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import ReactGA from 'react-ga4';
|
||||
import theme from "../theme.jsx";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import { findSpecificApp } from "../components/AppFramework.jsx"
|
||||
import {
|
||||
Paper,
|
||||
Typography,
|
||||
@@ -9,65 +12,97 @@ import {
|
||||
Button,
|
||||
Grid,
|
||||
Card,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
// import magic wand icon from material ui icons
|
||||
import {
|
||||
AutoFixHigh as AutoFixHighIcon,
|
||||
ArrowForward as ArrowForwardIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
|
||||
const Priority = (props) => {
|
||||
const { globalUrl, userdata, serverside, priority, checkLogin, } = props;
|
||||
|
||||
const { globalUrl, userdata, serverside, priority, checkLogin, setAdminTab, setCurTab, appFramework, } = props;
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
let navigate = useNavigate();
|
||||
|
||||
var realigned = false
|
||||
let newdescription = priority.description
|
||||
const descsplit = priority.description.split("&")
|
||||
if (appFramework !== undefined && descsplit.length === 5 && priority.description.includes(":default")) {
|
||||
console.log("descsplit: ", descsplit)
|
||||
if (descsplit[1] === "") {
|
||||
const item = findSpecificApp(appFramework, descsplit[0])
|
||||
console.log("item: ", item)
|
||||
if (item !== null) {
|
||||
descsplit[1] = item.large_image
|
||||
descsplit[0] = descsplit[0].split(":")[0]
|
||||
}
|
||||
|
||||
realigned = true
|
||||
}
|
||||
|
||||
if (descsplit[3] === "") {
|
||||
const item = findSpecificApp(appFramework, descsplit[2])
|
||||
console.log("item: ", item)
|
||||
if (item !== null) {
|
||||
descsplit[3] = item.large_image
|
||||
descsplit[2] = descsplit[2].split(":")[0]
|
||||
}
|
||||
|
||||
realigned = true
|
||||
}
|
||||
|
||||
newdescription = descsplit.join("&")
|
||||
}
|
||||
|
||||
const changeRecommendation = (recommendation, action) => {
|
||||
const data = {
|
||||
action: action,
|
||||
name: recommendation.name,
|
||||
};
|
||||
const data = {
|
||||
action: action,
|
||||
name: recommendation.name,
|
||||
};
|
||||
|
||||
fetch(`${globalUrl}/api/v1/recommendations/modify`, {
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
} else {
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === true) {
|
||||
if (checkLogin !== undefined) {
|
||||
checkLogin()
|
||||
}
|
||||
} else {
|
||||
if (responseJson.success === false && responseJson.reason !== undefined) {
|
||||
alert.error("Failed change recommendation: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed change recommendation");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.info("Failed dismissing alert. Please contact support@shuffler.io if this persists.");
|
||||
});
|
||||
fetch(`${globalUrl}/api/v1/recommendations/modify`, {
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
} else {
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === true) {
|
||||
if (checkLogin !== undefined) {
|
||||
checkLogin()
|
||||
}
|
||||
} else {
|
||||
if (responseJson.success === false && responseJson.reason !== undefined) {
|
||||
toast("Failed change recommendation: ", responseJson.reason)
|
||||
} else {
|
||||
toast("Failed change recommendation");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast("Failed dismissing alert. Please contact support@shuffler.io if this persists.");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", height: 70, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: 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}
|
||||
@@ -77,17 +112,17 @@ const Priority = (props) => {
|
||||
</span>
|
||||
{priority.type === "usecase" && priority.description.includes("&") ?
|
||||
<span style={{display: "flex", marginTop: 10, }}>
|
||||
<img src={priority.description.split("&")[1]} alt={priority.name} style={{height: 30, width: 30, marginRight: 5, borderRadius: theme.palette.borderRadius, marginRight: 10, }} />
|
||||
<img src={newdescription.split("&")[1]} alt={priority.name} style={{height: "auto", width: 30, marginRight: realigned ? -10 : 10, borderRadius: theme.palette.borderRadius, marginTop: realigned ? 5 : 0 }} />
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 3, }}>
|
||||
{priority.description.split("&")[0]}
|
||||
{newdescription.split("&")[0]}
|
||||
</Typography>
|
||||
|
||||
{priority.description.split("&").length > 3 ?
|
||||
{newdescription.split("&").length > 3 ?
|
||||
<span style={{display: "flex", }}>
|
||||
<ArrowForwardIcon style={{marginLeft: 15, marginRight: 15, }}/>
|
||||
<img src={priority.description.split("&")[3]} alt={priority.name+"2"} style={{height: 30, width: 30, borderRadius: theme.palette.borderRadius, marginRight: 10, }} />
|
||||
<img src={newdescription.split("&")[3]} alt={priority.name+"2"} style={{height: "auto", width: 30, marginRight: realigned ? -10 : 10, borderRadius: theme.palette.borderRadius, marginTop: realigned ? 5 : 0 }} />
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 3}}>
|
||||
{priority.description.split("&")[2]}
|
||||
{newdescription.split("&")[2]}
|
||||
</Typography>
|
||||
</span>
|
||||
: null}
|
||||
@@ -100,17 +135,30 @@ const Priority = (props) => {
|
||||
}
|
||||
</div>
|
||||
<div style={{flex: 1, display: "flex", marginLeft: 30, }}>
|
||||
<Button style={{height: 50, borderRadius: 25, marginTop: 8, width: 175, marginRight: 10, color: priority.active === false ? "white" : "black", backgroundColor: priority.active === false ? theme.palette.inputColor : "white", }} variant="contained" color="secondary" onClick={() => {
|
||||
/*
|
||||
ReactGA.event({
|
||||
category: "",
|
||||
action: `partner_${partner.name}_click`,
|
||||
label: "",
|
||||
})
|
||||
*/
|
||||
<Button style={{height: 50, borderRadius: 25, marginTop: 8, width: 175, marginRight: 10, color: priority.active === false ? "white" : "black", backgroundColor: priority.active === false ? theme.palette.inputColor : "rgba(255,255,255,0.8)", }} variant="contained" color="secondary" onClick={() => {
|
||||
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "recommendation",
|
||||
action: `click_${priority.name}`,
|
||||
label: "",
|
||||
})
|
||||
}
|
||||
|
||||
navigate(priority.url)
|
||||
|
||||
if (setAdminTab !== undefined && setCurTab !== undefined) {
|
||||
if (priority.description.toLowerCase().includes("notification workflow")) {
|
||||
setCurTab(0)
|
||||
setAdminTab(0)
|
||||
}
|
||||
|
||||
if (priority.description.toLowerCase().includes("hybrid shuffle")) {
|
||||
setCurTab(6)
|
||||
}
|
||||
}
|
||||
}}>
|
||||
explore
|
||||
Explore
|
||||
</Button>
|
||||
{priority.active === true ?
|
||||
<Button style={{borderRadius: 25, width: 100, height: 50, marginTop: 8, }} variant="text" color="secondary" onClick={() => {
|
||||
|
||||
@@ -2,6 +2,20 @@ import { useEffect } from "react";
|
||||
//import { withRouter } from "react-router-dom";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
export const removeQuery = (query) => {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
if (params[query] !== undefined) {
|
||||
delete params[query]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
const queryString = Object.keys(params).map(key => key + '=' + params[key]).join('&')
|
||||
const newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + queryString
|
||||
window.history.pushState({path:newurl},'',newurl)
|
||||
}
|
||||
|
||||
// ensures scrolling happens in the right way on different pages and when changing
|
||||
function ScrollToTop({ getUserNotifications, curpath, setCurpath, history }) {
|
||||
let location = useLocation();
|
||||
@@ -9,7 +23,7 @@ function ScrollToTop({ getUserNotifications, curpath, setCurpath, history }) {
|
||||
useEffect(() => {
|
||||
// Custom handler for certain scroll mechanics
|
||||
//
|
||||
console.log("OLD: ", curpath, "NeW: ", window.location.pathname)
|
||||
//console.log("OLD: ", curpath, "NeW: ", window.location.pathname)
|
||||
if (curpath === window.location.pathname && curpath === "/usecases") {
|
||||
} else {
|
||||
|
||||
|
||||
@@ -1,648 +0,0 @@
|
||||
import React, {useState, useEffect, useRef} from 'react';
|
||||
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
|
||||
import {
|
||||
Chip,
|
||||
IconButton,
|
||||
TextField,
|
||||
InputAdornment,
|
||||
List,
|
||||
Card,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Avatar,
|
||||
Typography,
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import {
|
||||
AvatarGroup,
|
||||
} from "@mui/material"
|
||||
|
||||
import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons'
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import aa from 'search-insights'
|
||||
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||
//import { InstantSearch, SearchBox, Hits, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||
|
||||
// https://www.algolia.com/doc/api-reference/widgets/search-box/react/
|
||||
const chipStyle = {
|
||||
backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",
|
||||
}
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const SearchField = props => {
|
||||
const { serverside, userdata } = props
|
||||
|
||||
const theme = useTheme();
|
||||
let navigate = useNavigate();
|
||||
const borderRadius = 3
|
||||
const node = useRef()
|
||||
const [searchOpen, setSearchOpen] = useState(false)
|
||||
const [oldPath, setOldPath] = useState("")
|
||||
|
||||
if (serverside === true) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (window !== undefined && window.location !== undefined && window.location.pathname === "/search") {
|
||||
return null
|
||||
}
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
|
||||
if (window.location.pathname !== oldPath) {
|
||||
setSearchOpen(false)
|
||||
setOldPath(window.location.pathname)
|
||||
}
|
||||
|
||||
//useEffect(() => {
|
||||
// if (searchOpen) {
|
||||
// var tarfield = document.getElementById("shuffle_search_field")
|
||||
// tarfield.focus()
|
||||
// }
|
||||
//}, searchOpen)
|
||||
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => {
|
||||
|
||||
/*
|
||||
endAdornment: (
|
||||
<InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => {
|
||||
event.preventDefault()
|
||||
}}>
|
||||
<CloseIcon style={{marginRight: 5,}} onClick={() => {
|
||||
setSearchOpen(false)
|
||||
}} />
|
||||
</InputAdornment>
|
||||
),
|
||||
*/
|
||||
|
||||
return (
|
||||
<form id="search_form" noValidate type="searchbox" action="" role="search" style={{margin: 0, }} onClick={() => {
|
||||
}}>
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
height: 50,
|
||||
margin: 0,
|
||||
},
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon style={{marginLeft: 5}}/>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
autoComplete='off'
|
||||
type="search"
|
||||
color="primary"
|
||||
placeholder="Find Public Apps, Workflows, Documentation and more"
|
||||
value={currentRefinement}
|
||||
id="shuffle_search_field"
|
||||
onClick={(event) => {
|
||||
if (!searchOpen) {
|
||||
setSearchOpen(true)
|
||||
setTimeout(() => {
|
||||
var tarfield = document.getElementById("shuffle_search_field")
|
||||
//console.log("TARFIELD: ", tarfield)
|
||||
tarfield.focus()
|
||||
}, 100)
|
||||
}
|
||||
}}
|
||||
onBlur={(event) => {
|
||||
setTimeout(() => {
|
||||
setSearchOpen(false)
|
||||
}, 500)
|
||||
}}
|
||||
onChange={(event) => {
|
||||
//if (event.currentTarget.value.length > 0 && !searchOpen) {
|
||||
// setSearchOpen(true)
|
||||
//}
|
||||
|
||||
refine(event.currentTarget.value)
|
||||
}}
|
||||
limit={5}
|
||||
/>
|
||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
const WorkflowHits = ({ hits }) => {
|
||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||
|
||||
var tmp = searchOpen
|
||||
if (!searchOpen) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||
const outerlistitemStyle = {
|
||||
width: "100%",
|
||||
overflowX: "hidden",
|
||||
overflowY: "hidden",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
}
|
||||
|
||||
if (hits.length > 4) {
|
||||
hits = hits.slice(0, 4)
|
||||
}
|
||||
|
||||
var type = "workflows"
|
||||
const baseImage = <PolymerIcon />
|
||||
|
||||
return (
|
||||
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: 405, height: 408, left: 75, boxShadows: "none",}}>
|
||||
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
||||
Workflows
|
||||
</Typography>
|
||||
|
||||
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
||||
{hits.length === 0 ?
|
||||
<ListItem style={outerlistitemStyle}>
|
||||
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||
<Avatar>
|
||||
<FolderIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={"No workflows found."}
|
||||
secondary={"Try a broader search term"}
|
||||
/>
|
||||
</ListItem>
|
||||
:
|
||||
hits.map((hit, index) => {
|
||||
const innerlistitemStyle = {
|
||||
width: positionInfo.width+35,
|
||||
overflowX: "hidden",
|
||||
overflowY: "hidden",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||
cursor: "pointer",
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
maxHeight: 75,
|
||||
minHeight: 75,
|
||||
maxWidth: 420,
|
||||
minWidth: "100%",
|
||||
}
|
||||
|
||||
const name = hit.name === undefined ?
|
||||
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
||||
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
||||
const secondaryText = hit.description !== undefined && hit.description !== null && hit.description.length > 3 ? hit.description.slice(0, 40)+"..." : ""
|
||||
const appGroup = hit.action_references === undefined || hit.action_references === null ? [] : hit.action_references
|
||||
const avatar = baseImage
|
||||
|
||||
var parsedUrl = isCloud ? `/workflows/${hit.objectID}` : `https://shuffler.io/workflows/${hit.objectID}`
|
||||
parsedUrl += `?queryID=${hit.__queryID}`
|
||||
|
||||
// <a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
return (
|
||||
<Link key={hit.objectID} to={{ pathname: parsedUrl }} rel="noopener noreferrer" style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||
//console.log("CLICK")
|
||||
setSearchOpen(true)
|
||||
|
||||
aa('init', {
|
||||
appId: searchClient.appId,
|
||||
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||
})
|
||||
|
||||
const timestamp = new Date().getTime()
|
||||
aa('sendEvents', [
|
||||
{
|
||||
eventType: 'click',
|
||||
eventName: 'Workflow Clicked',
|
||||
index: 'workflows',
|
||||
objectIDs: [hit.objectID],
|
||||
timestamp: timestamp,
|
||||
queryID: hit.__queryID,
|
||||
positions: [hit.__position],
|
||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||
}
|
||||
])
|
||||
|
||||
if (!isCloud) {
|
||||
event.preventDefault()
|
||||
window.open(parsedUrl, '_blank');
|
||||
}
|
||||
}}>
|
||||
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||
setMouseHoverIndex(index)
|
||||
}}>
|
||||
<ListItemAvatar>
|
||||
{avatar}
|
||||
</ListItemAvatar>
|
||||
<div style={{}}>
|
||||
<ListItemText
|
||||
primary={name}
|
||||
/>
|
||||
<AvatarGroup max={10} style={{flexDirection: "row", padding: 0, margin: 0, itemAlign: "left", textAlign: "left",}}>
|
||||
{appGroup.map((app, index) => {
|
||||
// Putting all this in secondary of ListItemText looked weird.
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
height: 24,
|
||||
width: 24,
|
||||
filter: "brightness(0.6)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/apps/"+app.id)
|
||||
}}
|
||||
>
|
||||
<Tooltip color="primary" title={app.name} placement="bottom">
|
||||
<Avatar alt={app.name} src={app.image_url} style={{width: 24, height: 24}}/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</AvatarGroup>
|
||||
</div>
|
||||
{/*
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
*/}
|
||||
</ListItem>
|
||||
</Link>
|
||||
)})
|
||||
}
|
||||
</List>
|
||||
{/*
|
||||
<span style={{display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
||||
<Link to="/search" style={{textDecoration: "none", color: "#f85a3e"}}>
|
||||
<Typography variant="body2" style={{}}>
|
||||
See all workflows
|
||||
</Typography>
|
||||
</Link>
|
||||
</span>
|
||||
*/}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const AppHits = ({ hits }) => {
|
||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||
|
||||
var tmp = searchOpen
|
||||
if (!searchOpen) {
|
||||
return null
|
||||
}
|
||||
|
||||
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||
const outerlistitemStyle = {
|
||||
width: "100%",
|
||||
overflowX: "hidden",
|
||||
overflowY: "hidden",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
}
|
||||
|
||||
if (hits.length > 4) {
|
||||
hits = hits.slice(0, 4)
|
||||
}
|
||||
|
||||
var type = "app"
|
||||
const baseImage = <LibraryBooksIcon />
|
||||
|
||||
return (
|
||||
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, width: 1155, height: 408, left: -305, boxShadows: "none",}}>
|
||||
<IconButton style={{zIndex: 5000, position: "absolute", right: 14, color: "grey"}} onClick={() => {
|
||||
setSearchOpen(false)
|
||||
}}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
||||
Apps
|
||||
</Typography>
|
||||
|
||||
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
||||
{hits.length === 0 ?
|
||||
<ListItem style={outerlistitemStyle}>
|
||||
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||
<Avatar>
|
||||
<FolderIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={"No apps found."}
|
||||
secondary={"Try a broader search term"}
|
||||
/>
|
||||
</ListItem>
|
||||
:
|
||||
hits.map((hit, index) => {
|
||||
const innerlistitemStyle = {
|
||||
width: positionInfo.width+35,
|
||||
overflowX: "hidden",
|
||||
overflowY: "hidden",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||
cursor: "pointer",
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
maxHeight: 75,
|
||||
minHeight: 75,
|
||||
maxWidth: 420,
|
||||
minWidth: "100%",
|
||||
}
|
||||
|
||||
const name = hit.name === undefined ?
|
||||
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title :
|
||||
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
||||
|
||||
var secondaryText = hit.data !== undefined ? hit.data.slice(0, 40)+"..." : ""
|
||||
const avatar = hit.image_url === undefined ?
|
||||
baseImage
|
||||
:
|
||||
<Avatar
|
||||
src={hit.image_url}
|
||||
variant="rounded"
|
||||
/>
|
||||
|
||||
//console.log(hit)
|
||||
if (hit.categories !== undefined && hit.categories !== null && hit.categories.length > 0) {
|
||||
secondaryText = hit.categories.slice(0,3).map((data, index) => {
|
||||
if (index === 0) {
|
||||
return data
|
||||
}
|
||||
|
||||
return ", "+data
|
||||
|
||||
/*
|
||||
<Chip
|
||||
key={index}
|
||||
style={chipStyle}
|
||||
label={data}
|
||||
onClick={() => {
|
||||
//handleChipClick
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
*/
|
||||
})
|
||||
}
|
||||
|
||||
var parsedUrl = isCloud ? `/apps/${hit.objectID}` : `https://shuffler.io/apps/${hit.objectID}`
|
||||
parsedUrl += `?queryID=${hit.__queryID}`
|
||||
|
||||
return (
|
||||
<Link key={hit.objectID} to={{ pathname: parsedUrl }} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||
console.log("CLICK")
|
||||
setSearchOpen(true)
|
||||
|
||||
aa('init', {
|
||||
appId: searchClient.appId,
|
||||
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||
})
|
||||
|
||||
const timestamp = new Date().getTime()
|
||||
aa('sendEvents', [
|
||||
{
|
||||
eventType: 'click',
|
||||
eventName: 'App Clicked',
|
||||
index: 'appsearch',
|
||||
objectIDs: [hit.objectID],
|
||||
timestamp: timestamp,
|
||||
queryID: hit.__queryID,
|
||||
positions: [hit.__position],
|
||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||
}
|
||||
])
|
||||
|
||||
if (!isCloud) {
|
||||
event.preventDefault()
|
||||
window.open(parsedUrl, '_blank');
|
||||
}
|
||||
}}>
|
||||
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||
setMouseHoverIndex(index)
|
||||
}}>
|
||||
<ListItemAvatar>
|
||||
{avatar}
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={name}
|
||||
secondary={secondaryText}
|
||||
/>
|
||||
{/*
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
*/}
|
||||
</ListItem>
|
||||
</Link>
|
||||
)})
|
||||
}
|
||||
</List>
|
||||
<span style={{display: "flex", textAlign: "left", float: "left", position: "absolute", left: 15, bottom: 10, }}>
|
||||
<Link to="/search" style={{textDecoration: "none", color: "#f85a3e"}}>
|
||||
<Typography variant="body1" style={{}}>
|
||||
See more
|
||||
</Typography>
|
||||
</Link>
|
||||
</span>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const DocHits = ({ hits }) => {
|
||||
const [mouseHoverIndex, setMouseHoverIndex] = useState(0)
|
||||
|
||||
var tmp = searchOpen
|
||||
if (!searchOpen) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
const positionInfo = document.activeElement.getBoundingClientRect()
|
||||
const outerlistitemStyle = {
|
||||
width: "100%",
|
||||
overflowX: "hidden",
|
||||
overflowY: "hidden",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
}
|
||||
|
||||
if (hits.length > 4) {
|
||||
hits = hits.slice(0, 4)
|
||||
}
|
||||
|
||||
const type = "documentation"
|
||||
const baseImage = <LibraryBooksIcon />
|
||||
|
||||
//console.log(type, hits.length, hits)
|
||||
|
||||
return (
|
||||
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: 405, height: 408, left: 470, boxShadows: "none",}}>
|
||||
<IconButton style={{zIndex: 5000, position: "absolute", right: 14, color: "grey"}} onClick={() => {
|
||||
setSearchOpen(false)
|
||||
}}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" style={{margin: "10px 10px 0px 20px", }}>
|
||||
Documentation
|
||||
</Typography>
|
||||
{/*
|
||||
<IconButton edge="end" aria-label="delete" style={{position: "absolute", top: 5, right: 15,}} onClick={() => {
|
||||
setSearchOpen(false)
|
||||
}}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
*/}
|
||||
<List style={{backgroundColor: theme.palette.inputColor, }}>
|
||||
{hits.length === 0 ?
|
||||
<ListItem style={outerlistitemStyle}>
|
||||
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||
<Avatar>
|
||||
<FolderIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={"No documentation."}
|
||||
secondary={"Try a broader search term"}
|
||||
/>
|
||||
</ListItem>
|
||||
:
|
||||
hits.map((hit, index) => {
|
||||
const innerlistitemStyle = {
|
||||
width: positionInfo.width+35,
|
||||
overflowX: "hidden",
|
||||
overflowY: "hidden",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
||||
backgroundColor: mouseHoverIndex === index ? "#1f2023" : "inherit",
|
||||
cursor: "pointer",
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
maxHeight: 75,
|
||||
minHeight: 75,
|
||||
maxWidth: 420,
|
||||
minWidth: "100%",
|
||||
}
|
||||
|
||||
var name = hit.name === undefined ?
|
||||
hit.filename.charAt(0).toUpperCase() + hit.filename.slice(1).replaceAll("_", " ") + " - " + hit.title
|
||||
:
|
||||
(hit.name.charAt(0).toUpperCase()+hit.name.slice(1)).replaceAll("_", " ")
|
||||
|
||||
if (name.length > 30) {
|
||||
name = name.slice(0, 30)+"..."
|
||||
}
|
||||
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 40)+"..." : ""
|
||||
const avatar = hit.image_url === undefined ?
|
||||
baseImage
|
||||
:
|
||||
<Avatar
|
||||
src={hit.image_url}
|
||||
variant="rounded"
|
||||
/>
|
||||
|
||||
var parsedUrl = hit.urlpath !== undefined ? hit.urlpath : ""
|
||||
parsedUrl += `?queryID=${hit.__queryID}`
|
||||
if (parsedUrl.includes("/apps/")) {
|
||||
const extraHash = hit.url_hash === undefined ? "" : `#${hit.url_hash}`
|
||||
|
||||
parsedUrl = `/apps/${hit.filename}?tab=docs&queryID=${hit.__queryID}${extraHash}`
|
||||
}
|
||||
|
||||
return (
|
||||
<Link key={hit.objectID} to={parsedUrl} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||
aa('init', {
|
||||
appId: searchClient.appId,
|
||||
apiKey: searchClient.transporter.queryParameters["x-algolia-api-key"]
|
||||
})
|
||||
|
||||
const timestamp = new Date().getTime()
|
||||
aa('sendEvents', [
|
||||
{
|
||||
eventType: 'click',
|
||||
eventName: 'Document Clicked',
|
||||
index: 'documentation',
|
||||
objectIDs: [hit.objectID],
|
||||
timestamp: timestamp,
|
||||
queryID: hit.__queryID,
|
||||
positions: [hit.__position],
|
||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||
}
|
||||
])
|
||||
|
||||
console.log("CLICK")
|
||||
setSearchOpen(true)
|
||||
}}>
|
||||
<ListItem key={hit.objectID} style={innerlistitemStyle} onMouseOver={() => {
|
||||
setMouseHoverIndex(index)
|
||||
}}>
|
||||
<ListItemAvatar>
|
||||
{avatar}
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={name}
|
||||
secondary={secondaryText}
|
||||
/>
|
||||
{/*
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
*/}
|
||||
</ListItem>
|
||||
</Link>
|
||||
)})
|
||||
}
|
||||
</List>
|
||||
{type === "documentation" ?
|
||||
<span style={{display: "flex", textAlign: "right", position: "absolute", right: 15, bottom: 10,}}>
|
||||
<Typography variant="body2" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
: null}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomSearchBox = connectSearchBox(SearchBox)
|
||||
const CustomAppHits = connectHits(AppHits)
|
||||
const CustomWorkflowHits = connectHits(WorkflowHits)
|
||||
const CustomDocHits = connectHits(DocHits)
|
||||
|
||||
return (
|
||||
<div ref={node} style={{width: "100%", maxWidth: 425, margin: "auto", position: "relative", zIndex: 12500,}}>
|
||||
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
|
||||
console.log("CLICKED")
|
||||
}}>
|
||||
<Configure clickAnalytics />
|
||||
<CustomSearchBox />
|
||||
<Index indexName="appsearch">
|
||||
<CustomAppHits />
|
||||
</Index>
|
||||
<Index indexName="documentation">
|
||||
<CustomDocHits />
|
||||
</Index>
|
||||
<Index indexName="workflows">
|
||||
<CustomWorkflowHits />
|
||||
</Index>
|
||||
</InstantSearch>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchField;
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, {useState, useEffect, useRef} from 'react';
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
|
||||
import {
|
||||
Chip,
|
||||
@@ -17,13 +16,13 @@ import {
|
||||
Avatar,
|
||||
Typography,
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
} from '@mui/material';
|
||||
|
||||
import {
|
||||
AvatarGroup,
|
||||
} from "@mui/material"
|
||||
|
||||
import {Close as CloseIcon, Folder as FolderIcon, Polymer as PolymerIcon, LibraryBooks as LibraryBooksIcon} from '@material-ui/icons'
|
||||
import {Search as SearchIcon, Close as CloseIcon, Folder as FolderIcon, Code as CodeIcon, LibraryBooks as LibraryBooksIcon} from '@mui/icons-material'
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import aa from 'search-insights'
|
||||
@@ -39,12 +38,12 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
|
||||
const SearchField = props => {
|
||||
const { serverside, userdata } = props
|
||||
|
||||
const theme = useTheme();
|
||||
let navigate = useNavigate();
|
||||
const borderRadius = 3
|
||||
const node = useRef()
|
||||
const [searchOpen, setSearchOpen] = useState(false)
|
||||
const [oldPath, setOldPath] = useState("")
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
if (serverside === true) {
|
||||
return null
|
||||
@@ -69,7 +68,13 @@ const SearchField = props => {
|
||||
//}, searchOpen)
|
||||
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled, } ) => {
|
||||
|
||||
const keyPressHandler = (e) => {
|
||||
// e.preventDefault();
|
||||
if (e.which === 13) {
|
||||
// alert("You pressed enter!");
|
||||
navigate("/search?q=" + currentRefinement, { state: value, replace: true });
|
||||
}
|
||||
};
|
||||
/*
|
||||
endAdornment: (
|
||||
<InputAdornment position="end" style={{textAlign: "right", zIndex: 5001, cursor: "pointer", width: 100, }} onMouseOver={(event) => {
|
||||
@@ -110,6 +115,7 @@ const SearchField = props => {
|
||||
color="primary"
|
||||
placeholder="Find Public Apps, Workflows, Documentation..."
|
||||
value={currentRefinement}
|
||||
onKeyDown={keyPressHandler}
|
||||
id="shuffle_search_field"
|
||||
onClick={(event) => {
|
||||
if (!searchOpen) {
|
||||
@@ -162,7 +168,7 @@ const SearchField = props => {
|
||||
}
|
||||
|
||||
var type = "workflows"
|
||||
const baseImage = <PolymerIcon />
|
||||
const baseImage = <CodeIcon />
|
||||
|
||||
return (
|
||||
<Card elevation={0} style={{position: "relative", marginLeft: 10, marginRight: 10, position: "absolute", color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: 405, height: 408, left: 75, boxShadows: "none",}}>
|
||||
@@ -208,11 +214,12 @@ const SearchField = props => {
|
||||
const avatar = baseImage
|
||||
|
||||
var parsedUrl = isCloud ? `/workflows/${hit.objectID}` : `https://shuffler.io/workflows/${hit.objectID}`
|
||||
|
||||
parsedUrl += `?queryID=${hit.__queryID}`
|
||||
|
||||
// <a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
return (
|
||||
<Link key={hit.objectID} to={{ pathname: parsedUrl }} rel="noopener noreferrer" style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||
<Link key={hit.objectID} to={parsedUrl} rel="noopener noreferrer" style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||
//console.log("CLICK")
|
||||
setSearchOpen(true)
|
||||
|
||||
@@ -404,7 +411,7 @@ const SearchField = props => {
|
||||
parsedUrl += `?queryID=${hit.__queryID}`
|
||||
|
||||
return (
|
||||
<Link key={hit.objectID} to={{ pathname: parsedUrl }} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||
<Link key={hit.objectID} to={parsedUrl} style={{textDecoration: "none", color: "white",}} onClick={(event) => {
|
||||
console.log("CLICK")
|
||||
setSearchOpen(true)
|
||||
|
||||
@@ -560,7 +567,8 @@ const SearchField = props => {
|
||||
if (parsedUrl.includes("/apps/")) {
|
||||
const extraHash = hit.url_hash === undefined ? "" : `#${hit.url_hash}`
|
||||
|
||||
parsedUrl = `/apps/${hit.filename}?tab=docs&queryID=${hit.__queryID}${extraHash}`
|
||||
parsedUrl = `/apps/${hit.filename}`
|
||||
parsedUrl += `?tab=docs&queryID=${hit.__queryID}${extraHash}`
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,7 @@ import AppFramework, { usecases } from "../components/AppFramework.jsx";
|
||||
import {Link} from 'react-router-dom';
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import { Button, LinearProgress, Typography } from '@material-ui/core';
|
||||
import { Button, LinearProgress, Typography } from '@mui/material';
|
||||
|
||||
export const securityFramework = [
|
||||
{
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
|
||||
const SettingsDialog = (props) => {
|
||||
const {
|
||||
classes,
|
||||
onClose,
|
||||
settingsOpen,
|
||||
settingsData,
|
||||
globalUrl,
|
||||
isLoggedIn,
|
||||
setIsLoggedIn,
|
||||
...other
|
||||
} = props;
|
||||
|
||||
const [password1, setPassword1] = useState("");
|
||||
const [password2, setPassword2] = useState("");
|
||||
const [password3, setPassword3] = useState("");
|
||||
|
||||
const handleValidateForm = () => {
|
||||
var passlength = 10;
|
||||
if (
|
||||
password1 === password2 &&
|
||||
password1.length >= passlength &&
|
||||
password3.length >= passlength
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const onChangePass1 = (e) => {
|
||||
setPassword1(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass2 = (e) => {
|
||||
setPassword2(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass3 = (e) => {
|
||||
setPassword3(e.target.value);
|
||||
};
|
||||
|
||||
const onSubmitPassReset = () => {
|
||||
console.log("Should change password");
|
||||
// Rofl, this can't possibly be typesafe
|
||||
var data =
|
||||
'{"password1": "' +
|
||||
password1 +
|
||||
'", "password2": "' +
|
||||
password2 +
|
||||
'", "password3": "' +
|
||||
password3 +
|
||||
'"}';
|
||||
|
||||
fetch(globalUrl + "/passwordreset", {
|
||||
body: data,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
if (responseJson.status === true) {
|
||||
console.log("SUCCESS");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
//PaperProps={{style: {minWidth: "500px"}}
|
||||
return (
|
||||
<Dialog open={settingsOpen} onClose={() => onClose()} {...other}>
|
||||
<DialogTitle>Settings</DialogTitle>
|
||||
<Divider />
|
||||
<div style={{ marginLeft: "15px", marginRight: "15px" }}>
|
||||
<h3>Username</h3>
|
||||
{settingsData.username}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "15px",
|
||||
marginRight: "15px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
<h3>ApiKey</h3>
|
||||
<TextField
|
||||
id="outlined-read-only-input"
|
||||
defaultValue={settingsData.apikey}
|
||||
value={settingsData.apikey}
|
||||
style={{ width: 320 }}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
<form style={{ margin: "15px 15px 15px 15px" }}>
|
||||
<h3>Change password</h3>
|
||||
<div>
|
||||
<TextField
|
||||
id="standard-password-input"
|
||||
label="Current password"
|
||||
type="password"
|
||||
name="password"
|
||||
style={{ width: 320 }}
|
||||
placeholder="********************************"
|
||||
autoComplete="current-password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass1}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label="Confirm current password"
|
||||
type="password"
|
||||
placeholder="********************************"
|
||||
name="password"
|
||||
style={{ width: 320 }}
|
||||
autoComplete="current-password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass2}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label="New password"
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="********************************"
|
||||
style={{ width: 320 }}
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass3}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: "10px" }}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="contained"
|
||||
onClick={onSubmitPassReset}
|
||||
type="button"
|
||||
style={{ flex: "1", marginRight: "5px" }}
|
||||
disabled={!handleValidateForm()}
|
||||
>
|
||||
SUBMIT
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="button"
|
||||
style={{ flex: "1" }}
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsDialog;
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {useState, useEffect, useLayoutEffect} from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
@@ -12,51 +13,51 @@ import {
|
||||
Menu,
|
||||
MenuItem,
|
||||
Button,
|
||||
} from '@material-ui/core';
|
||||
} from '@mui/material';
|
||||
|
||||
import theme from '../theme.jsx';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import { orange } from '@mui/material/colors';
|
||||
import { isMobile } from "react-device-detect"
|
||||
import NestedMenuItem from "material-ui-nested-menu-item";
|
||||
import { NestedMenuItem } from "mui-nested-menu"
|
||||
import { GetParsedPaths, FindJsonPath } from "../views/Apps.jsx";
|
||||
import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx";
|
||||
|
||||
import {
|
||||
FullscreenExit as FullscreenExitIcon,
|
||||
Extension as ExtensionIcon,
|
||||
Apps as AppsIcon,
|
||||
FavoriteBorder as FavoriteBorderIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
FormatListNumbered as FormatListNumberedIcon,
|
||||
Apps as AppsIcon,
|
||||
FavoriteBorder as FavoriteBorderIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
FormatListNumbered as FormatListNumberedIcon,
|
||||
SquareFoot as SquareFootIcon,
|
||||
Circle as CircleIcon,
|
||||
Add as AddIcon,
|
||||
Circle as CircleIcon,
|
||||
Add as AddIcon,
|
||||
PlayArrow as PlayArrowIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import {
|
||||
AutoFixHigh as AutoFixHighIcon,
|
||||
Close as CloseIcon,
|
||||
CompressOutlined,
|
||||
QrCodeScannerOutlined,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
import { validateJson } from "../views/Workflows.jsx";
|
||||
import ReactJson from "react-json-view";
|
||||
import PaperComponent from "../components/PaperComponent.jsx";
|
||||
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import 'codemirror/keymap/sublime';
|
||||
import 'codemirror/addon/selection/mark-selection.js'
|
||||
import 'codemirror/theme/gruvbox-dark.css';
|
||||
import 'codemirror/theme/duotone-light.css';
|
||||
//import 'codemirror/keymap/sublime';
|
||||
//import 'codemirror/addon/selection/mark-selection.js'
|
||||
//import 'codemirror/theme/gruvbox-dark.css';
|
||||
//import 'codemirror/theme/duotone-light.css';
|
||||
import {indentWithTab} from "@codemirror/commands"
|
||||
import { padding, textAlign } from '@mui/system';
|
||||
import data from '../frameworkStyle.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
|
||||
import { tags } from '@lezer/highlight';
|
||||
|
||||
|
||||
const liquidFilters = [
|
||||
{"name": "Size", "value": "size", "example": ""},
|
||||
@@ -81,44 +82,39 @@ const pythonFilters = [
|
||||
{"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``},
|
||||
]
|
||||
|
||||
//const shuffleTheme = createTheme({
|
||||
// theme: 'dark',
|
||||
// settings: {
|
||||
// background: '#282828',
|
||||
// foreground: '#282828',
|
||||
// caret: '#5d00ff',
|
||||
// selection: '#036dd626',
|
||||
// selectionMatch: '#036dd626',
|
||||
// lineHighlight: '#8a91991a',
|
||||
// gutterBackground: '#282828',
|
||||
// gutterForeground: '#8a919966',
|
||||
// fontSize: 18,
|
||||
// borderRadius: theme.palette.borderRadius,
|
||||
// border: `2px solid ${theme.palette.inputColor}`,
|
||||
// },
|
||||
// styles: [
|
||||
// { tag: tags.comment, color: '#787b8099' },
|
||||
// { tag: tags.variableName, color: '#0080ff' },
|
||||
// { tag: [tags.string, tags.special(tags.brace)], color: '#5c6166' },
|
||||
// { tag: tags.number, color: '#5c6166' },
|
||||
// { tag: tags.bool, color: '#5c6166' },
|
||||
// { tag: tags.null, color: '#5c6166' },
|
||||
// { tag: tags.keyword, color: '#5c6166' },
|
||||
// { tag: tags.operator, color: '#5c6166' },
|
||||
// { tag: tags.className, color: '#5c6166' },
|
||||
// { tag: tags.definition(tags.typeName), color: '#5c6166' },
|
||||
// { tag: tags.typeName, color: '#5c6166' },
|
||||
// { tag: tags.angleBracket, color: '#5c6166' },
|
||||
// { tag: tags.tagName, color: '#5c6166' },
|
||||
// { tag: tags.attributeName, color: '#5c6166' },
|
||||
// ],
|
||||
//});
|
||||
const shuffleTheme = createTheme({
|
||||
theme: 'dark',
|
||||
settings: {
|
||||
background: "rgba(40,40,40, 1)",
|
||||
foreground: '#75baff',
|
||||
caret: '#5d00ff',
|
||||
selection: '#036dd626',
|
||||
selectionMatch: '#036dd626',
|
||||
lineHighlight: '#8a91991a',
|
||||
gutterForeground: '#8a919966',
|
||||
},
|
||||
styles: [
|
||||
{ tag: t.comment, color: '#787b8099' },
|
||||
{ tag: t.variableName, color: '#0080ff' },
|
||||
{ tag: [t.string, t.special(t.brace)], color: '#5c6166' },
|
||||
{ tag: t.number, color: '#5c6166' },
|
||||
{ tag: t.bool, color: '#5c6166' },
|
||||
{ tag: t.null, color: '#5c6166' },
|
||||
{ tag: t.keyword, color: '#5c6166' },
|
||||
{ tag: t.operator, color: '#5c6166' },
|
||||
{ tag: t.className, color: '#5c6166' },
|
||||
{ tag: t.definition(t.typeName), color: '#5c6166' },
|
||||
{ tag: t.typeName, color: '#5c6166' },
|
||||
{ tag: t.angleBracket, color: '#5c6166' },
|
||||
{ tag: t.tagName, color: '#5c6166' },
|
||||
{ tag: t.attributeName, color: '#5c6166' },
|
||||
],
|
||||
});
|
||||
|
||||
const CodeEditor = (props) => {
|
||||
const {
|
||||
globalUrl,
|
||||
fieldCount,
|
||||
setFieldCount,
|
||||
actionlist,
|
||||
changeActionParameterCodeMirror,
|
||||
expansionModalOpen,
|
||||
@@ -132,6 +128,8 @@ const CodeEditor = (props) => {
|
||||
selectedAction ,
|
||||
workflowExecutions,
|
||||
getParents,
|
||||
|
||||
fieldname,
|
||||
} = props
|
||||
|
||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||
@@ -140,7 +138,7 @@ const CodeEditor = (props) => {
|
||||
const [validation, setValidation] = React.useState(false);
|
||||
const [expOutput, setExpOutput] = React.useState(" ");
|
||||
const [linewrap, setlinewrap] = React.useState(true);
|
||||
const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark");
|
||||
//const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark");
|
||||
const [editorPopupOpen, setEditorPopupOpen] = React.useState(false);
|
||||
|
||||
const [currentCharacter, setCurrentCharacter] = React.useState(-1);
|
||||
@@ -155,8 +153,8 @@ const CodeEditor = (props) => {
|
||||
const [mainVariables, setMainVariables] = React.useState([]);
|
||||
const [availableVariables, setAvailableVariables] = React.useState([]);
|
||||
|
||||
const [menuPosition, setMenuPosition] = useState(null);
|
||||
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
|
||||
const [menuPosition, setMenuPosition] = useState(null);
|
||||
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
|
||||
|
||||
const [isAiLoading, setIsAiLoading] = React.useState(false);
|
||||
|
||||
@@ -206,6 +204,9 @@ const CodeEditor = (props) => {
|
||||
|
||||
setAvailableVariables(allVariables)
|
||||
setMainVariables(tmpVariables)
|
||||
|
||||
console.log("Checking local codedata: ", localcodedata)
|
||||
expectedOutput(localcodedata)
|
||||
}, [])
|
||||
|
||||
const aiSubmit = (value, inputAction) => {
|
||||
@@ -821,10 +822,6 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
const executeSingleAction = (inputdata) => {
|
||||
//if (serverside === true) {
|
||||
// return
|
||||
//}
|
||||
|
||||
if (validation === true) {
|
||||
inputdata = JSON.stringify(inputdata)
|
||||
}
|
||||
@@ -832,7 +829,10 @@ const CodeEditor = (props) => {
|
||||
// Shuffle Tools 1.2.0 (in most cases?)
|
||||
const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a"
|
||||
|
||||
const actiondata = {"description":"Repeats the call parameter","id":"","name":"repeat_back_to_me","label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters":[{"description":"The message to repeat","id":"","name":"call","example":"REPEATING: Hello world","value":inputdata,"multiline":true,"options":null,"action_field":"","variant":"STATIC_VALUE","required":true,"configuration":false,"tags":null,"schema":{"type":"string"},"skip_multicheck":false,"value_replace":null,"unique_toggled":false,"autocompleted":false}],"execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}}
|
||||
const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : "repeat_back_to_me"
|
||||
const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : [{"name":"call", "value": inputdata}]
|
||||
|
||||
const actiondata = {"description":"Repeats the call parameter","id":"","name":actionname,"label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters": params, "execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}}
|
||||
|
||||
setExecutionResult({
|
||||
"valid": false,
|
||||
@@ -863,12 +863,12 @@ const CodeEditor = (props) => {
|
||||
var newResult = {}
|
||||
if (responseJson.success === true && responseJson.result !== null && responseJson.result !== undefined && responseJson.result.length > 0) {
|
||||
const result = responseJson.result.slice(0, 50)+"..."
|
||||
//alert.info("SUCCESS: "+result)
|
||||
//toast("SUCCESS: "+result)
|
||||
|
||||
const validate = validateJson(responseJson.result)
|
||||
newResult = validate
|
||||
} else if (responseJson.success === false && responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||
alert.error(responseJson.reason)
|
||||
toast(responseJson.reason)
|
||||
newResult = {"valid": false, "result": responseJson.reason}
|
||||
} else if (responseJson.success === true) {
|
||||
newResult = {"valid": false, "result": "Couldn't finish execution. Please fill all the required fields, and retry the execution."}
|
||||
@@ -884,7 +884,7 @@ const CodeEditor = (props) => {
|
||||
setExecuting(false)
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error("Execution error: "+error.toString())
|
||||
//toast("Execution error: "+error.toString())
|
||||
console.log("error: ", error)
|
||||
setExecuting(false)
|
||||
})
|
||||
@@ -919,10 +919,23 @@ const CodeEditor = (props) => {
|
||||
maxHeight: isMobile ? "100%" : 720,
|
||||
border: theme.palette.defaultBorder,
|
||||
padding: isMobile ? "25px 10px 25px 10px" : 25,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
zIndex: 5000,
|
||||
position: "absolute",
|
||||
top: 14,
|
||||
right: 18,
|
||||
color: "grey",
|
||||
}}
|
||||
onClick={() => {
|
||||
setExpansionModalOpen(false)
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 1, }}>
|
||||
{ isFileEditor ?
|
||||
@@ -1020,6 +1033,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={liquidOpen ? 'basic-menu' : undefined}
|
||||
aria-expanded={liquidOpen ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 100,
|
||||
@@ -1055,6 +1069,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={mathOpen ? 'basic-menu' : undefined}
|
||||
aria-expanded={mathOpen ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 100,
|
||||
@@ -1090,6 +1105,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={pythonOpen ? 'basic-menu' : undefined}
|
||||
aria-expanded={pythonOpen ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 100,
|
||||
@@ -1125,6 +1141,7 @@ const CodeEditor = (props) => {
|
||||
aria-controls={!!menuPosition ? 'basic-menu' : undefined}
|
||||
aria-expanded={!!menuPosition ? 'true' : undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
style={{
|
||||
textTransform: "none",
|
||||
width: 130,
|
||||
@@ -1381,7 +1398,7 @@ const CodeEditor = (props) => {
|
||||
position: "relative",
|
||||
}}>
|
||||
<CodeMirror
|
||||
value = {localcodedata}
|
||||
value={localcodedata}
|
||||
height={isFileEditor ? 450 : 525}
|
||||
width={isFileEditor ? 650 : 600}
|
||||
style={{
|
||||
@@ -1389,6 +1406,8 @@ const CodeEditor = (props) => {
|
||||
wordBreak: "break-word",
|
||||
marginTop: 0,
|
||||
paddingTop: 0,
|
||||
backgroundColor: "rgba(40,40,40,1)",
|
||||
minHeight: 470,
|
||||
}}
|
||||
onCursorActivity = {(value) => {
|
||||
// console.log(value.getCursor())
|
||||
@@ -1398,27 +1417,22 @@ const CodeEditor = (props) => {
|
||||
findIndex(value.getCursor().line, value.getCursor().ch)
|
||||
highlight_variables(value)
|
||||
}}
|
||||
onChange={(value) => {
|
||||
//console.log("Value: '", value.getValue(), "'")
|
||||
onChange={(value, viewUpdate) => {
|
||||
console.log("Value: ", value, viewUpdate)
|
||||
setlocalcodedata(value)
|
||||
expectedOutput(value)
|
||||
|
||||
setlocalcodedata(value.getValue())
|
||||
expectedOutput(value.getValue())
|
||||
|
||||
if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){
|
||||
setEditorPopupOpen(true)
|
||||
}
|
||||
|
||||
// console.log(findIndex(value.getValue()))
|
||||
// highlight_variables(value)
|
||||
//if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){
|
||||
// setEditorPopupOpen(true)
|
||||
//}
|
||||
}}
|
||||
extensions={[indentWithTab]}
|
||||
extensions={[]}//indentWithTab]}
|
||||
theme={shuffleTheme}
|
||||
options={{
|
||||
styleSelectedText: true,
|
||||
theme: codeTheme,
|
||||
keyMap: 'sublime',
|
||||
mode: validation === true ? "json" : "python",
|
||||
lineWrapping: linewrap,
|
||||
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
@@ -1553,7 +1567,7 @@ const CodeEditor = (props) => {
|
||||
<IconButton disabled={executing} color="primary" style={{border: `1px solid ${theme.palette.primary.main}`, marginLeft: 100, padding: 8}} variant="contained" onClick={() => {
|
||||
executeSingleAction(expOutput)
|
||||
}}>
|
||||
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
|
||||
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' or 'execute python' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
|
||||
{executing ? <CircularProgress style={{height: 18, width: 18, }} /> : <PlayArrowIcon style={{height: 18, width: 18, }} /> }
|
||||
|
||||
</Tooltip>
|
||||
@@ -1664,34 +1678,29 @@ const CodeEditor = (props) => {
|
||||
|
||||
|
||||
<div style={{display: 'flex',}}>
|
||||
<button
|
||||
<Button
|
||||
style={{
|
||||
color: "white",
|
||||
background: "#383b49",
|
||||
border: "none",
|
||||
height: 35,
|
||||
flex: 1,
|
||||
marginLeft: 5,
|
||||
marginTop: 5,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setExpansionModalOpen(false);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{
|
||||
color: "white",
|
||||
background: "#f85a3e",
|
||||
border: "none",
|
||||
|
||||
height: 35,
|
||||
flex: 1,
|
||||
marginLeft: 10,
|
||||
marginTop: 5,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={(event) => {
|
||||
// Take localcodedata through the Shuffle JSON parser just in case
|
||||
@@ -1709,15 +1718,26 @@ const CodeEditor = (props) => {
|
||||
runUpdateText(fixedcodedata);
|
||||
setcodedata(fixedcodedata);
|
||||
setExpansionModalOpen(false)
|
||||
} else {
|
||||
changeActionParameterCodeMirror(event, fieldCount, fixedcodedata)
|
||||
} else if (changeActionParameterCodeMirror !== undefined) {
|
||||
//changeActionParameterCodeMirror(event, fieldCount, fixedcodedata)
|
||||
changeActionParameterCodeMirror(event, fieldCount, fixedcodedata, actionlist)
|
||||
setExpansionModalOpen(false)
|
||||
setcodedata(fixedcodedata)
|
||||
}
|
||||
|
||||
// Check if fieldname is set, and try to find and inject the text
|
||||
if (fieldname !== undefined && fieldname !== null && fieldname.length > 0) {
|
||||
const foundfield = document.getElementById(fieldname)
|
||||
if (foundfield !== undefined && foundfield !== null) {
|
||||
foundfield.value = fixedcodedata
|
||||
}
|
||||
}
|
||||
|
||||
setExpansionModalOpen(false)
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
CircularProgress,
|
||||
Tooltip,
|
||||
Dialog,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { toast } from 'react-toastify';
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
|
||||
import PaperComponent from "../components/PaperComponent.jsx"
|
||||
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
|
||||
@@ -25,7 +26,7 @@ import {
|
||||
CircularProgress,
|
||||
Tooltip,
|
||||
Divider,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
const defaultValue = {"id": "", "name": "Build your own",
|
||||
"source": {"text": "No trigger selected", "error": ""},
|
||||
@@ -349,14 +350,14 @@ const UsecaseSearch = (props) => {
|
||||
const [firstRequest, setFirstRequest] = React.useState(true);
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
|
||||
useEffect(() => {
|
||||
// if (firstRequest !== true && workflow.id !== undefined && autotry === true && setUsecaseSearch !== undefined && authenticationModalOpen === false && configureWorkflowModalOpen === false) {
|
||||
//
|
||||
if (autotry === true && configureWorkflowModalOpen === false && workflow.id !== undefined && setUsecaseSearch !== undefined) {
|
||||
console.log("Close it?")
|
||||
alert.info("Workflow successfully added! Add more apps, and we will suggest more workflows")
|
||||
toast("Workflow successfully added! Add more apps, and we will suggest more workflows")
|
||||
|
||||
if (setCloseWindow !== undefined) {
|
||||
setCloseWindow(true)
|
||||
@@ -793,7 +794,7 @@ const UsecaseSearch = (props) => {
|
||||
console.log("Deleted workflow")
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("Delete workflow error: ", error.toString());
|
||||
})
|
||||
}
|
||||
@@ -822,7 +823,7 @@ const UsecaseSearch = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
console.log("Get workflows error: ", error.toString());
|
||||
})
|
||||
}
|
||||
@@ -894,9 +895,9 @@ const UsecaseSearch = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Error setting workflow: ", responseJson.reason)
|
||||
toast("Error setting workflow: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Error setting workflow.")
|
||||
toast("Error setting workflow.")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -905,7 +906,7 @@ const UsecaseSearch = (props) => {
|
||||
return responseJson;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -930,7 +931,7 @@ const UsecaseSearch = (props) => {
|
||||
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== null && responseJson.reason !== undefined) {
|
||||
//alert.error(responseJson.reason)
|
||||
//toast(responseJson.reason)
|
||||
}
|
||||
|
||||
if (responseJson.source === "") {
|
||||
@@ -1009,13 +1010,13 @@ const UsecaseSearch = (props) => {
|
||||
responseJson.status,
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success("Successfully generated " + responseJson.name);
|
||||
toast("Successfully generated " + responseJson.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Generate error: " + error.toString());
|
||||
toast("Generate error: " + error.toString());
|
||||
})
|
||||
|
||||
|
||||
@@ -1057,7 +1058,7 @@ const UsecaseSearch = (props) => {
|
||||
.catch((error) => {
|
||||
setIsUploading(false)
|
||||
console.log("Merge err: ", error.toString())
|
||||
//alert.error("Err: " + error.toString());
|
||||
//toast("Err: " + error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1207,7 +1208,7 @@ const UsecaseSearch = (props) => {
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
//alert.error("Errors were found. Click them to sort sort them out or go to the next usecase.")
|
||||
//toast("Errors were found. Click them to sort sort them out or go to the next usecase.")
|
||||
|
||||
//setUpdate(Math.random())
|
||||
//setIsUploading(false)
|
||||
@@ -1268,13 +1269,13 @@ const UsecaseSearch = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed to activate the app")
|
||||
toast("Failed to activate the app")
|
||||
} else {
|
||||
//alert.success("App activated for your organization! Refresh the page to use the app.")
|
||||
//toast("App activated for your organization! Refresh the page to use the app.")
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error(error.toString())
|
||||
//toast(error.toString())
|
||||
console.log("Activate app error: ", error.toString())
|
||||
});
|
||||
}
|
||||
@@ -1304,9 +1305,9 @@ const UsecaseSearch = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed updating default app: " + responseJson.reason)
|
||||
toast("Failed updating default app: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to update framework for your org.")
|
||||
toast("Failed to update framework for your org.")
|
||||
|
||||
}
|
||||
} else {
|
||||
@@ -1319,7 +1320,7 @@ const UsecaseSearch = (props) => {
|
||||
//setFrameworkData(responseJson)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
@@ -1516,7 +1517,7 @@ const UsecaseSearch = (props) => {
|
||||
return (
|
||||
<div key={curindex} style={{display: "flex", maxHeight: 40, minHeight: 40, borderTop: "1px solid rgba(255,255,255,0.3)", }} onClick={() => {
|
||||
if (subdata.disabled === true) {
|
||||
//alert.info("Usecase not available yet.")
|
||||
//toast("Usecase not available yet.")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,10 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import {Link} from 'react-router-dom';
|
||||
import theme from '../theme.jsx';
|
||||
import { removeQuery } from '../components/ScrollToTop.jsx';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons';
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material';
|
||||
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import { InstantSearch, Configure, connectSearchBox, connectHits } from 'react-instantsearch-dom';
|
||||
@@ -18,28 +19,26 @@ import {
|
||||
Tooltip,
|
||||
Zoom,
|
||||
Chip,
|
||||
} from '@material-ui/core';
|
||||
} from '@mui/material';
|
||||
|
||||
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
||||
import WorkflowPaperNew from "../components/WorkflowPaperNew.jsx"
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const AppGrid = props => {
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, } = props
|
||||
|
||||
const isCloud =
|
||||
window.location.host === "localhost:3002" ||
|
||||
window.location.host === "shuffler.io";
|
||||
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, alternativeView, onlyResults, inputsearch } = props
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
|
||||
const xs = parsedXs === undefined || parsedXs === null ? isMobile ? 6 : 4 : 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("");
|
||||
const [usecases, setUsecases] = React.useState([]);
|
||||
const [usecases, setUsecases] = React.useState([]);
|
||||
|
||||
const [localMessage, setLocalMessage] = React.useState("");
|
||||
|
||||
const buttonStyle = {borderRadius: 30, height: 50, width: 220, margin: isMobile ? "15px auto 15px auto" : 20, fontSize: 18,}
|
||||
|
||||
@@ -71,7 +70,7 @@ const AppGrid = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
@@ -154,23 +153,25 @@ const AppGrid = props => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success !== false) {
|
||||
console.log("Usecases: ", responseJson)
|
||||
//handleKeysetting(responseJson, workflows)
|
||||
}
|
||||
if (responseJson.success !== false) {
|
||||
//handleKeysetting(responseJson, workflows)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsecases()
|
||||
|
||||
}, [])
|
||||
|
||||
|
||||
// value={currentRefinement}
|
||||
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
|
||||
var defaultSearch = ""
|
||||
useEffect(() => {
|
||||
if (window !== undefined && window.location !== undefined && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
@@ -179,20 +180,30 @@ const AppGrid = props => {
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
console.log("Got query: ", foundQuery)
|
||||
refine(foundQuery)
|
||||
defaultSearch = foundQuery
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) {
|
||||
//setLocalMessage(inputsearch)
|
||||
refine(inputsearch)
|
||||
defaultSearch = inputsearch
|
||||
return null
|
||||
} else if (onlyResults === true) {
|
||||
// Don't return anything unless refinement works
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<form noValidate action="" role="search">
|
||||
{onlyResults !== true ?
|
||||
<TextField
|
||||
defaultValue={defaultSearch}
|
||||
fullWidth
|
||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||
InputProps={{
|
||||
style:{
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
height: 50,
|
||||
},
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
@@ -207,12 +218,13 @@ const AppGrid = props => {
|
||||
placeholder="Find Workflows..."
|
||||
id="shuffle_search_field"
|
||||
onChange={(event) => {
|
||||
removeQuery("q")
|
||||
refine(event.currentTarget.value)
|
||||
}}
|
||||
limit={5}
|
||||
/>
|
||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||
</form>
|
||||
: null}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -229,29 +241,33 @@ const AppGrid = props => {
|
||||
var counted = 0
|
||||
|
||||
return (
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{hits.map((data, index) => {
|
||||
workflowDelay += 50
|
||||
<div>
|
||||
{onlyResults === true && hits.length > 0 ?
|
||||
null
|
||||
: null}
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{hits.map((data, index) => {
|
||||
workflowDelay += 50
|
||||
|
||||
if (counted === 12/xs*rowHandler) {
|
||||
return null
|
||||
}
|
||||
if (counted === 12/xs*rowHandler) {
|
||||
return null
|
||||
}
|
||||
|
||||
counted += 1
|
||||
counted += 1
|
||||
|
||||
return (
|
||||
<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>
|
||||
<Grid item xs={xs} style={{ padding: "12px 10px 12px 10px" }}>
|
||||
return (
|
||||
<Grid item xs={xs} style={{ padding: "12px 10px 12px 10px",}}>
|
||||
{/*<Zoom key={index} in={true} style={{ transitionDelay: `${workflowDelay}ms` }}>*/}
|
||||
{alternativeView === true ?
|
||||
<WorkflowPaperNew key={index} data={data} />
|
||||
:
|
||||
<WorkflowPaper key={index} data={data} />
|
||||
}
|
||||
</Grid>
|
||||
</Zoom>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -332,11 +348,11 @@ const AppGrid = props => {
|
||||
fullWidth={true}
|
||||
placeholder="What apps do you want to see?"
|
||||
type=""
|
||||
id="standard-required"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
autoComplete="off"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
@@ -354,15 +370,16 @@ const AppGrid = props => {
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
|
||||
<span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
{onlyResults === true ? null :
|
||||
<span style={{position: "absolute", display: "flex", textAlign: "right", float: "right", right: 0, bottom: 120, }}>
|
||||
<Typography variant="body2" color="textSecondary" style={{}}>
|
||||
Search by
|
||||
</Typography>
|
||||
<a rel="noopener noreferrer" href="https://www.algolia.com/" target="_blank" style={{textDecoration: "none", color: "white"}}>
|
||||
<img src={"/images/logo-algolia-nebula-blue-full.svg"} alt="Algolia logo" style={{height: 17, marginLeft: 5, marginTop: 3,}} />
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
import React, { useState, useEffect, useLayoutEffect } from "react";
|
||||
import theme from '../theme';
|
||||
|
||||
import {
|
||||
Chip,
|
||||
Typography,
|
||||
Paper,
|
||||
Avatar,
|
||||
Grid,
|
||||
Tooltip,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
AvatarGroup,
|
||||
} from "@mui/material"
|
||||
|
||||
import {
|
||||
Restore as RestoreIcon,
|
||||
Edit as EditIcon,
|
||||
BubbleChart as BubbleChartIcon,
|
||||
MoreVert as MoreVertIcon,
|
||||
} from '@material-ui/icons';
|
||||
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
const workflowActionStyle = {
|
||||
display: "flex",
|
||||
width: 160,
|
||||
height: 44,
|
||||
justifyContent: "space-between",
|
||||
}
|
||||
|
||||
const paperAppStyle = {
|
||||
minHeight: 130,
|
||||
maxHeight: 130,
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
color: "white",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
padding: "12px 12px 0px 15px",
|
||||
borderRadius: 5,
|
||||
display: "flex",
|
||||
boxSizing: "border-box",
|
||||
position: "relative",
|
||||
}
|
||||
|
||||
const chipStyle = {
|
||||
backgroundColor: "#3d3f43",
|
||||
marginRight: 5,
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
height: 28,
|
||||
cursor: "pointer",
|
||||
borderColor: "#3d3f43",
|
||||
color: "white",
|
||||
}
|
||||
|
||||
const WorkflowPaper = (props) => {
|
||||
const { data } = props;
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const appGroup = data.action_references === undefined || data.action_references === null ? [] : data.action_references
|
||||
|
||||
//console.log("Workflow: ", data)
|
||||
var boxColor = "#86c142";
|
||||
|
||||
var parsedName = data.name;
|
||||
if (
|
||||
parsedName !== undefined &&
|
||||
parsedName !== null &&
|
||||
parsedName.length > 20
|
||||
) {
|
||||
parsedName = parsedName.slice(0, 21) + "..";
|
||||
}
|
||||
|
||||
|
||||
const imageStyle = {
|
||||
width: 24,
|
||||
height: 24,
|
||||
marginRight: 10,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}
|
||||
var image = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.image !== undefined && data.creator_info.image !== null && data.creator_info.image.length > 0 ? <Avatar alt={data.creator} src={data.creator_info.image} style={imageStyle}/> : <Avatar alt={"shuffle_image"} src={theme.palette.defaultImage} style={imageStyle}/>
|
||||
const creatorname = data.creator_info !== undefined && data.creator_info !== null && data.creator_info.username !== undefined && data.creator_info.username !== null && data.creator_info.username.length > 0 ? data.creator_info.username : ""
|
||||
var orgName = "";
|
||||
var orgId = "";
|
||||
if ((data.objectID === undefined || data.objectID === null) && data.id !== undefined && data.id !== null) {
|
||||
data.objectID = data.id
|
||||
}
|
||||
|
||||
//console.log("IMG: ", data)
|
||||
var parsedUrl = `/workflows/${data.objectID}`
|
||||
if (data.__queryID !== undefined && data.__queryID !== null) {
|
||||
parsedUrl += `?queryID=${data.__queryID}`
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{width: "100%", position: "relative",}}>
|
||||
<Paper square style={paperAppStyle}>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 1,
|
||||
left: 1,
|
||||
height: 12,
|
||||
width: 12,
|
||||
backgroundColor: boxColor,
|
||||
borderRadius: "0 100px 0 0",
|
||||
}}
|
||||
/>
|
||||
<Grid
|
||||
item
|
||||
style={{ display: "flex", flexDirection: "column", width: "100%" }}
|
||||
>
|
||||
<Grid item style={{ display: "flex", maxHeight: 34 }}>
|
||||
<Tooltip title={`${creatorname}`} placement="bottom">
|
||||
<div
|
||||
style={{ cursor: data.creator_info !== undefined ? "pointer" : "inherit" }}
|
||||
onClick={() => {
|
||||
if (data.creator_info !== undefined) {
|
||||
navigate("/creators/"+data.creator_info.username)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{image}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={`Edit ${data.name}`} placement="bottom">
|
||||
<Typography
|
||||
variant="body1"
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
paddingBottom: 0,
|
||||
maxHeight: 30,
|
||||
flex: 10,
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
to={parsedUrl}
|
||||
style={{ textDecoration: "none", color: "inherit" }}
|
||||
>
|
||||
{parsedName}
|
||||
</Link>
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<Grid item style={workflowActionStyle}>
|
||||
{appGroup.length > 0 ?
|
||||
<div style={{display: "flex", marginTop: 8, }}>
|
||||
<AvatarGroup max={4} style={{marginLeft: 5, maxHeight: 24,}}>
|
||||
{appGroup.map((app, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
height: 24,
|
||||
width: 24,
|
||||
filter: "brightness(0.6)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/apps/"+app.id)
|
||||
}}
|
||||
>
|
||||
<Tooltip color="primary" title={app.name} placement="bottom">
|
||||
<Avatar alt={app.name} src={app.image_url} style={{width: 24, height: 24}}/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</AvatarGroup>
|
||||
</div>
|
||||
:
|
||||
<Tooltip color="primary" title="Action amount" placement="bottom">
|
||||
<span style={{ color: "#979797", display: "flex" }}>
|
||||
<BubbleChartIcon
|
||||
style={{ marginTop: "auto", marginBottom: "auto" }}
|
||||
/>
|
||||
<Typography
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
{data.actions === undefined || data.actions === null ? 1 : data.actions.length}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title="Trigger amount"
|
||||
placement="bottom"
|
||||
>
|
||||
<span
|
||||
style={{ marginLeft: 15, color: "#979797", display: "flex" }}
|
||||
>
|
||||
<RestoreIcon
|
||||
style={{
|
||||
color: "#979797",
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
{data.triggers === undefined || data.triggers === null ? 1 : data.triggers.length}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip color="primary" title="Subflows used" placement="bottom">
|
||||
<span
|
||||
style={{
|
||||
marginLeft: 15,
|
||||
display: "flex",
|
||||
color: "#979797",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={{
|
||||
color: "#979797",
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M0 0H15V15H0V0ZM16 16H18V18H16V16ZM16 13H18V15H16V13ZM16 10H18V12H16V10ZM16 7H18V9H16V7ZM16 4H18V6H16V4ZM13 16H15V18H13V16ZM10 16H12V18H10V16ZM7 16H9V18H7V16ZM4 16H6V18H4V16Z"
|
||||
fill="#979797"
|
||||
/>
|
||||
</svg>
|
||||
<Typography
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto",
|
||||
}}
|
||||
>
|
||||
{0}
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
style={{
|
||||
justifyContent: "left",
|
||||
overflow: "hidden",
|
||||
marginTop: 5,
|
||||
}}
|
||||
>
|
||||
{data.tags !== undefined && data.tags !== null
|
||||
? data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
style={chipStyle}
|
||||
label={tag}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
/>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowPaper
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Avatar,
|
||||
Grid,
|
||||
Tooltip,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
AvatarGroup,
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Edit as EditIcon,
|
||||
BubbleChart as BubbleChartIcon,
|
||||
MoreVert as MoreVertIcon,
|
||||
} from '@material-ui/icons';
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
@@ -277,7 +277,7 @@ const WorkflowPaper = (props) => {
|
||||
>
|
||||
{data.tags !== undefined && data.tags !== null
|
||||
? data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
if (index >= 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Grid,
|
||||
Tooltip,
|
||||
Button,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
AvatarGroup,
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
Edit as EditIcon,
|
||||
BubbleChart as BubbleChartIcon,
|
||||
MoreVert as MoreVertIcon,
|
||||
} from '@material-ui/icons';
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { toast } from "react-toastify"
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Drawer,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Check as CheckIcon,
|
||||
TrendingFlat as TrendingFlatIcon,
|
||||
Close as CloseIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
|
||||
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
|
||||
|
||||
const WorkflowTemplatePopup = (props) => {
|
||||
const { userdata, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps } = props;
|
||||
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const [workflowLoading, setWorkflowLoading] = useState(false);
|
||||
const [workflow, setWorkflow] = useState({});
|
||||
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
let navigate = useNavigate();
|
||||
|
||||
const imagestyleWrapper = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
}
|
||||
|
||||
const imagestyleWrapperDefault = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
}
|
||||
|
||||
const imagestyle = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
overflow: "hidden",
|
||||
}
|
||||
|
||||
const imagestyleDefault = {
|
||||
display: "block",
|
||||
marginLeft: 11,
|
||||
marginTop: 11,
|
||||
height: 35,
|
||||
width: "auto",
|
||||
}
|
||||
|
||||
if (title === undefined || title === null || title === "") {
|
||||
console.log("No title for workflow template popup!");
|
||||
return null
|
||||
}
|
||||
|
||||
const getWorkflow = (workflowId) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/${workflowId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
console.log("Error in workflow loading for ID ", workflowId)
|
||||
} else {
|
||||
setWorkflow(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const loadAppAuth = () => {
|
||||
if (userdata === undefined || userdata === null) {
|
||||
setErrorMessage("You need to be logged in to try usecases. Redirecting in 5 seconds...")
|
||||
// Send the user to the login screen after 3 seconds
|
||||
setTimeout(() => {
|
||||
// Make it cancel if the state modalOpen changes
|
||||
if (modalOpen === false) {
|
||||
return
|
||||
}
|
||||
|
||||
navigate("/login?view=" + window.location.pathname + window.location.search)
|
||||
}, 4500)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
fetch(`${globalUrl}/api/v1/apps/authentication`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting app auth :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
toast("Failed to get app auth: " + responseJson.reason);
|
||||
return
|
||||
}
|
||||
|
||||
var newauth = [];
|
||||
for (let authkey in responseJson.data) {
|
||||
if (responseJson.data[authkey].defined === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newauth.push(responseJson.data[authkey]);
|
||||
}
|
||||
|
||||
setAppAuthentication(newauth);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
console.log("New auth error: ", error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const getGeneratedWorkflow = () => {
|
||||
// POST
|
||||
// https://shuffler.io/api/v1/workflows/merge
|
||||
// destination: {app_id: "b9c2feaf99b6309dabaeaa8518c61d3d", app_name: "Servicenow_API", app_version: "",…}
|
||||
// id: ""
|
||||
// middle:[]
|
||||
// name: "Email analysis"
|
||||
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
|
||||
|
||||
|
||||
if (srcapp.includes(":default") || dstapp.includes(":default")) {
|
||||
toast("You need to select both a source and destination app before generating this workflow.")
|
||||
return
|
||||
}
|
||||
|
||||
setWorkflowLoading(true)
|
||||
|
||||
// FIXME: Remove hardcoding here after testing, and user srcapp/dstapp
|
||||
const newsrcapp = srcapp
|
||||
const newdstapp = dstapp
|
||||
|
||||
const mergedata = {
|
||||
name: title,
|
||||
id: "",
|
||||
source: {
|
||||
app_name: newsrcapp,
|
||||
},
|
||||
middle: [],
|
||||
destination: {
|
||||
app_name: newdstapp,
|
||||
},
|
||||
}
|
||||
|
||||
//fetch(globalUrl + "/api/v1/workflows/merge", {
|
||||
fetch("https://shuffler.io/api/v1/workflows/merge", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(mergedata),
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
setWorkflowLoading(false)
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
console.log("Error in workflow template: ", responseJson.error);
|
||||
|
||||
setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.")
|
||||
|
||||
setIsActive(true)
|
||||
//setTimeout(() => {
|
||||
// setModalOpen(false)
|
||||
//}, 5000)
|
||||
} else {
|
||||
console.log("Success in workflow template: ", responseJson);
|
||||
setIsActive(true)
|
||||
if (responseJson.workflow_id === "") {
|
||||
console.log("Failed to build workflow for these tools. Closing in 3 seconds.")
|
||||
return
|
||||
}
|
||||
|
||||
getWorkflow(responseJson.workflow_id)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const isFinished = () => {
|
||||
// Look for configuration fields being done in the current modal
|
||||
// 1. Start by finding the modal
|
||||
const template = document.getElementById("workflow-template")
|
||||
if (template === null || template == undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Find item in template with id app-config
|
||||
const appconfig = template.getElementsByClassName("app-config")
|
||||
if (appconfig === null || appconfig == undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const ModalView = () => {
|
||||
return (
|
||||
<Drawer
|
||||
anchor={"left"}
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: "black",
|
||||
color: "white",
|
||||
minWidth: 700,
|
||||
maxWidth: 700,
|
||||
paddingTop: 75,
|
||||
itemAlign: "center",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
zIndex: 5000,
|
||||
position: "absolute",
|
||||
top: 14,
|
||||
right: 14,
|
||||
color: "white",
|
||||
}}
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent style={{marginTop: 0, marginLeft: 75, maxWidth: 470, }}>
|
||||
<Typography variant="h4">
|
||||
<b>Configure Workflow</b>
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}>
|
||||
Selected Workflow:
|
||||
</Typography>
|
||||
<div style={{marginBottom: 0, }} id="workflow-template">
|
||||
<WorkflowTemplatePopup2
|
||||
globalUrl={globalUrl}
|
||||
img1={img1}
|
||||
srcapp={srcapp}
|
||||
img2={img2}
|
||||
dstapp={dstapp}
|
||||
title={title}
|
||||
description={description}
|
||||
visualOnly={true}
|
||||
/>
|
||||
</div>
|
||||
{workflowLoading ?
|
||||
<div style={{marginTop: 75, textAlign: "center", }}>
|
||||
<Typography variant="h4"> Generating the Workflow...
|
||||
</Typography>
|
||||
<CircularProgress style={{marginLeft: 125, marginTop: 10, }}/>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<Typography variant="h6" style={{marginTop: 75, }}>
|
||||
{errorMessage !== "" ? errorMessage : ""}
|
||||
</Typography>
|
||||
</div>
|
||||
}
|
||||
<ConfigureWorkflow
|
||||
userdata={userdata}
|
||||
theme={theme}
|
||||
globalUrl={globalUrl}
|
||||
workflow={workflow}
|
||||
appAuthentication={appAuthentication}
|
||||
setAppAuthentication={setAppAuthentication}
|
||||
apps={apps}
|
||||
/>
|
||||
{errorMessage === "" ?
|
||||
<Button
|
||||
style={{marginTop: 50, }}
|
||||
variant={isFinished() ? "contained" : "outlined"}
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
: null}
|
||||
</DialogContent>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
var parsedTitle = title
|
||||
const maxlength = 30
|
||||
if (title.length > maxlength) {
|
||||
parsedTitle = title.substring(0, maxlength) + "..."
|
||||
}
|
||||
|
||||
parsedTitle = parsedTitle.replaceAll("_", " ")
|
||||
|
||||
const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : ""
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", maxWidth: isCloud ? 470 : 450, minWidth: isCloud ? 470 : 450, height: 78, borderRadius: 8 }}>
|
||||
<ModalView />
|
||||
<div
|
||||
// variant={isActive === 1 ? "contained" : "outlined"}
|
||||
color="secondary"
|
||||
disabled={visualOnly === true}
|
||||
style={{
|
||||
margin: 4,
|
||||
width: "100%",
|
||||
borderRadius: 8,
|
||||
textTransform: "none",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
border: isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? "1px solid #f85a3e" : "1px solid rgba(33, 33, 33, 1)",
|
||||
cursor: isActive ? errorMessage !== "" ? "not-allowed" : "pointer" : "pointer",
|
||||
padding: "10px 20px 10px 20px",
|
||||
position: "relative",
|
||||
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setIsHovered(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setIsHovered(false)
|
||||
}}
|
||||
onClick={() => {
|
||||
if (visualOnly === true) {
|
||||
console.log("Not showing more than visuals.")
|
||||
return
|
||||
}
|
||||
|
||||
//setIsActive(!isActive)
|
||||
if (errorMessage !== "") {
|
||||
toast("Already failed to generate a workflow for this usecase. Please try again later or contact support@shuffler.io.")
|
||||
|
||||
setModalOpen(true)
|
||||
} else if (isActive) {
|
||||
toast("Workflow already generated. Please try another workflow template!")
|
||||
|
||||
// FIXME: Remove these?
|
||||
loadAppAuth()
|
||||
setModalOpen(true)
|
||||
//getGeneratedWorkflow()
|
||||
} else {
|
||||
|
||||
loadAppAuth()
|
||||
setModalOpen(true)
|
||||
getGeneratedWorkflow()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", itemAlign: "left", textAlign: "left", }}>
|
||||
<div style={{display: "flex", flex: 1, marginTop: 3, }}>
|
||||
{img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ?
|
||||
<Tooltip title={srcapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
|
||||
<span style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
|
||||
<img src={img1} style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleDefault : imagestyle} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
:
|
||||
<span style={{width: 50, }} />
|
||||
}
|
||||
{img2 !== undefined && img2 !== "" && dstapp !== undefined && dstapp !== "" ?
|
||||
<Tooltip title={dstapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
|
||||
<span style={{display: "flex", }}>
|
||||
<TrendingFlatIcon style={{ marginTop: 7, }} />
|
||||
<span style={dstapp !== undefined && dstapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
|
||||
<img src={img2} style={dstapp !== undefined && dstapp.includes(":default") ? imagestyleDefault : imagestyle} />
|
||||
</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
:
|
||||
<span style={{width: 50, }} />
|
||||
}
|
||||
</div>
|
||||
<div style={{ flex: 3, marginLeft: 20, }}>
|
||||
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, }} color="rgba(241, 241, 241, 1)">
|
||||
{parsedTitle}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, marginRight: 0, maxHeight: 16, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
|
||||
{parsedDescription}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{isActive === true && errorMessage === "" ?
|
||||
<CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowTemplatePopup
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import {Link} from 'react-router-dom';
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@material-ui/icons';
|
||||
import { Search as SearchIcon, CloudQueue as CloudQueueIcon, Code as CodeIcon } from '@mui/icons-material';
|
||||
|
||||
//import algoliasearch from 'algoliasearch/lite';
|
||||
import algoliasearch from 'algoliasearch';
|
||||
import { InstantSearch, connectSearchBox, connectHits } from 'react-instantsearch-dom';
|
||||
import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@material-ui/core';
|
||||
import { Grid, Paper, TextField, ButtonBase, InputAdornment, Typography, Button, Tooltip} from '@mui/material';
|
||||
|
||||
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
|
||||
const WorkflowSearch = props => {
|
||||
@@ -16,7 +16,6 @@ const WorkflowSearch = props => {
|
||||
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("");
|
||||
@@ -54,7 +53,7 @@ const WorkflowSearch = props => {
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.reason)
|
||||
//alert.info("Thanks for submitting!")
|
||||
//toast("Thanks for submitting!")
|
||||
} else {
|
||||
setFormMessage(errorMessage)
|
||||
}
|
||||
|
||||
@@ -1,432 +0,0 @@
|
||||
const data = [
|
||||
{
|
||||
selector: "node",
|
||||
css: {
|
||||
label: "data(label)",
|
||||
"text-valign": "center",
|
||||
"font-family":
|
||||
"Segoe UI, Tahoma, Geneva, Verdana, sans-serif, sans-serif",
|
||||
"font-weight": "lighter",
|
||||
"margin-right": "10px",
|
||||
"font-size": "18px",
|
||||
width: "80px",
|
||||
height: "80px",
|
||||
color: "white",
|
||||
padding: "10px",
|
||||
margin: "5px",
|
||||
"border-width": "1px",
|
||||
"text-margin-x": "10px",
|
||||
"z-index": 5001,
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "edge",
|
||||
css: {
|
||||
"target-arrow-shape": "triangle",
|
||||
"target-arrow-color": "grey",
|
||||
"curve-style": "unbundled-bezier",
|
||||
label: "data(label)",
|
||||
"text-margin-y": "-15px",
|
||||
width: "5px",
|
||||
color: "white",
|
||||
"line-fill": "linear-gradient",
|
||||
"line-gradient-stop-positions": ["0.0", "100"],
|
||||
"line-gradient-stop-colors": ["grey", "grey"],
|
||||
"z-index": 5001,
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[type="ACTION"]`,
|
||||
css: {
|
||||
shape: "roundrectangle",
|
||||
"background-color": "#213243",
|
||||
"border-color": "#81c784",
|
||||
"background-width": "100%",
|
||||
"background-height": "100%",
|
||||
"border-radius": "5px",
|
||||
"z-index": 5001,
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[type="COMMENT"]`,
|
||||
css: {
|
||||
shape: "roundrectangle",
|
||||
color: "data(color)",
|
||||
width: "data(width)",
|
||||
height: "data(height)",
|
||||
padding: "0px",
|
||||
margin: "0px",
|
||||
"background-color": "data(backgroundcolor)",
|
||||
"background-image": "data(backgroundimage)",
|
||||
"border-color": "#ffffff",
|
||||
"text-margin-x": "0px",
|
||||
"z-index": 4999,
|
||||
"border-radius": "5px",
|
||||
"background-opacity": "0.5",
|
||||
"text-wrap": "wrap",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[app_name="Shuffle Tools"]`,
|
||||
css: {
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
"z-index": 5000,
|
||||
"font-size": "0px",
|
||||
"background-width": "75%",
|
||||
"background-height": "75%",
|
||||
"background-color": "data(iconBackground)",
|
||||
"background-fill": "data(fillstyle)",
|
||||
"background-gradient-direction": "to-right",
|
||||
"background-gradient-stop-colors": "data(fillGradient)",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[app_name="Testing"]`,
|
||||
css: {
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
"z-index": 5000,
|
||||
"font-size": "0px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[?small_image]`,
|
||||
css: {
|
||||
"background-image": "data(small_image)",
|
||||
"text-halign": "right",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[?large_image]`,
|
||||
css: {
|
||||
"background-image": "data(large_image)",
|
||||
"text-halign": "right",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[type="CONDITION"]`,
|
||||
css: {
|
||||
shape: "diamond",
|
||||
"border-color": "##FFEB3B",
|
||||
padding: "30px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[type="eventAction"]`,
|
||||
css: {
|
||||
"background-color": "#edbd21",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[type="TRIGGER"]`,
|
||||
css: {
|
||||
shape: "octagon",
|
||||
"border-radius": "5px",
|
||||
"border-color": "orange",
|
||||
"background-color": "#213243",
|
||||
"background-width": "100%",
|
||||
"background-height": "100%",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[status="running"]`,
|
||||
css: {
|
||||
"border-color": "#81c784",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[status="stopped"]`,
|
||||
css: {
|
||||
"border-color": "orange",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'node[type="mq"]',
|
||||
css: {
|
||||
"background-color": "#edbd21",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node[?isButton]",
|
||||
css: {
|
||||
shape: "ellipse",
|
||||
width: "15px",
|
||||
height: "15px",
|
||||
"z-index": "5002",
|
||||
"font-size": "0px",
|
||||
border: "1px solid rgba(255,255,255,0.9)",
|
||||
"background-image": "data(icon)",
|
||||
"background-color": "data(iconBackground)",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node[?isSuggestion]",
|
||||
css: {
|
||||
shape: "ellipse",
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
"z-index": "5002",
|
||||
"font-size": "0px",
|
||||
border: "1px solid rgba(255,255,255,0.9)",
|
||||
"background-image": "data(large_image)",
|
||||
"background-color": "data(iconBackground)",
|
||||
label: "data(label)",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node[?canConnect]",
|
||||
css: {
|
||||
"border-color": "#f86a3e",
|
||||
"border-width": "10px",
|
||||
"z-index": "5002",
|
||||
"background-color": "#f86a3e",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node[?isDescriptor]",
|
||||
css: {
|
||||
shape: "ellipse",
|
||||
"border-color": "#80deea",
|
||||
width: "5px",
|
||||
height: "5px",
|
||||
"z-index": "5002",
|
||||
"font-size": "10px",
|
||||
"text-valign": "center",
|
||||
"text-halign": "center",
|
||||
border: "1px solid black",
|
||||
"margin-right": "0px",
|
||||
"text-margin-x": "0px",
|
||||
"background-color": "data(imageColor)",
|
||||
"background-image": "data(image)",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node[?isStartNode]",
|
||||
css: {
|
||||
shape: "ellipse",
|
||||
"border-color": "#80deea",
|
||||
width: "80px",
|
||||
height: "80px",
|
||||
"font-size": "18px",
|
||||
"background-width": "100%",
|
||||
"background-height": "100%",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node[!is_valid]",
|
||||
css: {
|
||||
"border-color": "red",
|
||||
"border-width": "10px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ":selected",
|
||||
css: {
|
||||
"background-color": "#77b0d0",
|
||||
"border-color": "#77b0d0",
|
||||
"border-width": "20px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".skipped-highlight",
|
||||
css: {
|
||||
"background-color": "grey",
|
||||
"border-color": "grey",
|
||||
"border-width": "8px",
|
||||
"transition-property": "background-color",
|
||||
"transition-duration": "0.5s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".success-highlight",
|
||||
css: {
|
||||
"background-color": "#41dcab",
|
||||
"border-color": "#41dcab",
|
||||
"border-width": "5px",
|
||||
"transition-property": "background-color",
|
||||
"transition-duration": "0.5s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".hover-highlight",
|
||||
css: {
|
||||
"background-color": "#5f9265",
|
||||
"border-color": "#5f9265",
|
||||
"border-width": "5px",
|
||||
"transition-property": "background-color",
|
||||
"transition-duration": "0.5s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".failure-highlight",
|
||||
css: {
|
||||
"background-color": "#8e3530",
|
||||
"border-color": "#8e3530",
|
||||
"border-width": "5px",
|
||||
"transition-property": "background-color",
|
||||
"transition-duration": "0.5s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".not-executing-highlight",
|
||||
css: {
|
||||
"background-color": "grey",
|
||||
"border-color": "grey",
|
||||
"border-width": "5px",
|
||||
"transition-property": "#ffef47",
|
||||
"transition-duration": "0.25s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".executing-highlight",
|
||||
css: {
|
||||
"background-color": "#ffef47",
|
||||
"border-color": "#ffef47",
|
||||
"border-width": "8px",
|
||||
"transition-property": "border-width",
|
||||
"transition-duration": "0.25s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".awaiting-data-highlight",
|
||||
css: {
|
||||
"background-color": "#f4ad42",
|
||||
"border-color": "#f4ad42",
|
||||
"border-width": "5px",
|
||||
"transition-property": "border-color",
|
||||
"transition-duration": "0.5s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".shuffle-hover-highlight",
|
||||
css: {
|
||||
"background-color": "#f85a3e",
|
||||
"border-color": "#f85a3e",
|
||||
"border-width": "12px",
|
||||
"transition-property": "border-width",
|
||||
"transition-duration": "0.25s",
|
||||
label: "data(label)",
|
||||
"font-size": "18px",
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "$node > node",
|
||||
css: {
|
||||
"padding-top": "10px",
|
||||
"padding-left": "10px",
|
||||
"padding-bottom": "10px",
|
||||
"padding-right": "10px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "edge.executing-highlight",
|
||||
css: {
|
||||
width: "5px",
|
||||
"target-arrow-color": "#ffef47",
|
||||
"line-color": "#ffef47",
|
||||
"transition-property": "line-color, width",
|
||||
"transition-duration": "0.25s",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `edge[?decorator]`,
|
||||
css: {
|
||||
width: "1px",
|
||||
"line-style": "dashed",
|
||||
"line-fill": "linear-gradient",
|
||||
"target-arrow-color": "#f34079",
|
||||
"line-gradient-stop-positions": ["0.0", "100"],
|
||||
"line-gradient-stop-colors": ["#f86a3e", "#f34079"],
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "edge.success-highlight",
|
||||
css: {
|
||||
width: "5px",
|
||||
"target-arrow-color": "#41dcab",
|
||||
"line-color": "#41dcab",
|
||||
"transition-property": "line-color, width",
|
||||
"transition-duration": "0.5s",
|
||||
"line-fill": "linear-gradient",
|
||||
"line-gradient-stop-positions": ["0.0", "100"],
|
||||
"line-gradient-stop-colors": ["#41dcab", "#41dcab"],
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".eh-handle",
|
||||
style: {
|
||||
"background-color": "#337ab7",
|
||||
width: "1px",
|
||||
height: "1px",
|
||||
shape: "circle",
|
||||
"border-width": "1px",
|
||||
"border-color": "black",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".eh-source",
|
||||
style: {
|
||||
"border-width": "3",
|
||||
"border-color": "#337ab7",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".eh-target",
|
||||
style: {
|
||||
"border-width": "3",
|
||||
"border-color": "#337ab7",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".eh-preview, .eh-ghost-edge",
|
||||
style: {
|
||||
"background-color": "#337ab7",
|
||||
"line-color": "#337ab7",
|
||||
"target-arrow-color": "#337ab7",
|
||||
"source-arrow-color": "#337ab7",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "edge:selected",
|
||||
css: {
|
||||
"target-arrow-color": "#f85a3e",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `edge[?source_workflow]`,
|
||||
css: {
|
||||
"background-opacity": "1",
|
||||
"font-size": "0px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: `node[?source_workflow]`,
|
||||
css: {
|
||||
"background-opacity": "0",
|
||||
"font-size": "0px",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: "node:selected",
|
||||
css: {
|
||||
"border-color": "#f86a3e",
|
||||
"border-width": "7px",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
//{
|
||||
// selector: 'edge[?hasErrors]',
|
||||
// css: {
|
||||
// 'target-arrow-color': '#991818',
|
||||
// 'line-color': '#991818',
|
||||
// 'line-style': 'dashed',
|
||||
// "line-fill": "linear-gradient",
|
||||
// "line-gradient-stop-positions": ["0.0", "100"],
|
||||
// "line-gradient-stop-colors": ["#991818", "#991818"],
|
||||
// },
|
||||
//},
|
||||
|
||||
export default data;
|
||||
@@ -63,7 +63,7 @@ const data = [
|
||||
"z-index": 4999,
|
||||
"border-radius": "5px",
|
||||
"background-opacity": "0.5",
|
||||
"text-wrap": "wrap",
|
||||
"text-wrap": "wrap",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -125,8 +125,8 @@ const data = [
|
||||
"border-radius": "5px",
|
||||
"border-color": "orange",
|
||||
"background-color": "#213243",
|
||||
"background-width": "100%",
|
||||
"background-height": "100%",
|
||||
"background-width": "100px",
|
||||
"background-height": "100px",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -163,15 +163,16 @@ const data = [
|
||||
{
|
||||
selector: "node[?isSuggestion]",
|
||||
css: {
|
||||
shape: "ellipse",
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
shape: "roundrectangle",
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
"z-index": "5002",
|
||||
"font-size": "0px",
|
||||
filter: "grayscale(100%)",
|
||||
border: "1px solid rgba(255,255,255,0.9)",
|
||||
"background-image": "data(large_image)",
|
||||
"background-color": "data(iconBackground)",
|
||||
label: "data(label)",
|
||||
"background-fit": "cover",
|
||||
"font-size": "20px",
|
||||
label: "data(label_replaced)",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -199,7 +200,7 @@ const data = [
|
||||
"text-margin-x": "0px",
|
||||
"background-color": "data(imageColor)",
|
||||
"background-image": "data(image)",
|
||||
label: "data(label)",
|
||||
label: "data(label)",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -337,9 +338,9 @@ const data = [
|
||||
width: "1px",
|
||||
"line-style": "dashed",
|
||||
"line-fill": "linear-gradient",
|
||||
"target-arrow-color": "#f34079",
|
||||
"target-arrow-color": "#555555",
|
||||
"line-gradient-stop-positions": ["0.0", "100"],
|
||||
"line-gradient-stop-colors": ["#f86a3e", "#f34079"],
|
||||
"line-gradient-stop-colors": ["#555555", "#555555"],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+12
-8
@@ -1,12 +1,16 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("root"));
|
||||
//import "./index.css";
|
||||
//import reportWebVitals from "./reportWebVitals";
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: http://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
const rootElement = document.getElementById("root");
|
||||
const root = createRoot(rootElement);
|
||||
root.render(
|
||||
<React.Fragment>
|
||||
<App />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
//reportWebVitals();
|
||||
|
||||
File diff suppressed because one or more lines are too long
+36
-43
@@ -1,42 +1,49 @@
|
||||
import React from "react";
|
||||
import { createMuiTheme } from "@material-ui/core/styles";
|
||||
import { createTheme, adaptV4Theme } from "@mui/material/styles";
|
||||
|
||||
const theme = createMuiTheme({
|
||||
//const theme = createTheme({
|
||||
const theme = createTheme(adaptV4Theme({
|
||||
palette: {
|
||||
theme: "dark",
|
||||
main: "#F86743",
|
||||
primary: {
|
||||
main: "#F86743",
|
||||
contrastText: "#ffffff",
|
||||
contrastText: "#ffffff",
|
||||
},
|
||||
secondary: {
|
||||
main: "#e8eaf6",
|
||||
contrastText: "#000000",
|
||||
},
|
||||
text: {
|
||||
secondary: "rgba(255,255,255,0.7)",
|
||||
},
|
||||
type: "dark",
|
||||
inputColor: "rgba(39,41,45,1)",
|
||||
//inputColor: "#383B40",
|
||||
surfaceColor: "#27292d",
|
||||
inputColor: "#383B40",
|
||||
platformColor: "#1c1c1d",
|
||||
backgroundColor: "#1a1a1a",
|
||||
green: "#5cc879",
|
||||
borderRadius: 5,
|
||||
defaultBorder: "1px solid rgba(255,255,255,0.3)",
|
||||
jsonTheme: "brewer",
|
||||
reactJsonStyle: {
|
||||
borderRadius: 5,
|
||||
border: "1px solid rgba(255,255,255,0.7)",
|
||||
padding: 5,
|
||||
},
|
||||
jsonTheme: "brewer",
|
||||
reactJsonStyle: {
|
||||
borderRadius: 5,
|
||||
border: "1px solid rgba(255,255,255,0.7)",
|
||||
padding: 5,
|
||||
},
|
||||
textFieldStyle: {
|
||||
backgroundColor: "#383B40",
|
||||
borderRadius: 5,
|
||||
},
|
||||
innerTextfieldStyle: {
|
||||
color: "white",
|
||||
minHeight: 50,
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
borderRadius: 5,
|
||||
// Removed since upgrading to mui 18
|
||||
//color: "white",
|
||||
//minHeight: 50,
|
||||
//marginLeft: "5px",
|
||||
//maxWidth: "95%",
|
||||
//fontSize: "1em",
|
||||
//borderRadius: 5,
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: "white",
|
||||
@@ -71,34 +78,20 @@ const theme = createMuiTheme({
|
||||
},
|
||||
},
|
||||
MuiCssBaseline: {
|
||||
'@global': {
|
||||
/*
|
||||
scrollbarColor: "#6b6b6b #2b2b2b",
|
||||
"&::-webkit-scrollbar, & *::-webkit-scrollbar": {
|
||||
backgroundColor: "#2b2b2b",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb, & *::-webkit-scrollbar-thumb": {
|
||||
borderRadius: 8,
|
||||
backgroundColor: "#6b6b6b",
|
||||
minHeight: 24,
|
||||
border: "3px solid #2b2b2b",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb:focus, & *::-webkit-scrollbar-thumb:focus": {
|
||||
backgroundColor: "#959595",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb:active, & *::-webkit-scrollbar-thumb:active": {
|
||||
backgroundColor: "#959595",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb:hover, & *::-webkit-scrollbar-thumb:hover": {
|
||||
backgroundColor: "#959595",
|
||||
},
|
||||
"&::-webkit-scrollbar-corner, & *::-webkit-scrollbar-corner": {
|
||||
backgroundColor: "#2b2b2b",
|
||||
},
|
||||
*/
|
||||
}
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: `
|
||||
@font-face {
|
||||
font-family: 'roboto';
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-weight: 300;
|
||||
src: local('roboto'), local('roboto'), format('truetype');
|
||||
unicodeRange: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF;
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
export default theme;
|
||||
|
||||
+203
-195
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState } from "react";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
|
||||
import {
|
||||
CircularProgress,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Button,
|
||||
Paper,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
|
||||
+2647
-1280
File diff suppressed because it is too large
Load Diff
+466
-198
File diff suppressed because it is too large
Load Diff
@@ -1,468 +0,0 @@
|
||||
|
||||
import { Grid, Divider, List, ListItem, ListItemText } from "@mui/material";
|
||||
import { experimentalStyled as styled } from '@mui/material/styles';
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from '@mui/material/Button';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
|
||||
import algoliasearch from "algoliasearch";
|
||||
//import algoliarecommend from "algoliarecommend";
|
||||
|
||||
const searchClient = algoliasearch(
|
||||
"JNSS5CFDZZ",
|
||||
"db08e40265e2941b9a7d8f644b6e5240"
|
||||
);
|
||||
|
||||
// https://www.algolia.com/doc/api-client/getting-started/install/
|
||||
/*const algoliarecommend = require('@algolia/recommend');
|
||||
|
||||
const client = algoliarecommend(
|
||||
"NSS5CFDZZ",
|
||||
"db08e40265e2941b9a7d8f644b6e5240"
|
||||
);*/
|
||||
|
||||
|
||||
const Item = styled(Paper)(({ theme }) => ({
|
||||
padding: theme.spacing(2),
|
||||
border: "0.0625rem solid #b2b2b2",
|
||||
borderRadius: "1.5625rem",
|
||||
boxSizing: "content-box",
|
||||
backgroundColor: "transparent",
|
||||
width: "200px",
|
||||
textAlign: 'center',
|
||||
color: theme.palette.text.secondary,
|
||||
marginTop: "20px",
|
||||
marginBottom: "20px",
|
||||
color: "textPrimary"
|
||||
}));
|
||||
const AppExplorer = (props) => {
|
||||
const [algoliaResult, setAlgoliaResult] = useState("");
|
||||
|
||||
const runAlgoliaAppSearch = (query) => {
|
||||
const index = searchClient.initIndex("appsearch");
|
||||
|
||||
index
|
||||
.search(`${query}`)
|
||||
.then(({ hits }) => {
|
||||
setAlgoliaResult(hits);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
runAlgoliaAppSearch("wazuh")
|
||||
|
||||
}, [])
|
||||
|
||||
|
||||
const brandApp = () => {
|
||||
const index = searchClient.initIndex("appsearch");
|
||||
|
||||
const replicaIndex = searchClient.initIndex('appsearch');
|
||||
replicaIndex.setSettings({
|
||||
customRanking: [
|
||||
"asc(time_edited)"
|
||||
]
|
||||
})
|
||||
|
||||
.then(({ hits }) => {
|
||||
console.log(hits);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
brandApp()
|
||||
|
||||
}, [])
|
||||
|
||||
/*const trandingApp = () => {
|
||||
|
||||
const index = client.getTrendingGlobalItems([
|
||||
{
|
||||
indexName: "appsearch",
|
||||
threshold: 60
|
||||
},
|
||||
])
|
||||
.then(({ results }) => {
|
||||
console.log(results);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
trandingApp();
|
||||
|
||||
}, [])
|
||||
*/
|
||||
|
||||
const SideBar = {
|
||||
minWidth: 250,
|
||||
|
||||
borderRight: "1px solid rgba(255,255,255,0.3)",
|
||||
left: 0,
|
||||
position: "sticky",
|
||||
minHeight: "90vh",
|
||||
maxHeight: "90vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
color: "white"
|
||||
};
|
||||
const contentbar = {
|
||||
padding: "40px",
|
||||
|
||||
};
|
||||
const boxdata = {
|
||||
paddingLeft: "30px"
|
||||
}
|
||||
const link = {
|
||||
textDecoration: "none"
|
||||
}
|
||||
const catItems = (
|
||||
<div style={SideBar}>
|
||||
<List>
|
||||
<ListItem >
|
||||
<ListItemText>
|
||||
<span><Typography variant="primary">Categories</Typography></span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<span></span>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
ASSETS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
CASES
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
COMMS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
EDR & AV
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
IAM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
INTEL
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
NETWORK
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary">
|
||||
SIEM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: "flex" }}>
|
||||
|
||||
{catItems}
|
||||
<div style={contentbar}>
|
||||
<Grid>
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Getting Started
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 4 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(algoliaResult.length)).map((_, index) => (
|
||||
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href={algoliaResult[0]["objectID"]} style={link}>
|
||||
<Item>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src={algoliaResult[0]["image_url"]} alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
{algoliaResult[0]["name"]}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
{algoliaResult[0]["description"].substring(0, 20)}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Most Popular
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href="#" style={link}>
|
||||
<Item>
|
||||
<div class="row">
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src="/images/testing.png" alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
App Name
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
Description
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Brand New
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href="#" style={link}>
|
||||
<Item>
|
||||
<div class="row">
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src="/images/testing.png" alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
App Name
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
Description
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
|
||||
<div className="row" >
|
||||
<div className="column" style={{ float: "left", width: "33.33%", marginTop: "20px", marginBottom: "20px", }}>
|
||||
<img src="/images/shuffle_logo.png" alt="shuffle" width="200px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
<Grid item xl={8} style={{ "border": "20px" }}>
|
||||
|
||||
<Typography type="title" variant="h6">
|
||||
Hybrid work
|
||||
</Typography>
|
||||
<div style={{
|
||||
paddingLeft: "50px",
|
||||
|
||||
}}>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={{ xs: 1, md: 3 }}
|
||||
columns={{ xs: 4, sm: 8, md: 12 }}
|
||||
>
|
||||
{Array.from(Array(3)).map((_, index) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={index}>
|
||||
<a href="#" style={link}>
|
||||
<Item>
|
||||
<div class="row">
|
||||
<div class="column" style={{ float: "left" }}>
|
||||
<img src="/images/testing.png" alt="shuffle" width="50px" />
|
||||
</div>
|
||||
<div class="column " style={boxdata}>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body1">
|
||||
App Name
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={boxdata}>
|
||||
<Typography align="left" variant="body2">
|
||||
Description
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Item>
|
||||
</a>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
<div className="row" style={{ display: "flex" }}>
|
||||
<div className="col" style={{ width: "40%", marginTop: "50px" }}>
|
||||
<Typography variant="h6">Don't see it? Build it!</Typography>
|
||||
<Typography variant="body2">Use our APIs to create an app that makes your working life better.And maybe even share it with the world.</Typography>
|
||||
<a href="#" target="_blank" rel="nonref"
|
||||
style={{
|
||||
background: "#FF4500",
|
||||
borderRadius: "3.125rem",
|
||||
color: "#fff",
|
||||
display: "block",
|
||||
fontSize: ".9375rem",
|
||||
fontWeight: "500",
|
||||
height: "1rem",
|
||||
letterSpacing: "-.02em",
|
||||
lineHeight: ".875rem",
|
||||
marginTop: "1.5rem",
|
||||
padding: "1.3125rem 1.375rem",
|
||||
textAlign: "center",
|
||||
textDecoration: "none",
|
||||
width: "8.5rem"
|
||||
}}><span>visit developer portal</span></a>
|
||||
</div>
|
||||
<div className="col" style={{ float: "right" }}>
|
||||
<div className="row" style={{ float: "left", marginLeft: "90px" }}>
|
||||
<div className="column" style={{ float: "left", margin: "60px 10px 20px 30px" }}>
|
||||
<img src="/images/demo1.png" alt="shuffle" width="90px" />
|
||||
</div>
|
||||
<div className="column" style={{ float: "left", margin: "60px 10px 20px 30px" }}>
|
||||
<img src="/images/demo1.png" alt="shuffle" width="90px" />
|
||||
</div>
|
||||
<div className="column" style={{ float: "left", margin: "60px 10px 20px 30px" }}>
|
||||
<img src="/images/demo1.png" alt="shuffle" width="90px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
export default AppExplorer;
|
||||
@@ -1,789 +0,0 @@
|
||||
import React from "react";
|
||||
import { Grid, Container, Divider, CardMedia, List, ListItem, ListItemText } from "@mui/material";
|
||||
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableContainer from "@material-ui/core/TableContainer";
|
||||
import TableHead from "@material-ui/core/TableHead";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
import { LineChart, LineSeries, BarChart } from "reaviz";
|
||||
import { Gridline, GridStripe } from "reaviz";
|
||||
import { GridlineSeries } from "reaviz";
|
||||
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import Select from '@material-ui/core/Select';
|
||||
|
||||
import { styled, alpha } from '@mui/material/styles';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Box from '@mui/material/Box';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import InputBase from '@mui/material/InputBase';
|
||||
import Badge from '@mui/material/Badge';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import AccountCircle from '@mui/icons-material/AccountCircle';
|
||||
import MailIcon from '@mui/icons-material/Mail';
|
||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||
import MoreIcon from '@mui/icons-material/MoreVert';
|
||||
import SearchField from "../components/Searchfield";
|
||||
import { SpaRounded } from "@material-ui/icons";
|
||||
import { isMobile } from "react-device-detect"
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: new Date("11/29/2019"),
|
||||
data: 10,
|
||||
},
|
||||
{
|
||||
key: new Date("11/30/2019"),
|
||||
data: 14,
|
||||
},
|
||||
{
|
||||
key: new Date("12/01/2019"),
|
||||
data: 5,
|
||||
},
|
||||
{
|
||||
key: new Date("12/02/2019"),
|
||||
data: 18,
|
||||
},
|
||||
];
|
||||
|
||||
const useStyles1 = makeStyles((theme) => ({
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
root: {
|
||||
minWidth: 275,
|
||||
},
|
||||
bullet: {
|
||||
display: "inline-block",
|
||||
margin: "0 2px",
|
||||
transform: "scale(0.8)",
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
},
|
||||
pos: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
});
|
||||
|
||||
function createData(name, calories, fat, carbs, protein) {
|
||||
return { name, calories, fat, carbs, protein };
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
|
||||
createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
|
||||
createData("Eclair", 262, 16.0, 24, 6.0),
|
||||
createData("Cupcake", 305, 3.7, 67, 4.3),
|
||||
createData("Gingerbread", 356, 16.0, 49, 3.9),
|
||||
];
|
||||
|
||||
const Search = styled('div')(({ theme }) => ({
|
||||
position: 'relative',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: alpha(theme.palette.common.white, 0.15),
|
||||
'&:hover': {
|
||||
backgroundColor: alpha(theme.palette.common.white, 0.25),
|
||||
},
|
||||
marginRight: theme.spacing(2),
|
||||
marginLeft: 0,
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginLeft: theme.spacing(3),
|
||||
width: 'auto',
|
||||
},
|
||||
}));
|
||||
|
||||
const SearchIconWrapper = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(0, 2),
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}));
|
||||
|
||||
const StyledInputBase = styled(InputBase)(({ theme }) => ({
|
||||
color: 'inherit',
|
||||
'& .MuiInputBase-input': {
|
||||
padding: theme.spacing(1, 1, 1, 0),
|
||||
// vertical padding + font size from searchIcon
|
||||
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
|
||||
transition: theme.transitions.create('width'),
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '20ch',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
function PrimarySearchAppBar() {
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null);
|
||||
|
||||
const isMenuOpen = Boolean(anchorEl);
|
||||
const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
|
||||
|
||||
const handleProfileMenuOpen = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMobileMenuClose = () => {
|
||||
setMobileMoreAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
handleMobileMenuClose();
|
||||
};
|
||||
|
||||
const handleMobileMenuOpen = (event) => {
|
||||
setMobileMoreAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const menuId = 'primary-search-account-menu';
|
||||
const renderMenu = (
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
id={menuId}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={isMenuOpen}
|
||||
onClose={handleMenuClose}
|
||||
>
|
||||
<MenuItem onClick={handleMenuClose}>Profile</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose}>My account</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const mobileMenuId = 'primary-search-account-menu-mobile';
|
||||
const renderMobileMenu = (
|
||||
<Menu
|
||||
anchorEl={mobileMoreAnchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
id={mobileMenuId}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={isMobileMenuOpen}
|
||||
onClose={handleMobileMenuClose}
|
||||
>
|
||||
<MenuItem>
|
||||
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
|
||||
<Badge badgeContent={4} color="error">
|
||||
<MailIcon />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<p>Messages</p>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<IconButton
|
||||
size="large"
|
||||
aria-label="show 17 new notifications"
|
||||
color="inherit"
|
||||
>
|
||||
<Badge badgeContent={17} color="error">
|
||||
<NotificationsIcon />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<p>Notifications</p>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleProfileMenuOpen}>
|
||||
<IconButton
|
||||
size="large"
|
||||
aria-label="account of current user"
|
||||
aria-controls="primary-search-account-menu"
|
||||
aria-haspopup="true"
|
||||
color="inherit"
|
||||
>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
<p>Profile</p>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<AppBar position="fixed" style={{ backgroundColor: "black", boxShadow: "unset" }}>
|
||||
<Toolbar>
|
||||
<img src="/images/Shuffle_logo.png" style={{ height: "3rem", width: "3rem" }} alt="shuffle img" />
|
||||
<SearchField />
|
||||
{/* <Box sx={{ flexGrow: 1 }} /> */}
|
||||
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
{renderMobileMenu}
|
||||
{renderMenu}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const AppHub = () => {
|
||||
const classes = useStyles();
|
||||
const classes1 = useStyles1();
|
||||
|
||||
const [usecases, setUsecases] = React.useState([
|
||||
{
|
||||
"name": "1. Collect",
|
||||
"color": "#c51152",
|
||||
"list": [
|
||||
{
|
||||
"name": "Email management",
|
||||
"priority": 100,
|
||||
"type": "communication",
|
||||
"items": {
|
||||
"name": "Release a quarantined message",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "EDR to ticket",
|
||||
"priority": 100,
|
||||
"type": "edr",
|
||||
"items": {
|
||||
"name": "Get host information",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "SIEM to ticket",
|
||||
"priority": 100,
|
||||
"type": "siem",
|
||||
"description": "Ensure tickets are forwarded to the correct destination. Alternatively add enrichment on it's way there.",
|
||||
"video": "https://www.youtube.com/watch?v=FBISHA7V15c&t=197s&ab_channel=OpenSecure",
|
||||
"blogpost": "https://medium.com/shuffle-automation/introducing-shuffle-an-open-source-soar-platform-part-1-58a529de7d12",
|
||||
"reference_image": "/images/detectionframework.png",
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "2-way Ticket synchronization",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "ChatOps",
|
||||
"priority": 70,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Threat Intel received",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Assign tickets",
|
||||
"priority": 30,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Firewall alerts",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "URL filtering",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "IDS/IPS alerts",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "Manage policies",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Deduplicate information",
|
||||
"priority": 70,
|
||||
"items": {},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "2. Enrich",
|
||||
"color": "#f4c20d",
|
||||
"list": [
|
||||
{
|
||||
"name": "Internal Enrichment",
|
||||
"priority": 100,
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "External historical Enrichment",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Realtime",
|
||||
"priority": 50,
|
||||
"items": {
|
||||
"name": "Analyze screenshots",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "3. Detect",
|
||||
"color": "#3cba54",
|
||||
"list": [
|
||||
{
|
||||
"name": "Search SIEM (Sigma)",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "Endpoint",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search EDR (OSQuery)",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search emails (Sublime)",
|
||||
"priority": 90,
|
||||
"items": {
|
||||
"name": "Check headers and IOCs",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search IOCs (ioc-finder)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Search files (Yara)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Memory Analysis (Volatility)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "IDS & IPS (Snort/Surricata)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Validate old tickets",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Honeypot access",
|
||||
"priority": 50,
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "4. Respond",
|
||||
"color": "#4885ed",
|
||||
"list": [
|
||||
{
|
||||
"name": "Eradicate malware",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Quarantine host(s)",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Block IPs, URLs, Domains and Hashes",
|
||||
"priority": 90,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Trigger scans",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Update indicators (FW, EDR, SIEM...)",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Autoblock activity when threat intel is received",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Lock/Delete/Reset account",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Lock vault",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Increase authentication",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Get policies from assets",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Run ansible scripts",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "5. Verify",
|
||||
"color": "#7f00ff",
|
||||
"list": [
|
||||
{
|
||||
"name": "Discover vulnerabilities",
|
||||
"priority": 80,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Discover assets",
|
||||
"priority": 80,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Ensure policies are followed",
|
||||
"priority": 80,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Find Inactive users",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Botnet tracker",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Ensure access rights match HR systems",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Ensure onboarding is followed",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Third party apps in SaaS",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Devices used for your cloud account",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Too much access in GCP/Azure/AWS/ other clouds",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Certificate validation",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Domain investigation with LetsEncrypt",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Monitor new DNS entries for domain with passive DNS",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Monitor and track password dumps",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Monitor for mentions of domain on darknet sites",
|
||||
"priority": 50,
|
||||
"items": {},
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"name": "Reporting",
|
||||
"priority": 50,
|
||||
"items": {
|
||||
"name": "Monthly reports",
|
||||
"items": {
|
||||
"name": "...",
|
||||
"items": {}
|
||||
}
|
||||
},
|
||||
"matches": []
|
||||
}
|
||||
],
|
||||
"matches": []
|
||||
}
|
||||
]);
|
||||
|
||||
const SideBar = {
|
||||
minWidth: 250,
|
||||
maxWidth: 300,
|
||||
borderRight: "1px solid rgba(255,255,255,0.3)",
|
||||
left: 0,
|
||||
position: "sticky",
|
||||
minHeight: "90vh",
|
||||
maxHeight: "90vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
color: "black"
|
||||
};
|
||||
|
||||
const [age, setAge] = React.useState(0);
|
||||
|
||||
const handleChange = (event) => {
|
||||
setAge(event.target.value);
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<CardContent style={{ padding: 0 }}>
|
||||
<div style={{
|
||||
background: "url('/images/home-header-bg.png')", height: "450px", backgroundSize: "cover",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center",
|
||||
position: "relative"
|
||||
}}>
|
||||
<div style={{ width: "95%", margin: "auto", position: "relative", height: "450px" }}>
|
||||
<div>
|
||||
<PrimarySearchAppBar />
|
||||
</div>
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
bottom: "10%",
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
width: "100%"
|
||||
}}>
|
||||
<div>
|
||||
<img src="/images/Shuffle_logo.png" style={{ height: "4rem", width: "4rem" }} alt="shuffle img" />
|
||||
<Typography type="title" variant="h1" color="#ef5d29">
|
||||
SHUFFLE
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<SearchField />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div style={SideBar}>
|
||||
<List>
|
||||
<ListItem >
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert", fontWeight: "bold" }}>Categories</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<span></span>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert" }}>Workflows</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert" }}>Apps</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<span style={{ fontSize: "25px", fontFamily: "revert" }}>Docs</span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
<div style={{ padding: "20px", width: "100%" }}>
|
||||
<Typography type="title" variant="h2" color="black">
|
||||
Workflow
|
||||
</Typography>
|
||||
<div style={{ width: "100%", minHeight: isMobile ? 0 : 71, maxHeight: isMobile ? 0 : 71, }}>
|
||||
{!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
<div style={{ display: "flex", }}>
|
||||
<Grid container spacing={2}>
|
||||
{usecases.map((usecase, index) => {
|
||||
//console.log(usecase)
|
||||
return (
|
||||
<Grid item xs={4}>
|
||||
<Paper
|
||||
key={usecase.name}
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: "transparent",
|
||||
marginRight: index === usecases.length - 1 ? 0 : 10,
|
||||
cursor: "pointer",
|
||||
overflow: "hidden",
|
||||
padding: 10,
|
||||
border: "0.0625rem solid #b2b2b2",
|
||||
borderRadius: "1.5625rem",
|
||||
boxSizing: "content-box",
|
||||
cursor: "pointer",
|
||||
height: "70px",
|
||||
}}
|
||||
onClick={() => {
|
||||
console.log("clicked...")
|
||||
}}
|
||||
>
|
||||
<a href={`/usecases?selected=${usecase.name}`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", }}>
|
||||
<Typography variant="body1" color="textPrimary">
|
||||
{usecase.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
In use: {usecase.matches.length}/{usecase.list.length}
|
||||
</Typography>
|
||||
</a>
|
||||
</Paper>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Card>
|
||||
<CardContent style={{ padding: 0 }}>
|
||||
<Typography type="title" variant="h3" color="#ffffff" style={{
|
||||
backgroundColor: "black", padding: "10px", height: "200px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
footer
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppHub;
|
||||
@@ -1,268 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Grid, Container, Divider, CardMedia, List, ListItem, ListItemText } from "@mui/material";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
|
||||
import theme from '../theme';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import AppGrid1 from "../components/AppGrid1.jsx"
|
||||
import WorkflowGrid from "../components/WorkflowGrid.jsx"
|
||||
import CreatorGrid from "../components/CreatorGrid.jsx"
|
||||
import DocsGrid from "../components/DocsGrid.jsx"
|
||||
import Button from '@mui/material/Button';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
|
||||
import {
|
||||
Tabs,
|
||||
Paper,
|
||||
Tab,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
Business as BusinessIcon,
|
||||
Apps as AppsIcon,
|
||||
Polymer as PolymerIcon,
|
||||
EmojiObjects as EmojiObjectsIcon,
|
||||
Description as DescriptionIcon,
|
||||
} from "@material-ui/icons";
|
||||
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
maxWidth: 1024,
|
||||
scrollX: "hidden",
|
||||
overflowX: "hidden",
|
||||
}
|
||||
|
||||
// Should be different if logged in :|
|
||||
const Appdemo = (props) => {
|
||||
const { globalUrl, isLoaded, serverside, userdata, hidemargins, } = props;
|
||||
const [appCategory,setAppCategory] = useState();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [curTab, setCurTab] = useState(0);
|
||||
const iconStyle = { marginRight: 10 };
|
||||
|
||||
useEffect(() => {
|
||||
if (serverside !== true && window.location.search !== undefined && window.location.search !== null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundTab = params["tab"]
|
||||
if (foundTab !== null && foundTab !== undefined) {
|
||||
for (var key in Object.keys(views)) {
|
||||
const value = views[key]
|
||||
console.log(key, value)
|
||||
if (value === foundTab) {
|
||||
setConfig("", key)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (serverside === true) {
|
||||
return null
|
||||
}
|
||||
|
||||
const boxStyle = {
|
||||
color: "white",
|
||||
flex: "1",
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
paddingLeft: 30,
|
||||
paddingRight: 30,
|
||||
paddingBottom: 30,
|
||||
paddingTop: hidemargins === true ? 0 : 30,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflowX: "hidden",
|
||||
minHeight: 400,
|
||||
}
|
||||
const NoArguments_NoReturn = () => {
|
||||
|
||||
alert('Function Called...');
|
||||
|
||||
}
|
||||
const SideBar = {
|
||||
minWidth: 250,
|
||||
maxWidth: 300,
|
||||
borderRight: "1px solid rgba(255,255,255,0.3)",
|
||||
left: 0,
|
||||
position: "sticky",
|
||||
minHeight: "90vh",
|
||||
maxHeight: "90vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
color: "white"
|
||||
};
|
||||
const catItems = (
|
||||
<div style={SideBar}>
|
||||
<List>
|
||||
<ListItem >
|
||||
<ListItemText>
|
||||
<span><Typography variant="primary">Categories</Typography></span>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<span></span>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button id="ASSETS" variant="primary" onClick={()=>{setAppCategory("assets")}}>
|
||||
ASSETS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("cases")}}>
|
||||
CASES
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("comms")}}>
|
||||
COMMS
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("edr av")}}>
|
||||
EDR & AV
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("iam")}}>
|
||||
IAM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("intel")}}>
|
||||
INTEL
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("network")}}>
|
||||
NETWORK
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText>
|
||||
<Button variant="primary" onClick={()=>{setAppCategory("siem")}}>
|
||||
SIEM
|
||||
</Button>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
)
|
||||
|
||||
const views = {
|
||||
0: "apps",
|
||||
1: "workflows",
|
||||
2: "docs",
|
||||
3: "creators",
|
||||
}
|
||||
|
||||
const setConfig = (event, inputValue) => {
|
||||
const newValue = parseInt(inputValue)
|
||||
|
||||
setCurTab(newValue)
|
||||
if (newValue === 0) {
|
||||
document.title = "Shuffle - search - apps";
|
||||
} else if (newValue === 1) {
|
||||
document.title = "Shuffle - search - workflows";
|
||||
} else if (newValue === 2) {
|
||||
document.title = "Shuffle - search - documentation";
|
||||
} else if (newValue === 3) {
|
||||
document.title = "Shuffle - search - creators";
|
||||
} else {
|
||||
document.title = "Shuffle - search";
|
||||
}
|
||||
|
||||
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
const foundQuery = params["q"]
|
||||
var extraQ = ""
|
||||
if (foundQuery !== null && foundQuery !== undefined) {
|
||||
extraQ = "&q="+foundQuery
|
||||
}
|
||||
|
||||
|
||||
if ((serverside === false || serverside === undefined) && window.location.pathname.includes("/search")) {
|
||||
navigate(`/search?tab=${views[newValue]}`+extraQ)
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoaded === false) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageDataBrowser =
|
||||
<div style={{paddingBottom: hidemargins === true ? 0 : 100, color: "white", backgroundColor: theme.palette.surfacColor}}>
|
||||
<div style={boxStyle}>
|
||||
<Tabs
|
||||
style={{width: 610, margin: "auto", marginTop: hidemargins === true ? 0 : 25, }}
|
||||
value={curTab}
|
||||
indicatorColor="primary"
|
||||
textColor="secondary"
|
||||
onChange={setConfig}
|
||||
aria-label="disabled tabs example"
|
||||
>
|
||||
<Tab
|
||||
label=<span>
|
||||
<AppsIcon style={iconStyle} /> Apps
|
||||
</span>
|
||||
/>
|
||||
|
||||
</Tabs>
|
||||
{curTab === 0 ?
|
||||
<AppGrid1 maxRows={3} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} searchValue={appCategory} key={appCategory} />
|
||||
:
|
||||
curTab === 1 ?
|
||||
window.location.pathname === "/search" ?
|
||||
<WorkflowGrid maxRows={3} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
<WorkflowGrid maxRows={3} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
curTab === 2 ?
|
||||
<DocsGrid maxRows={6} parsedXs={12} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
curTab === 3 ?
|
||||
<CreatorGrid parsedXs={4} showSuggestion={true} globalUrl={globalUrl} isMobile={isMobile} userdata={userdata} />
|
||||
:
|
||||
null}
|
||||
</div>
|
||||
</div>
|
||||
//{/*alternativeView={true} />*/}
|
||||
|
||||
const loadedCheck = isLoaded ?
|
||||
<div>
|
||||
<div style={bodyDivStyle}>{landingpageDataBrowser}</div>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
</div>
|
||||
|
||||
// #1f2023?
|
||||
return(
|
||||
<div style={{backgroundColor: "#1f2023", display: "flex"}}>
|
||||
{catItems}
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Appdemo;
|
||||
+406
-78
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
IconButton,
|
||||
@@ -32,9 +33,10 @@ import {
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Avatar,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
AutoFixHigh as AutoFixHighIcon,
|
||||
LockOpen as LockOpenIcon,
|
||||
OpenInNew as OpenInNewIcon,
|
||||
Apps as AppsIcon,
|
||||
@@ -46,20 +48,20 @@ import {
|
||||
Search as SearchIcon,
|
||||
Folder as FolderIcon,
|
||||
LibraryBooks as LibraryBooksIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import {
|
||||
ForkRight as ForkRightIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import aa from 'search-insights'
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
|
||||
import YAML from "yaml";
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
|
||||
const surfaceColor = "#27292D";
|
||||
@@ -271,9 +273,8 @@ const Apps = (props) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded, userdata } = props;
|
||||
|
||||
//const [workflows, setWorkflows] = React.useState([]);
|
||||
const theme = useTheme();
|
||||
const baseRepository = "https://github.com/frikky/shuffle-apps";
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [selectedApp, setSelectedApp] = React.useState({});
|
||||
@@ -286,6 +287,7 @@ const Apps = (props) => {
|
||||
const [selectedAction, setSelectedAction] = React.useState({});
|
||||
const [searchBackend, setSearchBackend] = React.useState(false);
|
||||
const [searchableApps, setSearchableApps] = React.useState([]);
|
||||
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
||||
|
||||
const [openApi, setOpenApi] = React.useState("");
|
||||
const [openApiData, setOpenApiData] = React.useState("");
|
||||
@@ -293,6 +295,8 @@ const Apps = (props) => {
|
||||
const [loadAppsModalOpen, setLoadAppsModalOpen] = React.useState(false);
|
||||
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
|
||||
const [openApiModal, setOpenApiModal] = React.useState(false);
|
||||
const [generateAppModal, setGenerateAppModal] = React.useState(false);
|
||||
|
||||
const [openApiModalType, setOpenApiModalType] = React.useState("");
|
||||
const [openApiError, setOpenApiError] = React.useState("");
|
||||
const [field1, setField1] = React.useState("");
|
||||
@@ -438,6 +442,7 @@ const Apps = (props) => {
|
||||
if (privateapps.length > 0) {
|
||||
if (selectedApp.id === undefined || selectedApp.id === null) {
|
||||
setSelectedApp(privateapps[0]);
|
||||
setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you")
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -455,7 +460,7 @@ const Apps = (props) => {
|
||||
//}, 5000)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -463,7 +468,7 @@ const Apps = (props) => {
|
||||
const downloadApp = (inputdata) => {
|
||||
const id = inputdata.id;
|
||||
|
||||
alert.info("Downloading..");
|
||||
toast("Downloading..");
|
||||
fetch(globalUrl + "/api/v1/apps/" + id + "/config", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -481,7 +486,7 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to download file");
|
||||
toast("Failed to download file");
|
||||
} else {
|
||||
console.log(responseJson);
|
||||
const basedata = atob(responseJson.openapi);
|
||||
@@ -539,7 +544,7 @@ const Apps = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -643,6 +648,7 @@ const Apps = (props) => {
|
||||
if (selectedApp.id !== data.id) {
|
||||
data.name = newAppname;
|
||||
setSelectedApp(data);
|
||||
setSharingConfiguration(data.sharing === true ? "public" : "you")
|
||||
|
||||
if (
|
||||
data.actions !== undefined &&
|
||||
@@ -655,7 +661,7 @@ const Apps = (props) => {
|
||||
}
|
||||
|
||||
if (data.sharing) {
|
||||
setSharingConfiguration(isCloud ? "public" : "everyone");
|
||||
setSharingConfiguration("public");
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -996,11 +1002,11 @@ const Apps = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const userRoles = ["you", isCloud ? "public" : "everyone"];
|
||||
const userRoles = ["you", "public"];
|
||||
|
||||
// Admin in org or creator of app
|
||||
// FIXME: Missing check for if same creator account
|
||||
const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated
|
||||
// Admin in org or creator of app
|
||||
// FIXME: Missing check for if same creator account
|
||||
const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated
|
||||
|
||||
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"),
|
||||
var baseInfo =
|
||||
@@ -1051,6 +1057,7 @@ const Apps = (props) => {
|
||||
console.log("New version: ", newversion);
|
||||
selectedApp.app_version = selectedApp.app_version;
|
||||
setSelectedApp(selectedApp);
|
||||
setSharingConfiguration(selectedApp.sharing === true ? "public" : "you")
|
||||
|
||||
if (newversion !== undefined && newversion !== null) {
|
||||
getApp(newversion.id, true);
|
||||
@@ -1153,17 +1160,22 @@ const Apps = (props) => {
|
||||
<Select
|
||||
value={sharingConfiguration}
|
||||
onChange={(event) => {
|
||||
alert.info("Changing sharing to " + event.target.value);
|
||||
|
||||
setSharingConfiguration(event.target.value);
|
||||
|
||||
if (event.target.value === "you") {
|
||||
toast("Changing sharing to " + event.target.value);
|
||||
updateAppField(selectedApp.id, "sharing", false);
|
||||
} else if (
|
||||
event.target.value === "everyone" ||
|
||||
event.target.value === "public"
|
||||
) {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
|
||||
if (!isCloud) {
|
||||
setPublishModalOpen(true)
|
||||
} else {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
"Can't handle value for sharing: ",
|
||||
@@ -1333,11 +1345,80 @@ const Apps = (props) => {
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const AppCreateButton = (props) => {
|
||||
const { text, func, icon } = props;
|
||||
|
||||
const [hover, setHover] = React.useState(false);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
onClick={func}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: 15,
|
||||
margin: 10,
|
||||
backgroundColor: hover ? theme.palette.surfaceColor : "transparent",
|
||||
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
|
||||
cursor: hover ? "pointer" : "default",
|
||||
textAlign: "center",
|
||||
minHeight: 150,
|
||||
maxHeight: 150,
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
<Typography>
|
||||
{text}
|
||||
</Typography>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{}}>
|
||||
<Paper square style={uploadViewPaperStyle}>
|
||||
<div style={{ margin: 25 }}>
|
||||
<h2>App Creator</h2>
|
||||
<h2 style={{
|
||||
textAlign: "center",
|
||||
}}>
|
||||
App Creator
|
||||
</h2>
|
||||
<div style={{display: "flex"}}>
|
||||
<AppCreateButton
|
||||
text="Generate from OpenAPI/Swagger"
|
||||
func={() => {
|
||||
setOpenApiModal(true)
|
||||
}}
|
||||
icon={<PublishIcon style={{minHeight: 50, maxHeigth: 50, }} />}
|
||||
/>
|
||||
<AppCreateButton
|
||||
text="Generate from Documentation"
|
||||
func={() => {
|
||||
setGenerateAppModal(true)
|
||||
}}
|
||||
icon={<AutoFixHighIcon style={{minHeight: 50, maxHeigth: 50, }} />}
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
to="/apps/new"
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
textDecoration: "none",
|
||||
color: "#f85a3e",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
component="label"
|
||||
color="secondary"
|
||||
style={{}}
|
||||
fullWidth
|
||||
>
|
||||
Create from scratch
|
||||
</Button>
|
||||
</Link>
|
||||
{/*
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
href="https://shuffler.io/docs/apps"
|
||||
@@ -1420,6 +1501,7 @@ const Apps = (props) => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
</Paper>
|
||||
<Paper square style={uploadViewPaperStyle}>
|
||||
@@ -1459,6 +1541,30 @@ const Apps = (props) => {
|
||||
//}
|
||||
};
|
||||
|
||||
const uploadFileDocumentation = (e) => {
|
||||
const isDropzone = e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
|
||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
try {
|
||||
reader.addEventListener("load", (e) => {
|
||||
const content = e.target.result;
|
||||
setOpenApiData(content);
|
||||
setIsDropzone(isDropzone);
|
||||
setOpenApiModal(true);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error in dropzone: ", e);
|
||||
}
|
||||
|
||||
try {
|
||||
reader.readAsText(files[0]);
|
||||
} catch (error) {
|
||||
toast("Failed to read file");
|
||||
}
|
||||
};
|
||||
|
||||
const uploadFile = (e) => {
|
||||
const isDropzone =
|
||||
e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
|
||||
@@ -1480,7 +1586,7 @@ const Apps = (props) => {
|
||||
try {
|
||||
reader.readAsText(files[0]);
|
||||
} catch (error) {
|
||||
alert.error("Failed to read file");
|
||||
toast("Failed to read file");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1579,9 +1685,9 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
alert.error("Failed to activate the app")
|
||||
toast("Failed to activate the app")
|
||||
} else {
|
||||
alert.success("App activated for your organization! Refresh the page to use the app.")
|
||||
toast("App activated for your organization! Refresh the page to use the app.")
|
||||
|
||||
if (refresh === true) {
|
||||
getApps()
|
||||
@@ -1589,7 +1695,7 @@ const Apps = (props) => {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
//alert.error(error.toString())
|
||||
//toast(error.toString())
|
||||
console.log("Activate app error: ", error.toString())
|
||||
});
|
||||
}
|
||||
@@ -1693,13 +1799,13 @@ const Apps = (props) => {
|
||||
return (
|
||||
<div style={{ textDecoration: "none", color: "white", }} onClick={(event) => {
|
||||
//if (!isCloud) {
|
||||
// alert.info("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.")
|
||||
// toast("Since this is an on-prem instance. You will need to activate the app yourself. Opening link to download it in a new window.")
|
||||
// setTimeout(() => {
|
||||
// event.preventDefault()
|
||||
// window.open(parsedUrl, '_blank')
|
||||
// }, 2000)
|
||||
//} else {
|
||||
alert.info(`Activating ${name}`)
|
||||
toast(`Activating ${name}`)
|
||||
//}
|
||||
|
||||
console.log("CLICK: ", hit)
|
||||
@@ -1868,12 +1974,6 @@ const Apps = (props) => {
|
||||
style={{ backgroundColor: inputColor, borderRadius: 5 }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
minHeight: "50px",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
fontSize: "1em",
|
||||
borderRadius: 5,
|
||||
},
|
||||
}}
|
||||
disabled={
|
||||
@@ -1894,27 +1994,27 @@ const Apps = (props) => {
|
||||
filteredApps.length > 0 ? (
|
||||
<div style={{ height: "75vh", overflowY: "auto" }}>
|
||||
{filteredApps.map((app, index) => {
|
||||
if (firstLoad) {
|
||||
appDelay += 75
|
||||
} else {
|
||||
//return returnData
|
||||
if (firstLoad) {
|
||||
appDelay += 75
|
||||
} else {
|
||||
//return returnData
|
||||
return <AppPaper app={app} />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Zoom key={index} in={true} style={{ transitionDelay: `${appDelay}ms` }}>
|
||||
<span>
|
||||
<AppPaper app={app} />
|
||||
</span>
|
||||
</Zoom>
|
||||
)
|
||||
<Zoom key={index} in={true} style={{ transitionDelay: `${appDelay}ms` }}>
|
||||
<div>
|
||||
<AppPaper app={app} />
|
||||
</div>
|
||||
</Zoom>
|
||||
)
|
||||
})}
|
||||
{cursearch.length > 0
|
||||
? null
|
||||
: searchableApps.map((app, index) => {
|
||||
return (
|
||||
<AppPaper app={app} />
|
||||
)
|
||||
<AppPaper app={app} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
@@ -2017,7 +2117,7 @@ const Apps = (props) => {
|
||||
|
||||
parsedData["force_update"] = forceUpdate;
|
||||
|
||||
alert.success("Getting specific apps from your URL.");
|
||||
toast("Getting specific apps from your URL.");
|
||||
var cors = "cors";
|
||||
fetch(globalUrl + "/api/v1/apps/get_existing", {
|
||||
method: "POST",
|
||||
@@ -2030,7 +2130,7 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Loaded existing apps!");
|
||||
toast("Loaded existing apps!");
|
||||
}
|
||||
|
||||
//stop()
|
||||
@@ -2041,12 +2141,12 @@ const Apps = (props) => {
|
||||
.then((responseJson) => {
|
||||
console.log("DATA: ", responseJson);
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed loading: " + responseJson.reason);
|
||||
toast("Failed loading: " + responseJson.reason);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("ERROR: ", error.toString());
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
//stop()
|
||||
|
||||
setIsLoading(false);
|
||||
@@ -2056,7 +2156,7 @@ const Apps = (props) => {
|
||||
|
||||
// Locally hotloads app from folder
|
||||
const hotloadApps = () => {
|
||||
alert.info("Hotloading apps from location in .env");
|
||||
toast("Hotloading apps from location in .env");
|
||||
setIsLoading(true);
|
||||
fetch(globalUrl + "/api/v1/apps/run_hotload", {
|
||||
mode: "cors",
|
||||
@@ -2068,7 +2168,7 @@ const Apps = (props) => {
|
||||
.then((response) => {
|
||||
setIsLoading(false);
|
||||
if (response.status === 200) {
|
||||
//alert.success("Hotloaded apps!")
|
||||
//toast("Hotloaded apps!")
|
||||
getApps();
|
||||
}
|
||||
|
||||
@@ -2076,14 +2176,14 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === true) {
|
||||
alert.info("Successfully finished hotload");
|
||||
toast("Successfully finished hotload");
|
||||
} else {
|
||||
alert.error("Failed hotload: ", responseJson.reason);
|
||||
toast("Failed hotload: ", responseJson.reason);
|
||||
//(responseJson.reason !== undefined && responseJson.reason.length > 0) {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2107,7 +2207,7 @@ const Apps = (props) => {
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2120,9 +2220,9 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
//alert.success("Successfully GOT app "+appId)
|
||||
//toast("Successfully GOT app "+appId)
|
||||
} else {
|
||||
alert.error("Failed getting app");
|
||||
toast("Failed getting app");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -2145,7 +2245,7 @@ const Apps = (props) => {
|
||||
responseJson.loop_versions = selectedApp.loop_versions;
|
||||
}
|
||||
|
||||
//alert.info("Should set app to selected")
|
||||
//toast("Should set app to selected")
|
||||
if (
|
||||
responseJson.actions !== undefined &&
|
||||
responseJson.actions !== null &&
|
||||
@@ -2156,15 +2256,16 @@ const Apps = (props) => {
|
||||
setSelectedAction({});
|
||||
}
|
||||
setSelectedApp(responseJson);
|
||||
setSharingConfiguration(responseJson.sharing === true ? "public" : "you")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const deleteApp = (appId) => {
|
||||
alert.info("Attempting to delete app");
|
||||
toast("Attempting to delete app");
|
||||
fetch(globalUrl + "/api/v1/apps/" + appId, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
@@ -2174,16 +2275,16 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Successfully deleted app");
|
||||
toast("Successfully deleted app");
|
||||
setTimeout(() => {
|
||||
getApps();
|
||||
}, 1000);
|
||||
} else {
|
||||
alert.error("Failed deleting app. Does it still exist?");
|
||||
toast("Failed deleting app. Does it still exist?");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2191,6 +2292,7 @@ const Apps = (props) => {
|
||||
const data = {};
|
||||
data[fieldname] = fieldvalue;
|
||||
|
||||
|
||||
console.log("DATA: ", data);
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/" + app_id, {
|
||||
@@ -2207,19 +2309,19 @@ const Apps = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
//console.log(responseJson)
|
||||
//alert.info(responseJson)
|
||||
//toast(responseJson)
|
||||
if (responseJson.success) {
|
||||
alert.success("Successfully updated app configuration");
|
||||
toast("Successfully updated app configuration");
|
||||
} else {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||
alert.error("Error: "+responseJson.reason);
|
||||
toast("Error: "+responseJson.reason);
|
||||
} else {
|
||||
alert.error("Error updating app configuration");
|
||||
}
|
||||
toast("Error updating app configuration. Are you the owner of this app?");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2250,10 +2352,62 @@ const Apps = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const validateDocumentationUrl = () => {
|
||||
setValidation(true);
|
||||
|
||||
// curl https://doc-to-openapi-stbuwivzoq-nw.a.run.app/doc_to_openapi -d '{"url": "https://gitlab.com/rhab/PyOTRS/-/raw/main/pyotrs/lib.py?ref_type=heads"}' -H "Content-Type: application/json"
|
||||
const urldata = {
|
||||
"url": openApi,
|
||||
}
|
||||
|
||||
//fetch("http://localhost:8080/doc_to_openapi", {
|
||||
fetch("https://doc-to-openapi-stbuwivzoq-nw.a.run.app/doc_to_openapi", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(urldata),
|
||||
})
|
||||
.then((response) => {
|
||||
setValidation(false);
|
||||
if (response.status !== 200) {
|
||||
toast("Error in generation: "+response.status);
|
||||
setOpenApiError("Error in generation - bad status: "+response.status);
|
||||
return response.text();
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
// Check if openapi or swagger in string of the json
|
||||
try {
|
||||
const parsedtext = JSON.stringify(responseJson);
|
||||
if (parsedtext.indexOf("openapi") === -1 && parsedtext.indexOf("swagger") === -1) {
|
||||
setValidation(false);
|
||||
setOpenApiError("Error in generation: "+parsedtext);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
setValidation(false);
|
||||
setOpenApiError("Error in generation (2): "+e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Validating response!");
|
||||
validateOpenApi(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
setValidation(false);
|
||||
toast(error.toString());
|
||||
setOpenApiError(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const validateRemote = () => {
|
||||
setValidation(true);
|
||||
|
||||
@@ -2288,7 +2442,7 @@ const Apps = (props) => {
|
||||
validateOpenApi(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setOpenApiError(error.toString());
|
||||
});
|
||||
};
|
||||
@@ -2345,12 +2499,12 @@ const Apps = (props) => {
|
||||
if (responseJson.reason !== undefined) {
|
||||
setOpenApiError(responseJson.reason);
|
||||
}
|
||||
alert.error("An error occurred in the response");
|
||||
toast("An error occurred in the response");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
setValidation(false);
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setOpenApiError(error.toString());
|
||||
});
|
||||
};
|
||||
@@ -2364,6 +2518,61 @@ const Apps = (props) => {
|
||||
setLoadAppsModalOpen(false);
|
||||
};
|
||||
|
||||
const publishModal = publishModalOpen ? (
|
||||
<Dialog
|
||||
open={publishModalOpen}
|
||||
onClose={() => {
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
padding: 50,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{ marginBottom: 0 }}>
|
||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
|
||||
Are you sure you want to PUBLISH this app?
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent
|
||||
style={{ color: "rgba(255,255,255,0.65)", textAlign: "center" }}
|
||||
>
|
||||
<div>
|
||||
<Typography variant="body1" style={{ marginBottom: 20 }}>
|
||||
Before publishing, make sure to sanitize the App for anything you don't want public.
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{ marginBottom: 20 }}>
|
||||
The published App is yours, and you can always change your public Apps after they are released.
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Yes
|
||||
</Button>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
No
|
||||
</Button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
const deleteModal = deleteModalOpen ? (
|
||||
<Dialog
|
||||
open={deleteModalOpen}
|
||||
@@ -2414,6 +2623,7 @@ const Apps = (props) => {
|
||||
const circularLoader = validation ? (
|
||||
<CircularProgress color="primary" />
|
||||
) : null;
|
||||
|
||||
const appsModalLoad = loadAppsModalOpen ? (
|
||||
<Dialog
|
||||
open={loadAppsModalOpen}
|
||||
@@ -2550,14 +2760,13 @@ const Apps = (props) => {
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
const errorText =
|
||||
openApiError.length > 0 ? (
|
||||
<div style={{ marginTop: 10 }}>Error: {openApiError}</div>
|
||||
) : null;
|
||||
const modalView = openApiModal ? (
|
||||
const errorText = openApiError.length > 0 ? ( <div style={{ marginTop: 10 }}>Error: {openApiError}</div>) : null;
|
||||
|
||||
const generateAppView = generateAppModal ? (
|
||||
<Dialog
|
||||
open={openApiModal}
|
||||
open={generateAppModal}
|
||||
onClose={() => {
|
||||
setGenerateAppModal(false);
|
||||
setOpenApiModal(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
@@ -2572,7 +2781,124 @@ const Apps = (props) => {
|
||||
<FormControl>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Create a new app
|
||||
Generate an app based on documentation (beta)
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
<Typography variant="body1">
|
||||
Paste in a URL, and we will make it into an app for you. This may take multiple minutes based on the size of the documentation. <b>{isCloud ? "" : "Uses Shuffle Cloud (https://shuffler.io) for processing (for now)."}</b>
|
||||
</Typography>
|
||||
<TextField
|
||||
style={{ backgroundColor: inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
endAdornment: (
|
||||
<Button
|
||||
style={{
|
||||
borderRadius: "0px",
|
||||
marginTop: "0px",
|
||||
height: "50px",
|
||||
}}
|
||||
variant="contained"
|
||||
disabled={openApi.length === 0 || appValidation.length > 0}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setOpenApiError("");
|
||||
validateDocumentationUrl();
|
||||
}}
|
||||
>
|
||||
Validate
|
||||
</Button>
|
||||
),
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setOpenApi(e.target.value);
|
||||
}}
|
||||
helperText={
|
||||
<span style={{ color: "white", marginBottom: "2px" }}>
|
||||
Should be a documentation page containing an API.
|
||||
</span>
|
||||
}
|
||||
placeholder="API Documentation URL"
|
||||
fullWidth
|
||||
/>
|
||||
<p>Or upload document with the content (coming soon)</p>
|
||||
<input
|
||||
hidden
|
||||
type="file"
|
||||
ref={upload}
|
||||
multiple={false}
|
||||
onChange={uploadFileDocumentation}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled
|
||||
onClick={() => upload.current.click()}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
{errorText}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{circularLoader}
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => {
|
||||
setGenerateAppModal(false);
|
||||
setOpenApiModal(false);
|
||||
setAppValidation("");
|
||||
setOpenApiError("");
|
||||
setOpenApi("");
|
||||
setOpenApiData("");
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "0px" }}
|
||||
disabled={appValidation.length === 0}
|
||||
onClick={() => {
|
||||
redirectOpenApi();
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</FormControl>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
|
||||
const modalView = openApiModal ? (
|
||||
<Dialog
|
||||
open={openApiModal}
|
||||
onClose={() => {
|
||||
setOpenApiModal(false);
|
||||
setGenerateAppModal(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Create a new app from OpenAPI / Swagger
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
@@ -2677,6 +3003,8 @@ const Apps = (props) => {
|
||||
<div>
|
||||
{appView}
|
||||
{modalView}
|
||||
{publishModal}
|
||||
{generateAppView}
|
||||
{appsModalLoad}
|
||||
{deleteModal}
|
||||
</div>
|
||||
|
||||
@@ -1,345 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
textAlign: "center",
|
||||
width: "900px",
|
||||
}
|
||||
|
||||
|
||||
// Should be different if logged in :|
|
||||
const Contact = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
}
|
||||
|
||||
const bodyTextStyle = {
|
||||
color: "#ffffff",
|
||||
}
|
||||
|
||||
const [firstname, setFirstname] = useState("");
|
||||
const [lastname, setLastname] = useState("");
|
||||
const [title, setTitle] = useState("");
|
||||
const [companyname, setCompanyname] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
const [formMessage, setFormMessage] = useState("");
|
||||
|
||||
const submitContact = () => {
|
||||
const data = {
|
||||
"firstname": firstname,
|
||||
"lastname": lastname,
|
||||
"title": title,
|
||||
"companyname": companyname,
|
||||
"email": email,
|
||||
"phone": phone,
|
||||
"message": message,
|
||||
}
|
||||
console.log(data)
|
||||
|
||||
fetch(globalUrl + "/api/v1/contact", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.success === true) {
|
||||
setFormMessage(response.message)
|
||||
} else {
|
||||
setFormMessage("Something went wrong. Please contact frikky@shuffler.io.")
|
||||
}
|
||||
console.log(response)
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
});
|
||||
}
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageDataBrowser =
|
||||
<div>
|
||||
<div style={bodyTextStyle}>
|
||||
<h3 style={{ color: "#f85a3e" }}>Contact us</h3>
|
||||
<h2>Lets talk!</h2>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Contact Details</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="First Name"
|
||||
type="firstname"
|
||||
id="standard-required"
|
||||
autoComplete="firstname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setFirstname(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Last Name"
|
||||
type="lastname"
|
||||
id="standard"
|
||||
autoComplete="lastname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setLastname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Job Title"
|
||||
type="jobtitle"
|
||||
id="standard-required"
|
||||
autoComplete="jobtitle"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
type="companyname"
|
||||
placeholder="Company Name"
|
||||
id="standard-required"
|
||||
autoComplete="companyname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setCompanyname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", marginRight: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
id="standard-required"
|
||||
autoComplete="email"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: "1", marginLeft: "15px", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
type="phone"
|
||||
placeholder="Phone number"
|
||||
id="standard-required"
|
||||
autoComplete="phone"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setPhone(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h2>Message</h2>
|
||||
</div>
|
||||
<div style={{ flex: 4 }}>
|
||||
<TextField
|
||||
multiline
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
rows="6"
|
||||
fullWidth={true}
|
||||
placeholder="What can we help you with?"
|
||||
id="filled-multiline-static"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={email.length <= 0 || message.length <= 0}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={submitContact}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<h3>{formMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const landingpageDataMobile =
|
||||
<div style={{ paddingBottom: "50px" }}>
|
||||
<div style={{ color: "white", textAlign: "center" }}>
|
||||
<h3 style={{ color: "#f85a3e" }}>Contact us</h3>
|
||||
<h2>Lets talk!</h2>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Contact Details</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Name"
|
||||
type="firstname"
|
||||
id="standard-required"
|
||||
autoComplete="firstname"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setFirstname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
id="standard-required"
|
||||
autoComplete="email"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h2>Message</h2>
|
||||
</div>
|
||||
<div style={{ flex: 4 }}>
|
||||
<TextField
|
||||
multiline
|
||||
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
rows="6"
|
||||
fullWidth={true}
|
||||
placeholder="What can we help you with?"
|
||||
id="filled-multiline-static"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={email.length <= 0 || message.length <= 0}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={submitContact}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<h3>{formMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
const loadedCheck = isLoaded ?
|
||||
<div>
|
||||
<BrowserView>
|
||||
<div style={bodyDivStyle}>{landingpageDataBrowser}</div>
|
||||
</BrowserView>
|
||||
<MobileView>
|
||||
{landingpageDataMobile}
|
||||
</MobileView>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Contact;
|
||||
+286
-186
@@ -1,18 +1,22 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import AppFramework from "../components/AppFramework.jsx";
|
||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||
import { makeStyles, } from "@mui/styles";
|
||||
// nodejs library that concatenates classes
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
import { useAlert } from "react-alert";
|
||||
import Autocomplete from "@material-ui/lab/Autocomplete";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { parsedDatatypeImages } from "../components/AppFramework.jsx"
|
||||
import { findSpecificApp } from "../components/AppFramework.jsx"
|
||||
|
||||
import {
|
||||
Autocomplete,
|
||||
Tooltip,
|
||||
TextField,
|
||||
IconButton,
|
||||
@@ -22,7 +26,7 @@ import {
|
||||
Paper,
|
||||
Chip,
|
||||
Checkbox,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
@@ -33,7 +37,7 @@ import {
|
||||
CheckBox as CheckBoxIcon,
|
||||
CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon,
|
||||
OpenInNew as OpenInNewIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
||||
import { removeParam } from "../views/AngularWorkflow.jsx"
|
||||
@@ -83,14 +87,15 @@ const useStyles = makeStyles({
|
||||
},
|
||||
inputRoot: {
|
||||
color: "white",
|
||||
// This matches the specificity of the default styles at https://github.com/mui-org/material-ui/blob/v4.11.3/packages/material-ui-lab/src/Autocomplete/Autocomplete.js#L90
|
||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#f86a3e",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => {
|
||||
|
||||
|
||||
const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => {
|
||||
const [expandedIndex, setExpandedIndex] = useState(-1);
|
||||
const [expandedItem, setExpandedItem] = useState(-1);
|
||||
const [inputUsecase, setInputUsecase] = useState({});
|
||||
@@ -103,17 +108,92 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
|
||||
const [selectedWorkflows, setSelectedWorkflows] = useState([])
|
||||
const [firstLoad, setFirstLoad] = useState(true)
|
||||
const [apps, setApps] = useState([])
|
||||
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [mitreTags, setMitreTags] = useState([]);
|
||||
|
||||
const loadApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson === null) {
|
||||
console.log("null-response from server")
|
||||
const pretend_apps = [{
|
||||
"name": "TBD",
|
||||
"app_name": "TBD",
|
||||
"app_version": "TBD",
|
||||
"description": "TBD",
|
||||
"version": "TBD",
|
||||
"large_image": "",
|
||||
}]
|
||||
|
||||
setApps(pretend_apps)
|
||||
return
|
||||
}
|
||||
|
||||
if (responseJson.success === false) {
|
||||
console.log("error loading apps: ", responseJson)
|
||||
return
|
||||
}
|
||||
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadApps()
|
||||
}, [])
|
||||
|
||||
if (keys === undefined || keys === null || keys.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const getUsecase = (name, index, subindex) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(name.replaceAll(" ", "_"))}`, {
|
||||
|
||||
const parseUsecase = (subcase) => {
|
||||
console.log("parseUsecase: ", subcase)
|
||||
const srcdata = findSpecificApp(frameworkData, subcase.type)
|
||||
const dstdata = findSpecificApp(frameworkData, subcase.last)
|
||||
|
||||
console.log("srcdata: ", srcdata)
|
||||
console.log("dstdata: ", dstdata)
|
||||
|
||||
if (srcdata !== undefined && srcdata !== null) {
|
||||
subcase.srcimg = srcdata.large_image
|
||||
subcase.srcapp = srcdata.name
|
||||
}
|
||||
|
||||
if (dstdata !== undefined && dstdata !== null) {
|
||||
subcase.dstimg = dstdata.large_image
|
||||
subcase.dstapp = dstdata.name
|
||||
}
|
||||
|
||||
return subcase
|
||||
}
|
||||
|
||||
const getUsecase = (subcase, index, subindex) => {
|
||||
subcase = parseUsecase(subcase)
|
||||
|
||||
// Timeout 50ms to delay it slightly
|
||||
setTimeout(() => {
|
||||
setInputUsecase(subcase)
|
||||
}, 50)
|
||||
|
||||
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -121,50 +201,58 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
setInputUsecase({
|
||||
"name": name,
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
console.log("In responseJson for usecase: ", responseJson)
|
||||
var parsedUsecase = responseJson
|
||||
|
||||
if (responseJson.success === false) {
|
||||
parsedUsecase = subcase
|
||||
} else {
|
||||
parsedUsecase = responseJson
|
||||
|
||||
parsedUsecase.srcimg = subcase.srcimg
|
||||
parsedUsecase.srcapp = subcase.srcapp
|
||||
parsedUsecase.dstimg = subcase.dstimg
|
||||
parsedUsecase.dstapp = subcase.dstapp
|
||||
}
|
||||
|
||||
// Look for the type of app and fill in img1, srcapp...
|
||||
setInputUsecase(parsedUsecase)
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setTimeout(() => {
|
||||
//console.log("Scroll!")
|
||||
const found = document.getElementById("selected_box");
|
||||
if (found !== undefined && found !== null) {
|
||||
//console.log("FOUND!!")
|
||||
found.scrollTo({
|
||||
top: 100,
|
||||
behavior: "smooth",
|
||||
})
|
||||
} else {
|
||||
setInputUsecase(responseJson)
|
||||
}
|
||||
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setTimeout(() => {
|
||||
//console.log("Scroll!")
|
||||
const found = document.getElementById("selected_box");
|
||||
if (found !== undefined && found !== null) {
|
||||
//console.log("FOUND!!")
|
||||
found.scrollTo({
|
||||
top: 100,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
})
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
})
|
||||
}
|
||||
|
||||
const setUsecaseItem = (inputUsecase) => {
|
||||
var parsedUsecase = inputUsecase
|
||||
@@ -213,19 +301,19 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed updating: " + responseJson.reason)
|
||||
//toast("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to update framework for your org.")
|
||||
//toast("Failed to update framework for your org.")
|
||||
}
|
||||
} else {
|
||||
//alert.info("Updated usecase.")
|
||||
//toast("Updated usecase.")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const setWorkflow = (workflowdata) => {
|
||||
const new_url = `${globalUrl}/api/v1/workflows/${workflowdata.id}`
|
||||
@@ -250,9 +338,9 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Error updating workflow: ", responseJson.reason)
|
||||
toast("Error updating workflow: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Error updating workflow.")
|
||||
toast("Error updating workflow.")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -261,7 +349,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
return responseJson;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Problem setting workflow: ", error.toString());
|
||||
toast("Problem setting workflow: ", error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -289,6 +377,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
if (selectedItem && subcase.matches.length > 0 && selectedWorkflows.length === 0 && firstLoad === true) {
|
||||
setFirstLoad(false)
|
||||
setSelectedWorkflows(subcase.matches)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,10 +405,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
}
|
||||
}
|
||||
|
||||
//const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color
|
||||
const finished = subcase.matches.length > 0
|
||||
const backgroundColor = theme.palette.surfaceColor
|
||||
//"inherit"
|
||||
const itemBorder = `${selectedItem ? "3px" : expandedItem >= 0 ? "0px" : "1px"} solid ${usecase.color}`
|
||||
|
||||
const fixedName = subcase.name.toLowerCase().replace("_", " ")
|
||||
@@ -330,14 +417,14 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
//setSelectedWorkflows([])
|
||||
if (selectedItem) {
|
||||
} else {
|
||||
getUsecase(subcase.name, index, subindex)
|
||||
getUsecase(subcase, index, subindex)
|
||||
navigate(`/usecases?selected_object=${fixedName}`)
|
||||
|
||||
//const newitem = removeParam("selected_object", cursearch);
|
||||
//navigate(curpath + newitem)
|
||||
}
|
||||
}}>
|
||||
<Paper style={{padding: 25, minHeight: 75, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||
<Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||
}}>
|
||||
{!selectedItem ?
|
||||
<div style={{textAlign: "left", position: "relative",}}>
|
||||
@@ -637,24 +724,36 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div style={{flex: 1, textAlign: "left", marginRight: 10, }}>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
{subcase.description}
|
||||
</Typography>
|
||||
<div style={{flex: 1, textAlign: "left", marginRight: 10, }}>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
{subcase.description}
|
||||
</Typography>
|
||||
|
||||
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
|
||||
<Typography variant="body1" style={{marginTop: 15, marginBottom: 10, }}>
|
||||
Select relevant workflows
|
||||
</Typography>
|
||||
: null}
|
||||
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
|
||||
<Typography variant="body1" style={{marginTop: 15, marginBottom: 10, }}>
|
||||
Select relevant workflows
|
||||
</Typography>
|
||||
:
|
||||
<span style={{display: "flex"}}>
|
||||
<Typography variant="body1" style={{marginTop: 15, marginBottom: 10, }}>
|
||||
Find workflows related to this usecase:
|
||||
|
||||
</Typography>
|
||||
<a href={`https://shuffler.io/search?tab=workflows&q=${subcase.name}`} style={{textDecoration: "none", }} target="_blank" rel="noopener noreferrer">
|
||||
<IconButton style={{paddingTop: 15, }}>
|
||||
<OpenInNewIcon style={{color: "#f85a3e", }}/>
|
||||
</IconButton>
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
|
||||
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
|
||||
<Autocomplete
|
||||
multiple
|
||||
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
|
||||
<Autocomplete
|
||||
multiple
|
||||
id="workflow_matching"
|
||||
options={workflows}
|
||||
autoHighlight
|
||||
value={selectedWorkflows}
|
||||
value={selectedWorkflows}
|
||||
classes={{ inputRoot: classes.inputRoot }}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
@@ -662,7 +761,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
getOptionSelected={(option, value) => option.id === value.id}
|
||||
getOptionSelected={(option, value) => option.id === value.id}
|
||||
getOptionLabel={(option) => {
|
||||
|
||||
if (
|
||||
@@ -685,120 +784,125 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
console.log("CLICK: ", newValue)
|
||||
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
|
||||
//setSelectedWorkflows=
|
||||
//var newvalue = []
|
||||
//for (var key in newValue) {
|
||||
// if (newValue[key].id !== undefined) {
|
||||
// newvalue.push(newValue[key].id)
|
||||
// }
|
||||
//}
|
||||
console.log("CLICK: ", newValue)
|
||||
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
|
||||
//setSelectedWorkflows=
|
||||
//var newvalue = []
|
||||
//for (var key in newValue) {
|
||||
// if (newValue[key].id !== undefined) {
|
||||
// newvalue.push(newValue[key].id)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Doing this way as you may want to remove some too
|
||||
for (var key in workflows) {
|
||||
if (!newValue.find(data => data.id === workflows[key].id)) {
|
||||
// Check if it has the one in it
|
||||
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
|
||||
if (filtered !== undefined && filtered !== null) {
|
||||
//console.log("Removing: ", workflows[key].name, workflows[key])
|
||||
workflows[key]["usecase_ids"] = filtered
|
||||
|
||||
setWorkflow(workflows[key])
|
||||
}
|
||||
}
|
||||
// Doing this way as you may want to remove some too
|
||||
for (var key in workflows) {
|
||||
if (!newValue.find(data => data.id === workflows[key].id)) {
|
||||
// Check if it has the one in it
|
||||
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
|
||||
if (filtered !== undefined && filtered !== null) {
|
||||
//console.log("Removing: ", workflows[key].name, workflows[key])
|
||||
workflows[key]["usecase_ids"] = filtered
|
||||
|
||||
setWorkflow(workflows[key])
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
|
||||
workflows[key]["usecase_ids"] = [subcase.name]
|
||||
console.log("Setting: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
|
||||
workflows[key]["usecase_ids"] = [subcase.name]
|
||||
console.log("Setting: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
|
||||
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
workflows[key]["usecase_ids"].push(subcase.name)
|
||||
console.log("Adding: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
workflows[key]["usecase_ids"].push(subcase.name)
|
||||
console.log("Adding: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWorkflows(workflows)
|
||||
console.log("New: ", newValue)
|
||||
setSelectedWorkflows(newValue)
|
||||
setWorkflows(workflows)
|
||||
console.log("New: ", newValue)
|
||||
setSelectedWorkflows(newValue)
|
||||
//setUpdate(Math.random())
|
||||
}}
|
||||
renderOption={(props, option) => {
|
||||
//console.log("In options?: ", props, option)
|
||||
renderOption={(props, data, state) => {
|
||||
var newname = data.name
|
||||
if (newname === undefined || newname === null) {
|
||||
newname = "placeholder"
|
||||
}
|
||||
|
||||
var newname = props.name
|
||||
if (newname === undefined || newname === null) {
|
||||
newname = "placeholder"
|
||||
}
|
||||
|
||||
if (newname.length > 2) {
|
||||
newname = newname.charAt(0).toUpperCase() + newname.substring(1)
|
||||
}
|
||||
return (
|
||||
<li {...props}>
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{props.image !== undefined && props.image !== null && props.image.length > 0 ?
|
||||
<img src={props.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<span>
|
||||
<Checkbox
|
||||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
|
||||
checkedIcon={<CheckBoxIcon fontSize="small" />}
|
||||
style={{ marginRight: 8 }}
|
||||
checked={option.selected}
|
||||
/>
|
||||
{newname}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
if (newname.length > 2) {
|
||||
newname = newname.charAt(0).toUpperCase() + newname.substring(1)
|
||||
}
|
||||
return (
|
||||
<li {...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, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<span>
|
||||
<Checkbox
|
||||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
|
||||
checkedIcon={<CheckBoxIcon fontSize="small" />}
|
||||
style={{ marginRight: 8 }}
|
||||
checked={selectedWorkflows.find(wf => wf.id === data.id) !== undefined}
|
||||
/>
|
||||
{newname}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
renderInput={(params) => {
|
||||
return (
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find your workflows"
|
||||
variant="outlined"
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find your workflows"
|
||||
variant="outlined"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
: null}
|
||||
: null}
|
||||
<span style={{top: 30, position: "relative",}}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
style={{marginTop: 0, marginLeft: 5, }}
|
||||
onClick={() => {}}
|
||||
>
|
||||
Try it out:
|
||||
</Typography>
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
|
||||
globalUrl={globalUrl}
|
||||
img1={inputUsecase.srcimg}
|
||||
srcapp={inputUsecase.srcapp}
|
||||
img2={inputUsecase.dstimg}
|
||||
dstapp={inputUsecase.dstapp}
|
||||
title={inputUsecase.name}
|
||||
description={inputUsecase.description}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
</span>
|
||||
{/*
|
||||
|
||||
{/*subcase.matches.length > 0 ?
|
||||
<Grid container style={{maxWidth: 325, marginTop: 10, }}>
|
||||
{subcase.matches.map((workflow, workflowindex) => {
|
||||
return (
|
||||
<Grid key={workflowindex} item index={workflowindex} xs={12}>
|
||||
<WorkflowPaper key={workflowindex} data={workflow} />
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
:
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflow selected yet.
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
{subcase.extra_buttons !== undefined && subcase.extra_buttons !== null && subcase.extra_buttons.length > 0 ?
|
||||
<div style={{marginTop: 25, }}>
|
||||
@@ -852,15 +956,9 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
<Typography variant="body1" style={{marginTop: 15, cursor: "pointer",}} onClick={() => {}}>
|
||||
See other Public Workflows for {} <OpenInNewIcon style={{marginTop: 5, marginLeft: 15, }}/>
|
||||
</Typography>
|
||||
{/*
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflows yet.
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
</a>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
}
|
||||
<div style={{
|
||||
@@ -868,6 +966,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
width: 350,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
padding: 5,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
}}>
|
||||
<AppFramework
|
||||
inputUsecase={inputUsecase}
|
||||
@@ -1026,7 +1126,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
|
||||
// What data do we fill in here? Idk
|
||||
const Dashboard = (props) => {
|
||||
const { globalUrl, isLoggedIn } = props;
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const [bigChartData, setBgChartData] = useState("data1");
|
||||
const [dayAmount, setDayAmount] = useState(7);
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
@@ -1118,16 +1218,16 @@ const Dashboard = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed loading: " + responseJson.reason)
|
||||
//toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to load framework for your org.")
|
||||
//toast("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
setFrameworkData(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1158,7 +1258,7 @@ const Dashboard = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
fetchUsecases()
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1258,7 +1358,7 @@ const Dashboard = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
@@ -1292,7 +1392,7 @@ const Dashboard = (props) => {
|
||||
setChangeme(stats_id);
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||
import { makeStyles, } from "@mui/styles";
|
||||
// nodejs library that concatenates classes
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
@@ -8,11 +8,12 @@ import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
import { useAlert } from "react-alert";
|
||||
import Autocomplete from "@material-ui/lab/Autocomplete";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
import {
|
||||
Autocomplete,
|
||||
Tooltip,
|
||||
TextField,
|
||||
IconButton,
|
||||
@@ -22,7 +23,7 @@ import {
|
||||
Paper,
|
||||
Chip,
|
||||
Checkbox,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Close as CloseIcon,
|
||||
@@ -33,7 +34,7 @@ import {
|
||||
CheckBox as CheckBoxIcon,
|
||||
CheckBoxOutlineBlank as CheckBoxOutlineBlankIcon,
|
||||
OpenInNew as OpenInNewIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
||||
import { removeParam } from "../views/AngularWorkflow.jsx"
|
||||
@@ -93,7 +94,6 @@ const useStyles = makeStyles({
|
||||
},
|
||||
inputRoot: {
|
||||
color: "white",
|
||||
// This matches the specificity of the default styles at https://github.com/mui-org/material-ui/blob/v4.11.3/packages/material-ui-lab/src/Autocomplete/Autocomplete.js#L90
|
||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "#f86a3e",
|
||||
},
|
||||
@@ -333,7 +333,7 @@ const RadialChart = ({keys, setSelectedCategory}) => {
|
||||
// What data do we fill in here? Idk
|
||||
const Dashboard = (props) => {
|
||||
const { globalUrl, isLoggedIn } = props;
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const [bigChartData, setBgChartData] = useState("data1");
|
||||
const [dayAmount, setDayAmount] = useState(7);
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
@@ -428,9 +428,9 @@ const Dashboard = (props) => {
|
||||
console.log("Resp: ", responseJson)
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed loading: " + responseJson.reason)
|
||||
//toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to load framework for your org.")
|
||||
//toast("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
var tmpdata = responseJson
|
||||
@@ -453,7 +453,7 @@ const Dashboard = (props) => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//toast(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
@@ -505,7 +505,7 @@ const Dashboard = (props) => {
|
||||
setChangeme(stats_id);
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error("ERROR: " + error.toString());
|
||||
//toast("ERROR: " + error.toString());
|
||||
console.log("ERROR: " + error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
+55
-27
@@ -1,10 +1,11 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import theme from '../theme.jsx';
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
import {
|
||||
Grid,
|
||||
@@ -18,12 +19,12 @@ import {
|
||||
Typography,
|
||||
Paper,
|
||||
List,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Link as LinkIcon,
|
||||
Edit as EditIcon,
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const Body = {
|
||||
//maxWidth: 1000,
|
||||
@@ -57,7 +58,6 @@ const Docs = (defaultprops) => {
|
||||
const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
// Quickfix for react router 5 -> 6
|
||||
const params = useParams();
|
||||
@@ -365,6 +365,7 @@ const Docs = (defaultprops) => {
|
||||
};
|
||||
|
||||
function OuterLink(props) {
|
||||
console.log("Link: ", props.href)
|
||||
if (props.href.includes("http") || props.href.includes("mailto")) {
|
||||
return (
|
||||
<a
|
||||
@@ -386,23 +387,32 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
|
||||
function Img(props) {
|
||||
return <img style={{ borderRadius: theme.palette.borderRadius, maxWidth: "100%", marginTop: 15, marginBottom: 15, }} alt={props.alt} src={props.src} />;
|
||||
return <img style={{ borderRadius: theme.palette.borderRadius, width: 750, maxWidth: "100%", marginTop: 15, marginBottom: 15, }} alt={props.alt} src={props.src} />;
|
||||
}
|
||||
|
||||
function CodeHandler(props) {
|
||||
console.log("PROPS: ", props)
|
||||
|
||||
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
|
||||
|
||||
return (
|
||||
<pre
|
||||
<div
|
||||
style={{
|
||||
padding: 15,
|
||||
minWidth: "50%",
|
||||
maxWidth: "100%",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
overflowX: "auto",
|
||||
overflowY: "hidden",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<code>{props.value}</code>
|
||||
</pre>
|
||||
<code
|
||||
style={{
|
||||
// Wrap if larger than X
|
||||
whiteSpace: "pre-wrap",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>{propvalue}</code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -435,7 +445,7 @@ const Docs = (defaultprops) => {
|
||||
href={selectedMeta.link}
|
||||
style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
>
|
||||
<Button style={{}} variant="outlined">
|
||||
<Button style={{color: "white", }} variant="outlined" color="secondary">
|
||||
<EditIcon /> Edit
|
||||
</Button>
|
||||
</a>
|
||||
@@ -637,7 +647,7 @@ const Docs = (defaultprops) => {
|
||||
|
||||
<Typography variant="h6" style={headerStyle}>Why Shuffle?</Typography>
|
||||
<Typography variant="body1">
|
||||
<b>Security first.</b> We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you <a href="https://shuffler.io/pricing?tag=docs" target="_blank" style={hrefStyle2}>find usecases</a> that fit your your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases.
|
||||
<b>Security first.</b> We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you <a href="https://shuffler.io/pricing?tag=docs" target="_blank" style={hrefStyle2}>find usecases</a> that fit your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h6" style={headerStyle}>Get help</Typography>
|
||||
@@ -790,22 +800,31 @@ const Docs = (defaultprops) => {
|
||||
:
|
||||
<div id="markdown_wrapper_outer" style={markdownStyle}>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
img: Img,
|
||||
code: CodeHandler,
|
||||
h1: Heading,
|
||||
h2: Heading,
|
||||
h3: Heading,
|
||||
h4: Heading,
|
||||
h5: Heading,
|
||||
h6: Heading,
|
||||
a: OuterLink,
|
||||
}}
|
||||
id="markdown_wrapper"
|
||||
escapeHtml={false}
|
||||
source={data}
|
||||
style={{maxWidth: "100%", minWidth: "100%", }}
|
||||
renderers={{
|
||||
link: OuterLink,
|
||||
image: Img,
|
||||
code: CodeHandler,
|
||||
heading: Heading,
|
||||
style={{
|
||||
maxWidth: "100%", minWidth: "100%",
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{data}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// remarkPlugins={[remarkGfm]}
|
||||
|
||||
const mobileStyle = {
|
||||
color: "white",
|
||||
@@ -868,16 +887,25 @@ const Docs = (defaultprops) => {
|
||||
:
|
||||
<div id="markdown_wrapper_outer" style={markdownStyle}>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
img: Img,
|
||||
code: CodeHandler,
|
||||
h1: Heading,
|
||||
h2: Heading,
|
||||
h3: Heading,
|
||||
h4: Heading,
|
||||
h5: Heading,
|
||||
h6: Heading,
|
||||
a: OuterLink,
|
||||
}}
|
||||
id="markdown_wrapper"
|
||||
escapeHtml={false}
|
||||
source={data}
|
||||
renderers={{
|
||||
link: OuterLink,
|
||||
image: Img,
|
||||
code: CodeHandler,
|
||||
heading: Heading,
|
||||
style={{
|
||||
maxWidth: "100%", minWidth: "100%",
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{data}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
}
|
||||
<Divider
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,393 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
|
||||
import WebhookImage from "../assets/img/webhook.png";
|
||||
import KafkaImage from "../assets/img/kafka.png";
|
||||
|
||||
import EditWorkflow from "./EditWorkflow";
|
||||
|
||||
const EditWebhook = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
|
||||
// FIXME
|
||||
//const [webhookData, setWebhookData] = useState(webhooktest)
|
||||
const [webhookData, setWebhookData] = useState({});
|
||||
const [workflows, setWorkflows] = useState([]);
|
||||
const [firstrequest, setFirstrequest] = React.useState(true);
|
||||
|
||||
const [selectedWorkflows, setSelectedWorkflows] = useState([]);
|
||||
|
||||
const getWorkflows = () => {
|
||||
fetch(globalUrl + "/api/v1/workflows", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflows :O!");
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setWorkflows(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const setWebhook = (inputdata) => {
|
||||
console.log(inputdata);
|
||||
|
||||
fetch(globalUrl + "/api/v1/hooks/" + props.match.params.key, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(inputdata),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
console.log(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const getCurrentWebhook = () => {
|
||||
fetch(globalUrl + "/api/v1/hooks/" + props.match.params.key, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200!");
|
||||
window.location.pathname = "webhooks";
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.actions === null) {
|
||||
responseJson.actions = [];
|
||||
}
|
||||
|
||||
if (responseJson.transforms === null) {
|
||||
responseJson.transforms = [];
|
||||
}
|
||||
|
||||
setWebhookData(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
//window.location.pathname = "webhooks"
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (firstrequest) {
|
||||
setFirstrequest(false);
|
||||
getCurrentWebhook();
|
||||
if (workflows.length <= 0) {
|
||||
getWorkflows();
|
||||
}
|
||||
}
|
||||
|
||||
// After everything is loaded
|
||||
if (
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 &&
|
||||
webhookData.actions.length > 0 &&
|
||||
workflows.length > 0 &&
|
||||
selectedWorkflows.length === 0
|
||||
) {
|
||||
// Setting startup actions. making like this in case we want other actions
|
||||
var tmpActionWorkflows = [];
|
||||
for (var key in webhookData.actions) {
|
||||
if (webhookData.actions[key].type === "workflow") {
|
||||
tmpActionWorkflows.push(webhookData.actions[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix duplicates... Meh
|
||||
var foundWorkflowIds = [];
|
||||
var tmpWorkflows = [];
|
||||
for (key in tmpActionWorkflows) {
|
||||
if (foundWorkflowIds.includes(tmpActionWorkflows[key].id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var subkey in workflows) {
|
||||
if (tmpActionWorkflows[key].id === workflows[subkey]["id_"]) {
|
||||
console.log(tmpActionWorkflows[key].id, workflows[subkey]["id_"]);
|
||||
foundWorkflowIds.push(tmpActionWorkflows[key].id);
|
||||
tmpWorkflows.push(workflows[subkey]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tmpWorkflows.length > 0) {
|
||||
setSelectedWorkflows(tmpWorkflows);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const hookPicture =
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 &&
|
||||
webhookData.type === "webhook" ? (
|
||||
<img src={WebhookImage} alt="webhook" width="100px" height="100px" />
|
||||
) : (
|
||||
<img src={KafkaImage} alt="MQ" width="100px" height="100px" />
|
||||
);
|
||||
|
||||
const executeHook = (action) => {
|
||||
fetch(
|
||||
globalUrl + "/api/v1/hooks/" + props.match.params.key + "/" + action,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
}
|
||||
)
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
setWebhookData({});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const headerPaperStyle = {
|
||||
display: "flex",
|
||||
maxHeight: "800px",
|
||||
minHeight: "800px",
|
||||
margin: "10px 30px 10px 10px",
|
||||
padding: "10px 5px 5px 5px",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
// FIXME - add with counter to change the correct one (not just edit)
|
||||
const addNewWorkflow = (event) => {
|
||||
// Verify if it already exists in the array. Returns if it exists
|
||||
for (var key in selectedWorkflows) {
|
||||
var item = selectedWorkflows[key];
|
||||
if (item["id_"] === event.target.value["id_"]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - make this possible for all accounts
|
||||
if (selectedWorkflows.length === 0) {
|
||||
console.log("ADD FIRST ITEM FOR SELECTEDWORKFLOWS");
|
||||
console.log(event.target.value);
|
||||
|
||||
// Cleanup previous actions
|
||||
var newActions = [];
|
||||
if (webhookData.actions.length > 0) {
|
||||
for (key in webhookData.actions) {
|
||||
if (
|
||||
webhookData.actions[key].type === "" ||
|
||||
webhookData.actions[key].type === undefined
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newActions.push(webhookData.actions[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - how to stringify this better hurr
|
||||
var formattedWorkflow = {
|
||||
type: "workflow",
|
||||
name: event.target.value.name,
|
||||
id: event.target.value.id_,
|
||||
field: "",
|
||||
};
|
||||
|
||||
// FIXME: patch this n
|
||||
newActions.push(formattedWorkflow);
|
||||
console.log(newActions);
|
||||
|
||||
webhookData.actions = newActions;
|
||||
setWebhook(webhookData);
|
||||
}
|
||||
|
||||
var tmpSelectedWorkflows = [].concat(selectedWorkflows, [
|
||||
event.target.value,
|
||||
]);
|
||||
setSelectedWorkflows(tmpSelectedWorkflows);
|
||||
};
|
||||
|
||||
// FIXME
|
||||
// Create a list with + button
|
||||
// For each, choose the new workflow I wanna add
|
||||
// Current: JUST ONE
|
||||
const selectedWorkflowIds = selectedWorkflows.map((data) => {
|
||||
return data["id_"];
|
||||
});
|
||||
const availableWorkflows = workflows.filter(
|
||||
(data) => !selectedWorkflowIds.includes(data["id_"])
|
||||
);
|
||||
|
||||
const WorkflowSelect = (counter) => {
|
||||
if (selectedWorkflows[counter.counter] === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(selectedWorkflows[0]);
|
||||
console.log(selectedWorkflows[0]);
|
||||
console.log(selectedWorkflows[0]);
|
||||
console.log(selectedWorkflows[counter.counter]);
|
||||
console.log(selectedWorkflows[counter.counter].name);
|
||||
return (
|
||||
<div>
|
||||
Workflow select:
|
||||
<Select
|
||||
value={selectedWorkflows[counter.counter].name}
|
||||
onChange={(event) => {
|
||||
addNewWorkflow(event, counter.counter);
|
||||
}}
|
||||
displayEmpty
|
||||
name="workflow"
|
||||
>
|
||||
{availableWorkflows.map((data) => (
|
||||
<MenuItem key={data.name} value={data} name={data.name}>
|
||||
{data.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const extraWorkflow =
|
||||
workflows.length > 0 && availableWorkflows.length > 0 ? (
|
||||
<WorkflowSelect counter={selectedWorkflows.length} />
|
||||
) : null;
|
||||
|
||||
const multiWorkflowSelect =
|
||||
workflows.length > 0 && selectedWorkflows.length > 0 ? (
|
||||
<div>
|
||||
{selectedWorkflows.map((data, count) => (
|
||||
<WorkflowSelect key={count} counter={count} />
|
||||
))}
|
||||
{extraWorkflow}
|
||||
</div>
|
||||
) : (
|
||||
<WorkflowSelect counter={0} />
|
||||
);
|
||||
|
||||
const headerInfo =
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 ? (
|
||||
<div>
|
||||
<Paper style={headerPaperStyle}>
|
||||
<div style={{ display: "flex", flex: "1" }}>
|
||||
<div style={{ flex: "1" }}>{hookPicture}</div>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", flex: "5" }}
|
||||
>
|
||||
<div style={{ flex: "1" }}>
|
||||
<h1>Name: {webhookData.info.name}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flex: "4" }}>
|
||||
Description: {webhookData.info.description}
|
||||
<div>Id: {webhookData.id}</div>
|
||||
<div>Url: {webhookData.info.url}</div>
|
||||
<div>Type: {webhookData.type}</div>
|
||||
<div>Status: {webhookData.status}</div>
|
||||
<div>
|
||||
CHOOSE ACTIONS:
|
||||
{multiWorkflowSelect}
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "row" }}>
|
||||
<div style={{ flex: "1" }}>
|
||||
<Button
|
||||
disabled={
|
||||
webhookData.running === true && webhookData.name !== ""
|
||||
}
|
||||
onClick={() => {
|
||||
executeHook("start");
|
||||
}}
|
||||
style={{
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
Start {webhookData.type}
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ flex: "1" }}>
|
||||
<Button
|
||||
disabled={webhookData.running === false}
|
||||
onClick={() => {
|
||||
executeHook("stop");
|
||||
}}
|
||||
style={{
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
}}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
Stop {webhookData.type}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
// FIXME - needs refresh every time you add a new workflow
|
||||
const workflowdata =
|
||||
Object.getOwnPropertyNames(webhookData).length > 0 &&
|
||||
selectedWorkflows.length > 0 ? (
|
||||
<EditWorkflow
|
||||
globalUrl={globalUrl}
|
||||
inputworkflows={selectedWorkflows}
|
||||
inputname={webhookData.info.name}
|
||||
inputtype={webhookData.type}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div style={{ display: "flex", backgroundColor: "#f7f7f7" }}>
|
||||
<div style={{ flex: 1 }}>{workflowdata}</div>
|
||||
<div style={{ flex: 1 }}>{headerInfo}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
// FIXME: Use this for testing
|
||||
// <EditWorkflow globalUrl={globalUrl} inputname={"Helo?"} inputtype={"webhook"} /> : null
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default EditWebhook;
|
||||
@@ -2,8 +2,8 @@ import React, {useState} from 'react';
|
||||
import {isMobile} from "react-device-detect";
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
import {Divider, List, ListItem, ListItemText, Card, CardContent, Grid, Typography, Button, ButtonGroup, FormControl, Dialog, DialogTitle, DialogActions, DialogContent, Tooltip} from '@material-ui/core';
|
||||
import {ExpandMore as ExpandMoreIcon, ExpandLess as ExpandLessIcon} from '@material-ui/icons';
|
||||
import {Divider, List, ListItem, ListItemText, Card, CardContent, Grid, Typography, Button, ButtonGroup, FormControl, Dialog, DialogTitle, DialogActions, DialogContent, Tooltip} from '@mui/material';
|
||||
import {ExpandMore as ExpandMoreIcon, ExpandLess as ExpandLessIcon} from '@mui/icons-material';
|
||||
|
||||
const hrefStyle = {
|
||||
textDecoration: "none",
|
||||
@@ -255,4 +255,4 @@ const Faq = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default Faq;
|
||||
export default Faq;
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState } from "react";
|
||||
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
marginTop: "100px",
|
||||
width: "500px",
|
||||
};
|
||||
|
||||
const ForgotPassword = (props) => {
|
||||
const { globalUrl, isLoaded, isLoggedIn, surfaceColor, inputColor } = props;
|
||||
|
||||
const boxStyle = {
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: surfaceColor,
|
||||
};
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [resetInfo, setResetInfo] = useState(
|
||||
"You will receive an email with instructions shortly."
|
||||
);
|
||||
|
||||
const handleValidateForm = () => {
|
||||
return username.length > 3;
|
||||
};
|
||||
|
||||
if (isLoggedIn === true) {
|
||||
window.location.pathname = "/";
|
||||
}
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
// FIXME - add some check here ROFL
|
||||
|
||||
// Just use this one?
|
||||
var data = { username: username };
|
||||
var baseurl = globalUrl;
|
||||
var url = baseurl + "/api/v1/passwordresetmail";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setResetInfo(responseJson["reason"]);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setResetInfo("Error in userdata: " + error);
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeUser = (e) => {
|
||||
setUsername(e.target.value);
|
||||
};
|
||||
|
||||
const data = (
|
||||
<div style={bodyDivStyle}>
|
||||
<Paper style={boxStyle}>
|
||||
<form onSubmit={onSubmit} style={{ margin: "15px 15px 15px 15px" }}>
|
||||
<h2>Password reset</h2>
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
fullWidth={true}
|
||||
color="primary"
|
||||
style={{ backgroundColor: inputColor }}
|
||||
InputProps={{
|
||||
style: {
|
||||
height: "50px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
type="username"
|
||||
placeholder="Username / Email"
|
||||
id="standard-required"
|
||||
autoComplete="username"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangeUser}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex", marginTop: "15px" }}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
style={{ flex: "1", marginRight: "5px" }}
|
||||
disabled={!handleValidateForm()}
|
||||
>
|
||||
SUBMIT
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ marginTop: "20px" }}>{resetInfo}</div>
|
||||
</form>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
|
||||
const loadedCheck = isLoaded ? <div>{data}</div> : <div></div>;
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default ForgotPassword;
|
||||
@@ -1,136 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
textAlign: "center",
|
||||
width: "768px",
|
||||
};
|
||||
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: "#e8eaf6",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
//const tmpdata = {
|
||||
// "username": "frikky",
|
||||
// "firstname": "fred",
|
||||
// "lastname": "ode",
|
||||
// "title": "topkek",
|
||||
// "companyname": "company here",
|
||||
// "email": "your email pls",
|
||||
// "phone": "PHONE!!",
|
||||
//}
|
||||
|
||||
// FIXME - add fetch for data fields
|
||||
// FIXME - remove tmpdata
|
||||
// FIXME: Use isLoggedIn :)
|
||||
const Settings = (props) => {
|
||||
const { globalUrl, isLoaded } = props;
|
||||
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [newPassword2, setNewPassword2] = useState("");
|
||||
const [passwordFormMessage, setPasswordFormMessage] = useState("");
|
||||
|
||||
const onPasswordChange = () => {
|
||||
const data = {
|
||||
newpassword: newPassword,
|
||||
newpassword2: newPassword2,
|
||||
reference: props.match.params.key,
|
||||
};
|
||||
const url = globalUrl + "/api/v1/passwordreset";
|
||||
fetch(url, {
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
crossDomain: true,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setPasswordFormMessage(responseJson["reason"]);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setPasswordFormMessage("Something went wrong.");
|
||||
});
|
||||
};
|
||||
|
||||
// This should "always" have data
|
||||
useEffect(() => {});
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageData = (
|
||||
<div style={{ display: "flex", marginTop: "80px" }}>
|
||||
<Paper style={boxStyle}>
|
||||
<h2>Password Reset</h2>
|
||||
<div style={{ flex: "1", display: "flex", flexDirection: "column" }}>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1" }}
|
||||
fullWidth={true}
|
||||
placeholder="New password"
|
||||
type="password"
|
||||
id="standard-required"
|
||||
autoComplete="password"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
style={{ flex: "1" }}
|
||||
fullWidth={true}
|
||||
type="password"
|
||||
placeholder="Repeat new password"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(e) => setNewPassword2(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
disabled={
|
||||
newPassword.length < 10 ||
|
||||
newPassword2.length < 10 ||
|
||||
newPassword !== newPassword2
|
||||
}
|
||||
style={{ width: "100%", height: "60px", marginTop: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => onPasswordChange()}
|
||||
>
|
||||
Submit password change
|
||||
</Button>
|
||||
<h3>{passwordFormMessage}</h3>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div style={bodyDivStyle}>{landingpageData}</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
export default Settings;
|
||||
@@ -2,18 +2,19 @@ import React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from "react-dom"
|
||||
|
||||
import AppFramework from "../components/AppFramework.jsx";
|
||||
import { useAlert } from "react-alert";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
Button,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
const Framework = (props) => {
|
||||
const {globalUrl, isLoaded, isLoggedIn, showOptions, selectedOption, rolling, } = props;
|
||||
|
||||
const alert = useAlert()
|
||||
//const alert = useAlert()
|
||||
const [frameworkLoaded, setFrameworkLoaded] = useState(false)
|
||||
const [frameworkData, setFrameworkData] = useState()
|
||||
|
||||
@@ -36,9 +37,9 @@ const Framework = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed loading: " + responseJson.reason)
|
||||
toast("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
alert.error("Failed to load framework for your org.")
|
||||
toast("Failed to load framework for your org.")
|
||||
}
|
||||
|
||||
setFrameworkLoaded(true)
|
||||
@@ -51,7 +52,7 @@ const Framework = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
setFrameworkLoaded(true)
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useContext } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
|
||||
import ReactGA from 'react-ga4';
|
||||
import SecurityFramework from '../components/SecurityFramework.jsx';
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
Badge,
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
} from "@material-ui/core";
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Check as CheckIcon,
|
||||
@@ -58,10 +58,7 @@ import {
|
||||
CloudDownload as CloudDownloadIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
import NestedMenuItem from "material-ui-nested-menu-item";
|
||||
//import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons';
|
||||
|
||||
//https://next.material-ui.com/components/material-icons/
|
||||
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||
|
||||
//import JSONPretty from 'react-json-pretty';
|
||||
@@ -69,8 +66,9 @@ import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { useAlert } from "react-alert";
|
||||
import ChipInput from "material-ui-chip-input";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const inputColor = "#383B40";
|
||||
@@ -126,8 +124,7 @@ const GettingStarted = (props) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded, userdata } = props;
|
||||
|
||||
document.title = "Getting Started with Shuffle";
|
||||
const theme = useTheme();
|
||||
const alert = useAlert();
|
||||
//const alert = useAlert();
|
||||
const classes = useStyles(theme);
|
||||
let navigate = useNavigate();
|
||||
const imgSize = 60;
|
||||
@@ -446,7 +443,7 @@ const GettingStarted = (props) => {
|
||||
const files = isDropzone ? e.dataTransfer.files : e.target.files;
|
||||
|
||||
const reader = new FileReader();
|
||||
alert.info("Starting upload. Please wait while we validate the workflows");
|
||||
toast("Starting upload. Please wait while we validate the workflows");
|
||||
|
||||
try {
|
||||
reader.addEventListener("load", (e) => {
|
||||
@@ -455,7 +452,7 @@ const GettingStarted = (props) => {
|
||||
try {
|
||||
data = JSON.parse(reader.result);
|
||||
} catch (e) {
|
||||
alert.error("Invalid JSON: " + e);
|
||||
toast("Invalid JSON: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -483,13 +480,13 @@ const GettingStarted = (props) => {
|
||||
false
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success(`Successfully imported ${data.name}`);
|
||||
toast(`Successfully imported ${data.name}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Import error: " + error.toString());
|
||||
toast("Import error: " + error.toString());
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -522,7 +519,7 @@ const GettingStarted = (props) => {
|
||||
window.location.pathname = "/login";
|
||||
}
|
||||
|
||||
alert.info("Failed getting workflows.");
|
||||
toast("Failed getting workflows.");
|
||||
setWorkflowDone(true);
|
||||
|
||||
return;
|
||||
@@ -563,7 +560,7 @@ const GettingStarted = (props) => {
|
||||
}, 100)
|
||||
} else {
|
||||
if (isLoggedIn) {
|
||||
alert.error("An error occurred while loading workflows");
|
||||
toast("An error occurred while loading workflows");
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -572,7 +569,7 @@ const GettingStarted = (props) => {
|
||||
.catch((error) => {
|
||||
setVideoViewOpen(true)
|
||||
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -689,7 +686,7 @@ const GettingStarted = (props) => {
|
||||
trigger.parameters[1].value = "webhook_" + trigger.id;
|
||||
// FIXME: Add auth here?
|
||||
} else {
|
||||
alert.info("Something is wrong with the webhook in the copy");
|
||||
toast("Something is wrong with the webhook in the copy");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,7 +804,7 @@ const GettingStarted = (props) => {
|
||||
data = sanitizeWorkflow(data);
|
||||
|
||||
if (data.subflows !== null && data.subflows !== undefined) {
|
||||
alert.info(
|
||||
toast(
|
||||
"Not exporting with subflows when sanitizing. Please manually export them."
|
||||
);
|
||||
data.subflows = [];
|
||||
@@ -834,7 +831,7 @@ const GettingStarted = (props) => {
|
||||
const publishWorkflow = (data) => {
|
||||
data = JSON.parse(JSON.stringify(data));
|
||||
data = sanitizeWorkflow(data);
|
||||
alert.info("Sanitizing and publishing " + data.name);
|
||||
toast("Sanitizing and publishing " + data.name);
|
||||
|
||||
// This ALWAYS talks to Shuffle cloud
|
||||
fetch(globalUrl + "/api/v1/workflows/" + data.id + "/publish", {
|
||||
@@ -851,9 +848,9 @@ const GettingStarted = (props) => {
|
||||
console.log("Status not 200 for workflow publish :O!");
|
||||
} else {
|
||||
if (isCloud) {
|
||||
alert.success("Successfully published workflow");
|
||||
toast("Successfully published workflow");
|
||||
} else {
|
||||
alert.success(
|
||||
toast(
|
||||
"Successfully published workflow to https://shuffler.io"
|
||||
);
|
||||
}
|
||||
@@ -863,19 +860,19 @@ const GettingStarted = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed publishing: ", responseJson.reason);
|
||||
toast("Failed publishing: ", responseJson.reason);
|
||||
}
|
||||
|
||||
getAvailableWorkflows();
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
const copyWorkflow = (data) => {
|
||||
data = JSON.parse(JSON.stringify(data));
|
||||
alert.success("Copying workflow " + data.name);
|
||||
toast("Copying workflow " + data.name);
|
||||
data.id = "";
|
||||
data.name = data.name + "_copy";
|
||||
data = deduplicateIds(data);
|
||||
@@ -902,7 +899,7 @@ const GettingStarted = (props) => {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -918,9 +915,9 @@ const GettingStarted = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting workflows :O!");
|
||||
alert.error("Failed deleting workflow. Do you have access?");
|
||||
toast("Failed deleting workflow. Do you have access?");
|
||||
} else {
|
||||
alert.success("Deleted workflow " + id);
|
||||
toast("Deleted workflow " + id);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@@ -931,7 +928,7 @@ const GettingStarted = (props) => {
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1061,13 +1058,6 @@ const GettingStarted = (props) => {
|
||||
<FileCopyIcon style={{ marginLeft: 0, marginRight: 8 }} />
|
||||
{"Duplicate Workflow"}
|
||||
</MenuItem>
|
||||
{/*<NestedMenuItem disabled={userdata.orgs === undefined || userdata.orgs === null || userdata.orgs.length === 1 || userdata.orgs.length >= 0} style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||
//copyWorkflow(data)
|
||||
//setOpen(false)
|
||||
}} key={"duplicate"}>
|
||||
<FileCopyIcon style={{marginLeft: 0, marginRight: 8}}/>
|
||||
{"Copy to Child Org"}
|
||||
</NestedMenuItem>*/}
|
||||
<MenuItem
|
||||
style={{ backgroundColor: inputColor, color: "white" }}
|
||||
onClick={() => {
|
||||
@@ -1283,7 +1273,7 @@ const GettingStarted = (props) => {
|
||||
}}
|
||||
onClick={() => {
|
||||
if (subflows === 0) {
|
||||
alert.info("No subflows for " + data.name);
|
||||
toast("No subflows for " + data.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1445,9 +1435,9 @@ const GettingStarted = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Error setting workflow: ", responseJson.reason)
|
||||
toast("Error setting workflow: ", responseJson.reason)
|
||||
} else {
|
||||
alert.error("Error setting workflow.")
|
||||
toast("Error setting workflow.")
|
||||
}
|
||||
|
||||
return
|
||||
@@ -1464,14 +1454,14 @@ const GettingStarted = (props) => {
|
||||
setImportLoading(false);
|
||||
setModalOpen(false);
|
||||
} else {
|
||||
alert.info("Successfully changed basic info for workflow");
|
||||
toast("Successfully changed basic info for workflow");
|
||||
setModalOpen(false);
|
||||
}
|
||||
|
||||
return responseJson;
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
setImportLoading(false);
|
||||
setModalOpen(false);
|
||||
setSubmitLoading(false);
|
||||
@@ -1487,7 +1477,7 @@ const GettingStarted = (props) => {
|
||||
const file = event.target.files[key];
|
||||
if (file.type !== "application/json") {
|
||||
if (file.type !== undefined) {
|
||||
alert.error("File has to contain valid json");
|
||||
toast("File has to contain valid json");
|
||||
setImportLoading(false);
|
||||
}
|
||||
|
||||
@@ -1501,7 +1491,7 @@ const GettingStarted = (props) => {
|
||||
try {
|
||||
data = JSON.parse(reader.result);
|
||||
} catch (e) {
|
||||
alert.error("Invalid JSON: " + e);
|
||||
toast("Invalid JSON: " + e);
|
||||
setImportLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -1533,13 +1523,13 @@ const GettingStarted = (props) => {
|
||||
false
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success("Successfully imported " + data.name);
|
||||
toast("Successfully imported " + data.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error("Import error: " + error.toString());
|
||||
toast("Import error: " + error.toString());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1738,7 +1728,7 @@ const GettingStarted = (props) => {
|
||||
}}
|
||||
onClick={() => {
|
||||
if (subflows === 0) {
|
||||
alert.info("No subflows for " + data.name);
|
||||
toast("No subflows for " + data.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1939,7 +1929,7 @@ const GettingStarted = (props) => {
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
<ChipInput
|
||||
<MuiChipsInput
|
||||
style={{ marginTop: 10 }}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -2179,7 +2169,7 @@ const GettingStarted = (props) => {
|
||||
})
|
||||
return
|
||||
} else {
|
||||
alert.success("TBD: Coming in version 1.0.0");
|
||||
toast("TBD: Coming in version 1.0.0");
|
||||
}
|
||||
|
||||
const ele = document.getElementById("shuffle_search_field")
|
||||
@@ -2190,7 +2180,7 @@ const GettingStarted = (props) => {
|
||||
ele.style.borderWidth = "2px"
|
||||
|
||||
} else {
|
||||
//alert.success("TBD: Coming in version 1.0.0");
|
||||
//toast("TBD: Coming in version 1.0.0");
|
||||
}
|
||||
}}>
|
||||
workflows made by other creators</span>!
|
||||
@@ -2444,7 +2434,7 @@ const GettingStarted = (props) => {
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ flex: 1, float: "right" }}>
|
||||
<ChipInput
|
||||
<MuiChipsInput
|
||||
style={{}}
|
||||
InputProps={{
|
||||
style: {
|
||||
@@ -2610,7 +2600,7 @@ const GettingStarted = (props) => {
|
||||
parsedData["field_2"] = field2;
|
||||
}
|
||||
|
||||
alert.success("Getting specific workflows from your URL.");
|
||||
toast("Getting specific workflows from your URL.");
|
||||
fetch(globalUrl + "/api/v1/workflows/download_remote", {
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
@@ -2622,7 +2612,7 @@ const GettingStarted = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert.success("Successfully loaded workflows from " + downloadUrl);
|
||||
toast("Successfully loaded workflows from " + downloadUrl);
|
||||
setTimeout(() => {
|
||||
getAvailableWorkflows();
|
||||
}, 1000);
|
||||
@@ -2633,14 +2623,14 @@ const GettingStarted = (props) => {
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
alert.error("Failed loading: " + responseJson.reason);
|
||||
toast("Failed loading: " + responseJson.reason);
|
||||
} else {
|
||||
alert.error("Failed loading");
|
||||
toast("Failed loading");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
toast(error.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Tooltip
|
||||
} from '@material-ui/core';
|
||||
} from '@mui/material';
|
||||
import FAQ from "./Faq.jsx";
|
||||
import Newsletter from "../components/Newsletter.jsx";
|
||||
import Services from "./Services.jsx";
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardActionArea from "@material-ui/core/CardActionArea";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import CardHeader from "@material-ui/core/CardHeader";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
const Workflows = (defaultprops) => {
|
||||
const { globalUrl, isLoggedIn, isLoaded } = defaultprops;
|
||||
|
||||
const theme = useTheme();
|
||||
const params = useParams();
|
||||
var props = JSON.parse(JSON.stringify(defaultprops))
|
||||
props.match = {}
|
||||
props.match.params = params
|
||||
|
||||
const [curView, setCurView] = useState(0);
|
||||
const [firstrequest, setFirstrequest] = useState(true);
|
||||
const [selectedItems, setSelectedItems] = useState([]);
|
||||
|
||||
const viewdata1 = [
|
||||
{
|
||||
title: "General",
|
||||
content: "Learn about our ticketing solutions",
|
||||
subitems: [
|
||||
{
|
||||
name: "Search",
|
||||
subtitle: "Search for anything, anywhere",
|
||||
},
|
||||
{
|
||||
name: "Message",
|
||||
subtitle: "Read and send messages",
|
||||
},
|
||||
{
|
||||
name: "Parse emails",
|
||||
subtitle: "what",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Ticketing",
|
||||
subitems: [
|
||||
{
|
||||
name: "Search",
|
||||
subtitle: "Search for anything, anywhere",
|
||||
},
|
||||
{
|
||||
name: "Message",
|
||||
subtitle: "Read and send messages",
|
||||
},
|
||||
{
|
||||
name: "Parse emails",
|
||||
subtitle: "what",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Threat intel",
|
||||
subitems: [
|
||||
{
|
||||
name: "Search",
|
||||
subtitle: "Search for anything, anywhere",
|
||||
},
|
||||
{
|
||||
name: "Message",
|
||||
subtitle: "Read and send messages",
|
||||
},
|
||||
{
|
||||
name: "Parse emails",
|
||||
subtitle: "what",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (firstrequest) {
|
||||
setFirstrequest(false);
|
||||
if (props.match.params.key) {
|
||||
console.log("PROPS: ", props.match.params.key);
|
||||
const viewitem = viewdata1.find(
|
||||
(item) =>
|
||||
item.title.toLowerCase() === props.match.params.key.toLowerCase()
|
||||
);
|
||||
if (viewitem !== undefined && viewitem !== null) {
|
||||
setCurView(1);
|
||||
//setSelectedItem(viewitem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cardContentStyle = {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
padding: 40,
|
||||
};
|
||||
|
||||
const outerGridView = {
|
||||
width: "100%",
|
||||
marginTop: 15,
|
||||
};
|
||||
|
||||
const paperStyle = {
|
||||
height: 300,
|
||||
color: "white",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
textAlign: "center",
|
||||
};
|
||||
|
||||
const HandleSelection = (data) => {
|
||||
const [selected, setSelected] = useState(false);
|
||||
|
||||
var baseStyle = JSON.parse(JSON.stringify(paperStyle));
|
||||
if (selected) {
|
||||
baseStyle.backgroundColor = "white";
|
||||
baseStyle.color = "black";
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
xs={4}
|
||||
onClick={() => {
|
||||
console.log(selectedItems);
|
||||
if (selected) {
|
||||
const index = selectedItems.findIndex(
|
||||
(item) => item.title === data.title
|
||||
);
|
||||
if (index >= 0) {
|
||||
selectedItems.splice(index, 1);
|
||||
setSelectedItems(selectedItems);
|
||||
}
|
||||
} else {
|
||||
selectedItems.push(data);
|
||||
setSelectedItems(selectedItems);
|
||||
}
|
||||
|
||||
setSelected(!selected);
|
||||
|
||||
//setCurView(1)
|
||||
//setSelectedItem(data)
|
||||
//window.location.pathname += "/"+data.title.toLowerCase()
|
||||
}}
|
||||
>
|
||||
<Card style={baseStyle}>
|
||||
<CardActionArea style={cardContentStyle}>
|
||||
<CardContent>
|
||||
<Typography variant="h4">{data.title}</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const view1 =
|
||||
curView === 0 ? (
|
||||
<div>
|
||||
<Typography variant="h4">What are you interested in?</Typography>
|
||||
<Grid container style={outerGridView} spacing={3}>
|
||||
{viewdata1.map((data) => {
|
||||
return HandleSelection(data);
|
||||
})}
|
||||
</Grid>
|
||||
{/*
|
||||
<Button variant="contained" color="primary" style={{height: 50, width: 300, margin: "auto",}} onClick={() => {
|
||||
setCurView(1)
|
||||
}}>
|
||||
Continue
|
||||
</Button>
|
||||
*/}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const view2 =
|
||||
curView === 1 ? (
|
||||
<div>
|
||||
<Typography variant="h4">Step 2.</Typography>
|
||||
{/*
|
||||
<Grid container style={outerGridView} spacing={3}>
|
||||
{selectedItem.subitems === undefined ? null :
|
||||
selectedItem.subitems.map(data => {
|
||||
return (
|
||||
<Grid item xs={4}>
|
||||
<Card style={paperStyle}>
|
||||
<CardActionArea style={cardContentStyle}>
|
||||
<CardContent>
|
||||
<Typography variant="h4">
|
||||
{data.name}
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{marginTop: 10}}>
|
||||
{data.subtitle}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
*/}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const baseView = (
|
||||
<div style={{ maxWidth: 1024, margin: "auto", paddingTop: 50 }}>
|
||||
{view1}
|
||||
{view2}
|
||||
</div>
|
||||
);
|
||||
|
||||
return <div>{baseView}</div>;
|
||||
};
|
||||
|
||||
export default Workflows;
|
||||
@@ -1,205 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
import ScheduleIcon from "@material-ui/icons/Schedule";
|
||||
import Web from "@material-ui/icons/Web";
|
||||
import AccountTree from "@material-ui/icons/AccountTree";
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
marginTop: "75px",
|
||||
textAlign: "center",
|
||||
width: "1100px",
|
||||
};
|
||||
|
||||
const surfaceColor = "#27292D";
|
||||
const boxStyle = {
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
height: "400px",
|
||||
//backgroundColor: "#e8eaf6",
|
||||
backgroundColor: surfaceColor,
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
};
|
||||
|
||||
const bodyTextStyle = {
|
||||
color: "#ffffff",
|
||||
};
|
||||
|
||||
const hrefStyle = {
|
||||
color: "black",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
// Should be different if logged in :|
|
||||
const LandingPage = (props) => {
|
||||
const { isLoaded } = props;
|
||||
|
||||
const textColor = "#8899A6";
|
||||
const iconColor = "#1DA1F2";
|
||||
const iconSize = "8em";
|
||||
const GridLayout = (header, description, link, icon) => {
|
||||
return (
|
||||
<Paper style={boxStyle}>
|
||||
<a href={link} style={hrefStyle}>
|
||||
<div style={{ flex: "1", color: "#FFFFFF" }}>
|
||||
<h2>{header}</h2>
|
||||
</div>
|
||||
<Divider />
|
||||
<div
|
||||
style={{
|
||||
flex: "3",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
marginTop: "10px",
|
||||
color: textColor,
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</div>
|
||||
<div style={{ margin: "auto" }}>{icon}</div>
|
||||
<Divider style={{ marginTop: "20px", marginBottom: "20px" }} />
|
||||
<div style={{ flex: "1", color: "#f85a3e" }}>
|
||||
<div style={{}}>Learn more</div>
|
||||
</div>
|
||||
</a>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
const listitems = [
|
||||
GridLayout(
|
||||
"Simple integrations",
|
||||
"Easily use others' or create your own integration",
|
||||
"/docs/apps",
|
||||
<Web
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
GridLayout(
|
||||
"Workflows",
|
||||
"Access the power of automation within minutes, whether its on premise or in the cloud",
|
||||
"/docs/workflows",
|
||||
<AccountTree
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
GridLayout(
|
||||
"Realtime actions",
|
||||
"Beat the clock by leveraging our realtime triggers",
|
||||
"/docs/triggers",
|
||||
<ScheduleIcon
|
||||
style={{ fontSize: iconSize, marginTop: "20px", color: iconColor }}
|
||||
/>
|
||||
),
|
||||
];
|
||||
|
||||
// The actual landing page
|
||||
// <img style={{width: "400px"}} alt={"logo"} src={Default}/>
|
||||
const landingpageDataBrowser = (
|
||||
<div>
|
||||
<div style={bodyTextStyle}>
|
||||
<h1>Shuffle</h1>
|
||||
<h3 style={{ color: "#8899A6" }}>
|
||||
A general automation solution for Infosec and IT Professionals
|
||||
</h3>
|
||||
</div>
|
||||
<a href="/register" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "180px", height: "50px", borderRadius: "0px" }}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
>
|
||||
Try it out
|
||||
</Button>
|
||||
</a>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "180px", height: "50px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
<div style={{ display: "flex", marginTop: "100px" }}>
|
||||
{listitems.map((item) => {
|
||||
return <div>{item}</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const landingpageDataMobile = (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
color: "white",
|
||||
textAlign: "center",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
}}
|
||||
>
|
||||
<h1>Shuffle</h1>
|
||||
<h3>A general automation solution for Infosec and IT Professionals</h3>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "220px", height: "60px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", marginTop: "100px" }}
|
||||
>
|
||||
<div>{listitems[0]}</div>
|
||||
<div style={{ marginTop: "20px" }}>{listitems[1]}</div>
|
||||
<div style={{ marginTop: "20px", marginBottom: "30px" }}>
|
||||
{listitems[2]}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
marginBottom: "30px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<a href="/contact" style={hrefStyle}>
|
||||
<Button
|
||||
style={{ width: "220px", height: "60px", borderRadius: "0px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Contact
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Reroute if the user is logged in
|
||||
// const landingSite = isLoggedIn ? <Workflows globalUrl={globalUrl}{...props} /> : <div style={bodyDivStyle}>{landingpageData}</div>
|
||||
const landingSite = <div style={bodyDivStyle}>{landingpageDataBrowser}</div>;
|
||||
|
||||
const loadedCheck = isLoaded ? (
|
||||
<div>
|
||||
<BrowserView>{landingSite}</BrowserView>
|
||||
<MobileView>{landingpageDataMobile}</MobileView>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
};
|
||||
export default LandingPage;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user