diff --git a/backend/package-lock.json b/backend/package-lock.json
index 4a48a510..c41c3e99 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -206,7 +206,6 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -2306,7 +2305,6 @@
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.12.tgz",
"integrity": "sha512-v6U3O01YohHO+IE3EIFXuRuu3VJILWzyMmSYZXpyBbnp0hk0mFyHxK2w3dF4I5WnbwiRbWlEXdeXFvPQ7qaZzw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"file-type": "21.3.0",
"iterare": "1.2.1",
@@ -2339,7 +2337,6 @@
"integrity": "sha512-97DzTYMf5RtGAVvX1cjwpKRiCUpkeQ9CCzSAenqkAhOmNVVFaApbhuw+xrDt13rsCa2hHVOYPrV4dBgOYMJjsA==",
"hasInstallScript": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@nuxt/opencollective": "0.4.1",
"fast-safe-stringify": "2.1.1",
@@ -2423,7 +2420,6 @@
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.12.tgz",
"integrity": "sha512-GYK/vHI0SGz5m8mxr7v3Urx8b9t78Cf/dj5aJMZlGd9/1D9OI1hAl00BaphjEXINUJ/BQLxIlF2zUjrYsd6enQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"cors": "2.8.5",
"express": "5.2.1",
@@ -3197,7 +3193,6 @@
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@@ -4016,7 +4011,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4043,7 +4037,6 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -4510,7 +4503,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -4731,7 +4723,6 @@
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"readdirp": "^4.0.1"
},
@@ -4779,15 +4770,13 @@
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/class-validator": {
"version": "0.14.3",
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz",
"integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/validator": "^13.15.3",
"libphonenumber-js": "^1.11.1",
@@ -6571,7 +6560,6 @@
"integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@jest/core": "30.2.0",
"@jest/types": "30.2.0",
@@ -8154,7 +8142,6 @@
"resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
"integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"passport-strategy": "1.x.x",
"pause": "0.0.1",
@@ -8546,8 +8533,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
- "license": "Apache-2.0",
- "peer": true
+ "license": "Apache-2.0"
},
"node_modules/require-directory": {
"version": "2.1.1",
@@ -8752,7 +8738,6 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -9785,7 +9770,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -10022,7 +10006,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -10246,7 +10229,6 @@
"integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
diff --git a/frontend/src/main-page/Footer.tsx b/frontend/src/Footer.tsx
similarity index 93%
rename from frontend/src/main-page/Footer.tsx
rename to frontend/src/Footer.tsx
index c3effd0a..ad2aa5d9 100644
--- a/frontend/src/main-page/Footer.tsx
+++ b/frontend/src/Footer.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { Group, Text, Link } from "@chakra-ui/react";
-import { FooterText } from "../translations/general";
+import { FooterText } from "./translations/general";
const Footer: React.FC = () => {
return (
diff --git a/frontend/src/Login.tsx b/frontend/src/Login.tsx
deleted file mode 100644
index 9fa1869f..00000000
--- a/frontend/src/Login.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import React, { useState } from "react";
-import { useAuthContext } from "./context/auth/authContext";
-import { observer } from "mobx-react-lite";
-import logo from "./images/logo.svg";
-import { useNavigate } from "react-router-dom";
-import "./external/bcanSatchel/mutators";
-import Button from "./components/Button";
-
-/**
- * Registered users can log in here
- */
-const Login = observer(() => {
- const [email, setEmail] = useState("");
- const [password, setPassword] = useState("");
- const [rememberMe, setRememberMe] = useState(false);
- const [showPassword, setShowPassword] = useState(false);
- const [failure, setFailure] = useState(false);
- const navigate = useNavigate();
- const { login } = useAuthContext();
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
-
- const success = await login(email, password);
-
- if (success) {
- navigate("/main/all-grants");
- } else {
- setFailure(true);
- }
- };
-
- return (
-
- {/*/ Left side: Registration form */}
-
-
- {/*/ Right side: logo */}
-
-
-
-
-
-
- );
-});
-
-export default Login;
diff --git a/frontend/src/RegisterLanding.tsx b/frontend/src/RegisterLanding.tsx
deleted file mode 100644
index b6905fd4..00000000
--- a/frontend/src/RegisterLanding.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Link } from "react-router-dom";
-import logo from "./images/logo.svg";
-import { useAuthContext } from "./context/auth/authContext";
-
-/**
- * Registered user landing page after signing up
- */
-const RegisterLanding = () => {
- const {logout} = useAuthContext();
- return (
-
-
-
-
-
-
-
-
-
- Account registration successful!
-
-
- Thank you for registering. Your account is currently under review by
- our team. You'll receive an email notification once your account has
- been approved. Please try logging in after receiving approval.
-
-
- {
- logout()
- }}
- >
- Back to Login
-
-
-
-
-
- );
-};
-
-export default RegisterLanding;
\ No newline at end of file
diff --git a/frontend/src/components/BrandingPanel.tsx b/frontend/src/components/BrandingPanel.tsx
new file mode 100644
index 00000000..83fff829
--- /dev/null
+++ b/frontend/src/components/BrandingPanel.tsx
@@ -0,0 +1,17 @@
+import logo from "../images/logo.svg";
+
+/**
+ * Right-hand branding panel with orange background and BostonCAN logo.
+ * Uses Tailwind and primary-800 for panel background.
+ */
+export default function BrandingPanel() {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/src/components/Button.tsx b/frontend/src/components/Button.tsx
index f18332cc..a09c4ce7 100644
--- a/frontend/src/components/Button.tsx
+++ b/frontend/src/components/Button.tsx
@@ -20,8 +20,10 @@ export default function Button({ text, onClick, className, logo, logoPosition, d
{logo && logoPosition === 'left' &&
diff --git a/frontend/src/components/InputField.tsx b/frontend/src/components/InputField.tsx
index 17aa45f3..719e827e 100644
--- a/frontend/src/components/InputField.tsx
+++ b/frontend/src/components/InputField.tsx
@@ -27,10 +27,11 @@ export default function InputField({
diff --git a/frontend/src/images/logo.png b/frontend/src/images/logo.png
deleted file mode 100644
index 56724990..00000000
Binary files a/frontend/src/images/logo.png and /dev/null differ
diff --git a/frontend/src/ForgotPassword.tsx b/frontend/src/login/ForgotPassword.tsx
similarity index 57%
rename from frontend/src/ForgotPassword.tsx
rename to frontend/src/login/ForgotPassword.tsx
index 799102fa..b2326c62 100644
--- a/frontend/src/ForgotPassword.tsx
+++ b/frontend/src/login/ForgotPassword.tsx
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
-import logo from "./images/logo.svg";
+import { BrandingPanel, InputField } from "../sign-up";
+import Button from "../components/Button";
/**
* Forgot Password page - allows users to request a password reset email
@@ -22,37 +23,20 @@ const ForgotPassword = () => {
return (
{/* Left side: Forgot Password form */}
-
+
-
Forgot Password
+ Forgot Password
{!submitted ? (
diff --git a/frontend/src/routes/AppRoutes.tsx b/frontend/src/routes/AppRoutes.tsx
index a514b0da..72ce35cd 100644
--- a/frontend/src/routes/AppRoutes.tsx
+++ b/frontend/src/routes/AppRoutes.tsx
@@ -3,13 +3,13 @@ import { Routes, Route, useLocation, Navigate } from "react-router-dom";
import { observer } from "mobx-react-lite";
import { useAuthContext } from "../context/auth/authContext";
import MainPage from "../main-page/MainPage";
-import Login from "../Login";
-import Register from "../Register";
-import ForgotPassword from "../ForgotPassword";
-import RegisterLanding from "../RegisterLanding";
+import Login from "../login/Login";
+import Register from "../sign-up/Register";
+import ForgotPassword from "../login/ForgotPassword";
+import RegisterLanding from "../sign-up/RegisterLanding";
import { getAppStore } from "../external/bcanSatchel/store";
import RestrictedPage from "../main-page/restricted/RestrictedPage";
-import Footer from "../main-page/Footer";
+import Footer from "../Footer";
/**
* AppRoutes:
diff --git a/frontend/src/sign-up/BrandingPanel.tsx b/frontend/src/sign-up/BrandingPanel.tsx
deleted file mode 100644
index 44f4c38e..00000000
--- a/frontend/src/sign-up/BrandingPanel.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import logo from "../images/logo.svg";
-
-/**
- * Right-hand branding panel with orange background and BostonCAN logo.
- * Uses Tailwind and primary-800 for panel background.
- */
-export default function BrandingPanel() {
- return (
-
-
-
- );
-}
diff --git a/frontend/src/sign-up/LoginPrompt.tsx b/frontend/src/sign-up/LoginPrompt.tsx
index 9186c3ce..1c4b93e4 100644
--- a/frontend/src/sign-up/LoginPrompt.tsx
+++ b/frontend/src/sign-up/LoginPrompt.tsx
@@ -8,7 +8,7 @@ export default function LoginPrompt() {
const navigate = useNavigate();
return (
-
+
Already have an account?{" "}
diff --git a/frontend/src/Register.tsx b/frontend/src/sign-up/Register.tsx
similarity index 91%
rename from frontend/src/Register.tsx
rename to frontend/src/sign-up/Register.tsx
index 520817fe..67de4889 100644
--- a/frontend/src/Register.tsx
+++ b/frontend/src/sign-up/Register.tsx
@@ -1,9 +1,8 @@
import React, { useState } from "react";
import { observer } from "mobx-react-lite";
import { useNavigate } from "react-router-dom";
-import { useAuthContext } from "./context/auth/authContext";
-import { SignUpForm, BrandingPanel } from "./sign-up";
-import "./styles/index.css";
+import { useAuthContext } from "../context/auth/authContext";
+import { SignUpForm, BrandingPanel } from ".";
const PASSWORD_REGEX =
/^(?=.*[!@#$%^&*])(?=.*\d)(?=.*[A-Z])(?=.*[a-z]).{8,}$/;
@@ -82,7 +81,7 @@ const Register = observer(() => {
return (
-
+
{
passwordsMatch={values.password === values.passwordRe}
/>
-
diff --git a/frontend/src/sign-up/RegisterLanding.tsx b/frontend/src/sign-up/RegisterLanding.tsx
new file mode 100644
index 00000000..5595d793
--- /dev/null
+++ b/frontend/src/sign-up/RegisterLanding.tsx
@@ -0,0 +1,41 @@
+import { Link } from "react-router-dom";
+import { useAuthContext } from "../context/auth/authContext";
+import Button from "../components/Button";
+import BrandingPanel from "../components/BrandingPanel";
+
+/**
+ * Registered user landing page after signing up
+ */
+const RegisterLanding = () => {
+ const {logout} = useAuthContext();
+ return (
+
+
+
+
+ Account registration successful!
+
+
+ Thank you for registering. Your account is currently under review by
+ our team. You'll receive an email notification once your account has
+ been approved. Please try logging in after receiving approval.
+
+
+ {
+ logout()
+ }}
+ />
+
+
+
+
+
+
+
+ );
+};
+
+export default RegisterLanding;
\ No newline at end of file
diff --git a/frontend/src/sign-up/SignUpButton.tsx b/frontend/src/sign-up/SignUpButton.tsx
index 0ea53f20..8ac22beb 100644
--- a/frontend/src/sign-up/SignUpButton.tsx
+++ b/frontend/src/sign-up/SignUpButton.tsx
@@ -16,10 +16,6 @@ export default function SignUpButton({ disabled }: SignUpButtonProps) {
type="submit"
disabled={disabled}
onClick={() => {}}
- className={`mt-8 w-full text-base font-bold text-white transition-opacity ${
- disabled
- ? "cursor-not-allowed bg-primary-700 opacity-70"
- : "bg-primary-900 hover:opacity-95"
- }`}/>
+ className={`mt-6 w-full bg-primary-900 text-base font-bold text-white`}/>
);
}
diff --git a/frontend/src/sign-up/SignUpForm.tsx b/frontend/src/sign-up/SignUpForm.tsx
index 78d343fc..8f5266fc 100644
--- a/frontend/src/sign-up/SignUpForm.tsx
+++ b/frontend/src/sign-up/SignUpForm.tsx
@@ -45,7 +45,7 @@ export default function SignUpForm({
return (
-
Sign Up
+
Sign Up
@@ -69,11 +69,10 @@ export default function SignUpForm({
/>
-
+
-
+
-
+
onChange("passwordRe", e.target.value)}
- error={errorItem === "password"}
+ error={errorItem === "password" || passwordsMatch === false}
/>
-
+ {!hasError && (
)}
{hasError && error?.message && (
-
+
{error.message}
)}
diff --git a/frontend/src/sign-up/index.ts b/frontend/src/sign-up/index.ts
index 39dca3d0..c6a92ab6 100644
--- a/frontend/src/sign-up/index.ts
+++ b/frontend/src/sign-up/index.ts
@@ -1,4 +1,4 @@
-export { default as BrandingPanel } from "./BrandingPanel";
+export { default as BrandingPanel } from "../components/BrandingPanel";
export { default as InputField } from "../components/InputField";
export { default as LoginPrompt } from "./LoginPrompt";
export { default as PasswordField } from "./PasswordField";