From 49c5291c7b64bc41f1c0027dc5c4bb2f747969f3 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 01:40:30 +0530 Subject: [PATCH 01/25] fix: disabling yash's autoscaling --- functions/onprem/worker/worker.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 189a619e..6052c9c0 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -4102,8 +4102,8 @@ func runWebserver(listener net.Listener) { log.Printf("[DEBUG] Running webserver config for SWARM and K8s") } /*** ENDREMOVE ***/ - var dockercli *dockerclient.Client - ctx := context.Background() + // var dockercli *dockerclient.Client + // ctx := context.Background() scaleReplicas := os.Getenv("SHUFFLE_APP_REPLICAS") if len(scaleReplicas) > 0 { tmpInt, err := strconv.Atoi(scaleReplicas) @@ -4130,7 +4130,7 @@ func runWebserver(listener net.Listener) { } if strings.ToLower(os.Getenv("SHUFFLE_SWARM_CONFIG")) == "run" || strings.ToLower(os.Getenv("SHUFFLE_APP_REPLICAS")) == "" { - go AutoScaleApps(ctx, dockercli, maxExecutionsPerMinute) + // go AutoScaleApps(ctx, dockercli, maxExecutionsPerMinute) } if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" { r.HandleFunc("/debug/pprof/", pprof.Index) From 8b8c9c5f10effd8d8a76bd1b28404ad4dc123f15 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 01:42:12 +0530 Subject: [PATCH 02/25] ci: enabling dev branch to make nightly releases --- .github/workflows/dockerbuild.yaml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 3d752c6a..023df0e2 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -3,7 +3,7 @@ name: dockerbuild on: push: branches: - - main + - dev paths: - "**" - "!.github/**" @@ -19,19 +19,19 @@ jobs: include: - app: frontend path: frontend - version: 2.0.0 + version: nightly experimental: true - app: backend path: backend - version: 2.0.0 + version: nightly experimental: true - app: orborus path: functions/onprem/orborus - version: 2.0.0 + version: nightly experimental: true - app: worker path: functions/onprem/worker - version: 2.0.0 + version: nightly experimental: true steps: - name: Checkout @@ -73,11 +73,8 @@ jobs: cache-to: type=local,dest=/tmp/.buildx-cache tags: | ghcr.io/shuffle/shuffle-${{ matrix.app }}:${{ matrix.version }} - ghcr.io/shuffle/shuffle-${{ matrix.app }}:latest ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:${{ matrix.version }} - ${{ secrets.DOCKERHUB_USERNAME }}/shuffle-${{ matrix.app }}:latest frikky/shuffle-${{ matrix.app }}:${{ matrix.version }} - frikky/shuffle-${{ matrix.app }}:latest frikky/shuffle:${{ matrix.app }} - name: Image digest From 25fcc68398fcc87642ad56bdfc386ab935d5f431 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 01:46:03 +0530 Subject: [PATCH 03/25] ci: workflow_dispatch trigger --- .github/workflows/dockerbuild.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 023df0e2..31ba17f3 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -1,6 +1,7 @@ name: dockerbuild on: + workflow_dispatch: push: branches: - dev From dc93ff4a7eb5ee09068765728fe4964421448a03 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 02:14:41 +0530 Subject: [PATCH 04/25] fix: further removing unnecessary window mentions --- functions/onprem/worker/worker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 6052c9c0..803576e2 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -3856,7 +3856,7 @@ func handleRunExecution(resp http.ResponseWriter, request *http.Request) { time.Sleep(time.Duration(30) * time.Second) checkUnfinished(resp, request, execRequest) }() - window.AddEvent(time.Now()) + // window.AddEvent(time.Now()) ctx := context.Background() From 2c8ed1a4185bca9488cb4214f5b7ecdf950be47d Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 04:39:56 +0530 Subject: [PATCH 05/25] ci: nightly branch change --- .github/workflows/dockerbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 31ba17f3..a04e87bd 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: branches: - - dev + - nightly paths: - "**" - "!.github/**" From 1579f525d00baa36883cf3b0e75cd5d0c952943b Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 04:50:35 +0530 Subject: [PATCH 06/25] Revert "fix: further removing unnecessary window mentions" This reverts commit dc93ff4a7eb5ee09068765728fe4964421448a03. --- functions/onprem/worker/worker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 86c0601e..4b81a566 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -3856,7 +3856,7 @@ func handleRunExecution(resp http.ResponseWriter, request *http.Request) { time.Sleep(time.Duration(30) * time.Second) checkUnfinished(resp, request, execRequest) }() - // window.AddEvent(time.Now()) + window.AddEvent(time.Now()) ctx := context.Background() From 742bdbd2216115ea01bc9bd8b3e59531b06ef7f0 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:24:50 +0530 Subject: [PATCH 07/25] Revert "Merge branch 'main' into nightly" This reverts commit 2d77af103a9ae0bc43cfe85a94097286a2177280, reversing changes made to 2c8ed1a4185bca9488cb4214f5b7ecdf950be47d. --- README.md | 1 + backend/go-app/go.mod | 16 +- backend/go-app/go.sum | 34 +- docker-compose.yml | 8 +- frontend/src/components/AppCreationModal.jsx | 64 +- frontend/src/components/AppModal.jsx | 39 +- frontend/src/components/AppSelection.jsx | 10 +- frontend/src/components/Billing.jsx | 376 +++---- frontend/src/components/BillingStats.jsx | 156 +-- frontend/src/components/CacheView.jsx | 21 +- frontend/src/components/Files.jsx | 60 +- frontend/src/components/LeftSideBar.jsx | 69 +- frontend/src/components/LicencePopup.jsx | 241 +--- frontend/src/components/Priorities.jsx | 176 +-- frontend/src/components/Priority.jsx | 2 +- frontend/src/views/Admin2.jsx | 5 +- frontend/src/views/Apps2.jsx | 185 ++- frontend/src/views/Workflows2.jsx | 1049 +++++++----------- functions/onprem/orborus/go.mod | 10 +- functions/onprem/orborus/go.sum | 26 +- functions/onprem/orborus/orborus.go | 3 +- functions/onprem/worker/worker.go | 74 +- 22 files changed, 917 insertions(+), 1708 deletions(-) diff --git a/README.md b/README.md index 9e5eae2d..b90944bf 100755 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Shuffle Automation [![CodeQL](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/codeql-analysis.yml) [![Autobuild](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml/badge.svg?branch=launch)](https://github.com/Shuffle/Shuffle/actions/workflows/dockerbuild.yaml) +[![Deploy to AWS](https://d1.awsstatic.com/cloudformation-deploy-to-aws-button.png)](https://console.aws.amazon.com/cloudformation/home?#/stacks/new?stackName=Shuffle-Instance&templateURL=https://shuffle-public-amis.s3.eu-north-1.amazonaws.com/template.yaml)

diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index b62c598d..11ecca3f 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -21,7 +21,7 @@ require ( github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 github.com/shuffle/shuffle-shared v0.6.90 - golang.org/x/crypto v0.31.0 + golang.org/x/crypto v0.22.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.63.2 gopkg.in/src-d/go-git.v4 v4.13.1 @@ -116,15 +116,15 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.19.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.18.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index 2210d186..eb35434b 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -334,6 +334,8 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdR github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shuffle/shuffle-shared v0.6.77 h1:KKtM50xW2DLuRHINxhp3uXrNH0AhiwkeiiU93a8fB3A= +github.com/shuffle/shuffle-shared v0.6.77/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= github.com/shuffle/shuffle-shared v0.6.90 h1:FzIYtEt44eWgEsW/9tj2ki7qq8FEm/HWXUok+THp72M= github.com/shuffle/shuffle-shared v0.6.90/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -408,8 +410,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 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= @@ -438,8 +440,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -466,8 +468,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 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= @@ -484,8 +486,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ 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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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= @@ -515,16 +517,16 @@ golang.org/x/sys v0.2.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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= 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= @@ -536,8 +538,8 @@ 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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -572,8 +574,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/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/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= 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= diff --git a/docker-compose.yml b/docker-compose.yml index e5bd604c..11838a86 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: frontend: - image: ghcr.io/shuffle/shuffle-frontend:1.4.0 + image: ghcr.io/shuffle/shuffle-frontend:1.4.2 container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -14,7 +14,7 @@ services: depends_on: - backend backend: - image: ghcr.io/shuffle/shuffle-backend:1.4.0 + image: ghcr.io/shuffle/shuffle-backend:1.4.2 container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} # Here for debugging: @@ -53,9 +53,9 @@ services: - SHUFFLE_PASS_WORKER_PROXY=${SHUFFLE_PASS_WORKER_PROXY} - SHUFFLE_PASS_APP_PROXY=${SHUFFLE_PASS_APP_PROXY} - SHUFFLE_STATS_DISABLED=true - # - SHUFFLE_SWARM_CONFIG=run + - SHUFFLE_SWARM_CONFIG=run - SHUFFLE_LOGS_DISABLED=true - - SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:1.4.0 + - SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:1.4.2 env_file: .env restart: unless-stopped security_opt: diff --git a/frontend/src/components/AppCreationModal.jsx b/frontend/src/components/AppCreationModal.jsx index f5a25169..7b650fbd 100644 --- a/frontend/src/components/AppCreationModal.jsx +++ b/frontend/src/components/AppCreationModal.jsx @@ -57,7 +57,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { : hover ? "" : "1px solid rgba(255,255,255,0.3)", - borderImage: makeFancy ? "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1" : "none", + borderImage: makeFancy ? "linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1" : "none", transition: 'all 0.2s ease-in-out', } @@ -340,7 +340,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { const circularLoader = validation ? : null - const errorText = openApiError?.length > 0 ?
Error: {openApiError}
: null + const errorText = openApiError?.length > 0 ?
Error: {openApiError}
: null // Common dialog styles const dialogStyle = { @@ -400,13 +400,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Create New App { - setOpenApi("") - setOpenApiError("") - setAppValidation("") - setValidation(false) - onClose() - }} + onClick={onClose} sx={{ color: 'rgba(255, 255, 255, 0.7)', '&:hover': { bgcolor: 'rgba(255, 255, 255, 0.1)' } @@ -483,14 +477,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Create New App from Open API or Swagger { - setOpenApiModal(false) - setGenerateAppModal(false) - setOpenApi("") - setOpenApiError("") - setAppValidation("") - setValidation(false) - }} + onClick={() => setOpenApiModal(false)} sx={{ color: 'rgba(255,255,255,0.7)', '&:hover': { bgcolor: 'rgba(255,255,255,0.1)' } @@ -500,16 +487,14 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { -
- +
+ Paste in the URI for the OpenAPI or find out How to find URI for openAPI? @@ -521,13 +506,12 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { fullWidth variant="outlined" placeholder="Open API URI" - style={{ fontFamily: theme?.typography?.fontFamily, fontSize: '16px' }} sx={{ '& .MuiOutlinedInput-root': { - height: '40px', + height: '40px' }, '& .MuiOutlinedInput-input::placeholder': { - fontSize: '16px' + fontSize: '14px' } }} onChange={(e) => setOpenApi(e.target.value)} @@ -542,7 +526,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { px: 3, textTransform: 'none', height: '40px', - fontSize: '16px', + fontSize: '14px', fontFamily: theme?.typography?.fontFamily, cursor: 'pointer' }} @@ -578,7 +562,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { sx={{ color: '#FF8544', borderColor: '#FF8544', - px: 5, + px: 3, py: 1, '&:hover': { borderColor: '#FF8544', @@ -586,8 +570,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { }, textTransform: 'none', fontSize: '14px', - fontFamily: theme?.typography?.fontFamily, - height: '40px' + fontFamily: theme?.typography?.fontFamily }} > Upload @@ -622,8 +605,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { }, textTransform: 'none', py: 1.5, - mb: 1.5, - fontSize: '16px', + fontSize: '14px', fontWeight: 500, width: '100%', maxWidth: '300px', @@ -666,7 +648,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Generate an app based on documentation (beta) @@ -674,10 +656,6 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { onClick={() => { setGenerateAppModal(false) setOpenApiModal(false) - setOpenApi("") - setOpenApiError("") - setAppValidation("") - setValidation(false) }} sx={{ color: 'rgba(255,255,255,0.7)', @@ -690,9 +668,9 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { Paste in a URL, and we will make it into an app for you. @@ -711,15 +689,18 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { color: 'white', '& fieldset': { borderWidth: '1px', - borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1", + borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1', + borderImageSlice: 1, }, '&:hover fieldset': { borderWidth: '1px', - borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1", + borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1', + borderImageSlice: 1, }, '&.Mui-focused fieldset': { borderWidth: '2px', - borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1", + borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1', + borderImageSlice: 1, } }, '& .MuiOutlinedInput-input::placeholder': { @@ -746,7 +727,6 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => { { {circularLoader} { !validation && - + This may take multiple minutes based on the size of the documentation. } diff --git a/frontend/src/components/AppModal.jsx b/frontend/src/components/AppModal.jsx index 546111ab..4fcc5d07 100644 --- a/frontend/src/components/AppModal.jsx +++ b/frontend/src/components/AppModal.jsx @@ -433,7 +433,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { fontFamily: theme?.typography?.fontFamily }} > - + About {app?.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())} { boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.2)" }} /> -
+
{newAppname} - - - - - + { + isCloud && ( + + + + + + ) + }
{ if (canEditApp) { const editUrl = "/apps/edit/" + (app?.id || app?.objectID); navigate(editUrl) - } else { + }else{ const forkUrl = "/apps/new?id=" + (app?.id || app?.objectID); navigate(forkUrl) } @@ -732,7 +735,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { bgcolor: '#FF8544', '&:hover': { bgcolor: '#FF8544' }, textTransform: 'none', - borderRadius: "4px", + borderRadius: 1, py: 1, px: 7, fontSize: "14px", diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index 73528ec2..e9539eeb 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -466,16 +466,14 @@ const AppSelection = props => { fontSize: 16, color: "rgba(158, 158, 158, 1)", fontFamily: theme?.typography?.fontFamily, - lineHeight: "24px" }} > - {`${window.location.pathname.includes("my_apps") || window.location.search.includes("my_apps") ? "You have no apps yet, select your starting apps here" : "Your organization has no apps yet, select your starting apps here"} - or discover more apps using the`} - { - navigate("/apps?tab=all_apps") + navigate("/apps2?tab=all_apps") }} - style={{ color: "#FF8444", fontWeight: "medium", fontSize: 16, cursor:"pointer", marginLeft: 6 }}>App Library + style={{ color: "#FF8444", fontWeight: "medium", fontSize: 16, cursor:"pointer" }}>App Library
) diff --git a/frontend/src/components/Billing.jsx b/frontend/src/components/Billing.jsx index 1bafe19b..c1ce38a3 100644 --- a/frontend/src/components/Billing.jsx +++ b/frontend/src/components/Billing.jsx @@ -51,13 +51,11 @@ import BillingStats from "./BillingStats.jsx"; import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" import DeleteIcon from '@mui/icons-material/Delete'; import { Context } from "../context/ContextApi.jsx"; -import LicencePopup from "./LicencePopup.jsx"; const Billing = memo((props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey,isLoaded, selectedOrganization, handleGetOrg, clickedFromOrgTab, removeCookie} = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, clickedFromOrgTab } = props; //const alert = useAlert(); let navigate = useNavigate(); - const [isLoggedIn, setIsLoggedIn] = useState(false) const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); const [dealList, setDealList] = React.useState([]); const [dealName, setDealName] = React.useState(""); @@ -75,17 +73,14 @@ const Billing = memo((props) => { const [currentAppRunsInNumber, setCurrentAppRunsInNumber] = useState(0); const [alertThresholds, setAlertThresholds] = useState(selectedOrganization.Billing !== undefined && selectedOrganization.Billing.AlertThreshold !== undefined && selectedOrganization.Billing.AlertThreshold !== null ? selectedOrganization.Billing.AlertThreshold : [{ percentage: '', count: '', Email_send: false }]); const [currentIndex, setCurrentIndex] = useState(0); - const [deleteAlertIndex, setDeleteAlertIndex] = useState(-1); const [deleteAlertVerification, setDeleteAlertVerification] = useState(false); + useEffect(() => { if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) { const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100; setCurrentAppRunsInPercentage(Math.round(percentage)); setCurrentAppRunsInNumber(userdata.app_execution_limit - userdata.app_execution_usage); } - if (userdata?.id?.length > 0 && isLoggedIn === false){ - setIsLoggedIn(true) - } }, [userdata]); const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email); @@ -187,8 +182,7 @@ const Billing = memo((props) => { const isCloud = window.location.host === "localhost:3002" || - window.location.host === "shuffler.io" || - window.location.host === "sandbox.shuffler.io"; + window.location.host === "shuffler.io"; billingInfo.subscription = { "active": true, @@ -425,16 +419,8 @@ const Billing = memo((props) => { const data = { org_id: orgId, email: newBillingEmail, - name: selectedOrganization.name, - description: selectedOrganization.description, - image: selectedOrganization.image, - defaults: selectedOrganization.defaults, - sso_config: selectedOrganization.sso_config, - mfa_required: selectedOrganization.mfa_required, billing: { email: newBillingEmail, - AlertThreshold: selectedOrganization?.Billing?.AlertThreshold, - Consultation: selectedOrganization?.Billing?.Consultation, }, }; @@ -761,21 +747,13 @@ const Billing = memo((props) => { open={openChangeEmailBox} onClose={handleCloseChangeEmailBox} PaperProps={{ - sx: { - borderRadius: theme?.palette?.DialogStyle?.borderRadius, - border: theme?.palette?.DialogStyle?.border, - minWidth: '440px', - fontFamily: theme?.typography?.fontFamily, - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - zIndex: 1000, - '& .MuiDialogContent-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - '& .MuiDialogTitle-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, + style: { + padding: '20px', + borderRadius: '8px', + maxWidth: '500px', + width: '100%' } - }} + }} > Change Billing Email @@ -832,7 +810,7 @@ const Billing = memo((props) => { color="primary" style={{ marginTop: !userdata.has_card_available ? 20 : 10, - borderRadius: 4, + borderRadius: 8, height: 40, fontSize: 16, color: userdata.has_card_available ? "#ff8544" : "#1a1a1a", @@ -865,7 +843,7 @@ const Billing = memo((props) => { color="primary" style={{ marginTop: 10, - borderRadius: 4, + borderRadius: 8, height: 40, fontSize: 16, color: "#1a1a1a", @@ -955,19 +933,11 @@ const Billing = memo((props) => { const url = `${globalUrl}/api/v1/orgs/${selectedOrganization.id}`; const data = { org_id: selectedOrganization.id, - name: selectedOrganization.name, - description: selectedOrganization.description, - image: selectedOrganization.image, - defaults: selectedOrganization.defaults, - sso_config: selectedOrganization.sso_config, - mfa_required: selectedOrganization.mfa_required, Billing: { Consultation: { hours: String(inputHour), minutes: String(inputMinutes), }, - AlertThreshold: selectedOrganization.Billing.AlertThreshold, - email: selectedOrganization.Billing.email, } }; @@ -1060,9 +1030,9 @@ const Billing = memo((props) => {
{ color="primary" style={{ marginTop: userdata.support ? 0 : 10, - borderRadius: 4, + borderRadius: 8, height: 40, fontSize: 16, color: "#1A1A1A", @@ -1172,23 +1142,8 @@ const Billing = memo((props) => { > Buy - { setClickOnBuy(false) }} PaperProps={{ - sx: { - borderRadius: theme?.palette?.DialogStyle?.borderRadius, - border: theme?.palette?.DialogStyle?.border, - minWidth: '440px', - fontFamily: theme?.typography?.fontFamily, - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - zIndex: 1000, - '& .MuiDialogContent-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - '& .MuiDialogTitle-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - } - }}> - + { setClickOnBuy(false) }} PaperProps={{style: {backgroundColor: "rgb(26, 26, 26)"}}}> + You will be taken to Stripe to book professional service hours. You can adjust the number of hours on the left side of the Stripe page. @@ -1223,9 +1178,10 @@ const Billing = memo((props) => { color="primary" style={{ marginTop: userdata.support ? 5 : 10, - borderRadius: 4, + borderRadius: 8, height: 40, fontSize: 16, + color: "#ff8544", textTransform: 'none', cursor: getProfessionalServices ? 'pointer' : 'not-allowed', opacity: getProfessionalServices ? 1 : 0.6, @@ -1246,23 +1202,13 @@ const Billing = memo((props) => { fullWidth style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }} PaperProps={{ - sx: { - borderRadius: theme?.palette?.DialogStyle?.borderRadius, - border: theme?.palette?.DialogStyle?.border, - minWidth: '440px', - fontFamily: theme?.typography?.fontFamily, - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - zIndex: 1000, - '& .MuiDialogContent-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - '& .MuiDialogTitle-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, + style: { + width: 500, + margin: 0, } - }} + }} > - + Upgrade Consultation Plan @@ -1298,7 +1244,7 @@ const Billing = memo((props) => {
-
+
- )} - setDeleteAlertVerification(false)} - sx={{ - '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)' }, - }} - > - Are you sure you want to delete this threshold? - - - - - -
- - + {alertThresholds[index].Email_send === true && } + { + alertThresholds.length > 1 && + ( + + ) + } + setDeleteAlertVerification(false)} sx={{ '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)', }, }}> + Are you sure you want to delete this threshold? + + + + + +
))}
@@ -2575,8 +2449,10 @@ const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => { : "auto", padding: "27px 10px 19px 27px", backgroundColor: '#212121', + borderRadius: '16px', height: '100%', boxSizing: 'border-box', + borderLeft: '1px solid #494949', overflow: 'hidden', maxHeight: "1700px", overflowY: "auto",scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }), [clickedFromOrgTab]); diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx index 231b5686..8b5d4a1c 100644 --- a/frontend/src/components/BillingStats.jsx +++ b/frontend/src/components/BillingStats.jsx @@ -648,13 +648,12 @@ const AppStats = (defaultprops) => { All shown statistics are gathered from Your Organisation Statistics. It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. The billing tracker is in Beta, and is always calculated manually before being invoiced.
-
-
+
{filteredStatistics !== undefined ?
{
: null} -
- {clickedFromOrgTab? ( -
-
- - Search from - +
+
-
-
- - Search until - - } /> -
+
+
+ } + /> +
- + ):(
@@ -890,6 +794,8 @@ const AppStats = (defaultprops) => {
)} + +
{appRuns === undefined ? diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 636509eb..4e8c6c24 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -412,7 +412,7 @@ const CacheView = memo((props) => {
@@ -711,7 +711,7 @@ const Files = memo((props) => { }} /> - {setOpenChangeEmailBox(false)}} - PaperProps={{ - sx: { - borderRadius: theme?.palette?.DialogStyle?.borderRadius, - border: theme?.palette?.DialogStyle?.border, - minWidth: '440px', - fontFamily: theme?.typography?.fontFamily, - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - zIndex: 1000, - '& .MuiDialogContent-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - '& .MuiDialogTitle-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - '& .MuiDialogActions-root': { - backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, - }, - } - }} - > - Change Billing Email - - - Enter the new billing email address. - - { if (event.key === 'Enter') HandleChangeBillingEmail(selectedOrganization.id) }} - onChange={(e) => setNewBillingEmail(e.target.value)} - /> - - - - - - -
+ {isCloud ? + Billing email: {selectedOrganization.org} : null}
- - - + + {/*
@@ -914,7 +736,7 @@ const LicencePopup = (props) => { return (
- + {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? {
- -
+ + + + + -
- - - -
) } diff --git a/frontend/src/components/Priorities.jsx b/frontend/src/components/Priorities.jsx index a78e8306..29078a6a 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -255,7 +255,7 @@ const Priorities = memo((props) => { onClick={() => { clearNotifications() }} - style={{marginLeft: 50, textTransform: "none", fontSize: 16}} + style={{marginLeft: 50, }} > Mark all as read @@ -265,7 +265,7 @@ const Priorities = memo((props) => { {clickedFromOrgTab? null : }

Suggestions

- + Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company.
These range from simple configurations in Shuffle to Usecases you may have missed.  { : null } - + {data.description}
- + + + {data.read === false ? ( + + ) : null} + + + + - {data.read === false ? ( - - ) : null} + + First seen: {new Date(data.created_at * 1000).toISOString().slice(0, 19)} + - - - - - - First seen:{" "} - {new Date(data.created_at * 1000).toISOString().slice(0, 19)} - - - - Last seen:{" "} - {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} - - - - Times seen: {data.amount} - -
+ + Last seen: {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} + + + Times seen: {data.amount} + + ); }) diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 4bd3a6f2..4d37142f 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -154,7 +154,7 @@ const Priority = (props) => { }
- @@ -2079,7 +2062,6 @@ const Apps2 = (props) => { handleAppClick={handleAppClick} leftSideBarOpenByClick={leftSideBarOpenByClick} userdata={userdata} - fetchApps={fetchApps} /> ))}
@@ -2124,7 +2106,6 @@ const Apps2 = (props) => { {appsToShow.map((data, index) => ( ))} diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 80ef805a..aa5bd735 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -1,5 +1,5 @@ // React & Core imports -import React, { useEffect, useContext, memo, useState, useRef } from "react"; +import React, { useEffect, useContext, memo, useState } from "react"; import { useLocation, useNavigate, Link } from "react-router-dom"; import ReactDOM from "react-dom" @@ -104,10 +104,7 @@ import { MuiChipsInput } from "mui-chips-input"; import { v4 as uuidv4 } from "uuid"; import theme from "../theme.jsx"; import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch, Configure, connectHits, connectSearchBox, connectRefinementList } from 'react-instantsearch-dom'; -import { debounce } from "lodash"; -import { removeQuery } from "../components/ScrollToTop.jsx"; - +import { InstantSearch, Configure, connectHits } from 'react-instantsearch-dom'; const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240"); @@ -671,8 +668,6 @@ const Workflows2 = (props) => { const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove const [selectedUsecases, setSelectedUsecases] = React.useState([]); const [filteredWorkflows, setFilteredWorkflows] = React.useState([]); - const [orgWorkflows, setOrgWorkflows] = React.useState([]); - const [myWorkflows, setMyWorkflows] = React.useState([]); const [selectedWorkflow, setSelectedWorkflow] = React.useState({}); const [workflowDone, setWorkflowDone] = React.useState(false); const [selectedWorkflowId, setSelectedWorkflowId] = React.useState(""); @@ -721,47 +716,18 @@ const Workflows2 = (props) => { const [apps, setApps] = React.useState([]); document.title = "Shuffle - Workflows"; - - useEffect(() => { - const queryParams = new URLSearchParams(location.search); - const tabParam = queryParams.get('tab'); - if (tabParam !== null && tabParam !== undefined) { - if (tabParam === 'org_workflows' && currTab !== 0) { - setCurrTab(0); - } else if (tabParam === 'my_workflows' && currTab !== 1) { - setCurrTab(1); - } else if (tabParam === 'all_workflows' && currTab !== 2) { - setCurrTab(2); - } - } - }, [location.search]); - const handleTabChange = (event, newValue) => { - setCurrTab(newValue); - // Update URL query params based on tab index - const tabMapping = { - 0: 'org_workflows', - 1: 'my_workflows', - 2: 'all_workflows' - }; - const queryParams = new URLSearchParams(location.search); - queryParams.set('tab', tabMapping[newValue]); - - - navigate(`${location.pathname}?${queryParams.toString()}`); - }; - - useEffect(() => { - if (currTab === 2) { + // Set loading when switching to public workflows tab + if (newValue === 2) { setIsLoadingPublicWorkflow(true); // Simulate loading time for the Algolia search results setTimeout(() => { setIsLoadingPublicWorkflow(false); - }, 2500); + }, 2000); } - }, [currTab]) - + setCurrTab(newValue); + }; const handleCreateWorkflow = () => { setModalOpen(true) @@ -1237,7 +1203,6 @@ const Workflows2 = (props) => { }, [isDropzone]); - const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", @@ -1275,7 +1240,6 @@ const Workflows2 = (props) => { const getAvailableWorkflows = (amount) => { var storageWorkflows = [] - setIsLoadingWorkflow(true) try { const storagewf = localStorage.getItem("workflows") storageWorkflows = JSON.parse(storagewf) @@ -1563,13 +1527,13 @@ const Workflows2 = (props) => { overflow: "hidden", width: "100%", color: "white", + padding: "12px 12px 0px 15px", display: "flex", fontFamily: theme?.typography?.fontFamily, boxSizing: "border-box", position: "relative", borderRadius: "8px", // backgroundColor: "#212121", - padding: "15px 15px 0px 20px", }; const gridContainer = { @@ -2205,6 +2169,7 @@ const Workflows2 = (props) => { ); }; + // Replace the loading sections in the main component with this const LoadingWorkflowGrid = () => { return (
{ if ( parsedName !== undefined && parsedName !== null && - parsedName.length > 35 + parsedName.length > 20 ) { - parsedName = parsedName.slice(0, 34) + ".."; + parsedName = parsedName.slice(0, 21) + ".."; } const actions = data.actions !== null ? data.actions.length : 0; @@ -2477,7 +2442,7 @@ const Workflows2 = (props) => { } return ( -
+
{selectedCategory !== "" ? @@ -3941,25 +3906,59 @@ const Workflows2 = (props) => { // } useEffect(() => { - if (currTab === 2) return; - if (userdata !== undefined && userdata !== null && filteredWorkflows.length > 0) { - var categoryWorkflows = [] + if (userdata !== undefined && userdata !== null && currTab !== 2) { + setIsLoadingWorkflow(true); + var filteredWorkflows = [] if (currTab === 0) { - categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) - setOrgWorkflows(categoryWorkflows) + filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) } else if (currTab === 1) { - categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id) - setMyWorkflows(categoryWorkflows) + filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id) } - setIsLoadingWorkflow(false); + setFilteredWorkflows(filteredWorkflows) + setTimeout(() => { + setIsLoadingWorkflow(false); + }, 500); } - }, [currTab, workflows, userdata, filteredWorkflows]) + }, [currTab, workflows, userdata]) + useEffect(() => { + console.log("SearchQuery: ", searchQuery) + }, [searchQuery]) + const Hits = ({ hits }) => { + var counted = 0 + console.log("Public workflows", hits) + return ( + isLoadingPublicWorkflow ? + ( + + ) : ( + +
+ {hits.map((data, index) => { + return + })} +
+ ) + + ) + } + + const CustomHits = connectHits(Hits) const handleCategoryChange = (e) => { @@ -3973,7 +3972,7 @@ const Workflows2 = (props) => { borderRadius: '4px', padding: "12px 16px", cursor: 'pointer', - minWidth: '40px', + minWidth: '40px', height: 'auto', display: 'flex', alignItems: 'center', @@ -3981,210 +3980,6 @@ const Workflows2 = (props) => { } - const Hits = ({ hits, isSearchStalled, searchQuery }) => { - - return ( - isLoadingPublicWorkflow ? ( - - ) : ( -
- {hits.map((data, index) => { - return - })} -
- ) - ) - }; - - const SearchBox = ({ refine, searchQuery, setSearchQuery }) => { - - const inputRef = useRef(null); - const [localQuery, setLocalQuery] = useState(searchQuery); - const location = useLocation(); - - - useEffect(() => { - if (searchQuery) { - setLocalQuery(searchQuery); - refine(searchQuery); - } - }, [searchQuery, refine]); - - // Debounced function to refine search - const debouncedRefine = useRef( - debounce((value) => { - setSearchQuery(value); - removeQuery("q"); - refine(value); - }, 300) - ).current; - - useEffect(() => { - const urlSearchParams = new URLSearchParams(window.location.search); - const params = Object.fromEntries(urlSearchParams.entries()); - const foundQuery = params["q"]; - if (foundQuery) { - setLocalQuery(foundQuery); - debouncedRefine(foundQuery); - } - }, [debouncedRefine]); - - useEffect(() => { - if (searchQuery === "") { - return; - } - if (inputRef.current) { - inputRef.current.focus(); - } - }, [searchQuery]); - - const handleChange = (event) => { - const value = event.target.value; - setLocalQuery(value); - debouncedRefine(value); - }; - - return ( - { - if (event.key === "Enter") { - event.preventDefault(); - } - }} - limit={5} - variant="outlined" - placeholder="Search Workflows" - /> - ) - } - - const CustomSearchBox = connectSearchBox(SearchBox) - const CustomHits = connectHits(Hits) - - const CategoryDropdown = ({ refine, currentRefinement, items }) => { - - const handleChange = (event) => { - // Get the selected values array from the event - const selectedValues = event.target.value; - refine(selectedValues); - }; - - return ( - - ); - }; - const CustomCategoryDropdown = connectRefinementList(CategoryDropdown) - - - const WorkflowView = memo(() => { if (workflows.length === 0) { } @@ -4195,413 +3990,363 @@ const Workflows2 = (props) => { const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) return ( <> - -
- - Workflows - +
+ + Workflows + -
- - - - - -
+
+ + + + + +
-
+
- {currTab === 2 ? ( - - ) : ( - - // - // - // ), - onKeyDown: (e) => { - // Prevent default behavior for Enter and Backspace - if (e.key === 'Enter' || e.key === 'Backspace') { - e.preventDefault(); - e.stopPropagation(); - e.target.focus(); + + + // + // + // ), + onKeyDown: (e) => { + // Prevent default behavior for Enter and Backspace + if (e.key === 'Enter' || e.key === 'Backspace') { + e.preventDefault(); + e.stopPropagation(); + e.target.focus(); + } + }, + }} + clearInputOnBlur={false} + sx={{ + // Container styling + '& .MuiOutlinedInput-root': { + height: "fit-content", + borderRadius: '4px', + backgroundColor: '#212121', + '& fieldset': { + borderColor: 'rgba(255, 255, 255, 0.23)', + }, + '&:hover fieldset': { + borderColor: 'rgba(255, 255, 255, 0.4)', + }, + }, + + // Adjust chip container to center vertically + '& .MuiInputBase-root': { + display: 'flex', + flexWrap: 'wrap', + gap: '4px', + padding: '4px 8px', + alignItems: 'center', + height: "fit-content", // Match height + }, + + // Rest of the styling remains the same... + }} + value={filters} + onChange={(chips) => { + setFilters(chips); + findWorkflow(chips); + }} + //onAdd={(chip) => { + // console.log("ADd: ", chip); + // addFilter(chip); + //}} + //onDelete={(_, index) => { + // console.log("Remove: ", index); + // removeFilter(index); + //}} + /> + + selected.length ? selected.join(', ') : 'All Categories'} > - - All Categories - - {usecases.map((usecase, index) => { - if (usecase?.name === "5. Verify") { - return null; - } - - const percentDone = usecase.matches.length > 0 ? parseInt(usecase.matches.length / usecase.list.length * 100) : 0 - if (percentDone === 0) { - usecase = findMatches(usecase, workflows) - } - - const category = usecase?.name.split(" ")[1] - return ( - { - if (!filters.includes(usecase?.name.toLowerCase())) { - addFilter(usecase.name) - } else { - removeFilter(filters.indexOf(usecase?.name.toLowerCase())) - } - }} +
+ +
+ -
- -
- - {category} - - - {usecase?.matches.length}/{usecase?.list.length} - -
-
- - ) - })} - - ) - } - { - currTab === 2 && ( - - ) - } - -
-
- - navigate("/workflows/debug")} - disabled={currTab === 2} - > - - - - - - { - const newView = view === "grid" ? "list" : "grid"; - localStorage.setItem("workflowView", newView); - setView(newView); - }} - disabled={currTab === 2} - > - {view === "grid" ? : } - - - - - upload.click()} - disabled={currTab === 2} - > - {submitLoading ? : } - - - - (upload = ref)} - onChange={importFiles} - /> - - - exportAllWorkflows(workflows)} - > - - - -
- -
- - -
-
- { - (isLoadingWorkflow && currTab !== 2) ? ( - - ) : ( - view === "grid" && currTab !== 2 ? ( - <> -
- - - - {currTab === 0 && orgWorkflows.map((data, index) => { - // Shouldn't be a part of this list - if (data.public === true) { - return null - } - - // if (firstLoad) { - // workflowDelay += 75 - // } else { - // return - // } - - return ( - - {/**/} - - {/**/} - - ) - })} - - { - currTab === 1 && myWorkflows.map((data, index) => { - if (data.public === true) { - return null - } - - // if (firstLoad) { - // workflowDelay += 75 - // } else { - // return - // } - - return ( - - {/**/} - - {/**/} - - ) - }) - } + {category} + + + {usecase?.matches.length}/{usecase?.list.length} +
- - ) : ( - currTab !== 2 && - ) +
+ ) - } + })} + - { - currTab === 2 && - ( - - ) - } +
+
+ + navigate("/workflows/debug")} + disabled={currTab === 2} + > + + + + + + { + const newView = view === "grid" ? "list" : "grid"; + localStorage.setItem("workflowView", newView); + setView(newView); + }} + disabled={currTab === 2} + > + {view === "grid" ? : } + + + + + upload.click()} + disabled={currTab === 2} + > + {submitLoading ? : } + + + + (upload = ref)} + onChange={importFiles} + /> + + + exportAllWorkflows(workflows)} + > + + + +
+
+ +
- - +
+ { + isLoadingWorkflow ? ( + + ) : ( + view === "grid" && currTab !== 2 ? ( + <> +
+ + + + {filteredWorkflows.map((data, index) => { + // Shouldn't be a part of this list + if (data.public === true) { + return null + } + + if (firstLoad) { + workflowDelay += 75 + } else { + return + } + + return ( + + {/**/} + + {/**/} + + ) + })} +
+ + ) : ( + currTab !== 2 && + ) + ) + } + + { + currTab === 2 && + ( + + + + + ) + } + +
+
+ ); @@ -5011,7 +4756,7 @@ const Workflows2 = (props) => { ); // Maybe use gridview or something, idk - return
{loadedCheck}
; + return
{loadedCheck}
; }; diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 9cf9229f..33577275 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -95,14 +95,14 @@ require ( go.opentelemetry.io/otel/sdk v1.30.0 // indirect go.opentelemetry.io/otel/trace v1.30.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.27.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index de1d6e82..02b90f14 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -301,6 +301,12 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= 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.6.74 h1:os3BDSFZnl4U8ZgsTAY8IsTDADcMXhbc1rS9UMa0BIY= +github.com/shuffle/shuffle-shared v0.6.74/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= +github.com/shuffle/shuffle-shared v0.6.79 h1:MIy5kcShHYN05ov/50YJ+la1C2v1rL8IENapOvX9I8U= +github.com/shuffle/shuffle-shared v0.6.79/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= +github.com/shuffle/shuffle-shared v0.6.83 h1:gceT91WtFqh3h9juzTipDhWpxZLfrdtbcsnK+XNj57g= +github.com/shuffle/shuffle-shared v0.6.83/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= github.com/shuffle/shuffle-shared v0.6.90 h1:FzIYtEt44eWgEsW/9tj2ki7qq8FEm/HWXUok+THp72M= github.com/shuffle/shuffle-shared v0.6.90/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -366,8 +372,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= 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= @@ -443,8 +449,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ 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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -473,16 +479,16 @@ golang.org/x/sys v0.2.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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= 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= @@ -494,8 +500,8 @@ 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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 7eaada58..b6a6b45b 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -608,8 +608,7 @@ func deployServiceWorkers(image string) { Condition: swarm.RestartPolicyConditionOnFailure, }, Placement: &swarm.Placement{ - Constraints: []string{ - }, + Constraints: []string{}, }, }, } diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 4b81a566..6052c9c0 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -27,13 +27,13 @@ import ( dockerclient "github.com/docker/docker/client" // This is for automatic removal of certain code :) - /*** ***/ + /*** STARTREMOVE ***/ "math/rand" "github.com/docker/docker/api/types/swarm" uuid "github.com/satori/go.uuid" - /*** ***/ + /*** ENDREMOVE ***/ "github.com/gorilla/mux" @@ -321,11 +321,11 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason */ } else { - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" { log.Printf("[DEBUG][%s] NOT cleaning up containers. IDS: %d, CLEANUP env: %s", workflowExecution.ExecutionId, 0, cleanupEnv) } - /*** ***/ + /*** ENDREMOVE ***/ } if len(reason) > 0 && len(nodeId) > 0 { @@ -393,7 +393,7 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason func int32Ptr(i int32) *int32 { return &i } -// ** ***/ +// ** STARTREMOVE ***/ func deployk8sApp(image string, identifier string, env []string) error { if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 { kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") @@ -631,7 +631,7 @@ func deployk8sApp(image string, identifier string, env []string) error { return nil } -//** ***/ +//** ENDREMOVE ***/ // Deploys the internal worker whenever something happens func deployApp(cli *dockerclient.Client, image string, identifier string, env []string, workflowExecution shuffle.WorkflowExecution, action shuffle.Action) error { @@ -772,7 +772,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] } } - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" { appName := strings.Replace(identifier, fmt.Sprintf("_%s", action.ID), "", -1) @@ -802,14 +802,14 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] return err } } else { - // ** ***/ + // ** STARTREMOVE ***/ exposedPort = 80 err = findAppInfoKubernetes(image, appName, env) if err != nil { log.Printf("[ERROR] Failed finding and creating port for %s: %s", appName, err) return err } - // ** ***/ + // ** ENDREMOVE ***/ } /* @@ -852,7 +852,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] return nil } - /*** ***/ + /*** ENDREMOVE ***/ // Max 10% CPU every second //CPUShares: 128, @@ -2404,7 +2404,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl return } - /*** ***/ + /*** STARTREMOVE ***/ if workflowExecution.Status == "WAITING" && (os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm") { log.Printf("[INFO][%s] Workflow execution is waiting while in swarm. Sending info to backend to ensure execution stops.", workflowExecution.ExecutionId) @@ -2416,7 +2416,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl shutdown(*workflowExecution, "", "", false) } } - /*** ***/ + /*** ENDREMOVE ***/ } else { if strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already been ran") || strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already finished") { log.Printf("[ERROR][%s] Skipping rerun of action result as it's already been ran: %s", workflowExecution.ExecutionId) @@ -2501,7 +2501,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl return } - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" { finished := shuffle.ValidateFinished(ctx, -1, *workflowExecution) if !finished { @@ -2516,7 +2516,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl sendResult(*workflowExecution, shutdownData) } } - /*** ***/ + /*** ENDREMOVE ***/ } else { log.Printf("[INFO][%s] Skipping setexec with status %s", workflowExecution.ExecutionId, workflowExecution.Status) @@ -2536,12 +2536,12 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl func sendSelfRequest(actionResult shuffle.ActionResult) { - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" { log.Printf("[INFO][%s] Not sending self request info since source is default (not swarm)", actionResult.ExecutionId) return } - /*** ***/ + /*** ENDREMOVE ***/ data, err := json.Marshal(actionResult) if err != nil { @@ -2680,11 +2680,11 @@ func validateFinished(workflowExecution shuffle.WorkflowExecution) bool { } } - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" { requestsSent += 1 } - /*** ***/ + /*** ENDREMOVE ***/ log.Printf("[DEBUG][%s] Should send full result to %s", workflowExecution.ExecutionId, baseUrl) @@ -2769,7 +2769,7 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { // GetLocalIP returns the non loopback local IP of the host func getLocalIP() string { - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("IS_KUBERNETES") == "true" { return "shuffle-workers" } @@ -2836,7 +2836,7 @@ func getLocalIP() string { return foundIP } } - /*** ***/ + /*** ENDREMOVE ***/ addrs, err := net.InterfaceAddrs() if err != nil { @@ -2887,7 +2887,7 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener { log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl) - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" { log.Printf("[DEBUG] Starting webserver (1) on port %d with hostname: %s", baseport, hostname) @@ -2907,7 +2907,7 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener { return listener } - /*** ***/ + /*** ENDREMOVE ***/ port := listener.Addr().(*net.TCPAddr).Port // Set the port environment variable @@ -2958,7 +2958,7 @@ func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) { */ } -/*** ***/ +/*** STARTREMOVE ***/ func deploySwarmService(dockercli *dockerclient.Client, name, image string, deployport int) error { log.Printf("[DEBUG] Deploying service for %s to swarm on port %d", name, deployport) //containerName := fmt.Sprintf("shuffle-worker-%s", parsedUuid) @@ -3128,10 +3128,10 @@ func deploySwarmService(dockercli *dockerclient.Client, name, image string, depl return nil } -/*** ***/ +/*** ENDREMOVE ***/ // Runs data discovery -/*** ***/ +/*** STARTREMOVE ***/ func findAppInfoKubernetes(image, name string, env []string) error { clientset, _, err := shuffle.GetKubernetesClient() @@ -3291,7 +3291,7 @@ func findAppInfo(image, name string) (int, error) { return exposedPort, nil } -/*** ***/ +/*** ENDREMOVE ***/ func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, action *shuffle.Action, workflowExecution *shuffle.WorkflowExecution) error { parsedRequest := shuffle.OrborusExecutionRequest{ @@ -3691,11 +3691,11 @@ func getStreamResultsWrapper(client *http.Client, req *http.Request, workflowExe // Initial loop etc func main() { - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" { logsDisabled = "true" } - /*** ***/ + /*** ENDREMOVE ***/ // Elasticsearch necessary to ensure we'ren ot running with Datastore configurations for minimal/maximal data sizes // Recursive import kind of :) _, err := shuffle.RunInit(*shuffle.GetDatastore(), *shuffle.GetStorage(), "", "worker", true, "elasticsearch", false, 0) @@ -3729,7 +3729,7 @@ func main() { swarmConfig := os.Getenv("SHUFFLE_SWARM_CONFIG") log.Printf("[INFO] Running with timezone %s and swarm config %#v", timezone, swarmConfig) - /*** ***/ + /*** STARTREMOVE ***/ if swarmConfig == "run" || swarmConfig == "swarm" { // Forcing download just in case on the first iteration. log.Printf("[INFO] Running in swarm mode - forcing download of apps") @@ -3744,7 +3744,7 @@ func main() { log.Printf("[ERROR] Stopped listener %#v - exiting.", listener) os.Exit(3) } - /*** ***/ + /*** ENDREMOVE ***/ authorization := "" executionId := "" @@ -4097,15 +4097,13 @@ func runWebserver(listener net.Listener) { // What would be require to run a workflow otherwise? // Maybe directly /workflow/run - /*** ***/ + /*** STARTREMOVE ***/ if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" { log.Printf("[DEBUG] Running webserver config for SWARM and K8s") } /*** ENDREMOVE ***/ // var dockercli *dockerclient.Client // ctx := context.Background() - /*** ***/ - scaleReplicas := os.Getenv("SHUFFLE_APP_REPLICAS") if len(scaleReplicas) > 0 { tmpInt, err := strconv.Atoi(scaleReplicas) @@ -4131,13 +4129,9 @@ func runWebserver(listener net.Listener) { log.Printf("[DEBUG] SHUFFLE_APP_EXECUTIONS_PER_MINUTE set to value %s. Trying to overwrite default (%d)", os.Getenv("SHUFFLE_APP_EXECUTIONS_PER_MINUTE"), maxExecutionsPerMinute) } - /* - ctx := context.Background() if strings.ToLower(os.Getenv("SHUFFLE_SWARM_CONFIG")) == "run" || strings.ToLower(os.Getenv("SHUFFLE_APP_REPLICAS")) == "" { // go AutoScaleApps(ctx, dockercli, maxExecutionsPerMinute) } - */ - if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" { r.HandleFunc("/debug/pprof/", pprof.Index) r.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP) @@ -4183,14 +4177,12 @@ func AutoScaleApps(ctx context.Context, client *dockerclient.Client, maxExecutio case <-ticker.C: count := window.CountEvents(time.Now()) - - appNumber := numberOfApps(ctx, client) + j := numberOfApps(ctx, client) workers := numberOfWorkers(ctx, client) - execPerMin := maxExecutionsPerMinute / workers if count >= execPerMin { log.Printf("[DEBUG] Too many executions per minute (%d). Scaling down to %d", count, execPerMin) - scaleApps(ctx, client, uint64(appNumber+1)) + scaleApps(ctx, client, uint64(j+1)) } } } From 3cadd3aeb3ed512ebee4bc70f1d4ee941a2ded80 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:26:23 +0530 Subject: [PATCH 08/25] fix: workflow as code disabled --- frontend/src/components/EditWorkflow.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/EditWorkflow.jsx b/frontend/src/components/EditWorkflow.jsx index a7694562..451967f6 100644 --- a/frontend/src/components/EditWorkflow.jsx +++ b/frontend/src/components/EditWorkflow.jsx @@ -1149,7 +1149,7 @@ const EditWorkflow = (props) => {
: null} - {!isEditing ? <> + {/* {!isEditing ? <>
} @@ -1164,7 +1164,7 @@ const EditWorkflow = (props) => { />
- : null} + : null} */} - { setClickOnBuy(false) }} PaperProps={{style: {backgroundColor: "rgb(26, 26, 26)"}}}> - + { setClickOnBuy(false) }} PaperProps={{ + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: '440px', + fontFamily: theme?.typography?.fontFamily, + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + zIndex: 1000, + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + } + }}> + You will be taken to Stripe to book professional service hours. You can adjust the number of hours on the left side of the Stripe page. @@ -1178,10 +1223,9 @@ const Billing = memo((props) => { color="primary" style={{ marginTop: userdata.support ? 5 : 10, - borderRadius: 8, + borderRadius: 4, height: 40, fontSize: 16, - color: "#ff8544", textTransform: 'none', cursor: getProfessionalServices ? 'pointer' : 'not-allowed', opacity: getProfessionalServices ? 1 : 0.6, @@ -1202,13 +1246,23 @@ const Billing = memo((props) => { fullWidth style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }} PaperProps={{ - style: { - width: 500, - margin: 0, + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: '440px', + fontFamily: theme?.typography?.fontFamily, + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + zIndex: 1000, + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, } - }} + }} > - + Upgrade Consultation Plan @@ -1244,7 +1298,7 @@ const Billing = memo((props) => {
-
+
- ) - } - setDeleteAlertVerification(false)} sx={{ '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)', }, }}> - Are you sure you want to delete this threshold? - - - - - -
+ + {alertThresholds[index].Email_send === true && ( + + + + )} + + {alertThresholds.length > 1 && ( + + )} + setDeleteAlertVerification(false)} + sx={{ + '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)' }, + }} + > + Are you sure you want to delete this threshold? + + + + + +
+ + ))}
@@ -2449,10 +2575,8 @@ const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => { : "auto", padding: "27px 10px 19px 27px", backgroundColor: '#212121', - borderRadius: '16px', height: '100%', boxSizing: 'border-box', - borderLeft: '1px solid #494949', overflow: 'hidden', maxHeight: "1700px", overflowY: "auto",scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }), [clickedFromOrgTab]); diff --git a/frontend/src/components/BillingStats.jsx b/frontend/src/components/BillingStats.jsx index 8b5d4a1c..231b5686 100644 --- a/frontend/src/components/BillingStats.jsx +++ b/frontend/src/components/BillingStats.jsx @@ -648,12 +648,13 @@ const AppStats = (defaultprops) => { All shown statistics are gathered from Your Organisation Statistics. It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. The billing tracker is in Beta, and is always calculated manually before being invoiced. -
+
+
{filteredStatistics !== undefined ?
{
: null} +
+ {clickedFromOrgTab? ( -
-
+
+
+ + Search from + } + slotProps={{ + textField: { + sx: { + "& .MuiOutlinedInput-root": { + "& fieldset": { + borderColor: "#494949 !important", + borderWidth: "1px !important", + }, + "&:hover fieldset": { + borderColor: "#FFFFFF !important", + }, + "&.Mui-focused fieldset": { + borderColor: "#FFFFFF !important", + }, + height: "35px", + fontSize: 16, + color: "#c8c8c8", + }, + }, + }, + }} + sx={{ + "& .MuiInputBase-root": { + height: "35px", + minHeight: "35px", + }, + "& .MuiInputBase-input": { + height: "35px", + padding: "0 14px", + boxSizing: "border-box", + color: "#c8c8c8", + fontSize: 16, + } + }} + ampm={false} + format="YYYY-MM-DD HH:mm:ss" + value={startTime} + onChange={handleStartTimeChange} /> -
-
+
+
+ + Search until + } + "& .MuiInputBase-root": { + height: "35px", + minHeight: "35px", + }, + "& .MuiInputBase-input": { + height: "35px", + padding: "0 14px", + boxSizing: "border-box", + color: "#c8c8c8", + fontSize: 16, + } + }} + ampm={false} + format="YYYY-MM-DD HH:mm:ss" + value={endTime} + onChange={handleEndTimeChange} /> -
+
- + ):(
@@ -794,8 +890,6 @@ const AppStats = (defaultprops) => {
)} - -
{appRuns === undefined ? diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 4e8c6c24..7929a7ab 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -303,7 +303,7 @@ const CacheView = memo((props) => { Key {
{
@@ -711,7 +711,7 @@ const Files = memo((props) => { }} /> @@ -1278,7 +1279,7 @@ useEffect(() => { }} disabled={userdata?.support === false} > - + • { display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", + fontSize: 18, color: userdata?.support && currentOpenTab === "detection" && currentPath.includes("/detection") ? "#F1F1F1" @@ -1329,7 +1331,7 @@ useEffect(() => { cursor: userdata?.support ? "pointer" : "not-allowed", }} > - + • { display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", + fontSize: 18, color: userdata?.support && currentOpenTab === "response" && currentPath.includes("/response") ? "#F1F1F1" @@ -1380,7 +1383,7 @@ useEffect(() => { cursor: userdata?.support ? "pointer" : "not-allowed", }} > - + • { display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", + fontSize: 18, color: userdata?.support && currentOpenTab === "response" && currentPath.includes("/response") ? "#F1F1F1" @@ -1458,7 +1462,7 @@ useEffect(() => { { }, transition: "opacity 0.3s ease", textTransform: "none", - fontSize: 16, + fontSize: 18, }} disableElevation disableRipple @@ -1718,13 +1722,14 @@ useEffect(() => { style={{ color: "#CDCDCD", margin: "0 5px", - fontSize: 16, + fontSize: 18, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: 150, textAlign: "center", marginLeft: 10, + }} > {userdata?.username} diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index 883f156d..b8d009a3 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -24,6 +24,7 @@ import { DialogActions, CardContent, ButtonGroup, + DialogContentText, } from "@mui/material"; import { useNavigate, Link } from "react-router-dom"; @@ -42,19 +43,17 @@ import { import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx"; import { handlePayasyougo } from "../views/HandlePaymentNew.jsx" +import Billing from "./Billing.jsx"; const LicencePopup = (props) => { - const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile } = props; + const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile, selectedOrganization, isCloud } = props; //const alert = useAlert(); let navigate = useNavigate(); - const isCloud = typeof window === 'undefined' || window === undefined || window.location === undefined ? true : window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; - const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false); const [dealList, setDealList] = React.useState([]); const [dealName, setDealName] = React.useState(""); const [dealAddress, setDealAddress] = React.useState(""); const [dealType, setDealType] = React.useState("MSSP"); - const [selectedOrganization, setSelectedOrganization] = React.useState({}); const [dealCountry, setDealCountry] = React.useState("United States"); const [dealCurrency, setDealCurrency] = React.useState("USD"); const [dealStatus, setDealStatus] = React.useState("initiated"); @@ -63,8 +62,8 @@ const LicencePopup = (props) => { const [dealerror, setDealerror] = React.useState(""); const [variant, setVariant] = useState(0) const [shuffleVariant, setShuffleVariant] = useState(isCloud ? 0 : 1) - - + const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email); + const [openChangeEmailBox, setOpenChangeEmailBox] = useState(false); // const parsedFields = maxFields === undefined ? 300 : maxFields const initialShuffleVariant = isCloud ? 0 : 1; const [paymentType, setPaymentType] = useState(0) @@ -78,6 +77,12 @@ const LicencePopup = (props) => { const [calculatedCost, setCalculatedCost] = useState("$600") const [selectedValue, setSelectedValue] = useState(100) + useEffect(() => { + if(selectedOrganization?.Billing?.Email !== BillingEmail) { + setBillingEmail(selectedOrganization?.Billing?.Email); + } + }, [selectedOrganization]) + // Onprem const [calculatedCores, setCalculatedCores] = useState('600') const [onpremSelectedValue, setOnpremSelectedValue] = useState(8) @@ -88,7 +93,7 @@ const LicencePopup = (props) => { const paperStyle = { padding: 20, borderRadius: theme.palette?.borderRadius, - height: "100%", + height: "100%" } billingInfo.subscription = { @@ -101,7 +106,8 @@ const LicencePopup = (props) => { "description": "Pay as you go", "features": [ "Basic Support", - "Limited App Runs (10.000)", + "Includes 10.000 app run/month for free. ", + "Pay for what you use with no minimum commitment and cancel anytime." ], "limit": 10000, } @@ -137,13 +143,14 @@ const LicencePopup = (props) => { }) } + const SubscriptionObject = (props) => { const { globalUrl, index, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props; const [signatureOpen, setSignatureOpen] = React.useState(false); const [tosChecked, setTosChecked] = React.useState(subscription.eula_signed) const [hovered, setHovered] = React.useState(false) - + const [newBillingEmail, setNewBillingEmail] = useState(''); var top_text = "Base Cloud Access" if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) { subscription.name = "Enterprise" @@ -191,6 +198,58 @@ const LicencePopup = (props) => { // newPaperstyle.border = "1px solid #f85a3e" } + const handleClickOpen = () => { + setOpenChangeEmailBox(true); + }; + + const HandleChangeBillingEmail = (orgId) => { + const email = newBillingEmail; + const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; + if (!emailPattern.test(email)) { + toast("Please enter a valid email address"); + return; + } else { + setNewBillingEmail(email); + } + + toast("Updating billing email. Please Wait") + + const data = { + org_id: orgId, + email: newBillingEmail, + billing: { + email: newBillingEmail, + }, + }; + + const url = `${globalUrl}/api/v1/orgs/${orgId}/billing`; + fetch(url, { + method: "POST", + body: JSON.stringify(data), + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.status !== 200) { + console.log("Bad status code in get org:", response.status); + } + return response.json(); + }).then((responseJson) => { + if (responseJson.success === true) { + toast.success("Successfully updated billing email"); + setBillingEmail(newBillingEmail); + setOpenChangeEmailBox(false); + } else { + toast.error("Failed to update billing email. Please try again."); + } + }) + .catch((error) => { + console.log("Error getting org:", error); + }); + } + return ( { color="primary" /> : null} - + {top_text} @@ -343,7 +402,7 @@ const LicencePopup = (props) => {
- + {subscription.name} @@ -443,21 +502,142 @@ const LicencePopup = (props) => { }) : null} - {isCloud ? - Billing email: {selectedOrganization.org} + + {subscription.name.includes("Scale") ? + "" + : + + userdata.has_card_available === true ? + "While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month." + : + isCloud ? + `You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.` + : + `You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.` + } + + {isCloud && (userdata.has_card_available === true || selectedOrganization?.Billing?.Email?.length > 0 )? +
+ Billing email: {BillingEmail} + + {setOpenChangeEmailBox(false)}} + PaperProps={{ + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + minWidth: '440px', + fontFamily: theme?.typography?.fontFamily, + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + zIndex: 1000, + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogActions-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + } + }} + > + Change Billing Email + + + Enter the new billing email address. + + { if (event.key === 'Enter') HandleChangeBillingEmail(selectedOrganization.id) }} + onChange={(e) => setNewBillingEmail(e.target.value)} + /> + + + + + + +
: null}
+ {isCloud ? ( + ) : null} + - - + {/*
@@ -547,14 +726,13 @@ const LicencePopup = (props) => { } const handleChange = (event, newValue) => { - console.log("Event, value: ", event.target, newValue) if (shuffleVariant === 1) { setSelectedValue(newValue) if (newValue === 32) { setCalculatedCost(`Get A Quote`) } else { - setCalculatedCost(`$${newValue * 75}`) + setCalculatedCost(`$${newValue * 120}`) } } else { setSelectedValue(newValue) @@ -736,7 +914,7 @@ const LicencePopup = (props) => { return (
- + {isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? {
- - - - - + +
+
+ + + +
) } diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index abdbcafd..e193460c 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -141,6 +141,7 @@ const Header = (props) => { const [subAnchorEl, setSubAnchorEl] = React.useState(null); const [upgradeHovered, setUpgradeHovered] = React.useState(false); const [showTopbar, setShowTopbar] = useState(false) // Set to true to show top bar + const [selectedOrganization, setSelectedOrganization] = useState({}); const stripeKey = typeof window === 'undefined' || window.location === undefined ? "" : window.location.origin === "https://shuffler.io" ? "pk_live_51PXYYMEJjT17t98N20qEqItyt1fLQjrnn41lPeG2PjnSlZHTDNKHuisAbW00s4KAn86nGuqB9uSVU4ds8MutbnMU00DPXpZ8ZD" : "pk_test_51PXYYMEJjT17t98NbDkojZ3DRvsFUQBs35LGMx3i436BXwEBVFKB9nCvHt0Q3M4MG3dz4mHheuWvfoYvpaL3GmsG00k1Rb2ksO" let navigate = useNavigate(); const classes = useStyles(); @@ -222,10 +223,45 @@ const Header = (props) => { window.location.host === "shuffler.io" || window.location.host === "localhost:5002"; + const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string") ? window.location.pathname : ""; + + const handleGetOrg = (orgId) => { + fetch(`${globalUrl}/api/v1/orgs/${orgId}`, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.status === 401) { + } + + return response.json(); + }) + .then((responseJson) => { + if (responseJson["success"] === false) { + console.log("Error getting org: ", responseJson); + } else { + setSelectedOrganization(responseJson); + } + }) + .catch((error) => { + console.log("Error getting org: ", error); + }); + }; + + + useEffect(() => { + if (isLoggedIn && userdata?.active_org?.id?.length > 0) { + handleGetOrg(userdata.active_org.id); + } + }, [isLoggedIn]); + // DEBUG HERE const handleClickLogout = () => { console.log("SHOULD LOG OUT"); @@ -441,7 +477,7 @@ const Header = (props) => { }) - + { handleClose(); @@ -537,9 +573,9 @@ const Header = (props) => { isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} - + selectedOrganization={selectedOrganization} billingInfo={billingInfo} - + isCloud={isCloud} userdata={userdata} stripeKey={stripeKey} setModalOpen={setModalOpen} @@ -590,7 +626,7 @@ const Header = (props) => { - + @@ -265,7 +265,7 @@ const Priorities = memo((props) => { {clickedFromOrgTab? null : }

Suggestions

- + Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company.
These range from simple configurations in Shuffle to Usecases you may have missed. 
{ {data.read === false ? : @@ -431,64 +431,119 @@ const NotificationItem = memo((props) => { : null } - + {data.description}
- - - {data.read === false ? ( - - ) : null} - - - - + + - - First seen: {new Date(data.created_at * 1000).toISOString().slice(0, 19)} - + {data.read === false ? ( + + ) : null} - - Last seen: {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} - + + + + + + + First seen:{" "} + {new Date(data.created_at * 1000).toISOString().slice(0, 19)} + + + + Last seen:{" "} + {new Date(data.updated_at * 1000).toISOString().slice(0, 19)} + + + + Times seen: {data.amount} + +
- - Times seen: {data.amount} - -
); }) diff --git a/frontend/src/components/Priority.jsx b/frontend/src/components/Priority.jsx index 4d37142f..4bd3a6f2 100644 --- a/frontend/src/components/Priority.jsx +++ b/frontend/src/components/Priority.jsx @@ -154,7 +154,7 @@ const Priority = (props) => { }
- @@ -2062,6 +2118,7 @@ const Apps2 = (props) => { handleAppClick={handleAppClick} leftSideBarOpenByClick={leftSideBarOpenByClick} userdata={userdata} + fetchApps={fetchApps} /> ))}
@@ -2106,6 +2163,7 @@ const Apps2 = (props) => { {appsToShow.map((data, index) => ( ))} diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index aa5bd735..7c124bf0 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -1,5 +1,5 @@ // React & Core imports -import React, { useEffect, useContext, memo, useState } from "react"; +import React, { useEffect, useContext, memo, useState, useRef } from "react"; import { useLocation, useNavigate, Link } from "react-router-dom"; import ReactDOM from "react-dom" @@ -104,7 +104,10 @@ import { MuiChipsInput } from "mui-chips-input"; import { v4 as uuidv4 } from "uuid"; import theme from "../theme.jsx"; import algoliasearch from 'algoliasearch/lite'; -import { InstantSearch, Configure, connectHits } from 'react-instantsearch-dom'; +import { InstantSearch, Configure, connectHits, connectSearchBox, connectRefinementList } from 'react-instantsearch-dom'; +import { debounce } from "lodash"; +import { removeQuery } from "../components/ScrollToTop.jsx"; + const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240"); @@ -668,6 +671,8 @@ const Workflows2 = (props) => { const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove const [selectedUsecases, setSelectedUsecases] = React.useState([]); const [filteredWorkflows, setFilteredWorkflows] = React.useState([]); + const [orgWorkflows, setOrgWorkflows] = React.useState([]); + const [myWorkflows, setMyWorkflows] = React.useState([]); const [selectedWorkflow, setSelectedWorkflow] = React.useState({}); const [workflowDone, setWorkflowDone] = React.useState(false); const [selectedWorkflowId, setSelectedWorkflowId] = React.useState(""); @@ -716,18 +721,47 @@ const Workflows2 = (props) => { const [apps, setApps] = React.useState([]); document.title = "Shuffle - Workflows"; + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const tabParam = queryParams.get('tab'); + if (tabParam !== null && tabParam !== undefined) { + if (tabParam === 'org_workflows' && currTab !== 0) { + setCurrTab(0); + } else if (tabParam === 'my_workflows' && currTab !== 1) { + setCurrTab(1); + } else if (tabParam === 'all_workflows' && currTab !== 2) { + setCurrTab(2); + } + } + }, [location.search]); + const handleTabChange = (event, newValue) => { - // Set loading when switching to public workflows tab - if (newValue === 2) { + setCurrTab(newValue); + // Update URL query params based on tab index + const tabMapping = { + 0: 'org_workflows', + 1: 'my_workflows', + 2: 'all_workflows' + }; + const queryParams = new URLSearchParams(location.search); + queryParams.set('tab', tabMapping[newValue]); + + + navigate(`${location.pathname}?${queryParams.toString()}`); + }; + + useEffect(() => { + if (currTab === 2) { setIsLoadingPublicWorkflow(true); // Simulate loading time for the Algolia search results setTimeout(() => { setIsLoadingPublicWorkflow(false); - }, 2000); + }, 2500); } + }, [currTab]) + - setCurrTab(newValue); - }; const handleCreateWorkflow = () => { setModalOpen(true) @@ -1203,6 +1237,7 @@ const Workflows2 = (props) => { }, [isDropzone]); + const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", @@ -1240,6 +1275,7 @@ const Workflows2 = (props) => { const getAvailableWorkflows = (amount) => { var storageWorkflows = [] + setIsLoadingWorkflow(true) try { const storagewf = localStorage.getItem("workflows") storageWorkflows = JSON.parse(storagewf) @@ -1283,8 +1319,15 @@ const Workflows2 = (props) => { return response.json(); }) .then((responseJson) => { - console.log("Response : /api/v1/workflows", responseJson) - if (responseJson !== undefined) { + if (responseJson !== undefined && responseJson !== null) { + if (responseJson.success === false) { + } else if (responseJson.length === 0) { + if (currTab !== 2) { + toast("No workflows found. Showing workflow discovery") + setCurrTab(2) + } + } + var newarray = [] for (var wfkey in responseJson) { const wf = responseJson[wfkey] @@ -1527,13 +1570,13 @@ const Workflows2 = (props) => { overflow: "hidden", width: "100%", color: "white", - padding: "12px 12px 0px 15px", display: "flex", fontFamily: theme?.typography?.fontFamily, boxSizing: "border-box", position: "relative", borderRadius: "8px", // backgroundColor: "#212121", + padding: "15px 15px 0px 20px", }; const gridContainer = { @@ -2169,7 +2212,6 @@ const Workflows2 = (props) => { ); }; - // Replace the loading sections in the main component with this const LoadingWorkflowGrid = () => { return (
{ if ( parsedName !== undefined && parsedName !== null && - parsedName.length > 20 + parsedName.length > 35 ) { - parsedName = parsedName.slice(0, 21) + ".."; + parsedName = parsedName.slice(0, 34) + ".."; } const actions = data.actions !== null ? data.actions.length : 0; @@ -2442,7 +2484,7 @@ const Workflows2 = (props) => { } return ( -
+
{selectedCategory !== "" ? @@ -3906,59 +3948,25 @@ const Workflows2 = (props) => { // } useEffect(() => { - if (userdata !== undefined && userdata !== null && currTab !== 2) { - setIsLoadingWorkflow(true); - var filteredWorkflows = [] + if (currTab === 2) return; + if (userdata !== undefined && userdata !== null && filteredWorkflows.length > 0) { + var categoryWorkflows = [] if (currTab === 0) { - filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) + categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id) + setOrgWorkflows(categoryWorkflows) } else if (currTab === 1) { - filteredWorkflows = workflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id) + categoryWorkflows = filteredWorkflows.filter(workflow => workflow?.org_id === userdata?.active_org?.id && workflow?.owner === userdata?.id) + setMyWorkflows(categoryWorkflows) } - setFilteredWorkflows(filteredWorkflows) - setTimeout(() => { - setIsLoadingWorkflow(false); - }, 500); + setIsLoadingWorkflow(false); } - }, [currTab, workflows, userdata]) + }, [currTab, workflows, userdata, filteredWorkflows]) - useEffect(() => { - console.log("SearchQuery: ", searchQuery) - }, [searchQuery]) - const Hits = ({ hits }) => { - var counted = 0 - console.log("Public workflows", hits) - return ( - isLoadingPublicWorkflow ? - ( - - ) : ( - -
- {hits.map((data, index) => { - return - })} -
- ) - - ) - } - - const CustomHits = connectHits(Hits) const handleCategoryChange = (e) => { @@ -3972,13 +3980,255 @@ const Workflows2 = (props) => { borderRadius: '4px', padding: "12px 16px", cursor: 'pointer', - minWidth: '40px', + minWidth: '40px', height: 'auto', display: 'flex', alignItems: 'center', justifyContent: 'center', } + + const iconButtonDisabledStyle = { + ...iconButtonStyle, + cursor: 'not-allowed', + opacity: 0.9, // Reduce opacity + } + + const Hits = ({ hits, isSearchStalled, searchQuery }) => { + + return ( + isLoadingPublicWorkflow ? ( + + ) : ( +
+ {hits.map((data, index) => { + return + })} +
+ ) + ) + }; + + const SearchBox = ({ refine, searchQuery, setSearchQuery }) => { + + const inputRef = useRef(null); + const [localQuery, setLocalQuery] = useState(searchQuery); + const location = useLocation(); + + + useEffect(() => { + if (searchQuery) { + setLocalQuery(searchQuery); + refine(searchQuery); + } + }, [searchQuery, refine]); + + // Debounced function to refine search + const debouncedRefine = useRef( + debounce((value) => { + setSearchQuery(value); + removeQuery("q"); + refine(value); + }, 300) + ).current; + + useEffect(() => { + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundQuery = params["q"]; + if (foundQuery) { + setLocalQuery(foundQuery); + debouncedRefine(foundQuery); + } + }, [debouncedRefine]); + + useEffect(() => { + if (searchQuery === "") { + return; + } + if (inputRef.current) { + inputRef.current.focus(); + } + }, [searchQuery]); + + const handleChange = (event) => { + const value = event.target.value; + setLocalQuery(value); + debouncedRefine(value); + }; + + return ( + { + if (event.key === "Enter") { + event.preventDefault(); + } + }} + limit={5} + variant="outlined" + placeholder="Search Workflows" + /> + ) + } + + const CustomSearchBox = connectSearchBox(SearchBox) + const CustomHits = connectHits(Hits) + + const CategoryDropdown = ({ refine, currentRefinement, items }) => { + + const handleChange = (event) => { + // Get the selected values array from the event + const selectedValues = event.target.value; + refine(selectedValues); + }; + + return ( + + ); + }; + const CustomCategoryDropdown = connectRefinementList(CategoryDropdown) + + const tabStyle = { + textTransform: 'none', + marginRight: 20, + fontFamily: theme?.typography?.fontFamily, + fontSize: 16, + borderBottom: "5px solid transparent", + minHeight: "48px", + padding: "12px 16px", + } + + const tabActive = { + borderBottom: "5px solid #FF8544", + borderRadius: "2px", + color: "#FF8544" + } + const WorkflowView = memo(() => { if (workflows.length === 0) { @@ -3990,363 +4240,443 @@ const Workflows2 = (props) => { const foundPriority = userdata === undefined || userdata === null || userdata.priorities === undefined || userdata.priorities === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true) return ( <> -
- - Workflows - + +
+ + {currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Workflows + -
- - - - - -
- - -
- - - - // - // - // ), - onKeyDown: (e) => { - // Prevent default behavior for Enter and Backspace - if (e.key === 'Enter' || e.key === 'Backspace') { - e.preventDefault(); - e.stopPropagation(); - e.target.focus(); - } - }, - }} - clearInputOnBlur={false} - sx={{ - // Container styling - '& .MuiOutlinedInput-root': { - height: "fit-content", - borderRadius: '4px', - backgroundColor: '#212121', - '& fieldset': { - borderColor: 'rgba(255, 255, 255, 0.23)', - }, - '&:hover fieldset': { - borderColor: 'rgba(255, 255, 255, 0.4)', - }, - }, - - // Adjust chip container to center vertically - '& .MuiInputBase-root': { - display: 'flex', - flexWrap: 'wrap', - gap: '4px', - padding: '4px 8px', - alignItems: 'center', - height: "fit-content", // Match height - }, - - // Rest of the styling remains the same... - }} - value={filters} - onChange={(chips) => { - setFilters(chips); - findWorkflow(chips); - }} - //onAdd={(chip) => { - // console.log("ADd: ", chip); - // addFilter(chip); - //}} - //onDelete={(_, index) => { - // console.log("Remove: ", index); - // removeFilter(index); - //}} - /> - - - - -
-
- - navigate("/workflows/debug")} - disabled={currTab === 2} - > - - - - - - { - const newView = view === "grid" ? "list" : "grid"; - localStorage.setItem("workflowView", newView); - setView(newView); - }} - disabled={currTab === 2} - > - {view === "grid" ? : } - - - - - upload.click()} - disabled={currTab === 2} - > - {submitLoading ? : } - - - - (upload = ref)} - onChange={importFiles} - /> - - - exportAllWorkflows(workflows)} - > - - - -
- + + + +
-
-
- { - isLoadingWorkflow ? ( - +
+ + {currTab === 2 ? ( + ) : ( - view === "grid" && currTab !== 2 ? ( - <> -
+ + // + // + // ), + onKeyDown: (e) => { + // Prevent default behavior for Enter and Backspace + if (e.key === 'Enter' || e.key === 'Backspace') { + e.preventDefault(); + e.stopPropagation(); + e.target.focus(); + } + }, + }} + clearInputOnBlur={false} + sx={{ + // Container styling + '& .MuiOutlinedInput-root': { + height: "fit-content", + borderRadius: '4px', + backgroundColor: '#212121', + '& fieldset': { + borderColor: 'rgba(255, 255, 255, 0.23)', + }, + '&:hover fieldset': { + borderColor: 'rgba(255, 255, 255, 0.4)', + }, + }, + // Adjust chip container to center vertically + '& .MuiInputBase-root': { + display: 'flex', + flexWrap: 'wrap', + gap: '4px', + padding: '4px 8px', + alignItems: 'center', + height: "fit-content", // Match height + backgroundColor: "#212121", + }, + // Rest of the styling remains the same... + }} + value={filters} + onChange={(chips) => { + setFilters(chips); + const remainingCategories = chips.map(chip => { + const match = chip.match(/\d+\.\s+(\w+)/i); + return match ? match[1] : chip; + }).filter(category => { + return usecases.some(usecase => + usecase.name.toLowerCase().includes(category.toLowerCase()) + ); + }); - {filteredWorkflows.map((data, index) => { - // Shouldn't be a part of this list - if (data.public === true) { - return null - } + setSelectedCategory(remainingCategories); + findWorkflow(chips); - if (firstLoad) { - workflowDelay += 75 - } else { - return - } + }} + //onAdd={(chip) => { + // console.log("ADd: ", chip); + // addFilter(chip); + //}} + //onDelete={(_, index) => { + // console.log("Remove: ", index); + // removeFilter(index); + //}} + /> + )} - return ( - - {/**/} - - {/**/} - - ) - })} -
- - ) : ( - currTab !== 2 && + { + currTab !== 2 && ( + ) - ) - } + } + { + currTab === 2 && ( + + ) + } - { - currTab === 2 && - ( - - - - - ) - } +
+
+ + navigate("/workflows/debug")} + disabled={currTab === 2} + > + + + + + { + const newView = view === "grid" ? "list" : "grid"; + localStorage.setItem("workflowView", newView); + setView(newView); + }} + disabled={currTab === 2} + > + {view === "grid" ? + : + + } + + + + + upload.click()} + disabled={currTab === 2} + > + {submitLoading ? + : + + } + + + + (upload = ref)} + onChange={importFiles} + /> + + + exportAllWorkflows(workflows)} + > + + + +
+ +
+ + +
+
+ { + (isLoadingWorkflow && currTab !== 2) ? ( + + ) : ( + view === "grid" && currTab !== 2 ? ( + <> +
+ + + + {currTab === 0 && orgWorkflows.map((data, index) => { + // Shouldn't be a part of this list + if (data.public === true) { + return null + } + + // if (firstLoad) { + // workflowDelay += 75 + // } else { + // return + // } + + return ( + + {/**/} + + {/**/} + + ) + })} + + { + currTab === 1 && myWorkflows.map((data, index) => { + if (data.public === true) { + return null + } + + // if (firstLoad) { + // workflowDelay += 75 + // } else { + // return + // } + + return ( + + {/**/} + + {/**/} + + ) + }) + } +
+ + ) : ( + currTab !== 2 && + ) + ) + } + + { + currTab === 2 && + ( + + ) + } + +
-
- + +
); @@ -4756,7 +5086,7 @@ const Workflows2 = (props) => { ); // Maybe use gridview or something, idk - return
{loadedCheck}
; + return
{loadedCheck}
; }; From 2fd7f65831ffb16a8e3235cd85ebc399953e896a Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 20 Dec 2024 23:25:33 +0200 Subject: [PATCH 19/25] Fixed regions --- frontend/src/components/LeftSideBar.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index def8d23c..3ceea0e8 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -662,7 +662,8 @@ useEffect(() => { }; const getRegionTag = (region_url) => { - let regiontag = "UK"; + //let regiontag = "UK"; + let regiontag = "EU"; if ( region_url !== undefined && region_url !== null && @@ -676,7 +677,7 @@ useEffect(() => { if (regiontag === "california") { regiontag = "US"; } else if (regiontag === "frankfurt") { - regiontag = "EU"; + regiontag = "EU-2"; } else if (regiontag === "ca"){ regiontag = "CA"; } From aab73b5ecc4a9d8afce2bf4c195dcd12a68d2e3f Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 9 Jan 2025 22:31:53 +0100 Subject: [PATCH 20/25] Fixed Tenzir force start from frontend --- frontend/src/components/AppModal.jsx | 218 +- frontend/src/components/AppSelection.jsx | 18 +- frontend/src/components/CacheView.jsx | 1 + frontend/src/components/Files.jsx | 456 +++- frontend/src/components/LeftSideBar.jsx | 55 +- .../src/components/ShuffleCodeEditor1.jsx | 1898 ++++++++--------- frontend/src/context/ContextApi.jsx | 9 +- frontend/src/views/Admin2.jsx | 33 +- frontend/src/views/AngularWorkflow.jsx | 28 +- frontend/src/views/ApiExplorerWrapper.jsx | 21 +- frontend/src/views/AppCreator.jsx | 8 +- frontend/src/views/Apps2.jsx | 950 +++++---- frontend/src/views/Workflows2.jsx | 28 +- functions/onprem/orborus/orborus.go | 5 + 14 files changed, 2164 insertions(+), 1564 deletions(-) diff --git a/frontend/src/components/AppModal.jsx b/frontend/src/components/AppModal.jsx index fce90fae..41ca61a8 100644 --- a/frontend/src/components/AppModal.jsx +++ b/frontend/src/components/AppModal.jsx @@ -11,6 +11,8 @@ import { Button, Stack, Avatar, + Skeleton, + Tooltip, } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; @@ -38,6 +40,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { const [inputUsecase, setInputUsecase] = useState({}) const [latestUsecase, setLatestUsecase] = useState([]) const [foundAppUsecase, setFoundAppUsecase] = useState({}) + const [usecaseLoading, setUsecaseLoading] = useState(false) const navigate = useNavigate(); const parseUsecase = (subcase) => { const srcdata = findSpecificApp(frameworkData, subcase.type) @@ -142,6 +145,19 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { }) setLatestUsecase(newUsecases) + if (newUsecases?.length > 0) { + const foundCategory = newUsecases?.find((category) => + category?.list?.some((subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name) + ); + + const foundSubcase = foundCategory?.list?.find( + (subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name + ); + + setFoundAppUsecase(foundSubcase); + } + setUsecaseLoading(false) + // Matching workflows with usecases if (responseJson.success !== false) { if (workflows !== undefined && workflows !== null && workflows.length > 0) { @@ -275,23 +291,12 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { useEffect(() => { + setUsecaseLoading(true) getAvailableWorkflows() getFramework() }, [app]) - useEffect(() => { - const foundCategory = latestUsecase?.find((category) => - category?.list?.some((subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name) - ); - - const foundSubcase = foundCategory?.list?.find( - (subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name - ); - - setFoundAppUsecase(foundSubcase); - }, [latestUsecase]) - const downloadApp = (inputdata) => { const id = inputdata.id; @@ -389,9 +394,8 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { newAppname = newAppname?.replaceAll("_", " "); } - var canEditApp = userdata.admin === "true" || userdata?.id === app?.owner || app?.owner === "" || (userdata.admin === "true" && userdata.active_org.id === app?.reference_org) || !app?.generated - + var canEditApp = userdata !== undefined && (userdata?.admin === "true" || userdata?.id === app?.owner || app?.owner === "" || (userdata?.admin === "true" && userdata?.active_org?.id === app?.reference_org)) || !app?.generated return ( { app?.private_id !== undefined && app?.private_id?.length > 0 && app?.generated ? ( - ) : null} + + + ) : null}
@@ -742,10 +773,13 @@ const AppModal = ({ open, onClose, app, globalUrl }) => { minWidth: '200px' }} onClick={() => { - navigate("/usecases2") + navigate("/usecases") }} + disabled={usecaseLoading} > - Find a Usecase + { + (foundAppUsecase !== undefined && foundAppUsecase !== null && usecaseLoading === false) ? "See usecase" : "Find a Usecase" + }
diff --git a/frontend/src/components/AppSelection.jsx b/frontend/src/components/AppSelection.jsx index 73528ec2..6d83f2b0 100644 --- a/frontend/src/components/AppSelection.jsx +++ b/frontend/src/components/AppSelection.jsx @@ -536,15 +536,16 @@ const AppSelection = props => { })}
- { - !isAppPage && ( - <> {!moreButton ? (
{ setMoreButton(true) setTimeout(() => { - navigate("/welcome?tab=2") + if (isAppPage) { + navigate("/apps?tab=all_apps") + } else { + navigate("/welcome?tab=2") + } }, 250) }} >See More Apps @@ -552,15 +553,14 @@ const AppSelection = props => {
- - ) - }
) diff --git a/frontend/src/components/CacheView.jsx b/frontend/src/components/CacheView.jsx index 7929a7ab..e7835220 100644 --- a/frontend/src/components/CacheView.jsx +++ b/frontend/src/components/CacheView.jsx @@ -537,6 +537,7 @@ const CacheView = memo((props) => { padding: "15px 5px", maxHeight: 300, verticalAlign: "middle", + maxWidth: 300, }} primary={validate.valid ? { const [downloadBranch, setDownloadBranch] = React.useState("main"); const [downloadFolder, setDownloadFolder] = React.useState("translation_standards"); const [contentLoading, setContentLoading] = React.useState(false) + const [selectAllChecked, setSelectAllChecked] = React.useState(false) + const [selectedFiles, setSelectedFiles] = useState([]); + const [selectedFileId, setSelectedFileId] = useState([]) + const [showFileCategoryPopup, setShowFileCategoryPopup] = useState(false) + const [updateToThisCategory, setUpdateToThisCategory] = useState("") + const [showDistributionPopup, setShowDistributionPopup] = useState(false) + const [selectedSubOrg, setSelectedSubOrg] = useState([]) + const [fileIdSelectedForDistribution, setFileIdSelectedForDistribution] = useState("") //const alert = useAlert(); const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"] var upload = ""; @@ -80,6 +92,56 @@ const Files = memo((props) => { } + const changeDistribution = (id, selectedSubOrg) => { + + editFileConfig(id, "suborg_distribute", [...new Set(selectedSubOrg)]) + } + + const editFileConfig = (id, parentAction, selectedSubOrg) => { + const data = { + id: id, + action: parentAction !== undefined && parentAction !== null ? parentAction : "change_category", + selected_suborgs: selectedSubOrg, + } + + const url = globalUrl + "/api/v1/files/" + id + "/config"; + + 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 overwriting files"); + } else { + toast("Successfully updated file!"); + setTimeout(() => { + getFiles(); + }, 1000); + } + }) + ) + .catch((error) => { + toast("Err: " + error.toString()); + }); + }; + + const handleFileCheckboxChange = (index) => { + setSelectedFiles((prevSelected) => { + const updatedSelected = [...prevSelected]; + updatedSelected[index] = !updatedSelected[index]; + return updatedSelected; + }); + }; + const runUpdateText = (text) =>{ fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, { method: "PUT", @@ -137,6 +199,7 @@ const Files = memo((props) => { if (responseJson.files !== undefined && responseJson.files !== null) { setFiles(responseJson.files); setShowLoader(false) + setShowDistributionPopup(false) } else if (responseJson.list !== undefined && responseJson.list !== null) { // Set the "namespace" field in all items if (namespace !== undefined && namespace !== null) { @@ -152,6 +215,7 @@ const Files = memo((props) => { } else { setFiles([]); setShowLoader(false) + setShowDistributionPopup(false) } if (namespace === undefined || namespace === null || namespace === "default") { @@ -256,7 +320,7 @@ const Files = memo((props) => { zIndex: 1000, minWidth: "800px", minHeight: "320px", - overflow: "hidden", + overflow: "auto", '& .MuiDialogContent-root': { backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, }, @@ -400,6 +464,127 @@ const Files = memo((props) => { : null + const handleSelectSubOrg = (id, action) => { + if (action === "all") { + const childOrgs = userdata.orgs.filter( + (data) => data.creator_org === userdata.active_org.id + ); + setSelectedSubOrg((prev) => { + if (prev.length === childOrgs.length) { + // If all child orgs are already selected, clear the selection + return []; + } else { + // Otherwise, select all child org IDs + return childOrgs.map((data) => data.id); + } + }); + } else if (action === "none") { + setSelectedSubOrg([]); + } else { + setSelectedSubOrg((prev) => { + if (prev.includes(id)) { + return prev.filter((data) => data !== id); + } else { + return [...prev, id]; + } + }); + } + }; + + const fileDistributionModal = showDistributionPopup ? ( + setShowDistributionPopup(false)} + PaperProps={{ + sx: { + borderRadius: theme?.palette?.DialogStyle?.borderRadius, + border: theme?.palette?.DialogStyle?.border, + fontFamily: theme?.typography?.fontFamily, + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + zIndex: 1000, + minWidth: "600px", + minHeight: "320px", + overflow: "auto", + '& .MuiDialogContent-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogTitle-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + '& .MuiDialogActions-root': { + backgroundColor: theme?.palette?.DialogStyle?.backgroundColor, + }, + }, + }} + > + +
+ Select sub-org to distribute files +
+
+ + {handleSelectSubOrg(null, "none")}}>None + {handleSelectSubOrg(null, "all")}}>All + {userdata.orgs.map((data, index) => { + if (data.creator_org !== userdata.active_org.id) { + return null; + } + + const imagesize = 22; + const imageStyle = { + width: imagesize, + height: imagesize, + pointerEvents: "none", + marginRight: 10, + marginLeft: data.id === userdata.active_org.id ? 0 : 20, + }; + + const image = data.image === "" ? ( + {data.name} + ) : ( + {data.name} + ); + + return ( + handleSelectSubOrg(data.id)} + style={{ display: "flex", alignItems: "center" }} + > + + {image} + {data.name} + + ); + })} + +
+ + +
+
+
+ + ): null + const deleteFile = (file) => { fetch(globalUrl + "/api/v1/files/" + file.id, { method: "DELETE", @@ -636,7 +821,7 @@ const Files = memo((props) => { setTimeout(() => { getFiles() - }, 2500); + }, 3000); }; const uploadFile = (e) => { @@ -649,6 +834,68 @@ const Files = memo((props) => { uploadFiles(files); }; + + const handleUpdateFileCategory = (namespace) => { + + if (selectedFiles.length === 0 && !selectAllChecked) { + toast("Please select files to update category") + return + } + + if (namespace === undefined || namespace === null || namespace === "") { + toast("Please select a category to update files to") + return + } + + const url = globalUrl + `/api/v1/files/namespaces/${namespace}/share` + + const data = { + SelectedFiles: selectedFileId, + } + + setShowLoader(true) + + 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 overwriting files"); + } else { + setSelectAllChecked(false) + setSelectedFiles([]) + setSelectedFileId([]) + setShowFileCategoryPopup(false) + setSelectedCategory(namespace) + setTimeout(() => { + getFiles(); + toast("Successfully updated file!"); + if (window.location.search.includes("category=")) { + const newurl = window.location.href.replace(/category=[^&]+/, `category=${namespace}`) + window.history.pushState({ path: newurl }, "", newurl) + } else { + window.history.pushState({ path: window.location.href }, "", `${window.location.href}&category=${namespace}`) + } + + }, 1000); + } + } + )) + .catch((error) => { + toast("Err: " + error.toString()); + }); + + } + return ( { }} onDrop={uploadFile} > + {fileDistributionModal}
@@ -739,13 +987,18 @@ const Files = memo((props) => { }} value={selectedCategory} onChange={(event) => { + if (selectAllChecked || selectedFiles.length > 0) { + setUpdateToThisCategory(event.target.value) + setShowFileCategoryPopup(true) + return + } setSelectedCategory(event.target.value) - if (event.target.value === "all" || event.target.value === "default") { getFiles() } else { getFiles(event.target.value) } + // Add it to the url as a query if (window.location.search.includes("category=")) { @@ -768,14 +1021,41 @@ const Files = memo((props) => { ); })} + setShowFileCategoryPopup(false)}> + File Categories + + Please note that your selected files ({selectedFileId?.length}) will be moved to the {updateToThisCategory} category. + + + + + + ) : null}
{renderTextBox ? -
- { tableLayout: "auto", display: "table", minWidth: 800, - overflowX: "auto" + overflowX: "auto", + paddingBottom: 0, }} > - - {["Name", "Workflow", "Md5", "Status", "Filesize", "Actions"].map((header, index) => ( - + {[ + + { + setSelectAllChecked((prev) => !prev); + setSelectedFiles((prev) => { + if (prev.length === files.length) { + return [] + } else { + return files.map((_, index) => !prev.includes(index)) + } + }) + if (selectAllChecked) { + setSelectedFileId([]) + } else { + setSelectedFileId( + files + .filter((file) => file.namespace === selectedCategory) + .map((file) => file.id) + ); + } + }} + /> + , + "Name", + "Workflow", + "Md5", + "Status", + "Filesize", + "Actions", + "Distribution" + ] + .filter(Boolean) + .map((header, index) => ( + - ))} - + }} + /> + ))} + {showLoader ? [...Array(6)].map((_, rowIndex) => ( { backgroundColor: "#212121", }} > - {Array(6) + {Array(8) .fill() .map((_, colIndex) => ( { if (index % 2 === 0) { bgColor = isSelectedFiles ? "#1A1A1A":"#1f2023"; } - + const isDistributed = file?.suborg_distribution?.length > 0 ? true : false; const filenamesplit = file.filename.split(".") const iseditable = file.filesize < 2000000 && file.status === "active" && allowedFileTypes.includes(filenamesplit[filenamesplit.length-1]) return ( @@ -953,6 +1286,26 @@ const Files = memo((props) => { primary={new Date(file.updated_at * 1000).toISOString()} /> */} + + {handleFileCheckboxChange(index); setSelectedFileId(prev => { + if (prev.includes(file.id)) { + return prev.filter((item) => item !== file.id) + } else { + return [...prev, file.id] + } + })}} + /> + { > { setOpenEditor(true) @@ -1137,7 +1490,6 @@ const Files = memo((props) => { { - console.log("file is : ", file) navigator.clipboard.writeText(file.id); document.execCommand("copy"); @@ -1154,7 +1506,7 @@ const Files = memo((props) => { > { deleteFile(file); @@ -1166,7 +1518,7 @@ const Files = memo((props) => { viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style={{ - stroke: file.status === "active" ? "#fd4c62" : "#c8c8c8", + stroke: file.status === "active" && file.org_id === selectedOrganization.id ? "#fd4c62" : "#c8c8c8", }} > { // overflow: "hidden", }} /> + + {selectedOrganization.id !== undefined && file?.org_id !== selectedOrganization.id ? + + + + : + + { + setShowDistributionPopup(true) + if(file?.suborg_distribution?.length > 0){ + setSelectedSubOrg(file.suborg_distribution) + }else{ + setSelectedSubOrg([]) + } + setFileIdSelectedForDistribution(file.id) + }} + /> + + } + ); }) diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 3ceea0e8..c6944955 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -58,6 +58,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { const navigate = useNavigate(); const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, setSearchBarModalOpen, searchBarModalOpen} = useContext(Context); + const [expandLeftNav, setExpandLeftNav] = useState(false); const [activeOrgName, setActiveOrgName] = useState( userdata?.active_org?.name || "Select Organziation" @@ -261,17 +262,20 @@ useEffect(() => { },[currentPath]); useEffect(() => { - UpdateTabStatus(); - const expandLeftNav1 = localStorage.getItem("expandLeftNav"); + UpdateTabStatus() + + const expandLeftNav1 = localStorage.getItem("expandLeftNav") if (expandLeftNav1 === "false") { - setLeftSideBarOpenByClick(false); - setLeftSideBarOpenByClick(false); + setLeftSideBarOpenByClick(false) } else { - setLeftSideBarOpenByClick(true); - setLeftSideBarOpenByClick(true); - setExpandLeftNav(true); + const currentLocation = window?.location?.pathname + if (currentLocation?.includes('/workflows/')) { + } else { + setLeftSideBarOpenByClick(true) + setExpandLeftNav(true) + } } - }, []); + }, []) const getAvailableWorkflows = useCallback((amount) => { @@ -533,7 +537,7 @@ useEffect(() => { - Version: 2.0.0-rc + Version: 2.0.0-rc2 @@ -591,6 +595,10 @@ useEffect(() => { org_id: orgId, }; + if (userdata?.active_org?.id === orgId) { + return + } + localStorage.setItem("globalUrl", ""); localStorage.setItem("getting_started_sidebar", "open"); @@ -765,11 +773,18 @@ useEffect(() => { "UK": "gb" }; - region = regionMapping[region_url] || "gb"; + region = regionMapping[region_url] || "eu"; return `https://flagcdn.com/48x36/${region}.png`; }; + useEffect(() => { + if (window?.location?.pathname?.includes("/workflows/")) { + setExpandLeftNav(false); + } + + }, [window?.location?.pathname]); + return (
{ zoom: 0.8, height: "calc((100vh - 32px)*1.2)", }} + onMouseLeave={() => { + if (window?.location?.pathname?.includes("/workflows/")) { + setExpandLeftNav(false); + } + }} + onMouseOver={() => { + if (window?.location?.pathname?.includes("/workflows/")) { + setExpandLeftNav(true); + } + } + + } > {modalView} { }} style={{ ...ButtonStyle, - backgroundColor: - currentOpenTab === "security" - ? "#2f2f2f" - : "transparent", }} onMouseOver={(event)=>{ event.currentTarget.style.backgroundColor = "#2f2f2f"; @@ -1443,6 +1466,8 @@ useEffect(() => { + + {recentworkflows?.length > 0 ? { }) } + : null } + { - const { - globalUrl, - fieldCount, - actionlist, - changeActionParameterCodeMirror, - expansionModalOpen, - setExpansionModalOpen, - codedata, - setcodedata, - isFileEditor, - runUpdateText, - toolsAppId, - parameterName, - selectedAction , + const { + globalUrl, + fieldCount, + actionlist, + changeActionParameterCodeMirror, + expansionModalOpen, + setExpansionModalOpen, + codedata, + setcodedata, + isFileEditor, + runUpdateText, + toolsAppId, + parameterName, + selectedAction, workflowExecutions, getParents, activeDialog, setActiveDialog, fieldname, contentLoading, - editorData, - + editorData, + setAiQueryModalOpen, fullScreenMode } = props @@ -120,12 +120,12 @@ const CodeEditor = (props) => { const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); //const { setContainer } = useCodeMirror({ - // container: editorRef.current, - // extensions, - // value: localcodedata, + // container: editorRef.current, + // extensions, + // value: localcodedata, //}) - // const {codelang, setcodelang} = props - + // const {codelang, setcodelang} = props + const [validation, setValidation] = React.useState(false); const [expOutput, setExpOutput] = React.useState(" "); const [linewrap, setlinewrap] = React.useState(true); @@ -146,15 +146,15 @@ const CodeEditor = (props) => { const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark"); - const [menuPosition, setMenuPosition] = useState(null); - const [showAutocomplete, setShowAutocomplete] = React.useState(false); - const [markers, setMarkers] = useState([]); + const [menuPosition, setMenuPosition] = useState(null); + const [showAutocomplete, setShowAutocomplete] = React.useState(false); + const [markers, setMarkers] = useState([]); const [isAiLoading, setIsAiLoading] = React.useState(false); - // let markers = []; + // let markers = []; const baseResult = "" const [executionResult, setExecutionResult] = useState({ - "valid": false, + "valid": false, "result": baseResult, }) const [executing, setExecuting] = useState(false) @@ -184,9 +184,9 @@ const CodeEditor = (props) => { return } - for(var i=0; i < actionlist.length; i++){ - allVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) - tmpVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) + for (var i = 0; i < actionlist.length; i++) { + allVariables.push('$' + actionlist[i].autocomplete.toLowerCase()) + tmpVariables.push('$' + actionlist[i].autocomplete.toLowerCase()) var parsedPaths = [] if (typeof actionlist[i].example === "object") { @@ -194,7 +194,7 @@ const CodeEditor = (props) => { } for (var key in parsedPaths) { - const fullpath = "$"+actionlist[i].autocomplete.toLowerCase()+parsedPaths[key].autocomplete + const fullpath = "$" + actionlist[i].autocomplete.toLowerCase() + parsedPaths[key].autocomplete if (!allVariables.includes(fullpath)) { allVariables.push(fullpath) allVariables.push(fullpath.toLowerCase()) @@ -211,73 +211,73 @@ const CodeEditor = (props) => { expectedOutput(localcodedata) }, [availableVariables]) - var to_be_copied = ""; + var to_be_copied = ""; const HandleJsonCopy = (base, copy, base_node_name) => { - if (typeof copy.name === "string") { - copy.name = copy.name.replaceAll(" ", "_"); - } + if (typeof copy.name === "string") { + copy.name = copy.name.replaceAll(" ", "_"); + } - //lol - if (typeof base === 'object' || typeof base === 'dict') { - base = JSON.stringify(base) - } + //lol + if (typeof base === 'object' || typeof base === 'dict') { + base = JSON.stringify(base) + } - if (base_node_name === "execution_argument" || base_node_name === "Execution Argument") { - base_node_name = "exec" - } + if (base_node_name === "execution_argument" || base_node_name === "Execution Argument") { + base_node_name = "exec" + } - console.log("COPY: ", base_node_name, copy); + console.log("COPY: ", base_node_name, copy); - //var newitem = JSON.parse(base); - var newitem = validateJson(base).result - to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); - for (let copykey in copy.namespace) { - if (copy.namespace[copykey].includes("Results for")) { - continue; - } + //var newitem = JSON.parse(base); + var newitem = validateJson(base).result + to_be_copied = "$" + base_node_name.toLowerCase().replaceAll(" ", "_"); + for (let copykey in copy.namespace) { + if (copy.namespace[copykey].includes("Results for")) { + continue; + } - if (newitem !== undefined && newitem !== null) { - newitem = newitem[copy.namespace[copykey]]; - if (!isNaN(copy.namespace[copykey])) { - to_be_copied += ".#"; - } else { - to_be_copied += "." + copy.namespace[copykey]; - } - } - } + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.namespace[copykey]]; + if (!isNaN(copy.namespace[copykey])) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.namespace[copykey]; + } + } + } - if (newitem !== undefined && newitem !== null) { - newitem = newitem[copy.name]; - if (!isNaN(copy.name)) { - to_be_copied += ".#"; - } else { - to_be_copied += "." + copy.name; - } - } + if (newitem !== undefined && newitem !== null) { + newitem = newitem[copy.name]; + if (!isNaN(copy.name)) { + to_be_copied += ".#"; + } else { + to_be_copied += "." + copy.name; + } + } - to_be_copied.replaceAll(" ", "_"); - const elementName = "copy_element_shuffle"; - var copyText = document.getElementById(elementName); - if (copyText !== null && copyText !== undefined) { - console.log("NAVIGATOR: ", navigator); - const clipboard = navigator.clipboard; - if (clipboard === undefined) { - toast("Can only copy over HTTPS (port 3443)"); - return; - } + to_be_copied.replaceAll(" ", "_"); + const elementName = "copy_element_shuffle"; + var copyText = document.getElementById(elementName); + if (copyText !== null && copyText !== undefined) { + console.log("NAVIGATOR: ", navigator); + const clipboard = navigator.clipboard; + if (clipboard === undefined) { + toast("Can only copy over HTTPS (port 3443)"); + return; + } - navigator.clipboard.writeText(to_be_copied); - copyText.select(); - copyText.setSelectionRange(0, 99999); /* For mobile devices */ + navigator.clipboard.writeText(to_be_copied); + copyText.select(); + copyText.setSelectionRange(0, 99999); /* For mobile devices */ - /* Copy the text inside the text field */ - document.execCommand("copy"); - console.log("COPYING!"); - toast("Copied JSON path to clipboard.") - } else { - console.log("Couldn't find element ", elementName); - } - } + /* Copy the text inside the text field */ + document.execCommand("copy"); + console.log("COPYING!"); + toast("Copied JSON path to clipboard.") + } else { + console.log("Couldn't find element ", elementName); + } + } const aiSubmit = (value, inputAction) => { if (value === undefined || value === "") { @@ -295,7 +295,7 @@ const CodeEditor = (props) => { console.log("Parents: ", parents) var actionlist = [] if (parents.length > 1) { - for (let [key,keyval] in Object.entries(parents)) { + for (let [key, keyval] in Object.entries(parents)) { const item = parents[key]; if (item.label === "Execution Argument") { continue; @@ -307,7 +307,7 @@ const CodeEditor = (props) => { if (workflowExecutions.length > 0) { // Look for the ID const found = false; - for (let [key,keyval] in Object.entries(workflowExecutions)) { + for (let [key, keyval] in Object.entries(workflowExecutions)) { if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) { continue; } @@ -357,8 +357,8 @@ const CodeEditor = (props) => { var fixedResults = [] for (var i = 0; i < actionlist.length; i++) { const item = actionlist[i]; - const responseFix = SetJsonDotnotation(item.example, "") - + const responseFix = SetJsonDotnotation(item.example, "") + // Check if json const validated = validateJson(responseFix) var exampledata = responseFix; @@ -400,50 +400,50 @@ const CodeEditor = (props) => { body: JSON.stringify(conversationData), credentials: "include", }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for stream results :O!"); - } - - return response.json(); - }) - .then((responseJson) => { - console.log("Conversation response: ", responseJson) - setIsAiLoading(false) - if (responseJson.success === false) { - if (responseJson.reason !== undefined) { + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!"); } - return - } + return response.json(); + }) + .then((responseJson) => { + console.log("Conversation response: ", responseJson) + setIsAiLoading(false) + if (responseJson.success === false) { + if (responseJson.reason !== undefined) { + } - if (inputAction !== undefined) { - console.log("In input action! Should check params if they match, and add suggestions") - - if (responseJson.parameters === undefined || responseJson.parameters.length === 0) { return } - for (let respParam of responseJson.parameters) { - if (respParam.name !== parameterName) { - continue + if (inputAction !== undefined) { + console.log("In input action! Should check params if they match, and add suggestions") + + if (responseJson.parameters === undefined || responseJson.parameters.length === 0) { + return } - if (respParam.value === "") { + for (let respParam of responseJson.parameters) { + if (respParam.name !== parameterName) { + continue + } + + if (respParam.value === "") { + break + } + + setlocalcodedata(respParam.value) break } - setlocalcodedata(respParam.value) - break + return } - - return - } - }) - .catch((error) => { - setIsAiLoading(false) - console.log("Conv response error: ", error); - }); + }) + .catch((error) => { + setIsAiLoading(false) + console.log("Conv response error: ", error); + }); } const autoFormat = (input) => { @@ -491,7 +491,7 @@ const CodeEditor = (props) => { const findIndex = (line, loc) => { var code_line = localcodedata.split('\n')[line] if (code_line === undefined) { - return + return } var dollar_occurences = [] @@ -499,21 +499,21 @@ const CodeEditor = (props) => { var variable_ranges = [] var popup = false - for(var ch=0; ch < code_line.length; ch++){ - if(code_line[ch] === '$'){ + for (var ch = 0; ch < code_line.length; ch++) { + if (code_line[ch] === '$') { dollar_occurences.push(ch) } } var variable_occurences = code_line.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) - try{ - for(var occ = 0; occ < variable_occurences.length; occ++){ + try { + for (var occ = 0; occ < variable_occurences.length; occ++) { dollar_occurences_len.push(variable_occurences[occ].length) } - } catch (e) {} + } catch (e) { } - for(var occ = 0; occ < dollar_occurences.length; occ++){ + for (var occ = 0; occ < dollar_occurences.length; occ++) { // var temp_arr = [] // for(var occ_len = 0; occ_len { // temp_arr.push(temp_arr[temp_arr.length-1]+1) // variable_ranges.push(temp_arr) var temp_arr = [dollar_occurences[occ]] - for(var occ_len = 0; occ_len < dollar_occurences_len[occ]; occ_len++){ - temp_arr.push(dollar_occurences[occ]+occ_len+1) + for (var occ_len = 0; occ_len < dollar_occurences_len[occ]; occ_len++) { + temp_arr.push(dollar_occurences[occ] + occ_len + 1) } - if(temp_arr.length==1) { - temp_arr.push(temp_arr[temp_arr.length-1]+1) + if (temp_arr.length == 1) { + temp_arr.push(temp_arr[temp_arr.length - 1] + 1) } variable_ranges.push(temp_arr) } - for(var occ = 0; occ { // Makes sure #0 and # are same, as we only visualize first one anyway if (tmpitem.startsWith("#")) { - removedIndexes += tmpitem.length-1 + removedIndexes += tmpitem.length - 1 tmpitem = "#" } @@ -585,26 +585,26 @@ const CodeEditor = (props) => { const highlight_variables = (value) => { if (value === undefined || value === null || value.length === 0) { - setMarkers([]) + setMarkers([]) return } // var session = localcodedata.getSession(); //var code_lines = localcodedata.split('\n') - var newMarkers = [] + var newMarkers = [] var code_lines = value.split('\n') for (var i = 0; i < code_lines.length; i++) { var current_code_line = code_lines[i] var variable_occurence = current_code_line.match(/[\\]{0,1}[$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,}/g); - + if (!variable_occurence) { continue; } - + var new_occurences = variable_occurence.filter((occurrence) => occurrence[0]); variable_occurence = new_occurences - + var dollar_occurence = []; for (let ch = 0; ch < current_code_line.length; ch++) { //if (current_code_line[ch] === '$' && (ch === 0)) { @@ -614,8 +614,8 @@ const CodeEditor = (props) => { } var dollar_occurence_len = [] - try{ - for(let occ = 0; occ < variable_occurence.length; occ++){ + try { + for (let occ = 0; occ < variable_occurence.length; occ++) { dollar_occurence_len.push(variable_occurence[occ].length) } } catch (e) { @@ -628,12 +628,12 @@ const CodeEditor = (props) => { //value.markText({line:i, ch:0}, {line:i, ch:code_lines[i].length-1}, {"css": "background-color: #; border-radius: 0px; color: inherit"}) } - for (let occ = 0; occ < variable_occurence.length; occ++) { - const fixedVariable = fixVariable(variable_occurence[occ]) - var correctVariable = availableVariables.includes(fixedVariable.toLowerCase()) + for (let occ = 0; occ < variable_occurence.length; occ++) { + const fixedVariable = fixVariable(variable_occurence[occ]) + var correctVariable = availableVariables.includes(fixedVariable) - var startCh = dollar_occurence[occ] - var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] + var startCh = dollar_occurence[occ] + var endCh = dollar_occurence[occ] + dollar_occurence_len[occ] try { newMarkers.push({ startRow: i, @@ -654,16 +654,16 @@ const CodeEditor = (props) => { type: "text", }) } - + setMarkers(newMarkers) - } + } } catch (e) { console.log("Error in color highlighting: ", e); } } - + setMarkers(newMarkers) } @@ -679,10 +679,10 @@ const CodeEditor = (props) => { var parsedVariable = currentVariable if (currentVariable === undefined || currentVariable === null) { console.log("Location: ", currentLocation) - parsedVariable= "$" + parsedVariable = "$" } - code_lines[currentLine] = code_lines[currentLine].slice(0,currentLocation[1]) + "$" + swapVariable + code_lines[currentLine].slice(currentLocation[1]+parsedVariable.length,) + code_lines[currentLine] = code_lines[currentLine].slice(0, currentLocation[1]) + "$" + swapVariable + code_lines[currentLine].slice(currentLocation[1] + parsedVariable.length,) // console.log(code_lines) var updatedCode = code_lines.join('\n') // console.log(updatedCode) @@ -713,7 +713,7 @@ const CodeEditor = (props) => { // Whelp this is inefficient af. Single loop pls // When the found array is empty. if (found !== null && found !== undefined) { - try { + try { for (var i = 0; i < found.length; i++) { try { // Finding if the value is in the list at all, and does initial replacement @@ -721,11 +721,11 @@ const CodeEditor = (props) => { var valuefound = false for (var j = 0; j < actionlist.length; j++) { - if(fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()) { + if (fixedVariable.slice(1,).toLowerCase() !== actionlist[j].autocomplete.toLowerCase()) { continue } - valuefound = true + valuefound = true try { if (typeof actionlist[j].example === "object") { @@ -737,25 +737,25 @@ const CodeEditor = (props) => { const newExample = fixStringInput(actionlist[j].example) input = input.replace(found[i], newExample, -1) } - } catch (e) { + } catch (e) { input = input.replace(found[i], actionlist[j].example, -1) } } - + if (!valuefound) { } - if (!valuefound && availableVariables.includes(fixedVariable.toLowerCase())) { + if (!valuefound && availableVariables.includes(fixedVariable)) { var shouldbreak = false - for (var k=0; k < actionlist.length; k++){ + for (var k = 0; k < actionlist.length; k++) { var parsedPaths = [] if (typeof actionlist[k].example === "object") { parsedPaths = GetParsedPaths(actionlist[k].example, ""); } for (var key in parsedPaths) { - const fullpath = "$"+actionlist[k].autocomplete.toLowerCase()+parsedPaths[key].autocomplete.toLowerCase() + const fullpath = "$" + actionlist[k].autocomplete.toLowerCase() + parsedPaths[key].autocomplete.toLowerCase() if (fullpath !== fixedVariable.toLowerCase()) { continue } @@ -776,7 +776,7 @@ const CodeEditor = (props) => { } else { if (typeof new_input === "string") { // Check if it contains any newlines, and replace them with raw newlines - new_input = fixStringInput(new_input) + new_input = fixStringInput(new_input) // Replace quotes with nothing } else { @@ -792,7 +792,7 @@ const CodeEditor = (props) => { input = input.replace(fixedVariable, new_input, -1) input = input.replace(found[i], new_input, -1) - shouldbreak = true + shouldbreak = true break } @@ -821,7 +821,7 @@ const CodeEditor = (props) => { } } - const handleItemClick = (values) => { + const handleItemClick = (values) => { if (values === undefined || values === null || values.length === 0) { return; } @@ -871,15 +871,15 @@ const CodeEditor = (props) => { setlocalcodedata(codedatasplit.join('\n')) - edited = true + edited = true } } if (edited === false) { if (!item.value.includes("{%") && !item.value.includes("{{")) { - setlocalcodedata(localcodedata+" | "+item.value+" }}") + setlocalcodedata(localcodedata + " | " + item.value + " }}") } else { - setlocalcodedata(localcodedata+item.value) + setlocalcodedata(localcodedata + item.value) } } @@ -897,12 +897,12 @@ const CodeEditor = (props) => { const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a" const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : selectedAction.name === "execute_bash" ? "execute_bash" : "repeat_back_to_me" - const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : actionname === "execute_bash" ? [{"name": "code", "value":inputdata}, {"name": "shuffle_input", "value": "", }] : [{"name":"call", "value": inputdata}] + const params = actionname === "execute_python" ? [{ "name": "code", "value": inputdata }] : actionname === "execute_bash" ? [{ "name": "code", "value": inputdata }, { "name": "shuffle_input", "value": "", }] : [{ "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":{}} + 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, + "valid": false, "result": baseResult, "errors": [], }) @@ -918,134 +918,134 @@ const CodeEditor = (props) => { body: JSON.stringify(actiondata), credentials: "include", }) - .then((response) => { - if (response.status !== 200) { - console.log("Status not 200 for stream results :O!") - } + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for stream results :O!") + } - return response.json() - }) - .then((responseJson) => { - //console.log("RESPONSE: ", responseJson) - var newResult = {} - if (responseJson.success === true && responseJson.result !== null && responseJson.result !== undefined && responseJson.result.length > 0) { - const result = responseJson.result.slice(0, 50)+"..." - //toast("SUCCESS: "+result) + return response.json() + }) + .then((responseJson) => { + //console.log("RESPONSE: ", responseJson) + var newResult = {} + if (responseJson.success === true && responseJson.result !== null && responseJson.result !== undefined && responseJson.result.length > 0) { + const result = responseJson.result.slice(0, 50) + "..." + //toast("SUCCESS: "+result) - const validate = validateJson(responseJson.result) - newResult = validate - } else if (responseJson.success === false && responseJson.reason !== undefined && responseJson.reason !== null) { - 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."} - } else { - newResult = {"valid": false, "result": "Couldn't finish execution (2). Please fill all the required fields, and validate the execution."} - } + const validate = validateJson(responseJson.result) + newResult = validate + } else if (responseJson.success === false && responseJson.reason !== undefined && responseJson.reason !== null) { + 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." } + } else { + newResult = { "valid": false, "result": "Couldn't finish execution (2). Please fill all the required fields, and validate the execution." } + } - if (responseJson.errors !== undefined && responseJson.errors !== null && responseJson.errors.length > 0) { - newResult.errors = responseJson.errors - } + if (responseJson.errors !== undefined && responseJson.errors !== null && responseJson.errors.length > 0) { + newResult.errors = responseJson.errors + } - setExecutionResult(newResult) - setExecuting(false) - }) - .catch(error => { - //toast("Execution error: "+error.toString()) - console.log("error: ", error) - setExecuting(false) - }) + setExecutionResult(newResult) + setExecuting(false) + }) + .catch(error => { + //toast("Execution error: "+error.toString()) + console.log("error: ", error) + setExecuting(false) + }) } - const adjustPosition = (menuPosition) => { - const { top, left, width, height } = menuPosition; - const windowWidth = window.innerWidth; - const windowHeight = window.innerHeight; + const adjustPosition = (menuPosition) => { + const { top, left, width, height } = menuPosition; + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; - let adjustedTop = top; - let adjustedLeft = left; + let adjustedTop = top; + let adjustedLeft = left; - // Adjust top position if menu overflows the bottom of the window - if (top + height > windowHeight) { - adjustedTop = windowHeight - height; - } + // Adjust top position if menu overflows the bottom of the window + if (top + height > windowHeight) { + adjustedTop = windowHeight - height; + } - // Adjust left position if menu overflows the right side of the window - if (left + width > windowWidth) { - adjustedLeft = windowWidth - width; - } + // Adjust left position if menu overflows the right side of the window + if (left + width > windowWidth) { + adjustedLeft = windowWidth - width; + } - return { - "top": adjustedTop, - "left": adjustedLeft - } - }; + return { + "top": adjustedTop, + "left": adjustedLeft + } + }; - // Define a custom completer for the Ace Editor - const customCompleter = { - getCompletions: function(editor, session, pos, prefix, callback) { - console.log("CUSTOM COMPLETER: ", prefix) + // Define a custom completer for the Ace Editor + const customCompleter = { + getCompletions: function(editor, session, pos, prefix, callback) { + console.log("CUSTOM COMPLETER: ", prefix) - callback(null, availableVariables.map((variable) => { - console.log("CUSTOM VAR: ", variable) + callback(null, availableVariables.map((variable) => { + console.log("CUSTOM VAR: ", variable) - return ({ - caption: variable, - value: variable, - meta: 'custom', - }) - })) - } - } + return ({ + caption: variable, + value: variable, + meta: 'custom', + }) + })) + } + } if (fullScreenMode) { return ( { - // setlocalcodedata(value) - // expectedOutput(value) - // highlight_variables(value,editor) - setlocalcodedata(value) - setcodedata(value) - }} - name="python-editor" - fontSize={14} - width="100%" - height="100%" - showPrintMargin={false} - showGutter={true} - markers={markers} - highlightActiveLine={false} - - enableBasicAutocompletion={true} - completers={[customCompleter]} + mode="python" + theme="gruvbox" + value={localcodedata} + onChange={(value, editor) => { + // setlocalcodedata(value) + // expectedOutput(value) + // highlight_variables(value,editor) + setlocalcodedata(value) + setcodedata(value) + }} + name="python-editor" + fontSize={14} + width="100%" + height="100%" + showPrintMargin={false} + showGutter={true} + markers={markers} + highlightActiveLine={false} - style={{ - wordBreak: "break-word", - marginTop: 0, - paddingBottom: 10, - overflowY: "auto", - whiteSpace: "pre-wrap", - wordWrap: "break-word", - backgroundColor: "rgba(40,40,40,1)", - zIndex: activeDialog === "codeeditor" ? 1200 : 1100, - }} + enableBasicAutocompletion={true} + completers={[customCompleter]} - setOptions={{ - enableBasicAutocompletion: true, - enableLiveAutocompletion: true, - enableSnippets: true, - showLineNumbers: true, - tabSize: 4, - fontFamily: "'JetBrains Mono', Consolas, monospace", - useSoftTabs: true - }} - /> + style={{ + wordBreak: "break-word", + marginTop: 0, + paddingBottom: 10, + overflowY: "auto", + whiteSpace: "pre-wrap", + wordWrap: "break-word", + backgroundColor: "rgba(40,40,40,1)", + zIndex: activeDialog === "codeeditor" ? 1200 : 1100, + }} + + setOptions={{ + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + showLineNumbers: true, + tabSize: 4, + fontFamily: "'JetBrains Mono', Consolas, monospace", + useSoftTabs: true + }} + /> ) } @@ -1054,14 +1054,14 @@ const CodeEditor = (props) => { aria-labelledby="draggable-dialog-title" // disableBackdropClick={true} disableEnforceFocus={true} - style={{ pointerEvents: "none", zIndex: activeDialog === "codeeditor" ? 1200 : 1100}} + style={{ pointerEvents: "none", zIndex: activeDialog === "codeeditor" ? 1200 : 1100 }} hideBackdrop={true} open={expansionModalOpen} onClose={() => { console.log("In closer") if (changeActionParameterCodeMirror !== undefined) { - changeActionParameterCodeMirror({target: {value: ""}}, fieldCount, localcodedata) + changeActionParameterCodeMirror({ target: { value: "" } }, fieldCount, localcodedata) } else { console.log("No action called changeActionParameterCodeMirror in code editor") } @@ -1084,91 +1084,91 @@ const CodeEditor = (props) => { maxHeight: isMobile ? "100%" : 700, border: theme.palette.defaultBorder, padding: isMobile ? "25px 10px 25px 10px" : 25, - zoom: 0.8, - backgroundColor: "black", + // zoom: 0.8, + backgroundColor: "black", }, }} > - {contentLoading === true ? - + + + : null} + + - - - : null} + title={`Move window`} + placement="left" + > + - { - }} - > - - + cursor: "move", + }} + onClick={() => { + }} + > + + - - { - setExpansionModalOpen(false) - }} - > - - + + { + setExpansionModalOpen(false) + }} + > + + -
-
- { isFileEditor ? +
+
+ {isFileEditor ?
-
- - File Editor ({localcodedata.length}) - + style={{ + display: 'flex', + }} + > +
+ + File Editor ({localcodedata.length}) + +
-
- : -
-
- {/* + : +
+
+ {/* { Code Editor */} - { isFileEditor ? null : -
- {selectedAction?.name === "execute_python" ? - - Run Python Code - - : - selectedAction.name === "execute_bash" ? - - Run Bash Code - - : -
- - { - setAnchorEl(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {liquidFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - - - { - setAnchorEl2(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {mathFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - - - { - setAnchorEl3(null); - }} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} - > - {pythonFilters.map((item, index) => { - return ( - { - handleClick(item) - }}>{item.name} - ) - })} - -
- } - - - { - handleMenuClose(); - }} - open={!!menuPosition} - style={{ - color: "white", - marginTop: 2, - maxHeight: 650, - }} - > - {actionlist?.map((innerdata) => { - const icon = - innerdata.type === "action" ? ( - - ) : innerdata.type === "workflow_variable" || - innerdata.type === "execution_variable" ? ( - - ) : ( - - ); - - const handleExecArgumentHover = (inside) => { - var exec_text_field = document.getElementById( - "execution_argument_input_field" - ); - if (exec_text_field !== null) { - if (inside) { - exec_text_field.style.border = "2px solid #f85a3e"; - } else { - exec_text_field.style.border = ""; - } - } - }; - - const handleActionHover = (inside, actionId) => { - }; - - const handleMouseover = () => { - if (innerdata.type === "Execution Argument") { - handleExecArgumentHover(true); - } else if (innerdata.type === "action") { - handleActionHover(true, innerdata.id); - } - }; - - const handleMouseOut = () => { - if (innerdata.type === "Execution Argument") { - handleExecArgumentHover(false); - } else if (innerdata.type === "action") { - handleActionHover(false, innerdata.id); - } - }; - - var parsedPaths = []; - if (typeof innerdata.example === "object") { - parsedPaths = GetParsedPaths(innerdata.example, ""); - } - - const coverColor = "#82ccc3" - //menuPosition.left -= 50 - //menuPosition.top -= 250 - //console.log("POS: ", menuPosition1) - var menuPosition1 = menuPosition - if (menuPosition1 === null) { - menuPosition1 = { - "left": 0, - "top": 0, - } - } else if (menuPosition1.top === null || menuPosition1.top === undefined) { - menuPosition1.top = 0 - } else if (menuPosition1.left === null || menuPosition1.left === undefined) { - menuPosition1.left = 0 - } - - //console.log("POS1: ", menuPosition1) - - return parsedPaths.length > 0 ? ( - - {icon} {innerdata.name} -
- } - parentMenuOpen={!!menuPosition} - style={{ - color: "white", - minWidth: 250, - maxWidth: 250, - maxHeight: 50, - overflow: "hidden", - }} - onClick={() => { - console.log("CLICKED: ", innerdata); - console.log(innerdata.example) - - //const handleClick = (item) => { - handleItemClick([innerdata]); - }} - > - - { - //console.log("HOVER: ", pathdata); - }} - onClick={() => { - handleItemClick([innerdata]); - }} - > - - {innerdata.name} + {isFileEditor ? null : +
+ {selectedAction?.name === "execute_python" ? + + Run Python Code + + : + selectedAction.name === "execute_bash" ? + + Run Bash Code - - {parsedPaths.map((pathdata, index) => { - // FIXME: Should be recursive in here - // - const icon = - pathdata.type === "value" ? ( - - ) : pathdata.type === "list" ? ( - - ) : ( - - ); - // + : +
+ + { + setAnchorEl(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {liquidFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl2(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {mathFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + + + { + setAnchorEl3(null); + }} + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + > + {pythonFilters.map((item, index) => { + return ( + { + handleClick(item) + }}>{item.name} + ) + })} + +
+ } - const indentation_count = (pathdata.name.match(/\./g) || []).length+1 - //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 - const boxPadding = 0 - const namesplit = pathdata.name.split(".") - const newname = namesplit[namesplit.length-1] - return ( - { + setMenuPosition({ + top: event.pageY, + left: event.pageX, + }) + }} + > + Autocomplete + + { + handleMenuClose(); + }} + open={!!menuPosition} + style={{ + color: "white", + marginTop: 2, + maxHeight: 650, + }} + > + {actionlist?.map((innerdata) => { + const icon = + innerdata.type === "action" ? ( + + ) : innerdata.type === "workflow_variable" || + innerdata.type === "execution_variable" ? ( + + ) : ( + + ); + + const handleExecArgumentHover = (inside) => { + var exec_text_field = document.getElementById( + "execution_argument_input_field" + ); + if (exec_text_field !== null) { + if (inside) { + exec_text_field.style.border = "2px solid #f85a3e"; + } else { + exec_text_field.style.border = ""; + } + } + }; + + const handleActionHover = (inside, actionId) => { + }; + + const handleMouseover = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(true); + } else if (innerdata.type === "action") { + handleActionHover(true, innerdata.id); + } + }; + + const handleMouseOut = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(false); + } else if (innerdata.type === "action") { + handleActionHover(false, innerdata.id); + } + }; + + var parsedPaths = []; + if (typeof innerdata.example === "object") { + parsedPaths = GetParsedPaths(innerdata.example, ""); + } + + const coverColor = "#82ccc3" + //menuPosition.left -= 50 + //menuPosition.top -= 250 + //console.log("POS: ", menuPosition1) + var menuPosition1 = menuPosition + if (menuPosition1 === null) { + menuPosition1 = { + "left": 0, + "top": 0, + } + } else if (menuPosition1.top === null || menuPosition1.top === undefined) { + menuPosition1.top = 0 + } else if (menuPosition1.left === null || menuPosition1.left === undefined) { + menuPosition1.left = 0 + } + + //console.log("POS1: ", menuPosition1) + + return parsedPaths.length > 0 ? ( + + {icon} {innerdata.name} +
+ } + parentMenuOpen={!!menuPosition} style={{ color: "white", minWidth: 250, maxWidth: 250, - padding: boxPadding, - }} - value={pathdata} - onMouseOver={() => { - //console.log("HOVER: ", pathdata); + maxHeight: 50, + overflow: "hidden", }} onClick={() => { - handleItemClick([innerdata, pathdata]); + console.log("CLICKED: ", innerdata); + console.log(innerdata.example) + + //const handleClick = (item) => { + handleItemClick([innerdata]); + }} + > + + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata]); + }} + > + + {innerdata.name} + + + {parsedPaths.map((pathdata, index) => { + // FIXME: Should be recursive in here + // + const icon = + pathdata.type === "value" ? ( + + ) : pathdata.type === "list" ? ( + + ) : ( + + ); + // + + const indentation_count = (pathdata.name.match(/\./g) || []).length + 1 + //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 + const boxPadding = 0 + const namesplit = pathdata.name.split(".") + const newname = namesplit[namesplit.length - 1] + return ( + { + //console.log("HOVER: ", pathdata); + }} + onClick={() => { + handleItemClick([innerdata, pathdata]); + }} + > + +
+ {Array(indentation_count).fill().map((subdata, subindex) => { + return ( +
+ ) + })} + {icon} {newname} + {pathdata.type === "list" ? { + e.preventDefault() + e.stopPropagation() + + console.log("INNER: ", innerdata, pathdata) + + // Removing .list from autocomplete + var newname = pathdata.name + if (newname.length > 5) { + newname = newname.slice(0, newname.length - 5) + } + + //selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}` + //selectedAction.parameters[count].value = selectedActionParameters[count].value; + //setSelectedAction(selectedAction); + //setShowDropdown(false); + setMenuPosition(null); + + // innerdata.name + // pathdata.name + //handleItemClick([innerdata, newpathdata]) + //console.log("CLICK LENGTH!") + }} /> : null} +
+ + + ); + })} + + + ) : ( + handleMouseover()} + onMouseOut={() => { + handleMouseOut(); + }} + onClick={() => { + handleItemClick([innerdata]); }} > -
- {Array(indentation_count).fill().map((subdata, subindex) => { - return ( -
- ) - })} - {icon} {newname} - {pathdata.type === "list" ? { - e.preventDefault() - e.stopPropagation() - - console.log("INNER: ", innerdata, pathdata) - - // Removing .list from autocomplete - var newname = pathdata.name - if (newname.length > 5) { - newname = newname.slice(0, newname.length-5) - } - - //selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}` - //selectedAction.parameters[count].value = selectedActionParameters[count].value; - //setSelectedAction(selectedAction); - //setShowDropdown(false); - setMenuPosition(null); - - // innerdata.name - // pathdata.name - //handleItemClick([innerdata, newpathdata]) - //console.log("CLICK LENGTH!") - }} /> : null} +
+ {icon} {innerdata.name}
); })} - - - ) : ( - handleMouseover()} - onMouseOut={() => { - handleMouseOut(); - }} - onClick={() => { - handleItemClick([innerdata]); - }} + +
+ } + + { + setlocalcodedata(editorData.example) + }} + color="secondary" + > + - -
- {icon} {innerdata.name} -
-
- - ); - })} - -
+ +
+ + { + if (setAiQueryModalOpen !== undefined) { + setAiQueryModalOpen(true) + } else { + autoFormat(localcodedata) + } + }} + > + + {isAiLoading ? + + : + + } + + +
+
} - + {(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor ? ( + { + highlight_variables(localcodedata) + }} + onCursorChange={(cursorPosition, editor, value) => { + setCurrentCharacter(cursorPosition.cursor.column) + setCurrentLine(cursorPosition.cursor.row) + findIndex(cursorPosition.row, cursorPosition.column) + + }} + onChange={(value, editor) => { + // setlocalcodedata(value) + // expectedOutput(value) + // highlight_variables(value,editor) + setlocalcodedata(value) + expectedOutput(value) + highlight_variables(value) + }} + setOptions={{ + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + showLineNumbers: true, + tabSize: 2, + wrap: true, + + useWorker: false, + enableBasicAutocompletion: [customCompleter], + }} + // options={options} + /> + ) : null} +
+ +
{ - setlocalcodedata(editorData.example) - }} - color="secondary" - > - - - - - { - if (setAiQueryModalOpen !== undefined) { - setAiQueryModalOpen(true) - } else { - autoFormat(localcodedata) - } }} > - - {isAiLoading ? - - : - - } - - +
-
- } - -
- {(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor ? ( - { - highlight_variables(localcodedata) - }} - onCursorChange={(cursorPosition, editor, value) => { - setCurrentCharacter(cursorPosition.cursor.column) - setCurrentLine(cursorPosition.cursor.row) - findIndex(cursorPosition.row, cursorPosition.column) - - }} - onChange={(value, editor) => { - // setlocalcodedata(value) - // expectedOutput(value) - // highlight_variables(value,editor) - setlocalcodedata(value) - expectedOutput(value) - highlight_variables(value) - }} - setOptions={{ - enableBasicAutocompletion: true, - enableLiveAutocompletion: true, - enableSnippets: true, - showLineNumbers: true, - tabSize: 2, - wrap: true, - - useWorker: false, - enableBasicAutocompletion: [customCompleter], - }} - // options={options} - /> - ): null} -
- -
-
-
- - {isFileEditor ? null : -
+ {isFileEditor ? null : +
- {isMobile ? null : + {isMobile ? null :
- + {selectedAction === undefined ? "" : selectedAction.name === "execute_python" || selectedAction.name === "execute_bash" ? "Code to run" : `Expected Output for '${selectedAction.name}'`} @@ -1722,46 +1722,46 @@ const CodeEditor = (props) => { } -
+
- - {isMobile ? null : - validation === true ? + {isMobile ? null : + validation === true ? { }} name={"JSON autocompletion"} /> - : + :

{ border: `2px solid ${theme.palette.inputColor}`, borderRadius: theme.palette?.borderRadius, maxHeight: 450, - minHeight: 450, - overflow: "auto", - wordWrap: "anywhere", + minHeight: 450, + overflow: "auto", + wordWrap: "anywhere", zIndex: activeDialog === "codeeditor" ? 1200 : 1100, }} > @@ -1803,16 +1803,16 @@ const CodeEditor = (props) => { }

- {executionResult.valid === true ? + {executionResult.valid === true ? { }} name={"Test result"} /> - : - - {executionResult.result.length > 0 ? - - - Test output - - - {executionResult.result} - - - : + : + + {executionResult.result.length > 0 ? + + + Test output + + + {executionResult.result} + + + : -
- - Output is based on the last VALID run of the node(s) you are referencing. Only updates when you refresh the Workflow Window. - - - No test output yet. - -
- } +
+ + Output is based on the last VALID run of the node(s) you are referencing. Only updates when you refresh the Workflow Window. + + + No test output yet. + +
+ } - {executionResult.errors !== undefined && executionResult.errors !== null && executionResult.errors.length > 0 ? - - Errors ({executionResult.errors.length}): {executionResult.errors.join("\n")} - - : null} -
+ {executionResult.errors !== undefined && executionResult.errors !== null && executionResult.errors.length > 0 ? + + Errors ({executionResult.errors.length}): {executionResult.errors.join("\n")} + + : null} +
}
- -
+ +
}
-
+
) diff --git a/frontend/src/context/ContextApi.jsx b/frontend/src/context/ContextApi.jsx index c9b02e34..3932dfd0 100644 --- a/frontend/src/context/ContextApi.jsx +++ b/frontend/src/context/ContextApi.jsx @@ -3,11 +3,18 @@ export const Context = createContext(); export const AppContext =(props) => { + const currentLocation = window?.location?.pathname; + // Left side bar global states const [searchBarModalOpen, setSearchBarModalOpen] = useState(false); - const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(false); + const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true) const [windowWidth, setWindowWidth] = useState(window.innerWidth); + useEffect(() => { + if (currentLocation?.includes('/workflows/') && leftSideBarOpenByClick === true) { + setLeftSideBarOpenByClick(false) + } + }, [leftSideBarOpenByClick]) //Calculate window width useEffect(() => { diff --git a/frontend/src/views/Admin2.jsx b/frontend/src/views/Admin2.jsx index f22da85b..2e68cef6 100644 --- a/frontend/src/views/Admin2.jsx +++ b/frontend/src/views/Admin2.jsx @@ -12,18 +12,18 @@ const Admin2 = (props) => { const [orgRequest, setOrgRequest] = React.useState(true); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const handleGetOrg = (orgId) => { - 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 foundorgid = params["org_id"]; - if (foundorgid !== undefined && foundorgid !== null) { - orgId = foundorgid; - } - } + // 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 foundorgid = params["org_id"]; + // if (foundorgid !== undefined && foundorgid !== null) { + // orgId = foundorgid; + // } + // } console.log("getting organization details for: ", orgId); // if (orgId === undefined) { @@ -154,16 +154,15 @@ const Admin2 = (props) => { }); }; - const urlSearchParams = new URLSearchParams(window.location.search); - const params = Object.fromEntries(urlSearchParams.entries()); - const foundOrgID = params["org_id"] useEffect(() => { - + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundOrgID = params["org_id"] if(foundOrgID !== null && foundOrgID !== undefined && userdata?.support && foundOrgID?.length > 0) { handleClickChangeOrg(foundOrgID) } - }, [foundOrgID]); + }, [userdata]); const handleClickChangeOrg = (orgId) => { // Don't really care about the logout diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 7550369c..ae87ef6a 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -399,7 +399,7 @@ const AngularWorkflow = (defaultprops) => { props.match = {} props.match.params = params - const { leftSideBarOpenByClick, windowWidth } = useContext(Context) + const { setLeftSideBarOpenByClick, leftSideBarOpenByClick, windowWidth } = useContext(Context) const [workflowAsCode, setWorkflowAsCode] = useState(false); var to_be_copied = ""; @@ -8662,6 +8662,9 @@ const releaseToConnectLabel = "Release to Connect" getApps() fetchUsecases() + setLeftSideBarOpenByClick(false) + localStorage.setItem("expandLeftNav", false) + const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search; // FIXME: Don't check specific one here @@ -10568,6 +10571,9 @@ const releaseToConnectLabel = "Release to Connect" const CustomAppHits = connectHits(AppHits) var shuffleToolsApp = apps.find((app) => app.name === "Shuffle Tools") + if (shuffleToolsApp !== undefined && shuffleToolsApp !== null) { + shuffleToolsApp = JSON.parse(JSON.stringify(shuffleToolsApp)) + } var viewedApps = [] return ( @@ -10620,24 +10626,24 @@ const releaseToConnectLabel = "Release to Connect"
- +
- +
- +
- +
- +
- +
- +
- +
: null} @@ -16242,7 +16248,7 @@ const releaseToConnectLabel = "Release to Connect" {!distributedFromParent ? isCorrectOrg ? null : - + Warning: Change { @@ -16313,7 +16319,7 @@ const releaseToConnectLabel = "Release to Connect" >Active Organization to edit this Workflow. : - + Warning: This workflow is controlled by your parent org and may not be editable. } diff --git a/frontend/src/views/ApiExplorerWrapper.jsx b/frontend/src/views/ApiExplorerWrapper.jsx index ca06d5a1..8de63fb9 100644 --- a/frontend/src/views/ApiExplorerWrapper.jsx +++ b/frontend/src/views/ApiExplorerWrapper.jsx @@ -69,6 +69,7 @@ const ApiExplorerWrapper = (props) => { const [authenticationType, setAuthenticationType] = React.useState(""); const [appAuthentication, setAppAuthentication] = useState([]); const [selectedMeta, setSelectedMeta] = useState(undefined); + const [appLoaded, setAppLoaded] = useState(false); const [selectedAction, setSelectedAction] = useState( { "app_name": selectedAppData.name, @@ -258,6 +259,7 @@ const ApiExplorerWrapper = (props) => { parsedapp.body === undefined ? parsedapp : JSON.parse(parsedapp.body); setOpenapi(data); + setAppLoaded(true); }; const handleAppAuthenticationType = (selectedAppData) => { @@ -773,7 +775,7 @@ const ApiExplorerWrapper = (props) => { ); }; - const skeletonLoader = ( + const SkeletonLoader = () => ( { - + { }} > - + - + - + { - + { ); + const AuthenticationData = (props) => { const selectedApp = props.app; @@ -1756,7 +1759,10 @@ const ApiExplorerWrapper = (props) => { return ( - + {appLoaded === false ? ( + + ) : ( + {authenticationModal} { isLoaded={isLoaded} /> + )} ); }; diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 143e070f..8e474eed 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -5811,9 +5811,11 @@ const AppCreator = (defaultprops) => { variant="outlined" color="secondary" onClick={() => { - var urlParams = new URLSearchParams(window.location.search); - if (!urlParams.has("id")) { - window.open(`/apis/${app.id}`, "_blank") + var urlParams = new URLSearchParams(window.location.search) + if (urlParams.has("id")) { + window.open(`/apis/${urlParams.get("id")}`, "_blank") + } else if (props.match.params.appid !== undefined && props.match.params.appid !== null && props.match.params.appid.length > 0) { + window.open(`/apis/${props.match.params.appid}`, "_blank") } else { toast.error("Build the app first.") } diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index 22c900b3..10b9cd78 100644 --- a/frontend/src/views/Apps2.jsx +++ b/frontend/src/views/Apps2.jsx @@ -1,7 +1,6 @@ import React, { useState, useEffect, useContext, useCallback, memo, useMemo, useRef } from "react"; import theme from "../theme.jsx"; import { isMobile } from "react-device-detect"; -import AppGrid from "../components/AppGrid.jsx"; import { useLocation, useNavigate } from "react-router-dom"; import { TextField, Button, Typography, MenuItem, Select, Tabs, Tab, Zoom, @@ -76,171 +75,190 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, onMouseOver={() => setMouseHoverIndex(index)} onMouseOut={() => setMouseHoverIndex(-1)} > - { - handleAppClick(data); + - {data.name} -
-
-
- {data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())} -
-
-
- {data.categories ? data.categories.join(", ") : "NA"} -
-
{ + handleAppClick(data); + }} + > + {data.name} +
+
+
+ {data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())} +
+
- {data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => ( - - {tag} - {tagIndex < data.tags.length - 1 ? ", " : ""} - - ))} + {data.categories ? data.categories.join(", ") : "NA"}
- {/* Deactivate button */} - {currTab === 0 && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true && ( +
- { - canEditApp && ( - - ) - } - + {data.generated !== true && data.tags && data.tags.slice(0, 2).map((tag, tagIndex) => ( + + {tag} + {tagIndex < data.tags.length - 1 ? ", " : ""} + + ))}
- )} + {/* Deactivate button */} + {currTab === 0 && !deactivatedIndexes.includes(index) && mouseHoverIndex === index && data.generated === true && ( +
+ { + canEditApp && ( + + ) + } + +
+ )} +
-
- + + ); @@ -263,10 +281,11 @@ const Hits = ({ const [allActivatedAppIds, setAllActivatedAppIds] = useState([]); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]); - const [isLoading, setIsLoading] = useState(true) + const [isLoading, setIsLoading] = useState(false) useEffect(() => { var baseurl = globalUrl; + setIsLoading(true) fetch(baseurl + "/api/v1/me", { credentials: "include", headers: { @@ -361,7 +380,34 @@ const Hits = ({ (
{hits?.length === 0 && searchQuery.length >= 0 ? ( - No apps found +
+ + + No apps found matching your search criteria + + + Try adjusting your search terms or filters to find what you're looking for + +
) : (
- { - console.log("App modal", data) - handleAppClick(data); + - {data.name} -
{ + console.log("App modal", data) + handleAppClick(data); }} > -
- {(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && } -
- {normalizedString(data.name)} -
-
- -
- {data.categories !== null - ? normalizedString(data.categories).join(", ") - : "NA"} -
+ />
-
- {hoverEffect === index && isCloud ? ( -
- {data.tags && ( - - - {data.tags.slice(0, 1).map((tag, tagIndex) => ( - - {normalizedString(tag)} - {tagIndex < 1 ? ", " : ""} - - ))} - - - )} -
- ) : ( -
- {data.tags && - data.tags.map((tag, tagIndex) => ( - - {normalizedString(tag)} - {tagIndex < data.tags.length - 1 ? ", " : ""} - - ))} -
- )} +
+ {(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && } +
+ {normalizedString(data.name)} +
-
- {hoverEffect === index && ( -
- {allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? ( - - ) : ( - - )} -
- )} + +
+ {data.categories !== null + ? normalizedString(data.categories).join(", ") + : "NA"} +
+
+
+ {hoverEffect === index && isCloud ? ( +
+ {data.tags && ( + + + {data.tags.slice(0, 1).map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < 1 ? ", " : ""} + + ))} + + + )} +
+ ) : ( +
+ {data.tags && + data.tags.map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < data.tags.length - 1 ? ", " : ""} + + ))} +
+ )} +
+
+ {hoverEffect === index && ( +
+ {allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? ( + + ) : ( + + )} +
+ )} +
-
- + + ); @@ -872,6 +940,42 @@ const filterApps = (apps, searchQuery, selectedCategory, selectedLabel) => { }); }; +const LoginPrompt = () => { + const navigate = useNavigate(); + return ( +
+ + Log in to see your organization's apps + + +
+ ) +}; + // Add this new component for the app skeleton const AppSkeleton = () => { return ( @@ -1853,34 +1957,34 @@ const Apps2 = (props) => { handleTabChange(event, newTab)} - style={{ - fontFamily: theme?.typography?.fontFamily, + style={{ + fontFamily: theme?.typography?.fontFamily, fontSize: 16, marginBottom: "-2px" }} - TabIndicatorProps={{ style: { display: 'none' } }} + TabIndicatorProps={{ style: { display: 'none' } }} > - - -
@@ -1895,6 +1999,7 @@ const Apps2 = (props) => { fullWidth variant="outlined" placeholder="Search for apps" + disabled={!isLoggedIn} value={searchQuery} id="shuffle_search_field" onChange={handleSearchChange} @@ -1945,6 +2050,7 @@ const Apps2 = (props) => { variant="outlined" value={selectedCategory} onChange={handleCategoryChange} + disabled={!isLoggedIn} displayEmpty multiple style={{ @@ -2009,6 +2115,7 @@ const Apps2 = (props) => { variant="outlined" value={selectedLabel} onChange={handleLabelChange} + disabled={!isLoggedIn} displayEmpty multiple style={{ @@ -2067,6 +2174,7 @@ const Apps2 = (props) => { variant="contained" color="primary" onClick={handleCreateApp} + disabled={!isLoggedIn} style={{ height: "100%", width: '100%', @@ -2092,51 +2200,55 @@ const Apps2 = (props) => { {isLoading ? ( ) : ( - <> - {appsToShow?.length > 0 && appsToShow !== undefined && !isLoading ? ( -
- {appsToShow.map((data, index) => ( - + {appsToShow?.length > 0 && appsToShow !== undefined && !isLoading ? ( +
+ {appsToShow.map((data, index) => ( + + ))} +
+ ) : ( +
+ - ))} -
- ) : ( -
- -
- )} - +
+ )} + + ) : ( + + ) )}
) @@ -2147,41 +2259,45 @@ const Apps2 = (props) => { {isLoading ? ( ) : ( - <> - {appsToShow?.length > 0 && appsToShow !== undefined ? ( -
- {appsToShow.map((data, index) => ( - + {appsToShow?.length > 0 && appsToShow !== undefined ? ( +
+ {appsToShow.map((data, index) => ( + + ))} +
+ ) : ( +
+ - ))} -
- ) : ( -
- -
- )} - +
+ )} + + ) : ( + + ) )}
) diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 7c124bf0..1ead2cd2 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -716,6 +716,8 @@ const Workflows2 = (props) => { const [videoViewOpen, setVideoViewOpen] = React.useState(false) const [gettingStartedItems, setGettingStartedItems] = React.useState([]) const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([]) + const [page, setPage] = React.useState(0); + const [pageSize, setPageSize] = React.useState(100); const [highlightIds, setHighlightIds] = React.useState([]) const [apps, setApps] = React.useState([]); @@ -751,15 +753,6 @@ const Workflows2 = (props) => { navigate(`${location.pathname}?${queryParams.toString()}`); }; - useEffect(() => { - if (currTab === 2) { - setIsLoadingPublicWorkflow(true); - // Simulate loading time for the Algolia search results - setTimeout(() => { - setIsLoadingPublicWorkflow(false); - }, 2500); - } - }, [currTab]) @@ -1322,6 +1315,8 @@ const Workflows2 = (props) => { if (responseJson !== undefined && responseJson !== null) { if (responseJson.success === false) { } else if (responseJson.length === 0) { + // When there are no workflows, we can set the loading to false + setIsLoadingWorkflow(false) if (currTab !== 2) { toast("No workflows found. Showing workflow discovery") setCurrTab(2) @@ -2483,6 +2478,7 @@ const Workflows2 = (props) => { } } + return (
@@ -2537,7 +2533,7 @@ const Workflows2 = (props) => { maxWidth: 310, padding: "12px 0", }}> - {(data?.image !== undefined || data?.image_url !== undefined) ? ( + {(data?.image !== undefined || (data?.image_url !== undefined && data?.image_url.length > 0)) ? (
{ className={classes.datagrid} rows={rows} columns={columns} - pageSize={100} + page={page} + onPageChange={(newPage) => { + setPage(newPage) + }} + pageSize={pageSize} + onPageSizeChange={(newPageSize) => { + setPageSize(newPageSize); + }} + rowsPerPageOptions={[25, 50, 100, 150]} checkboxSelection autoHeight density="standard" @@ -3962,7 +3966,7 @@ const Workflows2 = (props) => { setIsLoadingWorkflow(false); } - }, [currTab, workflows, userdata, filteredWorkflows]) + }, [currTab, workflows, userdata, filteredWorkflows, filters]) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 9c9c9709..ecebe91f 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -2298,6 +2298,10 @@ func main() { } else if incRequest.Type == "START_TENZIR" { log.Printf("[INFO] Got job to start tenzir") + // Manual command = overrides to allow starting of Tenzir from the frontend anyway. + os.Setenv("SHUFFLE_SKIP_PIPELINES", "false") + tenzirDisabled = false + err := deployTenzirNode() if err != nil { if strings.Contains(fmt.Sprintf("%s", err), "node available") { @@ -3456,6 +3460,7 @@ func sendPipelineHealthStatus() (shuffle.LakeConfig, error) { if (!strings.Contains(err.Error(), "SHUFFLE_SKIP_PIPELINES") && !strings.Contains(err.Error(), "Kubernetes not implemented for Tenzir node")) && !strings.Contains(err.Error(), "Tenzir Node is already running") && !strings.Contains(err.Error(), "docker daemon") { log.Printf("[ERROR] Tenzir node connection problem: %s", err) + } else { tenzirDisabled = true log.Printf("[ERROR] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err) From 38cb469cbbe43e0f72d98e2b421a8df41dbc3d24 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 9 Jan 2025 22:46:36 +0100 Subject: [PATCH 21/25] Fixed some issues where Pipeline startups are really annoying --- functions/onprem/orborus/orborus.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index ecebe91f..6dfa6402 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -2302,11 +2302,16 @@ func main() { os.Setenv("SHUFFLE_SKIP_PIPELINES", "false") tenzirDisabled = false + // Removed either way + toBeRemoved.Data = append(toBeRemoved.Data, incRequest) + err := deployTenzirNode() if err != nil { if strings.Contains(fmt.Sprintf("%s", err), "node available") { - toBeRemoved.Data = append(toBeRemoved.Data, incRequest) - } else { + // Disabling until UI is updated + os.Setenv("SHUFFLE_SKIP_PIPELINES", "true") + tenzirDisabled = true + log.Printf("[ERROR] Failed to start tenzir, reason: %s", err) err = shuffle.CreateOrgNotification( ctx, @@ -2322,9 +2327,6 @@ func main() { return } } - - } else { - toBeRemoved.Data = append(toBeRemoved.Data, incRequest) } } else { @@ -2642,7 +2644,7 @@ func deployTenzirNode() error { ctx := context.Background() cacheKey := "tenzir-key" - imageName := "tenzir/tenzir:latest" + imageName := "frikky/shuffle:tenzir" containerName := "tenzir-node" containerStartOptions := container.StartOptions{} _, err = shuffle.GetCache(ctx, cacheKey) @@ -2827,9 +2829,13 @@ func createAndStartTenzirNode(ctx context.Context, containerName, imageName stri }, } + // FIXME: Is this necessary? Seems to screw up networking: + // conflicting options: hostname and the network mode + /* if isKubernetes != "true" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" { hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) } + */ resp, err := dockercli.ContainerCreate(ctx, config, hostConfig, networkingConfig, nil, containerName) if err != nil { From aab46cca61a46bbe3909294ebfcb56d4162e2542 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 9 Jan 2025 22:54:35 +0100 Subject: [PATCH 22/25] Minor UI fixes for Shuffle Tools dragging --- frontend/src/components/DetectionExplorer.jsx | 10 +++++++--- frontend/src/views/AngularWorkflow.jsx | 12 +++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/DetectionExplorer.jsx b/frontend/src/components/DetectionExplorer.jsx index 81eda8c0..fed09d26 100644 --- a/frontend/src/components/DetectionExplorer.jsx +++ b/frontend/src/components/DetectionExplorer.jsx @@ -133,14 +133,18 @@ const DetectionExplorer = (props) => { const handleConnectClick = () => { if (detectionWorkflowId !== "") { + console.log("Already have a workflow ID for this detection") + //toast.info(`Already have a detection workflow for ${detectionInfo?.category}`) // FIXME: Show the Usecase UI for how to fix the workflow(s) // Instead loading full workflow and showing it directly? Hmm //toast.warn("Please reload the UI to load the detection status") - return + //return } if (isDetectionActive) { - return + console.log("Already connected") + //toast.info(`Connected to ${detectionInfo?.category}`) + //return } if (detectionInfo.category === undefined || detectionInfo.category === null) { @@ -276,7 +280,7 @@ const DetectionExplorer = (props) => { }} > - {detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rules)`} + {detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rule${filteredRules?.length > 1 ? "s" : ""})`}
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index ae87ef6a..bfeff035 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -10021,7 +10021,14 @@ const releaseToConnectLabel = "Release to Connect" const small = props.small const actionString = props.action const [hover, setHover] = React.useState(false); - + React.useEffect(() => { + if(app.name === "Shuffle Tools"){ + if (app.actions !== undefined && (app.actions === null || app.actions.length === 1)) { + loadAppConfig(app.id, false) + } + } + }, []) + if (app === undefined || app === null) { return (
{ clickedApp(hit) From 3cd91bc2829956f7487790c4dcdf276ac9c57693 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 9 Jan 2025 23:12:58 +0100 Subject: [PATCH 23/25] Removed a printline from orborus --- functions/onprem/orborus/orborus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 6dfa6402..10573709 100755 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -2627,7 +2627,7 @@ func handlePipeline(incRequest shuffle.ExecutionRequest) error { func deployTenzirNode() error { if os.Getenv("SHUFFLE_SKIP_PIPELINES") == "false" || os.Getenv("SHUFFLE_PIPELINE_ENABLED") == "true" { // return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") - log.Printf("[INFO] Pipelines are enabled by user") + //log.Printf("[INFO] Pipelines are enabled by user") } else { return errors.New("Pipelines are disabled by user with SHUFFLE_SKIP_PIPELINES") } From 09ab53f24219ee552483d9b558fe407e55788b65 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 9 Jan 2025 23:13:10 +0100 Subject: [PATCH 24/25] Bumped gomod for orborus --- functions/onprem/orborus/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 33577275..38a4d2e2 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/docker v27.0.2+incompatible github.com/docker/go-connections v0.5.0 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.6.90 + github.com/shuffle/shuffle-shared v0.6.93 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 ) From dc258d1497f86ab118edfc0586492a7a5853a70b Mon Sep 17 00:00:00 2001 From: Frikky Date: Mon, 13 Jan 2025 14:26:03 +0100 Subject: [PATCH 25/25] Frontend merges --- backend/go-app/go.mod | 2 +- frontend/src/components/ParsedAction.jsx | 14 ++++++++++---- frontend/src/components/ShuffleCodeEditor1.jsx | 5 ++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 11ecca3f..b9ee6c05 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -20,7 +20,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/h2non/filetype v1.1.3 github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.6.90 + github.com/shuffle/shuffle-shared v0.6.94 golang.org/x/crypto v0.22.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.63.2 diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 5749edfa..df84ad89 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1484,6 +1484,14 @@ const ParsedAction = (props) => { const selectedAppIcon = selectedAction.large_image + + var newAppname = selectedAction?.name?.charAt(0).toUpperCase() + selectedAction?.name?.substring(1) + if (newAppname === undefined || newAppname === null) { + newAppname = "" + } else { + newAppname = newAppname.replaceAll("_", " ") + } + return (
@@ -1506,11 +1514,9 @@ const ParsedAction = (props) => { border: "2px solid rgba(255,255,255,0.3)", }} /> +

- {( - selectedAction?.app_name?.charAt(0).toUpperCase() + - selectedAction?.app_name?.substring(1) - )?.replaceAll("_", " ")} + {newAppname}

diff --git a/frontend/src/components/ShuffleCodeEditor1.jsx b/frontend/src/components/ShuffleCodeEditor1.jsx index 85469445..f28668fa 100644 --- a/frontend/src/components/ShuffleCodeEditor1.jsx +++ b/frontend/src/components/ShuffleCodeEditor1.jsx @@ -1843,7 +1843,10 @@ const CodeEditor = (props) => { variant='body2' color='textSecondary' > - Output is based on the last VALID run of the node(s) you are referencing. Only updates when you refresh the Workflow Window. + Output is based on the last VALID run of the node(s) you are referencing. Refresh the page to get updated Variable values.  + {selectedAction?.name === "execute_python" ? + "For Python: exit() to stop a python script ANYWHERE." + : null} No test output yet.