From d7a1a7e92ad5283169330c89b29bf7628ace1862 Mon Sep 17 00:00:00 2001 From: Deep-Singh14 Date: Wed, 13 May 2026 00:09:32 -0700 Subject: [PATCH 01/38] merged with some bugs --- .gitignore | 3 + package-lock.json | 1095 +++++++++++++---------- src/app/api/events/[eventId]/route.ts | 85 ++ src/app/api/events/route.ts | 97 ++ src/app/api/users/[id]/route.ts | 9 +- src/app/api/users/route.ts | 23 +- src/app/eventform/page.tsx | 190 ++++ src/app/events/[eventId]/page.tsx | 54 ++ src/app/events/page.tsx | 318 +++++++ src/app/globals.css | 301 +++++++ src/components/EventCard/EventCard.tsx | 1 + src/components/EventCard/EventGrid.tsx | 21 + src/components/NavBar/BottomSection.tsx | 4 + src/components/events/EventCard.tsx | 112 +++ src/components/events/EventModal.tsx | 138 +++ src/database/db.ts | 4 + src/database/eventSchema.ts | 38 + src/lib/clerk.ts | 14 +- src/lib/utils.ts | 29 + src/services/events.ts | 41 + src/types/event.ts | 23 + tsconfig.json | 5 +- 22 files changed, 2113 insertions(+), 492 deletions(-) create mode 100644 src/app/api/events/[eventId]/route.ts create mode 100644 src/app/api/events/route.ts create mode 100644 src/app/eventform/page.tsx create mode 100644 src/app/events/[eventId]/page.tsx create mode 100644 src/app/events/page.tsx create mode 100644 src/components/EventCard/EventCard.tsx create mode 100644 src/components/EventCard/EventGrid.tsx create mode 100644 src/components/events/EventCard.tsx create mode 100644 src/components/events/EventModal.tsx create mode 100644 src/database/eventSchema.ts create mode 100644 src/services/events.ts create mode 100644 src/types/event.ts diff --git a/.gitignore b/.gitignore index 89934f90..de44e4f7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# clerk configuration (can include secrets) +/.clerk/ diff --git a/package-lock.json b/package-lock.json index a70dd9a9..3669b3f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,13 +101,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -175,14 +176,15 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -304,6 +306,15 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", @@ -318,25 +329,27 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -358,9 +371,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -413,17 +427,19 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -451,23 +467,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1192,15 +1210,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -1829,61 +1848,54 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2041,33 +2053,32 @@ } }, "node_modules/@clerk/clerk-react": { - "version": "5.31.8", - "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.31.8.tgz", - "integrity": "sha512-GPhOdI7drAaamiKIhzfWiOVe4zw4wUi1sKp6khgUzcjr9hRopdZvzMts0fU+XLHFnYUSX8IPw4c0CDXY1wBKuw==", + "version": "5.61.6", + "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.61.6.tgz", + "integrity": "sha512-OiyBlrnkRr9IhZtPd7EwlzhYScBpvNKJ8lgg7Uw6JElzJYz854IeQaez5mAfpiib3LcW/Dn53E2PQhagcuLJ3Q==", "license": "MIT", "dependencies": { - "@clerk/shared": "^3.9.5", - "@clerk/types": "^4.59.3", + "@clerk/shared": "^3.47.5", "tslib": "2.8.1" }, "engines": { "node": ">=18.17.0" }, "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" } }, "node_modules/@clerk/nextjs": { - "version": "6.20.2", - "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-6.20.2.tgz", - "integrity": "sha512-rBtAdx2PFxexBDU41GEmEQwSsfbTU7J7OVBKRtmXAXFMYdknGNw41674sFBTaG+wjbTYrhW7wsXcyphEUyVMoQ==", + "version": "6.39.3", + "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-6.39.3.tgz", + "integrity": "sha512-a64lJ1IlV1uA7eEe8DOx+v2bkNOhnTsNlB5THP/xkHvynHqZhc74Yt05sm1vTniWwhJpJspAZ95pCWUX/RVZ2Q==", "license": "MIT", "dependencies": { - "@clerk/backend": "^1.34.0", - "@clerk/clerk-react": "^5.31.8", - "@clerk/shared": "^3.9.5", - "@clerk/types": "^4.59.3", + "@clerk/backend": "^2.33.3", + "@clerk/clerk-react": "^5.61.6", + "@clerk/shared": "^3.47.5", + "@clerk/types": "^4.101.23", "server-only": "0.0.1", "tslib": "2.8.1" }, @@ -2075,31 +2086,46 @@ "node": ">=18.17.0" }, "peerDependencies": { - "next": "^13.5.7 || ^14.2.25 || ^15.2.3", - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + "next": "^13.5.7 || ^14.2.25 || ^15.2.3 || ^16", + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/nextjs/node_modules/@clerk/backend": { + "version": "2.33.3", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.33.3.tgz", + "integrity": "sha512-cgkFVEYFG2nZn4QDuYBhiAwPtMdo8Yj7DAtq/SBQ5C/ainh3uxNRDgUj4bFn52qJkWLiCkraYJIw1b8dEUbUBg==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.47.5", + "@clerk/types": "^4.101.23", + "standardwebhooks": "^1.0.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" } }, "node_modules/@clerk/shared": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.9.5.tgz", - "integrity": "sha512-KeIug5qV4LnzZD+16SLkJvdONPs2HQ7I1A7jbHYOGB37vQrQrus64Wu5XeNzbWFTN1Z5fAPSGuja8MfT2cBT4A==", + "version": "3.47.5", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.47.5.tgz", + "integrity": "sha512-rDVe73/VN2NZXhtrLRHshkUpQDrevAqDRxeXUl2M0IBEBkcl+VMHlV7fep53cVWo0b3gIqLk82pmmi+WoyF/xg==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@clerk/types": "^4.59.3", + "csstype": "3.1.3", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.9.0", - "swr": "^2.3.3" + "swr": "2.3.4" }, "engines": { "node": ">=18.17.0" }, "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" }, "peerDependenciesMeta": { "react": { @@ -2111,21 +2137,22 @@ } }, "node_modules/@clerk/types": { - "version": "4.59.3", - "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.59.3.tgz", - "integrity": "sha512-xwOO/hfABzbFr3f1RaVXHsDDQ0+jYpm84GiaUDxo+mLsYUgD9f2GmGjKkgWybXzvsBsgZlycSwRXkeDD6utFqg==", + "version": "4.101.23", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.101.23.tgz", + "integrity": "sha512-t5ypYYDkT5TPaNIDjLnYk9GpkJgwNTBiS7h6FuUTjoySQtf7amNDS1A1eOu7NOcVpqiSeKg+0wzGxxcre00kMA==", "license": "MIT", "dependencies": { - "csstype": "3.1.3" + "@clerk/shared": "^3.47.5" }, "engines": { "node": ">=18.17.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -2402,13 +2429,24 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", - "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], + "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -2420,16 +2458,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", - "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], + "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -2441,16 +2480,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" @@ -2460,12 +2500,13 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" @@ -2475,12 +2516,13 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2490,12 +2532,13 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2505,12 +2548,29 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", "cpu": [ "ppc64" ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2520,12 +2580,13 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2535,12 +2596,13 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2550,12 +2612,13 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2565,12 +2628,13 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2580,12 +2644,13 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", - "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2597,16 +2662,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" + "@img/sharp-libvips-linux-arm": "1.2.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", - "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2618,16 +2684,61 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", - "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2639,16 +2750,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", - "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2660,16 +2772,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", - "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2681,16 +2794,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", - "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2702,19 +2816,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", - "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ "wasm32" ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.0" + "@emnapi/runtime": "^1.7.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -2723,13 +2838,33 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", - "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" @@ -2742,12 +2877,13 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", - "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" @@ -2836,9 +2972,10 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -3271,16 +3408,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -3291,41 +3425,35 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", - "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz", + "integrity": "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" } }, "node_modules/@next/env": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.1.tgz", - "integrity": "sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==" + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz", + "integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==", + "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { "version": "14.2.15", @@ -3337,12 +3465,13 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.1.tgz", - "integrity": "sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz", + "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3352,12 +3481,13 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.1.tgz", - "integrity": "sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz", + "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3367,12 +3497,13 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.1.tgz", - "integrity": "sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz", + "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3382,12 +3513,13 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.1.tgz", - "integrity": "sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz", + "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3397,12 +3529,13 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.1.tgz", - "integrity": "sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz", + "integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3412,12 +3545,13 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.1.tgz", - "integrity": "sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz", + "integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3427,12 +3561,13 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.1.tgz", - "integrity": "sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz", + "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -3442,12 +3577,13 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.1.tgz", - "integrity": "sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz", + "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -3915,10 +4051,11 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" }, "node_modules/@swc/helpers": { "version": "0.5.15", @@ -4091,10 +4228,11 @@ } }, "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } @@ -4471,21 +4609,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -4630,10 +4770,11 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5131,6 +5272,18 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -5216,9 +5369,9 @@ } }, "node_modules/bson": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz", - "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "license": "Apache-2.0", "engines": { "node": ">=16.20.1" @@ -5230,17 +5383,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -5601,19 +5743,6 @@ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -5630,16 +5759,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/color2k": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", @@ -5665,10 +5784,11 @@ } }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } @@ -5740,9 +5860,10 @@ } }, "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", "engines": { "node": ">= 6" } @@ -5926,9 +6047,10 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -6060,9 +6182,10 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "optional": true, "engines": { "node": ">=8" @@ -6219,14 +6342,14 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/editorconfig": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", - "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz", + "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==", "license": "MIT", "dependencies": { "@one-ini/wasm": "0.1.1", "commander": "^10.0.0", - "minimatch": "9.0.1", + "minimatch": "^9.0.1", "semver": "^7.5.3" }, "bin": { @@ -6237,9 +6360,9 @@ } }, "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -6255,12 +6378,12 @@ } }, "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -7166,6 +7289,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -7204,19 +7333,21 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7271,10 +7402,11 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" }, "node_modules/focus-lock": { "version": "1.3.6", @@ -7312,14 +7444,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -7386,6 +7520,20 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7583,19 +7731,21 @@ "license": "BSD-2-Clause" }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -9595,18 +9745,19 @@ } }, "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/js-beautify/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -9639,12 +9790,12 @@ } }, "node_modules/js-beautify/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -9668,10 +9819,11 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -9913,21 +10065,22 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", - "dev": true, - "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.3.6", - "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", - "micromatch": "~4.0.8", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.5.0" + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -9940,10 +10093,11 @@ } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -10063,10 +10217,11 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -10400,9 +10555,10 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -10428,14 +10584,14 @@ } }, "node_modules/mongodb": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz", - "integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", "license": "Apache-2.0", "dependencies": { - "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.3", - "mongodb-connection-string-url": "^3.0.0" + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" }, "engines": { "node": ">=16.20.1" @@ -10446,7 +10602,7 @@ "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", + "snappy": "^7.3.2", "socks": "^2.7.1" }, "peerDependenciesMeta": { @@ -10484,14 +10640,14 @@ } }, "node_modules/mongoose": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.14.1.tgz", - "integrity": "sha512-ijd12vjqUBr5Btqqflu0c/o8Oed5JpdaE0AKO9TjGxCgywYwnzt6ynR1ySjhgxGxrYVeXC0t1P11f1zlRiE93Q==", + "version": "8.23.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.23.1.tgz", + "integrity": "sha512-gHSPD8qEwRmiXapK17hEnFWZdcFENMegHTcw5XIIg2+7R8eXQvdwSiMpD/A2oG8tKzFLLHyRXd8/eaDPAVwZgQ==", "license": "MIT", "dependencies": { - "bson": "^6.10.3", + "bson": "^6.10.4", "kareem": "2.6.3", - "mongodb": "~6.16.0", + "mongodb": "~6.20.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -10553,9 +10709,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -10576,14 +10732,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/next": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/next/-/next-15.3.1.tgz", - "integrity": "sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", + "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==", + "license": "MIT", "dependencies": { - "@next/env": "15.3.1", - "@swc/counter": "0.1.3", + "@next/env": "16.2.6", "@swc/helpers": "0.5.15", - "busboy": "1.6.0", + "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -10592,22 +10748,22 @@ "next": "dist/bin/next" }, "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.3.1", - "@next/swc-darwin-x64": "15.3.1", - "@next/swc-linux-arm64-gnu": "15.3.1", - "@next/swc-linux-arm64-musl": "15.3.1", - "@next/swc-linux-x64-gnu": "15.3.1", - "@next/swc-linux-x64-musl": "15.3.1", - "@next/swc-win32-arm64-msvc": "15.3.1", - "@next/swc-win32-x64-msvc": "15.3.1", - "sharp": "^0.34.1" + "@next/swc-darwin-arm64": "16.2.6", + "@next/swc-darwin-x64": "16.2.6", + "@next/swc-linux-arm64-gnu": "16.2.6", + "@next/swc-linux-arm64-musl": "16.2.6", + "@next/swc-linux-x64-gnu": "16.2.6", + "@next/swc-linux-x64-musl": "16.2.6", + "@next/swc-win32-arm64-msvc": "16.2.6", + "@next/swc-win32-x64-msvc": "16.2.6", + "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", + "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", @@ -11092,9 +11248,10 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -11206,9 +11363,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "funding": [ { "type": "opencollective", @@ -11225,7 +11382,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -11817,11 +11974,6 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", @@ -12171,9 +12323,10 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -12220,15 +12373,16 @@ } }, "node_modules/sharp": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", - "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, + "license": "Apache-2.0", "optional": true, "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.7.1" + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -12237,26 +12391,30 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.1", - "@img/sharp-darwin-x64": "0.34.1", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.1", - "@img/sharp-linux-arm64": "0.34.1", - "@img/sharp-linux-s390x": "0.34.1", - "@img/sharp-linux-x64": "0.34.1", - "@img/sharp-linuxmusl-arm64": "0.34.1", - "@img/sharp-linuxmusl-x64": "0.34.1", - "@img/sharp-wasm32": "0.34.1", - "@img/sharp-win32-ia32": "0.34.1", - "@img/sharp-win32-x64": "0.34.1" + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" } }, "node_modules/shebang-command": { @@ -12313,21 +12471,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "optional": true - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -12483,6 +12626,16 @@ "node": ">=8" } }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/std-env": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", @@ -12501,14 +12654,6 @@ "node": ">= 0.4" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -12853,9 +12998,9 @@ } }, "node_modules/swr": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", - "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz", + "integrity": "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==", "license": "MIT", "dependencies": { "dequal": "^2.0.3", @@ -13801,14 +13946,18 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { diff --git a/src/app/api/events/[eventId]/route.ts b/src/app/api/events/[eventId]/route.ts new file mode 100644 index 00000000..8473e808 --- /dev/null +++ b/src/app/api/events/[eventId]/route.ts @@ -0,0 +1,85 @@ +import connectDB from "@/database/db"; +import Event from "@/database/eventSchema"; +import { withApiAuth } from "@/lib/auth"; +import { NextRequest, NextResponse } from "next/server"; + +function getEventId(req: NextRequest) { + return req.nextUrl.pathname.split("/").pop(); +} + +export const GET = withApiAuth( + async (req: NextRequest) => { + try { + await connectDB(); + + const eventId = getEventId(req); + const event = await Event.findById(eventId); + + if (!event) { + return NextResponse.json({ message: "Event not found" }, { status: 404 }); + } + + return NextResponse.json(event, { status: 200 }); + } catch (error) { + return NextResponse.json({ message: "Error fetching event", error }, { status: 500 }); + } + }, + { requireAuth: false }, +); + +export const PUT = withApiAuth( + async (req: NextRequest, { auth }) => { + try { + await connectDB(); + + const eventId = getEventId(req); + const eventData = await req.json(); + + const existingEvent = await Event.findById(eventId); + if (!existingEvent) { + return NextResponse.json({ message: "Event not found" }, { status: 404 }); + } + + if (auth.role === "nonprofit" && existingEvent.createdByUserId !== auth.userId) { + return NextResponse.json({ message: "Insufficient permissions" }, { status: 403 }); + } + + const updatedEvent = await Event.findByIdAndUpdate(eventId, eventData, { new: true }); + return NextResponse.json({ message: "Event updated successfully", event: updatedEvent }); + } catch (error) { + return NextResponse.json({ message: "Error updating event", error }, { status: 500 }); + } + }, + { + requireAuth: true, + allowedRoles: ["nonprofit", "spokes_admin"], + }, +); + +export const DELETE = withApiAuth( + async (req: NextRequest, { auth }) => { + try { + await connectDB(); + + const eventId = getEventId(req); + const event = await Event.findById(eventId); + + if (!event) { + return NextResponse.json({ message: "Event not found" }, { status: 404 }); + } + + if (auth.role === "nonprofit" && event.createdByUserId !== auth.userId) { + return NextResponse.json({ message: "Insufficient permissions" }, { status: 403 }); + } + + await Event.findByIdAndDelete(eventId); + return NextResponse.json({ message: "Event deleted successfully" }, { status: 200 }); + } catch (error) { + return NextResponse.json({ message: "Error deleting event", error }, { status: 500 }); + } + }, + { + requireAuth: true, + allowedRoles: ["nonprofit", "spokes_admin"], + }, +); diff --git a/src/app/api/events/route.ts b/src/app/api/events/route.ts new file mode 100644 index 00000000..27ff8862 --- /dev/null +++ b/src/app/api/events/route.ts @@ -0,0 +1,97 @@ +import connectDB from "@/database/db"; +import Event from "@/database/eventSchema"; +import User from "@/database/userSchema"; +import { withApiAuth } from "@/lib/auth"; +import { NextRequest, NextResponse } from "next/server"; + +const requiredFields = [ + "eventName", + "date", + "time", + "location", + "locationType", + "category", + "description", + "organization", +]; + +function validateEventPayload(payload: Record) { + const missingFields = requiredFields.filter((field) => { + const value = payload[field]; + return typeof value !== "string" || value.trim().length === 0; + }); + + if (missingFields.length > 0) { + return `Missing required fields: ${missingFields.join(", ")}`; + } + + if (Number.isNaN(new Date(payload.date as string).getTime())) { + return "date must be a valid date string."; + } + + if (payload.locationType !== "remote" && payload.locationType !== "in-person") { + return "locationType must be either remote or in-person."; + } + + return null; +} + +export const GET = withApiAuth( + async () => { + try { + await connectDB(); + + const events = await Event.find({}).sort({ date: 1 }); + return NextResponse.json(events, { status: 200 }); + } catch (error: any) { + console.error("Failed to fetch events:", error); + + return NextResponse.json( + { message: "Failed to fetch events", error: error?.message ?? "Unknown events error" }, + { status: 500 }, + ); + } + }, + { requireAuth: false }, +); + +export const POST = withApiAuth( + async (req: NextRequest, { auth }) => { + try { + await connectDB(); + + const eventData = await req.json(); + if (!eventData || typeof eventData !== "object") { + return NextResponse.json({ message: "Invalid event input" }, { status: 400 }); + } + + const validationError = validateEventPayload(eventData); + if (validationError) { + return NextResponse.json({ message: validationError }, { status: 400 }); + } + + const mongoUser = await User.findById(auth.userId); + if (!mongoUser) { + return NextResponse.json({ message: "User not found in DB" }, { status: 404 }); + } + + const newEvent = await Event.create({ + ...eventData, + createdByUserId: auth.userId, + }); + + return NextResponse.json({ message: "Event created successfully", event: newEvent }, { status: 201 }); + } catch (error: any) { + console.error("Could not create event:", error); + + return NextResponse.json( + { message: "Could not create event", error: error?.message ?? "Unknown event creation error" }, + { status: 500 }, + ); + } + }, + { + requireAuth: true, + allowedRoles: ["nonprofit", "spokes_admin"], + }, +); diff --git a/src/app/api/users/[id]/route.ts b/src/app/api/users/[id]/route.ts index f2fd523a..d29be6db 100644 --- a/src/app/api/users/[id]/route.ts +++ b/src/app/api/users/[id]/route.ts @@ -29,8 +29,13 @@ export const GET = withApiAuth( } return NextResponse.json(user, { status: 200 }); - } catch (error) { - return NextResponse.json({ message: "Error fetching user", error }, { status: 500 }); + } catch (error: any) { + console.error("Error fetching user:", error); + + return NextResponse.json( + { message: "Error fetching user", error: error?.message ?? "Unknown user fetch error" }, + { status: 500 }, + ); } }, { diff --git a/src/app/api/users/route.ts b/src/app/api/users/route.ts index 6f9e3b17..5aff2ed6 100644 --- a/src/app/api/users/route.ts +++ b/src/app/api/users/route.ts @@ -16,12 +16,17 @@ export const POST = withApiAuth( return NextResponse.json({ message: "Missing user data" }, { status: 400 }); } - const name = `${firstName ?? ""} ${lastName ?? ""}`.trim(); + const name = `${firstName ?? ""} ${lastName ?? ""}`.trim() || email; + const parsedPaidMember = paidMember === true || paidMember === "true"; // Check if user already exists const existingUser = await User.findById(userId); if (existingUser) { - return NextResponse.json({ message: "User already exists" }, { status: 400 }); + await updateUserMetadata(userId, { + onboardingComplete: true, + }); + + return NextResponse.json(existingUser, { status: 200 }); } // Create new user @@ -30,7 +35,7 @@ export const POST = withApiAuth( name, email: email, postedJobs: [], - paidMember: paidMember, + paidMember: parsedPaidMember, organizationName: organizationName, }); await newUser.save(); @@ -41,8 +46,16 @@ export const POST = withApiAuth( }); return NextResponse.json(newUser, { status: 201 }); - } catch (error) { - return NextResponse.json({ message: "Failed to connect user to database.", error }, { status: 500 }); + } catch (error: any) { + console.error("Failed to complete onboarding:", error); + + return NextResponse.json( + { + message: "Failed to connect user to database.", + error: error?.message ?? "Unknown onboarding error", + }, + { status: 500 }, + ); } }, { diff --git a/src/app/eventform/page.tsx b/src/app/eventform/page.tsx new file mode 100644 index 00000000..9d7016f2 --- /dev/null +++ b/src/app/eventform/page.tsx @@ -0,0 +1,190 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { createEvent } from "@/services/events"; +import { useAuth } from "@clerk/nextjs"; + +export default function EventFormPage() { + const router = useRouter(); + const { isSignedIn } = useAuth(); + const [serverError, setServerError] = useState(null); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setServerError(null); + + const formData = new FormData(e.currentTarget); + const data = { + organization: formData.get("organization") as string, + eventName: formData.get("eventName") as string, + date: formData.get("date") as string, + time: formData.get("time") as string, + description: formData.get("description") as string, + location: formData.get("location") as string, + locationType: formData.get("locationType") as string, + category: formData.get("category") as string, + }; + + try { + await createEvent(data); + router.push("/events"); + } catch (error) { + setServerError(error instanceof Error ? error.message : "An unexpected error occurred. Please try again."); + } + }; + + if (!isSignedIn) { + return ( +
+
+

Create New Event

+

Please sign in to create an event.

+
+
+ ); + } + + const inputClass = + "w-full rounded-sm bg-gray-100 px-3 py-2.5 text-sm text-gray-800 placeholder-gray-400 outline-none focus:bg-gray-200"; + const labelClass = "block text-sm font-bold text-black mb-1"; + const fieldClass = "mb-4"; + + return ( +
+
+

Create New Event

+ + {serverError && ( +
{serverError}
+ )} + +
+

Event Information

+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+ ); +} diff --git a/src/app/events/[eventId]/page.tsx b/src/app/events/[eventId]/page.tsx new file mode 100644 index 00000000..d0d98b34 --- /dev/null +++ b/src/app/events/[eventId]/page.tsx @@ -0,0 +1,54 @@ +import Link from "next/link"; +import connectDB from "@/database/db"; +import Event, { type IEvent } from "@/database/eventSchema"; +import { formatDate } from "@/lib/utils"; + +type EventPageProps = { + params: { + eventId: string; + }; +}; + +export default async function EventPage({ params }: EventPageProps) { + await connectDB(); + + const event = (await Event.findById(params.eventId).lean()) as IEvent | null; + + if (!event) { + return ( +
+
+

Event not found

+ Back to Events +
+
+ ); + } + + return ( +
+
+ + Back to Events + +
+ {event.category} +

{event.eventName}

+

{event.organization}

+
+

+ Date: {formatDate(event.date)} +

+

+ Time: {event.time} +

+

+ Location: {event.location} +

+
+

{event.description}

+
+
+
+ ); +} diff --git a/src/app/events/page.tsx b/src/app/events/page.tsx new file mode 100644 index 00000000..1906ff0b --- /dev/null +++ b/src/app/events/page.tsx @@ -0,0 +1,318 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; +import EventCard from "@/components/events/EventCard"; +import { getAllEvents } from "@/services/events"; +import type { EventRecord } from "@/types/event"; + +const FAVORITES_STORAGE_KEY = "favoriteEventIds"; + +type EventsTab = "all" | "favorites"; + +function filterEvents( + events: EventRecord[], + dateFilter: string, + categoryFilter: string, + locationFilter: string, +): EventRecord[] { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + return events.filter((event) => { + if (categoryFilter && event.category?.toLowerCase() !== categoryFilter.toLowerCase()) { + return false; + } + + if (locationFilter && event.location !== locationFilter) { + return false; + } + + if (!dateFilter) { + return true; + } + + const eventDate = new Date(event.date); + eventDate.setHours(0, 0, 0, 0); + + if (dateFilter === "today") { + return eventDate.getTime() === today.getTime(); + } + + if (dateFilter === "week") { + const weekFromNow = new Date(today); + weekFromNow.setDate(weekFromNow.getDate() + 7); + return eventDate >= today && eventDate <= weekFromNow; + } + + if (dateFilter === "month") { + const monthFromNow = new Date(today); + monthFromNow.setMonth(monthFromNow.getMonth() + 1); + return eventDate >= today && eventDate <= monthFromNow; + } + + return true; + }); +} + +function getTopLocations(events: EventRecord[]): string[] { + const counts = new Map(); + + for (const event of events) { + const location = event.location?.trim(); + if (!location) continue; + counts.set(location, (counts.get(location) ?? 0) + 1); + } + + return [...counts.entries()] + .sort((a, b) => { + if (b[1] !== a[1]) { + return b[1] - a[1]; + } + return a[0].localeCompare(b[0]); + }) + .map(([location]) => location); +} + +export default function EventsPage() { + const [events, setEvents] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [loadError, setLoadError] = useState(null); + const [dateFilter, setDateFilter] = useState(""); + const [categoryFilter, setCategoryFilter] = useState(""); + const [locationFilter, setLocationFilter] = useState(""); + const [favoriteEventIds, setFavoriteEventIds] = useState([]); + const [activeTab, setActiveTab] = useState("all"); + const [showAllLocations, setShowAllLocations] = useState(false); + + useEffect(() => { + let isCancelled = false; + + async function loadEvents() { + setIsLoading(true); + setLoadError(null); + + try { + const response = await getAllEvents(); + + if (!isCancelled) { + setEvents(response); + } + } catch (error) { + console.error("Error loading events:", error); + if (!isCancelled) { + setEvents([]); + setLoadError("Failed to load events."); + } + } finally { + if (!isCancelled) { + setIsLoading(false); + } + } + } + + void loadEvents(); + + return () => { + isCancelled = true; + }; + }, []); + + useEffect(() => { + const storedFavorites = localStorage.getItem(FAVORITES_STORAGE_KEY); + if (!storedFavorites) { + return; + } + + try { + const parsed = JSON.parse(storedFavorites); + if (Array.isArray(parsed)) { + setFavoriteEventIds(parsed.filter((id): id is string => typeof id === "string")); + } + } catch { + setFavoriteEventIds([]); + } + }, []); + + const filteredEvents = useMemo( + () => filterEvents(events, dateFilter, categoryFilter, locationFilter), + [events, dateFilter, categoryFilter, locationFilter], + ); + + const favoriteEventIdSet = useMemo(() => new Set(favoriteEventIds), [favoriteEventIds]); + const topLocations = useMemo(() => getTopLocations(events), [events]); + + const displayedEvents = useMemo(() => { + if (activeTab === "favorites") { + return filteredEvents.filter((event) => event.id && favoriteEventIdSet.has(event.id)); + } + + return filteredEvents; + }, [activeTab, favoriteEventIdSet, filteredEvents]); + + const hasActiveFilters = Boolean(dateFilter || categoryFilter || locationFilter); + + const handleToggleFavorite = (eventId?: string) => { + if (!eventId) { + return; + } + + setFavoriteEventIds((prev) => { + const next = prev.includes(eventId) ? prev.filter((id) => id !== eventId) : [...prev, eventId]; + localStorage.setItem(FAVORITES_STORAGE_KEY, JSON.stringify(next)); + return next; + }); + }; + + const clearFilters = () => { + setDateFilter(""); + setCategoryFilter(""); + setLocationFilter(""); + }; + + return ( +
+
+
+

Events

+

Browse nonprofit events. Use filters and favorite events to view them later.

+
+ +
+ + +
+
+ + +
+ + {isLoading ? ( +

Loading events...

+ ) : loadError ? ( +

{loadError}

+ ) : displayedEvents.length > 0 ? ( +
    + {displayedEvents.map((event) => ( +
  • + +
  • + ))} +
+ ) : events.length === 0 ? ( +

No events available.

+ ) : activeTab === "favorites" ? ( +

No favorite events match your filters.

+ ) : ( +

No events match your filters.

+ )} +
+
+
+
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css index fc6e38d4..ad6270de 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -190,6 +190,307 @@ html { } } +.page-main { + min-height: calc(100vh - 80px); + background-color: #f9fafb; +} + +.page-content { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +.page-content--narrow { + max-width: 720px; +} + +.page-content--no-padding { + padding-left: 0; + padding-right: 0; +} + +.page-hero { + margin-bottom: 2rem; +} + +.page-hero h1 { + font-size: 1.75rem; + font-weight: 700; + color: #111827; + margin-bottom: 0.5rem; +} + +.page-hero p { + color: #6b7280; + font-size: 1rem; +} + +.page-hero--compact { + padding: 0 2rem 1.5rem; +} + +.page-hero--compact h1 { + margin-bottom: 0.25rem; +} + +.page-hero--compact p { + font-size: 0.9375rem; +} + +.events-layout { + display: flex; + gap: 0; + min-height: 400px; +} + +.filter-sidebar { + width: 260px; + flex-shrink: 0; + background-color: white; + border-right: 1px solid #e5e7eb; + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1.25rem; + align-self: flex-start; +} + +.filter-sidebar-title { + font-size: 0.875rem; + font-weight: 600; + color: #374151; + margin-bottom: 0.25rem; +} + +.filter-sidebar-group { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.filter-sidebar-group label { + font-size: 0.75rem; + font-weight: 500; + color: #6b7280; +} + +.filter-location-option { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.375rem 0.5rem; + border-radius: 6px; + font-size: 0.875rem; + color: #374151; + background: transparent; + border: none; + cursor: pointer; + width: 100%; + text-align: left; + transition: background-color 0.15s; +} + +.filter-location-option:hover { + background-color: #f3f4f6; +} + +.filter-location-option.active { + background-color: #eff6ff; + color: #1d4ed8; + font-weight: 500; +} + +.filter-location-view-more { + font-size: 0.75rem; + color: #6b7280; + background: transparent; + border: none; + cursor: pointer; + padding: 0.25rem 0.5rem; + text-decoration: underline; +} + +.filter-location-view-more:hover { + color: #374151; +} + +.filter-sidebar-clear { + padding: 0.5rem 1rem; + font-size: 0.875rem; + color: #6b7280; + background: transparent; + border: 1px solid #d1d5db; + border-radius: 6px; + cursor: pointer; + margin-top: 0.25rem; +} + +.filter-sidebar-clear:hover { + background-color: #f3f4f6; + color: #374151; +} + +.events-main { + flex: 1; + padding: 0 2rem 2rem; + min-width: 0; +} + +.events-empty { + color: #6b7280; + font-size: 0.9375rem; + padding: 2rem; +} + +.events-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.events-list-item { + margin: 0; +} + +.event-card { + display: flex; + flex-direction: column; + position: relative; + background: white; + border-radius: 8px; + padding: 1rem 1.25rem; + text-decoration: none; + color: inherit; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + border: 1px solid #e5e7eb; + transition: + box-shadow 0.2s ease, + border-color 0.2s ease; +} + +.event-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: #d1d5db; +} + +.event-card-category { + display: inline-flex; + align-items: center; + font-size: 0.75rem; + font-weight: 600; + color: #ea580c; + background-color: #fff7ed; + border: 1px solid #fed7aa; + border-radius: 999px; + padding: 0.125rem 0.5rem; + text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; +} + +.event-card-title { + font-size: 1.125rem; + font-weight: 600; + color: #111827; + margin: 0; +} + +.event-card-description { + font-size: 0.875rem; + color: #4b5563; + margin-top: 0.75rem; + line-height: 1.5; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.event-detail-back { + display: inline-block; + font-size: 0.875rem; + color: #2563eb; + text-decoration: none; + margin-bottom: 1.5rem; +} + +.event-detail-back:hover { + text-decoration: underline; +} + +.event-detail { + background: white; + border-radius: 8px; + padding: 2rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.event-detail-category { + display: inline-block; + font-size: 0.75rem; + font-weight: 600; + color: #ea580c; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 0.5rem; +} + +.event-detail-title { + font-size: 1.75rem; + font-weight: 700; + color: #111827; + margin-bottom: 0.25rem; +} + +.event-detail-org { + font-size: 1rem; + color: #6b7280; + margin-bottom: 1.5rem; +} + +.event-detail-meta { + display: flex; + flex-wrap: wrap; + gap: 1rem 2rem; + margin-bottom: 1.5rem; + padding: 1rem 0; + border-top: 1px solid #e5e7eb; + border-bottom: 1px solid #e5e7eb; +} + +.event-detail-meta p { + font-size: 0.9375rem; + color: #374151; + margin: 0; +} + +.event-detail-description { + font-size: 1rem; + line-height: 1.6; + color: #4b5563; + margin-bottom: 1.5rem; +} + +@media (max-width: 768px) { + .events-layout { + flex-direction: column; + } + + .filter-sidebar { + width: 100%; + border-right: none; + border-bottom: 1px solid #e5e7eb; + } + + .events-main { + padding: 1.5rem; + } +} + @layer base { * { @apply border-border outline-ring/50; diff --git a/src/components/EventCard/EventCard.tsx b/src/components/EventCard/EventCard.tsx new file mode 100644 index 00000000..a056164a --- /dev/null +++ b/src/components/EventCard/EventCard.tsx @@ -0,0 +1 @@ +export { default } from "@/components/events/EventCard"; diff --git a/src/components/EventCard/EventGrid.tsx b/src/components/EventCard/EventGrid.tsx new file mode 100644 index 00000000..1d5532e8 --- /dev/null +++ b/src/components/EventCard/EventGrid.tsx @@ -0,0 +1,21 @@ +import type { EventRecord } from "@/types/event"; +import EventCard from "@/components/events/EventCard"; + +export default function EventGrid({ events }: { events: EventRecord[] }) { + if (events.length === 0) { + return

No events available.

; + } + + return ( +
    + {events.map((event) => ( +
  • + +
  • + ))} +
+ ); +} diff --git a/src/components/NavBar/BottomSection.tsx b/src/components/NavBar/BottomSection.tsx index f95915a2..cb3ff43f 100644 --- a/src/components/NavBar/BottomSection.tsx +++ b/src/components/NavBar/BottomSection.tsx @@ -58,7 +58,9 @@ export default function BottomSection() { */} {/* prod */} + {isSignedIn && } + {isSignedIn && setIsMobileMenuOpen(false)} />} {isSignedIn && setIsMobileMenuOpen(false)} />} {isSignedIn && isSpokesAdmin && } @@ -114,7 +116,9 @@ export default function BottomSection() { setIsMobileMenuOpen(false)} /> */} {/* prod */} + setIsMobileMenuOpen(false)} /> {isSignedIn && setIsMobileMenuOpen(false)} />} + {isSignedIn && setIsMobileMenuOpen(false)} />} {isSignedIn && setIsMobileMenuOpen(false)} />} {isSignedIn && isSpokesAdmin && ( setIsMobileMenuOpen(false)} /> diff --git a/src/components/events/EventCard.tsx b/src/components/events/EventCard.tsx new file mode 100644 index 00000000..305f0c30 --- /dev/null +++ b/src/components/events/EventCard.tsx @@ -0,0 +1,112 @@ +import { formatDate } from "@/lib/utils"; +import type { EventRecord } from "@/types/event"; +import { EventModal } from "./EventModal"; +import { FaStar } from "react-icons/fa"; +import { FiStar } from "react-icons/fi"; + +type EventCardProps = { + event: EventRecord; + isFavorite?: boolean; + onToggleFavorite?: (eventId?: string) => void; +}; + +export default function EventCard({ event, isFavorite = false, onToggleFavorite }: EventCardProps) { + const favoriteLabel = isFavorite ? "Remove from favorites" : "Add to favorites"; + const month = event.date.toLocaleString("en-US", { month: "short", timeZone: "UTC" }).toUpperCase(); + const day = event.date.getUTCDate(); + const year = event.date.getUTCFullYear(); + + return ( +
+ +
+
+ {month} + {day} + {year} +
+ +
+
+
+

{event.eventName}

+ {event.category} +
+ {onToggleFavorite && ( + + )} +
+ +
+

+ + + + + + + {formatDate(event.date)} +

+ {event.time && ( +

+ + + + + {event.time} +

+ )} + {event.location && ( +

+ + + + + {event.location} +

+ )} + {event.description &&

{event.description}

} +
+
+
+
+
+ ); +} diff --git a/src/components/events/EventModal.tsx b/src/components/events/EventModal.tsx new file mode 100644 index 00000000..e0f490a1 --- /dev/null +++ b/src/components/events/EventModal.tsx @@ -0,0 +1,138 @@ +"use client"; + +import * as React from "react"; +import Image from "next/image"; +import Link from "next/link"; +import { formatDate } from "@/lib/utils"; +import type { EventRecord } from "@/types/event"; + +export interface EventModalProps { + event: EventRecord; + children: React.ReactNode; +} + +export function EventModal({ event, children }: EventModalProps) { + const [open, setOpen] = React.useState(false); + + return ( + <> +
{ + e.preventDefault(); + e.stopPropagation(); + setOpen(true); + }} + > + {children} +
+ + {open && ( +
setOpen(false)}> +
+ +
e.stopPropagation()} + > +
+ {event.eventImage ? ( + {event.eventName} + ) : ( +
+ )} + +
+ +
+ {event.category && ( + + {event.category} + + )} + +

{event.eventName}

+

By {event.organization}

+ +
+

+ + + + + + + {formatDate(event.date)} +

+ {event.time && ( +

+ + + + + {event.time} +

+ )} + {event.location && ( +

+ + + + + {event.location} +

+ )} +
+ + {event.description &&

{event.description}

} + + setOpen(false)} + > + Log In / Sign Up + +
+
+
+ )} + + ); +} diff --git a/src/database/db.ts b/src/database/db.ts index 6e72b0c6..9fde005e 100644 --- a/src/database/db.ts +++ b/src/database/db.ts @@ -9,6 +9,10 @@ let connection: typeof mongoose; * @returns {Promise} */ const connectDB = async () => { + if (!url) { + throw new Error("Missing MONGO_URI environment variable."); + } + if (!connection) { connection = await mongoose.connect(url); return connection; diff --git a/src/database/eventSchema.ts b/src/database/eventSchema.ts new file mode 100644 index 00000000..48cf9d57 --- /dev/null +++ b/src/database/eventSchema.ts @@ -0,0 +1,38 @@ +import { models, model, Schema } from "mongoose"; + +export interface IEvent { + _id: string; + date: Date; + eventName: string; + time: string; + location: string; + locationType: "remote" | "in-person"; + category: string; + description: string; + organization: string; + eventImage?: string; + organizationIcon?: string; + createdByUserId: string; + createdAt?: Date; + updatedAt?: Date; +} + +const EventSchema = new Schema( + { + date: { type: Date, required: true }, + eventName: { type: String, required: true, trim: true }, + time: { type: String, required: true, trim: true }, + location: { type: String, required: true, trim: true }, + locationType: { type: String, required: true, enum: ["remote", "in-person"], trim: true }, + category: { type: String, required: true, trim: true }, + description: { type: String, required: true, trim: true }, + organization: { type: String, required: true, trim: true }, + eventImage: { type: String, required: false, trim: true }, + organizationIcon: { type: String, required: false, trim: true }, + createdByUserId: { type: String, required: true }, + }, + { timestamps: true }, +); + +const Event = models.Event || model("Event", EventSchema, "calendars"); +export default Event; diff --git a/src/lib/clerk.ts b/src/lib/clerk.ts index ff14c82c..476c5a0a 100644 --- a/src/lib/clerk.ts +++ b/src/lib/clerk.ts @@ -7,15 +7,9 @@ import { clerkClient } from "@clerk/nextjs/server"; * @returns A promise that resolves when the update is complete */ export async function updateUserMetadata(userId: string, metadata: Record) { - try { - const client = await clerkClient(); + const client = await clerkClient(); - await client.users.updateUserMetadata(userId, { - publicMetadata: metadata, - }); - return { success: true }; - } catch (error) { - console.error("Error updating user metadata:", error); - return { success: false, error }; - } + await client.users.updateUserMetadata(userId, { + publicMetadata: metadata, + }); } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 132ecbff..3d8b7cd9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,6 @@ import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; +import type { EventApiRecord, EventRecord } from "@/types/event"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -42,3 +43,31 @@ export function isExpired(jobStatus: string, approvedDate?: Date): boolean { if (!approvedDate) return false; return new Date(approvedDate) < getThirtyDaysAgo() || jobStatus.toLowerCase() === "expired"; } + +function toDate(input: string | Date | undefined): Date | undefined { + if (!input) return undefined; + return input instanceof Date ? input : new Date(input); +} + +export function normalizeEvent(event: EventApiRecord): EventRecord { + return { + ...event, + id: event.id ?? event._id, + date: toDate(event.date) ?? new Date(), + createdAt: toDate(event.createdAt), + updatedAt: toDate(event.updatedAt), + }; +} + +export function formatDate(input: string | Date): string { + const date = input instanceof Date ? input : new Date(input); + const fmt = new Intl.DateTimeFormat("en-US", { + timeZone: "UTC", + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + + return fmt.format(date); +} diff --git a/src/services/events.ts b/src/services/events.ts new file mode 100644 index 00000000..7bd39ee9 --- /dev/null +++ b/src/services/events.ts @@ -0,0 +1,41 @@ +import { normalizeEvent } from "@/lib/utils"; +import type { EventApiRecord, EventRecord } from "@/types/event"; + +export async function getAllEvents(): Promise { + const response = await fetch("/api/events", { headers: { Accept: "application/json" } }); + + if (!response.ok) { + const result = await response.json().catch(() => null); + throw new Error(result?.error || result?.message || "Failed to fetch events"); + } + + const events: EventApiRecord[] = await response.json(); + return events.map(normalizeEvent); +} + +export async function getEventById(id: string): Promise { + const response = await fetch(`/api/events/${encodeURIComponent(id)}`, { headers: { Accept: "application/json" } }); + + if (!response.ok) { + throw new Error("Failed to fetch event"); + } + + const event: EventApiRecord = await response.json(); + return normalizeEvent(event); +} + +export async function createEvent(data: unknown): Promise { + const response = await fetch("/api/events", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + }); + + const result = await response.json(); + + if (!response.ok) { + throw new Error(result?.message || "Failed to create event"); + } + + return normalizeEvent(result.event ?? result); +} diff --git a/src/types/event.ts b/src/types/event.ts new file mode 100644 index 00000000..5eb1e923 --- /dev/null +++ b/src/types/event.ts @@ -0,0 +1,23 @@ +export interface EventRecord { + id?: string; + _id?: string; + date: Date; + eventName: string; + time: string; + location: string; + locationType: "remote" | "in-person"; + category: string; + organization: string; + description: string; + eventImage?: string; + organizationIcon?: string; + createdByUserId?: string; + createdAt?: Date; + updatedAt?: Date; +} + +export type EventApiRecord = Omit & { + date: string | Date; + createdAt?: string | Date; + updatedAt?: string | Date; +}; diff --git a/tsconfig.json b/tsconfig.json index bcf6eee8..f6742028 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "moduleResolution": "Bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -29,7 +29,8 @@ "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", - "src/components/ChakraCarousel/carousel.js" + "src/components/ChakraCarousel/carousel.js", + ".next/dev/types/**/*.ts" ], "exclude": ["node_modules"] } From a259684f091ef5695c69361ea3a17bf2cd38844d Mon Sep 17 00:00:00 2001 From: kbeltr03 Date: Fri, 15 May 2026 23:11:11 -0700 Subject: [PATCH 02/38] some bug fixes but things work well --- package-lock.json | 36 +++++--- package.json | 4 +- src/app/api/__test__/EventsApi.test.ts | 119 +++++++++++++++++++++++++ src/app/api/events/[eventId]/route.ts | 16 +++- src/app/api/events/route.ts | 42 ++------- src/app/api/send/new/route.ts | 22 +++-- src/app/api/send/reject/route.ts | 8 +- src/app/api/send/update/route.ts | 12 ++- src/app/eventform/page.tsx | 15 ---- src/app/jobform/JobFormPage.client.tsx | 22 ++--- src/database/eventSchema.ts | 2 +- src/lib/events.ts | 51 +++++++++++ src/lib/url.ts | 15 ++++ src/middleware.ts | 9 +- 14 files changed, 286 insertions(+), 87 deletions(-) create mode 100644 src/app/api/__test__/EventsApi.test.ts create mode 100644 src/lib/events.ts create mode 100644 src/lib/url.ts diff --git a/package-lock.json b/package-lock.json index 3669b3f1..0c692fa4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -126,6 +126,7 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -1957,6 +1958,7 @@ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.10.7.tgz", "integrity": "sha512-GX1dCmnvrxxyZEofDX9GMAtRakZJKnUqFM9k8qhaycPaeyfkiTNNTjhPNX917hgVx1yhC3kcJOs5IeC7yW56/g==", "license": "MIT", + "peer": true, "dependencies": { "@chakra-ui/hooks": "2.4.4", "@chakra-ui/styled-system": "2.12.2", @@ -1982,6 +1984,7 @@ "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.12.2.tgz", "integrity": "sha512-BlQ7i3+GYC0S0c72B+paa0sYo+QeNSMfz6fwQRFsc8A5Aax9i9lSdRL+vwJVC+k6r/0HWfRwk016R2RD2ihEwQ==", "license": "MIT", + "peer": true, "dependencies": { "@chakra-ui/utils": "2.2.4", "csstype": "^3.1.2" @@ -2232,6 +2235,7 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -2285,6 +2289,7 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -4090,6 +4095,7 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.66.0.tgz", "integrity": "sha512-z3sYixFQJe8hndFnXgWu7C79ctL+pI0KAelYyW+khaNJ1m22lWrhJU2QrsTcRKMuVPtoZvfBYrTStIdKo+x0Xw==", "license": "MIT", + "peer": true, "dependencies": { "@tanstack/query-core": "5.66.0" }, @@ -4143,7 +4149,6 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -4241,8 +4246,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -4412,6 +4416,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "devOptional": true, + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4422,6 +4427,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", "devOptional": true, + "peer": true, "dependencies": { "@types/react": "*" } @@ -4503,6 +4509,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.10.0", "@typescript-eslint/types": "8.10.0", @@ -4719,6 +4726,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5335,6 +5343,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -6241,8 +6250,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/dom-serializer": { "version": "2.0.0", @@ -6713,6 +6721,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -6795,6 +6804,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6888,6 +6898,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -7478,6 +7489,7 @@ "version": "11.15.0", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.15.0.tgz", "integrity": "sha512-MLk8IvZntxOMg7lDBLw2qgTHHv664bYoYmnFTmE0Gm/FW67aOJk0WM3ctMcG+Xhcv+vh5uyyXwxvxhSeJzSe+w==", + "peer": true, "dependencies": { "motion-dom": "^11.14.3", "motion-utils": "^11.14.3", @@ -8680,6 +8692,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -10414,7 +10427,6 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -10736,6 +10748,7 @@ "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==", "license": "MIT", + "peer": true, "dependencies": { "@next/env": "16.2.6", "@swc/helpers": "0.5.15", @@ -11381,6 +11394,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11519,6 +11533,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11546,7 +11561,6 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -11561,7 +11575,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "peer": true, "engines": { "node": ">=10" }, @@ -11573,8 +11586,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "peer": true + "dev": true }, "node_modules/prompts": { "version": "2.4.2", @@ -11670,6 +11682,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -11692,6 +11705,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -13047,6 +13061,7 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -13411,6 +13426,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index c1f5ab68..35482718 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint", - "lint:fix": "next lint --fix", + "lint": "eslint .", + "lint:fix": "eslint . --fix", "format": "prettier --write .", "test": "jest --watchAll=false", "prepare": "husky" diff --git a/src/app/api/__test__/EventsApi.test.ts b/src/app/api/__test__/EventsApi.test.ts new file mode 100644 index 00000000..71597a4c --- /dev/null +++ b/src/app/api/__test__/EventsApi.test.ts @@ -0,0 +1,119 @@ +import Event from "@/database/eventSchema"; +import User from "@/database/userSchema"; +import { POST } from "@/app/api/events/route"; +import { PUT } from "@/app/api/events/[eventId]/route"; + +const mockAuth = { + userId: "user-1", + role: "nonprofit", +}; + +jest.mock("@/database/db", () => ({ + __esModule: true, + default: jest.fn(() => Promise.resolve()), +})); + +jest.mock("@/database/eventSchema", () => ({ + __esModule: true, + default: { + findById: jest.fn(), + findByIdAndUpdate: jest.fn(), + create: jest.fn(), + }, +})); + +jest.mock("@/database/userSchema", () => ({ + __esModule: true, + default: { + findById: jest.fn(), + }, +})); + +jest.mock("@/lib/auth", () => ({ + withApiAuth: (handler: Function) => { + return async (req: Request, context: any = {}) => handler(req, { ...context, auth: mockAuth }); + }, +})); + +jest.mock("next/server", () => ({ + NextResponse: { + json: (body: unknown, init?: { status?: number }) => ({ + status: init?.status ?? 200, + json: async () => body, + }), + }, +})); + +const validEventPayload = { + eventName: "Community Workshop", + date: "2026-02-27", + time: "6:00 PM", + location: "Innovation Hub", + locationType: "in-person", + category: "workshop", + description: "A useful workshop.", +}; + +function jsonRequest(path: string, body: Record) { + return { + nextUrl: { pathname: path }, + json: async () => body, + } as any; +} + +describe("Events API", () => { + beforeEach(() => { + jest.clearAllMocks(); + mockAuth.userId = "user-1"; + mockAuth.role = "nonprofit"; + }); + + test("POST derives organization and ownership from the authenticated Mongo user", async () => { + (User.findById as jest.Mock).mockResolvedValue({ organizationName: "Spokes Nonprofit" }); + (Event.create as jest.Mock).mockResolvedValue({ _id: "event-1", ...validEventPayload }); + + const response = await POST( + jsonRequest("/api/events", { + ...validEventPayload, + organization: "Spoofed Org", + createdByUserId: "attacker", + }), + {}, + ); + + expect(response.status).toBe(201); + expect(Event.create).toHaveBeenCalledWith({ + ...validEventPayload, + organization: "Spokes Nonprofit", + createdByUserId: "user-1", + }); + }); + + test("PUT blocks nonprofits from updating events they do not own", async () => { + (Event.findById as jest.Mock).mockResolvedValue({ _id: "event-1", createdByUserId: "user-2" }); + + const response = await PUT(jsonRequest("/api/events/event-1", { eventName: "Updated" }), {}); + const result = await response.json(); + + expect(response.status).toBe(403); + expect(result.message).toBe("Insufficient permissions"); + expect(Event.findByIdAndUpdate).not.toHaveBeenCalled(); + }); + + test("PUT only writes whitelisted mutable event fields", async () => { + (Event.findById as jest.Mock).mockResolvedValue({ _id: "event-1", createdByUserId: "user-1" }); + (Event.findByIdAndUpdate as jest.Mock).mockResolvedValue({ _id: "event-1", eventName: "Updated Event" }); + + const response = await PUT( + jsonRequest("/api/events/event-1", { + eventName: "Updated Event", + organization: "Spoofed Org", + createdByUserId: "attacker", + }), + {}, + ); + + expect(response.status).toBe(200); + expect(Event.findByIdAndUpdate).toHaveBeenCalledWith("event-1", { eventName: "Updated Event" }, { new: true }); + }); +}); diff --git a/src/app/api/events/[eventId]/route.ts b/src/app/api/events/[eventId]/route.ts index 8473e808..9de07f39 100644 --- a/src/app/api/events/[eventId]/route.ts +++ b/src/app/api/events/[eventId]/route.ts @@ -1,6 +1,7 @@ import connectDB from "@/database/db"; import Event from "@/database/eventSchema"; import { withApiAuth } from "@/lib/auth"; +import { sanitizeEventPayload, validateEventPayload } from "@/lib/events"; import { NextRequest, NextResponse } from "next/server"; function getEventId(req: NextRequest) { @@ -34,6 +35,9 @@ export const PUT = withApiAuth( const eventId = getEventId(req); const eventData = await req.json(); + if (!eventData || typeof eventData !== "object") { + return NextResponse.json({ message: "Invalid event input" }, { status: 400 }); + } const existingEvent = await Event.findById(eventId); if (!existingEvent) { @@ -44,7 +48,17 @@ export const PUT = withApiAuth( return NextResponse.json({ message: "Insufficient permissions" }, { status: 403 }); } - const updatedEvent = await Event.findByIdAndUpdate(eventId, eventData, { new: true }); + const validationError = validateEventPayload(eventData, { partial: true }); + if (validationError) { + return NextResponse.json({ message: validationError }, { status: 400 }); + } + + const sanitizedEventData = sanitizeEventPayload(eventData); + if (Object.keys(sanitizedEventData).length === 0) { + return NextResponse.json({ message: "No valid event fields provided" }, { status: 400 }); + } + + const updatedEvent = await Event.findByIdAndUpdate(eventId, sanitizedEventData, { new: true }); return NextResponse.json({ message: "Event updated successfully", event: updatedEvent }); } catch (error) { return NextResponse.json({ message: "Error updating event", error }, { status: 500 }); diff --git a/src/app/api/events/route.ts b/src/app/api/events/route.ts index 27ff8862..062eca88 100644 --- a/src/app/api/events/route.ts +++ b/src/app/api/events/route.ts @@ -2,40 +2,9 @@ import connectDB from "@/database/db"; import Event from "@/database/eventSchema"; import User from "@/database/userSchema"; import { withApiAuth } from "@/lib/auth"; +import { sanitizeEventPayload, validateEventPayload } from "@/lib/events"; import { NextRequest, NextResponse } from "next/server"; -const requiredFields = [ - "eventName", - "date", - "time", - "location", - "locationType", - "category", - "description", - "organization", -]; - -function validateEventPayload(payload: Record) { - const missingFields = requiredFields.filter((field) => { - const value = payload[field]; - return typeof value !== "string" || value.trim().length === 0; - }); - - if (missingFields.length > 0) { - return `Missing required fields: ${missingFields.join(", ")}`; - } - - if (Number.isNaN(new Date(payload.date as string).getTime())) { - return "date must be a valid date string."; - } - - if (payload.locationType !== "remote" && payload.locationType !== "in-person") { - return "locationType must be either remote or in-person."; - } - - return null; -} - export const GET = withApiAuth( async () => { try { @@ -75,8 +44,15 @@ export const POST = withApiAuth( return NextResponse.json({ message: "User not found in DB" }, { status: 404 }); } + if (!mongoUser.organizationName) { + return NextResponse.json({ message: "User organization is required to create an event" }, { status: 400 }); + } + + const sanitizedEventData = sanitizeEventPayload(eventData); + const newEvent = await Event.create({ - ...eventData, + ...sanitizedEventData, + organization: mongoUser.organizationName, createdByUserId: auth.userId, }); diff --git a/src/app/api/send/new/route.ts b/src/app/api/send/new/route.ts index bbc7954c..8af2e5cd 100644 --- a/src/app/api/send/new/route.ts +++ b/src/app/api/send/new/route.ts @@ -2,17 +2,25 @@ import { NewJob } from "@/components/EmailTemplates/NewJob"; import { Resend } from "resend"; import { NextRequest, NextResponse } from "next/server"; import { withApiAuth } from "@/lib/auth"; +import { getAdminUrl } from "@/lib/url"; -const resend = new Resend(process.env.RESEND_API_KEY); const adminEmail = process.env.ADMIN_EMAIL || ""; -const adminURL = process.env.NEXT_PUBLIC_ADMIN_URL || "/admin"; const senderEmail = process.env.SENDER_EMAIL || "onboarding@resend.dev"; export const POST = withApiAuth( - async (req: NextRequest, { auth }) => { + async (req: NextRequest) => { try { + if (!process.env.RESEND_API_KEY) { + return NextResponse.json({ error: "Email service is not configured" }, { status: 500 }); + } + + if (!adminEmail) { + return NextResponse.json({ error: "ADMIN_EMAIL is not configured" }, { status: 500 }); + } + const jobData = await req.json(); + const resend = new Resend(process.env.RESEND_API_KEY); const { data, error } = await resend.emails.send({ from: `Spokes Job Board <${senderEmail}>`, to: [adminEmail], @@ -28,15 +36,15 @@ export const POST = withApiAuth( contactEmail: jobData.contactEmail, detailURL: jobData.detailURL, applyNowURL: jobData.applyNowURL, - adminURL: adminURL, + adminURL: getAdminUrl(), }), }); if (error) { - return NextResponse.json({ error }, { status: 500 }); + return NextResponse.json({ error: error.message }, { status: 500 }); } return NextResponse.json({ success: true, data }); - } catch (error) { - return NextResponse.json({ error: "Failed to send email" }, { status: 500 }); + } catch (error: any) { + return NextResponse.json({ error: error?.message || "Failed to send email" }, { status: 500 }); } }, { diff --git a/src/app/api/send/reject/route.ts b/src/app/api/send/reject/route.ts index 924eefd7..73cc5fe6 100644 --- a/src/app/api/send/reject/route.ts +++ b/src/app/api/send/reject/route.ts @@ -3,17 +3,21 @@ import { Resend } from "resend"; import { NextRequest, NextResponse } from "next/server"; import { withApiAuth } from "@/lib/auth"; -const resend = new Resend(process.env.RESEND_API_KEY); const senderEmail = process.env.SENDER_EMAIL || "onboarding@resend.dev"; export const POST = withApiAuth( - async (req: NextRequest, { auth }) => { + async (req: NextRequest) => { try { + if (!process.env.RESEND_API_KEY) { + return NextResponse.json({ error: "Email service is not configured" }, { status: 500 }); + } + const jobData = await req.json(); if (!jobData.rejectionReason || jobData.rejectionReason.trim() === "") { jobData.rejectionReason = "No reason provided."; } + const resend = new Resend(process.env.RESEND_API_KEY); const { data, error } = await resend.emails.send({ from: `Spokes Job Board <${senderEmail}>`, to: [`${jobData.contactEmail}`], diff --git a/src/app/api/send/update/route.ts b/src/app/api/send/update/route.ts index 5ed0bd00..709d3644 100644 --- a/src/app/api/send/update/route.ts +++ b/src/app/api/send/update/route.ts @@ -2,15 +2,18 @@ import { NextRequest, NextResponse } from "next/server"; import { withApiAuth } from "@/lib/auth"; import { Resend } from "resend"; import { UpdatedJob } from "@/components/EmailTemplates/UpdatedJob"; +import { getAdminUrl } from "@/lib/url"; -const resend = new Resend(process.env.RESEND_API_KEY); const adminEmail = process.env.ADMIN_EMAIL || ""; -const adminURL = process.env.NEXT_PUBLIC_ADMIN_URL || "/admin"; const senderEmail = process.env.SENDER_EMAIL || "onboarding@resend.dev"; export const POST = withApiAuth( - async (req: NextRequest, { auth }) => { + async (req: NextRequest) => { try { + if (!process.env.RESEND_API_KEY) { + return NextResponse.json({ error: "Email service is not configured" }, { status: 500 }); + } + const jobData = await req.json(); // Validate required fields @@ -18,6 +21,7 @@ export const POST = withApiAuth( return NextResponse.json({ error: "Missing required job data" }, { status: 400 }); } + const resend = new Resend(process.env.RESEND_API_KEY); const { data, error } = await resend.emails.send({ from: `Spokes Job Board <${senderEmail}>`, to: [adminEmail], @@ -33,7 +37,7 @@ export const POST = withApiAuth( contactEmail: jobData.contactEmail || "Not provided", detailURL: jobData.detailURL, applyNowURL: jobData.applyNowURL, - adminURL: adminURL, + adminURL: getAdminUrl(), }), }); diff --git a/src/app/eventform/page.tsx b/src/app/eventform/page.tsx index 9d7016f2..e9e6cc9d 100644 --- a/src/app/eventform/page.tsx +++ b/src/app/eventform/page.tsx @@ -16,7 +16,6 @@ export default function EventFormPage() { const formData = new FormData(e.currentTarget); const data = { - organization: formData.get("organization") as string, eventName: formData.get("eventName") as string, date: formData.get("date") as string, time: formData.get("time") as string, @@ -62,20 +61,6 @@ export default function EventFormPage() {

Event Information

-
- - -
-
+ ); +} + +function EventBoardMessage({ title, message }: { title: string; message: string }) { + return ( +
+ + + +

{title}

+

{message}

+
); } diff --git a/src/components/events/EventCard.tsx b/src/components/events/EventCard.tsx index 305f0c30..8c14a363 100644 --- a/src/components/events/EventCard.tsx +++ b/src/components/events/EventCard.tsx @@ -1,6 +1,7 @@ import { formatDate } from "@/lib/utils"; import type { EventRecord } from "@/types/event"; import { EventModal } from "./EventModal"; +import { Button } from "@chakra-ui/react"; import { FaStar } from "react-icons/fa"; import { FiStar } from "react-icons/fi"; @@ -17,20 +18,20 @@ export default function EventCard({ event, isFavorite = false, onToggleFavorite const year = event.date.getUTCFullYear(); return ( -
- -
-
- {month} - {day} - {year} +
+
+
+
+ {month} + {day} + {year}
-
+
-
-

{event.eventName}

- {event.category} +
+

{event.eventName}

+

{event.organization}

{onToggleFavorite && (
- + + {event.description && ( +

{event.description}

+ )} + +
+ +
+
+ + {event.category} + + {event.locationType && ( + + {event.locationType.replace("-", " ")} + + )} +
+
+ +
+ + + + +
+
); } From 8406237687feeae3de58ac0c38d5bc2a2a9be0da Mon Sep 17 00:00:00 2001 From: kbeltr03 Date: Thu, 21 May 2026 15:29:07 -0700 Subject: [PATCH 06/38] removed category --- src/app/api/__test__/EventsApi.test.ts | 1 - src/app/eventform/page.tsx | 16 ---- src/app/events/[eventId]/page.tsx | 1 - src/app/events/page.tsx | 101 ++++++++++--------------- src/app/globals.css | 25 ------ src/components/events/EventCard.tsx | 45 +---------- src/components/events/EventModal.tsx | 10 +-- src/database/eventSchema.ts | 2 - src/lib/events.ts | 10 +-- src/types/event.ts | 1 - 10 files changed, 47 insertions(+), 165 deletions(-) diff --git a/src/app/api/__test__/EventsApi.test.ts b/src/app/api/__test__/EventsApi.test.ts index 71597a4c..e01afb66 100644 --- a/src/app/api/__test__/EventsApi.test.ts +++ b/src/app/api/__test__/EventsApi.test.ts @@ -50,7 +50,6 @@ const validEventPayload = { time: "6:00 PM", location: "Innovation Hub", locationType: "in-person", - category: "workshop", description: "A useful workshop.", }; diff --git a/src/app/eventform/page.tsx b/src/app/eventform/page.tsx index e9e6cc9d..40501368 100644 --- a/src/app/eventform/page.tsx +++ b/src/app/eventform/page.tsx @@ -22,7 +22,6 @@ export default function EventFormPage() { description: formData.get("description") as string, location: formData.get("location") as string, locationType: formData.get("locationType") as string, - category: formData.get("category") as string, }; try { @@ -145,21 +144,6 @@ export default function EventFormPage() {
-
- - -
-
-
-
Category
-
- {[ - { value: "volunteer", label: "Volunteer" }, - { value: "fundraiser", label: "Fundraiser" }, - { value: "workshop", label: "Workshop" }, - { value: "community", label: "Community" }, - ].map(({ value, label }) => ( - setCategoryFilter(categoryFilter === value ? "" : value)} - /> - ))} -
-
-
Location
@@ -272,11 +254,11 @@ export default function EventsPage() {
setActiveTab("favorites")} + onClick={() => setActiveTab("recent")} > - Favorites + Recently Viewed
@@ -289,20 +271,15 @@ export default function EventsPage() { ) : displayedEvents.length > 0 ? (
{displayedEvents.map((event) => ( - + ))}
) : events.length === 0 ? ( - ) : activeTab === "favorites" ? ( + ) : activeTab === "recent" ? ( ) : ( void; + onEventView?: (event: EventRecord) => void; }; -export default function EventCard({ event, isFavorite = false, onToggleFavorite }: EventCardProps) { - const favoriteLabel = isFavorite ? "Remove from favorites" : "Add to favorites"; +export default function EventCard({ event, onEventView }: EventCardProps) { const month = event.date.toLocaleString("en-US", { month: "short", timeZone: "UTC" }).toUpperCase(); const day = event.date.getUTCDate(); const year = event.date.getUTCFullYear(); @@ -33,22 +29,6 @@ export default function EventCard({ event, isFavorite = false, onToggleFavorite

{event.eventName}

{event.organization}

- {onToggleFavorite && ( - - )}
@@ -115,9 +95,6 @@ export default function EventCard({ event, isFavorite = false, onToggleFavorite
- - {event.category} - {event.locationType && ( {event.locationType.replace("-", " ")} @@ -126,26 +103,12 @@ export default function EventCard({ event, isFavorite = false, onToggleFavorite
-
- +
+ -
diff --git a/src/components/events/EventModal.tsx b/src/components/events/EventModal.tsx index e0f490a1..5da733b8 100644 --- a/src/components/events/EventModal.tsx +++ b/src/components/events/EventModal.tsx @@ -9,9 +9,10 @@ import type { EventRecord } from "@/types/event"; export interface EventModalProps { event: EventRecord; children: React.ReactNode; + onOpen?: (event: EventRecord) => void; } -export function EventModal({ event, children }: EventModalProps) { +export function EventModal({ event, children, onOpen }: EventModalProps) { const [open, setOpen] = React.useState(false); return ( @@ -20,6 +21,7 @@ export function EventModal({ event, children }: EventModalProps) { onClick={(e) => { e.preventDefault(); e.stopPropagation(); + onOpen?.(event); setOpen(true); }} > @@ -57,12 +59,6 @@ export function EventModal({ event, children }: EventModalProps) {
- {event.category && ( - - {event.category} - - )} -

{event.eventName}

By {event.organization}

diff --git a/src/database/eventSchema.ts b/src/database/eventSchema.ts index 162a9f4f..0055c25c 100644 --- a/src/database/eventSchema.ts +++ b/src/database/eventSchema.ts @@ -7,7 +7,6 @@ export interface IEvent { time: string; location: string; locationType: "remote" | "in-person"; - category: string; description: string; organization: string; eventImage?: string; @@ -24,7 +23,6 @@ const EventSchema = new Schema( time: { type: String, required: true, trim: true }, location: { type: String, required: true, trim: true }, locationType: { type: String, required: true, enum: ["remote", "in-person"], trim: true }, - category: { type: String, required: true, trim: true }, description: { type: String, required: true, trim: true }, organization: { type: String, required: true, trim: true }, eventImage: { type: String, required: false, trim: true }, diff --git a/src/lib/events.ts b/src/lib/events.ts index f0a69c35..d32926c8 100644 --- a/src/lib/events.ts +++ b/src/lib/events.ts @@ -1,12 +1,4 @@ -const requiredEventFields = [ - "eventName", - "date", - "time", - "location", - "locationType", - "category", - "description", -] as const; +const requiredEventFields = ["eventName", "date", "time", "location", "locationType", "description"] as const; const mutableEventFields = [...requiredEventFields, "eventImage", "organizationIcon"] as const; diff --git a/src/types/event.ts b/src/types/event.ts index 5eb1e923..e6917e49 100644 --- a/src/types/event.ts +++ b/src/types/event.ts @@ -6,7 +6,6 @@ export interface EventRecord { time: string; location: string; locationType: "remote" | "in-person"; - category: string; organization: string; description: string; eventImage?: string; From 3a0e52051c5fb06f41ab1ef26686bcb258350179 Mon Sep 17 00:00:00 2001 From: kbeltr03 Date: Thu, 21 May 2026 15:39:30 -0700 Subject: [PATCH 07/38] styles --- src/app/eventform/page.tsx | 181 ++++++++++++++-------------- src/components/Footer/Footer.tsx | 6 - src/components/JobCard/JobBadge.tsx | 6 + src/components/events/EventCard.tsx | 9 +- 4 files changed, 97 insertions(+), 105 deletions(-) diff --git a/src/app/eventform/page.tsx b/src/app/eventform/page.tsx index 40501368..59fa40f9 100644 --- a/src/app/eventform/page.tsx +++ b/src/app/eventform/page.tsx @@ -4,11 +4,26 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { createEvent } from "@/services/events"; import { useAuth } from "@clerk/nextjs"; +import { Box, Button, FormControl, FormLabel, Heading, Input, Stack, useRadioGroup, VStack } from "@chakra-ui/react"; +import RadioCard from "@/components/RadioCard"; export default function EventFormPage() { const router = useRouter(); const { isSignedIn } = useAuth(); const [serverError, setServerError] = useState(null); + const [locationType, setLocationType] = useState(""); + + const locationTypeColorMapping = { + Remote: "#F8B1B8", + "In-Person": "#C6D3FF", + }; + + const locationTypeOptions = ["Remote", "In-Person"]; + + const { getRootProps: getLocationTypeRootProps, getRadioProps: getLocationTypeRadioProps } = useRadioGroup({ + name: "locationType", + onChange: (value) => setLocationType(value), + }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -34,126 +49,108 @@ export default function EventFormPage() { if (!isSignedIn) { return ( -
-
-

Create New Event

-

Please sign in to create an event.

-
-
+ +
Create New Event
+

Please sign in to create an event.

+
); } - const inputClass = - "w-full rounded-sm bg-gray-100 px-3 py-2.5 text-sm text-gray-800 placeholder-gray-400 outline-none focus:bg-gray-200"; - const labelClass = "block text-sm font-bold text-black mb-1"; - const fieldClass = "mb-4"; - return ( -
-
-

Create New Event

- - {serverError && ( -
{serverError}
- )} - - -

Event Information

- -
- - +
Create New Event
+ + + Event Information + + + {serverError && ( +
{serverError}
+ )} + + + + + Event Title + -
- -
- -
- - -
-
- -
- - + + + Event Date + + + + + + + + Event Description + -
+ -
- - + Event Location + -
- -
- - -
+ + + + Location Type + + {locationTypeOptions.map((value) => { + const radio = getLocationTypeRadioProps({ value: value.toLowerCase() }); + return ( + + {value} + + ); + })} + +
- +
- -
-
+ + + ); } diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index dfd3ba9d..7f942fc2 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -2,7 +2,6 @@ import React from "react"; import Link from "next/link"; import Image from "next/image"; -import { usePathname } from "next/navigation"; import { GrInstagram } from "react-icons/gr"; import { ImFacebook2 } from "react-icons/im"; import { SiLinkedin } from "react-icons/si"; @@ -10,11 +9,6 @@ import { useUser } from "@clerk/nextjs"; const Footer: React.FC = () => { const { user } = useUser(); - const pathname = usePathname(); - - if (pathname === "/jobs") { - return null; - } return (