diff --git a/client_web/index.html b/client_web/index.html index 90f6e6e..2882e77 100644 --- a/client_web/index.html +++ b/client_web/index.html @@ -9,5 +9,6 @@
+ diff --git a/client_web/package-lock.json b/client_web/package-lock.json index 374542b..6f99840 100644 --- a/client_web/package-lock.json +++ b/client_web/package-lock.json @@ -10,6 +10,9 @@ "dependencies": { "@azure/msal-browser": "^3.27.0", "@react-oauth/google": "^0.12.1", + "@tsparticles/engine": "^3.7.1", + "@tsparticles/react": "^3.0.0", + "@tsparticles/slim": "^3.7.1", "@types/js-cookie": "^3.0.6", "antd": "^5.22.1", "axios": "^1.7.7", @@ -18,7 +21,8 @@ "js-cookie": "^3.0.5", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.28.0" + "react-router-dom": "^6.28.0", + "react-toastify": "^10.0.6" }, "devDependencies": { "@eslint/js": "^9.13.0", @@ -1636,6 +1640,470 @@ "node": ">=10" } }, + "node_modules/@tsparticles/basic": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/basic/-/basic-3.7.1.tgz", + "integrity": "sha512-oJMJ3qzYUROYaOEsaFXkVynxT2OTWBXbQ9MNc1bJi/bVc1VOU44VN7X/KmiZjD+w1U+Qalk6BeVvDRwpFshblw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1", + "@tsparticles/move-base": "3.7.1", + "@tsparticles/plugin-hex-color": "3.7.1", + "@tsparticles/plugin-hsl-color": "3.7.1", + "@tsparticles/plugin-rgb-color": "3.7.1", + "@tsparticles/shape-circle": "3.7.1", + "@tsparticles/updater-color": "3.7.1", + "@tsparticles/updater-opacity": "3.7.1", + "@tsparticles/updater-out-modes": "3.7.1", + "@tsparticles/updater-size": "3.7.1" + } + }, + "node_modules/@tsparticles/engine": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/engine/-/engine-3.7.1.tgz", + "integrity": "sha512-GYzBgq/oOE9YJdOL1++MoawWmYg4AvVct6CIrJGx84ZRb3U2owYmLsRGabYl0qX1CWWOvUG569043RJmyp/vQA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "hasInstallScript": true, + "license": "MIT" + }, + "node_modules/@tsparticles/interaction-external-attract": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-attract/-/interaction-external-attract-3.7.1.tgz", + "integrity": "sha512-cpnMsFJ7ZJNKccpQvskKvSs1ofknByHE6FGqbEb17ij7HqvbECQOCOVKHPFnYipHe14cXor/Cd+nVisRcTASoQ==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-bounce": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-bounce/-/interaction-external-bounce-3.7.1.tgz", + "integrity": "sha512-npvU9Qt6WDonjezHqi+hWM44ga2Oh5yXdr8eSoJpvuHZrCP7rIdRSz5XseHouO1bMS9DbXk86sx4qwrhB5w58w==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-bubble": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-bubble/-/interaction-external-bubble-3.7.1.tgz", + "integrity": "sha512-WdbYL46lMfuf2g5kfVB1hhhxRBtEXDvnwz8PJwLKurSThL/27bqsqysyXsMzXtLByXUneGhtJTj4D5I5RYdgjA==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-connect": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-connect/-/interaction-external-connect-3.7.1.tgz", + "integrity": "sha512-hqx0ANIbjLIz/nxmk0LvqANBiNLLmVybbCA7N+xDHtEORvpKjNlKEvMz6Razocl6vRjoHZ/olSwcxIG84dh/cg==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-grab": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-grab/-/interaction-external-grab-3.7.1.tgz", + "integrity": "sha512-JMYpFW+7YvkpK5MYlt4Ec3Gwb5ZxS7RLVL8IRUSd5yJOw25husPTYg+FQywxrt5WhKe+tPsCAYo+uGIbTTHi9w==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-pause": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-pause/-/interaction-external-pause-3.7.1.tgz", + "integrity": "sha512-Kkp+7sCe24hawH0XvS1V6UCCuHfMvpLK7oseqSam9Gt4SyGrFvaqIXxkjXhRhn9MysJyKFPBV4/dtBM1HR9p6A==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-push": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-push/-/interaction-external-push-3.7.1.tgz", + "integrity": "sha512-4VoaR5jvXgQdB7irtq4uSZYr5c+D6TBTVEnLVpBfJhUs6jhw6mgN5g7yp5izIYkK0AlcO431MHn8dvJacvRLDw==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-remove": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-remove/-/interaction-external-remove-3.7.1.tgz", + "integrity": "sha512-FRBW7U7zD5MkO6/b7e8iSMk/UTtRLY2XiIVFZNsKri3Re3yPpvZzzd5tl2YlYGQlg1Xc+K8SJYMQQA3PtgQ/Tg==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-repulse": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-repulse/-/interaction-external-repulse-3.7.1.tgz", + "integrity": "sha512-mwM06dVmg2FEvHMQsPOfRBQWACbjf3qnelODkqI9DSVxQ0B8DESP4BYNXyraFGYv00YiPzRv5Xy/uejHdbsQUA==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-external-slow": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-external-slow/-/interaction-external-slow-3.7.1.tgz", + "integrity": "sha512-CfCAs3kUQC3pLOj0dbzn5AolQyBHgjxORLdfnYBhepvFV1BXB+4ytChRfXBzjykBPI6U+rCnw5Fk/vVjAroSFA==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-particles-attract": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-particles-attract/-/interaction-particles-attract-3.7.1.tgz", + "integrity": "sha512-UABbBORKaiItAT8vR0t4ye2H3VE6/Ah4zcylBlnq0Jd5yDkyP4rnkwhicaY6y4Zlfwyx+0PWdAC4f/ziFAyObg==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-particles-collisions": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-particles-collisions/-/interaction-particles-collisions-3.7.1.tgz", + "integrity": "sha512-0GY9++Gn2KXViyeifXWkH7a2UO5+uRwyS1rDeTN8eleyiq2j9zQf4xztZEIft8T0hTetq2rkWxQ92j2kev6NVA==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/interaction-particles-links": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/interaction-particles-links/-/interaction-particles-links-3.7.1.tgz", + "integrity": "sha512-BxCXAAOBNmEvlyOQzwprryW8YdtMIop2v4kgSCff5MCtDwYWoQIfzaQlWbBAkD9ey6BoF8iMjhBUaY1MnDecTA==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/move-base": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/move-base/-/move-base-3.7.1.tgz", + "integrity": "sha512-LPtMHwJHhzwfRIcSAk814fY9NcRiICwaEbapaJSYyP1DwscSXqOWoyAEWwzV9hMgAcPdsED6nGeg8RCXGm58lw==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/move-parallax": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/move-parallax/-/move-parallax-3.7.1.tgz", + "integrity": "sha512-B40azo6EJyMdI+kmIxpqWDaObPwODTYLDCikzkZ73n5tS6OhFUlkz81Scfo+g1iGTdryKFygUKhVGcG1EFuA5g==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/plugin-easing-quad": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/plugin-easing-quad/-/plugin-easing-quad-3.7.1.tgz", + "integrity": "sha512-nSwKCRe6C/noCi3dyZlm1GiQGask0aXdWDuS36b82iwzwQ01cBTXeXR25mLr4fsfMLFfYAZXyBxEMMpw3rkSiw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/plugin-hex-color": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/plugin-hex-color/-/plugin-hex-color-3.7.1.tgz", + "integrity": "sha512-7xu3MV8EdNNejjYyEmrq5fCDdYAcqz/9VatLpnwtwR5Q5t2qI0tD4CrcGaFfC/rTAVJacfiJe02UV/hlj03KKA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/plugin-hsl-color": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/plugin-hsl-color/-/plugin-hsl-color-3.7.1.tgz", + "integrity": "sha512-zzAI1CuoCMBJhgeYZ5Rq42nGbPg35ZzIs11eQegjsWG5Msm5QKSj60qPzERnoUcCc4HCKtIWP7rYMz6h3xpoEg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/plugin-rgb-color": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/plugin-rgb-color/-/plugin-rgb-color-3.7.1.tgz", + "integrity": "sha512-taEraTpCYR6jpjflqBL95tN0zFU8JrAChXTt8mxVn7gddxoNMHI/LGymEPRCweLukwV6GQyAGOkeGEdWDPtYTA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/react": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@tsparticles/react/-/react-3.0.0.tgz", + "integrity": "sha512-hjGEtTT1cwv6BcjL+GcVgH++KYs52bIuQGW3PWv7z3tMa8g0bd6RI/vWSLj7p//NZ3uTjEIeilYIUPBh7Jfq/Q==", + "peerDependencies": { + "@tsparticles/engine": "^3.0.2", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@tsparticles/shape-circle": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/shape-circle/-/shape-circle-3.7.1.tgz", + "integrity": "sha512-kmOWaUuFwuTtcCFYjuyJbdA5qDqWdGsharLalYnIczkLu2c1I8jJo/OmGePKhWn62ocu7mqKMomfElkIcw2AsA==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/shape-emoji": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/shape-emoji/-/shape-emoji-3.7.1.tgz", + "integrity": "sha512-mX18c/xhYVljS/r5Xbowzclw+1YwhtWoQFOOfkmjjZppA+RjgcwSKLvH6E20PaH1yVTjBOfSF+3POKpwsULzTg==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/shape-image": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/shape-image/-/shape-image-3.7.1.tgz", + "integrity": "sha512-eDzfkQhqLY6fb9QH2Vo9TGfdJBFFpYnojhxQxc7IdzIwOFMD3JK4B52RVl9oowR+rNE8dNp6P2L+eMAF4yld0g==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/shape-line": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/shape-line/-/shape-line-3.7.1.tgz", + "integrity": "sha512-lMPYApUpg7avxmYPfHHr4dQepZSNn/g0Q1/g2+lnTi8ZtUBiCZ2WMVy9R3GOzyozbnzigLQ6AJRnOpsUZV7H4g==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/shape-polygon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/shape-polygon/-/shape-polygon-3.7.1.tgz", + "integrity": "sha512-5FrRfpYC3qnvV2nXBLE4Q0v+SMNWJO8xgzh6MBFwfptvqH4EOrqc/58eS5x0jlf+evwf9LjPgeGkOTcwaHHcYQ==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/shape-square": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/shape-square/-/shape-square-3.7.1.tgz", + "integrity": "sha512-7VCqbRwinjBZ+Ryme27rOtl+jKrET8qDthqZLrAoj3WONBqyt+R9q6SXAJ9WodqEX68IBvcluqbFY5qDZm8iAQ==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/shape-star": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/shape-star/-/shape-star-3.7.1.tgz", + "integrity": "sha512-3G4oipioyWKLEQYT11Sx3k6AObu3dbv/A5LRqGGTQm5IR6UACa+INwykZYI0a+MdJJMb83E0e4Fn3hlZbi0/8w==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/slim": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/slim/-/slim-3.7.1.tgz", + "integrity": "sha512-OtJEhud2KleX7OxiG2r/VYriHNIwTpFm3sPFy4EOJzAD0EW7KZoKXGpGn5gwGI1NWeB0jso92yNTrTC2ZTW0qw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/matteobruni" + }, + { + "type": "github", + "url": "https://github.com/sponsors/tsparticles" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/matteobruni" + } + ], + "license": "MIT", + "dependencies": { + "@tsparticles/basic": "3.7.1", + "@tsparticles/engine": "3.7.1", + "@tsparticles/interaction-external-attract": "3.7.1", + "@tsparticles/interaction-external-bounce": "3.7.1", + "@tsparticles/interaction-external-bubble": "3.7.1", + "@tsparticles/interaction-external-connect": "3.7.1", + "@tsparticles/interaction-external-grab": "3.7.1", + "@tsparticles/interaction-external-pause": "3.7.1", + "@tsparticles/interaction-external-push": "3.7.1", + "@tsparticles/interaction-external-remove": "3.7.1", + "@tsparticles/interaction-external-repulse": "3.7.1", + "@tsparticles/interaction-external-slow": "3.7.1", + "@tsparticles/interaction-particles-attract": "3.7.1", + "@tsparticles/interaction-particles-collisions": "3.7.1", + "@tsparticles/interaction-particles-links": "3.7.1", + "@tsparticles/move-parallax": "3.7.1", + "@tsparticles/plugin-easing-quad": "3.7.1", + "@tsparticles/shape-emoji": "3.7.1", + "@tsparticles/shape-image": "3.7.1", + "@tsparticles/shape-line": "3.7.1", + "@tsparticles/shape-polygon": "3.7.1", + "@tsparticles/shape-square": "3.7.1", + "@tsparticles/shape-star": "3.7.1", + "@tsparticles/updater-life": "3.7.1", + "@tsparticles/updater-rotate": "3.7.1", + "@tsparticles/updater-stroke-color": "3.7.1" + } + }, + "node_modules/@tsparticles/updater-color": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/updater-color/-/updater-color-3.7.1.tgz", + "integrity": "sha512-QimV3yn17dcdJx7PpTwLtw9BhkQ0q8qFF035OdcZpnynBPAO/hg0zvSMpMGoeuDVFH02wWBy4h2/BYCv6wh6Sw==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/updater-life": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/updater-life/-/updater-life-3.7.1.tgz", + "integrity": "sha512-NY5gUrgO5AsARNC0usP9PKahXf7JCxbP/H1vzTfA0SJw4veANfWTldOvhIlcm2CHVP5P1b827p0hWsBHECwz7A==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/updater-opacity": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/updater-opacity/-/updater-opacity-3.7.1.tgz", + "integrity": "sha512-YcyviCooTv7SAKw7sxd84CfJqZ7dYPSdYZzCpedV6TKIObRiwLqXlyLXQGJ3YltghKQSCSolmVy8woWBCDm1qA==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/updater-out-modes": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/updater-out-modes/-/updater-out-modes-3.7.1.tgz", + "integrity": "sha512-Cb5sWquRtUYLSiFpmBjjYKRdpNV52diCo9+qMtK1oVlldDBhUwqO+1TQjdlaA2yl5DURlY9ZfOHXvY+IT7CHCw==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/updater-rotate": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/updater-rotate/-/updater-rotate-3.7.1.tgz", + "integrity": "sha512-toVHwl+h6SvtA8dyxSA2kMH2QdDA71vehuAa+HoRqf1y06h5kxyYiMKZFHCqDJ6lFfRPs47MjrC9dD2bDz14MQ==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/updater-size": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/updater-size/-/updater-size-3.7.1.tgz", + "integrity": "sha512-+Y0H0PnDJVIsJ+zHTyubYu1jtRFmVnY1dAv3VCjScIDw6bcpL/ol+HrtHTGIX0WbMyUfjCyALfAoaXi/Wm8VcQ==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, + "node_modules/@tsparticles/updater-stroke-color": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@tsparticles/updater-stroke-color/-/updater-stroke-color-3.7.1.tgz", + "integrity": "sha512-VHhQkCNuxjx/Hy7A+g0Yijb24T0+wQ3jNsF/yfrR9dEdZWSBiimZLvV1bilPdAeEtieAJTAZo2VNhcD1snF0iQ==", + "license": "MIT", + "dependencies": { + "@tsparticles/engine": "3.7.1" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2376,6 +2844,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4767,6 +5244,19 @@ "react-dom": ">=16.8" } }, + "node_modules/react-toastify": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", + "integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", diff --git a/client_web/package.json b/client_web/package.json index 80fa366..b5426a6 100644 --- a/client_web/package.json +++ b/client_web/package.json @@ -16,6 +16,9 @@ "dependencies": { "@azure/msal-browser": "^3.27.0", "@react-oauth/google": "^0.12.1", + "@tsparticles/engine": "^3.7.1", + "@tsparticles/react": "^3.0.0", + "@tsparticles/slim": "^3.7.1", "@types/js-cookie": "^3.0.6", "antd": "^5.22.1", "axios": "^1.7.7", @@ -24,7 +27,8 @@ "js-cookie": "^3.0.5", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.28.0" + "react-router-dom": "^6.28.0", + "react-toastify": "^10.0.6" }, "devDependencies": { "@eslint/js": "^9.13.0", diff --git a/client_web/src/App.tsx b/client_web/src/App.tsx index 7fa89ee..f11e255 100644 --- a/client_web/src/App.tsx +++ b/client_web/src/App.tsx @@ -1,6 +1,17 @@ import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import { ContextManager } from "./Context/ContextManager.tsx"; -import { uri } from './Config/uri.ts'; +// @ts-ignore +import { uri } from '@Config/uri'; + +import { useEffect, useMemo, useState } from "react"; +import Particles, { initParticlesEngine } from "@tsparticles/react"; +import { + type Container, + type ISourceOptions, + MoveDirection, + OutMode, +} from "@tsparticles/engine"; +import { loadSlim } from "@tsparticles/slim"; import Home from './Pages/Home'; import NotFound from './Pages/NotFound'; @@ -19,30 +30,140 @@ import WorkflowsTable from "./Pages/Workflows/WorkflowsTable"; import Dashboard from './Pages/Dashboard/Dashboard.tsx'; +import { ToastContainer } from 'react-toastify'; +// @ts-ignore +import 'react-toastify/dist/ReactToastify.css'; + const App = () => { + const [init, setInit] = useState(false); + + useEffect(() => { + initParticlesEngine(async (engine) => { + await loadSlim(engine); + }).then(() => { + setInit(true); + }); + }, []); + + const particlesLoaded = async (container?: Container): Promise => { + console.log(container); + }; + + const options: ISourceOptions = useMemo( + () => ({ + background: { + color: { + value: "#0077b6", + }, + }, + fpsLimit: 120, + interactivity: { + events: { + onClick: { + enable: true, + mode: "push", + }, + onHover: { + enable: true, + mode: "repulse", + }, + }, + modes: { + push: { + quantity: 1, + }, + repulse: { + distance: 200, + duration: 0.4, + }, + }, + }, + particles: { + color: { + value: "#ffffff", + }, + links: { + color: "#ffffff", + distance: 150, + enable: true, + opacity: 0.5, + width: 1, + }, + move: { + direction: MoveDirection.none, + enable: true, + outModes: { + default: OutMode.out, + }, + random: false, + speed: 6, + straight: false, + }, + number: { + density: { + enable: true, + }, + value: 80, + }, + opacity: { + value: 0.5, + }, + shape: { + type: "circle", + }, + size: { + value: { min: 1, max: 5 }, + }, + }, + detectRetina: true, + }), + [], + ); + return ( - - - - - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - - - - + <> + + {init && ( + + )} + + + + + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + } /> + + + + + ); }; diff --git a/client_web/src/Components/LinkButton.tsx b/client_web/src/Components/LinkButton.tsx index 2e1146c..8bc4463 100644 --- a/client_web/src/Components/LinkButton.tsx +++ b/client_web/src/Components/LinkButton.tsx @@ -6,7 +6,7 @@ interface LinkButtonProps { text: string; url?: string; style?: React.CSSProperties; - type?: "primary" | "default" | "dashed" | "link" | "text" | undefined; + type?: "primary" | "default" | "dashed" | "link" | "text" | "danger" | undefined; goBack?: boolean; } @@ -21,8 +21,10 @@ const LinkButton: React.FC = ({ text, url, type = "primary", st } }; + const buttonStyle = type === "danger" ? { ...style, backgroundColor: 'red', borderColor: 'red', color: 'white' } : style; + return ( - ); diff --git a/client_web/src/Pages/Auth/Forms/Login.tsx b/client_web/src/Pages/Auth/Forms/Login.tsx index dbfaa37..b7508c6 100644 --- a/client_web/src/Pages/Auth/Forms/Login.tsx +++ b/client_web/src/Pages/Auth/Forms/Login.tsx @@ -41,9 +41,6 @@ const Login = () => { return (
{ width: '100%', top: 0, left: 0 - }}> + }} role="main">
{ @@ -41,9 +42,6 @@ const Register = () => { return (
{ width: '100%', top: 0, left: 0 - }}> + }} role="main"> { return ( -
- {/* Header */} - + <div style={{padding: 24, position: 'relative', zIndex: 1}} role="main"> + <Title level={3} style={{marginBottom: 16}}> Dashboard {/* Stats Cards */} - + - - + + {/* TODO: Replace X with the number of active automations */} - X + X Active Automations - - + + {/* TODO: Replace X with the number of tasks completed */} - X + X Tasks Completed - - + + {/* TODO: Replace X with the number of pending updates */} - X + X Pending Updates @@ -54,15 +53,15 @@ const Dashboard: FC = () => { {/* Main Content Area */} - + {/* Activity content would go here */} - - - - + + + + diff --git a/client_web/src/Pages/Home.tsx b/client_web/src/Pages/Home.tsx index b39954c..f270a28 100644 --- a/client_web/src/Pages/Home.tsx +++ b/client_web/src/Pages/Home.tsx @@ -96,18 +96,20 @@ const Home = () => { ].map((item) => ( - - {item.step} - - {item.title} - {item.description} + + + {item.step} + + {item.title} + {item.description} + ))} diff --git a/client_web/src/Pages/Workflows/CreateWorkflow.tsx b/client_web/src/Pages/Workflows/CreateWorkflow.tsx index f015b55..73dc2a8 100644 --- a/client_web/src/Pages/Workflows/CreateWorkflow.tsx +++ b/client_web/src/Pages/Workflows/CreateWorkflow.tsx @@ -1,16 +1,25 @@ -import React from "react"; -import {Button, Row, Typography, Spin, Space} from "antd"; +import React, { useState } from "react"; +import { Button, Row, Typography, Spin, Space, Card, List, Form, Input, Col, Collapse } from "antd"; // @ts-ignore import Security from "@/Components/Security.tsx"; // @ts-ignore import LinkButton from "@/Components/LinkButton.tsx"; +// @ts-ignore +import { normalizeName } from "@/Pages/Workflows/CreateWorkflow.utils"; // @ts-ignore -import { Workflow } from "@/types"; +import { About, Client, Server, Service, Action, Reaction } from "@/types"; +import {toast} from "react-toastify"; + +const { Title, Text } = Typography; +const { Panel } = Collapse; -const { Title } = Typography; +interface SelectedItem { + id: string; + name: string; +} -const _workflow: Workflow = { +const _data: About = { // Fixtures client: { host: "10.101.53.35" }, @@ -39,6 +48,75 @@ const _workflow: Workflow = { description: "The user likes a message" } ] + }, + { + name: "twitter", + actions: [ + { + name: "new_tweet", + description: "A new tweet is posted" + }, + { + name: "new_follower", + description: "The user gains a new follower" + } + ], + reactions: [ + { + name: "retweet", + description: "The user retweets a tweet" + }, + { + name: "like_tweet", + description: "The user likes a tweet" + } + ] + }, + { + name: "github", + actions: [ + { + name: "new_issue", + description: "A new issue is created in a repository" + }, + { + name: "new_pull_request", + description: "A new pull request is created in a repository" + } + ], + reactions: [ + { + name: "create_issue", + description: "The user creates a new issue" + }, + { + name: "merge_pull_request", + description: "The user merges a pull request" + } + ] + }, + { + name: "slack", + actions: [ + { + name: "new_message", + description: "A new message is posted in a channel" + }, + { + name: "new_reaction", + description: "A new reaction is added to a message" + } + ], + reactions: [ + { + name: "send_message", + description: "The user sends a message to a channel" + }, + { + name: "add_reaction", + description: "The user adds a reaction to a message" + } + ] } ] } @@ -46,35 +124,295 @@ const _workflow: Workflow = { const CreateWorkflow: React.FC = () => { const [loading, setLoading] = React.useState(false); - const [_, setWorkflow] = React.useState(null); + const [about, setAbout] = React.useState(null); + const [selectedActions, setSelectedActions] = React.useState([]); + const [selectedReactions, setSelectedReactions] = React.useState([]); + const [workflowName, setWorkflowName] = React.useState(''); + const [workflowDescription, setWorkflowDescription] = React.useState(''); + const [workflowNameTouched, setWorkflowNameTouched] = React.useState(false); + const [activeActionKeys, setActiveActionKeys] = useState([]); + const [activeReactionKeys, setActiveReactionKeys] = useState([]); React.useEffect(() => { setLoading(true); - setWorkflow(_workflow); // TODO: Fetch workflow from the server + setAbout(_data); // TODO: Fetch workflow from the server setLoading(false); }, []); + const toggleAction = (actionName: string) => { + setSelectedActions(prev => [ + ...prev, + { id: `${actionName}-${Date.now()}`, name: actionName } + ]); + }; + + const toggleReaction = (reactionName: string) => { + setSelectedReactions(prev => [ + ...prev, + { id: `${reactionName}-${Date.now()}`, name: reactionName } + ]); + }; + + const handleCreateWorkflow = () => { + toast.error("API not connected yet"); + if (!workflowName) { + alert("Workflow name is required"); + return; + } + console.log('Create Workflow', workflowName, workflowDescription, selectedActions, selectedReactions); + }; + + const handleFoldAllActions = () => { + setActiveActionKeys([]); + }; + + const handleUnfoldAllActions = () => { + if (about) { + setActiveActionKeys(about.server.services.map((service: Service) => service.name)); + } + }; + + const handleFoldAllReactions = () => { + setActiveReactionKeys([]); + }; + + const handleUnfoldAllReactions = () => { + if (about) { + setActiveReactionKeys(about.server.services.map((service: Service) => service.name)); + } + }; + return ( -
- + <div style={{ padding: '16px 24px', position: 'relative', zIndex: 1 }} role="main"> + <Title level={3} style={{ marginBottom: 16 }}> Create Workflow {loading ? ( - + ) : ( <> - - - This is where you would create a new workflow. - + + + + + + setWorkflowName(e.target.value)} + onBlur={() => setWorkflowNameTouched(true)} + aria-required="true" + /> + + + setWorkflowDescription(e.target.value)} + /> + + + + - - - - - + + + + + + + + + + {about?.server?.services.map((service: Service) => ( + + ( + toggleAction(action.name)} + style={{ + cursor: 'pointer', + backgroundColor: selectedActions.some(a => a.name === action.name) + ? '#e6f7ff' + : 'transparent' + }} + > + + {service.name} +
+ {action.description} + + } + /> +
+ )} + /> +
+ ))} +
+
+ + + + +
+ {selectedActions.length > 0 && ( + + When: + + {selectedActions.map(action => ( + { + e.stopPropagation(); + setSelectedActions(prev => prev.filter(a => a.id !== action.id)); + }} + > + × + + } + > + {normalizeName(action.name)} + + ))} + + + )} + + {selectedReactions.length > 0 && ( + + Then: + + {selectedReactions.map(reaction => ( + { + e.stopPropagation(); + setSelectedReactions(prev => prev.filter(r => r.id !== reaction.id)); + }} + > + × + + } + > + {normalizeName(reaction.name)} + + ))} + + + )} + + + + + + +
+
+ + + + + + + + + + {about?.server?.services.map((service: Service) => ( + + ( + toggleReaction(reaction.name)} + style={{ + cursor: 'pointer', + backgroundColor: selectedReactions.some(r => r.name === reaction.name) + ? '#e6f7ff' + : 'transparent' + }} + > + + {service.name} +
+ {reaction.description} + + } + /> +
+ )} + /> +
+ ))} +
+
+
)} diff --git a/client_web/src/Pages/Workflows/CreateWorkflow.utils.ts b/client_web/src/Pages/Workflows/CreateWorkflow.utils.ts new file mode 100644 index 0000000..424b810 --- /dev/null +++ b/client_web/src/Pages/Workflows/CreateWorkflow.utils.ts @@ -0,0 +1,3 @@ +export const normalizeName = (name: string): string => { + return name.replace(/_/g, " ").replace(/\b\w/g, (c: string): string => c.toUpperCase()); +}; diff --git a/client_web/src/Pages/Workflows/WorkflowsTable.tsx b/client_web/src/Pages/Workflows/WorkflowsTable.tsx index a3ffa3e..c372afb 100644 --- a/client_web/src/Pages/Workflows/WorkflowsTable.tsx +++ b/client_web/src/Pages/Workflows/WorkflowsTable.tsx @@ -1,5 +1,5 @@ import React from "react"; -import {Button, Row, Typography, Spin, Space} from "antd"; +import { Row, Typography, Spin, Space } from "antd"; // @ts-ignore import Security from "@/Components/Security.tsx"; // @ts-ignore @@ -22,8 +22,8 @@ const WorkflowTable: React.FC = () => { return ( -
- + <div style={{padding: 24, position: 'relative', zIndex: 1}} role="main"> + <Title level={3} style={{marginBottom: 24}}> Create Workflow @@ -38,8 +38,7 @@ const WorkflowTable: React.FC = () => { - - + diff --git a/client_web/src/types.ts b/client_web/src/types.ts index 07ef7e5..9208147 100644 --- a/client_web/src/types.ts +++ b/client_web/src/types.ts @@ -1,11 +1,19 @@ +export type Parameter = { + name: string; + description: string; + type: "string" | "number" | "datetime"; +}; + export type Action = { name: string; description: string; + parameters: Parameter[]; }; export type Reaction = { name: string; description: string; + parameters: Parameter[]; }; export type Service = { @@ -23,7 +31,7 @@ export type Client = { host: string; }; -export type Workflow = { +export type About = { client: Client; server: Server; }; diff --git a/client_web/tsconfig.json b/client_web/tsconfig.json index d1c8ece..76f5e42 100644 --- a/client_web/tsconfig.json +++ b/client_web/tsconfig.json @@ -10,9 +10,8 @@ "@/*": ["src/*"], "@Config/*": ["src/Config/*"] }, - "suppress": [ - 2307 - ] + "skipLibCheck": true, + "strict": true }, "include": ["src"] } diff --git a/docs/source/client_web/index.rst b/docs/source/client_web/index.rst index 0f4fcca..e357c70 100644 --- a/docs/source/client_web/index.rst +++ b/docs/source/client_web/index.rst @@ -32,7 +32,8 @@ Contents -------- .. toctree:: - :maxdepth: 2 + :maxdepth: 2 - installation - context + installation + context + workflowCreation diff --git a/docs/source/client_web/workflowCreation.rst b/docs/source/client_web/workflowCreation.rst new file mode 100644 index 0000000..4a32989 --- /dev/null +++ b/docs/source/client_web/workflowCreation.rst @@ -0,0 +1,52 @@ +Create Workflow +=============== + +The "Create Workflow" feature allows users to create custom workflows by selecting actions and reactions from various services. Follow the steps below to create a workflow. + +Steps to Create a Workflow +-------------------------- + +1. **Enter Workflow Name**: + - Provide a name for your workflow in the "Enter workflow name" field. + - This field is required. + +2. **Enter Workflow Description**: + - Optionally, provide a description for your workflow in the "Enter workflow description" field. + +3. **Select Actions**: + - Browse through the available actions listed under "Available Actions". + - Click on an action to add it to your workflow. + - Use the "Fold All" and "Unfold All" buttons to manage the visibility of action categories. + +4. **Select Reactions**: + - Browse through the available reactions listed under "Available Reactions". + - Click on a reaction to add it to your workflow. + - Use the "Fold All" and "Unfold All" buttons to manage the visibility of reaction categories. + +5. **Review Selected Items**: + - The selected actions and reactions will be displayed in the "Selected Items" section. + - You can remove any selected action or reaction by clicking the "×" button next to it. + +6. **Clear Selections**: + - Use the "Clear Actions" button to remove all selected actions. + - Use the "Clear Reactions" button to remove all selected reactions. + +7. **Create Workflow**: + - Click the "Create Workflow" button to create your workflow. + - Ensure that you have provided a workflow name and selected at least one action and one reaction. + - If the workflow creation is successful, a confirmation message will be displayed. + +8. **Cancel**: + - Click the "Cancel" button to discard the workflow creation process and go back to the previous page. + +Accessibility Features +---------------------- + +- The "Create Workflow" page is designed to be accessible with keyboard navigation and screen readers. +- The "Fold All" and "Unfold All" buttons help manage the visibility of action and reaction categories for easier navigation. + +Error Handling +-------------- + +- If the workflow name is not provided, an error message will be displayed indicating that the workflow name is required. +- If the workflow creation fails, an error message will be displayed.