diff --git a/diplomas/package-lock.json b/diplomas/package-lock.json index 7d3d824..e4946ec 100644 --- a/diplomas/package-lock.json +++ b/diplomas/package-lock.json @@ -20,8 +20,11 @@ "@supabase/supabase-js": "^2.49.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cors": "^2.8.5", "diplomas": "file:", + "express": "^5.1.0", "lucide-react": "^0.488.0", + "mysql2": "^3.14.1", "next": "15.3.0", "papaparse": "^5.5.2", "react": "^19.0.0", @@ -2809,6 +2812,18 @@ "win32" ] }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", @@ -3075,6 +3090,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/axe-core": { "version": "4.10.3", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", @@ -3099,6 +3122,25 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3132,6 +3174,14 @@ "node": ">=10.16.0" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -3154,7 +3204,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -3167,7 +3216,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -3314,6 +3362,25 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", @@ -3322,6 +3389,26 @@ "node": ">=18" } }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "engines": { + "node": ">=6.6.0" + } + }, + "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==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -3408,7 +3495,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -3461,6 +3547,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -3495,7 +3597,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -3505,12 +3606,25 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -3593,7 +3707,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -3602,7 +3715,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -3638,7 +3750,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -3690,6 +3801,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4106,6 +4222,63 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4185,6 +4358,22 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4235,6 +4424,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/frac": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", @@ -4243,11 +4440,18 @@ "node": ">=0.8" } }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4281,11 +4485,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -4317,7 +4528,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -4399,7 +4609,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -4471,7 +4680,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -4498,7 +4706,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -4506,6 +4713,32 @@ "node": ">= 0.4" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4540,6 +4773,11 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4554,6 +4792,14 @@ "node": ">= 0.4" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -4787,6 +5033,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -5313,6 +5569,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5325,6 +5586,28 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/lucide-react": { "version": "0.488.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.488.0.tgz", @@ -5337,11 +5620,29 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "engines": { "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5364,6 +5665,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5388,8 +5708,37 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mysql2": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.1.tgz", + "integrity": "sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } }, "node_modules/nanoid": { "version": "3.3.11", @@ -5414,6 +5763,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/next/-/next-15.3.0.tgz", @@ -5498,7 +5855,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5507,7 +5863,6 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -5609,6 +5964,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5690,6 +6064,14 @@ "node": ">=6" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5714,6 +6096,14 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5788,6 +6178,18 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5797,6 +6199,20 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5817,6 +6233,28 @@ } ] }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", @@ -6013,6 +6451,21 @@ "node": ">=0.10.0" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6055,6 +6508,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -6088,6 +6560,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -6105,6 +6582,46 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -6151,6 +6668,11 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, "node_modules/sharp": { "version": "0.34.1", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", @@ -6216,7 +6738,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -6235,7 +6756,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -6251,7 +6771,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6269,7 +6788,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6301,6 +6819,14 @@ "node": ">=0.10.0" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/ssf": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", @@ -6318,6 +6844,14 @@ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "dev": true }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -6578,6 +7112,14 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -6632,6 +7174,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -6743,6 +7298,14 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unrs-resolver": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.5.0.tgz", @@ -6820,6 +7383,14 @@ } } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -6959,6 +7530,11 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "node_modules/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", diff --git a/diplomas/package.json b/diplomas/package.json index f19bb18..c78ae7a 100644 --- a/diplomas/package.json +++ b/diplomas/package.json @@ -21,8 +21,11 @@ "@supabase/supabase-js": "^2.49.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cors": "^2.8.5", "diplomas": "file:", + "express": "^5.1.0", "lucide-react": "^0.488.0", + "mysql2": "^3.14.1", "next": "15.3.0", "papaparse": "^5.5.2", "react": "^19.0.0", diff --git a/diplomas/src/components/app-sidebar.jsx b/diplomas/src/components/app-sidebar.jsx index eef192b..d290e46 100644 --- a/diplomas/src/components/app-sidebar.jsx +++ b/diplomas/src/components/app-sidebar.jsx @@ -52,6 +52,15 @@ const data = { }, ], }, + { + title: "Diplomas", + items: [ + { + title: "Creación de diplomas", + url: "/diplomasVista", + }, + ], + }, ], }; diff --git a/diplomas/src/pages/alumnosArchivo.jsx b/diplomas/src/pages/alumnosArchivo.jsx index 83cd140..91026ee 100644 --- a/diplomas/src/pages/alumnosArchivo.jsx +++ b/diplomas/src/pages/alumnosArchivo.jsx @@ -1,34 +1,75 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import Papa from "papaparse"; import * as XLSX from "xlsx"; import Layout from "@/components/layout/Layout"; import { Button } from "@/components/ui/button"; +import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; export default function AlumnosArchivo() { const [archivo, setArchivo] = useState(null); const [datos, setDatos] = useState([]); + const [dialogoAbierto, setDialogoAbierto] = useState(false); + const [mensajeDialogo, setMensajeDialogo] = useState(""); + + useEffect(() => { + if (archivo) extraerContenido(); + }, [archivo]); + + const registrarAlumnos = async () => { + if (datos.length === 0) return; + + const errores = []; + + for (const alumno of datos) { + const res = await fetch("/api/alumno", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + nombre: alumno.nombre, + correo: alumno.correo, + nombreCurso: alumno.nombreCurso, + }), + }); + + const resultado = await res.json(); + if (!res.ok) { + errores.push({ alumno, error: resultado.error || "Error desconocido" }); + } + } + + if (errores.length > 0) { + setMensajeDialogo(`Se registraron algunos errores:\n${JSON.stringify(errores, null, 2)}`); + } else { + setMensajeDialogo("Todos los alumnos fueron registrados correctamente."); + setArchivo(null); + setDatos([]); + } + setDialogoAbierto(true); + }; const manejarArchivo = (e) => { const file = e.target.files[0]; - if (file && (file.name.endsWith(".csv") || file.name.endsWith(".xlsx"))) { - setArchivo(file); - } else { - alert("Solo se permiten archivos .csv o .xlsx"); - } + if (validarArchivo(file)) setArchivo(file); }; const manejarSoltar = (e) => { e.preventDefault(); const file = e.dataTransfer.files[0]; - if (file && (file.name.endsWith(".csv") || file.name.endsWith(".xlsx"))) { - setArchivo(file); - } else { - alert("Solo se permiten archivos .csv o .xlsx"); - } + if (validarArchivo(file)) setArchivo(file); }; const manejarArrastrar = (e) => e.preventDefault(); + const validarArchivo = (file) => { + if (file && (file.name.endsWith(".csv") || file.name.endsWith(".xlsx"))) { + return true; + } else { + setMensajeDialogo("Solo se permiten archivos .csv o .xlsx"); + setDialogoAbierto(true); + return false; + } + }; + const extraerContenido = () => { if (!archivo) return; @@ -39,7 +80,6 @@ export default function AlumnosArchivo() { header: true, skipEmptyLines: true, complete: (result) => { - console.log("Contenido CSV:", result.data); setDatos(result.data); }, error: (error) => { @@ -55,7 +95,6 @@ export default function AlumnosArchivo() { const contenido = XLSX.utils.sheet_to_json(workbook.Sheets[hoja], { defval: "", }); - console.log("Contenido XLSX:", contenido); setDatos(contenido); }; reader.readAsArrayBuffer(archivo); @@ -64,7 +103,7 @@ export default function AlumnosArchivo() { return ( <Layout> - <div className="w-[60vw] pt-10 flex flex-col items-end justify-center"> + <div className="w-[60vw] pt-10 flex flex-col items-end justify-center text-black"> <div className="bg-white p-8 font-sans text-center w-[70%] flex flex-col items-center"> <h1 className="text-xl font-semibold mb-4 text-black"> Nuevo alumno @@ -90,22 +129,47 @@ export default function AlumnosArchivo() { </label> <Button - onClick={extraerContenido} - className="bg-green-400 hover:bg-green-500 text-white font-bold py-2 px-4 rounded-md" + onClick={registrarAlumnos} + className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-md mt-4" > - Extraer contenido + Registrar alumnos </Button> {datos.length > 0 && ( - <div className="mt-6 text-left w-full max-w-md"> - <h3 className="font-bold mb-2">Contenido extraído:</h3> - <pre className="bg-gray-100 p-2 rounded overflow-x-auto text-sm"> - {JSON.stringify(datos, null, 2)} - </pre> + <div className="mt-6 text-left w-full overflow-auto"> + <h3 className="font-bold mb-2">Vista previa del archivo:</h3> + <table className="min-w-full bg-white border border-gray-300 text-sm"> + <thead className="bg-gray-100 text-gray-700"> + <tr> + {Object.keys(datos[0]).map((columna, index) => ( + <th key={index} className="border px-4 py-2">{columna}</th> + ))} + </tr> + </thead> + <tbody> + {datos.map((fila, index) => ( + <tr key={index}> + {Object.values(fila).map((valor, i) => ( + <td key={i} className="border px-4 py-1">{valor}</td> + ))} + </tr> + ))} + </tbody> + </table> </div> )} </div> </div> + + {/* Dialog Component */} + <Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}> + <DialogContent> + <DialogHeader> + <DialogTitle className="text-black">Información</DialogTitle> + <DialogDescription>{mensajeDialogo}</DialogDescription> + </DialogHeader> + </DialogContent> + </Dialog> </Layout> ); -} +} \ No newline at end of file diff --git a/diplomas/src/pages/alumnosVista.jsx b/diplomas/src/pages/alumnosVista.jsx index aa38f3a..f1dad9f 100644 --- a/diplomas/src/pages/alumnosVista.jsx +++ b/diplomas/src/pages/alumnosVista.jsx @@ -20,7 +20,6 @@ import { SelectValue, } from "@/components/ui/select"; - export default function AlumnosVista() { const [alumnos, setAlumnos] = useState([]); const [alumnoEditando, setAlumnoEditando] = useState(null); @@ -28,10 +27,13 @@ export default function AlumnosVista() { const [nuevoCorreo, setNuevoCorreo] = useState(""); const [mostrarModal, setMostrarModal] = useState(false); const [modalMensaje, setModalMensaje] = useState(""); - const [nuevoCurso, setNuevoCurso] = useState(""); - + const [nuevoCurso, setNuevoCurso] = useState(""); const [cursos, setCursos] = useState([]); + // Estado para confirmación de eliminación + const [confirmarEliminar, setConfirmarEliminar] = useState(false); + const [alumnoAEliminar, setAlumnoAEliminar] = useState(null); + useEffect(() => { cargarAlumnos(); cargarCursos(); @@ -60,7 +62,7 @@ export default function AlumnosVista() { setAlumnoEditando(alumno.id); setNuevoNombre(alumno.nombre); setNuevoCorreo(alumno.correo); - setNuevoCurso(alumno.nombreCurso); // Asumiendo que tienes un campo cursoId en el alumno + setNuevoCurso(alumno.nombreCurso); }; // Cancelar edición @@ -78,7 +80,7 @@ export default function AlumnosVista() { .update({ nombre: nuevoNombre, correo: nuevoCorreo, - nombreCurso: nuevoCurso, // Asumiendo que tienes un campo cursoId en el alumno + nombreCurso: nuevoCurso, }) .eq("id", id); @@ -93,9 +95,15 @@ export default function AlumnosVista() { setMostrarModal(true); }; + // Confirmar eliminación + const confirmarEliminacion = (id) => { + setAlumnoAEliminar(id); + setConfirmarEliminar(true); + }; + // Eliminar alumno - const eliminarAlumno = async (id) => { - const { error } = await supabaseClient.from("alumno").delete().eq("id", id); + const eliminarAlumno = async () => { + const { error } = await supabaseClient.from("alumno").delete().eq("id", alumnoAEliminar); if (error) { console.error("Error eliminando alumno:", error.message); setModalMensaje("Error al eliminar el alumno"); @@ -103,6 +111,7 @@ export default function AlumnosVista() { setModalMensaje("Alumno eliminado exitosamente"); await cargarAlumnos(); } + setConfirmarEliminar(false); setMostrarModal(true); }; @@ -191,7 +200,7 @@ export default function AlumnosVista() { <Button className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-3 rounded" - onClick={() => eliminarAlumno(alumno.id)} + onClick={() => confirmarEliminacion(alumno.id)} > Eliminar </Button> @@ -203,7 +212,33 @@ export default function AlumnosVista() { </table> </div> - {/* Modal */} + {/* Modal de confirmación */} + <Dialog open={confirmarEliminar} onOpenChange={setConfirmarEliminar}> + <DialogContent> + <DialogHeader> + <DialogTitle className="text-black">Confirmar eliminación</DialogTitle> + <DialogDescription> + ¿Estás seguro de que deseas eliminar este alumno? Esta acción no se puede deshacer. + </DialogDescription> + </DialogHeader> + <DialogFooter> + <Button + className="bg-red-500 hover:bg-red-700 text-white" + onClick={eliminarAlumno} + > + Eliminar + </Button> + <Button + className="bg-gray-400 hover:bg-gray-600 text-white" + onClick={() => setConfirmarEliminar(false)} + > + Cancelar + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + + {/* Modal de resultado */} <Dialog open={mostrarModal} onOpenChange={setMostrarModal}> <DialogContent> <DialogHeader> @@ -219,4 +254,4 @@ export default function AlumnosVista() { </Dialog> </Layout> ); -} +} \ No newline at end of file diff --git a/diplomas/src/pages/api/alumno.js b/diplomas/src/pages/api/alumno.js new file mode 100644 index 0000000..6278fe9 --- /dev/null +++ b/diplomas/src/pages/api/alumno.js @@ -0,0 +1,29 @@ +// pages/api/alumno.js +import { createClient } from "@/utils/supabase"; + +export default async function handler(req, res) { + if (req.method !== "POST") { + return res.status(405).json({ error: "Método no permitido" }); + } + + try { + const supabase = createClient({ req, res }); + const { nombre, correo, nombreCurso } = req.body; + + if (!nombre || !correo || !nombreCurso) { + return res.status(400).json({ error: "Faltan datos del alumno" }); + } + + const { data, error } = await supabase.from("alumno").insert([ + { nombre, correo, nombreCurso }, + ]); + + if (error) { + return res.status(500).json({ error: "Error al insertar en Supabase", detalles: error.message }); + } + + return res.status(200).json({ mensaje: "Alumno registrado", data }); + } catch (err) { + return res.status(500).json({ error: "Error interno del servidor", detalles: err.message }); + } +} diff --git a/diplomas/src/pages/api/curso.js b/diplomas/src/pages/api/curso.js new file mode 100644 index 0000000..81d3c00 --- /dev/null +++ b/diplomas/src/pages/api/curso.js @@ -0,0 +1,29 @@ +// pages/api/curso.js +import { createClient } from "@/utils/supabase"; + +export default async function handler(req, res) { + if (req.method !== "POST") { + return res.status(405).json({ error: "Método no permitido" }); + } + + try { + const supabase = createClient({ req, res }); + const { nombre, horas, descripcion, competencias } = req.body; + + if (!nombre || !horas || !descripcion || !competencias) { + return res.status(400).json({ error: "Faltan datos del curso" }); + } + + const { data, error } = await supabase + .from("curso") + .insert([{ nombre, horas, descripcion, competencias }]); + + if (error) { + return res.status(500).json({ error: "Error al insertar en Supabase", detalles: error.message }); + } + + return res.status(200).json({ mensaje: "Curso registrado", data }); + } catch (err) { + return res.status(500).json({ error: "Error interno del servidor", detalles: err.message }); + } +} diff --git a/diplomas/src/pages/cursosArchivo.jsx b/diplomas/src/pages/cursosArchivo.jsx index d9401c4..49b6067 100644 --- a/diplomas/src/pages/cursosArchivo.jsx +++ b/diplomas/src/pages/cursosArchivo.jsx @@ -1,80 +1,178 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import Papa from "papaparse"; +import * as XLSX from "xlsx"; import Layout from "@/components/layout/Layout"; import { Button } from "@/components/ui/button"; +import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; -export default function CursosArchivo() { +export default function cursosArchivo() { const [archivo, setArchivo] = useState(null); - const [datosCSV, setDatosCSV] = useState([]); + const [datos, setDatos] = useState([]); + const [dialogoAbierto, setDialogoAbierto] = useState(false); + const [mensajeDialogo, setMensajeDialogo] = useState(""); + + useEffect(() => { + if (archivo) extraerContenido(); + }, [archivo]); + + const registrarCursos = async () => { + if (datos.length === 0) return; + + const errores = []; + + for (const curso of datos) { + const res = await fetch("/api/curso", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + nombre: curso.nombre, + horas: curso.horas, + descripcion: curso.descripcion, + competencias: curso.competencias + ? curso.competencias.split(",").map(c => c.trim()) + : [], + }), + }); + + const resultado = await res.json(); + if (!res.ok) { + errores.push({ curso, error: resultado.error || "Error desconocido" }); + } + } + + if (errores.length > 0) { + setMensajeDialogo(`Se registraron algunos errores:\n${JSON.stringify(errores, null, 2)}`); + } else { + setMensajeDialogo("Todos los cursos fueron registrados correctamente."); + setArchivo(null); + setDatos([]); + } + setDialogoAbierto(true); + }; const manejarArchivo = (e) => { const file = e.target.files[0]; - setArchivo(file); + if (validarArchivo(file)) setArchivo(file); }; const manejarSoltar = (e) => { e.preventDefault(); const file = e.dataTransfer.files[0]; - setArchivo(file); + if (validarArchivo(file)) setArchivo(file); }; - const manejarArrastrar = (e) => { - e.preventDefault(); + const manejarArrastrar = (e) => e.preventDefault(); + + const validarArchivo = (file) => { + if (file && (file.name.endsWith(".csv") || file.name.endsWith(".xlsx"))) { + return true; + } else { + setMensajeDialogo("Solo se permiten archivos .csv o .xlsx"); + setDialogoAbierto(true); + return false; + } }; const extraerContenido = () => { if (!archivo) return; - Papa.parse(archivo, { - header: true, - skipEmptyLines: true, - complete: (result) => { - console.log("Contenido CSV:", result.data); - setDatosCSV(result.data); - }, - error: (error) => { - console.error("Error al leer el CSV:", error.message); - }, - }); + const extension = archivo.name.split(".").pop().toLowerCase(); + + if (extension === "csv") { + Papa.parse(archivo, { + header: true, + skipEmptyLines: true, + complete: (result) => { + setDatos(result.data); + }, + error: (error) => { + console.error("Error al leer el CSV:", error.message); + }, + }); + } else if (extension === "xlsx") { + const reader = new FileReader(); + reader.onload = (e) => { + const data = new Uint8Array(e.target.result); + const workbook = XLSX.read(data, { type: "array" }); + const hoja = workbook.SheetNames[0]; + const contenido = XLSX.utils.sheet_to_json(workbook.Sheets[hoja], { + defval: "", + }); + setDatos(contenido); + }; + reader.readAsArrayBuffer(archivo); + } }; return ( <Layout> - <div className="w-[60vw] pt-10 flex flex-col items-end justify-center"> + <div className="w-[60vw] pt-10 flex flex-col items-end justify-center text-black"> <div className="bg-white p-8 font-sans text-center w-[70%] flex flex-col items-center"> - <h1 className="text-xl font-semibold mb-4 text-black">Nuevo curso</h1> - <h1 + <h1 className="text-xl font-semibold mb-4 text-black"> + Nuevo curso + </h1> + <label htmlFor="archivo" onDrop={manejarSoltar} onDragOver={manejarArrastrar} className="border-2 border-gray-300 rounded-md p-8 text-gray-600 cursor-pointer w-80 text-center mb-4" > - Arrastra y suelta un archivo o busca un archivo + {archivo ? ( + <span className="text-black font-medium">{archivo.name}</span> + ) : ( + <span>Arrastra y suelta un archivo o haz clic para seleccionarlo</span> + )} <input type="file" id="archivo" - accept=".csv" + accept=".csv, .xlsx" onChange={manejarArchivo} className="hidden" /> - </h1> + </label> + <Button - onClick={extraerContenido} - className="bg-green-400 hover:bg-green-500 text-white font-bold py-2 px-4 rounded-md" + onClick={registrarCursos} + className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-md mt-4" > - Extraer contenido + Registrar cursos </Button> - {datosCSV.length > 0 && ( - <div className="mt-6 text-left w-full max-w-md"> - <h3 className="font-bold mb-2">Contenido extraído:</h3> - <pre className="bg-gray-100 p-2 rounded overflow-x-auto text-sm"> - {JSON.stringify(datosCSV, null, 2)} - </pre> + {datos.length > 0 && ( + <div className="mt-6 text-left w-full overflow-auto"> + <h3 className="font-bold mb-2">Vista previa del archivo:</h3> + <table className="min-w-full bg-white border border-gray-300 text-sm"> + <thead className="bg-gray-100 text-gray-700"> + <tr> + {Object.keys(datos[0]).map((columna, index) => ( + <th key={index} className="border px-4 py-2">{columna}</th> + ))} + </tr> + </thead> + <tbody> + {datos.map((fila, index) => ( + <tr key={index}> + {Object.values(fila).map((valor, i) => ( + <td key={i} className="border px-4 py-1">{valor}</td> + ))} + </tr> + ))} + </tbody> + </table> </div> )} </div> </div> + + {/* Dialog Component */} + <Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}> + <DialogContent> + <DialogHeader> + <DialogTitle className="text-black">Información</DialogTitle> + <DialogDescription>{mensajeDialogo}</DialogDescription> + </DialogHeader> + </DialogContent> + </Dialog> </Layout> ); -} +} \ No newline at end of file diff --git a/diplomas/src/pages/cursosVista.jsx b/diplomas/src/pages/cursosVista.jsx index 8da0065..fa01d47 100644 --- a/diplomas/src/pages/cursosVista.jsx +++ b/diplomas/src/pages/cursosVista.jsx @@ -21,6 +21,10 @@ export default function CursosVista() { const [mostrarModal, setMostrarModal] = useState(false); const [modalMensaje, setModalMensaje] = useState(""); + // Estado para confirmación de eliminación + const [confirmarEliminar, setConfirmarEliminar] = useState(false); + const [cursoAEliminar, setCursoAEliminar] = useState(null); + useEffect(() => { cargarCursos(); }, []); @@ -69,8 +73,13 @@ export default function CursosVista() { setMostrarModal(true); }; - const eliminarCurso = async (id) => { - const { error } = await supabaseClient.from("curso").delete().eq("id", id); + const confirmarEliminacion = (id) => { + setCursoAEliminar(id); + setConfirmarEliminar(true); + }; + + const eliminarCurso = async () => { + const { error } = await supabaseClient.from("curso").delete().eq("id", cursoAEliminar); if (error) { console.error("Error eliminando curso:", error.message); setModalMensaje("Error al eliminar el curso"); @@ -78,6 +87,7 @@ export default function CursosVista() { setModalMensaje("Curso eliminado exitosamente"); await cargarCursos(); } + setConfirmarEliminar(false); setMostrarModal(true); }; @@ -149,7 +159,7 @@ export default function CursosVista() { </Button> <Button className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-3 rounded" - onClick={() => eliminarCurso(curso.id)} + onClick={() => confirmarEliminacion(curso.id)} > Eliminar </Button> @@ -161,7 +171,33 @@ export default function CursosVista() { </table> </div> - {/* Modal */} + {/* Modal de confirmación */} + <Dialog open={confirmarEliminar} onOpenChange={setConfirmarEliminar}> + <DialogContent> + <DialogHeader> + <DialogTitle className="text-black">Confirmar eliminación</DialogTitle> + <DialogDescription> + ¿Estás seguro de que deseas eliminar este curso? Esta acción no se puede deshacer. + </DialogDescription> + </DialogHeader> + <DialogFooter> + <Button + className="bg-red-500 hover:bg-red-700 text-white" + onClick={eliminarCurso} + > + Eliminar + </Button> + <Button + className="bg-gray-400 hover:bg-gray-600 text-white" + onClick={() => setConfirmarEliminar(false)} + > + Cancelar + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + + {/* Modal de resultado */} <Dialog open={mostrarModal} onOpenChange={setMostrarModal}> <DialogContent> <DialogHeader> @@ -177,4 +213,4 @@ export default function CursosVista() { </Dialog> </Layout> ); -} +} \ No newline at end of file