diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..9f1f131d6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.vite
\ No newline at end of file
diff --git a/Client/package-lock.json b/Client/package-lock.json
index 2ad129bd4..fe3d684f5 100644
--- a/Client/package-lock.json
+++ b/Client/package-lock.json
@@ -527,358 +527,6 @@
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
- "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
- "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
- "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
- "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
- "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
- "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
- "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
- "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
- "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
- "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
- "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
- "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
- "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
- "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
- "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
- "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
- "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
- "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
- "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
- "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
- "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
- "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@esbuild/win32-x64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
@@ -975,9 +623,9 @@
}
},
"node_modules/@floating-ui/dom": {
- "version": "1.6.4",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.4.tgz",
- "integrity": "sha512-0G8R+zOvQsAG1pg2Q99P21jiqxqGBW1iRe/iXHsBRBxnpXKFI8QwbB4x5KmYLggNO5m34IQgOIu9SCRfR/WWiQ==",
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
+ "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
"dependencies": {
"@floating-ui/core": "^1.0.0",
"@floating-ui/utils": "^0.2.0"
@@ -1476,208 +1124,13 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.16.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz",
- "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==",
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
+ "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==",
"engines": {
"node": ">=14.0.0"
}
},
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
- "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
- "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
- "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
- "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
- "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
- "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
- "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
- "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
- "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
- "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
- "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
- "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
- "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
- "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.17.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
- "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
@@ -2981,20 +2434,6 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -4186,9 +3625,9 @@
]
},
"node_modules/react": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
- "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -4197,15 +3636,15 @@
}
},
"node_modules/react-dom": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
- "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"dependencies": {
"loose-envify": "^1.1.0",
- "scheduler": "^0.23.2"
+ "scheduler": "^0.23.0"
},
"peerDependencies": {
- "react": "^18.3.1"
+ "react": "^18.2.0"
}
},
"node_modules/react-is": {
@@ -4223,11 +3662,11 @@
}
},
"node_modules/react-router": {
- "version": "6.23.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz",
- "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==",
+ "version": "6.23.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
+ "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==",
"dependencies": {
- "@remix-run/router": "1.16.0"
+ "@remix-run/router": "1.16.1"
},
"engines": {
"node": ">=14.0.0"
@@ -4237,12 +3676,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.23.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz",
- "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==",
+ "version": "6.23.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz",
+ "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==",
"dependencies": {
- "@remix-run/router": "1.16.0",
- "react-router": "6.23.0"
+ "@remix-run/router": "1.16.1",
+ "react-router": "6.23.1"
},
"engines": {
"node": ">=14.0.0"
diff --git a/Client/src/Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx
new file mode 100644
index 000000000..f3363e866
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx
@@ -0,0 +1,71 @@
+import "./announcementUpdateSubscription.css";
+import React from "react";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - Title text for the update subscription dialog (required)
+ * @param {string} props.text - Body text for the update subscription dialog (required)
+ * @param {string} props.cancel - Text for the negative (cancel) button (required)
+ * @param {string} props.positive - Text for the positive button (required)
+ * @param {string} props.header - Title text for the email subscription section (required)
+ * @param {string} props.button - Text for the subscribe button (required)
+ * @returns {JSX.Element} - Renders the announcement update subscription component
+ */
+
+const AnnouncementUpdateSubscription = ({
+ title,
+ text,
+ cancel,
+ positive,
+ header,
+ button,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
+
{title}
+
+
{text}
+
+
+ {cancel}
+ {positive}
+
+
+
+
+
{header}
+
+
+ {button}
+
+
+
+ );
+};
+
+AnnouncementUpdateSubscription.propTypes = {
+ title: PropTypes.string.isRequired,
+ text: PropTypes.string.isRequired,
+ cancel: PropTypes.string.isRequired,
+ positive: PropTypes.string.isRequired,
+ header: PropTypes.string.isRequired,
+ button: PropTypes.string.isRequired,
+};
+
+export default AnnouncementUpdateSubscription;
diff --git a/Client/src/Components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css
new file mode 100644
index 000000000..7974ee799
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css
@@ -0,0 +1,99 @@
+:root {
+ --update-subscription-color-0: #101828;
+ --update-subscription-color-1: #475467;
+ --update-subscription-color-2: #fff;
+ --update-subscription-color-3: #eaecf0;
+ --update-subscription-color-4: #7f56d9;
+ --update-subscription-color-5: #1570ef;
+ --update-subscription-color-6: #344054;
+ --update-subscription-color-7: #d0d5dd;
+ --update-subscription-padding-0: 12px 16px;
+ --update-subscription-margin-right: 12px;
+ --update-subscription-font-size: 13px;
+ --update-subscription-radius-0: 4px;
+ --spacing-1: 16px;
+ --spacing-2: 20px;
+}
+
+.update-subscription,
+.update-subscription-dialog {
+ max-width: 770px;
+ border-radius: 4px;
+ border: 1px solid #eaecf0;
+}
+
+.update-subscription-dialog {
+ padding: 15px 30px;
+}
+
+.subscription-dialog-title {
+ font-size: 1rem;
+ font-weight: 700;
+ color: var(--update-subscription-color-0);
+}
+
+.v-spacing-small {
+ height: 10px;
+}
+
+.v-spacing-medium {
+ height: 15px;
+}
+
+.subscription-dialog-text {
+ font-size: 13px;
+ color: var(--update-subscription-color-1);
+}
+
+.subscription-dialog-controls {
+ display: flex;
+}
+
+.controls-negative {
+ padding: var(--update-subscription-padding-0);
+ margin-right: var(--update-subscription-margin-right);
+ border-radius: var(--update-subscription-radius-0);
+ border: 1px solid var(--update-subscription-color-3);
+ background-color: var(--update-subscription-color-2);
+ font-size: var(--update-subscription-font-size);
+}
+
+.controls-positive,
+.subscribe-button {
+ padding: var(--update-subscription-padding-0);
+ margin-right: var(--update-subscription-margin-right);
+ border-radius: var(--update-subscription-radius-0);
+ border: 1px solid var(--update-subscription-color-4);
+ background-color: var(--update-subscription-color-5);
+ color: var(--update-subscription-color-2);
+ font-size: var(--update-subscription-font-size);
+}
+
+.update-subscription-title {
+ color: var(--update-subscription-color-6);
+ padding: 0 var(--spacing-2);
+ font-weight: 700;
+ font-size: 13px;
+ margin-bottom: 10px;
+}
+
+.subscription-email {
+ display: flex;
+ padding: 0 var(--spacing-2);
+}
+
+.subscription-email-field {
+ width: 290px;
+ height: 24px;
+ border-radius: var(--update-subscription-radius-0);
+ border: 1px solid var(--update-subscription-color-7);
+ box-shadow: 0px 1px 2px 0px rgba(101, 182, 28, 0.05);
+ padding: 5px 15px;
+ margin-right: var(--spacing-2);
+}
+
+.subscribe-button {
+ width: 100px;
+ height: 34px;
+ padding: 2px 15px;
+}
diff --git a/Client/src/Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx b/Client/src/Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx
new file mode 100644
index 000000000..2abbadb1e
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx
@@ -0,0 +1,55 @@
+import "./announcementsDualButton.css";
+import React from "react";
+import CloseIcon from "@mui/icons-material/Close";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.subject - The announcement subject text
+ * @param {string} props.body - The announcement body text content
+ * @param {string} props.esc - The text for the escape button
+ * @param {string} props.primary - The text for the primary button
+ * @returns {JSX.Element} - Renders the announcement dual button component
+ */
+
+const AnnouncementsDualButton = ({ subject, body, esc, primary }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
+
{subject}
+
+
{body}
+
+
+
+
+
+
+
+ );
+};
+
+AnnouncementsDualButton.propTypes = {
+ subject: PropTypes.string.isRequired,
+ body: PropTypes.string.isRequired,
+ esc: PropTypes.string.isRequired,
+ primary: PropTypes.string.isRequired,
+};
+
+export default AnnouncementsDualButton;
diff --git a/Client/src/Components/Announcements/AnnouncementsDualButton/announcementsDualButton.css b/Client/src/Components/Announcements/AnnouncementsDualButton/announcementsDualButton.css
new file mode 100644
index 000000000..837fd0bb5
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsDualButton/announcementsDualButton.css
@@ -0,0 +1,38 @@
+:root {
+ --announcement-border-color: #d0d5dd;
+ --announcement-border-radius: 4px;
+ --announcement-border-radius-2: 8px;
+ /* --announcement-font-family: "Roboto", "Helvetica", "Arial", sans-serif; */
+ --announcement-font-size-0: 13px;
+ --announcement-font-color-0: #344054;
+ --announcement-font-color-1: #475467;
+ --announcement-font-color-2: #1570ef;
+ --announcement-padding: 16px;
+ --info-icon-frame-color: #eaecf0;
+}
+
+.announcement-without-tile {
+ max-width: 343px;
+ width: fit-content;
+ display: flex;
+ border: 1px solid var(--announcement-border-color);
+ /* font-family: var(--announcement-font-family); */
+ padding: var(--announcement-padding);
+ border-radius: var(--announcement-border-radius);
+}
+
+.h-spacing {
+ width: 20px;
+}
+
+.announcement-without-content-subject {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-0);
+ font-weight: 700;
+ margin-bottom: 10px;
+}
+
+.announcement-without-content-body {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-1);
+}
diff --git a/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx b/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx
new file mode 100644
index 000000000..574c255a0
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx
@@ -0,0 +1,65 @@
+import "./announcementsDualButtonWithIcon.css";
+import React from "react";
+import CloseIcon from "@mui/icons-material/Close";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {React.ReactNode} props.icon - Optional React node for an announcement icon
+ * @param {string} props.subject - The announcement subject text
+ * @param {string} props.body - The announcement body text content
+ * @param {string} props.esc - The text for the escape button (usually "Close")
+ * @param {string} props.primary - The text for the primary button
+ * @returns {JSX.Element} - Renders the announcement dual button with icon component
+ */
+
+const AnnouncementsDualButtonWithIcon = ({
+ icon,
+ subject,
+ body,
+ esc,
+ primary,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ {icon &&
{icon}
}
+
+ {subject && (
+
{subject}
+ )}
+ {body &&
{body}
}
+ {(esc || primary) && (
+
+ {esc &&
{esc}
}
+ {primary && (
+
{primary}
+ )}
+
+ )}
+
+
+
+
+
+ );
+};
+
+AnnouncementsDualButtonWithIcon.propTypes = {
+ icon: PropTypes.object,
+ subject: PropTypes.string,
+ body: PropTypes.string,
+ esc: PropTypes.string,
+ primary: PropTypes.string,
+};
+
+export default AnnouncementsDualButtonWithIcon;
diff --git a/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css b/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css
new file mode 100644
index 000000000..80c6ae265
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css
@@ -0,0 +1,69 @@
+:root {
+ --announcement-border-color: #d0d5dd;
+ --announcement-border-radius: 4px;
+ --announcement-border-radius-2: 8px;
+ --announcement-font-size-0: 13px;
+ --announcement-font-color-0: #344054;
+ --announcement-font-color-1: #475467;
+ --announcement-font-color-2: #1570ef;
+ --announcement-padding: 16px;
+ --info-icon-frame-color: #eaecf0;
+}
+
+.announcement-tile {
+ width: fit-content;
+ display: flex;
+ border: 1px solid var(--announcement-border-color);
+ padding: var(--announcement-padding);
+ border-radius: var(--announcement-border-radius);
+}
+
+.info-icon-frame {
+ display: flex;
+ width: 40px;
+ height: 40px;
+ border: 1px solid var(--info-icon-frame-color);
+ border-radius: var(--announcement-border-radius-2);
+ justify-content: center;
+ align-items: center;
+}
+
+.announcement-content {
+ padding: 0 var(--announcement-padding);
+}
+
+.announcement-content-subject {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-0);
+ font-weight: 700;
+ margin-bottom: 10px;
+}
+
+.announcement-content-body {
+ font-size: var(--announcement-font-size-0);
+ color: var(--announcement-font-color-1);
+ font-weight: 300;
+}
+
+.announcement-content-controllers {
+ display: flex;
+ font-size: var(--announcement-font-size-0);
+ margin-top: 15px;
+}
+
+.controllers-button-esc {
+ font-size: var(--announcement-font-size-0);
+ font-weight: 600;
+ margin-right: 12px;
+}
+
+.controllers-button-primary {
+ font-size: var(--announcement-font-size-0);
+ font-weight: 600;
+ color: var(--announcement-font-color-2);
+}
+
+.announcement-close {
+ width: 24px;
+ height: 24px;
+}
diff --git a/Client/src/Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx b/Client/src/Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx
new file mode 100644
index 000000000..20a179052
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx
@@ -0,0 +1,36 @@
+import "./announcementsMessageBar.css";
+import React from "react";
+import CloseIcon from "@mui/icons-material/Close";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.message - The announcement message text content (required)
+ * @returns {JSX.Element} - Renders the announcement message bar component
+ */
+const AnnouncementsMessageBar = ({ message }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+AnnouncementsMessageBar.propTypes = {
+ message: PropTypes.string.isRequired,
+};
+
+export default AnnouncementsMessageBar;
diff --git a/Client/src/Components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css b/Client/src/Components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css
new file mode 100644
index 000000000..2e9f95e83
--- /dev/null
+++ b/Client/src/Components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css
@@ -0,0 +1,12 @@
+:root {
+ --announcement-font-size-0: 13px;
+ --announcement-padding: 16px;
+}
+
+.announcement-messagebar-body {
+ font-size: var(--announcement-font-size-0);
+ padding: 0 var(--announcement-padding) 0 0;
+ justify-content: center;
+ align-items: center;
+ margin: auto;
+}
diff --git a/Client/src/Components/Charts/Servers/ServerStatus.jsx b/Client/src/Components/Charts/Servers/ServerStatus.jsx
new file mode 100644
index 000000000..f910abf27
--- /dev/null
+++ b/Client/src/Components/Charts/Servers/ServerStatus.jsx
@@ -0,0 +1,37 @@
+import React from "react";
+import "./serverStatus.css";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the server status (required)
+ * @param {string} props.value - The value text to be displayed (required)
+ * @param {string} props.state - The state of the server (e.g., "online", "offline", "warning") (required)
+ * @returns {JSX.Element} - Renders the server status component
+ */
+const ServerStatus = ({ title, value, state }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+ServerStatus.propTypes = {
+ title: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ state: PropTypes.string.isRequired,
+};
+
+export default ServerStatus;
diff --git a/Client/src/Components/Charts/Servers/serverStatus.css b/Client/src/Components/Charts/Servers/serverStatus.css
new file mode 100644
index 000000000..9126d9b3b
--- /dev/null
+++ b/Client/src/Components/Charts/Servers/serverStatus.css
@@ -0,0 +1,44 @@
+:root {
+ --spacing-general-0: 10px 25px;
+ --spacing-general-1: 20px;
+ --color-border-0: #eaecf0;
+ --border-radius-0: 4px;
+ --font-size-0: 1rem;
+ --font-size-2: 36px;
+
+ --color-up: #17b26a;
+ --color-down: #f04438;
+ --color-pause: #fdb022;
+}
+
+.server-status-tile {
+ width: 230px;
+ margin: var(--spacing-general-0);
+ margin-left: 10px;
+ padding: var(--spacing-general-1);
+ border: 1px solid var(--color-border-0);
+ border-radius: var(--border-radius-0);
+}
+
+.server-status-tile-title {
+ font-size: var(--font-size-0);
+ font-weight: bold;
+ margin-bottom: 25px;
+}
+
+.server-status-tile-value {
+ font-size: var(--font-size-2);
+ font-weight: bolder;
+}
+
+.up {
+ color: var(--color-up);
+}
+
+.down {
+ color: var(--color-down);
+}
+
+.pause {
+ color: var(--color-pause);
+}
diff --git a/Client/src/Components/Charts/Statistics/Statistic.jsx b/Client/src/Components/Charts/Statistics/Statistic.jsx
new file mode 100644
index 000000000..99ad2066f
--- /dev/null
+++ b/Client/src/Components/Charts/Statistics/Statistic.jsx
@@ -0,0 +1,35 @@
+import React from "react";
+import "./statistic.css";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the statistic (required)
+ * @param {string} props.value - The numerical or textual value for the statistic (required)
+ * @returns {JSX.Element} - Renders the statistic component
+ */
+const Statistic = ({ title, value }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+Statistic.propTypes = {
+ title: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+};
+
+export default Statistic;
diff --git a/Client/src/Components/Charts/Statistics/statistic.css b/Client/src/Components/Charts/Statistics/statistic.css
new file mode 100644
index 000000000..a8834199d
--- /dev/null
+++ b/Client/src/Components/Charts/Statistics/statistic.css
@@ -0,0 +1,31 @@
+:root {
+ --stats-spacing-general-0: 5px 10px;
+ --color-border-0: #eaecf0;
+ --border-radius-0: 4px;
+ --font-size-0: 1rem;
+ --font-size-1: 13px;
+ --font-color-0: #344054;
+ --margin-bottom-0: 10px;
+}
+
+.statistic-tile {
+ margin: var(--stats-spacing-general-0);
+ padding: var(--stats-spacing-general-0);
+ border: 1px solid var(--color-border-0);
+ border-radius: var(--border-radius-0);
+
+ width: 180px;
+}
+
+.statistic-tile-title {
+ font-size: var(--font-size-0);
+ color: var(--font-color-0);
+ margin-bottom: var(--margin-bottom-0);
+}
+
+.statistic-tile-value {
+ font-size: var(--font-size-1);
+ color: var(--font-color-0);
+ font-weight: 700;
+ margin-bottom: var(--margin-bottom-0);
+}
diff --git a/Client/src/Components/Icons/ComplexAlert/ComplexAlert.jsx b/Client/src/Components/Icons/ComplexAlert/ComplexAlert.jsx
new file mode 100644
index 000000000..67af3ab88
--- /dev/null
+++ b/Client/src/Components/Icons/ComplexAlert/ComplexAlert.jsx
@@ -0,0 +1,36 @@
+import "./complexAlert.css";
+import React from "react";
+import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
+import PropTypes from "prop-types";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {"red" | "green"} props.theme - The color theme for the alert (required) - must be either "red" or "green"
+ * @returns {JSX.Element} - Renders the complex alert component with dynamic color theme
+ */
+const ComplexAlert = ({ theme }) => {
+ if (theme === "red") {
+ return (
+
+ );
+ } else if (theme === "green") {
+ return (
+
+ );
+ }
+};
+
+ComplexAlert.propTypes = {
+ theme: PropTypes.string.isRequired,
+};
+
+export default ComplexAlert;
diff --git a/Client/src/Components/Icons/ComplexAlert/complexAlert.css b/Client/src/Components/Icons/ComplexAlert/complexAlert.css
new file mode 100644
index 000000000..ab3cea714
--- /dev/null
+++ b/Client/src/Components/Icons/ComplexAlert/complexAlert.css
@@ -0,0 +1,44 @@
+.icon-holder-outer-red {
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ border: 2px solid #d92d2019;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-holder-inner-red {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ border: 2px solid #d92d204d;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-holder-outer-green {
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ border: 2px solid #07945519;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-holder-inner-green {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ border: 2px solid #0794554d;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.icon-core {
+ width: 20px;
+ height: 20px;
+}
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx b/Client/src/Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx
new file mode 100644
index 000000000..42a3f2a8a
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx
@@ -0,0 +1,55 @@
+import "./dualButtonPopupModal.css";
+import React from "react";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {boolean} [props.open=true] - Controls the visibility of the modal (defaults to true)
+ * @param {string} props.subject - The title text for the modal (required)
+ * @param {string} props.description - The description text for the modal (required)
+ * @param {string} props.esc - The text for the discard button (usually "Cancel", "Dismiss" or "Discard") (required)
+ * @param {string} props.save - The text for the save button (required)
+ * @returns {JSX.Element} - Renders the dual button popup modal component
+ */
+const DualButtonPopupModal = ({
+ open = true,
+ subject,
+ description,
+ esc,
+ save,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
{subject}
+
{description}
+
+
+ {esc}
+ {save}
+
+
+ );
+};
+
+DualButtonPopupModal.propTypes = {
+ open: PropTypes.bool,
+ subject: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ esc: PropTypes.string.isRequired,
+ save: PropTypes.string.isRequired,
+};
+
+export default DualButtonPopupModal;
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css b/Client/src/Components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css
new file mode 100644
index 000000000..92ac7f5d6
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css
@@ -0,0 +1,60 @@
+:root {
+ --popup-color-border-0: #f2f2f2;
+ --popup-color-0: #101828;
+ --popup-color-1: #475467;
+ --color-0: #1570ef;
+ --color-1: #175cd3;
+}
+
+.dual-button-popup-modal-holder {
+ width: 380px;
+ height: 130px;
+ margin: 10px 20px;
+ padding: 30px;
+ box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03),
+ 0px 20px 24px -4px rgba(16, 24, 40, 0.08);
+ border: 1px solid var(--popup-color-border-0);
+ border-radius: 4px;
+}
+
+.dual-button-popup-modal-subject {
+ color: var(--popup-color-0);
+ font-weight: bolder;
+ font-size: 16px;
+}
+
+.dual-button-popup-modal-description {
+ color: var(--popup-color-1);
+ font-size: 13px;
+ margin-top: 10px;
+}
+
+.dual-modal-spacing {
+ height: 40px;
+}
+
+.dual-button-popup-modal-controllers {
+ display: flex;
+ justify-content: end;
+}
+
+.blue-save-button {
+ width: 145px;
+ height: 35px;
+ padding: 5px 20px;
+ background-color: var(--color-0);
+ border: 1px solid var(--color-1);
+ border-radius: 4px;
+ margin: 5px 0;
+ margin-left: 20px;
+ color: white;
+ cursor: pointer;
+ font-size: 13px;
+}
+
+.transparent-discard-button {
+ border: none;
+ background-color: transparent;
+ margin: 5px 10px;
+ font-size: 13px;
+}
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx
new file mode 100644
index 000000000..b8b424323
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx
@@ -0,0 +1,73 @@
+import "./dualButtonPopupModalWithTextfields.css";
+import React from "react";
+import CloseIcon from "@mui/icons-material/Close";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the modal (required)
+ * @returns {JSX.Element} - Renders a single text field component within a popup modal
+ */
+const PopupModalTextfield = ({ title }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.title - The title text for the modal (required)
+ * @param {string} props.esc - The text for the cancel button (required)
+ * @param {string} props.save - The text for the save button (required)
+ * @returns {JSX.Element} - Renders the dual button popup modal component with text fields
+ */
+const DualButtonPopupModalWithTextfields = ({ title, esc, save }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+
+
+
+
+
+
+ {esc}
+ {save}
+
+
+ );
+};
+
+DualButtonPopupModalWithTextfields.propTypes = {
+ title: PropTypes.string.isRequired,
+ esc: PropTypes.string.isRequired,
+ save: PropTypes.string.isRequired,
+};
+
+export default DualButtonPopupModalWithTextfields;
diff --git a/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css
new file mode 100644
index 000000000..a1be3cd3a
--- /dev/null
+++ b/Client/src/Components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css
@@ -0,0 +1,61 @@
+:root {
+ --popup-color-with-input-border-0: #f2f2f2;
+ --popup-color-with-input-border-1: #d0d5dd;
+ --popup-modal-with-input-title-color: #344054;
+}
+
+.popup-modal-holder {
+ width: 480px;
+ font-family: var(--popup-font-family-0);
+ padding: 50px 40px;
+ border-radius: 4px;
+ margin: 10px 20px;
+ border: 1px solid var(--popup-color-with-input-border-0);
+ box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03),
+ 0px 20px 24px -4px rgba(16, 24, 40, 0.08);
+}
+
+.popup-modal-header {
+ display: flex;
+ justify-content: space-between;
+ font-weight: bold;
+}
+
+.popup-modal-close {
+ cursor: pointer;
+}
+
+.spacing-height {
+ height: 30px;
+}
+
+.popup-modal-input-title {
+ margin-bottom: 15px;
+ color: var(--popup-modal-with-input-title-color);
+}
+
+.popup-modal-text-field {
+ width: 100%;
+ height: 35px;
+ border: 1px solid var(--popup-color-with-input-border-1);
+ border-radius: 4px;
+}
+
+.popup-modal-controllers {
+ display: flex;
+ justify-content: end;
+}
+
+.white-cancel-button {
+ border: none;
+ background-color: transparent;
+ font-family: var(--popup-font-family-0);
+ margin: 5px 10px;
+ width: 145px;
+ height: 35px;
+ padding: 5px 20px;
+ border: 1px solid var(--popup-color-with-input-border-1);
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 13px;
+}
diff --git a/Client/src/Components/TextFields/Description/DescriptionTextField.jsx b/Client/src/Components/TextFields/Description/DescriptionTextField.jsx
new file mode 100644
index 000000000..7f3ba908a
--- /dev/null
+++ b/Client/src/Components/TextFields/Description/DescriptionTextField.jsx
@@ -0,0 +1,47 @@
+import React from "react";
+import "./descriptionTextField.css";
+import { TextField } from "@mui/material";
+import PropTypes from "prop-types";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.hintText - The hint text displayed within the text field (required)
+ * @param {boolean} props.hasError - A flag indicating if the text field has an error (required)
+ * @returns {JSX.Element} - Renders the description text field component
+ */
+const DescriptionTextField = ({ hintText, hasError }) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ return (
+
+ );
+};
+
+DescriptionTextField.propTypes = {
+ hintText: PropTypes.string.isRequired,
+ hasError: PropTypes.bool.isRequired,
+};
+
+export default DescriptionTextField;
diff --git a/Client/src/Components/TextFields/Description/descriptionTextField.css b/Client/src/Components/TextFields/Description/descriptionTextField.css
new file mode 100644
index 000000000..28616863e
--- /dev/null
+++ b/Client/src/Components/TextFields/Description/descriptionTextField.css
@@ -0,0 +1,45 @@
+:root {
+ --color-title-1: #344054;
+ --text-field-margin-0: 10px 20px;
+ --description-color-0: #344054;
+}
+
+.description-field-holder {
+ margin: var(--text-field-margin-0);
+ margin-top: 20px;
+}
+
+.text-field-title {
+ color: var(--color-title-1);
+ font-weight: bold;
+ margin-bottom: 10px;
+ font-size: 13px;
+ color: var(--description-color-0);
+ font-weight: bold;
+}
+
+.description-field-area {
+ min-width: 320px;
+ font-size: 13px;
+ border-radius: 8px;
+}
+
+.description-field-label {
+ margin-top: 10px;
+ font-size: 11px;
+}
+
+.with-error {
+ color: red;
+}
+
+#description-field-area-helper-text {
+ margin-left: 0;
+ margin-top: 10px;
+ font-size: 11px;
+}
+
+#description-field-area {
+ min-width: 320px;
+ font-size: 13px;
+}
diff --git a/Client/src/Components/TextFields/Email/EmailTextField.jsx b/Client/src/Components/TextFields/Email/EmailTextField.jsx
new file mode 100644
index 000000000..b1428e1c5
--- /dev/null
+++ b/Client/src/Components/TextFields/Email/EmailTextField.jsx
@@ -0,0 +1,88 @@
+import React, { useState } from "react";
+import "./emailTextField.css";
+import { InputAdornment, TextField } from "@mui/material";
+import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
+import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {string} props.id - Unique ID for the text field (optional)
+ * @param {string} props.label - The label text displayed above the text field (optional)
+ * @param {"standard" | "outlined" | "filled"} props.variant - The variant of the text field (e.g., "outlined") (optional)
+ * @param {string} props.placeholder - The placeholder text displayed within the text field (optional)
+ * @param {"error" | "help"} [props.icon] - The type of icon to display (error or help) (optional)
+ * @param {string} props.helperText - The helper text displayed below the text field (optional)
+ * @param {boolean} props.error - A flag indicating if the text field has an error (required)
+ * @returns {JSX.Element} - Renders the email text field component with dynamic icon display
+ */
+const EmailTextField = ({
+ id,
+ label,
+ variant,
+ placeholder,
+ icon,
+ helperText,
+ error,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ const [showIcon, setShowIcon] = useState(false);
+
+ const handleMouseEnter = () => setShowIcon(true);
+ const handleMouseLeave = () => setShowIcon(false);
+
+ return (
+
+
Email
+
+
+
+ {error && showIcon && (
+
+ )}
+ {error && !showIcon && (
+
+ )}
+ {!error && showIcon && (
+
+ )}
+
+
+ ),
+ }}
+ helperText={helperText}
+ onMouseEnter={handleMouseEnter}
+ onMouseLeave={handleMouseLeave}
+ />
+
+
+ );
+};
+
+export default EmailTextField;
diff --git a/Client/src/Components/TextFields/Email/emailTextField.css b/Client/src/Components/TextFields/Email/emailTextField.css
new file mode 100644
index 000000000..ff38a29df
--- /dev/null
+++ b/Client/src/Components/TextFields/Email/emailTextField.css
@@ -0,0 +1,41 @@
+:root {
+ --textfield-color-0: #344054;
+}
+
+.email-text-field {
+ position: relative;
+ margin: 10px 20px;
+}
+
+#outlined-basic {
+ width: 320px;
+ height: 14px;
+ font-size: 13px;
+ padding: 8px 12px;
+}
+
+.email-text-field-title {
+ margin: 0 20px;
+ margin-top: 30px;
+ font-size: 13px;
+ color: var(--textfield-color-0);
+ font-weight: bold;
+}
+
+#outlined-basic-helper-text {
+ margin-left: 0;
+ margin-top: 10px;
+ font-size: 11px;
+}
+
+.text-field-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.icon-holder {
+ width: 16px;
+ height: 16px;
+ padding: 0;
+ margin: 0;
+}
diff --git a/Client/src/Components/TextFields/Website/WebsiteTextField.jsx b/Client/src/Components/TextFields/Website/WebsiteTextField.jsx
new file mode 100644
index 000000000..d18e33dd9
--- /dev/null
+++ b/Client/src/Components/TextFields/Website/WebsiteTextField.jsx
@@ -0,0 +1,72 @@
+import React, { useState } from "react";
+import "./websiteTextField.css";
+import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
+import ContentCopyIcon from "@mui/icons-material/ContentCopy";
+import { useTheme } from "@mui/material";
+
+/**
+ * @component
+ * @param {Object} props
+ * @param {boolean} [props.showHelp=true] - Controls the visibility of the help icon (defaults to true)
+ * @param {boolean} [props.hasCopyButton=false] - Controls the visibility of the copy button (defaults to false)
+ * @param {string} props.hintText - The hint text displayed below the text field (required)
+ * @returns {JSX.Element} - Renders the website text field component with optional help and copy features
+ */
+const WebsiteTextField = ({
+ showHelp = true,
+ hasCopyButton = false,
+ hintText,
+}) => {
+ const theme = useTheme();
+
+ const fontLookup = {
+ default: theme.font.default.font,
+ };
+
+ const fontFamily = fontLookup["default"];
+
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = () => {
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1000);
+ };
+
+ return (
+
+
Website
+
+
http://
+
+ {showHelp && (
+
+
+
+ )}
+ {hasCopyButton && (
+
+
+ {copied ? " Copied " : " Copy "}
+
+ )}
+
+
{hintText}
+
+ );
+};
+
+export default WebsiteTextField;
diff --git a/Client/src/Components/TextFields/Website/websiteTextField.css b/Client/src/Components/TextFields/Website/websiteTextField.css
new file mode 100644
index 000000000..66cdf4fa4
--- /dev/null
+++ b/Client/src/Components/TextFields/Website/websiteTextField.css
@@ -0,0 +1,82 @@
+:root {
+ --website-color-0: #344054;
+ --border-color: #d0d5dd;
+ --label-font-color: #475467;
+ --icon-color: #98a2b3;
+ --text-field-margin-0: 10px 20px;
+ --text-field-margin-1: 10px 20px 5px;
+}
+
+.website-textfield-title {
+ margin: 0 20px;
+ margin-top: 30px;
+ font-size: 13px;
+ color: var(--website-color-0);
+ font-weight: bold;
+}
+
+.website-textfield {
+ display: flex;
+ align-items: center;
+ position: relative;
+ margin: var(--text-field-margin-1);
+}
+
+.website-textfield-hint {
+ margin: var(--text-field-margin-0);
+ font-size: 11px;
+}
+
+.website-label {
+ padding: 6px 20px 6px 10px;
+ border: solid 1px var(--border-color);
+ border-right: none;
+ border-radius: 4px 0 0 4px;
+ font-size: 13px;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: var(--label-font-color);
+}
+
+.website-url {
+ width: 248px;
+ height: 31.5px;
+ padding: 0px 15px;
+ border: solid 1px var(--border-color);
+}
+
+.website-icon-holder {
+ width: 40px;
+ height: 33px;
+ padding: 5px;
+ border: solid 1px var(--border-color);
+ border-left: none;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.copy-to-clipboard-button {
+ padding: 5.5px 20px 6px 10px;
+ border: solid 1px var(--border-color);
+ border-left: none;
+ border-radius: 0 8px 8px 0;
+ font-size: 13px;
+ text-align: center;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ color: var(--label-font-color);
+ cursor: pointer;
+}
+
+.copy-icon {
+ margin-right: 5px;
+}
+
+.with-no-copy-button {
+ display: none;
+}
diff --git a/Client/src/Components/Tooltips/TooltipWithTail/TooltipWithTail.jsx b/Client/src/Components/Tooltips/TooltipWithTail/TooltipWithTail.jsx
new file mode 100644
index 000000000..1b1fb8b4d
--- /dev/null
+++ b/Client/src/Components/Tooltips/TooltipWithTail/TooltipWithTail.jsx
@@ -0,0 +1,45 @@
+import "./tooltipWithTail.css";
+import React from "react";
+import { styled } from "@mui/material/styles";
+import Tooltip, { tooltipClasses } from "@mui/material/Tooltip";
+import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
+
+const CustomizedTooltip = styled(
+ ({ className, placement, arrow, ...props }) => (
+
+ )
+)(({ theme }) => ({
+ [`& .${tooltipClasses.tooltip}`]: {
+ backgroundColor: "black",
+ },
+ [`& .${tooltipClasses.arrow}`]: {
+ color: "black",
+ },
+}));
+
+function TooltipWithTail({ placement, arrow = false, title, text }) {
+ return (
+
+
+ {title && {title}
}
+ {text}
+
+ }
+ className="tooltip-holder"
+ >
+
+
+
+ );
+}
+
+export default TooltipWithTail;
diff --git a/Client/src/Components/Tooltips/TooltipWithTail/tooltipWithTail.css b/Client/src/Components/Tooltips/TooltipWithTail/tooltipWithTail.css
new file mode 100644
index 000000000..1b1adf507
--- /dev/null
+++ b/Client/src/Components/Tooltips/TooltipWithTail/tooltipWithTail.css
@@ -0,0 +1,17 @@
+:root {
+ --tooltip-color-0: #d0d5dd;
+ --tooltip-margin: 5px;
+ --tooltip-font-size: 12px;
+}
+
+.tooltip-title {
+ font-size: var(--tooltip-font-size);
+ font-weight: bold;
+ margin: var(--tooltip-margin);
+}
+
+.tooltip-description {
+ font-size: var(--tooltip-font-size);
+ color: var(--tooltip-color-0);
+ margin: var(--tooltip-margin);
+}
diff --git a/Client/src/Pages/PlayGround/PlayGround-Charts.jsx b/Client/src/Pages/PlayGround/PlayGround-Charts.jsx
new file mode 100644
index 000000000..906f2a84a
--- /dev/null
+++ b/Client/src/Pages/PlayGround/PlayGround-Charts.jsx
@@ -0,0 +1,25 @@
+import React from "react";
+import "./playGround.css";
+import Statistic from "../../Components/Charts/Statistics/Statistic";
+import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
+
+function PlayGroundCharts() {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default PlayGroundCharts;
diff --git a/Client/src/Pages/PlayGround/PlayGround-Popup-Modals.jsx b/Client/src/Pages/PlayGround/PlayGround-Popup-Modals.jsx
new file mode 100644
index 000000000..a3b89349c
--- /dev/null
+++ b/Client/src/Pages/PlayGround/PlayGround-Popup-Modals.jsx
@@ -0,0 +1,25 @@
+import React from "react";
+import DualButtonPopupModal from "../../Components/PopupModals/DualButtonPopupModal/DualButtonPopupModal";
+import DualButtonPopupModalWithTextfields from "../../Components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields";
+
+function PlayGroundPopupModals() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default PlayGroundPopupModals;
diff --git a/Client/src/Pages/PlayGround/PlayGround-Tooltips.jsx b/Client/src/Pages/PlayGround/PlayGround-Tooltips.jsx
new file mode 100644
index 000000000..3713f889c
--- /dev/null
+++ b/Client/src/Pages/PlayGround/PlayGround-Tooltips.jsx
@@ -0,0 +1,34 @@
+import React from "react";
+import TooltipWithTail from "../../Components/Tooltips/TooltipWithTail/TooltipWithTail";
+import "./playGround.css";
+
+function PlayGroundTooltips() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default PlayGroundTooltips;
diff --git a/Client/src/Pages/PlayGround/PlayGround-announcements.jsx b/Client/src/Pages/PlayGround/PlayGround-announcements.jsx
new file mode 100644
index 000000000..aa4bd1913
--- /dev/null
+++ b/Client/src/Pages/PlayGround/PlayGround-announcements.jsx
@@ -0,0 +1,73 @@
+import "./playGround.css";
+import React from "react";
+import AnnouncementsDualButtonWithIcon from "../../Components/Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import ComplexAlert from "../../Components/Icons/ComplexAlert/ComplexAlert";
+import AnnouncementsMessageBar from "../../Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar";
+import AnnouncementsDualButton from "../../Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton";
+import AnnouncementUpdateSubscription from "../../Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription";
+
+function PlayGroundAnnouncements() {
+ return (
+
+ }
+ subject="We’ve just released a new feature"
+ body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid
+ pariatur, ipsum dolor."
+ esc="Dismiss"
+ primary="View changes"
+ />
+
+
+ }
+ subject="There was a problem with that action"
+ body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid
+ pariatur, ipsum dolor."
+ esc="Dismiss"
+ primary="Learn more"
+ />
+
+
+ }
+ subject="Successfully updated profile"
+ body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid
+ pariatur, ipsum dolor."
+ esc="Dismiss"
+ primary="Learn more"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default PlayGroundAnnouncements;
diff --git a/Client/src/Pages/PlayGround/PlayGround.jsx b/Client/src/Pages/PlayGround/PlayGround.jsx
new file mode 100644
index 000000000..29f4ebe5d
--- /dev/null
+++ b/Client/src/Pages/PlayGround/PlayGround.jsx
@@ -0,0 +1,63 @@
+import React from "react";
+import EmailTextField from "../../Components/TextFields/Email/EmailTextField";
+import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
+import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
+import WebsiteTextField from "../../Components/TextFields/Website/WebsiteTextField";
+import DescriptionTextField from "../../Components/TextFields/Description/DescriptionTextField";
+
+// This Component is just for the development and test
+// purposes and just to see what my components look like while development
+// and will be removed in the end
+function PlayGround() {
+ return (
+
+ {/* Default state of Email Text Fields */}
+
+ {/* Email Text Field when validation error occures */}
+ }
+ helperText="This is an error message"
+ />
+ {/* Email Text Field while mouse hover */}
+ }
+ helperText="This is a hint text to help user."
+ />
+
+
+ {/* Now, illustration of the Website text fields */}
+
+
+
+
+
+
+ {/* Now, illustration of the Description text fields */}
+
+
+
+
+ );
+}
+
+export default PlayGround;
diff --git a/Client/src/Pages/PlayGround/playGround.css b/Client/src/Pages/PlayGround/playGround.css
new file mode 100644
index 000000000..cd6d5e87b
--- /dev/null
+++ b/Client/src/Pages/PlayGround/playGround.css
@@ -0,0 +1,19 @@
+.play-ground-charts {
+ display: flex;
+}
+
+.play-ground-charts-spacing {
+ height: 110px;
+}
+
+.tooltip-playground {
+ margin: auto;
+ padding: 5%;
+ width: 5%;
+ justify-content: center;
+ text-align: center;
+}
+
+.playground {
+ padding: 40px;
+}
diff --git a/Client/src/Utils/Theme.js b/Client/src/Utils/Theme.js
index 99a442dfb..068e24572 100644
--- a/Client/src/Utils/Theme.js
+++ b/Client/src/Utils/Theme.js
@@ -16,6 +16,10 @@ const labelRed = "#F04438";
//Section colros
const sectionBorder = "#D0D5DD";
const sectionBg = "#F8F9F8";
+
+// Global Font Family
+const fontFamilyDefault = '"Roboto", "Helvetica", "Arial", sans-serif';
+
const theme = createTheme({
palette: {
primary: {
@@ -48,6 +52,11 @@ const theme = createTheme({
bgColor: sectionBg,
},
},
+ font :{
+ default: {
+ font: fontFamilyDefault,
+ },
+ },
shape: {
borderRadius: 4,
},
diff --git a/Client/src/assets/icons/icon-red-error-0.svg b/Client/src/assets/icons/icon-red-error-0.svg
new file mode 100644
index 000000000..f052582ba
--- /dev/null
+++ b/Client/src/assets/icons/icon-red-error-0.svg
@@ -0,0 +1,3 @@
+
+
+