From 3554698475330bc42dc838c0356284164010d071 Mon Sep 17 00:00:00 2001 From: shivam <141029609+js-1608@users.noreply.github.com> Date: Tue, 18 Mar 2025 19:56:13 +0530 Subject: [PATCH] updated admin and user and res code --- package-lock.json | 226 ++++++++++++++ package.json | 4 + src/App.css | 247 +++++++++++++++ src/App.js | 605 ++++++++++++++++++++++++++++++++++-- src/pages/AdminPannel.js | 1 + src/pages/Home.js | 465 +++++++++++++++++++-------- src/pages/TeamResult.js | 75 +++-- src/services/DataService.js | 4 +- 8 files changed, 1441 insertions(+), 186 deletions(-) diff --git a/package-lock.json b/package-lock.json index b78204c..58e160f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,15 @@ "@testing-library/react": "^16.2.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.8.3", + "cors": "^2.8.5", + "http": "^0.0.1-security", + "jsonwebtoken": "^9.0.2", "lucide-react": "^0.479.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^7.3.0", "react-scripts": "5.0.1", + "sanitize-html": "^2.14.0", "socket.io-client": "^4.8.1", "web-vitals": "^2.1.4" }, @@ -4585,6 +4589,7 @@ "version": "1.8.3", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -5160,6 +5165,12 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5727,6 +5738,19 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -6620,6 +6644,15 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8769,6 +8802,11 @@ "entities": "^2.0.0" } }, + "node_modules/http": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", + "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" + }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -9319,6 +9357,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -10645,6 +10692,28 @@ "node": ">=0.10.0" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -10659,6 +10728,27 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10791,6 +10881,42 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -10801,6 +10927,12 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -11595,6 +11727,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "license": "MIT" + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -14078,6 +14216,94 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sanitize-html": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.14.0.tgz", + "integrity": "sha512-CafX+IUPxZshXqqRaG9ZClSlfPVjSxI0td7n07hk8QO2oO+9JDnlcL8iM8TWeOXOIBFgIOx6zioTzM53AOMn3g==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/sanitize-html/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/sanitize.css": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", diff --git a/package.json b/package.json index 8f64f05..a96ff1d 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,15 @@ "@testing-library/react": "^16.2.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.8.3", + "cors": "^2.8.5", + "http": "^0.0.1-security", + "jsonwebtoken": "^9.0.2", "lucide-react": "^0.479.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^7.3.0", "react-scripts": "5.0.1", + "sanitize-html": "^2.14.0", "socket.io-client": "^4.8.1", "web-vitals": "^2.1.4" }, diff --git a/src/App.css b/src/App.css index e69de29..aa6fa22 100644 --- a/src/App.css +++ b/src/App.css @@ -0,0 +1,247 @@ +/* Admin Panel Styles */ +:root { + --primary-color: #3498db; + --secondary-color: #2ecc71; + --danger-color: #e74c3c; + --light-color: #f4f4f4; + --dark-color: #333; + --success-color: #27ae60; + --warning-color: #f39c12; + --error-color: #c0392b; + } + + * { + box-sizing: border-box; + margin: 0; + padding: 0; + } + + body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; + background-color: #f8f9fa; + color: var(--dark-color); + } + + .container { + max-width: 1200px; + margin: 0 auto; + padding: 0 15px; + } + + /* Login Page */ + .login-container { + max-width: 400px; + margin: 100px auto; + padding: 20px; + background-color: white; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } + + .login-container h2 { + text-align: center; + margin-bottom: 20px; + color: var(--primary-color); + } + + .form-group { + margin-bottom: 15px; + } + + .form-group label { + display: block; + margin-bottom: 5px; + font-weight: 600; + } + + .form-group input, .form-group select { + width: 100%; + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 16px; + } + + .error { + color: var(--error-color); + background-color: #fadbd8; + padding: 8px; + margin-bottom: 15px; + border-radius: 4px; + } + + /* Buttons */ + .btn-primary, .btn-secondary, .btn-danger { + display: inline-block; + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + font-weight: 600; + text-align: center; + text-decoration: none; + margin-right: 5px; + transition: background-color 0.3s; + } + + .btn-primary { + background-color: var(--primary-color); + color: white; + } + + .btn-secondary { + background-color: var(--secondary-color); + color: white; + } + + .btn-danger { + background-color: var(--danger-color); + color: white; + } + + .btn-primary:hover { + background-color: #2980b9; + } + + .btn-secondary:hover { + background-color: #27ae60; + } + + .btn-danger:hover { + background-color: #c0392b; + } + + /* Dashboard */ + .dashboard-container { + min-height: 100vh; + display: flex; + flex-direction: column; + } + + .dashboard-header { + background-color: var(--primary-color); + color: white; + padding: 15px; + display: flex; + justify-content: space-between; + align-items: center; + } + + .dashboard-nav { + background-color: var(--light-color); + display: flex; + padding: 10px; + border-bottom: 1px solid #ddd; + } + + .nav-link { + color: var(--dark-color); + text-decoration: none; + padding: 8px 16px; + margin-right: 10px; + border-radius: 4px; + font-weight: 600; + } + + .nav-link:hover { + background-color: #e0e0e0; + } + + .dashboard-content { + flex: 1; + padding: 20px; + } + + /* Team List */ + .team-list-container { + margin-bottom: 20px; + } + + .team-list-container h2 { + margin-bottom: 15px; + } + + .team-table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + } + + .team-table th, .team-table td { + border: 1px solid #ddd; + padding: 10px; + text-align: left; + } + + .team-table th { + background-color: #f2f2f2; + } + + /* Forms */ + .team-form-container, .result-form-container { + max-width: 600px; + margin: 0 auto; + padding: 20px; + background-color: white; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } + + .team-form-container h2, .result-form-container h2 { + margin-bottom: 20px; + } + + /* Calendar */ + .calendar-container { + margin-bottom: 20px; + } + + .date-picker { + margin-bottom: 20px; + } + + .date-picker label { + margin-right: 10px; + font-weight: 600; + } + + .results-container { + margin-top: 20px; + } + + .results-container h3 { + margin-bottom: 15px; + } + + .results-table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + } + + .results-table th, .results-table td { + border: 1px solid #ddd; + padding: 10px; + text-align: left; + } + + .results-table th { + background-color: #f2f2f2; + } + + /* Responsive adjustments */ + @media (max-width: 768px) { + .dashboard-nav { + flex-direction: column; + } + + .nav-link { + margin-bottom: 5px; + } + + .team-table, .results-table { + font-size: 14px; + } + } \ No newline at end of file diff --git a/src/App.js b/src/App.js index 25d1e4b..2479ef0 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,587 @@ -import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import Home from "./pages/Home"; -import Admin from "./pages/Admin"; -import TeamsResults from "./pages/TeamResult"; -import AdminPanel from "./pages/AdminPannel"; -import { SattaUserView } from "./pages/UserView"; +import React, { useState, useEffect } from 'react'; +import { BrowserRouter as Router, Route, Routes, Navigate, Link } from 'react-router-dom'; +import './App.css'; +import { useParams,useNavigate,useLocation } from 'react-router-dom'; +import Home from './pages/Home'; +import TeamResults from './pages/TeamResult'; +// Auth Context +const AuthContext = React.createContext(); - -export default function App() { +const AuthProvider = ({ children }) => { + const [token, setToken] = useState(localStorage.getItem('token')); + + const login = (newToken) => { + localStorage.setItem('token', newToken); + setToken(newToken); + }; + + const logout = () => { + localStorage.removeItem('token'); + setToken(null); + }; + return ( - - - } /> - }/> - }/> - - - {/* test */} - } /> - }/> - - + + {children} + ); -} +}; + +// API Service +const API_URL = 'http://localhost:5500'; + +const apiService = { + login: async (accessKey, password) => { + const response = await fetch(`${API_URL}/admin/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ accessKey, password }) + }); + console.log(accessKey); + + + if (!response.ok) { + throw new Error('Login failed'); + } + + return response.json(); + }, + + getTeams: async (token) => { + const response = await fetch(`${API_URL}/api/teams`, { + headers: { 'Authorization': `Bearer ${token}` } + }); + + if (!response.ok) { + throw new Error('Failed to fetch teams'); + } + + return response.json(); + }, + + createTeam: async (teamData, token) => { + const response = await fetch(`${API_URL}/api/teams`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(teamData) + }); + + if (!response.ok) { + throw new Error('Failed to create team'); + } + + return response.json(); + }, + + updateTeam: async (id, teamData, token) => { + const response = await fetch(`${API_URL}/api/teams/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(teamData) + }); + + if (!response.ok) { + throw new Error('Failed to update team'); + } + + return response.json(); + }, + + deleteTeam: async (id, token) => { + const response = await fetch(`${API_URL}/api/teams/${id}`, { + method: 'DELETE', + headers: { 'Authorization': `Bearer ${token}` } + }); + + if (!response.ok) { + throw new Error('Failed to delete team'); + } + + return response.json(); + }, + + publishResult: async (resultData, token) => { + const response = await fetch(`${API_URL}/admin/results`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(resultData) + }); + + if (!response.ok) { + throw new Error('Failed to publish result'); + } + + return response.json(); + }, + + getDailyResults: async (date, token) => { + const response = await fetch(`${API_URL}/api/results/daily?date=${date}`, { + headers: { 'Authorization': `Bearer ${token}` } + }); + + if (!response.ok) { + throw new Error('Failed to fetch daily results'); + } + + return response.json(); + } +}; + +// Components +const Login = () => { + const [accessKey, setAccessKey] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const { login } = React.useContext(AuthContext); + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + const data = await apiService.login(accessKey, password); + login(data.token); + } catch (err) { + setError('Invalid credentials'); + } + }; + + return ( +
+

Admin Login

+ {error &&
{error}
} +
+
+ + setAccessKey(e.target.value)} + required + /> +
+
+ + setPassword(e.target.value)} + required + /> +
+ +
+
+ ); +}; + +const TeamList = () => { + const [teams, setTeams] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const { token } = React.useContext(AuthContext); + + useEffect(() => { + const fetchTeams = async () => { + try { + const data = await apiService.getTeams(token); + setTeams(data); + setLoading(false); + } catch (err) { + setError('Failed to fetch teams'); + setLoading(false); + } + }; + + fetchTeams(); + }, [token]); + + const handleDelete = async (id) => { + if (window.confirm('Are you sure you want to delete this team?')) { + try { + await apiService.deleteTeam(id, token); + setTeams(teams.filter(team => team.id !== id)); + } catch (err) { + setError('Failed to delete team'); + } + } + }; + + if (loading) return
Loading...
; + if (error) return
{error}
; + + return ( +
+

Team Management

+ Add New Team + + + + + + + + + + {teams.map(team => ( + + + + + + ))} + +
IDNameActions
{team.id}{team.name} + Edit + +
+
+ ); +}; + +const TeamForm = ({ isEdit = false }) => { + const [name, setName] = useState(''); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(''); + const { token } = React.useContext(AuthContext); + const { id } = useParams(); + const navigate = useNavigate(); + + useEffect(() => { + if (isEdit && id) { + const fetchTeam = async () => { + try { + const teams = await apiService.getTeams(token); + const team = teams.find(t => t.id === parseInt(id)); + if (team) { + setName(team.name); + } + } catch (err) { + setError('Failed to fetch team details'); + } + }; + + fetchTeam(); + } + }, [isEdit, id, token]); + + const handleSubmit = async (e) => { + e.preventDefault(); + setSubmitting(true); + + try { + if (isEdit) { + await apiService.updateTeam(id, { name }, token); + } else { + await apiService.createTeam({ name }, token); + } + navigate('/teams'); + } catch (err) { + setError(isEdit ? 'Failed to update team' : 'Failed to create team'); + setSubmitting(false); + } + }; + + return ( +
+

{isEdit ? 'Edit Team' : 'Add New Team'}

+ {error &&
{error}
} +
+
+ + setName(e.target.value)} + required + /> +
+ + Cancel +
+
+ ); +}; + +const ResultCalendar = () => { + const [date, setDate] = useState(new Date().toISOString().split('T')[0]); + const [results, setResults] = useState([]); + const [teams, setTeams] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const { token } = React.useContext(AuthContext); + + useEffect(() => { + const fetchData = async () => { + try { + const [teamsData, resultsData] = await Promise.all([ + apiService.getTeams(token), + apiService.getDailyResults(date, token) + ]); + setTeams(teamsData); + setResults(resultsData); + setLoading(false); + } catch (err) { + setError('Failed to fetch data'); + setLoading(false); + } + }; + + fetchData(); + }, [date, token]); + + const handleDateChange = (e) => { + setDate(e.target.value); + }; + + if (loading) return
Loading...
; + if (error) return
{error}
; + + return ( +
+

Results Calendar

+
+ + +
+ +
+

Results for {date}

+ Add New Result + + {results.length === 0 ? ( +

No results for this date.

+ ) : ( + + + + + + + + + + {results.map(result => ( + + + + + + ))} + +
TeamResultActions
{result.team}{result.result} + Edit +
+ )} +
+
+ ); +}; + +const ResultForm = ({ isEdit = false }) => { + const [formData, setFormData] = useState({ + team_id: '', + result: '', + result_date: new Date().toISOString().split('T')[0] + }); + const [teams, setTeams] = useState([]); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(''); + const { token } = React.useContext(AuthContext); + const { id } = useParams(); + const navigate = useNavigate(); + const location = useLocation(); + + useEffect(() => { + const fetchTeams = async () => { + try { + const teamsData = await apiService.getTeams(token); + setTeams(teamsData); + + // Set date from query params if available + const params = new URLSearchParams(location.search); + const dateParam = params.get('date'); + if (dateParam) { + setFormData(prev => ({ ...prev, result_date: dateParam })); + } + + // If editing, fetch the result details + if (isEdit && id) { + // This is a simplified approach. In a real app, you'd have an API endpoint to fetch a specific result + const results = await apiService.getDailyResults(dateParam, token); + const result = results.find(r => r.id === parseInt(id)); + if (result) { + setFormData({ + team_id: result.team_id, + result: result.result, + result_date: result.result_date + }); + } + } + } catch (err) { + setError('Failed to fetch data'); + } + }; + + fetchTeams(); + }, [isEdit, id, token, location.search]); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ ...prev, [name]: value })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setSubmitting(true); + + try { + await apiService.publishResult(formData, token); + navigate(`/results?date=${formData.result_date}`); + } catch (err) { + setError('Failed to publish result'); + setSubmitting(false); + } + }; + + return ( +
+

{isEdit ? 'Edit Result' : 'Add New Result'}

+ {error &&
{error}
} +
+
+ + +
+
+ + +
+
+ + +
+ + Cancel +
+
+ ); +}; + +const Dashboard = () => { + const { logout } = React.useContext(AuthContext); + + return ( +
+
+

Admin Dashboard

+ +
+ + + +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+
+ ); +}; + +// Protected Route +const ProtectedRoute = ({ children }) => { + const { isAuthenticated } = React.useContext(AuthContext); + + if (!isAuthenticated) { + return ; + } + + return children; +}; + +// App +const App = () => { + return ( + + + + } /> + + + + } + /> + } /> + } /> + + + + + ); +}; + +export default App; \ No newline at end of file diff --git a/src/pages/AdminPannel.js b/src/pages/AdminPannel.js index f273d61..a5fdba7 100644 --- a/src/pages/AdminPannel.js +++ b/src/pages/AdminPannel.js @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { PlusCircle, Trash2, Edit, BarChart2 } from 'lucide-react'; // import dataService from '../services/dataService'; import dataService from '../services/DataService'; +import { useParams,useNavigate,useLocation } from 'react-router-dom'; const AdminPanel = () => { const [teams, setTeams] = useState([]); diff --git a/src/pages/Home.js b/src/pages/Home.js index 0a5b685..dc1746b 100644 --- a/src/pages/Home.js +++ b/src/pages/Home.js @@ -1,137 +1,284 @@ -import React, { useState } from 'react'; -import { BarChart2, Calendar } from 'lucide-react'; -import { useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; +import { BarChart2, Calendar, RefreshCw } from 'lucide-react'; +import axios from 'axios'; + const Home = () => { - const [teams, setTeams] = useState([ - { id: 1, name: 'BIKANER SUPER', time: '02:20 AM', results: { '2025-03-11': '04', '2025-03-12': '61' } }, - { id: 2, name: 'DESAWAR', time: '05:00 AM', results: { '2025-03-11': '79', '2025-03-12': '55' } }, - { id: 3, name: 'FARIDABAD', time: '06:00 PM', results: { '2025-03-11': '78', '2025-03-12': '98' } }, - { id: 4, name: 'GHAZIABAD', time: '09:30 PM', results: { '2025-03-11': '19', '2025-03-12': '23' } }, - { id: 5, name: 'GALI', time: '11:30 PM', results: { '2025-03-11': '72', '2025-03-12': 'XX' } }, - ]); - - const [dates] = useState(['2025-03-11', '2025-03-12']); + const [teams, setTeams] = useState([]); + const [dates, setDates] = useState([]); const [selectedTeam, setSelectedTeam] = useState(null); const [showChartView, setShowChartView] = useState(false); const [showCalendar, setShowCalendar] = useState(false); - - - - // Show chart for selected team - const handleViewChart = (team) => { - setSelectedTeam(team); - setShowChartView(true); - setShowCalendar(false); - }; - - // Generate mock chart data for the selected team - const generateChartData = () => { - if (!selectedTeam) return []; - - // Generate some random data for demonstration - const mockData = []; - const currentDate = new Date(); - - for (let i = 0; i < 30; i++) { - const date = new Date(currentDate); - date.setDate(date.getDate() - i); - const dateStr = date.toISOString().split('T')[0]; - - mockData.unshift({ - date: dateStr, - result: Math.floor(Math.random() * 100).toString().padStart(2, '0') - }); - } - - return mockData; - }; - - // Generate calendar view with results - const generateCalendarData = () => { - const calendarDays = []; - const currentDate = new Date(); - const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); - const lastDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); - - // Add empty cells for days before the first of the month - const firstDayWeekday = firstDayOfMonth.getDay(); - for (let i = 0; i < firstDayWeekday; i++) { - calendarDays.push(null); - } - - // Add days of the month - for (let i = 1; i <= lastDayOfMonth.getDate(); i++) { - const date = new Date(currentDate.getFullYear(), currentDate.getMonth(), i); - const dateStr = date.toISOString().split('T')[0]; - - // Generate mock results for each team on this date - const dayResults = {}; - teams.forEach(team => { - dayResults[team.id] = Math.floor(Math.random() * 100).toString().padStart(2, '0'); - }); - - calendarDays.push({ - day: i, - date: dateStr, - results: dayResults - }); - } - - return calendarDays; - }; - const [currentTime, setCurrentTime] = useState(""); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [calendarData, setCalendarData] = useState([]); + const [currentMonth, setCurrentMonth] = useState(new Date()); + // API URL + const API_URL = 'http://localhost:5500/api'; + + // Fetch teams data useEffect(() => { + + const fetchTeams = async () => { + try { + setLoading(true); + // Get all teams + const teamsResponse = await axios.get(`${API_URL}/teams`); + alert("teamsResponse"); + + // Get today's date and format it + const today = new Date(); + const todayFormatted = today.toISOString().split('T')[0]; + // Get yesterday's date and format it + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + const yesterdayFormatted = yesterday.toISOString().split('T')[0]; + + // Set dates for display + setDates([yesterdayFormatted, todayFormatted]); + + // Get today's results + const todayResultsResponse = await axios.get(`${API_URL}/today`); + + // Get yesterday's results for each team + const yesterdayResultsPromises = teamsResponse.data.map(team => + axios.get(`${API_URL}/results?team=${team.name}&date=${yesterdayFormatted}`) + .then(response => response.data) + .catch(() => null) // If no result, return null + ); + + const yesterdayResults = await Promise.all(yesterdayResultsPromises); + + // Combine team data with results + const teamsWithResults = teamsResponse.data.map((team, index) => { + const results = {}; + + // Add yesterday's result if available + if (yesterdayResults[index]) { + results[yesterdayFormatted] = yesterdayResults[index].result; + } + + // Add today's result if available + const todayResult = todayResultsResponse.data.find(r => r.team === team.name); + if (todayResult) { + results[todayFormatted] = todayResult.result; + } + + // Extract time from team name or use default + let time = "XX:XX"; + const timePart = team.name.match(/\d{2}:\d{2}\s*(?:AM|PM)/i); + if (timePart) { + time = timePart[0]; + } + + return { + id: team.id, + name: team.name, + time: time, + results: results + }; + }); + + setTeams(teamsWithResults); + setLoading(false); + } catch (err) { + console.error("Error fetching data:", err); + setError("Failed to load team data. Please try again later."); + setLoading(false); + } + }; + + fetchTeams(); + + // Update current time every minute + const interval = setInterval(() => { + const now = new Date(); + const formattedTime = now.toLocaleString("en-IN", { timeZone: "Asia/Kolkata" }); + setCurrentTime(formattedTime); + }, 60000); + + // Set initial time const now = new Date(); const formattedTime = now.toLocaleString("en-IN", { timeZone: "Asia/Kolkata" }); setCurrentTime(formattedTime); + + return () => clearInterval(interval); }, []); + // Show chart for selected team + const handleViewChart = async (team) => { + try { + setLoading(true); + // Get monthly results for the selected team + const currentDate = new Date(); + const month = currentDate.getMonth() + 1; + const year = currentDate.getFullYear(); + + const response = await axios.post(`${API_URL}/results/monthly`, { + team: team.name, + month: `${year}-${month.toString().padStart(2, '0')}` + }); + + setSelectedTeam({ + ...team, + chartData: response.data + }); + + setShowChartView(true); + setShowCalendar(false); + setLoading(false); + } catch (err) { + console.error("Error fetching chart data:", err); + setError("Failed to load chart data. Please try again later."); + setLoading(false); + } + }; + + // Load calendar data + const loadCalendarData = async (year, month) => { + try { + setLoading(true); + + // Calculate first and last day of month + const firstDay = new Date(year, month, 1).toISOString().split('T')[0]; + const lastDay = new Date(year, month + 1, 0).toISOString().split('T')[0]; + + // Get results for each day in the month + const dailyResultsPromises = []; + const currentDate = new Date(year, month, 1); + const lastDate = new Date(year, month + 1, 0); + + while (currentDate <= lastDate) { + const dateString = currentDate.toISOString().split('T')[0]; + dailyResultsPromises.push( + axios.get(`${API_URL}/results/daily?date=${dateString}`) + .then(response => ({ + date: dateString, + results: response.data + })) + .catch(() => ({ + date: dateString, + results: [] + })) + ); + currentDate.setDate(currentDate.getDate() + 1); + } + + const allResults = await Promise.all(dailyResultsPromises); + + // Format calendar data + const calendarDays = []; + const firstDayOfMonth = new Date(year, month, 1); + const firstDayWeekday = firstDayOfMonth.getDay(); + + // Add empty cells for days before the first of the month + for (let i = 0; i < firstDayWeekday; i++) { + calendarDays.push(null); + } + + // Add days with results + for (let i = 1; i <= lastDate.getDate(); i++) { + const dateObj = new Date(year, month, i); + const dateStr = dateObj.toISOString().split('T')[0]; + + const dayData = allResults.find(r => r.date === dateStr); + let teamResults = {}; + + if (dayData && dayData.results.length > 0) { + dayData.results.forEach(result => { + teamResults[result.team] = result.result; + }); + } + + calendarDays.push({ + day: i, + date: dateStr, + results: teamResults + }); + } + + setCalendarData(calendarDays); + setLoading(false); + } catch (err) { + console.error("Error loading calendar data:", err); + setError("Failed to load calendar data. Please try again later."); + setLoading(false); + } + }; + + // Handle calendar view button click + const handleCalendarView = () => { + const now = new Date(); + setCurrentMonth(now); + loadCalendarData(now.getFullYear(), now.getMonth()); + setShowCalendar(true); + setShowChartView(false); + }; + + // Handle month change in calendar + const handleMonthChange = (increment) => { + const newMonth = new Date(currentMonth); + newMonth.setMonth(newMonth.getMonth() + increment); + setCurrentMonth(newMonth); + loadCalendarData(newMonth.getFullYear(), newMonth.getMonth()); + }; + + // Refresh data + const handleRefresh = () => { + window.location.reload(); + }; + return (
-
- {/* Header */} -

SATTA-KING-FAST.com

- - {/* Advertisement Banner */} -
- Advertisement +
+ {/* Header */} +

SATTA-KING-FAST.com

+ + {/* Advertisement Banner */} +
+ Advertisement +
+ + {/* Informational Text */} +

+ Delhi Diamond Satta Result And Monthly Satta Chart of March 2025 With Combined Chart of Gali, Desawar, Ghaziabad, Faridabad And Shri Ganesh from Satta King Fast, Satta King Result, Satta King Chart, Black Satta King and Satta King 786. +

+ + {/* Disclaimer */} +

+ Satta-King-Fast.com is the most popular gaming discussion forum for players to use freely and we are not in partnership with any gaming company. +

+ + {/* Warning Message */} +

+ कृपया ध्यान दें, लीक गेम के नाम पर किसी को कोई पैसा न दें, ना पहले ना बाद में - धन्यवाद +

+ + {/* Contact Link */} +

+ हमसे संपर्क करने के लिए ➡ यहाँ क्लिक करें +

+ + {/* Timestamp */} +

+ Updated: {currentTime} IST. +

- - {/* Informational Text */} -

- Delhi Diamond Satta Result And Monthly Satta Chart of March 2025 With Combined Chart of Gali, Desawar, Ghaziabad, Faridabad And Shri Ganesh from Satta King Fast, Satta King Result, Satta King Chart, Black Satta King and Satta King 786. -

- - {/* Disclaimer */} -

- Satta-King-Fast.com is the most popular gaming discussion forum for players to use freely and we are not in partnership with any gaming company. -

- - {/* Warning Message */} -

- कृपया ध्यान दें, लीक गेम के नाम पर किसी को कोई पैसा न दें, ना पहले ना बाद में - धन्यवाद -

- - {/* Contact Link */} -

- हमसे संपर्क करने के लिए ➡ यहाँ क्लिक करें -

- - {/* Timestamp */} -

- Updated: {currentTime} IST. -

-
+ {error && ( +
+

{error}

+
+ )} +
- Bikaner Super Satta Result of March 12, 2025 & March 11, 2025 + {teams.length > 0 && teams[0].name} Satta Result of {dates.length > 1 && new Date(dates[1]).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })} & {dates.length > 0 && new Date(dates[0]).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}
{/* Controls */} @@ -141,19 +288,35 @@ const Home = () => {
+
+ {/* Loading indicator */} + {loading && ( +
+
+ + Loading data... +
+
+ )} + {/* Chart View */} - {showChartView && selectedTeam && ( + {!loading && showChartView && selectedTeam && (

Monthly Chart: {selectedTeam.name}

@@ -165,12 +328,17 @@ const Home = () => { - {generateChartData().map((item, index) => ( + {selectedTeam.chartData && selectedTeam.chartData.map((item, index) => ( - {new Date(item.date).toLocaleDateString()} + {new Date(item.result_date).toLocaleDateString()} {item.result} ))} + {(!selectedTeam.chartData || selectedTeam.chartData.length === 0) && ( + + No chart data available + + )}
@@ -186,11 +354,27 @@ const Home = () => { )} {/* Calendar View */} - {showCalendar && ( + {!loading && showCalendar && (
-

- Calendar View: {new Date().toLocaleDateString('en-US', { month: 'long', year: 'numeric' })} -

+
+ + +

+ {currentMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })} +

+ + +
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => ( @@ -199,14 +383,18 @@ const Home = () => {
- {generateCalendarData().map((day, index) => ( + {calendarData.map((day, index) => (
{day && ( <>
{day.day}
{teams.map(team => (
- {team.name.split(' ')[0]}: {day.results[team.id]} + {day.results[team.name] && ( + <> + {team.name.split(' ')[0]}: {day.results[team.name]} + + )}
))} @@ -227,17 +415,17 @@ const Home = () => { )} {/* Teams Table */} - {!showCalendar && ( + {!loading && !showCalendar && !showChartView && (
@@ -248,10 +436,10 @@ const Home = () => { - - + + ))} + {teams.length === 0 && ( + + + + )}
Games List - {new Date(dates[0]).toLocaleDateString('en-US', { weekday: 'short' })} {new Date(dates[0]).getDate()}th + {dates.length > 0 && new Date(dates[0]).toLocaleDateString('en-US', { weekday: 'short' })} {dates.length > 0 && new Date(dates[0]).getDate()}th - {new Date(dates[1]).toLocaleDateString('en-US', { weekday: 'short' })} {new Date(dates[1]).getDate()}th + {dates.length > 1 && new Date(dates[1]).toLocaleDateString('en-US', { weekday: 'short' })} {dates.length > 1 && new Date(dates[1]).getDate()}th Chart
{team.name}
at {team.time}
-
Record Chart
+
handleViewChart(team)}>Record Chart
{team.results[dates[0]] || 'XX'}{team.results[dates[1]] || 'XX'}{dates.length > 0 && team.results[dates[0]] || 'XX'}{dates.length > 1 && team.results[dates[1]] || 'XX'}
No teams found
- +
)} diff --git a/src/pages/TeamResult.js b/src/pages/TeamResult.js index fe3a833..9251708 100644 --- a/src/pages/TeamResult.js +++ b/src/pages/TeamResult.js @@ -1,34 +1,50 @@ import React, { useState, useEffect } from "react"; +// Function to sanitize potential XSS threats in strings +const sanitizeString = (str) => { + const div = document.createElement("div"); + div.textContent = str; + return div.innerHTML; +}; + const TeamResults = () => { const [teams, setTeams] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - - const fetchData = async () => { - try { - const response = await fetch("http://localhost:5000/api/teams/"); - - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - const data = await response.json(); // Parse JSON response - setTeams(data); // Store the entire API response in state - } catch (error) { - console.error("Error fetching data:", error); - setError(error.message); - } finally { - setLoading(false); - } - }; useEffect(() => { + const fetchData = async () => { + try { + const response = await fetch("http://localhost:5500/api/teams"); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const data = await response.json(); + + // Ensure safe rendering of team names + const sanitizedData = data.map((team) => ({ + ...team, + name: sanitizeString(team.name), + announcement_time: team.announcement_time || "N/A", // Handle missing time + results: team.results || {} // Ensure results exist + })); + + setTeams(sanitizedData); + } catch (error) { + console.error("Error fetching data:", error); + setError(error.message); + } finally { + setLoading(false); + } + }; + fetchData(); }, []); if (loading) return

Loading...

; - if (error) return

Error: {error}

; + if (error) return

Error: {error}

; return (
@@ -37,16 +53,21 @@ const TeamResults = () => { {teams.map((team) => (

{team.name}

-

Time: {team.time}

+

Time: {team.announcement_time}

+

Results:

-
    - {Object.entries(team.results).map(([date, result]) => ( -
  • - {date}: {result} -
  • - ))} -
+ {Object.keys(team.results).length > 0 ? ( +
    + {Object.entries(team.results).map(([date, result]) => ( +
  • + {date}: {result} +
  • + ))} +
+ ) : ( +

No results available

+ )}
))} diff --git a/src/services/DataService.js b/src/services/DataService.js index 4133386..633d9a1 100644 --- a/src/services/DataService.js +++ b/src/services/DataService.js @@ -2,8 +2,8 @@ import axios from 'axios'; import { io } from 'socket.io-client'; -const API_URL = 'http://localhost:5000/api'; -const socket = io('http://localhost:5000'); +const API_URL = 'http://localhost:3000/api'; +const socket = io('http://localhost:3000'); export const DataService = { // Get all teams