diff --git a/client/package-lock.json b/client/package-lock.json index c01bfe8a..e51fc2c5 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -28,7 +28,7 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", - "vite": "^5.2.0" + "vite": "^5.4.18" } }, "node_modules/@alloc/quick-lru": { @@ -1065,9 +1065,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "cpu": [ "arm" ], @@ -1079,9 +1079,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "cpu": [ "arm64" ], @@ -1093,9 +1093,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "cpu": [ "arm64" ], @@ -1107,9 +1107,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "cpu": [ "x64" ], @@ -1120,10 +1120,38 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "cpu": [ "arm" ], @@ -1135,9 +1163,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "cpu": [ "arm" ], @@ -1149,9 +1177,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "cpu": [ "arm64" ], @@ -1163,9 +1191,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "cpu": [ "arm64" ], @@ -1176,10 +1204,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "cpu": [ "ppc64" ], @@ -1191,9 +1233,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "cpu": [ "riscv64" ], @@ -1205,9 +1261,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "cpu": [ "s390x" ], @@ -1219,9 +1275,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "cpu": [ "x64" ], @@ -1233,9 +1289,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "cpu": [ "x64" ], @@ -1247,9 +1303,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "cpu": [ "arm64" ], @@ -1261,9 +1317,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "cpu": [ "ia32" ], @@ -1275,9 +1331,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "cpu": [ "x64" ], @@ -1334,9 +1390,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true, "license": "MIT" }, @@ -4012,9 +4068,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -4321,9 +4377,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { @@ -4367,9 +4423,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -4386,9 +4442,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -4803,13 +4859,13 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -4819,22 +4875,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" } }, @@ -5004,9 +5064,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -5569,15 +5629,15 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz", - "integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==", + "version": "5.4.18", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz", + "integrity": "sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -5596,6 +5656,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -5613,6 +5674,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/client/package.json b/client/package.json index abb180b8..b6a25092 100644 --- a/client/package.json +++ b/client/package.json @@ -30,6 +30,6 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", - "vite": "^5.2.0" + "vite": "^5.4.18" } } diff --git a/client/src/App.jsx b/client/src/App.jsx index 70eb0733..552f20e4 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -21,6 +21,9 @@ import ThemeContext from './assets/context/ThemeContext'; import useAuth from './auth/useAuth'; import { ProfileProvider } from './assets/context/ProfileContext'; import Network from './assets/components/network/Network'; +import Adminpage from './assets/components/Admin/Adminpage'; +import Usersprofile from './assets/components/Admin/Usersprofile'; + function App() { @@ -28,42 +31,64 @@ function App() { const { auth } = useAuth(); return ( -
-
- +
+
+ } /> } /> } /> }> }> - } /> - } /> - } /> + } /> + } /> + } /> {/* Protected Routes */} }> - }> + + } + > }> - } /> - } /> + } /> + } + /> }> } /> } /> - } /> - } /> + } /> + + } /> + + + + }> + }> + } /> + } > + } /> + } /> + {/* End Protected Routes */} -
- ) + ); } export default App diff --git a/client/src/assets/components/Admin/Adminpage.jsx b/client/src/assets/components/Admin/Adminpage.jsx new file mode 100644 index 00000000..a4dce056 --- /dev/null +++ b/client/src/assets/components/Admin/Adminpage.jsx @@ -0,0 +1,385 @@ +import React, { useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import Loading from "../subcomponents/Loading"; +import useAuth from "../../../auth/useAuth"; +import { axiosPrivate } from "../../../api/axios"; +import { FaTrashAlt, FaUserCircle, FaUserEdit } from "react-icons/fa"; + +const Adminpage = () => { + const { auth } = useAuth(); + const [users, setUsers] = useState([]); + const [loading, setLoading] = useState(true); + + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [userToDelete, setUserToDelete] = useState(null); + const [statusMessage, setStatusMessage] = useState(null); + + const [editModalOpen, setEditModalOpen] = useState(false); + const [userToEdit, setUserToEdit] = useState(null); + const [editData, setEditData] = useState({ + firstName: "", + lastName: "", + email: "", + username: "", + }); + + const [searchTerm, setSearchTerm] = useState(""); + + const PER_PAGE_USERS = 6; + const [currentPage, setCurrentPage] = useState(1); + + const fetchUsers = async () => { + setLoading(true); + try { + const response = await axiosPrivate.get("/api/users", { + headers: { Authorization: `Bearer ${auth?.accessToken}` }, + }); + setUsers(response.data); + } catch (err) { + console.error("Error fetching users:", err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchUsers(); + }, [auth?.accessToken]); + + const confirmDelete = (user) => { + setUserToDelete(user); + setShowDeleteModal(true); + }; + + const handleDeleteConfirmed = async () => { + if (!userToDelete) return; + try { + const res = await axiosPrivate.delete(`/api/users/${userToDelete._id}`, { + headers: { Authorization: `Bearer ${auth?.accessToken}` }, + }); + + if (res.status === 200 || res.status === 204) { + setStatusMessage({ + type: "success", + text: `User "${userToDelete.username}" deleted successfully.`, + }); + fetchUsers(); + } else throw new Error("Unexpected response status"); + } catch (err) { + setStatusMessage({ type: "error", text: "Failed to delete user." }); + } finally { + setShowDeleteModal(false); + setUserToDelete(null); + } + }; + + const openEditModal = (user) => { + setUserToEdit(user); + setEditData({ + firstName: user.firstName || "", + lastName: user.lastName || "", + email: user.email || "", + username: user.username || "", + }); + setEditModalOpen(true); + }; + + const handleEditChange = (e) => { + const { name, value } = e.target; + setEditData((prev) => ({ ...prev, [name]: value })); + }; + + const handleEditSave = async () => { + try { + const res = await axiosPrivate.put( + `/profile/${userToEdit.username}/edit`, + editData, + { + headers: { Authorization: `Bearer ${auth?.accessToken}` }, + } + ); + if (res.status === 200) { + setStatusMessage({ + type: "success", + text: "User updated successfully.", + }); + setEditModalOpen(false); + fetchUsers(); + } else { + setStatusMessage({ type: "error", text: "Failed to update user." }); + } + } catch (err) { + setStatusMessage({ + type: "error", + text: err?.response?.data?.message || "Update failed.", + }); + } + }; + + const filteredUsers = users.filter((user) => { + const fullName = `${user.firstName} ${user.lastName}`.toLowerCase(); + return ( + fullName.includes(searchTerm.toLowerCase()) || + user.username.toLowerCase().includes(searchTerm.toLowerCase()) + ); + }); + + const paginatedUsers = filteredUsers.slice( + (currentPage - 1) * PER_PAGE_USERS, + currentPage * PER_PAGE_USERS + ); + + const totalPages = Math.ceil(filteredUsers.length / PER_PAGE_USERS); + + const handlePageChange = (page) => { + if (page >= 1 && page <= totalPages) setCurrentPage(page); + }; + + const getRoleBadge = (role) => { + let roleClass = "bg-gray-300 text-slate-800"; + let roleText = "User"; + + if (role === "admin") { + roleClass = "bg-red-500 text-white"; + roleText = "Admin"; + } else if (role === "owner") { + roleClass = "bg-blue-500 text-white"; + roleText = "Owner"; + } + + return ( + + {roleText} + + ); + }; + + return ( +
+

+ User Management +

+ + setSearchTerm(e.target.value)} + /> + + {statusMessage && ( +
+ {statusMessage.text} +
+ )} + + {loading ? ( + + ) : ( + <> + {filteredUsers.length === 0 ? ( +
+ No users found matching {searchTerm} +
+ ) : ( + <> +
+ {paginatedUsers.map((user) => ( +
+
+
+

+ + {user.firstName} {user.lastName} +

+ +
+

+ Username: {user.username} +

+

+ Email: {user.email} +

+ +
{getRoleBadge(user.role)}
+
+ +
+ + + View Profile + + + +
+
+ ))} +
+ + {/* pagination area */} +
+ + + {Array.from({ length: totalPages }, (_, i) => ( + + ))} + + +
+ + )} + + )} + + {/* Delete Modal */} + {showDeleteModal && userToDelete && ( +
+
+

+ Confirm Deletion +

+

+ Are you sure you want to delete{" "} + {userToDelete.username}? +

+
+ + +
+
+
+ )} + + {/* Edit Modal */} + {editModalOpen && userToEdit && ( +
+
+

+ Edit User +

+ + + + + + + + + + + + + +
+ + +
+
+ +
+
+
+ )} +
+ ); +}; + +export default Adminpage; diff --git a/client/src/assets/components/Admin/Usersprofile.jsx b/client/src/assets/components/Admin/Usersprofile.jsx new file mode 100644 index 00000000..195595a3 --- /dev/null +++ b/client/src/assets/components/Admin/Usersprofile.jsx @@ -0,0 +1,140 @@ +import React, { useEffect, useState } from 'react'; +import { useParams, Link } from 'react-router-dom'; +import { FaUserEdit } from "react-icons/fa"; +import Loading from '../subcomponents/Loading'; +import useAuth from '../../../auth/useAuth'; +import axiosPrivate from '../../../api/axios'; + +const UsersProfile = () => { + const { username } = useParams(); + const { auth } = useAuth(); + + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [isBioExpanded, setIsBioExpanded] = useState(false); + + const isAdminOrOwner = ['admin', 'owner'].includes(auth?.role); + + useEffect(() => { + const fetchProfile = async () => { + try { + const response = await axiosPrivate.get(`/profile/${username}`, { + headers: { Authorization: `Bearer ${auth?.accessToken}` }, + withCredentials: true + }); + setProfile(response.data); + } catch (error) { + console.error('Failed to fetch user profile:', error); + setProfile(null); + } finally { + setLoading(false); + } + }; + + if (isAdminOrOwner) fetchProfile(); + else setLoading(false); + }, [username, auth?.accessToken]); + + if (loading) return ; + if (!profile) return

Profile not found or unauthorized.

; + + const handleBioToggle = () => setIsBioExpanded(prevState => !prevState); + + return ( +
+ {/* Edit Icon */} + {isAdminOrOwner && ( + + + + )} + + {/* Profile Top */} +
+ {/* Avatar */} +
+ {profile.avatar ? ( + Profile Avatar + ) : ( + No Avatar + )} +
+ + {/* Name & Bio */} +
+

{profile.name}

+

@{profile.username}

+ + {/* Bio with "Read More" */} +
+ {isBioExpanded ? ( + <> +

{profile.bio}

+ + + ) : ( + <> +

{profile.bio?.substring(0, 150)}...

+ + + )} +
+
+
+ + {/* Info Grid */} +
+
+

Email: {profile.email}

+

Location: {profile.location || "Not specified"}

+

Role: {profile.role}

+
+
+ {profile.website && ( +

Website: {profile.website}

+ )} + {profile.github && ( +

GitHub: {profile.github}

+ )} + {profile.linkedin && ( +

LinkedIn: {profile.linkedin}

+ )} + {profile.otherWebsite && ( +

Other Website: {profile.otherWebsite}

+ )} +
+
+ + {/* Skills */} +
+

Skills

+ {profile.skills && profile.skills.length > 0 ? ( +
+ {profile.skills.map((skill, index) => ( + + {skill} + + ))} +
+ ) : ( +

No skills listed.

+ )} +
+
+ ); +}; + +export default UsersProfile; diff --git a/client/src/assets/components/layout/NavBar.jsx b/client/src/assets/components/layout/NavBar.jsx index f1d7d33d..c932a410 100644 --- a/client/src/assets/components/layout/NavBar.jsx +++ b/client/src/assets/components/layout/NavBar.jsx @@ -1,178 +1,289 @@ -import { useContext } from "react"; -import { NavLink, Link } from 'react-router-dom' +import { useContext, useEffect, useState } from "react"; +import { NavLink, Link } from "react-router-dom"; import useLogout from "../../../auth/useLogout"; // Icon Imports import { IoMdHome } from "react-icons/io"; import { HiNewspaper } from "react-icons/hi2"; import { IoMdPeople } from "react-icons/io"; -import { GiSuitcase } from "react-icons/gi"; +import { GiSuitcase } from "react-icons/gi"; +import { TbUserSquareRounded } from "react-icons/tb"; import { FaUserCircle } from "react-icons/fa"; import { IoSettingsSharp } from "react-icons/io5"; import { RiLogoutBoxLine } from "react-icons/ri"; // Context Imports import ThemeContext from "../../context/ThemeContext"; +import useAuth from "../../../auth/useAuth"; + const NavBar = ({ avatar, username }) => { + const { auth } = useAuth(); - const { darkMode, actions } = useContext(ThemeContext) - - const profileURL = `/profile/${username}`; - - const logout = useLogout(); - - const handleLogout = async () => { - await logout(); - }; - - return ( -
- - {/* Nav start */} -
- - {/* Mobile Nav */} -
-
- - - -
-
    -
  • Home
  • - {/*
  • Tester
  • */} -
  • Blogs
  • -
  • Network
  • -
  • Jobs
  • -
-
- - {/* DevConnect Link */} - DevConnect -
+ const { darkMode, actions } = useContext(ThemeContext); - {/* Desktop Nav */} -
-
    -
  • Home
  • - {/*
  • Tester
  • */} -
  • Blogs
  • -
  • Network
  • -
  • Jobs
  • -
-
+ const profileURL = `/profile/${username}`; + + const logout = useLogout(); + + + const handleLogout = async () => { + await logout(); + }; + + return ( +
+ {/* Nav start */} +
+ {/* Mobile Nav */} +
+
+ + + +
+
    +
  • + + + + + Home + +
  • + {/*
  • Tester
  • */} +
  • + + + + + Blogs + +
  • +
  • + + + + + Network + +
  • +
  • + + + + + Jobs + +
  • + {(auth?.role === "admin" || auth?.role === "owner") && ( +
  • + + + + + Adminarea + +
  • + )} +
+
+ + {/* DevConnect Link */} + + DevConnect + +
+ + {/* Desktop Nav */} +
+
    +
  • + + + + + Home + +
  • + {/*
  • Tester
  • */} +
  • + + + + + Blogs + +
  • +
  • + + + + + Network + +
  • +
  • + + + + + Jobs + +
  • + {/* for admin and owner */} + {(auth?.role === "admin" || auth?.role === "owner") && ( +
  • + + + + + Adminarea + +
  • + )} +
+
+ + {/* Nav End */} +
+ {/* Search Icon */} + + + {/* Notifications */} + - {/* Nav End */} -
- - {/* Search Icon */} - - - - {/* Notifications */} - - - {/* Profile Avatar and Menu */} -
-
-
- Tailwind CSS Navbar component -
-
-
    -
  • - {/* Theme Switcher */} - -
  • -
  • Profile
  • -
  • -
  • -
-
+ {/* Profile Avatar and Menu */} +
+
+
+ Tailwind CSS Navbar component
+
+
    +
  • + {/* Theme Switcher */} + +
  • +
  • + + + + + Profile + +
  • +
  • + +
  • +
  • + +
  • +
- ) -} +
+
+ ); +}; -export default NavBar \ No newline at end of file +export default NavBar; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..05d8e780 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "DevConnect", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/server/controllers/userController.js b/server/controllers/userController.js index 24f8a673..e2e1fa24 100644 --- a/server/controllers/userController.js +++ b/server/controllers/userController.js @@ -3,6 +3,8 @@ const User = require('../models/User'); const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); + +//getallusers const getAllUsers = async (req, res) => { try { const users = await User.find().select('-password -refreshToken'); @@ -13,6 +15,7 @@ const getAllUsers = async (req, res) => { } } +//createuser const createUser = async (req, res) => { try { const { email, username, firstName, lastName, password, role } = req.body; @@ -49,6 +52,7 @@ const createUser = async (req, res) => { } } +//updateuser const updateUser = async (req, res) => { try { const { id } = req.params; @@ -103,6 +107,8 @@ const updateUser = async (req, res) => { } } + +//deleteuser const deleteUser = async (req, res) => { try { const { id } = req.params;