From 1cee8cda3694a3ee9f2d37e637609bd1c9bb8243 Mon Sep 17 00:00:00 2001
From: Rebecca Smith <2145912+Rebeccals@users.noreply.github.com>
Date: Tue, 8 Apr 2025 14:19:12 -0700
Subject: [PATCH 1/6] Updated Onboarding Page
---
frontend/bitmatch/package-lock.json | 442 ++++++++++++++++++++
frontend/bitmatch/package.json | 3 +
frontend/bitmatch/src/App.jsx | 27 +-
frontend/bitmatch/src/views/OnboardPage.jsx | 247 +++++++----
frontend/bitmatch/src/views/SignInPage.jsx | 2 +-
5 files changed, 642 insertions(+), 79 deletions(-)
diff --git a/frontend/bitmatch/package-lock.json b/frontend/bitmatch/package-lock.json
index 1b9f647..bc61c46 100644
--- a/frontend/bitmatch/package-lock.json
+++ b/frontend/bitmatch/package-lock.json
@@ -9,11 +9,14 @@
"version": "0.0.0",
"dependencies": {
"@clerk/clerk-react": "^5.24.0",
+ "@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-scroll-area": "^1.2.3",
+ "@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-slot": "^1.1.2",
+ "@radix-ui/react-toast": "^1.2.7",
"@radix-ui/react-toggle": "^1.1.2",
"axios": "^1.8.3",
"class-variance-authority": "^0.7.1",
@@ -1168,6 +1171,62 @@
}
}
},
+ "node_modules/@radix-ui/react-checkbox": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz",
+ "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.1",
+ "@radix-ui/react-compose-refs": "1.1.1",
+ "@radix-ui/react-context": "1.1.1",
+ "@radix-ui/react-presence": "1.1.2",
+ "@radix-ui/react-primitive": "2.0.2",
+ "@radix-ui/react-use-controllable-state": "1.1.0",
+ "@radix-ui/react-use-previous": "1.1.0",
+ "@radix-ui/react-use-size": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz",
+ "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.1",
+ "@radix-ui/react-context": "1.1.1",
+ "@radix-ui/react-primitive": "2.0.2",
+ "@radix-ui/react-slot": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
@@ -1527,6 +1586,49 @@
}
}
},
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz",
+ "integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.0",
+ "@radix-ui/primitive": "1.1.1",
+ "@radix-ui/react-collection": "1.1.2",
+ "@radix-ui/react-compose-refs": "1.1.1",
+ "@radix-ui/react-context": "1.1.1",
+ "@radix-ui/react-direction": "1.1.0",
+ "@radix-ui/react-dismissable-layer": "1.1.5",
+ "@radix-ui/react-focus-guards": "1.1.1",
+ "@radix-ui/react-focus-scope": "1.1.2",
+ "@radix-ui/react-id": "1.1.0",
+ "@radix-ui/react-popper": "1.2.2",
+ "@radix-ui/react-portal": "1.1.4",
+ "@radix-ui/react-primitive": "2.0.2",
+ "@radix-ui/react-slot": "1.1.2",
+ "@radix-ui/react-use-callback-ref": "1.1.0",
+ "@radix-ui/react-use-controllable-state": "1.1.0",
+ "@radix-ui/react-use-layout-effect": "1.1.0",
+ "@radix-ui/react-use-previous": "1.1.0",
+ "@radix-ui/react-visually-hidden": "1.1.2",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.6.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
@@ -1544,6 +1646,307 @@
}
}
},
+ "node_modules/@radix-ui/react-toast": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.7.tgz",
+ "integrity": "sha512-0IWTbAUKvzdpOaWDMZisXZvScXzF0phaQjWspK8RUMEUxjLbli+886mB/kXTIC3F+t5vQ0n0vYn+dsX8s+WdfA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.2",
+ "@radix-ui/react-collection": "1.1.3",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-dismissable-layer": "1.1.6",
+ "@radix-ui/react-portal": "1.1.5",
+ "@radix-ui/react-presence": "1.1.3",
+ "@radix-ui/react-primitive": "2.0.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-controllable-state": "1.1.1",
+ "@radix-ui/react-use-layout-effect": "1.1.1",
+ "@radix-ui/react-visually-hidden": "1.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/primitive": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
+ "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-collection": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.3.tgz",
+ "integrity": "sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-primitive": "2.0.3",
+ "@radix-ui/react-slot": "1.2.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.6.tgz",
+ "integrity": "sha512-7gpgMT2gyKym9Jz2ZhlRXSg2y6cNQIK8d/cqBZ0RBCaps8pFryCWXiUKI+uHGFrhMrbGUP7U6PWgiXzIxoyF3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.2",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-primitive": "2.0.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-escape-keydown": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-portal": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.5.tgz",
+ "integrity": "sha512-ps/67ZqsFm+Mb6lSPJpfhRLrVL2i2fntgCmGMqqth4eaGUf+knAuuRtWVJrNjUhExgmdRqftSgzpf0DF0n6yXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.0.3",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-presence": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz",
+ "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-primitive": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.3.tgz",
+ "integrity": "sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
+ "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
+ "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.1.tgz",
+ "integrity": "sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-callback-ref": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
+ "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-callback-ref": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
+ "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.3.tgz",
+ "integrity": "sha512-oXSF3ZQRd5fvomd9hmUCb2EHSZbPp3ZSHAHJJU/DlF9XoFkJBBW8RHU/E8WEH+RbSfJd/QFA0sl8ClJXknBwHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-toggle": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.2.tgz",
@@ -1635,6 +2038,21 @@
}
}
},
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz",
+ "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-rect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
@@ -1671,6 +2089,29 @@
}
}
},
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz",
+ "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/rect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
@@ -2539,6 +2980,7 @@
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
"dependencies": {
"clsx": "^2.1.1"
},
diff --git a/frontend/bitmatch/package.json b/frontend/bitmatch/package.json
index 5d62722..734b044 100644
--- a/frontend/bitmatch/package.json
+++ b/frontend/bitmatch/package.json
@@ -11,11 +11,14 @@
},
"dependencies": {
"@clerk/clerk-react": "^5.24.0",
+ "@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-scroll-area": "^1.2.3",
+ "@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-slot": "^1.1.2",
+ "@radix-ui/react-toast": "^1.2.7",
"@radix-ui/react-toggle": "^1.1.2",
"axios": "^1.8.3",
"class-variance-authority": "^0.7.1",
diff --git a/frontend/bitmatch/src/App.jsx b/frontend/bitmatch/src/App.jsx
index 1dbb200..1ed5e5c 100644
--- a/frontend/bitmatch/src/App.jsx
+++ b/frontend/bitmatch/src/App.jsx
@@ -16,7 +16,15 @@ import ProjectDetailPage from "./views/IndividualProjectPage";
import AddProjectPage from "./views/AddProjectPage";
import SignUpPage from "./views/SignUpPage";
import SignInPage from "./views/SignInPage";
+
import OnboardPage from "./views/OnboardPage";
+import InterestPage from './components/onboarding/Interest';
+import LocationPage from './components/onboarding/Location';
+import PositionPage from './components/onboarding/Roles';
+import SkillsPage from './components/onboarding/Skills';
+import UserPage from './components/onboarding/CreateProfile';
+
+// Uncomment when ready
// import BrowsePage from "./views/BrowsePage";
// import AboutPage from "./views/AboutPage";
@@ -26,10 +34,9 @@ function AppRoutes() {
const location = useLocation();
const { isSignedIn } = useUser();
const isLanding = location.pathname === "/";
- const layoutClass =
- isLanding && !isSignedIn
- ? "py-8"
- : "container mx-auto px-4 py-16 flex pb-6 flex-col items-center justify-center min-h-screen";
+ const layoutClass = !isSignedIn
+ ? "py-8"
+ : "container mx-auto px-4 py-16 flex pb-6 flex-col items-center justify-center min-h-screen";
return (
@@ -78,10 +85,18 @@ function AppRoutes() {
{/* Public pages */}
} />
} />
-
} />
+
+ {/* Onboarding with redirect and nested routes */}
+
}>
+
} />
+
} />
+
} />
+
} />
+
} />
+
{/* Uncomment when ready */}
- {/*
+ {/*
} />
} />
*/}
diff --git a/frontend/bitmatch/src/views/OnboardPage.jsx b/frontend/bitmatch/src/views/OnboardPage.jsx
index ca532fd..c611954 100644
--- a/frontend/bitmatch/src/views/OnboardPage.jsx
+++ b/frontend/bitmatch/src/views/OnboardPage.jsx
@@ -1,82 +1,185 @@
-import React, { useState } from "react";
-import { useNavigate } from "react-router-dom";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Label } from "@/components/ui/Label";
-import { Textarea } from "@/components/ui/Textarea";
+'use client'
+
+import { useState, useRef } from 'react'
+import { ChevronLeft, ChevronRight } from 'lucide-react'
+import { motion, AnimatePresence } from 'framer-motion'
+import { Button } from '@/components/ui/button'
+import { Toaster } from '@/components/ui/toaster'
+import CreateProfile from '@/components/onboarding/CreateProfile'
+import Location from '@/components/onboarding/Location'
+import Roles from '@/components/onboarding/Roles'
+import Interest from '@/components/onboarding/Interest'
+import Skills from '@/components/onboarding/Skills'
+import StepIndicator from '@/components/onboarding/StepIndicator'
export default function OnboardPage() {
- const navigate = useNavigate();
- const [formData, setFormData] = useState({
- role: "",
- interests: "",
- bio: "",
- });
+ const [currentStep, setCurrentStep] = useState(1)
+ const [formData, setFormData] = useState({})
+ const totalSteps = 5
+
+ const createProfileRef = useRef()
+ const locationRef = useRef()
+ const rolesRef = useRef()
+ const interestRef = useRef()
+ const skillsRef = useRef()
+
+ const stepTitles = [
+ 'Create your Profile',
+ 'Location',
+ 'Roles & Positions',
+ 'Project Interests',
+ 'List Skill Sets',
+ ]
+
+ const updateStepData = (data) => {
+ setFormData((prev) => ({ ...prev, ...data }))
+ }
+
+ const validateStep = () => {
+ switch (currentStep) {
+ case 1:
+ return createProfileRef.current?.validate() ?? false
+ case 2:
+ return locationRef.current?.validate() ?? false
+ case 3:
+ return rolesRef.current?.validate() ?? false
+ case 4:
+ return interestRef.current?.validate() ?? false
+ case 5:
+ return skillsRef.current?.validate() ?? false
+ default:
+ return true
+ }
+ }
+
+ const handleNext = () => {
+ if (validateStep()) {
+ setCurrentStep(currentStep + 1)
+ } else {
+ toast({
+ variant: 'destructive',
+ title: '🚫 Incomplete Step',
+ description: 'Please complete this step before proceeding.',
+ })
+ }
+ }
+
+ const handlePrevious = () => {
+ if (currentStep > 1) {
+ setCurrentStep(currentStep - 1)
+ }
+ }
- const handleChange = (e) => {
- const { name, value } = e.target;
- setFormData((prev) => ({ ...prev, [name]: value }));
- };
+ const handleSubmit = async () => {
+ try {
+ const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/user-profiles/`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(formData),
+ })
- const handleSubmit = (e) => {
- e.preventDefault();
+ if (!res.ok) throw new Error('Failed to submit')
+ alert('✅ Profile submitted successfully!')
+ } catch (err) {
+ console.error(err)
+ alert('❌ Submission failed')
+ }
+ }
- // You can send this data to Supabase, Clerk user metadata, or your backend here
- console.log("Onboarding data submitted:", formData);
+ const renderStep = () => {
+ const stepProps = { onDataChange: updateStepData, formData }
- // Redirect to dashboard or home
- navigate("/dashboard");
- };
+ switch (currentStep) {
+ case 1:
+ return
+ case 2:
+ return
+ case 3:
+ return
+ case 4:
+ return
+ case 5:
+ return
+ default:
+ return null
+ }
+ }
return (
-
-
-
Work in Progress!
-
- This is just a placeholder for the onboarding process. Nothing works
- right now.
-
-
-
+ <>
+
{/* ✅ Required for showing toasts */}
+
+
+ {/* Header */}
+
+
{stepTitles[currentStep - 1]}
+
+
+ {/* Progress Bar */}
+
+
+ {/* Step Indicator */}
+
+
+ {/* Main Content */}
+
+
+
+ {renderStep()}
+
+
+
+ {/* Optional JSON debug preview */}
+ {/*
+ {JSON.stringify(formData, null, 2)}
+ */}
+
+
+ {/* Navigation */}
+
+ {currentStep > 1 ? (
+
+ ) : (
+
+ )}
+
+ {currentStep < totalSteps ? (
+
+ ) : (
+
+ )}
+
-
- );
+ >
+ )
}
diff --git a/frontend/bitmatch/src/views/SignInPage.jsx b/frontend/bitmatch/src/views/SignInPage.jsx
index 7aec7bc..7fa581b 100644
--- a/frontend/bitmatch/src/views/SignInPage.jsx
+++ b/frontend/bitmatch/src/views/SignInPage.jsx
@@ -9,7 +9,7 @@ export default function SignInPage() {
path="/sign-in"
routing="path"
signUpUrl="/sign-up"
- redirectUrl="/dashboard"
+ forceRedirectUrl="/"
appearance={{
elements: {
rootBox: "mx-auto",
From e1088532370657cd5d1ce2b93bb48e970b064ae5 Mon Sep 17 00:00:00 2001
From: Rebecca Smith <2145912+Rebeccals@users.noreply.github.com>
Date: Tue, 8 Apr 2025 14:19:29 -0700
Subject: [PATCH 2/6] More onboarding updates
---
.../components/onboarding/CreateProfile.jsx | 126 ++++++++++++++
.../src/components/onboarding/Interest.jsx | 88 ++++++++++
.../src/components/onboarding/Location.jsx | 97 +++++++++++
.../src/components/onboarding/Roles.jsx | 88 ++++++++++
.../src/components/onboarding/Skills.jsx | 88 ++++++++++
.../components/onboarding/StepIndicator.jsx | 20 +++
frontend/bitmatch/src/components/ui/badge.jsx | 14 ++
.../bitmatch/src/components/ui/checkbox.jsx | 29 ++++
frontend/bitmatch/src/components/ui/toast.jsx | 85 ++++++++++
.../bitmatch/src/components/ui/toaster.jsx | 33 ++++
frontend/bitmatch/src/hooks/use-toast.js | 155 ++++++++++++++++++
11 files changed, 823 insertions(+)
create mode 100644 frontend/bitmatch/src/components/onboarding/CreateProfile.jsx
create mode 100644 frontend/bitmatch/src/components/onboarding/Interest.jsx
create mode 100644 frontend/bitmatch/src/components/onboarding/Location.jsx
create mode 100644 frontend/bitmatch/src/components/onboarding/Roles.jsx
create mode 100644 frontend/bitmatch/src/components/onboarding/Skills.jsx
create mode 100644 frontend/bitmatch/src/components/onboarding/StepIndicator.jsx
create mode 100644 frontend/bitmatch/src/components/ui/badge.jsx
create mode 100644 frontend/bitmatch/src/components/ui/checkbox.jsx
create mode 100644 frontend/bitmatch/src/components/ui/toast.jsx
create mode 100644 frontend/bitmatch/src/components/ui/toaster.jsx
create mode 100644 frontend/bitmatch/src/hooks/use-toast.js
diff --git a/frontend/bitmatch/src/components/onboarding/CreateProfile.jsx b/frontend/bitmatch/src/components/onboarding/CreateProfile.jsx
new file mode 100644
index 0000000..19c6ef8
--- /dev/null
+++ b/frontend/bitmatch/src/components/onboarding/CreateProfile.jsx
@@ -0,0 +1,126 @@
+'use client'
+
+import {
+ forwardRef,
+ useEffect,
+ useState,
+ useImperativeHandle,
+} from 'react'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Badge } from '@/components/ui/badge'
+import { X } from 'lucide-react'
+
+// ✅ Component with forwardRef
+const CreateProfile = forwardRef(({ onDataChange, formData }, ref) => {
+ const [firstName, setFirstName] = useState(formData?.first_name || '')
+ const [lastName, setLastName] = useState(formData?.last_name || '')
+ const [colleges, setColleges] = useState(formData?.colleges || [])
+ const [newCollege, setNewCollege] = useState('')
+ const [errors, setErrors] = useState({})
+
+ // Send data to parent on change
+ useEffect(() => {
+ onDataChange({ first_name: firstName, last_name: lastName, colleges })
+ }, [firstName, lastName, colleges])
+
+ // Validation logic exposed via ref
+ useImperativeHandle(ref, () => ({
+ validate: () => {
+ const newErrors = {}
+ if (!firstName.trim()) newErrors.firstName = 'First name is required.'
+ if (!lastName.trim()) newErrors.lastName = 'Last name is required.'
+ if (colleges.length === 0) newErrors.colleges = 'At least one college is required.'
+ setErrors(newErrors)
+ return Object.keys(newErrors).length === 0
+ },
+ }))
+
+ const handleAddCollege = () => {
+ const trimmed = newCollege.trim()
+ if (trimmed && !colleges.includes(trimmed)) {
+ const updated = [...colleges, trimmed].sort()
+ setColleges(updated)
+ setNewCollege('')
+ }
+ }
+
+ const handleRemoveCollege = (college) => {
+ const updated = colleges.filter((c) => c !== college).sort()
+ setColleges(updated)
+ }
+
+ return (
+
+ {/* First Name */}
+
+
+
setFirstName(e.target.value)}
+ placeholder="Enter your first name"
+ />
+ {errors.firstName &&
{errors.firstName}
}
+
+
+ {/* Last Name */}
+
+
+
setLastName(e.target.value)}
+ placeholder="Enter your last name"
+ />
+ {errors.lastName &&
{errors.lastName}
}
+
+
+ {/* Colleges */}
+
+
+
+ setNewCollege(e.target.value)}
+ placeholder="Add college or university"
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ handleAddCollege()
+ }
+ }}
+ />
+
+
+
+
+ {colleges.map((college) => (
+
+ {college}
+
+
+ ))}
+
+
+ {errors.colleges &&
{errors.colleges}
}
+
+
+ )
+})
+
+// ✅ Export wrapped with forwardRef
+export default CreateProfile
diff --git a/frontend/bitmatch/src/components/onboarding/Interest.jsx b/frontend/bitmatch/src/components/onboarding/Interest.jsx
new file mode 100644
index 0000000..a1602c1
--- /dev/null
+++ b/frontend/bitmatch/src/components/onboarding/Interest.jsx
@@ -0,0 +1,88 @@
+'use client'
+
+import {
+ forwardRef,
+ useEffect,
+ useState,
+ useImperativeHandle,
+} from 'react'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { X } from 'lucide-react'
+
+const Interest = forwardRef(({ onDataChange, formData }, ref) => {
+ const [interests, setInterests] = useState(formData?.interests || [])
+ const [newInterest, setNewInterest] = useState('')
+ const [error, setError] = useState('')
+
+ useEffect(() => {
+ onDataChange({ interests })
+ }, [interests])
+
+ const validate = () => {
+ if (interests.length === 0) {
+ setError('Please add at least one interest.')
+ return false
+ }
+ setError('')
+ return true
+ }
+
+ useImperativeHandle(ref, () => ({ validate }))
+
+ const handleAdd = () => {
+ const trimmed = newInterest.trim()
+ if (trimmed && !interests.includes(trimmed)) {
+ setInterests([...interests, trimmed].sort())
+ setNewInterest('')
+ setError('')
+ }
+ }
+
+ const handleRemove = (interest) => {
+ setInterests(interests.filter((i) => i !== interest))
+ }
+
+ return (
+
+
+
+
+ setNewInterest(e.target.value)}
+ placeholder="Type a category"
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ handleAdd()
+ }
+ }}
+ />
+
+
+
+ {error &&
{error}
}
+
+
+ {interests.map((interest) => (
+
+ {interest}
+
+
+ ))}
+
+
+ )
+})
+
+export default Interest
diff --git a/frontend/bitmatch/src/components/onboarding/Location.jsx b/frontend/bitmatch/src/components/onboarding/Location.jsx
new file mode 100644
index 0000000..3c169a4
--- /dev/null
+++ b/frontend/bitmatch/src/components/onboarding/Location.jsx
@@ -0,0 +1,97 @@
+'use client'
+
+import {
+ forwardRef,
+ useEffect,
+ useState,
+ useImperativeHandle,
+} from 'react'
+import { Checkbox } from '@/components/ui/checkbox'
+
+const Location = forwardRef(({ onDataChange, formData }, ref) => {
+ const [location, setLocation] = useState(formData?.location || '')
+ const [preferences, setPreferences] = useState(
+ formData?.location_preferences?.filter(p => p !== '') || ["I'm location flexible (REMOTE)"]
+ )
+ const [other, setOther] = useState('')
+ const [errors, setErrors] = useState({})
+
+ const options = [
+ "At my location",
+ "At my University or College location",
+ "I'm location flexible (REMOTE)",
+ ]
+
+ useEffect(() => {
+ const allPreferences = [...preferences, ...(other ? [other] : [])]
+ onDataChange({ location, location_preferences: allPreferences })
+ }, [location, preferences, other])
+
+ const validate = () => {
+ const newErrors = {}
+ if (!location.trim()) newErrors.location = 'Location is required.'
+ if (preferences.length === 0 && !other.trim()) {
+ newErrors.preferences = 'Please select or enter at least one preference.'
+ }
+ setErrors(newErrors)
+ return Object.keys(newErrors).length === 0
+ }
+
+ useImperativeHandle(ref, () => ({ validate }))
+
+ const togglePreference = (value) => {
+ setPreferences((prev) =>
+ prev.includes(value)
+ ? prev.filter((p) => p !== value)
+ : [...prev, value]
+ )
+ }
+
+ return (
+
+
+
+
setLocation(e.target.value)}
+ placeholder="Enter your location"
+ />
+ {errors.location &&
{errors.location}
}
+
+
+
+
+
+ {options.map((option) => (
+
+ togglePreference(option)}
+ />
+
+
+ ))}
+
+
+
+ setOther(e.target.value)}
+ placeholder="Add custom preference"
+ />
+
+
+ {errors.preferences &&
{errors.preferences}
}
+
+
+ )
+})
+
+export default Location
diff --git a/frontend/bitmatch/src/components/onboarding/Roles.jsx b/frontend/bitmatch/src/components/onboarding/Roles.jsx
new file mode 100644
index 0000000..2a88cd5
--- /dev/null
+++ b/frontend/bitmatch/src/components/onboarding/Roles.jsx
@@ -0,0 +1,88 @@
+'use client'
+
+import {
+ forwardRef,
+ useEffect,
+ useState,
+ useImperativeHandle,
+} from 'react'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { X } from 'lucide-react'
+
+const Roles = forwardRef(({ onDataChange, formData }, ref) => {
+ const [roles, setRoles] = useState(formData?.roles || [])
+ const [newRole, setNewRole] = useState('')
+ const [error, setError] = useState('')
+
+ useEffect(() => {
+ onDataChange({ roles })
+ }, [roles])
+
+ const validate = () => {
+ if (roles.length === 0) {
+ setError('Please add at least one role.')
+ return false
+ }
+ setError('')
+ return true
+ }
+
+ useImperativeHandle(ref, () => ({ validate }))
+
+ const handleAdd = () => {
+ const trimmed = newRole.trim()
+ if (trimmed && !roles.includes(trimmed)) {
+ setRoles([...roles, trimmed].sort())
+ setNewRole('')
+ setError('')
+ }
+ }
+
+ const handleRemove = (role) => {
+ setRoles(roles.filter((r) => r !== role))
+ }
+
+ return (
+
+
+
+
+ setNewRole(e.target.value)}
+ placeholder="Type a position"
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ handleAdd()
+ }
+ }}
+ />
+
+
+
+ {error &&
{error}
}
+
+
+ {roles.map((role) => (
+
+ {role}
+
+
+ ))}
+
+
+ )
+})
+
+export default Roles
diff --git a/frontend/bitmatch/src/components/onboarding/Skills.jsx b/frontend/bitmatch/src/components/onboarding/Skills.jsx
new file mode 100644
index 0000000..cfce8cf
--- /dev/null
+++ b/frontend/bitmatch/src/components/onboarding/Skills.jsx
@@ -0,0 +1,88 @@
+'use client'
+
+import {
+ forwardRef,
+ useEffect,
+ useState,
+ useImperativeHandle,
+} from 'react'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { X } from 'lucide-react'
+
+const Skills = forwardRef(({ onDataChange, formData }, ref) => {
+ const [skills, setSkills] = useState(formData?.skills || [])
+ const [newSkill, setNewSkill] = useState('')
+ const [error, setError] = useState('')
+
+ useEffect(() => {
+ onDataChange({ skills })
+ }, [skills])
+
+ const validate = () => {
+ if (skills.length === 0) {
+ setError('Please add at least one skill.')
+ return false
+ }
+ setError('')
+ return true
+ }
+
+ useImperativeHandle(ref, () => ({ validate }))
+
+ const handleAdd = () => {
+ const trimmed = newSkill.trim()
+ if (trimmed && !skills.includes(trimmed)) {
+ setSkills([...skills, trimmed].sort())
+ setNewSkill('')
+ setError('')
+ }
+ }
+
+ const handleRemove = (skill) => {
+ setSkills(skills.filter((s) => s !== skill))
+ }
+
+ return (
+
+
+
+
+ setNewSkill(e.target.value)}
+ placeholder="Type a skill"
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ handleAdd()
+ }
+ }}
+ />
+
+
+
+ {error &&
{error}
}
+
+
+ {skills.map((skill) => (
+
+ {skill}
+
+
+ ))}
+
+
+ )
+})
+
+export default Skills
diff --git a/frontend/bitmatch/src/components/onboarding/StepIndicator.jsx b/frontend/bitmatch/src/components/onboarding/StepIndicator.jsx
new file mode 100644
index 0000000..2301757
--- /dev/null
+++ b/frontend/bitmatch/src/components/onboarding/StepIndicator.jsx
@@ -0,0 +1,20 @@
+export default function StepIndicator({ currentStep, totalSteps }) {
+ return (
+
+ {Array.from({ length: totalSteps }).map((_, index) => {
+ const isActive = index + 1 === currentStep
+ return (
+
+ )
+ })}
+
+ )
+}
diff --git a/frontend/bitmatch/src/components/ui/badge.jsx b/frontend/bitmatch/src/components/ui/badge.jsx
new file mode 100644
index 0000000..f7fe6f2
--- /dev/null
+++ b/frontend/bitmatch/src/components/ui/badge.jsx
@@ -0,0 +1,14 @@
+import React from "react"
+import { cn } from "@/lib/utils"
+
+export function Badge({ className, ...props }) {
+ return (
+
+ )
+}
diff --git a/frontend/bitmatch/src/components/ui/checkbox.jsx b/frontend/bitmatch/src/components/ui/checkbox.jsx
new file mode 100644
index 0000000..b52b792
--- /dev/null
+++ b/frontend/bitmatch/src/components/ui/checkbox.jsx
@@ -0,0 +1,29 @@
+'use client'
+
+import React, { forwardRef } from "react"
+import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
+import { Check } from "lucide-react"
+import { cn } from "@/lib/utils"
+
+const Checkbox = forwardRef((props, ref) => {
+ const { className, ...rest } = props
+
+ return (
+
+
+
+
+
+ )
+})
+
+Checkbox.displayName = "Checkbox"
+
+export { Checkbox }
diff --git a/frontend/bitmatch/src/components/ui/toast.jsx b/frontend/bitmatch/src/components/ui/toast.jsx
new file mode 100644
index 0000000..f8936f1
--- /dev/null
+++ b/frontend/bitmatch/src/components/ui/toast.jsx
@@ -0,0 +1,85 @@
+import * as React from "react"
+import * as ToastPrimitives from "@radix-ui/react-toast"
+import { cva } from "class-variance-authority";
+import { X } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const ToastProvider = ToastPrimitives.Provider
+
+const ToastViewport = React.forwardRef(({ className, ...props }, ref) => (
+
+))
+ToastViewport.displayName = ToastPrimitives.Viewport.displayName
+
+const toastVariants = cva(
+ "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
+ {
+ variants: {
+ variant: {
+ default: "border bg-background text-foreground",
+ destructive:
+ "destructive group border-destructive bg-destructive text-destructive-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+const Toast = React.forwardRef(({ className, variant, ...props }, ref) => {
+ return (
+
+ );
+})
+Toast.displayName = ToastPrimitives.Root.displayName
+
+const ToastAction = React.forwardRef(({ className, ...props }, ref) => (
+
+))
+ToastAction.displayName = ToastPrimitives.Action.displayName
+
+const ToastClose = React.forwardRef(({ className, ...props }, ref) => (
+
+
+
+))
+ToastClose.displayName = ToastPrimitives.Close.displayName
+
+const ToastTitle = React.forwardRef(({ className, ...props }, ref) => (
+
+))
+ToastTitle.displayName = ToastPrimitives.Title.displayName
+
+const ToastDescription = React.forwardRef(({ className, ...props }, ref) => (
+
+))
+ToastDescription.displayName = ToastPrimitives.Description.displayName
+
+export { ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastClose, ToastAction };
diff --git a/frontend/bitmatch/src/components/ui/toaster.jsx b/frontend/bitmatch/src/components/ui/toaster.jsx
new file mode 100644
index 0000000..892a53d
--- /dev/null
+++ b/frontend/bitmatch/src/components/ui/toaster.jsx
@@ -0,0 +1,33 @@
+import { useToast } from "@/hooks/use-toast"
+import {
+ Toast,
+ ToastClose,
+ ToastDescription,
+ ToastProvider,
+ ToastTitle,
+ ToastViewport,
+} from "@/components/ui/toast"
+
+export function Toaster() {
+ const { toasts } = useToast()
+
+ return (
+
+ {toasts.map(function ({ id, title, description, action, ...props }) {
+ return (
+
+
+ {title && {title}}
+ {description && (
+ {description}
+ )}
+
+ {action}
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/frontend/bitmatch/src/hooks/use-toast.js b/frontend/bitmatch/src/hooks/use-toast.js
new file mode 100644
index 0000000..03accc0
--- /dev/null
+++ b/frontend/bitmatch/src/hooks/use-toast.js
@@ -0,0 +1,155 @@
+"use client";
+// Inspired by react-hot-toast library
+import * as React from "react"
+
+const TOAST_LIMIT = 1
+const TOAST_REMOVE_DELAY = 1000000
+
+const actionTypes = {
+ ADD_TOAST: "ADD_TOAST",
+ UPDATE_TOAST: "UPDATE_TOAST",
+ DISMISS_TOAST: "DISMISS_TOAST",
+ REMOVE_TOAST: "REMOVE_TOAST"
+}
+
+let count = 0
+
+function genId() {
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
+ return count.toString();
+}
+
+const toastTimeouts = new Map()
+
+const addToRemoveQueue = (toastId) => {
+ if (toastTimeouts.has(toastId)) {
+ return
+ }
+
+ const timeout = setTimeout(() => {
+ toastTimeouts.delete(toastId)
+ dispatch({
+ type: "REMOVE_TOAST",
+ toastId: toastId,
+ })
+ }, TOAST_REMOVE_DELAY)
+
+ toastTimeouts.set(toastId, timeout)
+}
+
+export const reducer = (state, action) => {
+ switch (action.type) {
+ case "ADD_TOAST":
+ return {
+ ...state,
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
+ };
+
+ case "UPDATE_TOAST":
+ return {
+ ...state,
+ toasts: state.toasts.map((t) =>
+ t.id === action.toast.id ? { ...t, ...action.toast } : t),
+ };
+
+ case "DISMISS_TOAST": {
+ const { toastId } = action
+
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
+ // but I'll keep it here for simplicity
+ if (toastId) {
+ addToRemoveQueue(toastId)
+ } else {
+ state.toasts.forEach((toast) => {
+ addToRemoveQueue(toast.id)
+ })
+ }
+
+ return {
+ ...state,
+ toasts: state.toasts.map((t) =>
+ t.id === toastId || toastId === undefined
+ ? {
+ ...t,
+ open: false,
+ }
+ : t),
+ };
+ }
+ case "REMOVE_TOAST":
+ if (action.toastId === undefined) {
+ return {
+ ...state,
+ toasts: [],
+ }
+ }
+ return {
+ ...state,
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
+ };
+ }
+}
+
+const listeners = []
+
+let memoryState = { toasts: [] }
+
+function dispatch(action) {
+ memoryState = reducer(memoryState, action)
+ listeners.forEach((listener) => {
+ listener(memoryState)
+ })
+}
+
+function toast({
+ ...props
+}) {
+ const id = genId()
+
+ const update = (props) =>
+ dispatch({
+ type: "UPDATE_TOAST",
+ toast: { ...props, id },
+ })
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
+
+ dispatch({
+ type: "ADD_TOAST",
+ toast: {
+ ...props,
+ id,
+ open: true,
+ onOpenChange: (open) => {
+ if (!open) dismiss()
+ },
+ },
+ })
+
+ return {
+ id: id,
+ dismiss,
+ update,
+ }
+}
+
+function useToast() {
+ const [state, setState] = React.useState(memoryState)
+
+ React.useEffect(() => {
+ listeners.push(setState)
+ return () => {
+ const index = listeners.indexOf(setState)
+ if (index > -1) {
+ listeners.splice(index, 1)
+ }
+ };
+ }, [state])
+
+ return {
+ ...state,
+ toast,
+ dismiss: (toastId) => dispatch({ type: "DISMISS_TOAST", toastId }),
+ };
+}
+
+export { useToast, toast }
From f71bf61ee29a44a27d68a74c47399fb42f0c8bda Mon Sep 17 00:00:00 2001
From: Rebecca Smith <2145912+Rebeccals@users.noreply.github.com>
Date: Thu, 10 Apr 2025 14:46:13 -0700
Subject: [PATCH 3/6] Fixed onboard container being too small. Added link to
home slideshow.
---
frontend/bitmatch/src/App.jsx | 128 ++++++++--------
.../components/onboarding/CreateProfile.jsx | 6 +-
.../src/components/onboarding/Location.jsx | 8 +-
.../src/components/ui/ImageSlideshow.jsx | 35 +++--
frontend/bitmatch/src/views/HomePage.jsx | 2 +-
frontend/bitmatch/src/views/OnboardPage.jsx | 141 +++++++++---------
6 files changed, 153 insertions(+), 167 deletions(-)
diff --git a/frontend/bitmatch/src/App.jsx b/frontend/bitmatch/src/App.jsx
index 1ed5e5c..0a9947c 100644
--- a/frontend/bitmatch/src/App.jsx
+++ b/frontend/bitmatch/src/App.jsx
@@ -24,93 +24,83 @@ import PositionPage from './components/onboarding/Roles';
import SkillsPage from './components/onboarding/Skills';
import UserPage from './components/onboarding/CreateProfile';
-// Uncomment when ready
-// import BrowsePage from "./views/BrowsePage";
-// import AboutPage from "./views/AboutPage";
-
import "./styles/global.css";
+// AppRoutes is separated for access to hooks
function AppRoutes() {
const location = useLocation();
const { isSignedIn } = useUser();
- const isLanding = location.pathname === "/";
- const layoutClass = !isSignedIn
- ? "py-8"
- : "container mx-auto px-4 py-16 flex pb-6 flex-col items-center justify-center min-h-screen";
+
+ const pathname = location.pathname;
+ const isLanding = pathname === "/" && !isSignedIn;
+ const isOnboard = pathname.startsWith("/onboard");
+
+ const shouldUseContainer = !isLanding && !isOnboard;
+
+ const layoutClass = shouldUseContainer
+ ? "container mx-auto px-4 py-16 pb-6 min-h-screen"
+ : "";
return (
-
-
- {/* Landing or Home */}
-
-
-
-
-
-
-
- >
- }
- />
+ <>
+
- {/* Signed-in only routes */}
-
-
-
- }
- />
-
-
-
- }
- />
-
-
-
- }
- />
+
+
+ {/* Landing or Home */}
+
+
+
+
+
+
+
+ >
+ }
+ />
- {/* Public pages */}
- } />
- } />
+ {/* Signed-in only routes */}
+ }
+ />
+ }
+ />
+ }
+ />
- {/* Onboarding with redirect and nested routes */}
- }>
- } />
- } />
- } />
- } />
- } />
-
+ {/* Public pages */}
+ } />
+ } />
- {/* Uncomment when ready */}
- {/*
- } />
- } />
- */}
-
-
+ {/* Onboarding */}
+
}>
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
+ >
);
}
+// Default export so main.jsx works
export default function App() {
return (
-
-
);
}
diff --git a/frontend/bitmatch/src/components/onboarding/CreateProfile.jsx b/frontend/bitmatch/src/components/onboarding/CreateProfile.jsx
index 19c6ef8..1e059f1 100644
--- a/frontend/bitmatch/src/components/onboarding/CreateProfile.jsx
+++ b/frontend/bitmatch/src/components/onboarding/CreateProfile.jsx
@@ -102,15 +102,15 @@ const CreateProfile = forwardRef(({ onDataChange, formData }, ref) => {
-
+
{colleges.map((college) => (
-
+
{college}
))}
diff --git a/frontend/bitmatch/src/components/onboarding/Location.jsx b/frontend/bitmatch/src/components/onboarding/Location.jsx
index 3c169a4..d99c234 100644
--- a/frontend/bitmatch/src/components/onboarding/Location.jsx
+++ b/frontend/bitmatch/src/components/onboarding/Location.jsx
@@ -11,15 +11,15 @@ import { Checkbox } from '@/components/ui/checkbox'
const Location = forwardRef(({ onDataChange, formData }, ref) => {
const [location, setLocation] = useState(formData?.location || '')
const [preferences, setPreferences] = useState(
- formData?.location_preferences?.filter(p => p !== '') || ["I'm location flexible (REMOTE)"]
+ formData?.location_preferences?.filter(p => p !== '') || ["Remote friendly projects"]
)
const [other, setOther] = useState('')
const [errors, setErrors] = useState({})
const options = [
- "At my location",
- "At my University or College location",
- "I'm location flexible (REMOTE)",
+ "Near my location",
+ "Near my University or College location",
+ "Remote friendly projects",
]
useEffect(() => {
diff --git a/frontend/bitmatch/src/components/ui/ImageSlideshow.jsx b/frontend/bitmatch/src/components/ui/ImageSlideshow.jsx
index 4b972b5..e747b76 100644
--- a/frontend/bitmatch/src/components/ui/ImageSlideshow.jsx
+++ b/frontend/bitmatch/src/components/ui/ImageSlideshow.jsx
@@ -67,26 +67,25 @@ export default function ImageSlideshow({ items }) {
}}
>
{/* Darker Overlay */}
-
-
-
- {slide.title}
-
-
{slide.description}
-
+
))}
diff --git a/frontend/bitmatch/src/views/HomePage.jsx b/frontend/bitmatch/src/views/HomePage.jsx
index eb694c4..efb0bc0 100644
--- a/frontend/bitmatch/src/views/HomePage.jsx
+++ b/frontend/bitmatch/src/views/HomePage.jsx
@@ -56,7 +56,7 @@ export default function Home() {
}
return (
-
+
diff --git a/frontend/bitmatch/src/views/OnboardPage.jsx b/frontend/bitmatch/src/views/OnboardPage.jsx
index c611954..e0c71f3 100644
--- a/frontend/bitmatch/src/views/OnboardPage.jsx
+++ b/frontend/bitmatch/src/views/OnboardPage.jsx
@@ -108,78 +108,75 @@ export default function OnboardPage() {
}
return (
- <>
-
{/* ✅ Required for showing toasts */}
-
-
- {/* Header */}
-
-
{stepTitles[currentStep - 1]}
-
-
- {/* Progress Bar */}
-
-
- {/* Step Indicator */}
-
-
- {/* Main Content */}
-
-
-
- {renderStep()}
-
-
-
- {/* Optional JSON debug preview */}
- {/*
- {JSON.stringify(formData, null, 2)}
- */}
-
-
- {/* Navigation */}
-
- {currentStep > 1 ? (
-
-
- Previous
-
- ) : (
-
- )}
-
- {currentStep < totalSteps ? (
-
- Next
-
-
- ) : (
-
- Submit
-
- )}
-
+
+ {/* Required for showing toasts */}
+ {/* Header */}
+
+
{stepTitles[currentStep - 1]}
- >
+
+ {/* Progress Bar */}
+
+
+ {/* Step Indicator */}
+
+
+ {/* Main Content */}
+
+
+
+ {renderStep()}
+
+
+
+ {/* Optional JSON debug preview */}
+ {/*
+ {JSON.stringify(formData, null, 2)}
+ */}
+
+
+ {/* Navigation */}
+
+ {currentStep > 1 ? (
+
+
+ Previous
+
+ ) : (
+
+ )}
+
+ {currentStep < totalSteps ? (
+
+ Next
+
+
+ ) : (
+
+ Submit
+
+ )}
+
+
)
}
From 92b6202773f0d758feb1e7898776245d6d85c6bc Mon Sep 17 00:00:00 2001
From: Rebecca Smith <2145912+Rebeccals@users.noreply.github.com>
Date: Thu, 10 Apr 2025 14:49:40 -0700
Subject: [PATCH 4/6] changed onboard so its public
---
frontend/bitmatch/src/App.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/bitmatch/src/App.jsx b/frontend/bitmatch/src/App.jsx
index 0a9947c..4431275 100644
--- a/frontend/bitmatch/src/App.jsx
+++ b/frontend/bitmatch/src/App.jsx
@@ -81,7 +81,7 @@ function AppRoutes() {
} />
{/* Onboarding */}
-
}>
+ }>
} />
} />
} />
From ee303c58a17dfecbf33676246e695f7ca55d322c Mon Sep 17 00:00:00 2001
From: Larry La
Date: Sat, 12 Apr 2025 10:16:49 -0700
Subject: [PATCH 5/6] chore: workflow double run fix
---
.github/workflows/deployment.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
index b587b23..742c666 100644
--- a/.github/workflows/deployment.yml
+++ b/.github/workflows/deployment.yml
@@ -6,7 +6,7 @@ on:
- "*"
pull_request:
branches:
- - "*"
+ - main
jobs:
terraform:
From fd00786e0c583183a06bbc19906a3f863503338c Mon Sep 17 00:00:00 2001
From: Larry La
Date: Sat, 12 Apr 2025 10:17:28 -0700
Subject: [PATCH 6/6] chore: double unit test run fix
---
.github/workflows/django-tests.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/django-tests.yml b/.github/workflows/django-tests.yml
index 0eebed1..43c15a9 100644
--- a/.github/workflows/django-tests.yml
+++ b/.github/workflows/django-tests.yml
@@ -6,7 +6,7 @@ on:
- "*"
pull_request:
branches:
- - "*"
+ - main
workflow_dispatch:
jobs: