diff --git a/.github/workflows/dockerbuild.yaml b/.github/workflows/dockerbuild.yaml index 790202eb..a04e87bd 100644 --- a/.github/workflows/dockerbuild.yaml +++ b/.github/workflows/dockerbuild.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: branches: - - main + - nightly paths: - "**" - "!.github/**" @@ -20,19 +20,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 @@ -74,11 +74,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 diff --git a/.github/workflows/nightly-release.yaml b/.github/workflows/nightly-release.yaml index ef888ced..228d8d5a 100644 --- a/.github/workflows/nightly-release.yaml +++ b/.github/workflows/nightly-release.yaml @@ -3,7 +3,7 @@ on: release: types: [published] branches: - - 2.0.0 + - nightly jobs: main: 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..b9ee6c05 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -20,8 +20,8 @@ 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 - golang.org/x/crypto v0.31.0 + 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 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/AppModal.jsx b/frontend/src/components/AppModal.jsx index 546111ab..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'; @@ -22,8 +24,8 @@ import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import LaunchIcon from '@mui/icons-material/Launch'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { CloudDownloadOutlined } from '@mui/icons-material'; -import { findSpecificApp } from './AppFramework'; -import theme from "../theme"; +import { findSpecificApp } from '../components/AppFramework.jsx'; +import theme from "../theme.jsx"; import YAML from 'yaml'; import { toast } from 'react-toastify'; import { Link } from 'react-router-dom'; @@ -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} 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 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/components/Files.jsx b/frontend/src/components/Files.jsx index b912e8ef..378bc532 100644 --- a/frontend/src/components/Files.jsx +++ b/frontend/src/components/Files.jsx @@ -22,6 +22,9 @@ import { DialogActions, Typography, Skeleton, + Checkbox, + Chip, + Menu, } from "@mui/material"; import { @@ -35,6 +38,7 @@ import { Publish as PublishIcon, Clear as ClearIcon, Add as AddIcon, + SelectAll, } from "@mui/icons-material"; import Dropzone from "../components/Dropzone.jsx"; @@ -61,6 +65,14 @@ const Files = memo((props) => { 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 d568127f..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-beta + 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"); @@ -662,7 +670,8 @@ useEffect(() => { }; const getRegionTag = (region_url) => { - let regiontag = "UK"; + //let regiontag = "UK"; + let regiontag = "EU"; if ( region_url !== undefined && region_url !== null && @@ -676,7 +685,7 @@ useEffect(() => { if (regiontag === "california") { regiontag = "US"; } else if (regiontag === "frankfurt") { - regiontag = "EU"; + regiontag = "EU-2"; } else if (regiontag === "ca"){ regiontag = "CA"; } @@ -764,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} { whiteSpace: "nowrap", }} > - Ctrl/Cmd+K + Ctrl+K ), disableUnderline: true, @@ -1153,17 +1181,14 @@ useEffect(() => { > @@ -1452,6 +1466,8 @@ useEffect(() => { + + {recentworkflows?.length > 0 ? { }) } + : null } + { 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/Priorities.jsx b/frontend/src/components/Priorities.jsx index a78e8306..ff359168 100644 --- a/frontend/src/components/Priorities.jsx +++ b/frontend/src/components/Priorities.jsx @@ -409,8 +409,8 @@ const NotificationItem = memo((props) => { {data.read === false ? : @@ -435,15 +435,12 @@ const NotificationItem = memo((props) => { {data.description}
+ ) : 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. 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. + +
+ } - {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..bfeff035 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 @@ -10018,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) @@ -10568,6 +10581,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 +10636,24 @@ const releaseToConnectLabel = "Release to Connect"
- +
- +
- +
- +
- +
- +
- +
- +
: null} @@ -16242,7 +16258,7 @@ const releaseToConnectLabel = "Release to Connect" {!distributedFromParent ? isCorrectOrg ? null : - + Warning: Change { @@ -16313,7 +16329,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 baf4c392..8e474eed 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -64,6 +64,7 @@ const inputColor = "#383B40"; const bodyDivStyle = { margin: "auto", width: "900px", + zoom: 0.8, }; const actionListStyle = { @@ -72,10 +73,11 @@ const actionListStyle = { paddingBottom: "10px", paddingTop: "10px", marginTop: "5px", - backgroundColor: inputColor, display: "flex", color: "white", - position: "relative", + position: "relative", + + backgroundColor: theme.palette.platformColor, }; const boxStyle = { @@ -89,7 +91,8 @@ const boxStyle = { paddingTop: "30px", display: "flex", flexDirection: "column", - backgroundColor: surfaceColor, + + backgroundColor: theme.palette.backgroundColor, }; const dividerStyle = { @@ -5808,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.") } @@ -6191,7 +6196,7 @@ const AppCreator = (defaultprops) => { {testView} */} -
+
{appDownloadData.length > 0 ? diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index 8e4944a5..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 ( @@ -1761,7 +1865,7 @@ const Apps2 = (props) => {
- Apps + {currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Apps {isCloud ? null : ( @@ -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 e948244b..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]) @@ -1319,8 +1312,17 @@ 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) { + // 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) + } + } + var newarray = [] for (var wfkey in responseJson) { const wf = responseJson[wfkey] @@ -2476,6 +2478,7 @@ const Workflows2 = (props) => { } } + return (
@@ -2530,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" @@ -3955,7 +3966,7 @@ const Workflows2 = (props) => { setIsLoadingWorkflow(false); } - }, [currTab, workflows, userdata, filteredWorkflows]) + }, [currTab, workflows, userdata, filteredWorkflows, filters]) @@ -4243,7 +4254,7 @@ const Workflows2 = (props) => { margin: "auto", }}> - Workflows + {currTab === 0 ? "Org" : currTab === 1 ? "Your" : "Discover"} Workflows
@@ -4258,21 +4269,21 @@ const Workflows2 = (props) => { TabIndicatorProps={{ style: { display: 'none' } }} > 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,14 +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") } - - /*** ***/ - var dockercli *dockerclient.Client - defer dockercli.Close() + /*** ENDREMOVE ***/ + // var dockercli *dockerclient.Client + // ctx := context.Background() scaleReplicas := os.Getenv("SHUFFLE_APP_REPLICAS") if len(scaleReplicas) > 0 { tmpInt, err := strconv.Atoi(scaleReplicas) @@ -4130,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) + // 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) @@ -4182,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)) } } }