From 4a23c4fe81ffe5da297b8a8d1b4134a0991c83b7 Mon Sep 17 00:00:00 2001 From: stevenseb Date: Sat, 3 May 2025 17:12:07 -0700 Subject: [PATCH 01/10] Basic functionality for auth0 registration, redirect to /signup and signup complete flag working --- .gitignore | 4 + package-lock.json | 524 ++++++++++++++++++++++++++++-- package.json | 10 +- src/auth0/login.tsx | 10 + src/auth0/profile.tsx | 29 ++ src/auth0/signup.tsx | 18 + src/components/Header.css | 25 ++ src/components/Header.tsx | 10 +- src/components/PublicHeader.tsx | 31 ++ src/components/RouteGuards.tsx | 86 +++++ src/main.tsx | 19 +- src/pages/LoginSignupRedirect.tsx | 34 ++ src/pages/SignupPage.tsx | 209 ++++++++++++ src/utils/mockDB.ts | 30 ++ vite.config.ts | 6 + 15 files changed, 1015 insertions(+), 30 deletions(-) create mode 100644 src/auth0/login.tsx create mode 100644 src/auth0/profile.tsx create mode 100644 src/auth0/signup.tsx create mode 100644 src/components/PublicHeader.tsx create mode 100644 src/components/RouteGuards.tsx create mode 100644 src/pages/LoginSignupRedirect.tsx create mode 100644 src/pages/SignupPage.tsx create mode 100644 src/utils/mockDB.ts diff --git a/.gitignore b/.gitignore index 059561c..d508123 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ dist-ssr .amplify amplify_outputs* amplifyconfiguration* + +# dotenv files +.env +.env.* diff --git a/package-lock.json b/package-lock.json index 8603828..149b8e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,18 @@ "name": "amplify-vite-react-template", "version": "0.0.0", "dependencies": { + "@auth0/auth0-react": "^2.3.0", "@aws-amplify/ui-react": "^6.5.5", + "@types/react-bootstrap": "^0.32.37", + "@types/react-modal": "^3.16.3", + "@types/react-router-dom": "^5.3.3", "aws-amplify": "^6.6.6", + "bootstrap": "^5.3.5", "react": "^18.2.0", + "react-bootstrap": "^2.10.9", "react-dom": "^18.2.0", - "vitest": "^3.1.1" + "react-modal": "^3.16.3", + "react-router-dom": "^7.5.3" }, "devDependencies": { "@aws-amplify/backend": "^1.5.0", @@ -31,7 +38,8 @@ "eslint-plugin-react-refresh": "^0.4.6", "tsx": "^4.7.2", "typescript": "^5.4.5", - "vite": "^5.4.10" + "vite": "^5.4.10", + "vitest": "^3.1.1" } }, "node_modules/@ampproject/remapping": { @@ -320,6 +328,25 @@ "node": ">=6" } }, + "node_modules/@auth0/auth0-react": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.3.0.tgz", + "integrity": "sha512-YYTc/DWWigKC9fURufR/79h3+3DAnIzbfEzJLZ8Z4Q0BXE0azru3pKUbU+vYzS4lMAJkclwLuAbUnLjK81vCpA==", + "license": "MIT", + "dependencies": { + "@auth0/auth0-spa-js": "^2.1.3" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17 || ^18 || ^19", + "react-dom": "^16.11.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/@auth0/auth0-spa-js": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.3.tgz", + "integrity": "sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ==", + "license": "MIT" + }, "node_modules/@aws-amplify/analytics": { "version": "7.0.53", "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-7.0.53.tgz", @@ -20170,12 +20197,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.9.tgz", - "integrity": "sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -21738,7 +21763,8 @@ "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==" + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -22090,6 +22116,16 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@radix-ui/number": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.0.tgz", @@ -22478,6 +22514,75 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@react-aria/ssr": { + "version": "3.9.8", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz", + "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.4.tgz", + "integrity": "sha512-N4C7haUc3vn4LTwVUPlkJN8Ach/+yIMvRuTVIhjilNHqegY60SGLrzud6errOMNJwSnmYFnt1J0H/k8FE3A4KA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@popperjs/core": "^2.11.8", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.5.0", + "@types/warning": "^3.0.3", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.4", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/@restart/hooks": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.1.tgz", + "integrity": "sha512-EMoH04NHS1pbn07iLTjIjgttuqb7qu4+/EyhAx27MHpoENcB2ZdSsLTNxmKD+WEPnZigo62Qc8zjGnNxoSE/5Q==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", @@ -22485,6 +22590,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -22497,6 +22603,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -22509,6 +22616,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -22521,6 +22629,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -22533,6 +22642,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22545,6 +22655,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22557,6 +22668,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22569,6 +22681,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22581,6 +22694,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22593,6 +22707,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22605,6 +22720,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22617,6 +22733,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22629,6 +22746,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -22641,6 +22759,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -22653,6 +22772,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -22665,6 +22785,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -23682,6 +23803,15 @@ "node": ">=16.0.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@types/aws-lambda": { "version": "8.10.145", "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz", @@ -23783,7 +23913,14 @@ "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -23800,6 +23937,7 @@ "version": "20.17.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.0.tgz", "integrity": "sha512-a7zRo0f0eLo9K5X9Wp5cAqTUNGzuFLDG2R7C4HY2BhcMAsxgSPuRvAC1ZB6QkuUQXf0YZAgfOX2ZyrBa2n4nHQ==", + "dev": true, "optional": true, "peer": true, "dependencies": { @@ -23809,19 +23947,26 @@ "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-bootstrap": { + "version": "0.32.37", + "resolved": "https://registry.npmjs.org/@types/react-bootstrap/-/react-bootstrap-0.32.37.tgz", + "integrity": "sha512-CVHj++uxsj1pRnM3RQ/NAXcWj+JwJZ3MqQ28sS1OQUD1sI2gRlbeAjRT+ak2nuwL+CY+gtnIsMaIDq0RNfN0PA==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", @@ -23831,11 +23976,56 @@ "@types/react": "*" } }, + "node_modules/@types/react-modal": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.3.tgz", + "integrity": "sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", @@ -24059,6 +24249,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "3.1.1", @@ -24074,6 +24265,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "3.1.1", @@ -24100,6 +24292,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "dev": true, "license": "MIT", "dependencies": { "tinyrainbow": "^2.0.0" @@ -24112,6 +24305,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/utils": "3.1.1", @@ -24125,6 +24319,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "3.1.1", @@ -24139,6 +24334,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "dev": true, "license": "MIT", "dependencies": { "tinyspy": "^3.0.2" @@ -24151,6 +24347,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "3.1.1", @@ -24401,6 +24598,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -25013,6 +25211,25 @@ "node": ">=0.6" } }, + "node_modules/bootstrap": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.5.tgz", + "integrity": "sha512-ct1CHKtiobRimyGzmsSldEtM03E8fcEX4Tb3dGXz1V8faRwM50+vfHwTzOxB3IlKO7m+9vTH3s/3C6T2EAPeTA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -25139,6 +25356,7 @@ "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -25259,6 +25477,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, "license": "MIT", "dependencies": { "assertion-error": "^2.0.1", @@ -25351,6 +25570,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 16" @@ -25371,6 +25591,12 @@ "node": ">=8" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -25536,6 +25762,15 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/core-js": { "version": "3.41.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", @@ -25692,6 +25927,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -25717,6 +25953,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -25888,6 +26125,15 @@ "node": ">= 0.6.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", @@ -25946,6 +26192,16 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -26101,6 +26357,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -26451,6 +26708,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" @@ -26488,10 +26746,17 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==", + "license": "BSD-3-Clause" + }, "node_modules/expect-type": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" @@ -26782,6 +27047,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -28321,6 +28587,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true, "license": "MIT" }, "node_modules/lower-case": { @@ -28354,6 +28621,7 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -28485,7 +28753,8 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/mute-stream": { "version": "2.0.0", @@ -28567,6 +28836,7 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, "funding": [ { "type": "github", @@ -28694,7 +28964,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -29128,12 +29397,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, "license": "MIT" }, "node_modules/pathval": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14.16" @@ -29237,7 +29508,8 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -29281,6 +29553,7 @@ "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -29391,6 +29664,30 @@ "asap": "~2.0.3" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "license": "MIT", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/property-expr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", @@ -29568,6 +29865,37 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.10.9", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.9.tgz", + "integrity": "sha512-TJUCuHcxdgYpOqeWmRApM/Dy0+hVsxNRFvq2aRFQuxhNi/+ivOxC5OdWIeHS3agxvzJ4Ev4nDw2ZdBl9ymd/JQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.9.4", + "@types/prop-types": "^15.7.12", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -29595,6 +29923,34 @@ "react": "^16.8.0 || ^17 || ^18 || ^19" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-modal": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.3.tgz", + "integrity": "sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw==", + "license": "MIT", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -29649,6 +30005,45 @@ } } }, + "node_modules/react-router": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.3.tgz", + "integrity": "sha512-3iUDM4/fZCQ89SXlDa+Ph3MevBrozBAI655OAfWQlTm9nBR0IKlrmNwFow5lPHttbwvITZfkeeeZFP6zt3F7pw==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.3.tgz", + "integrity": "sha512-cK0jSaTyW4jV9SRKAItMIQfWZ/D6WEZafgHuuCb9g+SjhLolY78qc+De4w/Cz9ybjvLzShAmaIMEXt8iF1Cm+A==", + "license": "MIT", + "dependencies": { + "react-router": "7.5.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -29671,6 +30066,22 @@ } } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -29705,11 +30116,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -29908,6 +30314,7 @@ "version": "4.24.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, "dependencies": { "@types/estree": "1.0.6" }, @@ -30203,6 +30610,12 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -30352,6 +30765,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, "license": "ISC" }, "node_modules/signal-exit": { @@ -30404,6 +30818,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -30458,12 +30873,14 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, "license": "MIT" }, "node_modules/std-env": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.1.tgz", "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==", + "dev": true, "license": "MIT" }, "node_modules/stdin-discarder": { @@ -30757,18 +31174,21 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, "license": "MIT" }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" @@ -30778,6 +31198,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" @@ -30787,6 +31208,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" @@ -31333,6 +31755,12 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -31518,10 +31946,26 @@ "node": ">=0.10.0" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, "optional": true, "peer": true }, @@ -31705,6 +32149,7 @@ "version": "5.4.10", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "dev": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -31763,6 +32208,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", @@ -31788,6 +32234,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "aix" @@ -31803,6 +32250,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -31818,6 +32266,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -31833,6 +32282,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -31848,6 +32298,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -31863,6 +32314,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -31878,6 +32330,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -31893,6 +32346,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -31908,6 +32362,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31923,6 +32378,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31938,6 +32394,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31953,6 +32410,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31968,6 +32426,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31983,6 +32442,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31998,6 +32458,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -32013,6 +32474,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -32028,6 +32490,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -32043,6 +32506,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -32058,6 +32522,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -32073,6 +32538,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -32088,6 +32554,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -32103,6 +32570,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -32118,6 +32586,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -32130,6 +32599,7 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -32167,6 +32637,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -32180,6 +32651,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/expect": "3.1.1", @@ -32245,6 +32717,15 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -32403,6 +32884,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", diff --git a/package.json b/package.json index ced5678..7db86fe 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,18 @@ "test": "vitest run --passWithNoTests" }, "dependencies": { + "@auth0/auth0-react": "^2.3.0", "@aws-amplify/ui-react": "^6.5.5", + "@types/react-bootstrap": "^0.32.37", + "@types/react-modal": "^3.16.3", + "@types/react-router-dom": "^5.3.3", "aws-amplify": "^6.6.6", + "bootstrap": "^5.3.5", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-bootstrap": "^2.10.9", + "react-dom": "^18.2.0", + "react-modal": "^3.16.3", + "react-router-dom": "^7.5.3" }, "devDependencies": { "@aws-amplify/backend": "^1.5.0", diff --git a/src/auth0/login.tsx b/src/auth0/login.tsx new file mode 100644 index 0000000..3deaf02 --- /dev/null +++ b/src/auth0/login.tsx @@ -0,0 +1,10 @@ +import { useAuth0 } from "@auth0/auth0-react"; +import React from "react"; + +const LoginButton: React.FC = () => { + const { loginWithRedirect } = useAuth0(); + + return ; +}; + +export default LoginButton; diff --git a/src/auth0/profile.tsx b/src/auth0/profile.tsx new file mode 100644 index 0000000..52e24aa --- /dev/null +++ b/src/auth0/profile.tsx @@ -0,0 +1,29 @@ +import { useAuth0 } from "@auth0/auth0-react"; +import React from "react"; + +interface User { + picture?: string; + name?: string; + email?: string; +} + +const Profile: React.FC = () => { + const { user, isAuthenticated, isLoading } = useAuth0(); + + if (isLoading) { + return
Loading ...
; + } + + return ( + isAuthenticated && + user && ( +
+ {user.name} +

{user.name}

+

{user.email}

+
+ ) + ); +}; + +export default Profile; diff --git a/src/auth0/signup.tsx b/src/auth0/signup.tsx new file mode 100644 index 0000000..5b43286 --- /dev/null +++ b/src/auth0/signup.tsx @@ -0,0 +1,18 @@ +import { useAuth0 } from "@auth0/auth0-react"; + +const SignupButton = () => { + const { loginWithRedirect } = useAuth0(); + + const handleSignup = () => { + loginWithRedirect({ + authorizationParams: { + screen_hint: "signup", + }, + appState: { returnTo: "/signup" } + }); + }; + + return ; +}; + +export default SignupButton; diff --git a/src/components/Header.css b/src/components/Header.css index ae108f2..92ad887 100644 --- a/src/components/Header.css +++ b/src/components/Header.css @@ -71,3 +71,28 @@ } +/* Styles for PublicHeader component - testing and development purposes only */ +.public-landing { + min-height: 100vh; + display: flex; + flex-direction: column; + } + + .welcome-content { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + padding: 2rem; + background: white; + } + + .loading { + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + } + \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx index ff4faca..087c63a 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -2,6 +2,10 @@ import React from 'react'; import './Header.css'; import helpfulIcon from '../assets/helpful_icon.png'; import profileIcon from '../assets/profile_icon.png'; +import LoginButton from "../auth0/login"; +// import LogoutButton from "../auth0/logout"; +import SignupButton from "../auth0/signup"; +// import SignupPage from "../SignupPage"; const Header: React.FC = () => { return ( @@ -17,8 +21,8 @@ const Header: React.FC = () => { Contact
- - + + Profile Icon
@@ -26,4 +30,4 @@ const Header: React.FC = () => { ); }; -export default Header; \ No newline at end of file +export default Header; diff --git a/src/components/PublicHeader.tsx b/src/components/PublicHeader.tsx new file mode 100644 index 0000000..d586ac1 --- /dev/null +++ b/src/components/PublicHeader.tsx @@ -0,0 +1,31 @@ +// components/PublicHeader.tsx +import React from 'react'; +import './Header.css'; +import helpfulIcon from '../assets/helpful_icon.png'; +import LoginButton from "../auth0/login"; +import SignupButton from "../auth0/signup"; + +const PublicHeader: React.FC = () => { + return ( +
+ +
+ ); +}; + +export default PublicHeader; diff --git a/src/components/RouteGuards.tsx b/src/components/RouteGuards.tsx new file mode 100644 index 0000000..e326c53 --- /dev/null +++ b/src/components/RouteGuards.tsx @@ -0,0 +1,86 @@ +import React from "react"; +import { useAuth0, Auth0Provider } from "@auth0/auth0-react"; +import { Navigate, useLocation, useSearchParams } from "react-router-dom"; +import Dashboard from "../pages/Dashboard"; +import SignupPage from "../pages/SignupPage"; +import { mockDB } from '../utils/mockDB'; + +// Protects the dashboard route: only accessible if authenticated AND signup is complete +export function ProtectedDashboard() { + const { isAuthenticated, isLoading, user } = useAuth0(); + const location = useLocation(); + + if (isLoading) return null; + + // If not authenticated or no user ID, redirect to home + if (!isAuthenticated || !user?.sub) { + return ; + } + + // Check if signup is complete (from mockDB only) + const signupComplete = mockDB.getSignupStatus(user.sub); + + if (!signupComplete) { + // If signup not complete, redirect to /signup + return ; + } + + return ; +} + +// Protects the signup route: only accessible if authenticated and signup is NOT complete +export function ProtectedSignup() { + const { isAuthenticated, isLoading, user, loginWithRedirect } = useAuth0(); + const location = useLocation(); + const [searchParams] = useSearchParams(); + + // Show Auth0 errors if present in the URL + const auth0Error = searchParams.get("error"); + const auth0ErrorDescription = searchParams.get("error_description"); + if (auth0Error) { + return ( +
+

Authentication Error

+

{auth0Error}: {auth0ErrorDescription}

+
+ ); + } + + if (isLoading) return null; + + // If not authenticated, trigger Auth0 login and redirect back to /signup after login + if (!isAuthenticated) { + loginWithRedirect({ appState: { returnTo: "/signup" } }); + return null; + } + + // If signup is already complete, redirect to dashboard + if (user?.sub && mockDB.getSignupStatus(user.sub)) { + return ; + } + + return ; +} + +// Auth0Provider wrapper for your app +interface Auth0ProviderWithRedirectProps { + children: React.ReactNode; +} + +export function Auth0ProviderWithRedirect({ children }: Auth0ProviderWithRedirectProps) { + const domain = import.meta.env.VITE_AUTH0_DOMAIN; + const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID; + const redirectUri = `${window.location.origin}/`; + + return ( + + {children} + + ); +} diff --git a/src/main.tsx b/src/main.tsx index ee13d49..8cd8692 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,16 +1,25 @@ import React from "react"; import ReactDOM from "react-dom/client"; -// import App from "./App.tsx"; -import Dashboard from "./pages/Dashboard.tsx"; +import { BrowserRouter, Routes, Route } from 'react-router-dom'; import "./index.css"; import { Amplify } from "aws-amplify"; - +import 'bootstrap/dist/css/bootstrap.min.css'; import outputs from "../amplify_outputs.json"; +import { Auth0ProviderWithRedirect, ProtectedDashboard, ProtectedSignup } from "./components/RouteGuards"; +import LoginSignupRedirect from "./pages/LoginSignupRedirect"; Amplify.configure(outputs); ReactDOM.createRoot(document.getElementById("root")!).render( - + + + + } /> + } /> + } /> + + + -); \ No newline at end of file +); diff --git a/src/pages/LoginSignupRedirect.tsx b/src/pages/LoginSignupRedirect.tsx new file mode 100644 index 0000000..bf14c5d --- /dev/null +++ b/src/pages/LoginSignupRedirect.tsx @@ -0,0 +1,34 @@ +// pages/LoginSignupRedirect.tsx +import { useAuth0 } from "@auth0/auth0-react"; +import { Navigate } from "react-router-dom"; +import PublicHeader from "../components/PublicHeader"; +import { mockDB } from "../utils/mockDB"; + +export default function LoginSignupRedirect() { + const { isAuthenticated, isLoading, user } = useAuth0(); + + if (isLoading) return
Loading...
; + + // Handle authenticated users + if (isAuthenticated) { + const userId = user?.sub || ''; + const signupComplete = user?.user_metadata?.signupComplete || + mockDB.getSignupStatus(userId); + + return signupComplete + ? + : ; + } + + // Public landing page + return ( +
+ +
+

Welcome to Our Platform

+

Get started by logging in or signing up

+
+
+ ); +} + diff --git a/src/pages/SignupPage.tsx b/src/pages/SignupPage.tsx new file mode 100644 index 0000000..faf4ed4 --- /dev/null +++ b/src/pages/SignupPage.tsx @@ -0,0 +1,209 @@ +import React, { useState, ChangeEvent, FormEvent, useEffect } from "react"; +import { useAuth0 } from "@auth0/auth0-react"; +import { useNavigate } from "react-router-dom"; +import Modal from "react-bootstrap/Modal"; +import Button from "react-bootstrap/Button"; +import { mockDB } from "../utils/mockDB"; + +interface FormData { + name: string; + skills: string; + phone: string; + slackHandle: string; + languages: string; + timezone: string; +} + +const SignupPage: React.FC = () => { + const { user, isLoading } = useAuth0(); +// const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + + const [formData, setFormData] = useState({ + name: "", + skills: "", + phone: "", + slackHandle: "", + languages: "", + timezone: "", + }); + const [termsAccepted, setTermsAccepted] = useState(false); + const [modalIsOpen, setModalIsOpen] = useState(false); + const [hasScrolledToBottom, setHasScrolledToBottom] = useState(false); + const [hasReadTerms, setHasReadTerms] = useState(false); + const [submitting, setSubmitting] = useState(false); + + // Redirect if user already completed signup (using mockDB) + useEffect(() => { + if (!isLoading && user?.sub && mockDB.getSignupStatus(user.sub)) { + navigate("/dashboard", { replace: true }); + } + }, [user, isLoading, navigate]); + + const handleInputChange = ( + e: ChangeEvent + ) => { + const { name, value } = e.target; + setFormData((prevData) => ({ ...prevData, [name]: value })); + }; + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + if (!validateForm()) { + return; + } + setSubmitting(true); + try { + if (user?.sub) { + mockDB.setSignupStatus(user.sub, true); + mockDB.storeUserData(user.sub, formData); + } + navigate("/dashboard", { replace: true }); + } catch (error) { + setSubmitting(false); + console.error("Error saving user data:", error); + alert("There was an error completing your signup. Please try again."); + } + }; + + const validateForm = (): boolean => { + // Add form validation logic here + return true; + }; + + const handleScroll = (e: React.UIEvent) => { + const target = e.target as HTMLDivElement; + const { scrollTop, scrollHeight, clientHeight } = target; + if (scrollTop + clientHeight >= scrollHeight - 10) { + setHasScrolledToBottom(true); + } + }; + + return ( +
+

Complete Your Signup

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+

Sample summary of terms/agreement.

+ +
+
+ setTermsAccepted(!termsAccepted)} + disabled={!hasReadTerms} + /> + +
+ +
+ + setModalIsOpen(false)} + aria-labelledby="terms-and-conditions" + size="lg" + centered + scrollable + > + + Terms and Conditions + + + {/* Add terms content here */} +
+ {/* Placeholder content to enable scrolling */} +
+
+ + + +
+
+ ); +}; + +export default SignupPage; diff --git a/src/utils/mockDB.ts b/src/utils/mockDB.ts new file mode 100644 index 0000000..607340b --- /dev/null +++ b/src/utils/mockDB.ts @@ -0,0 +1,30 @@ +// src/utils/mockDB.ts +type UserData = { + name: string; + skills: string; + phone: string; + slackHandle: string; + languages: string; + timezone: string; + }; + + export const mockDB = { + getSignupStatus: (userId: string): boolean => { + return localStorage.getItem(`user_${userId}_signupComplete`) === 'true'; + }, + + setSignupStatus: (userId: string, status: boolean): void => { + localStorage.setItem(`user_${userId}_signupComplete`, status.toString()); + }, + + storeUserData: (userId: string, data: UserData): void => { + localStorage.setItem(`user_${userId}`, JSON.stringify(data)); + }, + + getUserData: (userId: string): UserData | null => { + const data = localStorage.getItem(`user_${userId}`); + return data ? JSON.parse(data) : null; + } + }; + + \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 5a33944..6ebbb92 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,13 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import dns from 'node:dns' +dns.setDefaultResultOrder('verbatim') // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], + server: { + host: 'localhost', + port: 5173, + }, }) From efcb35c43f027cbf85f41dd6ebd4e6b2f13bb049 Mon Sep 17 00:00:00 2001 From: stevenseb Date: Sun, 4 May 2025 23:07:39 -0700 Subject: [PATCH 02/10] Added to basic functionality with form fields and styling to match Figma design --- index.html | 4 +- package-lock.json | 339 +++++++++++++++++++++---- package.json | 3 +- public/helpful-logo.png | Bin 0 -> 16881 bytes public/vite.svg | 1 - src/assets/react.svg | 1 - src/auth0/signup.tsx | 2 +- src/components/BackNavigationFix.tsx | 16 ++ src/components/Card.tsx | 2 +- src/components/Header.tsx | 6 +- src/components/PublicHeader.tsx | 17 +- src/components/RouteGuards.tsx | 13 +- src/components/SignupForm.tsx | 216 ++++++++++++++++ src/components/TermsModal.tsx | 60 +++++ src/components/{ => styles}/Card.css | 0 src/components/{ => styles}/Header.css | 0 src/components/styles/PublicHeader.css | 132 ++++++++++ src/components/styles/selectStyles.ts | 45 ++++ src/data/languages.ts | 8 + src/data/skills.ts | 12 + src/data/timezones.ts | 43 ++++ src/index.css | 19 +- src/main.tsx | 2 + src/pages/Dashboard.tsx | 4 +- src/pages/LoginSignupRedirect.tsx | 8 +- src/pages/SignupPage.tsx | 257 ++++++------------- src/pages/{ => styles}/Dashboard.css | 0 src/pages/styles/SignupPage.css | 244 ++++++++++++++++++ src/types/formTypes.ts | 19 ++ src/utils/mockDB.ts | 51 ++-- 30 files changed, 1247 insertions(+), 277 deletions(-) create mode 100644 public/helpful-logo.png delete mode 100644 public/vite.svg delete mode 100644 src/assets/react.svg create mode 100644 src/components/BackNavigationFix.tsx create mode 100644 src/components/SignupForm.tsx create mode 100644 src/components/TermsModal.tsx rename src/components/{ => styles}/Card.css (100%) rename src/components/{ => styles}/Header.css (100%) create mode 100644 src/components/styles/PublicHeader.css create mode 100644 src/components/styles/selectStyles.ts create mode 100644 src/data/languages.ts create mode 100644 src/data/skills.ts create mode 100644 src/data/timezones.ts rename src/pages/{ => styles}/Dashboard.css (100%) create mode 100644 src/pages/styles/SignupPage.css create mode 100644 src/types/formTypes.ts diff --git a/index.html b/index.html index e4b78ea..4753fa9 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,9 @@ - + - Vite + React + TS + Dashboard - Helpful Engineering
diff --git a/package-lock.json b/package-lock.json index 149b8e6..c6c192f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,8 @@ "react-bootstrap": "^2.10.9", "react-dom": "^18.2.0", "react-modal": "^3.16.3", - "react-router-dom": "^7.5.3" + "react-router-dom": "^7.5.3", + "react-select": "^5.10.1" }, "devDependencies": { "@aws-amplify/backend": "^1.5.0", @@ -19327,7 +19328,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -19548,7 +19548,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -19561,7 +19560,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.9.tgz", "integrity": "sha512-OwS2CM5KocvQ/k7dFJa8i5bNGJP0hXWfVCfDkqRFP1IreH1JDC7wG6eCYCi0+McbfT8OR/kNqsI0UU0xP9H6PQ==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -19668,7 +19666,6 @@ "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==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -19677,7 +19674,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -19721,7 +19717,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, "dependencies": { "@babel/types": "^7.27.0" }, @@ -19736,7 +19731,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -20209,7 +20203,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.27.0", @@ -20223,7 +20216,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -20236,7 +20228,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.27.0", @@ -20254,7 +20245,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", - "dev": true, "dependencies": { "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0", @@ -20270,7 +20260,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -20283,7 +20272,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -20302,6 +20290,126 @@ "to-fast-properties": "^2.0.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -20817,6 +20925,12 @@ "react-dom": ">=16.8.0" } }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, "node_modules/@graphql-codegen/core": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-4.0.2.tgz", @@ -21732,7 +21846,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -21746,7 +21859,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -21755,7 +21867,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -21763,14 +21874,12 @@ "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==", - "dev": true + "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==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -23944,6 +24053,12 @@ "undici-types": "~6.19.2" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", @@ -25088,6 +25203,21 @@ "babel-runtime": "^6.22.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/babel-plugin-syntax-trailing-function-commas": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", @@ -25413,7 +25543,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -25782,6 +25911,22 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-fetch": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", @@ -25927,7 +26072,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -26270,6 +26414,15 @@ "node": ">=4" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.23.9", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", @@ -26467,7 +26620,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -26944,6 +27096,12 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -27061,7 +27219,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -27270,7 +27427,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "engines": { "node": ">=4" } @@ -27491,7 +27647,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -27518,6 +27673,15 @@ "hjson": "bin/hjson" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -27594,7 +27758,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -27711,6 +27874,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -27801,7 +27970,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -28335,6 +28503,12 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -28482,6 +28656,12 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -28656,6 +28836,12 @@ "is-buffer": "~1.1.6" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -28753,8 +28939,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mute-stream": { "version": "2.0.0", @@ -29246,7 +29431,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -29277,6 +29461,24 @@ "node": ">=14" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", @@ -29338,8 +29540,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-root": { "version": "0.1.1", @@ -29388,7 +29589,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -29508,8 +29708,7 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -30044,6 +30243,46 @@ "react-dom": ">=18" } }, + "node_modules/react-select": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.1.tgz", + "integrity": "sha512-roPEZUL4aRZDx6DcsD+ZNreVl+fM8VsKn0Wtex1v4IazH60ILp5xhdlp464IsEAlJdXeD+BhDAFsBVMfvLQueA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-select/node_modules/@floating-ui/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/react-select/node_modules/@floating-ui/dom": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.0", + "@floating-ui/utils": "^0.2.9" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -30176,7 +30415,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -30196,7 +30434,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -30809,7 +31046,6 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -31090,6 +31326,12 @@ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -31106,7 +31348,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -32083,11 +32324,12 @@ } }, "node_modules/use-isomorphic-layout-effect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz", + "integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -33020,6 +33262,15 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 7db86fe..34e39b7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "react-bootstrap": "^2.10.9", "react-dom": "^18.2.0", "react-modal": "^3.16.3", - "react-router-dom": "^7.5.3" + "react-router-dom": "^7.5.3", + "react-select": "^5.10.1" }, "devDependencies": { "@aws-amplify/backend": "^1.5.0", diff --git a/public/helpful-logo.png b/public/helpful-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a2dee6fffc2aafd2a1b8b07b45a013754932c8d3 GIT binary patch literal 16881 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 O1B0ilpUXO@geCx7rp2WI literal 0 HcmV?d00001 diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/auth0/signup.tsx b/src/auth0/signup.tsx index 5b43286..8e14980 100644 --- a/src/auth0/signup.tsx +++ b/src/auth0/signup.tsx @@ -12,7 +12,7 @@ const SignupButton = () => { }); }; - return ; + return ; }; export default SignupButton; diff --git a/src/components/BackNavigationFix.tsx b/src/components/BackNavigationFix.tsx new file mode 100644 index 0000000..2a25ba3 --- /dev/null +++ b/src/components/BackNavigationFix.tsx @@ -0,0 +1,16 @@ +// This function is used to fix the back navigation issue after signup where navigating back in history can show a stale or invalid Auth0 transaction page, leading to errors. This is a known issue when using auth0 and SPA. Need further testing as it doesn't seem to solve this problem. + +import { useEffect } from "react"; + +export default function BackNavigationFix() { + useEffect(() => { + const handlePageShow = (event: PageTransitionEvent) => { + if (event.persisted) { + window.location.reload(); + } + }; + window.addEventListener("pageshow", handlePageShow); + return () => window.removeEventListener("pageshow", handlePageShow); + }, []); + return null; +} diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 872e9d9..6d6b38b 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import './Card.css'; +import './styles/Card.css'; interface CardProps { title: string; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 087c63a..fd5cbca 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import './Header.css'; +import './styles/Header.css'; import helpfulIcon from '../assets/helpful_icon.png'; import profileIcon from '../assets/profile_icon.png'; +// will need to work on the transition between login and signup, dynamic change to header buttons based on logged in status this was a work around for now to have a blank page (using the component: PublicHeader.tsx) with a button that has auth0 functionality to simulate a signup coming in from the public WP site. For the sake of time, it was easier to wait and fix this later. (Eric) import LoginButton from "../auth0/login"; -// import LogoutButton from "../auth0/logout"; +// import LogoutButton from "../auth0/logout"; <--- logout button is implemented in auth0 folder, just need time to sort out the logged in view of header, add the button and test it. import SignupButton from "../auth0/signup"; -// import SignupPage from "../SignupPage"; const Header: React.FC = () => { return ( diff --git a/src/components/PublicHeader.tsx b/src/components/PublicHeader.tsx index d586ac1..c3e19af 100644 --- a/src/components/PublicHeader.tsx +++ b/src/components/PublicHeader.tsx @@ -1,16 +1,18 @@ -// components/PublicHeader.tsx -import React from 'react'; -import './Header.css'; -import helpfulIcon from '../assets/helpful_icon.png'; -import LoginButton from "../auth0/login"; +import "./styles/PublicHeader.css"; +import helpfulIcon from "../assets/helpful_icon.png"; import SignupButton from "../auth0/signup"; const PublicHeader: React.FC = () => { return (
diff --git a/src/components/RouteGuards.tsx b/src/components/RouteGuards.tsx index e326c53..9cd7893 100644 --- a/src/components/RouteGuards.tsx +++ b/src/components/RouteGuards.tsx @@ -1,9 +1,8 @@ -import React from "react"; import { useAuth0, Auth0Provider } from "@auth0/auth0-react"; import { Navigate, useLocation, useSearchParams } from "react-router-dom"; import Dashboard from "../pages/Dashboard"; import SignupPage from "../pages/SignupPage"; -import { mockDB } from '../utils/mockDB'; +import { getUserData } from '../utils/mockDB'; // Protects the dashboard route: only accessible if authenticated AND signup is complete export function ProtectedDashboard() { @@ -17,10 +16,10 @@ export function ProtectedDashboard() { return ; } - // Check if signup is complete (from mockDB only) - const signupComplete = mockDB.getSignupStatus(user.sub); + // Check if signup is complete using mockDB. Will need to hook up to our preferred DB for production + const userData = getUserData(user.sub); - if (!signupComplete) { + if (!userData) { // If signup not complete, redirect to /signup return ; } @@ -55,14 +54,14 @@ export function ProtectedSignup() { } // If signup is already complete, redirect to dashboard - if (user?.sub && mockDB.getSignupStatus(user.sub)) { + if (user?.sub && getUserData(user.sub)) { return ; } return ; } -// Auth0Provider wrapper for your app +// Auth0Provider wrapper interface Auth0ProviderWithRedirectProps { children: React.ReactNode; } diff --git a/src/components/SignupForm.tsx b/src/components/SignupForm.tsx new file mode 100644 index 0000000..3c5befe --- /dev/null +++ b/src/components/SignupForm.tsx @@ -0,0 +1,216 @@ +import { useState, ChangeEvent } from "react"; +import Select from "react-select"; +import Button from "react-bootstrap/Button"; +import TermsModal from "./TermsModal"; +import languages from "../data/languages"; +import skills from "../data/skills"; +import timezones from "../data/timezones"; +import { SignupFormProps } from "../types/formTypes"; +import { customSelectStyles } from "./styles/selectStyles"; + +// Setup skills options for react-select +const skillOptions = skills.map((skill) => ({ + value: skill, + label: skill, +})); + +const yearsOfExperienceOptions = Array.from({ length: 81 }, (_, i) => + i.toString() +); + +const SignupForm: React.FC = ({ + formData, + setFormData, + submitting, + onSubmit, + userName, +}) => { + const [termsAccepted, setTermsAccepted] = useState(false); + const [modalIsOpen, setModalIsOpen] = useState(false); + const [hasScrolledToBottom, setHasScrolledToBottom] = useState(false); + const [hasReadTerms, setHasReadTerms] = useState(false); + + const handleInputChange = ( + e: ChangeEvent + ) => { + const { name, value } = e.target as HTMLInputElement & HTMLSelectElement; + setFormData((prev) => ({ ...prev, [name]: value })); + }; + + const validateForm = (): boolean => { + return ( + termsAccepted && + !!formData.name && + !!formData.timezone && + !!formData.profession && + !!formData.yearsOfExperience && + !!formData.language && + formData.skills.length > 0 + ); + }; + + const handleScroll = (e: React.UIEvent) => { + const target = e.target as HTMLDivElement; + const { scrollTop, scrollHeight, clientHeight } = target; + if (scrollTop + clientHeight >= scrollHeight - 10) { + setHasScrolledToBottom(true); + } + }; + + return ( + <> +

+ Welcome {userName || "!"}! Tell us more about you +

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {/* Skills with react-select */} +
+ + setTermsAccepted(!termsAccepted)} + disabled={!hasReadTerms} + /> + +
+ + +
+ setModalIsOpen(false)} + hasScrolledToBottom={hasScrolledToBottom} + onScroll={handleScroll} + onAccept={() => setHasReadTerms(true)} +/> + + ); +}; + +export default SignupForm; diff --git a/src/components/TermsModal.tsx b/src/components/TermsModal.tsx new file mode 100644 index 0000000..edb0560 --- /dev/null +++ b/src/components/TermsModal.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import Modal from "react-bootstrap/Modal"; +import Button from "react-bootstrap/Button"; + +interface TermsModalProps { + isOpen: boolean; + onClose: () => void; + hasScrolledToBottom: boolean; + onScroll: (e: React.UIEvent) => void; + onAccept: () => void; +} + +const TermsModal: React.FC = ({ + isOpen, + onClose, + hasScrolledToBottom, + onScroll, + onAccept, +}) => { + return ( + + + + Terms and Conditions + + + +
+

+ By using this website, you agree to the following terms and conditions: +

+
+
+ + + +
+ ); +}; + +export default TermsModal; diff --git a/src/components/Card.css b/src/components/styles/Card.css similarity index 100% rename from src/components/Card.css rename to src/components/styles/Card.css diff --git a/src/components/Header.css b/src/components/styles/Header.css similarity index 100% rename from src/components/Header.css rename to src/components/styles/Header.css diff --git a/src/components/styles/PublicHeader.css b/src/components/styles/PublicHeader.css new file mode 100644 index 0000000..7463373 --- /dev/null +++ b/src/components/styles/PublicHeader.css @@ -0,0 +1,132 @@ +.header-container { + width: 100%; + background-color: #1F5378; + color: #fff; + position: sticky; + top: 0; + z-index: 1000; + box-shadow: 0 4px 12px rgba(0,0,0,0.08); + padding: 0; +} + +.header-nav { + display: flex; + align-items: center; + width: 100%; + max-width: 2000px !important; + margin: 0 auto; + height: 70px; + padding: 0 32px; +} + +.logo-section { + display: flex; + align-items: center; + margin-right: auto; + } + + +.helpful-icon { + width: 40px; + height: 40px; + margin-right: 10px; +} + +.logo-text { + font-size: 1.6rem; + font-weight: 700; + letter-spacing: 2px; + color: #fff; + text-decoration: none; +} + +.nav-links { + display: flex; + align-items: center; + gap: 2.2rem; + margin-left: auto; + margin-right: auto; +} + +.nav-links a { + color: #fff; + text-decoration: none; + font-size: 1.08rem; + font-weight: 600; + letter-spacing: 0.02em; + transition: color 0.15s; + padding: 2px 0; + border-bottom: 2px solid transparent; +} + +.nav-links a:hover, +.nav-links a:focus { + color: #E5A932; + border-bottom: 2px solid #E5A932; +} + +.header-actions { + display: flex; + align-items: center; + gap: 1.1rem; + flex: 0 0 auto; +} + +.donate-button { + background-color: #3B8223; + color: #fff; + border: none; + border-radius: 4px; + font-weight: 600; + font-size: 1.03rem; + padding: 0.6rem 1.4rem; + cursor: pointer; + transition: background 0.15s; + margin-right: 8px; +} + +.donate-button:hover, +.donate-button:focus { + background-color: #2E5C1A; +} + +.signinup-button { + background: #fff; + color: #000; + border: 2px solid #1F5378; + border-radius: 4px; + font-weight: 600; + font-size: 1.03rem; + padding: 0.6rem 1.4rem; + cursor: pointer; + transition: background 0.15s, color 0.15s; +} + +.signinup-button:hover, +.signinup-button:focus { + background: #E5A932; + color: #1F5378; + border-color: #E5A932; +} + +/* Responsive: Stack nav on mobile */ +@media (max-width: 900px) { + .header-nav { + flex-direction: column; + height: auto; + padding: 0 10px; + gap: 8px; + } + .logo-section, + .header-actions { + justify-content: center; + width: 100%; + margin-bottom: 8px; + } + .nav-links { + gap: 1.2rem; + margin: 8px 0; + width: 100%; + justify-content: center; + } +} diff --git a/src/components/styles/selectStyles.ts b/src/components/styles/selectStyles.ts new file mode 100644 index 0000000..a63a771 --- /dev/null +++ b/src/components/styles/selectStyles.ts @@ -0,0 +1,45 @@ +import { StylesConfig } from "react-select"; + + + +export const customSelectStyles: StylesConfig<{ value: string; label: string }, true> = { + control: (base, state) => ({ + ...base, + borderRadius: 28, + borderColor: state.isFocused ? "#1F5378" : "#000", + boxShadow: "none", + minHeight: 56, + fontSize: "1.15rem", + paddingLeft: 2, + "&:hover": { borderColor: "#1F5378" }, + }), + multiValue: (base) => ({ + ...base, + backgroundColor: "#1F5378", + color: "#fff", + borderRadius: 16, + padding: "2px 8px", + alignItems: "center", + }), + multiValueLabel: (base) => ({ + ...base, + color: "#fff", + fontWeight: 500, + padding: "0 6px", + }), + multiValueRemove: (base) => ({ + ...base, + color: "#CD3333", + ":hover": { + backgroundColor: "#fff", + color: "#a80000", + }, + fontSize: "1.1em", + marginLeft: 6, + marginRight: 2, + }), + menu: (base) => ({ + ...base, + zIndex: 9999, + }), +}; diff --git a/src/data/languages.ts b/src/data/languages.ts new file mode 100644 index 0000000..ae69e6c --- /dev/null +++ b/src/data/languages.ts @@ -0,0 +1,8 @@ +// A short sample list, may be expanded as needed +const languages = [ + "English", "Spanish", "Mandarin", "Hindi", "Arabic", "Portuguese", "Bengali", + "Russian", "Japanese", "Punjabi", "German", "French", "Italian", "Korean", + "Vietnamese", "Turkish", "Polish", "Dutch", "Swedish", "Greek", "Czech" + ]; + export default languages; + \ No newline at end of file diff --git a/src/data/skills.ts b/src/data/skills.ts new file mode 100644 index 0000000..bace4e6 --- /dev/null +++ b/src/data/skills.ts @@ -0,0 +1,12 @@ +// A short list of skills for testing + +const skills = [ + "Python", "Java", "C++", "C#", "JavaScript", "TypeScript", "Go", "Rust", + "MATLAB", "R", "SQL", "HTML/CSS", "React", "Angular", "Vue.js", "Node.js", + "Machine Learning", "Data Science", "Project Management", "CAD", "SolidWorks", + "Mechanical Engineering", "Electrical Engineering", "Civil Engineering", + "Biomedical Engineering", "Chemical Engineering", "Physics", "Mathematics", + "Statistics", "Lab Techniques", "Research", "Technical Writing", "Git", + "Linux", "Cloud Computing", "AWS", "Azure", "Docker", "Kubernetes" + ]; + export default skills; diff --git a/src/data/timezones.ts b/src/data/timezones.ts new file mode 100644 index 0000000..5c36b69 --- /dev/null +++ b/src/data/timezones.ts @@ -0,0 +1,43 @@ + +const timezones = [ + { value: "Pacific/Midway", label: "(UTC-11:00) Pacific/Midway" }, + { value: "Pacific/Honolulu", label: "(UTC-10:00) Pacific/Honolulu" }, + { value: "America/Anchorage", label: "(UTC-09:00) America/Anchorage" }, + { value: "America/Los_Angeles", label: "(UTC-08:00) America/Los_Angeles" }, + { value: "America/Denver", label: "(UTC-07:00) America/Denver" }, + { value: "America/Chicago", label: "(UTC-06:00) America/Chicago" }, + { value: "America/New_York", label: "(UTC-05:00) America/New_York" }, + { value: "America/Caracas", label: "(UTC-04:00) America/Caracas" }, + { value: "America/Halifax", label: "(UTC-04:00) America/Halifax" }, + { value: "America/St_Johns", label: "(UTC-03:30) America/St_Johns" }, + { value: "America/Argentina/Buenos_Aires", label: "(UTC-03:00) America/Argentina/Buenos_Aires" }, + { value: "America/Sao_Paulo", label: "(UTC-03:00) America/Sao_Paulo" }, + { value: "Atlantic/Azores", label: "(UTC-01:00) Atlantic/Azores" }, + { value: "Europe/London", label: "(UTC+00:00) Europe/London" }, + { value: "Europe/Berlin", label: "(UTC+01:00) Europe/Berlin" }, + { value: "Europe/Paris", label: "(UTC+01:00) Europe/Paris" }, + { value: "Europe/Athens", label: "(UTC+02:00) Europe/Athens" }, + { value: "Europe/Helsinki", label: "(UTC+02:00) Europe/Helsinki" }, + { value: "Europe/Moscow", label: "(UTC+03:00) Europe/Moscow" }, + { value: "Asia/Jerusalem", label: "(UTC+03:00) Asia/Jerusalem" }, + { value: "Asia/Baghdad", label: "(UTC+03:00) Asia/Baghdad" }, + { value: "Asia/Tehran", label: "(UTC+03:30) Asia/Tehran" }, + { value: "Asia/Dubai", label: "(UTC+04:00) Asia/Dubai" }, + { value: "Asia/Kabul", label: "(UTC+04:30) Asia/Kabul" }, + { value: "Asia/Karachi", label: "(UTC+05:00) Asia/Karachi" }, + { value: "Asia/Kolkata", label: "(UTC+05:30) Asia/Kolkata" }, + { value: "Asia/Kathmandu", label: "(UTC+05:45) Asia/Kathmandu" }, + { value: "Asia/Dhaka", label: "(UTC+06:00) Asia/Dhaka" }, + { value: "Asia/Jakarta", label: "(UTC+07:00) Asia/Jakarta" }, + { value: "Asia/Shanghai", label: "(UTC+08:00) Asia/Shanghai" }, + { value: "Asia/Tokyo", label: "(UTC+09:00) Asia/Tokyo" }, + { value: "Australia/Adelaide", label: "(UTC+09:30) Australia/Adelaide" }, + { value: "Australia/Sydney", label: "(UTC+10:00) Australia/Sydney" }, + { value: "Pacific/Noumea", label: "(UTC+11:00) Pacific/Noumea" }, + { value: "Pacific/Auckland", label: "(UTC+12:00) Pacific/Auckland" }, + { value: "Pacific/Chatham", label: "(UTC+12:45) Pacific/Chatham" }, + { value: "Pacific/Tongatapu", label: "(UTC+13:00) Pacific/Tongatapu" }, + { value: "Pacific/Kiritimati", label: "(UTC+14:00) Pacific/Kiritimati" } + ]; + export default timezones; + \ No newline at end of file diff --git a/src/index.css b/src/index.css index d22dafc..2c1ad15 100644 --- a/src/index.css +++ b/src/index.css @@ -1,6 +1,6 @@ body { margin: 0; - background: linear-gradient(180deg, rgb(117, 81, 194), rgb(255, 255, 255)); + background: #FFFFFF; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; height: 100vh; width: 100vw; @@ -12,6 +12,21 @@ body { height: 100%; } +:root { + --primary: #1F5378; + --primary-hover: #18425F; + --secondary: #3B8223; + --secondary-hover: #2E5C1A; + --link: #366ADA; + --input-bg: #F7F7FA; + --input-border: #000000; + --header-bg: #000000; + --header-text: #fff; + --success: #01B375; + --warning: #E5A932; + --error: #CD3333; + } + button { border-radius: 8px; border: 1px solid transparent; @@ -30,4 +45,4 @@ button:hover { button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; -} \ No newline at end of file +} diff --git a/src/main.tsx b/src/main.tsx index 8cd8692..b20af67 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,6 +7,7 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import outputs from "../amplify_outputs.json"; import { Auth0ProviderWithRedirect, ProtectedDashboard, ProtectedSignup } from "./components/RouteGuards"; import LoginSignupRedirect from "./pages/LoginSignupRedirect"; +import BackNavigationFix from "./components/BackNavigationFix"; Amplify.configure(outputs); @@ -14,6 +15,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render( + } /> } /> diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 528cd24..33b264d 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import './Dashboard.css'; +import './styles/Dashboard.css'; import Header from '../components/Header.tsx'; import MyProjects from '../views/MyProjects'; import Opportunities from '../views/Opportunities'; @@ -88,4 +88,4 @@ const Dashboard: React.FC = () => { ); }; -export default Dashboard; \ No newline at end of file +export default Dashboard; diff --git a/src/pages/LoginSignupRedirect.tsx b/src/pages/LoginSignupRedirect.tsx index bf14c5d..e0c6431 100644 --- a/src/pages/LoginSignupRedirect.tsx +++ b/src/pages/LoginSignupRedirect.tsx @@ -1,8 +1,7 @@ -// pages/LoginSignupRedirect.tsx import { useAuth0 } from "@auth0/auth0-react"; import { Navigate } from "react-router-dom"; import PublicHeader from "../components/PublicHeader"; -import { mockDB } from "../utils/mockDB"; +import { getUserData } from "../utils/mockDB"; // Updated import export default function LoginSignupRedirect() { const { isAuthenticated, isLoading, user } = useAuth0(); @@ -12,8 +11,8 @@ export default function LoginSignupRedirect() { // Handle authenticated users if (isAuthenticated) { const userId = user?.sub || ''; - const signupComplete = user?.user_metadata?.signupComplete || - mockDB.getSignupStatus(userId); + // Check both Auth0 metadata and mockDB for signup completion + const signupComplete = user?.user_metadata?.signupComplete || getUserData(userId); return signupComplete ? @@ -31,4 +30,3 @@ export default function LoginSignupRedirect() {
); } - diff --git a/src/pages/SignupPage.tsx b/src/pages/SignupPage.tsx index faf4ed4..efef6b2 100644 --- a/src/pages/SignupPage.tsx +++ b/src/pages/SignupPage.tsx @@ -1,207 +1,110 @@ -import React, { useState, ChangeEvent, FormEvent, useEffect } from "react"; +import { useState, FormEvent, useEffect } from "react"; import { useAuth0 } from "@auth0/auth0-react"; -import { useNavigate } from "react-router-dom"; -import Modal from "react-bootstrap/Modal"; -import Button from "react-bootstrap/Button"; -import { mockDB } from "../utils/mockDB"; - -interface FormData { - name: string; - skills: string; - phone: string; - slackHandle: string; - languages: string; - timezone: string; -} +import { useSearchParams, useNavigate } from "react-router-dom"; +import { saveUserData, getUserData } from "../utils/mockDB"; +import PublicHeader from "../components/PublicHeader"; +import SignupForm from "../components/SignupForm"; +import "./styles/SignupPage.css"; +import { FormData } from "../types/formTypes"; const SignupPage: React.FC = () => { - const { user, isLoading } = useAuth0(); -// const [searchParams] = useSearchParams(); + const { user, getAccessTokenSilently, getAccessTokenWithPopup, isLoading } = + useAuth0(); + const [searchParams] = useSearchParams(); const navigate = useNavigate(); + const stateParam = searchParams.get("state"); const [formData, setFormData] = useState({ name: "", - skills: "", - phone: "", - slackHandle: "", - languages: "", timezone: "", + profession: "", + yearsOfExperience: "", + language: "", + skills: [], }); - const [termsAccepted, setTermsAccepted] = useState(false); - const [modalIsOpen, setModalIsOpen] = useState(false); - const [hasScrolledToBottom, setHasScrolledToBottom] = useState(false); - const [hasReadTerms, setHasReadTerms] = useState(false); const [submitting, setSubmitting] = useState(false); - // Redirect if user already completed signup (using mockDB) useEffect(() => { - if (!isLoading && user?.sub && mockDB.getSignupStatus(user.sub)) { - navigate("/dashboard", { replace: true }); + if (!isLoading && user?.sub) { + // Check mockDB for existing signup + const existingData = getUserData(user.sub); + if (existingData) { + navigate("/dashboard", { replace: true }); + } } }, [user, isLoading, navigate]); - const handleInputChange = ( - e: ChangeEvent - ) => { - const { name, value } = e.target; - setFormData((prevData) => ({ ...prevData, [name]: value })); - }; - - const handleSubmit = (e: FormEvent) => { + const handleSubmit = async (e: FormEvent) => { e.preventDefault(); - if (!validateForm()) { - return; - } setSubmitting(true); try { + let token: string | undefined; + const tokenOptions = { + authorizationParams: { + // Eventually may need to include audience setting for 3rd party API registration and JWT issuance but may be able to bypass auth0 consent popup with settings in auth0 dashboard + // audience: `https://${import.meta.env.VITE_AUTH0_AUDIENCE}`, + scope: "update:current_user_metadata", + }, + }; + + try { + token = await getAccessTokenSilently(tokenOptions); + } catch (silentError) { + token = await getAccessTokenWithPopup(tokenOptions); + } + + if (!token) { + throw new Error("Failed to acquire access token"); + } + + await fetch( + `https://${import.meta.env.VITE_AUTH0_DOMAIN}/api/v2/users/${user?.sub}`, + { + method: "PATCH", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + user_metadata: { + ...formData, + skills: formData.skills.map((s) => s.value), + signupComplete: true, + }, + }), + } + ); + + // Save to mockDB if (user?.sub) { - mockDB.setSignupStatus(user.sub, true); - mockDB.storeUserData(user.sub, formData); + saveUserData(user.sub, { + ...formData, + skills: formData.skills.map((s) => s.value), + }); + } + + if (stateParam) { + window.location.href = `https://${import.meta.env.VITE_AUTH0_DOMAIN}/continue?state=${stateParam}`; + } else { + navigate("/dashboard", { replace: true }); } - navigate("/dashboard", { replace: true }); } catch (error) { setSubmitting(false); - console.error("Error saving user data:", error); + console.error("Error updating user data:", error); alert("There was an error completing your signup. Please try again."); } }; - const validateForm = (): boolean => { - // Add form validation logic here - return true; - }; - - const handleScroll = (e: React.UIEvent) => { - const target = e.target as HTMLDivElement; - const { scrollTop, scrollHeight, clientHeight } = target; - if (scrollTop + clientHeight >= scrollHeight - 10) { - setHasScrolledToBottom(true); - } - }; - return ( -
-

Complete Your Signup

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-

Sample summary of terms/agreement.

- -
-
- setTermsAccepted(!termsAccepted)} - disabled={!hasReadTerms} - /> - -
- -
- - setModalIsOpen(false)} - aria-labelledby="terms-and-conditions" - size="lg" - centered - scrollable - > - - Terms and Conditions - - - {/* Add terms content here */} -
- {/* Placeholder content to enable scrolling */} -
-
- - - -
+
+ +
); }; diff --git a/src/pages/Dashboard.css b/src/pages/styles/Dashboard.css similarity index 100% rename from src/pages/Dashboard.css rename to src/pages/styles/Dashboard.css diff --git a/src/pages/styles/SignupPage.css b/src/pages/styles/SignupPage.css new file mode 100644 index 0000000..7c398ca --- /dev/null +++ b/src/pages/styles/SignupPage.css @@ -0,0 +1,244 @@ +.signup-page { + background: #fff; + min-height: 100vh; + padding-bottom: 48px; + } + + .signup-page .signup-form-container { + background: transparent; + max-width: 480px; + margin: 48px auto 0 auto; + padding: 0; + border-radius: 0; + box-shadow: none; + } + + .signup-page .signup-title { + font-size: 2.4rem; + font-weight: 700; + color: #000; + text-align: center; + margin-bottom: 40px; + margin-top: 24px; + letter-spacing: -1px; + } + + .signup-page .signup-title .highlight { + color: #3B8223; + } + + /* Form fields */ + .signup-page .signup-form { + display: flex; + flex-direction: column; + gap: 28px; + } + + .signup-page .signup-form .form-group { + display: flex; + flex-direction: column; + gap: 4px; + width: 100%; + } + + .signup-page .signup-form label { + font-size: 1.07rem; + font-weight: 500; + color: #222; + margin-bottom: 0; + margin-left: 8px; + margin-top: 0; + letter-spacing: 0.01em; + } + + /* Input and select styling */ + .signup-page .signup-form .form-control { + width: 100%; + border-radius: 28px; + border: 2px solid #000; + background: #fff; + font-size: 1.15rem; + padding: 16px 24px; + margin-top: 4px; + margin-bottom: 0; + box-sizing: border-box; + transition: border-color 0.2s; + appearance: none; + outline: none; + } + + .signup-page .signup-form .form-control:focus { + border-color: #1F5378; + background: #f7f7fa; + } + + .signup-page .signup-form .form-control::placeholder { + color: #9b9bae; + font-size: 1.08rem; + opacity: 1; + } + + /* Down arrow for select */ + .signup-page .signup-form select.form-control { + background-image: url("data:image/svg+xml,%3Csvg width='16' height='10' viewBox='0 0 16 10' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L8 8L15 1' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 18px center; + background-size: 18px 12px; + padding-right: 44px; + cursor: pointer; + } + + /* For multi-select, remove arrow */ + .signup-page .signup-form select.form-control[multiple] { + background-image: none; + height: 56px; + min-height: 56px; + padding-right: 24px; + } + + .signup-page .skills-select-container { + display: flex; + flex-direction: column; + gap: 10px; + } + + .signup-page .skills-select { + min-height: 90px; + max-height: 220px; + } + + .signup-page .selected-skills { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 6px; + } + + .signup-page .skill-chip { + display: flex; + align-items: center; + background: #1F5378; + color: #fff; + border-radius: 16px; + padding: 5px 12px 5px 14px; + font-size: 1rem; + font-weight: 500; + margin-bottom: 4px; + } + + .signup-page .remove-skill { + background: transparent; + border: none; + color: #CD3333; + font-size: 1.1em; + margin-left: 10px; + cursor: pointer; + padding: 0 2px; + display: flex; + align-items: center; + } + + .signup-page .remove-skill:hover, + .signup-page .remove-skill:focus { + color: #a80000; + } + + /* Placeholder style for select */ + .signup-page .signup-form select.form-control:invalid { + color: #9b9bae; + } + + /* Button styling */ + .signup-page .signup-form .submit-btn { + width: 100%; + max-width: 340px; + display: block; + margin: 32px auto 0 auto; + background: #1F5378; + border: none; + border-radius: 14px; + color: #fff; + font-weight: 600; + font-size: 1.3rem; + padding: 18px 0; + box-shadow: none; + transition: background 0.2s; + letter-spacing: 0.01em; + } + + .signup-page .signup-form .submit-btn:disabled { + background: #b5c7d6; + color: #fff; + } + + .signup-page .signup-form .submit-btn:hover, + .signup-page .signup-form .submit-btn:focus { + background: #18425F; + } + + /* Terms and checkbox */ + .signup-page .terms-group { + display: flex; + align-items: center; + gap: 10px; + margin-top: 18px; + } + + .signup-page .terms-link { + color: #366ADA; + font-weight: 500; + padding-left: 0; + text-decoration: underline; + } + + .signup-page .checkbox-group { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; + margin-top: 10px; + } + + .signup-page .form-text { + color: #666; + font-size: 0.95rem; + margin-top: 6px; + margin-left: 8px; + } + + /* Modal Styles */ + .signup-page .modal-content { + border-radius: 12px; + } + + .signup-page .modal-header { + border-bottom: 1px solid #e5e5e5; + } + + .signup-page .modal-footer { + border-top: 1px solid #e5e5e5; + } + + /* Responsive */ + @media (max-width: 600px) { + .signup-page .signup-form-container { + padding: 0 8px; + } + + .signup-page .signup-title { + font-size: 1.3rem; + margin-top: 16px; + margin-bottom: 24px; + } + + .signup-page .signup-form .submit-btn { + font-size: 1.1rem; + padding: 14px 0; + } + + .signup-page .signup-form .form-control { + font-size: 1rem; + padding: 12px 16px; + } + } + \ No newline at end of file diff --git a/src/types/formTypes.ts b/src/types/formTypes.ts new file mode 100644 index 0000000..8ffe0b1 --- /dev/null +++ b/src/types/formTypes.ts @@ -0,0 +1,19 @@ +// Signup Form types and props +import { FormEvent } from "react"; + +export interface FormData { + name: string; + timezone: string; + profession: string; + yearsOfExperience: string; + language: string; + skills: { value: string; label: string }[]; +} + +export interface SignupFormProps { + formData: FormData; + setFormData: React.Dispatch>; + submitting: boolean; + onSubmit: (e: FormEvent) => Promise; + userName?: string; +} diff --git a/src/utils/mockDB.ts b/src/utils/mockDB.ts index 607340b..c6d3b8b 100644 --- a/src/utils/mockDB.ts +++ b/src/utils/mockDB.ts @@ -1,30 +1,37 @@ -// src/utils/mockDB.ts -type UserData = { +// This file is used to mock a database using localStorage. +// It provides functions to save and retrieve user data. +export type UserData = { name: string; - skills: string; - phone: string; - slackHandle: string; - languages: string; timezone: string; + profession: string; + yearsOfExperience: string; + language: string; + skills: string[]; }; - export const mockDB = { - getSignupStatus: (userId: string): boolean => { - return localStorage.getItem(`user_${userId}_signupComplete`) === 'true'; - }, - - setSignupStatus: (userId: string, status: boolean): void => { - localStorage.setItem(`user_${userId}_signupComplete`, status.toString()); - }, + const STORAGE_KEY = "mockDB"; - storeUserData: (userId: string, data: UserData): void => { - localStorage.setItem(`user_${userId}`, JSON.stringify(data)); - }, + // Helper to get the full DB object from localStorage + function getDB(): { [userId: string]: UserData } { + const raw = localStorage.getItem(STORAGE_KEY); + return raw ? JSON.parse(raw) : {}; + } - getUserData: (userId: string): UserData | null => { - const data = localStorage.getItem(`user_${userId}`); - return data ? JSON.parse(data) : null; - } - }; + // Save the full DB object back to localStorage + function setDB(db: { [userId: string]: UserData }) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(db)); + } + + // Save or update a user's data + export function saveUserData(userId: string, data: UserData) { + const db = getDB(); + db[userId] = data; + setDB(db); + } + // Retrieve a user's data + export function getUserData(userId: string): UserData | undefined { + const db = getDB(); + return db[userId]; + } \ No newline at end of file From bc1cfe81106fb7184b9634a72d87eea6dc55c132 Mon Sep 17 00:00:00 2001 From: stevenseb Date: Thu, 29 May 2025 08:42:51 -0700 Subject: [PATCH 03/10] styling changes to better match design --- src/components/Header.tsx | 6 +- src/components/PublicHeader.tsx | 3 +- src/components/SignupForm.tsx | 45 +++++++++----- .../{PublicHeader.css => publicHeader.css} | 14 ++--- src/components/styles/selectStyles.ts | 24 ++++++-- src/components/styles/signupForm.css | 48 +++++++++++++++ src/index.css | 14 +++-- src/pages/SignupPage.tsx | 3 +- src/pages/styles/SignupPage.css | 58 +++---------------- src/types/formTypes.ts | 2 +- src/utils/mockDB.ts | 2 +- vite.config.ts | 2 +- 12 files changed, 128 insertions(+), 93 deletions(-) rename src/components/styles/{PublicHeader.css => publicHeader.css} (91%) create mode 100644 src/components/styles/signupForm.css diff --git a/src/components/Header.tsx b/src/components/Header.tsx index fd5cbca..9252d50 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,9 +1,9 @@ -import React from 'react'; +import '../index.css'; import './styles/Header.css'; import helpfulIcon from '../assets/helpful_icon.png'; import profileIcon from '../assets/profile_icon.png'; // will need to work on the transition between login and signup, dynamic change to header buttons based on logged in status this was a work around for now to have a blank page (using the component: PublicHeader.tsx) with a button that has auth0 functionality to simulate a signup coming in from the public WP site. For the sake of time, it was easier to wait and fix this later. (Eric) -import LoginButton from "../auth0/login"; +// import LoginButton from "../auth0/login"; // import LogoutButton from "../auth0/logout"; <--- logout button is implemented in auth0 folder, just need time to sort out the logged in view of header, add the button and test it. import SignupButton from "../auth0/signup"; @@ -21,7 +21,7 @@ const Header: React.FC = () => { Contact
- + {/* */} Profile Icon
diff --git a/src/components/PublicHeader.tsx b/src/components/PublicHeader.tsx index c3e19af..f2fdb77 100644 --- a/src/components/PublicHeader.tsx +++ b/src/components/PublicHeader.tsx @@ -1,4 +1,5 @@ -import "./styles/PublicHeader.css"; +import '../index.css'; +import "./styles/publicHeader.css"; import helpfulIcon from "../assets/helpful_icon.png"; import SignupButton from "../auth0/signup"; diff --git a/src/components/SignupForm.tsx b/src/components/SignupForm.tsx index 3c5befe..eed229e 100644 --- a/src/components/SignupForm.tsx +++ b/src/components/SignupForm.tsx @@ -7,6 +7,7 @@ import skills from "../data/skills"; import timezones from "../data/timezones"; import { SignupFormProps } from "../types/formTypes"; import { customSelectStyles } from "./styles/selectStyles"; +import "./styles/signupForm.css"; // Setup skills options for react-select const skillOptions = skills.map((skill) => ({ @@ -14,6 +15,12 @@ const skillOptions = skills.map((skill) => ({ label: skill, })); +// Setup language options for react-select +const languageOptions = languages.map((language) => ({ + value: language, + label: language, +})); + const yearsOfExperienceOptions = Array.from({ length: 81 }, (_, i) => i.toString() ); @@ -44,7 +51,7 @@ const SignupForm: React.FC = ({ !!formData.timezone && !!formData.profession && !!formData.yearsOfExperience && - !!formData.language && + formData.languages.length > 0 && formData.skills.length > 0 ); }; @@ -127,23 +134,29 @@ const SignupForm: React.FC = ({ ))} + {/* Languages with react-select */}
- - + setFormData((prev) => ({ + ...prev, + languages: selected as { value: string; label: string }[], + })) + } + isMulti + placeholder="Select your languages..." + styles={customSelectStyles} + closeMenuOnSelect={false} required - > - - {languages.map((lang) => ( - - ))} - + /> + + Start typing to filter and select multiple languages. +
{/* Skills with react-select */}
diff --git a/src/components/styles/PublicHeader.css b/src/components/styles/publicHeader.css similarity index 91% rename from src/components/styles/PublicHeader.css rename to src/components/styles/publicHeader.css index 7463373..89512a6 100644 --- a/src/components/styles/PublicHeader.css +++ b/src/components/styles/publicHeader.css @@ -1,7 +1,7 @@ .header-container { width: 100%; - background-color: #1F5378; - color: #fff; + background-color: var(--header-bg); + color: var(--header-text); position: sticky; top: 0; z-index: 1000; @@ -13,7 +13,7 @@ display: flex; align-items: center; width: 100%; - max-width: 2000px !important; + max-width: 1800px; margin: 0 auto; height: 70px; padding: 0 32px; @@ -36,7 +36,7 @@ font-size: 1.6rem; font-weight: 700; letter-spacing: 2px; - color: #fff; + color: var(--header-text); text-decoration: none; } @@ -49,7 +49,7 @@ } .nav-links a { - color: #fff; + color: var(--header-text); text-decoration: none; font-size: 1.08rem; font-weight: 600; @@ -110,7 +110,7 @@ } /* Responsive: Stack nav on mobile */ -@media (max-width: 900px) { +/* @media (max-width: 900px) { .header-nav { flex-direction: column; height: auto; @@ -129,4 +129,4 @@ width: 100%; justify-content: center; } -} +} */ diff --git a/src/components/styles/selectStyles.ts b/src/components/styles/selectStyles.ts index a63a771..f80292d 100644 --- a/src/components/styles/selectStyles.ts +++ b/src/components/styles/selectStyles.ts @@ -5,25 +5,25 @@ import { StylesConfig } from "react-select"; export const customSelectStyles: StylesConfig<{ value: string; label: string }, true> = { control: (base, state) => ({ ...base, - borderRadius: 28, + borderRadius: 10, borderColor: state.isFocused ? "#1F5378" : "#000", boxShadow: "none", minHeight: 56, - fontSize: "1.15rem", + fontSize: "1rem", paddingLeft: 2, "&:hover": { borderColor: "#1F5378" }, }), multiValue: (base) => ({ ...base, - backgroundColor: "#1F5378", + backgroundColor: "#F7F7FA", color: "#fff", - borderRadius: 16, + borderRadius: 10, padding: "2px 8px", alignItems: "center", }), multiValueLabel: (base) => ({ ...base, - color: "#fff", + color: "#000", fontWeight: 500, padding: "0 6px", }), @@ -31,7 +31,7 @@ export const customSelectStyles: StylesConfig<{ value: string; label: string }, ...base, color: "#CD3333", ":hover": { - backgroundColor: "#fff", + backgroundColor: "darkgray", color: "#a80000", }, fontSize: "1.1em", @@ -42,4 +42,16 @@ export const customSelectStyles: StylesConfig<{ value: string; label: string }, ...base, zIndex: 9999, }), + + clearIndicator: (base) => ({ + ...base, + cursor: "pointer", + color: "#CD3333", + padding: "8px", + ":hover": { + color: "#000", + backgroundColor: "#f0f0f0", + }, +}), + }; diff --git a/src/components/styles/signupForm.css b/src/components/styles/signupForm.css new file mode 100644 index 0000000..dcfdeeb --- /dev/null +++ b/src/components/styles/signupForm.css @@ -0,0 +1,48 @@ +/* Form container - 75% width and responsive */ +.signup-form-container { + width: 75%; + max-width: 1200px; /* Prevents it from getting too wide on large screens */ + margin: 0 auto; /* Centers the container */ + padding: 20px; + box-sizing: border-box; +} + +/* Form field background color and padding */ +.form-control { + background-color: #F7F7FA; + padding-bottom: 12px; + /* border: 1px solid #ddd; */ + /* border-radius: 4px; */ +} + +/* Add bottom padding to each form group */ +.form-group { + padding-bottom: 12px; + margin-bottom: 1rem; /* Maintains spacing between form groups */ +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .signup-form-container { + width: 90%; /* Wider on mobile devices */ + padding: 15px; + } +} + +@media (max-width: 480px) { + .signup-form-container { + width: 95%; /* Even wider on small mobile devices */ + padding: 10px; + } +} + +/* Optional: Style the form itself for better layout */ +.signup-form { + width: 100%; +} + +/* Optional: Ensure form fields take full width within container */ +.signup-form-fields .form-control { + width: 100%; + box-sizing: border-box; +} diff --git a/src/index.css b/src/index.css index 2c1ad15..9ada673 100644 --- a/src/index.css +++ b/src/index.css @@ -13,15 +13,17 @@ body { } :root { - --primary: #1F5378; - --primary-hover: #18425F; - --secondary: #3B8223; - --secondary-hover: #2E5C1A; + --primary-btn: #1F5378; + --primary-btn-label: #FFFFFF; + --primary-btn-hover: #18425F; + --secondary-btn: #3B8223; + --secondary-btn-label: #FFFFFF; + --secondary-btn-hover: #2E5C1A; --link: #366ADA; --input-bg: #F7F7FA; --input-border: #000000; - --header-bg: #000000; - --header-text: #fff; + --header-bg: #EEF1F8; + --header-text: #000000; --success: #01B375; --warning: #E5A932; --error: #CD3333; diff --git a/src/pages/SignupPage.tsx b/src/pages/SignupPage.tsx index efef6b2..60d65d7 100644 --- a/src/pages/SignupPage.tsx +++ b/src/pages/SignupPage.tsx @@ -19,7 +19,7 @@ const SignupPage: React.FC = () => { timezone: "", profession: "", yearsOfExperience: "", - language: "", + languages: [], skills: [], }); const [submitting, setSubmitting] = useState(false); @@ -79,6 +79,7 @@ const SignupPage: React.FC = () => { if (user?.sub) { saveUserData(user.sub, { ...formData, + languages: formData.languages.map((s) => s.value), skills: formData.skills.map((s) => s.value), }); } diff --git a/src/pages/styles/SignupPage.css b/src/pages/styles/SignupPage.css index 7c398ca..d4e0b17 100644 --- a/src/pages/styles/SignupPage.css +++ b/src/pages/styles/SignupPage.css @@ -6,9 +6,9 @@ .signup-page .signup-form-container { background: transparent; - max-width: 480px; + max-width: 1080px; margin: 48px auto 0 auto; - padding: 0; + padding: 0 0 30px 0; border-radius: 0; box-shadow: none; } @@ -18,9 +18,9 @@ font-weight: 700; color: #000; text-align: center; - margin-bottom: 40px; - margin-top: 24px; + margin: 40px auto 24px auto; letter-spacing: -1px; + max-width: 95%; } .signup-page .signup-title .highlight { @@ -54,7 +54,7 @@ /* Input and select styling */ .signup-page .signup-form .form-control { width: 100%; - border-radius: 28px; + border-radius: 10px; border: 2px solid #000; background: #fff; font-size: 1.15rem; @@ -68,8 +68,8 @@ } .signup-page .signup-form .form-control:focus { - border-color: #1F5378; - background: #f7f7fa; + border-color: #366ADA; + background: #F7F7FA; } .signup-page .signup-form .form-control::placeholder { @@ -102,47 +102,6 @@ gap: 10px; } - .signup-page .skills-select { - min-height: 90px; - max-height: 220px; - } - - .signup-page .selected-skills { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-top: 6px; - } - - .signup-page .skill-chip { - display: flex; - align-items: center; - background: #1F5378; - color: #fff; - border-radius: 16px; - padding: 5px 12px 5px 14px; - font-size: 1rem; - font-weight: 500; - margin-bottom: 4px; - } - - .signup-page .remove-skill { - background: transparent; - border: none; - color: #CD3333; - font-size: 1.1em; - margin-left: 10px; - cursor: pointer; - padding: 0 2px; - display: flex; - align-items: center; - } - - .signup-page .remove-skill:hover, - .signup-page .remove-skill:focus { - color: #a80000; - } - /* Placeholder style for select */ .signup-page .signup-form select.form-control:invalid { color: #9b9bae; @@ -154,7 +113,7 @@ max-width: 340px; display: block; margin: 32px auto 0 auto; - background: #1F5378; + background: #3B8223; border: none; border-radius: 14px; color: #fff; @@ -241,4 +200,3 @@ padding: 12px 16px; } } - \ No newline at end of file diff --git a/src/types/formTypes.ts b/src/types/formTypes.ts index 8ffe0b1..cbbdcdb 100644 --- a/src/types/formTypes.ts +++ b/src/types/formTypes.ts @@ -6,7 +6,7 @@ export interface FormData { timezone: string; profession: string; yearsOfExperience: string; - language: string; + languages: { value: string; label: string }[]; skills: { value: string; label: string }[]; } diff --git a/src/utils/mockDB.ts b/src/utils/mockDB.ts index c6d3b8b..cf01c93 100644 --- a/src/utils/mockDB.ts +++ b/src/utils/mockDB.ts @@ -5,7 +5,7 @@ export type UserData = { timezone: string; profession: string; yearsOfExperience: string; - language: string; + languages: string[]; skills: string[]; }; diff --git a/vite.config.ts b/vite.config.ts index 6ebbb92..c53e2b4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,7 +7,7 @@ dns.setDefaultResultOrder('verbatim') export default defineConfig({ plugins: [react()], server: { - host: 'localhost', + host: true, port: 5173, }, }) From b1311edc99e03a65d5676101770c963f597780e1 Mon Sep 17 00:00:00 2001 From: stevenseb Date: Sat, 12 Jul 2025 22:15:44 -0700 Subject: [PATCH 04/10] added contributor agreement, react markdown package and header changes --- package-lock.json | 1155 +++++++++++++++++++++++- package.json | 1 + public/Contributor-Agreement.md | 30 + src/auth0/signup.tsx | 2 +- src/components/Header.tsx | 18 +- src/components/PublicHeader.tsx | 16 +- src/components/SignupForm.tsx | 10 +- src/components/TermsModal.tsx | 32 +- src/components/styles/Header.css | 136 +-- src/components/styles/publicHeader.css | 70 +- src/pages/SignupPage.tsx | 4 +- 11 files changed, 1335 insertions(+), 139 deletions(-) create mode 100644 public/Contributor-Agreement.md diff --git a/package-lock.json b/package-lock.json index c6c192f..3274660 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "react": "^18.2.0", "react-bootstrap": "^2.10.9", "react-dom": "^18.2.0", + "react-markdown": "^10.1.0", "react-modal": "^3.16.3", "react-router-dom": "^7.5.3", "react-select": "^5.10.1" @@ -24019,11 +24020,37 @@ "node": ">=6.9.0" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } }, "node_modules/@types/history": { "version": "4.7.11", @@ -24042,6 +24069,21 @@ "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", "dev": true }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.17.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.0.tgz", @@ -24130,6 +24172,12 @@ "@types/react": "*" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -24338,8 +24386,7 @@ "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "node_modules/@vitejs/plugin-react": { "version": "4.3.3", @@ -25307,6 +25354,16 @@ "node": ">=0.10.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -25602,6 +25659,16 @@ "upper-case-first": "^2.0.2" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chai": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", @@ -25680,6 +25747,46 @@ "tslib": "^2.0.3" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -25844,6 +25951,16 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", @@ -26093,6 +26210,19 @@ "node": ">=0.10.0" } }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -26307,6 +26437,19 @@ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dijkstrajs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", @@ -26856,6 +26999,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -26914,6 +27067,12 @@ "node": ">=12.0.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -27654,6 +27813,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/header-case": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", @@ -27682,6 +27881,16 @@ "react-is": "^16.7.0" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -27813,6 +28022,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -27857,6 +28072,30 @@ "node": ">=0.10.0" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -28013,6 +28252,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -28102,6 +28351,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -28191,7 +28450,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, "engines": { "node": ">=12" }, @@ -28752,6 +29010,16 @@ "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", "dev": true }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -28836,6 +29104,159 @@ "is-buffer": "~1.1.6" } }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", @@ -28857,6 +29278,448 @@ "node": ">= 8" } }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -29438,6 +30301,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -29893,6 +30781,16 @@ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", "dev": true }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -30134,6 +31032,33 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", "license": "MIT" }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-modal": { "version": "3.16.3", "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.3.tgz", @@ -30386,6 +31311,39 @@ "invariant": "^2.2.4" } }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", @@ -31078,6 +32036,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -31261,6 +32229,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -31326,6 +32308,24 @@ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, + "node_modules/style-to-js": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", + "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.9" + } + }, + "node_modules/style-to-object": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", + "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -31521,6 +32521,16 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -31530,6 +32540,16 @@ "node": ">=0.10.0" } }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -32222,6 +33242,93 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -32387,6 +33494,34 @@ "node": ">=12" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "5.4.10", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", @@ -33380,6 +34515,16 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 34e39b7..977a9ca 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react": "^18.2.0", "react-bootstrap": "^2.10.9", "react-dom": "^18.2.0", + "react-markdown": "^10.1.0", "react-modal": "^3.16.3", "react-router-dom": "^7.5.3", "react-select": "^5.10.1" diff --git a/public/Contributor-Agreement.md b/public/Contributor-Agreement.md new file mode 100644 index 0000000..4e9813d --- /dev/null +++ b/public/Contributor-Agreement.md @@ -0,0 +1,30 @@ +# Contributor Agreement + +Thank you for joining the Helpful community! + +Helpful Engineering is an international, open-source community incubator focused on mobilizing people to help solve critical issues. With thousands of volunteers worldwide, our mission is to support projects through innovations in engineering, community resources, software, and manufacturing. All volunteers **must be at least 18 years old.** If you are under the age of eighteen, your parent or guardian must agree to this Contributor Agreement on your behalf. + +Whether you are here to create a new project or offer support to an existing one, we’re happy you showed up. In order for us to stay focused on our mission and to do our best work, here are our Code of Conduct and Community Rules: + +**Our Code of Conduct.** You agree to adopt and uphold the Helpful Code of Conduct while working with and representing the Helpful community: + +- **BE KIND.** We expect everyone to act with mutual respect, dignity, and compassion. This includes abstaining from prejudicial words, or actions, involving race, gender, class, sexual orientation, gender presentation, physical ability, or religious affiliations. +- **BE COMMUNICATIVE**. Transparency is key, and you will be asked for updates on your role and activities in the community. Following existing workflows and having frequent communication keeps everyone on the same page and decreases the likelihood of critical mistakes, misunderstandings, or duplicated work. +- **BE THOUGHTFUL.** We love new ideas and new collaborations, but our community values making decisions carefully. Large decisions should be made with input from others and not unilaterally! If you have questions, ask and let’s find a solution. +- **BE ACCOUNTABLE.** Mistakes happen even with the best of intentions. If you say you’re going to do something, do it. If you can’t do it on time, inform your co-collaborators in a timely manner so that others can step in and support you. +- **BE TRUSTWORTHY.** We stress safeguarding community resources including sensitive information, passwords, personal information, and other internal data. This means you won’t divert or utilize Helpful resources or relationships towards another effort – public, or private. If you become aware of a conflict of interest, you will timely speak to an Officer or Board Director of Helpful to discuss the way forward. +- **BE CONSCIENTIOUS**. The Helpful community is counting on you to be professional. Helpful consists of many volunteers giving their time and effort. Please leave your workspace, files, and tools in an orderly fashion such that they are accessible to other members of the community. If you decide to move on from Helpful, please share your contact information with and assist the next person who will continue the excellent work that you’ve already done. +- **BE RESPECTFUL.** Many people are volunteering their time, tools, and other resources to make this effort come together, so please be respectful of individuals, their time, tools and resources. Make an effort to be professional and helpful. +- **BE HELPFUL.** Our primary goal is to do work that is helpful! + +## Our Community Rules + +- You understand that you are not considered an employee of Helpful and will not be entitled to receive any employee benefits. +- You are responsible for the contributions you make to the Helpful community, including any content which you may disseminate using Helpful resources (your “**Content**”). +- Although you retain ownership rights in your Content, you understand and acknowledge that, when you disseminate your Content using Helpful resources, it becomes Helpful Work Product (as defined below) and, as such, it becomes subject to an open-source license. + - “**Helpful Work Product**” is all documents, data, work product and/or other materials, including any intellectual property rights therein, that are developed or prepared by you, other Helpful volunteers, or other third parties using Helpful resources. + - You have an obligation to offer Helpful Work Product to the public using an open-source license compliant with the Helpful Licensing Guide which is available at (the “**Helpful Licensing Guide**”). + - **If you are working on an existing project using Helpful resources**, there must be a license compliant with the Helpful Licensing Guide set forth in the applicable repository for that project. If this is the case, you have an obligation to comply with that license. If this is not the case, you have an obligation to add a compliant license. + - **If you are establishing a new project using Helpful resources**, you have an obligation to use and comply with a license compliant with the Helpful Licensing Guide for that project and to publish such license in any applicable repository. +- You hereby waive any and all claims you may now or hereafter have in any jurisdiction to so-called “moral rights” or rights of droit moral with respect to Helpful Work Product, to the extent such rights would conflict with this Agreement. +- Lastly, in order to ensure that it remains open source and properly credited, all Helpful Work Product must be maintained on and reflected in a Helpful-designated and maintained repository. diff --git a/src/auth0/signup.tsx b/src/auth0/signup.tsx index 8e14980..ee94cbe 100644 --- a/src/auth0/signup.tsx +++ b/src/auth0/signup.tsx @@ -12,7 +12,7 @@ const SignupButton = () => { }); }; - return ; + return ; }; export default SignupButton; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 9252d50..ab03eac 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,18 +1,17 @@ -import '../index.css'; -import './styles/Header.css'; -import helpfulIcon from '../assets/helpful_icon.png'; -import profileIcon from '../assets/profile_icon.png'; -// will need to work on the transition between login and signup, dynamic change to header buttons based on logged in status this was a work around for now to have a blank page (using the component: PublicHeader.tsx) with a button that has auth0 functionality to simulate a signup coming in from the public WP site. For the sake of time, it was easier to wait and fix this later. (Eric) -// import LoginButton from "../auth0/login"; -// import LogoutButton from "../auth0/logout"; <--- logout button is implemented in auth0 folder, just need time to sort out the logged in view of header, add the button and test it. +import helpfulIcon from "../assets/helpful_icon.png"; +import profileIcon from "../assets/profile_icon.png"; import SignupButton from "../auth0/signup"; +import "./styles/Header.css"; const Header: React.FC = () => { return (
diff --git a/src/components/PublicHeader.tsx b/src/components/PublicHeader.tsx index f2fdb77..ab03eac 100644 --- a/src/components/PublicHeader.tsx +++ b/src/components/PublicHeader.tsx @@ -1,17 +1,15 @@ -import '../index.css'; -import "./styles/publicHeader.css"; import helpfulIcon from "../assets/helpful_icon.png"; +import profileIcon from "../assets/profile_icon.png"; import SignupButton from "../auth0/signup"; +import "./styles/Header.css"; -const PublicHeader: React.FC = () => { +const Header: React.FC = () => { return (
); }; -export default PublicHeader; +export default Header; diff --git a/src/components/SignupForm.tsx b/src/components/SignupForm.tsx index eed229e..8024ffc 100644 --- a/src/components/SignupForm.tsx +++ b/src/components/SignupForm.tsx @@ -56,8 +56,8 @@ const SignupForm: React.FC = ({ ); }; - const handleScroll = (e: React.UIEvent) => { - const target = e.target as HTMLDivElement; + const handleScroll = (e: React.UIEvent) => { + const target = e.target as HTMLElement; const { scrollTop, scrollHeight, clientHeight } = target; if (scrollTop + clientHeight >= scrollHeight - 10) { setHasScrolledToBottom(true); @@ -220,8 +220,12 @@ const SignupForm: React.FC = ({ onClose={() => setModalIsOpen(false)} hasScrolledToBottom={hasScrolledToBottom} onScroll={handleScroll} - onAccept={() => setHasReadTerms(true)} + onAccept={() => { + setHasReadTerms(true); + setTermsAccepted(true); + }} /> + ); }; diff --git a/src/components/TermsModal.tsx b/src/components/TermsModal.tsx index edb0560..5e5ce4c 100644 --- a/src/components/TermsModal.tsx +++ b/src/components/TermsModal.tsx @@ -1,45 +1,45 @@ -import React from "react"; +import { useEffect, useState } from "react"; import Modal from "react-bootstrap/Modal"; import Button from "react-bootstrap/Button"; +import ReactMarkdown from "react-markdown"; interface TermsModalProps { isOpen: boolean; onClose: () => void; hasScrolledToBottom: boolean; - onScroll: (e: React.UIEvent) => void; + onScroll: (event: React.UIEvent) => void; onAccept: () => void; } -const TermsModal: React.FC = ({ +const TermsModal = ({ isOpen, onClose, hasScrolledToBottom, onScroll, onAccept, -}) => { +}: TermsModalProps) => { + const [markdown, setMarkdown] = useState(""); + + useEffect(() => { + fetch("/Contributor-Agreement.md") + .then((res) => res.text()) + .then((text) => setMarkdown(text)); + }, []); + return ( - - - Terms and Conditions - - -
-

- By using this website, you agree to the following terms and conditions: -

-
+ {markdown}
diff --git a/src/components/styles/Header.css b/src/components/styles/Header.css index 92ad887..b61b11d 100644 --- a/src/components/styles/Header.css +++ b/src/components/styles/Header.css @@ -1,98 +1,116 @@ .header-container { width: 100%; - background-color: white; - color: black; - padding: 1rem; - display: flex; - justify-content: center; - align-items: center; + background: var(--header-bg, #fff); + color: var(--header-text, #222); position: sticky; top: 0; z-index: 1000; - box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); + box-shadow: 0 4px 12px rgba(0,0,0,0.08); + padding: 0; } .header-nav { - width: 100%; display: flex; - justify-content: space-between; - align-items: center; - padding: 0 1rem; + flex-direction: row; + align-items: center; /* Vertically center all items */ + width: 100%; + max-width: 1800px; + margin: 0 auto; + height: 70px; + padding: 0 32px; + box-sizing: border-box; } -.nav-links { +.logo-section { display: flex; align-items: center; - gap: 1rem; - padding-left: 1rem; } .helpful-icon { - width: 30px; - height: 30px; - margin-right: 0.5rem; -} - -.profile-icon { width: 40px; height: 40px; - margin-right: 0.5rem; + margin-right: 10px; +} + +.logo-text { + font-size: 1.6rem; + font-weight: 700; + letter-spacing: 2px; + color: var(--header-text, #222); + text-decoration: none; +} + +.nav-links { + display: flex; + align-items: center; + gap: 2.2rem; + margin-left: 24px; /* space between logo and links */ + margin-right: auto; /* pushes nav-buttons to the right */ } .nav-links a { - color: black; + color: var(--header-text, #222); text-decoration: none; - font-size: 1rem; + font-size: 16px; + font-weight: 400; + letter-spacing: 0.02em; + transition: color 0.15s; + padding: 2px 0; + border-bottom: 2px solid transparent; +} + +.nav-links a:hover, +.nav-links a:focus { + color: #424140; + font-size: 17px; + border-bottom: 2px solid #353434; } .nav-buttons { display: flex; - gap: 0.5rem; - justify-content: flex-end; - padding-right: 1rem; + align-items: center; + gap: 1.1rem; } -.login-button { - background-color: white; - color: #282c34; - border: 1px solid #282c34; - padding: 0.5rem 1rem; - cursor: pointer; - font-size: 1rem; +.profile-icon { + width: 40px; + height: 40px; + margin-left: 8px; } .signup-button { - background-color: green; - color: white; - border: none; - padding: 0.5rem 1rem; + background-color: #3B8223; + color: #fff; + border: 1px solid transparent; + border-radius: 4px; + font-weight: 700; + font-size: 1.03rem; + padding: 8px 16px; cursor: pointer; - font-size: 1rem; + transition: background 0.15s; } +.button { + border-radius: 4px; +} -/* Styles for PublicHeader component - testing and development purposes only */ -.public-landing { - min-height: 100vh; - display: flex; +.signup-button:hover, +.signup-button:focus { + background-color: #2E5C1A; +} + +@media (max-width: 900px) { + .header-nav { flex-direction: column; + height: auto; + padding: 12px; + align-items: flex-start; } - - .welcome-content { - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - text-align: center; - padding: 2rem; - background: white; + .nav-links { + margin: 16px 0 0 0; + gap: 1.2rem; } - - .loading { - height: 100vh; - display: flex; - align-items: center; - justify-content: center; + .nav-buttons { + margin-top: 12px; } - \ No newline at end of file +} diff --git a/src/components/styles/publicHeader.css b/src/components/styles/publicHeader.css index 89512a6..9bc227b 100644 --- a/src/components/styles/publicHeader.css +++ b/src/components/styles/publicHeader.css @@ -1,7 +1,7 @@ .header-container { width: 100%; - background-color: var(--header-bg); - color: var(--header-text); + background: var(--header-bg, #fff); + color: var(--header-text, #222); position: sticky; top: 0; z-index: 1000; @@ -11,20 +11,20 @@ .header-nav { display: flex; - align-items: center; + flex-direction: row; + align-items: center; /* Vertically center all items */ width: 100%; max-width: 1800px; margin: 0 auto; height: 70px; padding: 0 32px; + box-sizing: border-box; } .logo-section { - display: flex; - align-items: center; - margin-right: auto; - } - + display: flex; + align-items: center; +} .helpful-icon { width: 40px; @@ -36,7 +36,7 @@ font-size: 1.6rem; font-weight: 700; letter-spacing: 2px; - color: var(--header-text); + color: var(--header-text, #222); text-decoration: none; } @@ -44,12 +44,12 @@ display: flex; align-items: center; gap: 2.2rem; - margin-left: auto; - margin-right: auto; + margin-left: 24px; /* space between logo and links */ + margin-right: auto; /* pushes nav-buttons to the right */ } .nav-links a { - color: var(--header-text); + color: var(--header-text, #222); text-decoration: none; font-size: 1.08rem; font-weight: 600; @@ -65,14 +65,19 @@ border-bottom: 2px solid #E5A932; } -.header-actions { +.nav-buttons { display: flex; align-items: center; gap: 1.1rem; - flex: 0 0 auto; } -.donate-button { +.profile-icon { + width: 40px; + height: 40px; + margin-left: 8px; +} + +.signup-button { background-color: #3B8223; color: #fff; border: none; @@ -82,32 +87,29 @@ padding: 0.6rem 1.4rem; cursor: pointer; transition: background 0.15s; - margin-right: 8px; } -.donate-button:hover, -.donate-button:focus { +.signup-button:hover, +.signup-button:focus { background-color: #2E5C1A; } -.signinup-button { - background: #fff; - color: #000; - border: 2px solid #1F5378; - border-radius: 4px; - font-weight: 600; - font-size: 1.03rem; - padding: 0.6rem 1.4rem; - cursor: pointer; - transition: background 0.15s, color 0.15s; +@media (max-width: 900px) { + .header-nav { + flex-direction: column; + height: auto; + padding: 12px; + align-items: flex-start; + } + .nav-links { + margin: 16px 0 0 0; + gap: 1.2rem; + } + .nav-buttons { + margin-top: 12px; + } } -.signinup-button:hover, -.signinup-button:focus { - background: #E5A932; - color: #1F5378; - border-color: #E5A932; -} /* Responsive: Stack nav on mobile */ /* @media (max-width: 900px) { diff --git a/src/pages/SignupPage.tsx b/src/pages/SignupPage.tsx index 60d65d7..85d2f75 100644 --- a/src/pages/SignupPage.tsx +++ b/src/pages/SignupPage.tsx @@ -2,7 +2,7 @@ import { useState, FormEvent, useEffect } from "react"; import { useAuth0 } from "@auth0/auth0-react"; import { useSearchParams, useNavigate } from "react-router-dom"; import { saveUserData, getUserData } from "../utils/mockDB"; -import PublicHeader from "../components/PublicHeader"; +import Header from "../components/Header"; import SignupForm from "../components/SignupForm"; import "./styles/SignupPage.css"; import { FormData } from "../types/formTypes"; @@ -98,7 +98,7 @@ const SignupPage: React.FC = () => { return (
- +
Date: Sun, 20 Jul 2025 14:09:54 -0700 Subject: [PATCH 05/10] header styling adjusted button conditional rendering added added auth0 avatar --- src/auth0/login.tsx | 2 +- src/auth0/logout.tsx | 17 +++ src/auth0/signup.tsx | 2 +- src/components/Header.tsx | 40 ++++++- src/components/PublicHeader.tsx | 31 ------ src/components/styles/Header.css | 39 ++++--- src/components/styles/publicHeader.css | 134 ----------------------- src/pages/Dashboard.tsx | 2 +- src/pages/LoginSignupRedirect.tsx | 7 +- src/pages/styles/LoginSignupRedirect.css | 27 +++++ 10 files changed, 112 insertions(+), 189 deletions(-) create mode 100644 src/auth0/logout.tsx delete mode 100644 src/components/PublicHeader.tsx delete mode 100644 src/components/styles/publicHeader.css create mode 100644 src/pages/styles/LoginSignupRedirect.css diff --git a/src/auth0/login.tsx b/src/auth0/login.tsx index 3deaf02..5d50629 100644 --- a/src/auth0/login.tsx +++ b/src/auth0/login.tsx @@ -4,7 +4,7 @@ import React from "react"; const LoginButton: React.FC = () => { const { loginWithRedirect } = useAuth0(); - return ; + return ; }; export default LoginButton; diff --git a/src/auth0/logout.tsx b/src/auth0/logout.tsx new file mode 100644 index 0000000..9193ed6 --- /dev/null +++ b/src/auth0/logout.tsx @@ -0,0 +1,17 @@ +import { useAuth0 } from "@auth0/auth0-react"; +import React from "react"; + +const LogoutButton: React.FC = () => { + const { logout } = useAuth0(); + + return +}; + +export default LogoutButton; diff --git a/src/auth0/signup.tsx b/src/auth0/signup.tsx index ee94cbe..1a4ec43 100644 --- a/src/auth0/signup.tsx +++ b/src/auth0/signup.tsx @@ -12,7 +12,7 @@ const SignupButton = () => { }); }; - return ; + return ; }; export default SignupButton; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index ab03eac..b2a8b7b 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,15 +1,28 @@ +import React from "react"; +import { useAuth0 } from "@auth0/auth0-react"; import helpfulIcon from "../assets/helpful_icon.png"; import profileIcon from "../assets/profile_icon.png"; import SignupButton from "../auth0/signup"; +import LogoutButton from "../auth0/logout"; +import LoginButton from "../auth0/login"; import "./styles/Header.css"; -const Header: React.FC = () => { +interface HeaderProps { + onProfileClick?: () => void; +} + +const Header: React.FC = ({ onProfileClick }) => { + const { + isAuthenticated, + isLoading, + user, + } = useAuth0(); + return (
diff --git a/src/components/PublicHeader.tsx b/src/components/PublicHeader.tsx deleted file mode 100644 index ab03eac..0000000 --- a/src/components/PublicHeader.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import helpfulIcon from "../assets/helpful_icon.png"; -import profileIcon from "../assets/profile_icon.png"; -import SignupButton from "../auth0/signup"; -import "./styles/Header.css"; - -const Header: React.FC = () => { - return ( -
- -
- ); -}; - -export default Header; diff --git a/src/components/styles/Header.css b/src/components/styles/Header.css index b61b11d..a5e8b20 100644 --- a/src/components/styles/Header.css +++ b/src/components/styles/Header.css @@ -1,18 +1,21 @@ .header-container { - width: 100%; - background: var(--header-bg, #fff); - color: var(--header-text, #222); + width: 100%; + background-color: white; + color: black; + padding: 1rem; + display: flex; + justify-content: center; + align-items: center; position: sticky; top: 0; z-index: 1000; - box-shadow: 0 4px 12px rgba(0,0,0,0.08); - padding: 0; + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); } .header-nav { display: flex; flex-direction: row; - align-items: center; /* Vertically center all items */ + align-items: center; width: 100%; max-width: 1800px; margin: 0 auto; @@ -44,16 +47,17 @@ display: flex; align-items: center; gap: 2.2rem; - margin-left: 24px; /* space between logo and links */ - margin-right: auto; /* pushes nav-buttons to the right */ + margin-left: 24px; + margin-right: auto; } .nav-links a { color: var(--header-text, #222); text-decoration: none; + font-family: 'Inter', sans-serif; font-size: 16px; font-weight: 400; - letter-spacing: 0.02em; + line-height: 150%; transition: color 0.15s; padding: 2px 0; border-bottom: 2px solid transparent; @@ -70,30 +74,37 @@ display: flex; align-items: center; gap: 1.1rem; + max-height: 40px; } .profile-icon { width: 40px; height: 40px; margin-left: 8px; + border-radius: 50%; } .signup-button { background-color: #3B8223; color: #fff; border: 1px solid transparent; +} + +.login-button, .logout-button { + background-color: #FFF; + color: #000; + border: 1px solid black; +} + +.header-button { border-radius: 4px; font-weight: 700; font-size: 1.03rem; - padding: 8px 16px; + padding: 6px 16px; cursor: pointer; transition: background 0.15s; } -.button { - border-radius: 4px; -} - .signup-button:hover, .signup-button:focus { background-color: #2E5C1A; diff --git a/src/components/styles/publicHeader.css b/src/components/styles/publicHeader.css deleted file mode 100644 index 9bc227b..0000000 --- a/src/components/styles/publicHeader.css +++ /dev/null @@ -1,134 +0,0 @@ -.header-container { - width: 100%; - background: var(--header-bg, #fff); - color: var(--header-text, #222); - position: sticky; - top: 0; - z-index: 1000; - box-shadow: 0 4px 12px rgba(0,0,0,0.08); - padding: 0; -} - -.header-nav { - display: flex; - flex-direction: row; - align-items: center; /* Vertically center all items */ - width: 100%; - max-width: 1800px; - margin: 0 auto; - height: 70px; - padding: 0 32px; - box-sizing: border-box; -} - -.logo-section { - display: flex; - align-items: center; -} - -.helpful-icon { - width: 40px; - height: 40px; - margin-right: 10px; -} - -.logo-text { - font-size: 1.6rem; - font-weight: 700; - letter-spacing: 2px; - color: var(--header-text, #222); - text-decoration: none; -} - -.nav-links { - display: flex; - align-items: center; - gap: 2.2rem; - margin-left: 24px; /* space between logo and links */ - margin-right: auto; /* pushes nav-buttons to the right */ -} - -.nav-links a { - color: var(--header-text, #222); - text-decoration: none; - font-size: 1.08rem; - font-weight: 600; - letter-spacing: 0.02em; - transition: color 0.15s; - padding: 2px 0; - border-bottom: 2px solid transparent; -} - -.nav-links a:hover, -.nav-links a:focus { - color: #E5A932; - border-bottom: 2px solid #E5A932; -} - -.nav-buttons { - display: flex; - align-items: center; - gap: 1.1rem; -} - -.profile-icon { - width: 40px; - height: 40px; - margin-left: 8px; -} - -.signup-button { - background-color: #3B8223; - color: #fff; - border: none; - border-radius: 4px; - font-weight: 600; - font-size: 1.03rem; - padding: 0.6rem 1.4rem; - cursor: pointer; - transition: background 0.15s; -} - -.signup-button:hover, -.signup-button:focus { - background-color: #2E5C1A; -} - -@media (max-width: 900px) { - .header-nav { - flex-direction: column; - height: auto; - padding: 12px; - align-items: flex-start; - } - .nav-links { - margin: 16px 0 0 0; - gap: 1.2rem; - } - .nav-buttons { - margin-top: 12px; - } -} - - -/* Responsive: Stack nav on mobile */ -/* @media (max-width: 900px) { - .header-nav { - flex-direction: column; - height: auto; - padding: 0 10px; - gap: 8px; - } - .logo-section, - .header-actions { - justify-content: center; - width: 100%; - margin-bottom: 8px; - } - .nav-links { - gap: 1.2rem; - margin: 8px 0; - width: 100%; - justify-content: center; - } -} */ diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 33b264d..d3e5905 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -3,7 +3,7 @@ import './styles/Dashboard.css'; import Header from '../components/Header.tsx'; import MyProjects from '../views/MyProjects'; import Opportunities from '../views/Opportunities'; -import DashboardView from '../views/DashboardView'; // Import the new DashboardView component +import DashboardView from '../views/DashboardView'; // Importing icons for sidebar import dashboardIcon from '../assets/dashboard_icon.png'; diff --git a/src/pages/LoginSignupRedirect.tsx b/src/pages/LoginSignupRedirect.tsx index e0c6431..6193870 100644 --- a/src/pages/LoginSignupRedirect.tsx +++ b/src/pages/LoginSignupRedirect.tsx @@ -1,7 +1,8 @@ import { useAuth0 } from "@auth0/auth0-react"; import { Navigate } from "react-router-dom"; -import PublicHeader from "../components/PublicHeader"; -import { getUserData } from "../utils/mockDB"; // Updated import +import Header from "../components/Header.tsx"; +import { getUserData } from "../utils/mockDB"; +import './styles/LoginSignupRedirect.css'; export default function LoginSignupRedirect() { const { isAuthenticated, isLoading, user } = useAuth0(); @@ -22,7 +23,7 @@ export default function LoginSignupRedirect() { // Public landing page return (
- +

Welcome to Our Platform

Get started by logging in or signing up

diff --git a/src/pages/styles/LoginSignupRedirect.css b/src/pages/styles/LoginSignupRedirect.css new file mode 100644 index 0000000..62f40ad --- /dev/null +++ b/src/pages/styles/LoginSignupRedirect.css @@ -0,0 +1,27 @@ +.public-landing { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + min-height: 100vh; + text-align: center; + background-color: #f9f9f9; + font-family: 'Inter', sans-serif; +} + +.welcome-content { + max-width: 600px; + padding: 20px; + padding-top: 200px; +} + +.welcome-content h1 { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 1rem; +} + +.welcome-content p { + font-size: 1.25rem; + color: #555; +} From fb9213b160342cf153578bd1bc3de696fb94f78f Mon Sep 17 00:00:00 2001 From: stevenseb Date: Mon, 18 Aug 2025 21:44:17 -0700 Subject: [PATCH 06/10] Partial fixes to signup form and header styling per MK review on Github --- src/components/SignupForm.tsx | 28 +- src/components/TermsModal.tsx | 2 +- src/components/styles/Header.css | 9 +- src/components/styles/selectStyles.ts | 3 +- src/components/styles/signupForm.css | 63 +++-- src/index.css | 6 + src/pages/styles/SignupPage.css | 390 +++++++++++++------------- 7 files changed, 266 insertions(+), 235 deletions(-) diff --git a/src/components/SignupForm.tsx b/src/components/SignupForm.tsx index 8024ffc..991f604 100644 --- a/src/components/SignupForm.tsx +++ b/src/components/SignupForm.tsx @@ -6,8 +6,8 @@ import languages from "../data/languages"; import skills from "../data/skills"; import timezones from "../data/timezones"; import { SignupFormProps } from "../types/formTypes"; -import { customSelectStyles } from "./styles/selectStyles"; import "./styles/signupForm.css"; +import { customSelectStyles } from "./styles/selectStyles"; // Setup skills options for react-select const skillOptions = skills.map((skill) => ({ @@ -75,7 +75,7 @@ const SignupForm: React.FC = ({
= ({
= ({
= ({ -
+
setModalIsOpen(false)} - hasScrolledToBottom={hasScrolledToBottom} - onScroll={handleScroll} - onAccept={() => { - setHasReadTerms(true); - setTermsAccepted(true); - }} -/> - + isOpen={modalIsOpen} + onClose={() => setModalIsOpen(false)} + hasScrolledToBottom={hasScrolledToBottom} + onScroll={handleScroll} + onAccept={() => { + setHasReadTerms(true); + setTermsAccepted(true); + }} + /> ); }; diff --git a/src/components/styles/selectStyles.ts b/src/components/styles/selectStyles.ts index 81ef7a7..f7b8114 100644 --- a/src/components/styles/selectStyles.ts +++ b/src/components/styles/selectStyles.ts @@ -5,8 +5,9 @@ import { StylesConfig } from "react-select"; export const customSelectStyles: StylesConfig<{ value: string; label: string }, true> = { control: (base, state) => ({ ...base, - border: "px solid black", - borderRadius: 4, + border: ".5px solid black", + backgroundColor: "#f8f9fb", + borderRadius: 10, borderColor: state.isFocused ? "#1F5378" : "#000", boxShadow: "none", minHeight: 56, diff --git a/src/components/styles/signupForm.css b/src/components/styles/signupForm.css index 64c0982..8c8e281 100644 --- a/src/components/styles/signupForm.css +++ b/src/components/styles/signupForm.css @@ -1,29 +1,54 @@ -/* Form container - 75% width and responsive */ -.signup-form-container { - width: 75%; - max-width: 1200px; - margin: 0 auto; - padding: 20px; - box-sizing: border-box; +/* SignupForm.css */ + +/* Form container */ +.signup-form { + width: 100%; + display: flex; + flex-direction: column; + gap: 28px; +} + +/* Each form group */ +.signup-form .form-group { + display: flex; + flex-direction: column; + gap: 4px; + width: 100%; + padding-bottom: 0; + margin-bottom: 14px; + color: #a0a0a0; } -/* Form field background color and padding */ -.custom-form-control { - border: .5px solid black; +/* Labels */ +.signup-form label { + font-size: 1.07rem; + font-weight: 500; + color: #222; + margin-bottom: 0; + margin-left: 8px; + margin-top: 0; + letter-spacing: 0.01em; +} + +/* Inputs and Selects */ +.signup-form .custom-form-control { + width: 100%; border-radius: 10px; - min-height: 56px; - padding: 0 12px; + border: 0.5px solid black; + background: none; font-size: 1rem; + padding: 0 12px; + min-height: 56px; line-height: 56px; box-sizing: border-box; - width: 100%; + color: inherit; } -.custom-form-control:invalid { +.signup-form .custom-form-control:invalid { color: #a0a0a0; } -.custom-form-control::placeholder { +.signup-form .custom-form-control::placeholder { color: #a0a0a0; font-size: 1rem; font-weight: 400; @@ -31,39 +56,150 @@ line-height: 56px; } -/* Add bottom padding to each form group */ -.form-group { - padding-bottom: 0; - margin-bottom: 14px; - color: #a0a0a0; +/* Down arrow for select */ +.signup-form select.custom-form-control { + /* Hide native dropdown arrow */ + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + + /* Custom arrow using inline SVG as background */ + background-image: url("data:image/svg+xml,%3Csvg width='14' height='8' viewBox='0 0 14 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L7 7L13 1' stroke='%23c7c7c1' stroke-width='2'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 16px center; + background-size: 14px 8px; + + padding-right: 40px; + cursor: pointer; + border-radius: 10px; + border: 0.5px solid rgb(180, 178, 178); + min-height: 56px; + font-size: 1rem; + box-sizing: border-box; + color: inherit; + background-color: transparent; } -.terms-group { - margin: 0px; - padding: 0px; +/* multi-select remove arrow */ +.signup-form select.custom-form-control[multiple] { + background-image: none; + height: 56px; + min-height: 56px; + padding-right: 24px; } -.signup-form { - width: 100%; +/* Placeholder style for select */ +.signup-form select.custom-form-control:invalid { + color: #9b9bae; } -.signup-form-fields .form-control { +/* Skills select container */ +.skills-select-container { + display: flex; + flex-direction: column; + gap: 10px; +} + +/* Submit button */ +.submit-btn { + background-color: var(--secondary-btn); + color: var(--secondary-btn-label); + border: none; + border-radius: 14px; + font-weight: 600; + font-size: 1.15rem; + padding: 14px 64px; width: 100%; - box-sizing: border-box; + max-width: 340px; + display: block; + margin-top: -12px; + letter-spacing: 0.01em; + box-shadow: none; + cursor: pointer; + transition: background-color 0.2s, color 0.2s; +} + +.submit-btn:hover, +.submit-btn:focus { + background-color: var(--secondary-btn-hover); + color: var(--secondary-btn-label); +} + +.submit-btn:disabled { + background: #a2c495; + color: #fff; + cursor: not-allowed; +} + +/* Terms link */ +.terms-link { + background: none; + color: var(--link); + border: none; + padding: 0; + margin-top: 32px; + font-size: 1rem; + font-weight: 500; + text-decoration: underline; + cursor: pointer; + transition: color 0.18s; + border-radius: 0; +} + +.terms-link:hover, +.terms-link:focus { + color: #18425f; + text-decoration: underline; +} + +/* Terms and checkbox */ +.terms-group { + margin: 0; + padding: 0; + display: flex; + align-items: center; + gap: 10px; + margin-top: 18px; +} + +.checkbox-group { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; + margin-top: 10px; +} + +/* Form text */ +.form-text { + color: #666; + font-size: 0.95rem; + margin-top: 6px; + margin-left: 8px; +} + +/* Modal */ +.modal-content { + border-radius: 12px; } +.modal-header { + border-bottom: 1px solid #e5e5e5; +} -/* Responsive adjustments */ +.modal-footer { + border-top: 1px solid #e5e5e5; +} + +/* Responsive */ @media (max-width: 768px) { - .signup-form-container { - width: 90%; /* Wider on mobile devices */ - padding: 15px; + .signup-form .submit-btn { + font-size: 1.1rem; + padding: 14px 0; } -} -@media (max-width: 480px) { - .signup-form-container { - width: 95%; - padding: 10px; + .signup-form .custom-form-control { + font-size: 1rem; + padding: 12px 16px; } } diff --git a/src/index.css b/src/index.css index 6e0b063..6beaaa7 100644 --- a/src/index.css +++ b/src/index.css @@ -1,6 +1,13 @@ +html, body, #root { + height: 100%; + margin: 0; + padding: 0; + background-color: #f8f9fb; + box-sizing: border-box; +} + body { margin: 0; - background: #FFFFFF; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; height: 100vh; width: 100vw; diff --git a/src/pages/styles/SignupPage.css b/src/pages/styles/SignupPage.css index ad66d82..e3beea6 100644 --- a/src/pages/styles/SignupPage.css +++ b/src/pages/styles/SignupPage.css @@ -1,10 +1,12 @@ +/* SignupPage.css */ + .signup-page { - background: #f8f9fb; min-height: 100vh; display: flex; flex-direction: column; padding-bottom: 48px; box-sizing: border-box; + background: #f8f9fb; } .signup-page .signup-form-container { @@ -13,10 +15,15 @@ padding: 0 0 30px 0; border-radius: 0; box-shadow: none; + width: 75%; + max-width: 1200px; + box-sizing: border-box; } -.signup-page .signup-title { - font-size: 2.4rem; +.signup-page .signup-title, +h1.signup-title { + font-size: 24px; + padding-top: 50px; font-weight: 700; color: #000; text-align: center; @@ -29,176 +36,22 @@ color: #3b8223; } -/* Form fields */ -.signup-page .signup-form { - display: flex; - flex-direction: column; - gap: 28px; -} - -.signup-page .signup-form .form-group { - display: flex; - flex-direction: column; - gap: 4px; - width: 100%; -} - -.signup-page .signup-form label { - font-size: 1.07rem; - font-weight: 500; - color: #222; - margin-bottom: 0; - margin-left: 8px; - margin-top: 0; - letter-spacing: 0.01em; -} - -/* Input and select styling */ -.signup-page .signup-form .form-control { - width: 100%; - border-radius: 10px; - border: 2px solid #000; - background: #fff; - font-size: 1.15rem; - padding: 16px 24px; - margin-top: 4px; - margin-bottom: 0; - box-sizing: border-box; - transition: border-color 0.2s; - appearance: none; - outline: none; -} - -.signup-page .signup-form .form-control:focus { - border-color: #366ada; - background: #f7f7fa; -} - -.signup-page .signup-form .form-control::placeholder { - color: #9b9bae; - font-size: 1.08rem; - opacity: 1; -} - -/* Down arrow for select */ -.signup-page .signup-form select.form-control { - background-image: url("data:image/svg+xml,%3Csvg width='16' height='10' viewBox='0 0 16 10' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L8 8L15 1' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 18px center; - background-size: 18px 12px; - padding-right: 44px; - cursor: pointer; -} - -/* For multi-select, remove arrow */ -.signup-page .signup-form select.form-control[multiple] { - background-image: none; - height: 56px; - min-height: 56px; - padding-right: 24px; -} - -.signup-page .skills-select-container { - display: flex; - flex-direction: column; - gap: 10px; -} - -/* Placeholder style for select */ -.signup-page .signup-form select.form-control:invalid { - color: #9b9bae; -} - -/* Button styling */ -.signup-page .signup-form .submit-btn { - width: 100%; - max-width: 340px; - display: block; - margin: 32px auto 0 auto; - background: #3b8223; - border: none; - border-radius: 14px; - color: #fff; - font-weight: 600; - font-size: 1.3rem; - padding: 18px 0; - box-shadow: none; - transition: background 0.2s; - letter-spacing: 0.01em; -} - -.signup-page .signup-form .submit-btn:disabled { - background: #b5c7d6; - color: #fff; -} - -.signup-page .signup-form .submit-btn:hover, -.signup-page .signup-form .submit-btn:focus { - background: #18425f; -} - -/* Terms and checkbox */ -.signup-page .terms-group { - display: flex; - align-items: center; - gap: 10px; - margin-top: 18px; -} - -.signup-page .terms-link { - color: #366ada; - font-weight: 500; - padding-left: 0; - text-decoration: underline; -} - -.signup-page .checkbox-group { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 16px; - margin-top: 10px; -} - -.signup-page .form-text { - color: #666; - font-size: 0.95rem; - margin-top: 6px; - margin-left: 8px; -} - -/* Modal Styles */ -.signup-page .modal-content { - border-radius: 12px; -} - -.signup-page .modal-header { - border-bottom: 1px solid #e5e5e5; -} - -.signup-page .modal-footer { - border-top: 1px solid #e5e5e5; +/* Responsive adjustments for container and title */ +@media (max-width: 768px) { + .signup-page .signup-form-container { + width: 90%; /* Wider on mobile devices */ + padding: 15px; + } } -/* Responsive */ -@media (max-width: 600px) { +@media (max-width: 480px) { .signup-page .signup-form-container { - padding: 0 8px; + width: 95%; + padding: 10px; } - .signup-page .signup-title { font-size: 1.3rem; margin-top: 16px; margin-bottom: 24px; } - - .signup-page .signup-form .submit-btn { - font-size: 1.1rem; - padding: 14px 0; - } - - .signup-page .signup-form .form-control { - font-size: 1rem; - padding: 12px 16px; - } } From 62da46f48a8037971f3d9ec44df37e2e990f6ef7 Mon Sep 17 00:00:00 2001 From: stevenseb Date: Wed, 20 Aug 2025 21:42:30 -0700 Subject: [PATCH 08/10] modified chip color for multiselect --- src/components/styles/selectStyles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/styles/selectStyles.ts b/src/components/styles/selectStyles.ts index f7b8114..d16fd95 100644 --- a/src/components/styles/selectStyles.ts +++ b/src/components/styles/selectStyles.ts @@ -17,7 +17,7 @@ export const customSelectStyles: StylesConfig<{ value: string; label: string }, }), multiValue: (base) => ({ ...base, - backgroundColor: "#F7F7FA", + backgroundColor: "#f2f3f0ff", color: "#fff", borderRadius: 10, padding: "2px 8px", From 8889a2a92e5b8d13837270e7ed4e459017ece957 Mon Sep 17 00:00:00 2001 From: stevenseb Date: Mon, 1 Sep 2025 13:22:10 -0700 Subject: [PATCH 09/10] header mostly done styling of signup form adjusted --- package-lock.json | 22 ++++++++ package.json | 1 + src/components/DownArrow.tsx | 19 +++++++ src/components/Header.tsx | 75 ++++++++++++++++++++------- src/components/SignupForm.tsx | 7 +++ src/components/styles/Header.css | 46 ++++++++++++++++ src/components/styles/selectStyles.ts | 6 +++ src/components/styles/signupForm.css | 28 ++++++---- 8 files changed, 176 insertions(+), 28 deletions(-) create mode 100644 src/components/DownArrow.tsx diff --git a/package-lock.json b/package-lock.json index 3274660..a6f663b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@auth0/auth0-react": "^2.3.0", "@aws-amplify/ui-react": "^6.5.5", + "@iconify/react": "^6.0.0", "@types/react-bootstrap": "^0.32.37", "@types/react-modal": "^3.16.3", "@types/react-router-dom": "^5.3.3", @@ -21466,6 +21467,27 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@iconify/react": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@iconify/react/-/react-6.0.0.tgz", + "integrity": "sha512-eqNscABVZS8eCpZLU/L5F5UokMS9mnCf56iS1nM9YYHdH8ZxqZL9zyjSwW60IOQFsXZkilbBiv+1paMXBhSQnw==", + "license": "MIT", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, "node_modules/@inquirer/checkbox": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.4.tgz", diff --git a/package.json b/package.json index 977a9ca..058c2d0 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@auth0/auth0-react": "^2.3.0", "@aws-amplify/ui-react": "^6.5.5", + "@iconify/react": "^6.0.0", "@types/react-bootstrap": "^0.32.37", "@types/react-modal": "^3.16.3", "@types/react-router-dom": "^5.3.3", diff --git a/src/components/DownArrow.tsx b/src/components/DownArrow.tsx new file mode 100644 index 0000000..015dbfa --- /dev/null +++ b/src/components/DownArrow.tsx @@ -0,0 +1,19 @@ +import { Icon } from '@iconify/react'; +import React from 'react'; + +interface DownArrowProps { + style?: React.CSSProperties; + size?: number | string; + color?: string; +} + +const DownArrow: React.FC = ({ style, size = 24, color = 'black' }) => ( + +); + +export default DownArrow; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index b2a8b7b..cff01cf 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,5 +1,6 @@ -import React from "react"; +import React, { useState, useEffect, useRef } from "react"; import { useAuth0 } from "@auth0/auth0-react"; +import DownArrow from "./DownArrow.tsx"; import helpfulIcon from "../assets/helpful_icon.png"; import profileIcon from "../assets/profile_icon.png"; import SignupButton from "../auth0/signup"; @@ -12,11 +13,24 @@ interface HeaderProps { } const Header: React.FC = ({ onProfileClick }) => { - const { - isAuthenticated, - isLoading, - user, - } = useAuth0(); + const { isAuthenticated, isLoading, user } = useAuth0(); + +const menuRef = useRef(null); + +useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + setMenuOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; +}, []); + + + const [menuOpen, setMenuOpen] = useState(false); return (
@@ -25,16 +39,19 @@ const Header: React.FC = ({ onProfileClick }) => { Helpful Icon
- {!isLoading && !isAuthenticated && ( + {/* {!isLoading && !isAuthenticated && ( <> @@ -45,15 +62,35 @@ const Header: React.FC = ({ onProfileClick }) => { <> - )} - - Profile Icon + )} */} +
+ + +
+
setMenuOpen(!menuOpen)} style={{ cursor: "pointer" }}> +
setMenuOpen(!menuOpen)} + style={{ cursor: "pointer" }} + > + Profile Icon + + {menuOpen && ( +
+ Profile + Dashboard + {!isAuthenticated && } + {isAuthenticated && } +
+ )} +
+
diff --git a/src/components/SignupForm.tsx b/src/components/SignupForm.tsx index 2eff8e6..b75c4e5 100644 --- a/src/components/SignupForm.tsx +++ b/src/components/SignupForm.tsx @@ -8,6 +8,9 @@ import { SignupFormProps } from "../types/formTypes"; import { customSelectStyles } from "./styles/selectStyles"; import "./styles/signupForm.css"; + + + // Setup skills options for react-select const skillOptions = skills.map((skill) => ({ value: skill, @@ -154,6 +157,8 @@ const SignupForm: React.FC = ({ styles={customSelectStyles} closeMenuOnSelect={false} required + classNamePrefix="custom-select" + className="custom-select-wrapper" /> Start typing to filter and select multiple languages. @@ -178,6 +183,8 @@ const SignupForm: React.FC = ({ styles={customSelectStyles} closeMenuOnSelect={false} required + classNamePrefix="custom-select" + className="custom-select-wrapper" /> Start typing to filter and select multiple skills. diff --git a/src/components/styles/Header.css b/src/components/styles/Header.css index c66f91e..2572a99 100644 --- a/src/components/styles/Header.css +++ b/src/components/styles/Header.css @@ -43,6 +43,12 @@ text-decoration: none; } +.bg-gray { + border: none; + color: black; + background-color: #E8E8E8; +} + .nav-links { display: flex; align-items: center; @@ -111,6 +117,46 @@ background-color: #2E5C1A; } +.profile-menu-container { + position: relative; + display: inline-block; +} + +.submenu { + position: absolute; + top: calc(100% + 26px); + right: 0; + background: white; + border: 1px solid #ccc; + border-radius: 6px; + box-shadow: 0 2px 8px rgba(0,0,0,0.15); + padding: 8px 12px; + display: flex; + flex-direction: column; + gap: 8px; + z-index: 1000; +} + +.submenu a, +.submenu button { + text-decoration: none; + color: #333; + padding: 6px 10px; + border-radius: 4px; + background: none; + border: none; + cursor: pointer; + font-size: 1rem; + text-align: left; +} + +.submenu a:hover, +.submenu button:hover { + background-color: #f0f0f0; +} + + + @media (max-width: 900px) { .header-nav { flex-direction: column; diff --git a/src/components/styles/selectStyles.ts b/src/components/styles/selectStyles.ts index d16fd95..9d934e2 100644 --- a/src/components/styles/selectStyles.ts +++ b/src/components/styles/selectStyles.ts @@ -40,6 +40,12 @@ export const customSelectStyles: StylesConfig<{ value: string; label: string }, marginLeft: 6, marginRight: 2, }), + dropdownIndicator: () => ({ + display: 'none', +}), + indicatorSeparator: () => ({ + display: 'none', +}), menu: (base) => ({ ...base, zIndex: 9999, diff --git a/src/components/styles/signupForm.css b/src/components/styles/signupForm.css index 8c8e281..bd76353 100644 --- a/src/components/styles/signupForm.css +++ b/src/components/styles/signupForm.css @@ -64,15 +64,13 @@ -moz-appearance: none; /* Custom arrow using inline SVG as background */ - background-image: url("data:image/svg+xml,%3Csvg width='14' height='8' viewBox='0 0 14 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L7 7L13 1' stroke='%23c7c7c1' stroke-width='2'/%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='m7 10l5 5l5-5z' stroke-width='2' stroke='currentColor'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 16px center; - background-size: 14px 8px; - + background-size: 24px 24px; padding-right: 40px; cursor: pointer; border-radius: 10px; - border: 0.5px solid rgb(180, 178, 178); min-height: 56px; font-size: 1rem; box-sizing: border-box; @@ -80,12 +78,24 @@ background-color: transparent; } -/* multi-select remove arrow */ -.signup-form select.custom-form-control[multiple] { - background-image: none; - height: 56px; +.custom-select-wrapper .custom-select__control { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='m7 10l5 5l5-5z' stroke-width='2' stroke='currentColor' /%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 16px center; + background-size: 24px 24px; + padding-right: 40px; + cursor: pointer; + border-radius: 10px; min-height: 56px; - padding-right: 24px; + font-size: 1rem; + box-sizing: border-box; + color: inherit; + background-color: transparent; +} + +/* remove the default indicator separator */ +.custom-select-wrapper .custom-select__indicator-separator { + display: none; } /* Placeholder style for select */ From 722222580d54643ad534c1471e1e169e8dade9c4 Mon Sep 17 00:00:00 2001 From: stevenseb Date: Wed, 15 Oct 2025 21:43:57 -0700 Subject: [PATCH 10/10] final push for Eric --- src/components/Header.tsx | 17 ++--------------- src/components/styles/Header.css | 6 +++--- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index cff01cf..c30c6ef 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -3,7 +3,6 @@ import { useAuth0 } from "@auth0/auth0-react"; import DownArrow from "./DownArrow.tsx"; import helpfulIcon from "../assets/helpful_icon.png"; import profileIcon from "../assets/profile_icon.png"; -import SignupButton from "../auth0/signup"; import LogoutButton from "../auth0/logout"; import LoginButton from "../auth0/login"; import "./styles/Header.css"; @@ -12,8 +11,8 @@ interface HeaderProps { onProfileClick?: () => void; } -const Header: React.FC = ({ onProfileClick }) => { - const { isAuthenticated, isLoading, user } = useAuth0(); +const Header: React.FC = () => { + const { isAuthenticated, user } = useAuth0(); const menuRef = useRef(null); @@ -51,18 +50,6 @@ useEffect(() => {
- {/* {!isLoading && !isAuthenticated && ( - <> - - - - )} - - {!isLoading && isAuthenticated && ( - <> - - - )} */}
diff --git a/src/components/styles/Header.css b/src/components/styles/Header.css index 2572a99..ce63350 100644 --- a/src/components/styles/Header.css +++ b/src/components/styles/Header.css @@ -124,12 +124,12 @@ .submenu { position: absolute; - top: calc(100% + 26px); + top: calc(100% + 22px); right: 0; background: white; - border: 1px solid #ccc; + opacity: 0.8; + border: .5px solid #ccc; border-radius: 6px; - box-shadow: 0 2px 8px rgba(0,0,0,0.15); padding: 8px 12px; display: flex; flex-direction: column;