Merge branch 'main' into nightly

This commit is contained in:
Aditya
2024-12-13 04:49:18 +05:30
22 changed files with 1706 additions and 915 deletions
-1
View File
@@ -6,7 +6,6 @@ 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)
</h1><h4 align="center">
+8 -8
View File
@@ -21,7 +21,7 @@ require (
github.com/h2non/filetype v1.1.3
github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.6.90
golang.org/x/crypto v0.22.0
golang.org/x/crypto v0.31.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.15.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.19.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/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/time v0.5.0 // indirect
golang.org/x/tools v0.18.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // 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
+16 -18
View File
@@ -334,8 +334,6 @@ 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=
@@ -410,8 +408,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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
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/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=
@@ -440,8 +438,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.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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/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=
@@ -468,8 +466,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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
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/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=
@@ -486,8 +484,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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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/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=
@@ -517,16 +515,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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/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.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
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=
@@ -538,8 +536,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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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/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=
@@ -574,8 +572,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.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
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/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=
+4 -4
View File
@@ -1,6 +1,6 @@
services:
frontend:
image: ghcr.io/shuffle/shuffle-frontend:1.4.2
image: ghcr.io/shuffle/shuffle-frontend:1.4.0
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.2
image: ghcr.io/shuffle/shuffle-backend:1.4.0
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.2
- SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:1.4.0
env_file: .env
restart: unless-stopped
security_opt:
+42 -22
View File
@@ -57,7 +57,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
: hover
? ""
: "1px solid rgba(255,255,255,0.3)",
borderImage: makeFancy ? "linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1" : "none",
borderImage: makeFancy ? "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1" : "none",
transition: 'all 0.2s ease-in-out',
}
@@ -340,7 +340,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
const circularLoader = validation ? <CircularProgress color="primary" /> : null
const errorText = openApiError?.length > 0 ? <div style={{ marginTop: 10 }}>Error: {openApiError}</div> : null
const errorText = openApiError?.length > 0 ? <div style={{ marginTop: 15, color: '#fd4c62' }}>Error: {openApiError}</div> : null
// Common dialog styles
const dialogStyle = {
@@ -400,7 +400,13 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
Create New App
</Typography>
<IconButton
onClick={onClose}
onClick={() => {
setOpenApi("")
setOpenApiError("")
setAppValidation("")
setValidation(false)
onClose()
}}
sx={{
color: 'rgba(255, 255, 255, 0.7)',
'&:hover': { bgcolor: 'rgba(255, 255, 255, 0.1)' }
@@ -477,7 +483,14 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
Create New App from Open API or Swagger
</Typography>
<IconButton
onClick={() => setOpenApiModal(false)}
onClick={() => {
setOpenApiModal(false)
setGenerateAppModal(false)
setOpenApi("")
setOpenApiError("")
setAppValidation("")
setValidation(false)
}}
sx={{
color: 'rgba(255,255,255,0.7)',
'&:hover': { bgcolor: 'rgba(255,255,255,0.1)' }
@@ -487,14 +500,16 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
</IconButton>
</DialogTitle>
<DialogContent sx={{ px: 4, py: 3 }}>
<div style={{ display: "flex", fontSize: '14px', gap: '5px', alignItems: 'center', marginBottom: '10px', fontFamily: theme?.typography?.fontFamily }}>
<Typography sx={{ color: 'rgba(255,255,255,0.85)', fontSize: '14px' }}>
<div style={{ display: "flex", fontSize: '14px', gap: '5px', alignItems: 'center', marginBottom: '10px', fontFamily: theme?.typography?.fontFamily, marginTop: '15px' }}>
<Typography sx={{ color: 'rgba(255,255,255,0.85)', fontSize: '16px' }}>
Paste in the URI for the OpenAPI or find out
</Typography>
<Link style={{
color: '#ff8544',
textDecoration: 'none',
textDecoration: 'underline',
fontSize: '16px',
fontFamily: theme?.typography?.fontFamily
}}>
How to find URI for openAPI?
</Link>
@@ -506,12 +521,13 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
fullWidth
variant="outlined"
placeholder="Open API URI"
style={{ fontFamily: theme?.typography?.fontFamily, fontSize: '16px' }}
sx={{
'& .MuiOutlinedInput-root': {
height: '40px'
height: '40px',
},
'& .MuiOutlinedInput-input::placeholder': {
fontSize: '14px'
fontSize: '16px'
}
}}
onChange={(e) => setOpenApi(e.target.value)}
@@ -526,7 +542,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
px: 3,
textTransform: 'none',
height: '40px',
fontSize: '14px',
fontSize: '16px',
fontFamily: theme?.typography?.fontFamily,
cursor: 'pointer'
}}
@@ -562,7 +578,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
sx={{
color: '#FF8544',
borderColor: '#FF8544',
px: 3,
px: 5,
py: 1,
'&:hover': {
borderColor: '#FF8544',
@@ -570,7 +586,8 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
},
textTransform: 'none',
fontSize: '14px',
fontFamily: theme?.typography?.fontFamily
fontFamily: theme?.typography?.fontFamily,
height: '40px'
}}
>
Upload
@@ -605,7 +622,8 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
},
textTransform: 'none',
py: 1.5,
fontSize: '14px',
mb: 1.5,
fontSize: '16px',
fontWeight: 500,
width: '100%',
maxWidth: '300px',
@@ -648,7 +666,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
<Typography variant="h6" sx={{
color: '#F1F1F1',
fontWeight: 500,
fontFamily: theme?.typography?.fontFamily
fontFamily: theme?.typography?.fontFamily,
}}>
Generate an app based on documentation (beta)
</Typography>
@@ -656,6 +674,10 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
onClick={() => {
setGenerateAppModal(false)
setOpenApiModal(false)
setOpenApi("")
setOpenApiError("")
setAppValidation("")
setValidation(false)
}}
sx={{
color: 'rgba(255,255,255,0.7)',
@@ -668,9 +690,9 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
<DialogContent sx={{ px: 4, py: 3, pt: 0 }}>
<Typography sx={{
color: 'rgba(255,255,255,0.85)',
mb: 3,
mb: 2,
fontSize: '14px',
mt:4,
mt: 2,
fontFamily: theme?.typography?.fontFamily
}}>
Paste in a URL, and we will make it into an app for you.
@@ -689,18 +711,15 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
color: 'white',
'& fieldset': {
borderWidth: '1px',
borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1',
borderImageSlice: 1,
borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1",
},
'&:hover fieldset': {
borderWidth: '1px',
borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1',
borderImageSlice: 1,
borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1",
},
'&.Mui-focused fieldset': {
borderWidth: '2px',
borderImage: 'linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1',
borderImageSlice: 1,
borderImage: "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1",
}
},
'& .MuiOutlinedInput-input::placeholder': {
@@ -727,6 +746,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
</DialogContent>
<DialogActions sx={{
py: 2,
pt: 1,
px: 3,
gap: 2,
display: 'flex',
@@ -736,7 +756,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
{circularLoader}
{
!validation &&
<Typography sx={{ color: 'rgba(255,255,255,0.85)', fontSize: '14px', fontFamily: theme?.typography?.fontFamily, pt: 2 }}>
<Typography sx={{ color: '#c5c5c5', fontSize: '14px', fontFamily: theme?.typography?.fontFamily, }}>
This may take multiple minutes based on the size of the documentation.
</Typography>
}
+18 -21
View File
@@ -433,7 +433,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
fontFamily: theme?.typography?.fontFamily
}}
>
<Typography component="div" sx={{ fontWeight: 500, color: "#F1F1F1", fontSize: "22px" }}>
<Typography component="div" sx={{ fontWeight: 500, color: "#F1F1F1", fontSize: "20px" }}>
About {app?.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
</Typography>
<IconButton
@@ -464,31 +464,28 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.2)"
}}
/>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "center", paddingLeft: 8 }}>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "center", paddingLeft: 8, }}>
<div style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}>
<Typography variant="h5" component="div" sx={{ fontWeight: 600 }}>
{newAppname}
</Typography>
{
isCloud && (
<Link
to={"/apps/" + (app?.id || app?.objectID)}
style={{ textDecoration: "none", color: "#f85a3e", marginTop: "-2px" }}
>
<IconButton
style={{
color: "#f85a3e",
fontSize: 20,
}}
>
<OpenInNewIcon />
</IconButton>
</Link>
)
}
<Link
to={"/apps/" + (app?.id || app?.objectID)}
style={{ textDecoration: "none", color: "#f85a3e", marginTop: "-2px" }}
>
<IconButton
style={{
color: "#f85a3e",
fontSize: 16,
}}
>
<OpenInNewIcon />
</IconButton>
</Link>
</div>
<Typography
variant="body2"
@@ -547,7 +544,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
if (canEditApp) {
const editUrl = "/apps/edit/" + (app?.id || app?.objectID);
navigate(editUrl)
}else{
} else {
const forkUrl = "/apps/new?id=" + (app?.id || app?.objectID);
navigate(forkUrl)
}
@@ -735,7 +732,7 @@ const AppModal = ({ open, onClose, app, globalUrl }) => {
bgcolor: '#FF8544',
'&:hover': { bgcolor: '#FF8544' },
textTransform: 'none',
borderRadius: 1,
borderRadius: "4px",
py: 1,
px: 7,
fontSize: "14px",
+6 -4
View File
@@ -466,14 +466,16 @@ const AppSelection = props => {
fontSize: 16,
color: "rgba(158, 158, 158, 1)",
fontFamily: theme?.typography?.fontFamily,
lineHeight: "24px"
}}
>
Your organization has no apps yet, select your starting apps here
or discover more apps using the <span
{`${window.location.pathname.includes("my_apps") || window.location.search.includes("my_apps") ? "You have no apps yet, select your starting apps here" : "Your organization has no apps yet, select your starting apps here"}
or discover more apps using the`}
<span
onClick={() => {
navigate("/apps2?tab=all_apps")
navigate("/apps?tab=all_apps")
}}
style={{ color: "#FF8444", fontWeight: "medium", fontSize: 16, cursor:"pointer" }}>App Library</span>
style={{ color: "#FF8444", fontWeight: "medium", fontSize: 16, cursor:"pointer", marginLeft: 6 }}>App Library</span>
</span>
</div>
)
+250 -126
View File
@@ -51,11 +51,13 @@ import BillingStats from "./BillingStats.jsx";
import { handlePayasyougo } from "../views/HandlePaymentNew.jsx"
import DeleteIcon from '@mui/icons-material/Delete';
import { Context } from "../context/ContextApi.jsx";
import LicencePopup from "./LicencePopup.jsx";
const Billing = memo((props) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, clickedFromOrgTab } = props;
const { globalUrl, userdata, serverside, billingInfo, stripeKey,isLoaded, selectedOrganization, handleGetOrg, clickedFromOrgTab, removeCookie} = props;
//const alert = useAlert();
let navigate = useNavigate();
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false);
const [dealList, setDealList] = React.useState([]);
const [dealName, setDealName] = React.useState("");
@@ -73,14 +75,17 @@ const Billing = memo((props) => {
const [currentAppRunsInNumber, setCurrentAppRunsInNumber] = useState(0);
const [alertThresholds, setAlertThresholds] = useState(selectedOrganization.Billing !== undefined && selectedOrganization.Billing.AlertThreshold !== undefined && selectedOrganization.Billing.AlertThreshold !== null ? selectedOrganization.Billing.AlertThreshold : [{ percentage: '', count: '', Email_send: false }]);
const [currentIndex, setCurrentIndex] = useState(0);
const [deleteAlertIndex, setDeleteAlertIndex] = useState(-1);
const [deleteAlertVerification, setDeleteAlertVerification] = useState(false);
useEffect(() => {
if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) {
const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100;
setCurrentAppRunsInPercentage(Math.round(percentage));
setCurrentAppRunsInNumber(userdata.app_execution_limit - userdata.app_execution_usage);
}
if (userdata?.id?.length > 0 && isLoggedIn === false){
setIsLoggedIn(true)
}
}, [userdata]);
const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email);
@@ -182,7 +187,8 @@ const Billing = memo((props) => {
const isCloud =
window.location.host === "localhost:3002" ||
window.location.host === "shuffler.io";
window.location.host === "shuffler.io" ||
window.location.host === "sandbox.shuffler.io";
billingInfo.subscription = {
"active": true,
@@ -419,8 +425,16 @@ const Billing = memo((props) => {
const data = {
org_id: orgId,
email: newBillingEmail,
name: selectedOrganization.name,
description: selectedOrganization.description,
image: selectedOrganization.image,
defaults: selectedOrganization.defaults,
sso_config: selectedOrganization.sso_config,
mfa_required: selectedOrganization.mfa_required,
billing: {
email: newBillingEmail,
AlertThreshold: selectedOrganization?.Billing?.AlertThreshold,
Consultation: selectedOrganization?.Billing?.Consultation,
},
};
@@ -747,13 +761,21 @@ const Billing = memo((props) => {
open={openChangeEmailBox}
onClose={handleCloseChangeEmailBox}
PaperProps={{
style: {
padding: '20px',
borderRadius: '8px',
maxWidth: '500px',
width: '100%'
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: '440px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}
}}
>
<DialogTitle style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>Change Billing Email</DialogTitle>
<DialogContent>
@@ -810,7 +832,7 @@ const Billing = memo((props) => {
color="primary"
style={{
marginTop: !userdata.has_card_available ? 20 : 10,
borderRadius: 8,
borderRadius: 4,
height: 40,
fontSize: 16,
color: userdata.has_card_available ? "#ff8544" : "#1a1a1a",
@@ -843,7 +865,7 @@ const Billing = memo((props) => {
color="primary"
style={{
marginTop: 10,
borderRadius: 8,
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#1a1a1a",
@@ -933,11 +955,19 @@ const Billing = memo((props) => {
const url = `${globalUrl}/api/v1/orgs/${selectedOrganization.id}`;
const data = {
org_id: selectedOrganization.id,
name: selectedOrganization.name,
description: selectedOrganization.description,
image: selectedOrganization.image,
defaults: selectedOrganization.defaults,
sso_config: selectedOrganization.sso_config,
mfa_required: selectedOrganization.mfa_required,
Billing: {
Consultation: {
hours: String(inputHour),
minutes: String(inputMinutes),
},
AlertThreshold: selectedOrganization.Billing.AlertThreshold,
email: selectedOrganization.Billing.email,
}
};
@@ -1030,9 +1060,9 @@ const Billing = memo((props) => {
<div style={{
padding: 20,
// maxWidth: 400,
width: 340,
width: "100%",
height: 480,
backgroundColor: hovered ? "#2b2b2b" : theme.palette.backgroundColor,
backgroundColor: hovered ? "#2b2b2b" : "#1e1e1e",
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
@@ -1123,7 +1153,7 @@ const Billing = memo((props) => {
color="primary"
style={{
marginTop: userdata.support ? 0 : 10,
borderRadius: 8,
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#1A1A1A",
@@ -1142,8 +1172,23 @@ const Billing = memo((props) => {
>
Buy
</Button>
<Dialog open={clickOnBuy} onClose={() => { setClickOnBuy(false) }} PaperProps={{style: {backgroundColor: "rgb(26, 26, 26)"}}}>
<Typography variant="body1" style={{ marginTop: 10, textAlign: 'center', padding: 20, fontSize: 18 }}>
<Dialog open={clickOnBuy} onClose={() => { setClickOnBuy(false) }} PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: '440px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}>
<Typography variant="body1" style={{ paddingTop: 10, textAlign: 'center', padding: 20, fontSize: 18, backgroundColor: theme?.palette?.DialogStyle?.backgroundColor }}>
You will be taken to Stripe to book professional service hours. You can adjust the number of hours on the left side of the Stripe page.
</Typography>
<DialogContent>
@@ -1178,10 +1223,9 @@ const Billing = memo((props) => {
color="primary"
style={{
marginTop: userdata.support ? 5 : 10,
borderRadius: 8,
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#ff8544",
textTransform: 'none',
cursor: getProfessionalServices ? 'pointer' : 'not-allowed',
opacity: getProfessionalServices ? 1 : 0.6,
@@ -1202,13 +1246,23 @@ const Billing = memo((props) => {
fullWidth
style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}
PaperProps={{
style: {
width: 500,
margin: 0,
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: '440px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}
}}
>
<DialogTitle style={{ marginTop: 10, textAlign: 'center', fontWeight: 'bold' }}>
<DialogTitle style={{ paddingTop: 10, textAlign: 'center', fontWeight: 'bold' }}>
Upgrade Consultation Plan
</DialogTitle>
<DialogContent style={{ padding: '24px', }}>
@@ -1244,7 +1298,7 @@ const Billing = memo((props) => {
<Button
variant="contained"
color="primary"
style={{ marginTop: '24px', display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none' }}
style={{ marginTop: '24px', borderRadius: 4, color: "#1a1a1a", backgroundColor: "#ff8544",fontSize: 16, display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none' }}
onClick={handleUpgradeConsultation}
>
Submit Request for {consultationHours} hours
@@ -1305,8 +1359,8 @@ const Billing = memo((props) => {
padding: 20,
height: 480,
// maxWidth: 400,
width: 340,
backgroundColor: hovered ? "#2b2b2b" : theme.palette.backgroundColor,
width: "100%",
backgroundColor: hovered ? "#2b2b2b" : "#1e1e1e",
borderRadius: theme.palette?.borderRadius * 2,
border: "1px solid rgba(255,255,255,0.3)",
marginRight: 10,
@@ -1354,7 +1408,7 @@ const Billing = memo((props) => {
</li>
</ul>
</div>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', marginTop: 30, width: 340 }}>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', marginTop: 30, }}>
<Button
fullWidth
disabled={false}
@@ -1362,12 +1416,13 @@ const Billing = memo((props) => {
color="primary"
style={{
marginTop: 10,
borderRadius: 8,
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#1A1A1A",
textTransform: 'none',
backgroundColor: "#FF8544"
backgroundColor: "#FF8544",
width: "100%",
}}
onClick={() => {
if (Cloud) {
@@ -1390,12 +1445,11 @@ const Billing = memo((props) => {
color="primary"
style={{
marginTop: 10,
borderRadius: 8,
borderRadius: 4,
height: 40,
fontSize: 16,
textTransform: 'none',
color: "#FF8544",
backgroundColor: "#1a1a1a",
width: "100%",
}}
onClick={() => {
if (Cloud) {
@@ -1417,14 +1471,23 @@ const Billing = memo((props) => {
fullWidth
style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}
PaperProps={{
style: {
width: 500,
margin: 0,
backgroundColor: "rgb(26, 26, 26)",
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: '440px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}
}}
>
<DialogTitle style={{ marginTop: 10, textAlign: 'center', fontWeight: 'bold' }}>
<DialogTitle style={{ paddingTop: 10, textAlign: 'center', fontWeight: 'bold' }}>
Private Training
</DialogTitle>
<DialogContent style={{ padding: '24px', }}>
@@ -1460,7 +1523,7 @@ const Billing = memo((props) => {
<Button
variant="contained"
color="primary"
style={{ marginTop: '24px', display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none', color: "#1a1a1a", backgroundColor: "#ff8544" }}
style={{ marginTop: '24px', fontSize: 16, display: 'block', marginLeft: 'auto', marginRight: 'auto', padding: '12px 24px', textTransform: 'none', color: "#1a1a1a", backgroundColor: "#ff8544" }}
onClick={handlePrivateTraining}
>
Submit Request
@@ -1819,7 +1882,6 @@ const Billing = memo((props) => {
setCurrentIndex(findCurrentIndex ? newAlertThresholds.findIndex(threshold => threshold.Email_send === false) : - 1);
toast.info("Alert Threshold deleted successfully. Don't forget to save your changes.");
};
const HandleEditOrgForAlertThreshold = (orgId) => {
// Use the `some` method to check for invalid counts
@@ -1840,6 +1902,12 @@ const Billing = memo((props) => {
const data = {
org_id: orgId,
name: selectedOrganization?.name,
description: selectedOrganization?.description,
image: selectedOrganization?.image,
defaults: selectedOrganization?.defaults,
sso_config: selectedOrganization?.sso_config,
mfa_required: selectedOrganization?.mfa_required,
billing: {
email: BillingEmail,
AlertThreshold: alertThresholds.map(threshold => ({
@@ -1847,6 +1915,7 @@ const Billing = memo((props) => {
percentage: parseInt(threshold.percentage, 10),
count: parseInt(threshold.count, 10),
})),
Consultation: selectedOrganization?.billing?.Consultation,
},
};
@@ -1901,12 +1970,12 @@ const Billing = memo((props) => {
Billing & Licensing
</Typography>}
{clickedFromOrgTab ?
<span style={{ color: "#9E9E9E" }}>{isCloud ?
<span style={{ color: "#9E9E9E", fontSize: 16 }}>{isCloud ?
"Get more out of Shuffle by adding your credit card, such as no App Run limitations, and priority support from our team. We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below."
:
"Shuffle is an Open Source automation platform, and no license is required. We do however offer a Scale license with HA guarantees, along with support hours. By buying a license on https://shuffler.io, you can get access to the license immediately, and if Cloud Syncronisation is enabled, the UI in your local instance will also update."
}</span> :
<Typography variant="body1" color="textSecondary" style={{ marginTop: 0, marginBottom: 10 }}>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 0, marginBottom: 10, fontSize: 16 }}>
{isCloud ?
"Get more out of Shuffle by adding your credit card, such as no App Run limitations, and priority support from our team. We use Stripe to manage subscriptions and do not store any of your billing information. You can manage your subscription and billing information below."
:
@@ -1917,19 +1986,19 @@ const Billing = memo((props) => {
{userdata.support === true ?
<div style={{ marginBottom: 10, marginTop: clickedFromOrgTab ? 16 : null, color: clickedFromOrgTab ? "#F1F1F1" : null }}>
For sales: Create&nbsp;
<a href={"https://docs.google.com/document/d/1N-ZJNn8lWaqiXITrqYcnTt53oXGLNYFEzc5PU-tdAps/copy"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
<a href={"https://docs.google.com/document/d/1N-ZJNn8lWaqiXITrqYcnTt53oXGLNYFEzc5PU-tdAps/copy"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#FF8444" }}>
EU contract
</a>
&nbsp;or&nbsp;
<a href={"https://docs.google.com/document/d/1cF-Cwxt1TcahlLrpl1GH2hFZO4gxppLeYjHW6QB0EzQ/copy"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
<a href={"https://docs.google.com/document/d/1cF-Cwxt1TcahlLrpl1GH2hFZO4gxppLeYjHW6QB0EzQ/copy"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#FF8444" }}>
NOT EU contract
</a>
&nbsp; - &nbsp;
<a href={"https://drive.google.com/drive/folders/1zVvwwkbQXW3p-DJYa0GBDzFo_ZnV_I_5"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
<a href={"https://drive.google.com/drive/folders/1zVvwwkbQXW3p-DJYa0GBDzFo_ZnV_I_5"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#FF8444" }}>
Google Drive Link
</a>
&nbsp; - &nbsp;
<a href={"https://github.com/Shuffle/Shuffle-docs/tree/master/handbook/Sales"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#f85a3e" }}>
<a href={"https://github.com/Shuffle/Shuffle-docs/tree/master/handbook/Sales"} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "#FF8444" }}>
Sales Process (old)
</a>
</div>
@@ -1944,22 +2013,24 @@ const Billing = memo((props) => {
</Typography>
: null}
<div style={{ display: "flex", width: clickedFromOrgTab ? 1030 : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: isChildOrg ? 0 : 580 }} >
<div style={{ display: "flex", width: clickedFromOrgTab ? "100%" : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: isChildOrg ? 0 : "100%", maxWidth: 860, marginTop: 20}} >
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null :
<SubscriptionObject
index={0}
globalUrl={globalUrl}
userdata={userdata}
serverside={serverside}
billingInfo={billingInfo}
stripeKey={stripeKey}
selectedOrganization={selectedOrganization}
subscription={billingInfo.subscription}
highlight={selectedOrganization.subscriptions === undefined || selectedOrganization.subscriptions === null || selectedOrganization.subscriptions.length === 0}
<LicencePopup
serverside={serverside}
removeCookie={removeCookie}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
billingInfo={billingInfo}
isCloud={isCloud}
userdata={userdata}
stripeKey={stripeKey}
{...props}
/>
: !isCloud ?
<span style={{ display: "flex", }}>
<SubscriptionObject
{/* <SubscriptionObject
index={0}
globalUrl={globalUrl}
userdata={userdata}
@@ -1977,8 +2048,22 @@ const Billing = memo((props) => {
],
}}
highlight={true}
/>
<SubscriptionObject
/> */}
<LicencePopup
{...props}
serverside={serverside}
removeCookie={removeCookie}
isLoaded={isLoaded}
isLoggedIn={isLoggedIn}
globalUrl={globalUrl}
selectedOrganization={selectedOrganization}
billingInfo={billingInfo}
isCloud={isCloud}
userdata={userdata}
stripeKey={stripeKey}
/>
{/* <SubscriptionObject
index={1}
globalUrl={globalUrl}
userdata={userdata}
@@ -1996,7 +2081,7 @@ const Billing = memo((props) => {
],
}}
highlight={false}
/>
/> */}
</span>
: null}
@@ -2255,14 +2340,14 @@ const Billing = memo((props) => {
</div>
) : null*/}
{!isChildOrg && isCloud && (
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 10 }}>
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 50, maxWidth: 860 }}>
<Typography style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}>
Professional Services
</Typography>
<Typography color="textSecondary" style={{fontSize: 16,}}>
We offer priority support through consultations and training to help you make the most of our product. If you have any questions, please reach out to us at support@shuffler.io.
</Typography>
<div style={{ display: 'flex', flexDirection: 'row', marginTop: 5 }}>
<div style={{ display: 'flex', flexDirection: 'row', marginTop: 5, }}>
{billingInfo.subscription !== undefined && billingInfo.subscription !== null ? (
isChildOrg ? null : (
<ConsultationManagement
@@ -2276,7 +2361,7 @@ const Billing = memo((props) => {
</div>
</div>
)}
<div style={{ marginTop: isCloud && 40, marginLeft: 10 }}>
<div style={{ marginTop: 40, marginLeft: 10 }}>
<Typography
style={{ marginBottom: 5, fontSize: 24, fontWeight: "bold" }}
>
@@ -2323,43 +2408,51 @@ const Billing = memo((props) => {
<div style={{ marginTop: 15 }}>
{alertThresholds.map((threshold, index) => (
<div key={index} style={{ display: 'flex', alignItems: 'center' }}>
<TextField
style={{
marginTop: 10,
backgroundColor: "#212121",
width: 250,
height: 50,
}}
InputProps={{
style: {
height: 50,
color: 'white',
},
endAdornment: '%',
}}
InputLabelProps={{
shrink: undefined,
}}
color="primary"
fullWidth
label="Alert threshold (%)"
type="number"
value={threshold.percentage}
onChange={(e) => updateAlertThreshold(index, 'percentage', e.target.value)}
margin="normal"
variant="outlined"
inputProps={{
max: 100,
}}
/>
<TextField
style={{
marginTop: 10,
backgroundColor: "#212121",
width: 250,
marginTop: 10,
height: 50,
backgroundColor: "#212121",
width: 250,
marginLeft: 15,
}}
InputProps={{
style: {
height: 50,
color: 'white',
},
endAdornment: '%',
style: {
height: 50,
color: 'white',
},
}}
color="primary"
fullWidth
label="Alert threshold (%)"
type="number"
value={threshold.percentage}
onChange={(e) => updateAlertThreshold(index, 'percentage', e.target.value)}
margin="normal"
variant="outlined"
inputProps={{
max: 100,
}}
/>
<TextField
style={{
marginTop: 10,
backgroundColor: "#212121",
width: 250,
marginLeft: 15,
}}
InputProps={{
style: {
height: 50,
color: 'white',
},
InputLabelProps={{
shrink: undefined,
}}
color="primary"
fullWidth
@@ -2370,34 +2463,67 @@ const Billing = memo((props) => {
margin="normal"
variant="outlined"
/>
<span style={{ marginLeft: alertThresholds[index].Email_send === true ? 10 : 35, color: 'green' }}>{alertThresholds[index].Email_send === true && <Tooltip title="We have already sent alert for this threshold."><CheckCircle /></Tooltip>}</span>
{
alertThresholds.length > 1 &&
(
<Button
disableRipple
disableElevation
sx={{
padding: 0,
'&:hover': {
backgroundColor: 'transparent',
},
}}
onClick={() => { setDeleteAlertVerification(true) }}
>
<DeleteIcon sx={{ color: theme.palette.secondary.main }} />
</Button>
)
}
<Dialog open={deleteAlertVerification} onClose={() => setDeleteAlertVerification(false)} sx={{ '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)', }, }}>
<DialogTitle>Are you sure you want to delete this threshold?</DialogTitle>
<DialogActions>
<Button style={{ textTransform: 'none', fontSize: 16 }} color="primary" onClick={() => setDeleteAlertVerification(false)}>Cancel</Button>
<Button style={{ textTransform: 'none', fontSize: 16 }} color="secondary" onClick={() => { handleDeleteAlertThreshold(index); setDeleteAlertVerification(false) }} >Delete</Button>
</DialogActions>
</Dialog>
</div>
<span
style={{
marginLeft: alertThresholds[index].Email_send === true ? 10 : 35,
color: 'green',
}}
>
{alertThresholds[index].Email_send === true && (
<Tooltip title="We have already sent alert for this threshold.">
<CheckCircle />
</Tooltip>
)}
</span>
{alertThresholds.length > 1 && (
<Button
disableRipple
disableElevation
sx={{
padding: 0,
'&:hover': {
backgroundColor: 'transparent',
},
}}
onClick={() => {
setDeleteAlertVerification(true);
setDeleteAlertIndex(index);
}}
>
<DeleteIcon sx={{ color: theme.palette.secondary.main }} />
</Button>
)}
<Dialog
open={deleteAlertVerification}
onClose={() => setDeleteAlertVerification(false)}
sx={{
'& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.3)' },
}}
>
<DialogTitle>Are you sure you want to delete this threshold?</DialogTitle>
<DialogActions>
<Button
style={{ textTransform: 'none', fontSize: 16 }}
color="primary"
onClick={() => setDeleteAlertVerification(false)}
>
Cancel
</Button>
<Button
style={{ textTransform: 'none', fontSize: 16 }}
color="secondary"
onClick={() => {
handleDeleteAlertThreshold(deleteAlertIndex);
setDeleteAlertVerification(false);
}}
>
Delete
</Button>
</DialogActions>
</Dialog>
</div>
))}
</div>
</div>
@@ -2449,10 +2575,8 @@ const PaddingWrapper = memo(({ clickedFromOrgTab, children }) => {
: "auto",
padding: "27px 10px 19px 27px",
backgroundColor: '#212121',
borderRadius: '16px',
height: '100%',
boxSizing: 'border-box',
borderLeft: '1px solid #494949',
overflow: 'hidden',
maxHeight: "1700px", overflowY: "auto",scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'
}), [clickedFromOrgTab]);
+123 -29
View File
@@ -648,12 +648,13 @@ const AppStats = (defaultprops) => {
All shown statistics are gathered from <a
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
target="_blank"
style={{ textDecoration: "none", color: "#f85a3e",}}
style={{ textDecoration: "none", color: "#FF8444",}}
>Your Organisation Statistics. </a>
It exists to give you more insight into your workflows, and to understand your utilization of the Shuffle platform. <b>The billing tracker is in Beta, and is always calculated manually before being invoiced.</b>
</Typography>
<div style={{display: "flex", textAlign: "center",}}>
<div style={{display: "flex", flexDirection: "column", textAlign: "center",}}>
<div style={{flexDirection: "row", }}>
{filteredStatistics !== undefined ?
<div style={{flex: 1, display: "flex", textAlign: "center",}}>
<Tooltip title={
@@ -726,40 +727,135 @@ const AppStats = (defaultprops) => {
</Tooltip>
</div>
: null}
</div>
{clickedFromOrgTab? (
<LocalizationProvider dateAdapter={AdapterDayjs} style={{ flex: 1 }}>
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
<div style={{ flex: 1, maxWidth: "200px", }}>
<div style={{ display: "flex", flexDirection: "row", width: "100%", gap: "10px", justifyContent: 'center', alignItems: 'center', paddingTop: 10 }}>
<div
style={{
display: "flex",
flexDirection: "row",
flex: 1,
alignItems: "center",
}}
>
<Typography
style={{
marginLeft: 10,
marginRight: 10,
fontSize: 16,
whiteSpace: "nowrap",
}}
color="textSecondary"
>
Search from
</Typography>
<DateTimePicker
sx={{
marginTop: 1,
marginLeft: 1,
}}
ampm={false}
label="Search from"
format="YYYY-MM-DD HH:mm:ss"
value={startTime}
onChange={handleStartTimeChange}
renderInput={(params) => <TextField {...params} />}
slotProps={{
textField: {
sx: {
"& .MuiOutlinedInput-root": {
"& fieldset": {
borderColor: "#494949 !important",
borderWidth: "1px !important",
},
"&:hover fieldset": {
borderColor: "#FFFFFF !important",
},
"&.Mui-focused fieldset": {
borderColor: "#FFFFFF !important",
},
height: "35px",
fontSize: 16,
color: "#c8c8c8",
},
},
},
}}
sx={{
"& .MuiInputBase-root": {
height: "35px",
minHeight: "35px",
},
"& .MuiInputBase-input": {
height: "35px",
padding: "0 14px",
boxSizing: "border-box",
color: "#c8c8c8",
fontSize: 16,
}
}}
ampm={false}
format="YYYY-MM-DD HH:mm:ss"
value={startTime}
onChange={handleStartTimeChange}
/>
</div>
<div style={{ flex: 1, maxWidth: "200px",}}>
</div>
<div
style={{
display: "flex",
flexDirection: "row",
flex: 1,
alignItems: "center",
}}
>
<Typography
style={{
marginLeft: 10,
marginRight: 10,
fontSize: 16,
whiteSpace: "nowrap",
}}
color="textSecondary"
>
Search until
</Typography>
<DateTimePicker
sx={{
slotProps={{
textField: {
sx: {
"& .MuiOutlinedInput-root": {
"& fieldset": {
borderColor: "#494949 !important",
borderWidth: "1px !important",
},
"&:hover fieldset": {
borderColor: "#FFFFFF !important",
},
"&.Mui-focused fieldset": {
borderColor: "#FFFFFF !important",
},
height: "35px",
fontSize: 16,
color: "#c8c8c8",
},
},
},
}}
sx={{
marginTop: 1,
marginLeft: 1,
}}
ampm={false}
label="Search until"
format="YYYY-MM-DD HH:mm:ss"
value={endTime}
onChange={handleEndTimeChange}
renderInput={(params) => <TextField {...params} />}
"& .MuiInputBase-root": {
height: "35px",
minHeight: "35px",
},
"& .MuiInputBase-input": {
height: "35px",
padding: "0 14px",
boxSizing: "border-box",
color: "#c8c8c8",
fontSize: 16,
}
}}
ampm={false}
format="YYYY-MM-DD HH:mm:ss"
value={endTime}
onChange={handleEndTimeChange}
/>
</div>
</div>
</div>
</LocalizationProvider>
</LocalizationProvider>
):(
<LocalizationProvider dateAdapter={AdapterDayjs} style={{flex: 1, }}>
<div style={{display: "flex", flexDirection: "column", }}>
@@ -794,8 +890,6 @@ const AppStats = (defaultprops) => {
</div>
</LocalizationProvider>
)}
</div>
{appRuns === undefined ?
+11 -10
View File
@@ -412,7 +412,7 @@ const CacheView = memo((props) => {
</span>
</div>
<Button
style={{backgroundColor: isSelectedDataStore? "#ff8544":null, fontSize: 16, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null, color:isSelectedDataStore?"#1a1a1a":null, borderRadius:isSelectedDataStore?8:null, width:isSelectedDataStore?162:null, height:isSelectedDataStore?40:null}}
style={{backgroundColor: isSelectedDataStore? "#ff8544":null, fontSize: 16, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null, color:isSelectedDataStore?"#1a1a1a":null, borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?162:null, height:isSelectedDataStore?40:null}}
variant="contained"
color="primary"
onClick={() =>{
@@ -425,7 +425,7 @@ const CacheView = memo((props) => {
Add Cache
</Button>
<Button
style={{ marginLeft: 5, marginRight: 15, backgroundColor: isSelectedDataStore?"#2F2F2F":null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null,borderRadius:isSelectedDataStore?8:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
style={{ marginLeft: 16, marginRight: 15, backgroundColor: isSelectedDataStore?"#2F2F2F":null, boxShadow: isSelectedDataStore ? "none":null,textTransform: isSelectedDataStore ? 'capitalize':null,borderRadius:isSelectedDataStore?4:null, width:isSelectedDataStore?81:null, height:isSelectedDataStore?40:null, }}
variant="contained"
color="primary"
onClick={() => listOrgCache(orgId)}
@@ -440,7 +440,7 @@ const CacheView = memo((props) => {
/>}
<div
style={{
borderRadius: 8,
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
width: "100%",
@@ -450,7 +450,7 @@ const CacheView = memo((props) => {
>
<List
style={{
borderRadius: 8,
borderRadius: 4,
paddingBottom: 0,
tableLayout: "auto",
display: "table",
@@ -465,7 +465,7 @@ const CacheView = memo((props) => {
primary={header}
style={{
display: "table-cell",
padding: "0px 8px 8px 8px",
padding: index === 0 ? "0px 8px 8px 15px": "0px 8px 8px 8px",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
@@ -518,13 +518,13 @@ const CacheView = memo((props) => {
const validate = validateJson(data.value);
return (
<ListItem key={index} style={{display:'table-row', backgroundColor: bgColor, maxHeight: 300, overflow: "auto", borderBottomLeftRadius: listCache?.length - 1 === index ? 8 : 0, borderBottomRightRadius: listCache?.length - 1 === index ? 8 : 0}}>
<ListItem key={index} style={{display:'table-row', backgroundColor: bgColor, maxHeight: 300, overflow: "auto", borderBottomLeftRadius: listCache?.length - 1 === index ? 8 : 0, borderBottomRightRadius: listCache?.length - 1 === index ? 8 : 0,}}>
<ListItemText
style={{
display: "table-cell",
overflow: "hidden",
padding: 8,
verticalAlign: "middle",
padding: "8px 8px 8px 15px"
}}
primary={data.key}
/>
@@ -533,9 +533,8 @@ const CacheView = memo((props) => {
display: "table-cell",
overflowY: "auto",
overflowX: "auto",
border: "1px solid rgba(255,255,255,0.7)",
borderRadius: 6,
backgroundColor: "#151515",
borderRadius: 4,
padding: "15px 5px",
maxHeight: 300,
verticalAlign: "middle",
}}
@@ -547,6 +546,8 @@ const CacheView = memo((props) => {
padding: 5,
maxHeight: 300,
overflowY: "auto",
backgroundColor: "#151515",
border: "1px solid rgba(255,255,255,0.7)",
}}
collapsed={true}
enableClipboard={(copy) => {
+37 -23
View File
@@ -690,7 +690,7 @@ const Files = memo((props) => {
onClick={() => {
upload.click();
}}
style={{backgroundColor: isSelectedFiles?'#ff8544':null, color:isSelectedFiles?"#212121":null, textTransform: 'none',fontSize: 16, borderRadius:isSelectedFiles?8:null, width:isSelectedFiles?143:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null,}}
style={{backgroundColor: isSelectedFiles?'#ff8544':null, color:isSelectedFiles?"#212121":null, textTransform: 'none',fontSize: 16, borderRadius:isSelectedFiles?4:null, width:isSelectedFiles?143:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null,}}
>
Upload files
</Button>
@@ -711,7 +711,7 @@ const Files = memo((props) => {
}}
/>
<Button
style={{ marginLeft: 16, marginRight: 15, backgroundColor:isSelectedFiles?"#2F2F2F":null,borderRadius:isSelectedFiles?8:null, width:isSelectedFiles?81:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null, }}
style={{ marginLeft: 16, marginRight: 15, backgroundColor:isSelectedFiles?"#2F2F2F":null,borderRadius:isSelectedFiles?4:null, width:isSelectedFiles?81:null, height:isSelectedFiles?35:null, boxShadow: isSelectedFiles?'none':null, }}
variant="contained"
color="primary"
onClick={() => getFiles(selectedCategory)}
@@ -788,7 +788,7 @@ const Files = memo((props) => {
:
<Tooltip title={"Add new file category"} style={{}} aria-label={""}>
<Button
style={{ marginLeft: 5, marginRight: 15, width: 169, height: 35, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
style={{ marginLeft: 5, marginRight: 15, width: 169, height: 35, borderRadius: 4, backgroundColor: "#494949", textTransform: 'none', fontSize: 16, color: "#f1f1f1" }}
color="primary"
onClick={() => {
setRenderTextBox(true);
@@ -838,7 +838,7 @@ const Files = memo((props) => {
/>}
<div
style={{
borderRadius: 8,
borderRadius: 4,
marginTop: 24,
border: "1px solid #494949",
width: "100%",
@@ -862,7 +862,7 @@ const Files = memo((props) => {
primary={header}
style={{
display: "table-cell",
padding: "0px 8px 8px 8px",
padding: index === 0 ? "0px 8px 8px 15px": "0px 8px 8px 8px",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
borderBottom: "1px solid #494949",
@@ -870,7 +870,7 @@ const Files = memo((props) => {
}}
primaryTypographyProps={{
style: {
paddingLeft: index === 3 ? 80 : 10,
paddingLeft: 10,
}
}}
/>
@@ -956,15 +956,13 @@ const Files = memo((props) => {
<ListItemText
primaryTypographyProps={{
style: {
display: 'table-cell',
maxWidth: "170px",
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
padding: 8
},
padding: "8px 8px 8px 20px"
}
}}
primary={file.filename}
/>
<ListItemText
@@ -1005,6 +1003,7 @@ const Files = memo((props) => {
>
<OpenInNewIcon
style={{
width: 24, height: 24,
color:
file.workflow_id !== "global"
? "#FF8444"
@@ -1044,7 +1043,7 @@ const Files = memo((props) => {
style={{
display: 'table-cell',
overflow: "hidden",
textAlign:isSelectedFiles?"left":null,
textAlign:isSelectedFiles?"center":null,
color: file.status === "active" ? "#2BC07E" : "#FD4C62"
}}
/>
@@ -1074,7 +1073,7 @@ const Files = memo((props) => {
}}
>
<img src="/icons/editIcon.svg" alt="edit icon"
style={{color: iseditable ? "white" : "grey",}}
style={{color: iseditable ? "white" : "grey", width: 24, height: 24}}
/>
</IconButton>
</span>
@@ -1120,6 +1119,7 @@ const Files = memo((props) => {
>
<img src="/icons/downloadIcon.svg" alt="download icon"
style={{
width: 24, height: 24,
color:
file.status === "active"
? "white"
@@ -1144,7 +1144,7 @@ const Files = memo((props) => {
toast(file.id + " copied to clipboard");
}}
>
<img src="/icons/copyIcon.svg" alt="copy icon" style={{ color: "white" }} />
<img src="/icons/copyIcon.svg" alt="copy icon" style={{ color: "white", width: 24, height: 24 }} />
</IconButton>
</Tooltip>
<Tooltip
@@ -1155,21 +1155,35 @@ const Files = memo((props) => {
<span>
<IconButton
disabled={file.status !== "active"}
style = {{padding: "6px"}}
style={{ padding: "6px" }}
onClick={() => {
deleteFile(file)
deleteFile(file);
}}
>
<img src="/icons/deleteIcon.svg" alt="delete icon"
style={{
color:
file.status === "active"
? "white"
: "grey",
}}
<svg
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
style={{
stroke: file.status === "active" ? "#fd4c62" : "#c8c8c8",
}}
>
<path
d="M5 7.20001H6.6H19.4"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
<path
d="M17.7996 7.2V18.4C17.7996 18.8243 17.631 19.2313 17.331 19.5314C17.0309 19.8314 16.624 20 16.1996 20H8.19961C7.77526 20 7.3683 19.8314 7.06824 19.5314C6.76818 19.2313 6.59961 18.8243 6.59961 18.4V7.2M8.99961 7.2V5.6C8.99961 5.17565 9.16818 4.76869 9.46824 4.46863C9.7683 4.16857 10.1753 4 10.5996 4H13.7996C14.224 4 14.6309 4.16857 14.931 4.46863C15.231 4.76869 15.3996 5.17565 15.3996 5.6V7.2"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
</svg>
</IconButton>
</span>
</span>
</Tooltip>
</span>
style={{
+42 -27
View File
@@ -229,7 +229,7 @@ useEffect(() => {
setOpenautomateTab(false);
setOpenSecurityTab(true);
setCurrentOpenTab("security");
} else if ((lastTabOpenByUser === "usecases" && currentPath.includes("/usecases2")) || currentPath.includes("/usecases2")) {
} else if ((lastTabOpenByUser === "usecases" && currentPath.includes("/usecases")) || currentPath.includes("/usecases")) {
setOpenautomateTab(true);
setOpenSecurityTab(false);
setCurrentOpenTab("usecases");
@@ -466,6 +466,7 @@ useEffect(() => {
onClick={(event) => {
handleClose();
}}
style={{fontSize: 18}}
>
<BusinessIcon style={{ marginRight: 5 }} /> Organization
</MenuItem>
@@ -475,6 +476,7 @@ useEffect(() => {
onClick={(event) => {
handleClose();
}}
style={{fontSize: 18}}
>
<SettingsIcon style={{ marginRight: 5 }} /> Account
</MenuItem>
@@ -486,6 +488,7 @@ useEffect(() => {
onClick={(event) => {
handleClose();
}}
style={{fontSize: 18}}
>
<NotificationsIcon style={{ marginRight: 5 }} /> Notifications ({
notifications === undefined || notifications === null ? 0 :
@@ -498,6 +501,7 @@ useEffect(() => {
onClick={(event) => {
handleClose();
}}
style={{fontSize: 18}}
>
<LightbulbIcon style={{ marginRight: 5 }} /> Use Cases
</MenuItem>
@@ -510,23 +514,25 @@ useEffect(() => {
onClick={(event) => {
handleClose();
}}
style={{fontSize: 18}}
>
<HelpOutlineIcon style={{ marginRight: 5 }} /> About
</MenuItem>
</Link>
<MenuItem
style={{ color: "white" }}
style={{ color: "white", fontSize: 18 }}
onClick={(event) => {
handleClickLogout();
event.preventDefault();
handleClose();
}}
>
<MeetingRoomIcon style={{ marginRight: 5 }} /> &nbsp;Logout
</MenuItem>
<Divider style={{ marginBottom: 10, }} />
<Typography variant="body2" color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, }}>
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
Version: 2.0.0-beta
</Typography>
</Menu>
@@ -717,6 +723,7 @@ useEffect(() => {
width: expandLeftNav ? "100%" : 30,
padding: 0,
},
fontSize: 18
};
const modalView = (
@@ -936,6 +943,7 @@ useEffect(() => {
display: "flex",
flexDirection: "column",
padding: expandLeftNav ? "0 16px" : "0px 8px",
gap: 1.2,
}}
>
<Box sx={{ display: "flex", flexDirection: "row", marginTop: 2.5, width: expandLeftNav ? "100%" : 48, padding: "0px", }}>
@@ -950,18 +958,18 @@ useEffect(() => {
variant="text"
style={{
...ButtonStyle,
backgroundColor: ((currentOpenTab === "automate" && currentPath.includes("/dashboards/automate"))|| (!expandLeftNav && (currentPath === "/workflows" || currentPath === "/usecases2" || currentPath.includes("/search"))))? "#2f2f2f": "transparent",
backgroundColor: ((currentOpenTab === "automate" && currentPath.includes("/dashboards/automate"))|| (!expandLeftNav && (currentPath === "/workflows" || currentPath === "/usecases" || currentPath.includes("/search"))))? "#2f2f2f": "transparent",
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
}}
onMouseOut={(event)=>{
event.currentTarget.style.backgroundColor = ((currentOpenTab === "automate" && currentPath.includes("/dashboards/automate"))|| (!expandLeftNav && (currentPath === "/workflows" || currentPath === "/usecases2" || currentPath.includes("/search"))))? "#2f2f2f": "transparent";
event.currentTarget.style.backgroundColor = ((currentOpenTab === "automate" && currentPath.includes("/dashboards/automate"))|| (!expandLeftNav && (currentPath === "/workflows" || currentPath === "/usecases" || currentPath.includes("/search"))))? "#2f2f2f": "transparent";
}}
>
<img
src={detectionIcon}
style={{ width: 16, height: 16, marginRight: expandLeftNav ? 10 : 0 }}
style={{ width: 18, height: 18, marginRight: expandLeftNav ? 10 : 0 }}
alt="Automate Icon"
/>
<span
@@ -1020,7 +1028,7 @@ useEffect(() => {
display: "flex",
flexDirection: "column",
paddingLeft: 16,
gap: 4,
gap: 10,
}}
>
<Link to="/usecases" style={hrefStyle}>
@@ -1032,8 +1040,9 @@ useEffect(() => {
style={{
width: "100%",
height: 35,
color: "#C8C8C8",
color: currentOpenTab === "usecases" && expandLeftNav && currentPath.includes("/usecases") ? "#FFFFFF" : "#C8C8C8",
justifyContent: "flex-start",
fontSize: 18,
textTransform: "none",
backgroundColor: currentOpenTab === "usecases" && expandLeftNav && currentPath.includes("/usecases")? "#2f2f2f": "transparent",
marginLeft: 16
@@ -1046,13 +1055,13 @@ useEffect(() => {
}}
disableRipple={expandLeftNav ? false : true}
>
<span style={{display: expandLeftNav ? "inline" : "none",opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", position: 'relative', left: !expandLeftNav ? 10: 0, marginRight: 10, fontSize: 16 }}></span>{" "}
<span style={{display: expandLeftNav ? "inline" : "none",opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", position: 'relative', left: !expandLeftNav ? 10: 0, marginRight: 10, fontSize: 18 }}></span>{" "}
<span
style={{
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
color: currentOpenTab === "usecases" && currentPath.includes("/usecases3") ? "#F1F1F1" : "#C8C8C8",
color: currentOpenTab === "usecases" && currentPath.includes("/usecases") ? "#FFFFFF" : "#C8C8C8",
}}
>
Usecases
@@ -1068,11 +1077,12 @@ useEffect(() => {
style={{
width: "100%",
height: 35,
color: "#C8C8C8",
color: currentOpenTab === "workflows" && currentPath === "/workflows" ? "#FFFFFF" : "#C8C8C8",
justifyContent: "flex-start",
textTransform: "none",
backgroundColor: currentOpenTab === "workflows" && currentPath === "/workflows" && expandLeftNav? "#2f2f2f": "transparent",
marginLeft: 16
marginLeft: 16,
fontSize: 18
}}
disableRipple={expandLeftNav ? false : true}
onMouseOver={(event)=>{
@@ -1082,13 +1092,13 @@ useEffect(() => {
event.currentTarget.style.backgroundColor = currentOpenTab === "workflows" && currentPath === "/workflows" && expandLeftNav? "#2f2f2f": "transparent";
}}
>
<span style={{display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", position: 'relative', left: !expandLeftNav ? 10: 0, marginRight: 10, fontSize: 16 }}></span>{" "}
<span style={{display: expandLeftNav ? "inline" : "none", opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", position: 'relative', left: !expandLeftNav ? 10: 0, marginRight: 10, fontSize: 18 }}></span>{" "}
<span
style={{
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
color: currentOpenTab === "workflows" && currentPath === "/workflows" ? "#F1F1F1" : "#C8C8C8",
color: currentOpenTab === "workflows" && currentPath === "/workflows" ? "#FFFFFF" : "#C8C8C8",
}}
>
Workflows
@@ -1104,11 +1114,12 @@ useEffect(() => {
style={{
width: "100%",
height: 35,
color: "#C8C8C8",
color: currentOpenTab === "apps" && currentPath.includes("/apps") ? "#FFFFFF" : "#C8C8C8",
justifyContent: "flex-start",
textTransform: "none",
backgroundColor: currentOpenTab === "apps" && expandLeftNav && currentPath.includes("/apps2") ? "#2f2f2f": "transparent",
marginLeft: 16
marginLeft: 16,
fontSize: 18
}}
onMouseOver={(event)=>{
event.currentTarget.style.backgroundColor = "#2f2f2f";
@@ -1118,13 +1129,13 @@ useEffect(() => {
}}
disableRipple={expandLeftNav ? false : true}
>
<span style={{display: expandLeftNav ? "inline" : "none",opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", position: 'relative', left: !expandLeftNav ? 10: 0, marginRight: 10, fontSize: 16 }}></span>{" "}
<span style={{display: expandLeftNav ? "inline" : "none",opacity: expandLeftNav ? 1 : 0, transition: "opacity 0.3s ease", position: 'relative', left: !expandLeftNav ? 10: 0, marginRight: 10, fontSize: 18 }}></span>{" "}
<span
style={{
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
color: currentOpenTab === "apps" && currentPath.includes("/apps2") ? "#F1F1F1" : "#C8C8C8",
color: currentOpenTab === "apps" && currentPath.includes("/apps") ? "#FFFFFF" : "#C8C8C8",
}}
>
Apps
@@ -1178,12 +1189,11 @@ useEffect(() => {
? "#2f2f2f"
: "transparent";
}}
disabled={!userdata?.support}
>
<ShieldOutlinedIcon
style={{
width: 16,
height: 16,
width: 18,
height: 18,
marginRight: expandLeftNav ? 10 : 0,
color: userdata?.support ? "inherit" : "#6F6F6F",
}}
@@ -1192,6 +1202,7 @@ useEffect(() => {
style={{
display: expandLeftNav ? "inline" : "none",
marginRight: "auto",
fontSize: 18,
color: userdata?.support
? currentOpenTab === "security" && currentPath.includes("/security")
? "#F1F1F1"
@@ -1278,7 +1289,7 @@ useEffect(() => {
}}
disabled={userdata?.support === false}
>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 16 }}>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}>
</span>
<span
@@ -1286,6 +1297,7 @@ useEffect(() => {
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
fontSize: 18,
color:
userdata?.support && currentOpenTab === "detection" && currentPath.includes("/detection")
? "#F1F1F1"
@@ -1329,7 +1341,7 @@ useEffect(() => {
cursor: userdata?.support ? "pointer" : "not-allowed",
}}
>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 16 }}>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}>
</span>
<span
@@ -1337,6 +1349,7 @@ useEffect(() => {
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
fontSize: 18,
color:
userdata?.support && currentOpenTab === "response" && currentPath.includes("/response")
? "#F1F1F1"
@@ -1380,7 +1393,7 @@ useEffect(() => {
cursor: userdata?.support ? "pointer" : "not-allowed",
}}
>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 16 }}>
<span style={{ position: "relative", left: !expandLeftNav ? 10 : 0, marginRight: 10, fontSize: 18 }}>
</span>
<span
@@ -1388,6 +1401,7 @@ useEffect(() => {
display: expandLeftNav ? "inline" : "none",
opacity: expandLeftNav ? 1 : 0,
transition: "opacity 0.3s ease",
fontSize: 18,
color:
userdata?.support && currentOpenTab === "response" && currentPath.includes("/response")
? "#F1F1F1"
@@ -1458,7 +1472,7 @@ useEffect(() => {
<Typography
style={{
color: "#CDCDCD",
fontSize: 14,
fontSize: 18,
padding: "8px 8px",
marginBottom: 10,
}}
@@ -1688,7 +1702,7 @@ useEffect(() => {
},
transition: "opacity 0.3s ease",
textTransform: "none",
fontSize: 16,
fontSize: 18,
}}
disableElevation
disableRipple
@@ -1718,13 +1732,14 @@ useEffect(() => {
style={{
color: "#CDCDCD",
margin: "0 5px",
fontSize: 16,
fontSize: 18,
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
maxWidth: 150,
textAlign: "center",
marginLeft: 10,
}}
>
{userdata?.username}
+211 -30
View File
@@ -24,6 +24,7 @@ import {
DialogActions,
CardContent,
ButtonGroup,
DialogContentText,
} from "@mui/material";
import { useNavigate, Link } from "react-router-dom";
@@ -42,19 +43,17 @@ import {
import { typecost, typecost_single, } from "../views/HandlePaymentNew.jsx";
import { handlePayasyougo } from "../views/HandlePaymentNew.jsx"
import Billing from "./Billing.jsx";
const LicencePopup = (props) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile } = props;
const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile, selectedOrganization, isCloud } = props;
//const alert = useAlert();
let navigate = useNavigate();
const isCloud = typeof window === 'undefined' || window === undefined || window.location === undefined ? true : window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false);
const [dealList, setDealList] = React.useState([]);
const [dealName, setDealName] = React.useState("");
const [dealAddress, setDealAddress] = React.useState("");
const [dealType, setDealType] = React.useState("MSSP");
const [selectedOrganization, setSelectedOrganization] = React.useState({});
const [dealCountry, setDealCountry] = React.useState("United States");
const [dealCurrency, setDealCurrency] = React.useState("USD");
const [dealStatus, setDealStatus] = React.useState("initiated");
@@ -63,8 +62,8 @@ const LicencePopup = (props) => {
const [dealerror, setDealerror] = React.useState("");
const [variant, setVariant] = useState(0)
const [shuffleVariant, setShuffleVariant] = useState(isCloud ? 0 : 1)
const [BillingEmail, setBillingEmail] = useState(selectedOrganization?.Billing?.Email);
const [openChangeEmailBox, setOpenChangeEmailBox] = useState(false);
// const parsedFields = maxFields === undefined ? 300 : maxFields
const initialShuffleVariant = isCloud ? 0 : 1;
const [paymentType, setPaymentType] = useState(0)
@@ -78,6 +77,12 @@ const LicencePopup = (props) => {
const [calculatedCost, setCalculatedCost] = useState("$600")
const [selectedValue, setSelectedValue] = useState(100)
useEffect(() => {
if(selectedOrganization?.Billing?.Email !== BillingEmail) {
setBillingEmail(selectedOrganization?.Billing?.Email);
}
}, [selectedOrganization])
// Onprem
const [calculatedCores, setCalculatedCores] = useState('600')
const [onpremSelectedValue, setOnpremSelectedValue] = useState(8)
@@ -101,7 +106,8 @@ const LicencePopup = (props) => {
"description": "Pay as you go",
"features": [
"Basic Support",
"Limited App Runs (10.000)",
"Includes 10.000 app run/month for free. ",
"Pay for what you use with no minimum commitment and cancel anytime."
],
"limit": 10000,
}
@@ -137,13 +143,14 @@ const LicencePopup = (props) => {
})
}
const SubscriptionObject = (props) => {
const { globalUrl, index, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, subscription, highlight, } = props;
const [signatureOpen, setSignatureOpen] = React.useState(false);
const [tosChecked, setTosChecked] = React.useState(subscription.eula_signed)
const [hovered, setHovered] = React.useState(false)
const [newBillingEmail, setNewBillingEmail] = useState('');
var top_text = "Base Cloud Access"
if (subscription.limit === undefined && subscription.level === undefined || subscription.level === null || subscription.level === 0) {
subscription.name = "Enterprise"
@@ -191,6 +198,58 @@ const LicencePopup = (props) => {
// newPaperstyle.border = "1px solid #f85a3e"
}
const handleClickOpen = () => {
setOpenChangeEmailBox(true);
};
const HandleChangeBillingEmail = (orgId) => {
const email = newBillingEmail;
const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if (!emailPattern.test(email)) {
toast("Please enter a valid email address");
return;
} else {
setNewBillingEmail(email);
}
toast("Updating billing email. Please Wait")
const data = {
org_id: orgId,
email: newBillingEmail,
billing: {
email: newBillingEmail,
},
};
const url = `${globalUrl}/api/v1/orgs/${orgId}/billing`;
fetch(url, {
method: "POST",
body: JSON.stringify(data),
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status !== 200) {
console.log("Bad status code in get org:", response.status);
}
return response.json();
}).then((responseJson) => {
if (responseJson.success === true) {
toast.success("Successfully updated billing email");
setBillingEmail(newBillingEmail);
setOpenChangeEmailBox(false);
} else {
toast.error("Failed to update billing email. Please try again.");
}
})
.catch((error) => {
console.log("Error getting org:", error);
});
}
return (
<Tooltip
style={{ borderRadius: theme.palette?.borderRadius, }}
@@ -308,7 +367,7 @@ const LicencePopup = (props) => {
color="primary"
/>
: null}
<Typography variant="h6" style={{ marginTop: 10, marginBottom: 10, flex: 5, }}>
<Typography variant="h6" style={{ marginTop: 10, marginBottom: 10, flex: 5, whiteSpace: 'nowrap' }}>
{top_text}
</Typography>
@@ -343,7 +402,7 @@ const LicencePopup = (props) => {
</div>
<Divider />
<div>
<Typography variant="body1" style={{ marginTop: 20, }}>
<Typography variant="body1" style={{ marginTop: 20,whiteSpace: 'nowrap' }}>
{subscription.name}
</Typography>
@@ -443,21 +502,141 @@ const LicencePopup = (props) => {
})
: null}
</ul>
{isCloud ?
<span>Billing email: {selectedOrganization.org}</span>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
{subscription.name.includes("Scale") ?
""
:
userdata.has_card_available === true ?
"While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month."
:
isCloud ?
`You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.`
:
`You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.`
}
</Typography>
{isCloud && (userdata.has_card_available === true || selectedOrganization?.Billing?.Email?.length > 0 )?
<div>
<span>Billing email: {BillingEmail}</span>
<Button
variant="contained"
onClick={handleClickOpen}
style={{
width: 'auto',
height: 20,
textTransform: 'none',
marginLeft: 'auto',
backgroundColor: 'transparent',
color: '#FF8544',
boxShadow: 'none',
border: 'none',
cursor: 'pointer',
transition: 'background-color 0.3s, color 0.3s',
}}
>
Change
</Button>
<Dialog
open={openChangeEmailBox}
onClose={()=> {setOpenChangeEmailBox(false)}}
PaperProps={{
sx: {
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
border: theme?.palette?.DialogStyle?.border,
minWidth: '440px',
fontFamily: theme?.typography?.fontFamily,
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
zIndex: 1000,
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogActions-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
}
}}
>
<DialogTitle style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>Change Billing Email</DialogTitle>
<DialogContent>
<DialogContentText style={{ marginBottom: '20px', fontSize: 16, }}>
Enter the new billing email address.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="email"
label="New Billing Email"
type="email"
fullWidth
value={newBillingEmail}
onKeyPress={(event) => { if (event.key === 'Enter') HandleChangeBillingEmail(selectedOrganization.id) }}
onChange={(e) => setNewBillingEmail(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button
onClick={()=> {setOpenChangeEmailBox(false)}}
color="primary"
style={{ textTransform: 'none', marginRight: '10px' }}
>
Cancel
</Button>
<Button
style={{ textTransform: 'none', backgroundColor: "#ff8544", color: "#1a1a1a" }}
onClick={(event) => HandleChangeBillingEmail(selectedOrganization.id)}
>
Save
</Button>
</DialogActions>
</Dialog>
</div>
: null}
</div>
<Button
fullWidth
disabled={false}
color="primary"
style={{
marginTop: !userdata.has_card_available ? 20 : 10,
borderRadius: 4,
height: 40,
fontSize: 16,
color: "#1a1a1a",
backgroundColor: "#ff8544",
// backgroundImage: userdata.has_card_available ? null : "linear-gradient(to right, #f86a3e, #f34079)",
textTransform: "none",
}}
onClick={() => {
if (isCloud) {
handlePayasyougo(userdata, selectedOrganization, BillingEmail)
//navigate("/pricing?tab=cloud&highlight=true")
} else {
//window.open("https://shuffler.io/pricing?tab=onprem&highlight=true", "_blank")
handlePayasyougo()
}
}}
>
{userdata.has_card_available === true ?
"Manage Card Details"
:
"Add Card Details"
}
</Button>
<Button
variant="outlined"
style={{
marginLeft: 115,
marginTop: 50,
borderRadius: 20,
width: 120,
color: "#FFFFFF",
marginTop: 10,
borderRadius: 4,
width: "100%",
cursor: "pointer",
textTransform: "capitalize",
backgroundColor: "transparent",
fontSize: 16,
position: "relative", // Required for positioning tooltip
}}
title="Click to book a call"
@@ -476,7 +655,7 @@ const LicencePopup = (props) => {
// }
}}
>
<span style={{color: "#f85a3e",}}>
<span>
Book a call
</span>
<span
@@ -498,8 +677,7 @@ const LicencePopup = (props) => {
Click to book a call
</span>
</Button>
</Paper>
{/*
<div style={{ paddingRight: 150, display: "flex", alignItems: "baseline" }}>
@@ -736,7 +914,7 @@ const LicencePopup = (props) => {
return (
<div>
<Grid container spacing={2} columns={16} style={{ flexDirection: "row", flexWrap: "nowrap", borderRadius: '16px', display: "flex" }}>
<Grid container spacing={2} columns={16} style={{ flexDirection: "row", flexWrap: "nowrap", borderRadius: '16px', display: "flex", }}>
<Grid item xs={8}>
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ?
<SubscriptionObject
@@ -895,13 +1073,11 @@ const LicencePopup = (props) => {
</div>
</div>
<div style={{ marginTop: 20, }} />
</Card>
</Grid>
</Grid>
</Grid>
<DialogActions style={{ marginTop: 15 }} >
<DialogActions style={{ marginTop: 15 }} >
<div style={{display: 'flex', flexDirection: 'column', width: "100%", justifyContent: 'space-between', gap: "10px"}}>
<Button
style={{ borderRadius: "0px", textTransform: "capitalize" }}
style={{ borderRadius: "4px", textTransform: "capitalize", width: "100%", fontSize: 16}}
variant="outlined"
onClick={() => {
if (isCloud) {
ReactGA.event({
@@ -914,7 +1090,6 @@ const LicencePopup = (props) => {
} else {
window.open("https://shuffler.io/pricing?tab=onprem", "_blank")
}
setModalOpen(false)
}}
color="primary"
>
@@ -922,7 +1097,7 @@ const LicencePopup = (props) => {
</Button>
<Button
variant="contained"
style={{ borderRadius: 20, width: 120, textTransform: "capitalize" }}
style={{ borderRadius: 4, textTransform: "capitalize", color: "#1a1a1a", backgroundColor: "#ff8544", width: "100%", fontSize: 16}}
onClick={() => {
if (isCloud) {
ReactGA.event({
@@ -937,6 +1112,7 @@ const LicencePopup = (props) => {
navigate(`/register?view=pricing&message=You need to create a user to continue`)
}
} else {
console.log("window.drift: ", window.drift)
if (window.drift !== undefined) {
window.drift.api.startInteraction({
interactionId: 386403,
@@ -948,7 +1124,12 @@ const LicencePopup = (props) => {
>
Upgrade
</Button>
</div>
</DialogActions>
</Card>
</Grid>
</Grid>
</Grid>
</div>
)
}
+122 -54
View File
@@ -255,7 +255,7 @@ const Priorities = memo((props) => {
onClick={() => {
clearNotifications()
}}
style={{marginLeft: 50, }}
style={{marginLeft: 50, textTransform: "none", fontSize: 16}}
>
Mark all as read
</Button>
@@ -265,7 +265,7 @@ const Priorities = memo((props) => {
{clickedFromOrgTab? null : <Divider style={{marginTop: 50, marginBottom: 50, }} />}
<h2 style={{ display: clickedFromOrgTab ? null:"inline", marginBottom: clickedFromOrgTab ? 8:null, marginTop: clickedFromOrgTab ? 30 :null, color: clickedFromOrgTab ? "#ffffff" : null }}>Suggestions</h2>
<span style={{ fontSize: 16, color: clickedFromOrgTab ?"#9E9E9E":null,marginLeft: clickedFromOrgTab ?null:25 }}>
<span style={{ fontSize: 16, color: clickedFromOrgTab ?"#9E9E9E":null,marginLeft: clickedFromOrgTab ?null:25, }}>
Suggestions are tasks identified by Shuffle to help you discover ways to protect your and customers' company. <br/>These range from simple configurations in Shuffle to Usecases you may have missed.&nbsp;
<a
target="_blank"
@@ -431,64 +431,132 @@ const NotificationItem = memo((props) => {
:
null
}
<Typography variant="body2" color="textSecondary" style={{ marginTop: 10, maxHeight: 200, overflowX: "hidden", overflowY: "auto", wordWrap: "break-word" }}>
<Typography variant="body2" color="textSecondary" style={{ marginTop: 10, maxHeight: 200, overflowX: "hidden", overflowY: "auto", wordWrap: "break-word", fontSize: 16 }}>
{data.description}
</Typography >
<div style={{ display: "flex" }}>
<ButtonGroup>
<Button
color="secondary"
variant="outlined"
style={{ marginTop: 15 }}
disabled={data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0}
onClick={() => {
window.open(data.reference_url, "_blank")
}}
>
Explore
</Button>
{data.read === false ? (
<Button
color="secondary"
variant="outlined"
style={{ marginTop: 15 }}
onClick={() => {
dismissNotification(data.id);
}}
>
Dismiss
</Button>
) : null}
<Tooltip title="Disabling a notification makes it so similar notifications to this one will NOT be re-opened. It will NOT forward notifications to your notification workflow, but WILL still keep counting." placement="top">
<Button
color="secondary"
variant={data.ignored === true ? "contained" : "outlined"}
style={{ marginTop: 15, }}
onClick={() => {
if (data.ignored === true) {
dismissNotification(data.id, false)
} else {
dismissNotification(data.id, true)
}
}}
>
{data.ignored === true ? "Re-enable" : "Disable"}
</Button>
</Tooltip>
</ButtonGroup>
<Button
style={{
marginTop: 15,
textTransform: "none",
fontSize: 16,
marginRight: 5,
border: "1px solid #ff8544",
color: "#ff8544",
width: 130,
opacity: data.reference_url ? 1 : 0.5,
cursor: data.reference_url ? "pointer" : "not-allowed",
}}
disabled={
!data.reference_url || data.reference_url.length === 0
}
onClick={() => {
window.open(data.reference_url, "_blank");
}}
>
Explore
</Button>
<Typography variant="body2" color="textSecondary" style={{ marginLeft: 20, marginTop: 20, wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
<b>First seen</b>: {new Date(data.created_at * 1000).toISOString().slice(0, 19)}
</Typography >
{data.read === false ? (
<Button
sx={{
marginTop: "15px",
border: "none",
textTransform: "none",
fontSize: 16,
color: "#ffffff",
marginRight: "5px",
backgroundColor: "transparent",
"&:hover": { backgroundColor: "transparent" },
}}
onClick={() => {
dismissNotification(data.id);
}}
>
Dismiss
</Button>
) : null}
<Typography variant="body2" color="textSecondary" style={{ marginLeft: 20, marginTop: 20, wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
<b>Last seen</b>: {new Date(data.updated_at * 1000).toISOString().slice(0, 19)}
</Typography >
<Tooltip
title="Disabling a notification makes it so similar notifications to this one will NOT be re-opened. It will NOT forward notifications to your notification workflow, but WILL still keep counting."
placement="top"
>
<Button
style={{
marginTop: 15,
textTransform: "none",
fontSize: 16,
width: 130,
border: data.ignored
? "1px solid #2bc07e"
: "1px solid #ff8544",
color: data.ignored ? "#ffffff" : "#ff8544",
backgroundColor: data.ignored ? "#2bc07e" : "transparent",
opacity: data.ignored || data.dismissable ? 1 : 0.5,
cursor: data.dismissable ? "pointer" : "not-allowed",
}}
disabled={!data.dismissable}
onClick={() => {
if (data.ignored) {
dismissNotification(data.id, false);
} else {
dismissNotification(data.id, true);
}
}}
>
{data.ignored ? "Re-enable" : "Disable"}
</Button>
</Tooltip>
<Typography
variant="body2"
color="textSecondary"
style={{
marginLeft: 20,
marginTop: 20,
wordWrap: "break-word",
overflow: "hidden",
textOverflow: "ellipsis",
fontSize: 16,
}}
>
<b>First seen</b>:{" "}
{new Date(data.created_at * 1000).toISOString().slice(0, 19)}
</Typography>
<Typography
variant="body2"
color="textSecondary"
style={{
marginLeft: 20,
marginTop: 20,
wordWrap: "break-word",
overflow: "hidden",
textOverflow: "ellipsis",
fontSize: 16,
}}
>
<b>Last seen</b>:{" "}
{new Date(data.updated_at * 1000).toISOString().slice(0, 19)}
</Typography>
<Typography
variant="body2"
color="textSecondary"
style={{
marginLeft: 20,
marginTop: 20,
wordWrap: "break-word",
overflow: "hidden",
textOverflow: "ellipsis",
fontSize: 16,
}}
>
<b>Times seen</b>: {data.amount}
</Typography>
</div>
<Typography variant="body2" color="textSecondary" style={{ marginLeft: 20, marginTop: 20, wordWrap: "break-word", overflow: "hidden", textOverflow: "ellipsis" }}>
<b>Times seen</b>: {data.amount}
</Typography >
</div>
</Paper>
);
})
+1 -1
View File
@@ -154,7 +154,7 @@ const Priority = (props) => {
}
</div>
<div style={{flex: 1, display: "flex", marginLeft: 30, }}>
<Button style={{height: 50, borderRadius: 25, fontSize:16, boxShadow: clickedFromOrgTab ? "none":null,textTransform: clickedFromOrgTab ? 'capitalize':null, marginTop: 8, width: 175, marginRight: 10, color: priority.active === false ? "white" :clickedFromOrgTab ?"#FF8444": "black", backgroundColor: priority.active === false ? theme.palette.inputColor :clickedFromOrgTab?"rgba(255, 132, 68, 0.2)":"rgba(255,255,255,0.8)", }} variant="contained" color="secondary" onClick={() => {
<Button style={{height: 50, borderRadius: 4, fontSize:16, boxShadow: clickedFromOrgTab ? "none":null,textTransform: clickedFromOrgTab ? 'capitalize':null, marginTop: 8, width: 175, marginRight: 10, color: priority.active === false ? "white" :clickedFromOrgTab ?"#ff8544": "black", backgroundColor: priority.active === false ? theme.palette.inputColor :clickedFromOrgTab?"transparent":"rgba(255,255,255,0.8)", border: "1px solid #ff8544"}} variant="contained" color="secondary" onClick={() => {
if (isCloud) {
ReactGA.event({
+3 -2
View File
@@ -238,7 +238,7 @@ const Admin2 = (props) => {
const data = {
name: name,
description: description,
org_id: orgId,
org_id: orgId?.length > 0 ? orgId : selectedOrganization?.id,
image: image,
defaults: defaults,
sso_config: sso_config,
@@ -263,6 +263,7 @@ const Admin2 = (props) => {
if (responseJson["success"] === false) {
toast("Failed updating org: ", responseJson.reason);
} else {
handleGetOrg(selectedOrganization?.id);
if (
lead_info === undefined ||
lead_info === null ||
@@ -329,7 +330,7 @@ const Admin2 = (props) => {
}
return (
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 0.8, }}>
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 29, zoom: 0.9}}>
<AdminNavBar userdata={userdata} isLoaded={isLoaded} selectedStatus={selectedStatus} setSelectedStatus={setSelectedStatus} selectedTab={selectedTab} orgId={selectedOrganization.id} handleStatusChange={handleStatusChange} handleEditOrg={handleEditOrg} handleGetOrg={handleGetOrg} setSelectedOrganization={setSelectedOrganization} selectedOrganization={selectedOrganization} setNotifications={setNotifications} stripeKey={stripeKey} notifications={notifications} checkLogin={checkLogin} globalUrl={globalUrl} isCloud={isCloud} serverside={serverside} />
</div>
);
+100 -81
View File
@@ -20,14 +20,18 @@ import {
IconButton,
} from "@mui/material";
import { Context } from "../context/ContextApi.jsx";
import Add from '@mui/icons-material/Add';
import CachedIcon from '@mui/icons-material/Cached';
import CloudDownloadIcon from '@mui/icons-material/CloudDownload';
import EditIcon from '@mui/icons-material/Edit';
import {
Add as AddIcon,
Edit as EditIcon,
Search as SearchIcon,
Clear as ClearIcon,
Close as CloseIcon,
Cached as CachedIcon,
CloudDownload as CloudDownloadIcon,
} from "@mui/icons-material";
import InputAdornment from '@mui/material/InputAdornment';
import Search from '@mui/icons-material/Search';
import ClearIcon from '@mui/icons-material/Clear';
import CloseIcon from '@mui/icons-material/Close';
import { ClearRefinements, connectHits, connectSearchBox, connectStateResults, InstantSearch, RefinementList, connectRefinementList, Configure } from "react-instantsearch-dom";
import { removeQuery } from "../components/ScrollToTop.jsx";
@@ -45,7 +49,7 @@ const searchClient = algoliasearch(
);
// AppCard Component
const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes, currTab, handleAppClick, leftSideBarOpenByClick, userdata }) => {
const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes, currTab, handleAppClick, leftSideBarOpenByClick, userdata, fetchApps }) => {
const navigate = useNavigate();
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000";
const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`;
@@ -117,9 +121,17 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
fontWeight: 600,
whiteSpace: "nowrap",
marginLeft: 8,
maxWidth: "90%",
gap: 8
}}>
{data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
<div style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: '#F1F1F1'
}}>
{data.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
</div>
</div>
<div style={{
overflow: "hidden",
@@ -182,12 +194,13 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
<Button
className="deactivate-button"
sx={{
width: 102,
width: 110,
height: 35,
borderRadius: 0.75,
bgcolor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
fontSize: 16,
fontFamily: theme?.typography?.fontFamily,
transition: "background-color 0.3s ease",
"&:hover": {
@@ -213,6 +226,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl,
toast.error(responseJson.reason);
} else {
toast.success("App Deactivated Successfully.");
fetchApps();
}
})
.catch(error => {
@@ -445,13 +459,21 @@ const Hits = ({
overflow: "hidden",
gap: 8,
fontWeight: 600,
maxWidth: "90%",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: '#F1F1F1'
}}
>
{(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && <Box sx={{ width: 8, height: 8, backgroundColor: "#02CB70", borderRadius: '50%' }} />}
{normalizedString(data.name)}
<div style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: '#F1F1F1'
}}>
{normalizedString(data.name)}
</div>
</div>
<div
@@ -535,18 +557,19 @@ const Hits = ({
paddingRight: 10,
minWidth: 110
}}>
{hoverEffect === index && isCloud && (
{hoverEffect === index && (
<div>
{allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? (
<Button
style={{
width: 102,
width: 110,
height: 35,
borderRadius: 3,
borderRadius: 4,
backgroundColor: "rgba(73, 73, 73, 1)",
color: "rgba(241, 241, 241, 1)",
textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16,
}}
onClick={(event) => {
handleActivateButton(event, data, "deactivate");
@@ -560,9 +583,10 @@ const Hits = ({
color: "black",
width: 102,
height: 35,
borderRadius: 3,
borderRadius: 4,
textTransform: "none",
fontFamily: theme?.typography?.fontFamily,
fontSize: 16
}}
onClick={(event) => {
handleActivateButton(event, data, "activate");
@@ -655,15 +679,15 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
event.preventDefault();
}
}}
style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
InputProps={{
style: {
borderRadius: 8,
borderRadius: 4,
height: 45
},
endAdornment: (
<InputAdornment position="end">
{localQuery?.length === 0 ? <Search /> : (
{localQuery?.length === 0 ? <SearchIcon /> : (
<ClearIcon
style={{
cursor: "pointer",
@@ -703,7 +727,7 @@ const CategoryDropdown = ({ items, currentRefinement, refine }) => {
onChange={handleChange}
displayEmpty
multiple
style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
renderValue={(selected) => {
if (selected.length === 0) return 'All Categories';
return (
@@ -764,7 +788,7 @@ const LabelDropdown = ({ items, currentRefinement, refine }) => {
onChange={handleChange}
displayEmpty
multiple
style={{ borderRadius: 8, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
style={{ borderRadius: 4, height: 45, fontFamily: theme?.typography?.fontFamily, flex: 1 }}
renderValue={(selected) => {
if (selected.length === 0) return 'All Labels';
return (
@@ -855,7 +879,7 @@ const AppSkeleton = () => {
backgroundColor: "#212121",
width: "100%",
height: 120,
borderRadius: 8,
borderRadius: 4,
}}>
<div style={{
display: "flex",
@@ -868,7 +892,7 @@ const AppSkeleton = () => {
width={100}
height={90}
style={{
borderRadius: 6,
borderRadius: 4,
backgroundColor: "rgba(255, 255, 255, 0.1)"
}}
/>
@@ -1024,43 +1048,43 @@ const Apps2 = (props) => {
}, []);
// Fetch apps based on the current tab : 0 -> org_apps, 1 -> my_apps, 2 -> all_apps
useEffect(() => {
const fetchApps = async () => {
const baseUrl = globalUrl;
let url;
setIsLoading(true);
const userId = userdata?.id;
if (currTab === 1 && userId) {
url = `${baseUrl}/api/v1/users/${userId}/apps`;
const fetchApps = async () => {
const baseUrl = globalUrl;
let url;
setIsLoading(true);
const userId = userdata?.id;
if (currTab === 1 && userId) {
url = `${baseUrl}/api/v1/users/${userId}/apps`;
} else if (currTab === 0) {
url = `${baseUrl}/api/v1/apps`;
}
try {
const response = await fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
if (currTab === 1) {
setAppsToShow(data);
setUserApps(data);
} else if (currTab === 0) {
url = `${baseUrl}/api/v1/apps`;
setAppsToShow(data);
setOrgApps(data);
// For testing the empty state
// setAppsToShow([]);
// setOrgApps([]);
}
try {
const response = await fetch(url, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
if (currTab === 1) {
setAppsToShow(data);
setUserApps(data);
} else if (currTab === 0) {
setAppsToShow(data);
setOrgApps(data);
// For testing the empty state
// setAppsToShow([]);
// setOrgApps([]);
}
setIsLoading(false);
} catch (err) {
console.error("Error fetching apps:", err);
setIsLoading(false);
}
};
setIsLoading(false);
} catch (err) {
console.error("Error fetching apps:", err);
setIsLoading(false);
}
};
useEffect(() => {
// Only fetch if we have required data
if (globalUrl && (currTab === 0 || (currTab === 1 && userdata?.id))) {
@@ -1164,15 +1188,16 @@ const Apps2 = (props) => {
return response.json();
})
.then((responseJson) => {
//responseJson = sortByKey(responseJson, "large_image")
//responseJson = sortByKey(responseJson, "is_valid")
//setFilteredApps(responseJson.filter(app => !internalIds.includes(app.name) && !(!app.activated && app.generated)))
console.log("responseJson: from getApps ", responseJson)
var privateapps = [];
var valid = [];
var invalid = [];
for (var key in responseJson) {
const app = responseJson[key];
if (app.categories !== undefined && app.categories !== null && app?.categories.includes("Eradication")) {
app.categories = ["EDR"]
}
if (app.is_valid && !(!app.activated && app.generated)) {
privateapps.push(app);
} else if (
@@ -1185,21 +1210,13 @@ const Apps2 = (props) => {
}
}
//console.log(privateapps)
//console.log(valid)
//console.log(invalid)
//console.log(privateapps)
//privateapps.reverse()
privateapps.push(...valid);
privateapps.push(...invalid);
console.log("privateapps: setting apps ", privateapps)
setAppsToShow(privateapps);
setOrgApps(privateapps);
setApps(privateapps);
// setCursearch("");
//handleSearchChange(event.target.value)
//setCursearch(event.target.value)
// setFilteredApps(privateapps);
if (privateapps.length > 0) {
if (selectedApp.id === undefined || selectedApp.id === null) {
@@ -1708,7 +1725,7 @@ const Apps2 = (props) => {
}
return (
<div style={{ paddingTop: 70, paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease", backgroundColor: "#1A1A1A", fontFamily: theme?.typography?.fontFamily, zoom: 0.8, }}>
<div style={{ paddingTop: 70, paddingLeft: leftSideBarOpenByClick ? 200 : 0, transition: "padding-left 0.3s ease", backgroundColor: "#1A1A1A", fontFamily: theme?.typography?.fontFamily, zoom: 0.7, }}>
<InstantSearch searchClient={searchClient} indexName="appsearch">
<AppModal
open={openModal}
@@ -1755,7 +1772,7 @@ const Apps2 = (props) => {
height: 45,
minWidth: 45,
backgroundColor: "#2F2F2F",
borderRadius: 8,
borderRadius: 4,
padding: "8px 16px",
}}
disabled={isLoading}
@@ -1795,7 +1812,7 @@ const Apps2 = (props) => {
height: 45,
minWidth: 45,
backgroundColor: "#2F2F2F",
borderRadius: 8,
borderRadius: 4,
padding: "8px 16px",
}}
disabled={isLoading}
@@ -1821,11 +1838,11 @@ const Apps2 = (props) => {
value={currTab}
onChange={(event, newTab) => handleTabChange(event, newTab)}
TabIndicatorProps={{ style: { height: '3px', borderRadius: 10, backgroundColor: "#FF8544" } }}
style={{ fontFamily: theme?.typography?.fontFamily }}
style={{ fontFamily: theme?.typography?.fontFamily, fontSize: 16 }}
>
<Tab label="Organization Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
<Tab label="My Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily }} />
<Tab label="Discover Apps" style={{ textTransform: 'none', fontFamily: theme?.typography?.fontFamily }} />
<Tab label="Organization Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily, fontSize: 16 }} />
<Tab label="My Apps" style={{ textTransform: 'none', marginRight: 20, fontFamily: theme?.typography?.fontFamily, fontSize: 16 }} />
<Tab label="Discover Apps" style={{ textTransform: 'none', fontFamily: theme?.typography?.fontFamily, fontSize: 16 }} />
</Tabs>
</div>
<div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 20, height: 45, paddingRight: 25 }}>
@@ -1850,12 +1867,12 @@ const Apps2 = (props) => {
limit={5}
InputProps={{
style: {
borderRadius: 8,
borderRadius: 4,
height: 45
},
endAdornment: (
<InputAdornment position="end">
{searchQuery.length === 0 ? <Search /> : (
{searchQuery.length === 0 ? <SearchIcon /> : (
<ClearIcon
style={{ cursor: "pointer" }}
onClick={() => {
@@ -1892,7 +1909,7 @@ const Apps2 = (props) => {
displayEmpty
multiple
style={{
borderRadius: 8,
borderRadius: 4,
height: 45,
fontFamily: theme?.typography?.fontFamily
}}
@@ -1956,7 +1973,7 @@ const Apps2 = (props) => {
displayEmpty
multiple
style={{
borderRadius: 8,
borderRadius: 4,
height: 45,
fontFamily: theme?.typography?.fontFamily
}}
@@ -2014,7 +2031,7 @@ const Apps2 = (props) => {
style={{
height: "100%",
width: '100%',
borderRadius: '7px',
borderRadius: '4px',
textTransform: 'none',
backgroundColor: "#FF8544",
color: "#1A1A1A",
@@ -2022,7 +2039,7 @@ const Apps2 = (props) => {
fontSize: 16,
fontWeight: 500
}}
startIcon={<Add style={{ color: "#1A1A1A" }} />}
startIcon={<AddIcon style={{ color: "#1A1A1A" }} />}
>
Create an App
</Button>
@@ -2062,6 +2079,7 @@ const Apps2 = (props) => {
handleAppClick={handleAppClick}
leftSideBarOpenByClick={leftSideBarOpenByClick}
userdata={userdata}
fetchApps={fetchApps}
/>
))}
</div>
@@ -2106,6 +2124,7 @@ const Apps2 = (props) => {
{appsToShow.map((data, index) => (
<AppCard key={index} data={data} index={index} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} globalUrl={globalUrl} deactivatedIndexes={deactivatedIndexes} currTab={currTab} userdata={userdata}
handleAppClick={handleAppClick} leftSideBarOpenByClick={leftSideBarOpenByClick}
fetchApps={fetchApps}
/>
))}
</div>
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -95,14 +95,14 @@ require (
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
go.opentelemetry.io/otel/trace v1.30.0 // indirect
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/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/time v0.3.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
+10 -16
View File
@@ -301,12 +301,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shuffle/shuffle-shared v0.6.74 h1:os3BDSFZnl4U8ZgsTAY8IsTDADcMXhbc1rS9UMa0BIY=
github.com/shuffle/shuffle-shared v0.6.74/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus=
github.com/shuffle/shuffle-shared v0.6.79 h1:MIy5kcShHYN05ov/50YJ+la1C2v1rL8IENapOvX9I8U=
github.com/shuffle/shuffle-shared v0.6.79/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus=
github.com/shuffle/shuffle-shared v0.6.83 h1:gceT91WtFqh3h9juzTipDhWpxZLfrdtbcsnK+XNj57g=
github.com/shuffle/shuffle-shared v0.6.83/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus=
github.com/shuffle/shuffle-shared v0.6.90 h1:FzIYtEt44eWgEsW/9tj2ki7qq8FEm/HWXUok+THp72M=
github.com/shuffle/shuffle-shared v0.6.90/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -372,8 +366,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
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/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=
@@ -449,8 +443,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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -479,16 +473,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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/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.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
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=
@@ -500,8 +494,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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+2 -1
View File
@@ -608,7 +608,8 @@ func deployServiceWorkers(image string) {
Condition: swarm.RestartPolicyConditionOnFailure,
},
Placement: &swarm.Placement{
Constraints: []string{},
Constraints: []string{
},
},
},
}
+41 -33
View File
@@ -27,13 +27,13 @@ import (
dockerclient "github.com/docker/docker/client"
// This is for automatic removal of certain code :)
/*** STARTREMOVE ***/
/*** ***/
"math/rand"
"github.com/docker/docker/api/types/swarm"
uuid "github.com/satori/go.uuid"
/*** ENDREMOVE ***/
/*** ***/
"github.com/gorilla/mux"
@@ -321,11 +321,11 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason
*/
} else {
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" {
log.Printf("[DEBUG][%s] NOT cleaning up containers. IDS: %d, CLEANUP env: %s", workflowExecution.ExecutionId, 0, cleanupEnv)
}
/*** ENDREMOVE ***/
/*** ***/
}
if len(reason) > 0 && len(nodeId) > 0 {
@@ -393,7 +393,7 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason
func int32Ptr(i int32) *int32 { return &i }
// ** STARTREMOVE ***/
// ** ***/
func deployk8sApp(image string, identifier string, env []string) error {
if len(os.Getenv("KUBERNETES_NAMESPACE")) > 0 {
kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE")
@@ -631,7 +631,7 @@ func deployk8sApp(image string, identifier string, env []string) error {
return nil
}
//** ENDREMOVE ***/
//** ***/
// Deploys the internal worker whenever something happens
func deployApp(cli *dockerclient.Client, image string, identifier string, env []string, workflowExecution shuffle.WorkflowExecution, action shuffle.Action) error {
@@ -772,7 +772,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
}
}
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
appName := strings.Replace(identifier, fmt.Sprintf("_%s", action.ID), "", -1)
@@ -802,14 +802,14 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
return err
}
} else {
// ** STARTREMOVE ***/
// ** ***/
exposedPort = 80
err = findAppInfoKubernetes(image, appName, env)
if err != nil {
log.Printf("[ERROR] Failed finding and creating port for %s: %s", appName, err)
return err
}
// ** ENDREMOVE ***/
// ** ***/
}
/*
@@ -852,7 +852,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
return nil
}
/*** ENDREMOVE ***/
/*** ***/
// Max 10% CPU every second
//CPUShares: 128,
@@ -2404,7 +2404,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
return
}
/*** STARTREMOVE ***/
/*** ***/
if workflowExecution.Status == "WAITING" && (os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm") {
log.Printf("[INFO][%s] Workflow execution is waiting while in swarm. Sending info to backend to ensure execution stops.", workflowExecution.ExecutionId)
@@ -2416,7 +2416,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
shutdown(*workflowExecution, "", "", false)
}
}
/*** ENDREMOVE ***/
/*** ***/
} else {
if strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already been ran") || strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already finished") {
log.Printf("[ERROR][%s] Skipping rerun of action result as it's already been ran: %s", workflowExecution.ExecutionId)
@@ -2501,7 +2501,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
return
}
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
finished := shuffle.ValidateFinished(ctx, -1, *workflowExecution)
if !finished {
@@ -2516,7 +2516,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
sendResult(*workflowExecution, shutdownData)
}
}
/*** ENDREMOVE ***/
/*** ***/
} else {
log.Printf("[INFO][%s] Skipping setexec with status %s", workflowExecution.ExecutionId, workflowExecution.Status)
@@ -2536,12 +2536,12 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
func sendSelfRequest(actionResult shuffle.ActionResult) {
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" {
log.Printf("[INFO][%s] Not sending self request info since source is default (not swarm)", actionResult.ExecutionId)
return
}
/*** ENDREMOVE ***/
/*** ***/
data, err := json.Marshal(actionResult)
if err != nil {
@@ -2680,11 +2680,11 @@ func validateFinished(workflowExecution shuffle.WorkflowExecution) bool {
}
}
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" {
requestsSent += 1
}
/*** ENDREMOVE ***/
/*** ***/
log.Printf("[DEBUG][%s] Should send full result to %s", workflowExecution.ExecutionId, baseUrl)
@@ -2769,7 +2769,7 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
// GetLocalIP returns the non loopback local IP of the host
func getLocalIP() string {
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("IS_KUBERNETES") == "true" {
return "shuffle-workers"
}
@@ -2836,7 +2836,7 @@ func getLocalIP() string {
return foundIP
}
}
/*** ENDREMOVE ***/
/*** ***/
addrs, err := net.InterfaceAddrs()
if err != nil {
@@ -2887,7 +2887,7 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener {
log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl)
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
log.Printf("[DEBUG] Starting webserver (1) on port %d with hostname: %s", baseport, hostname)
@@ -2907,7 +2907,7 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener {
return listener
}
/*** ENDREMOVE ***/
/*** ***/
port := listener.Addr().(*net.TCPAddr).Port
// Set the port environment variable
@@ -2958,7 +2958,7 @@ func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) {
*/
}
/*** STARTREMOVE ***/
/*** ***/
func deploySwarmService(dockercli *dockerclient.Client, name, image string, deployport int) error {
log.Printf("[DEBUG] Deploying service for %s to swarm on port %d", name, deployport)
//containerName := fmt.Sprintf("shuffle-worker-%s", parsedUuid)
@@ -3128,10 +3128,10 @@ func deploySwarmService(dockercli *dockerclient.Client, name, image string, depl
return nil
}
/*** ENDREMOVE ***/
/*** ***/
// Runs data discovery
/*** STARTREMOVE ***/
/*** ***/
func findAppInfoKubernetes(image, name string, env []string) error {
clientset, _, err := shuffle.GetKubernetesClient()
@@ -3291,7 +3291,7 @@ func findAppInfo(image, name string) (int, error) {
return exposedPort, nil
}
/*** ENDREMOVE ***/
/*** ***/
func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, action *shuffle.Action, workflowExecution *shuffle.WorkflowExecution) error {
parsedRequest := shuffle.OrborusExecutionRequest{
@@ -3691,11 +3691,11 @@ func getStreamResultsWrapper(client *http.Client, req *http.Request, workflowExe
// Initial loop etc
func main() {
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
logsDisabled = "true"
}
/*** ENDREMOVE ***/
/*** ***/
// Elasticsearch necessary to ensure we'ren ot running with Datastore configurations for minimal/maximal data sizes
// Recursive import kind of :)
_, err := shuffle.RunInit(*shuffle.GetDatastore(), *shuffle.GetStorage(), "", "worker", true, "elasticsearch", false, 0)
@@ -3729,7 +3729,7 @@ func main() {
swarmConfig := os.Getenv("SHUFFLE_SWARM_CONFIG")
log.Printf("[INFO] Running with timezone %s and swarm config %#v", timezone, swarmConfig)
/*** STARTREMOVE ***/
/*** ***/
if swarmConfig == "run" || swarmConfig == "swarm" {
// Forcing download just in case on the first iteration.
log.Printf("[INFO] Running in swarm mode - forcing download of apps")
@@ -3744,7 +3744,7 @@ func main() {
log.Printf("[ERROR] Stopped listener %#v - exiting.", listener)
os.Exit(3)
}
/*** ENDREMOVE ***/
/*** ***/
authorization := ""
executionId := ""
@@ -4097,13 +4097,15 @@ func runWebserver(listener net.Listener) {
// What would be require to run a workflow otherwise?
// Maybe directly /workflow/run
/*** STARTREMOVE ***/
/*** ***/
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
log.Printf("[DEBUG] Running webserver config for SWARM and K8s")
}
/*** ENDREMOVE ***/
// var dockercli *dockerclient.Client
// ctx := context.Background()
/*** ***/
scaleReplicas := os.Getenv("SHUFFLE_APP_REPLICAS")
if len(scaleReplicas) > 0 {
tmpInt, err := strconv.Atoi(scaleReplicas)
@@ -4129,9 +4131,13 @@ func runWebserver(listener net.Listener) {
log.Printf("[DEBUG] SHUFFLE_APP_EXECUTIONS_PER_MINUTE set to value %s. Trying to overwrite default (%d)", os.Getenv("SHUFFLE_APP_EXECUTIONS_PER_MINUTE"), maxExecutionsPerMinute)
}
/*
ctx := context.Background()
if strings.ToLower(os.Getenv("SHUFFLE_SWARM_CONFIG")) == "run" || strings.ToLower(os.Getenv("SHUFFLE_APP_REPLICAS")) == "" {
// go AutoScaleApps(ctx, dockercli, maxExecutionsPerMinute)
}
*/
if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" {
r.HandleFunc("/debug/pprof/", pprof.Index)
r.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
@@ -4177,12 +4183,14 @@ func AutoScaleApps(ctx context.Context, client *dockerclient.Client, maxExecutio
case <-ticker.C:
count := window.CountEvents(time.Now())
j := numberOfApps(ctx, client)
appNumber := 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(j+1))
scaleApps(ctx, client, uint64(appNumber+1))
}
}
}