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 */} -
-
-

Log in

-
-
-
-
- -
- setEmail(e.target.value)} - placeholder="Enter your email address" - className="w-full rounded-xl border border-grey-600 bg-white py-3 px-4 text-base placeholder:text-grey-600 focus:outline-none focus:ring-2 focus:ring-primary-900 focus:border-transparent" - /> -
-
-
- -
- setPassword(e.target.value)} - placeholder="Enter your password" - className="w-full rounded-xl border border-grey-600 bg-white py-3 px-4 pr-12 text-base placeholder:text-grey-600 focus:outline-none focus:ring-2 focus:ring-primary-900 focus:border-transparent" - /> - -
-
-
- - {/* Remember Me & Forgot Password */} -
- - -
- - {/* Error Message */} -
- {failure && ( -
- Your password is incorrect or this account doesn't exist. -
- )} -
- -
- -
- - {/*/ Right side: logo */} -
-
- BCAN 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 ( -
-
-
- BCAN Logo -
-
-
-
-

- 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. -

- - - -
-
-
- ); -}; - -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 ( +
+ BCAN Logo +
+ ); +} 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 + @@ -84,14 +68,8 @@ const ForgotPassword = () => { {/* Right side: Logo */} -
-
- BCAN Logo -
+
+
); diff --git a/frontend/src/login/Login.tsx b/frontend/src/login/Login.tsx new file mode 100644 index 00000000..8b45648f --- /dev/null +++ b/frontend/src/login/Login.tsx @@ -0,0 +1,118 @@ +import React, { useState } from "react"; +import { useAuthContext } from "../context/auth/authContext"; +import { observer } from "mobx-react-lite"; +import { useNavigate } from "react-router-dom"; +import "../external/bcanSatchel/mutators"; +import Button from "../components/Button"; +import { BrandingPanel, InputField, PasswordField } from "../sign-up"; + +/** + * Registered users can log in here + */ +const Login = observer(() => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [rememberMe, setRememberMe] = 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 */} +
+
+

Log in

+
+ +
+
+
+ setEmail(e.target.value)} error={failure} /> +
+
+
+
+ setPassword(e.target.value)} + error={failure} + /> +
+
+
+ + {/* Remember Me & Forgot Password */} +
+ + +
+ + {/* Error Message */} +
+ {failure && ( +
+ Your password is incorrect or this account doesn't exist. +
+ )} +
+ +
+ +
+ + {/*/ Right side: logo */} +
+ +
+ + ); +}); + +export default Login; diff --git a/frontend/src/main-page/MainPage.tsx b/frontend/src/main-page/MainPage.tsx index 6e050116..642d5357 100644 --- a/frontend/src/main-page/MainPage.tsx +++ b/frontend/src/main-page/MainPage.tsx @@ -6,7 +6,7 @@ import Users from "./users/Users"; import RestrictedPage from "./restricted/RestrictedPage"; import CashFlowPage from "./cash-flow/CashFlowPage"; import Settings from "./settings/Settings"; -import Footer from "./Footer"; +import Footer from "../Footer"; function MainPage() { return ( diff --git a/frontend/src/main-page/grants/grant-view/CostBenefitAnalysis.tsx b/frontend/src/main-page/grants/grant-view/CostBenefitAnalysis.tsx index 450294f5..4320a1b6 100644 --- a/frontend/src/main-page/grants/grant-view/CostBenefitAnalysis.tsx +++ b/frontend/src/main-page/grants/grant-view/CostBenefitAnalysis.tsx @@ -107,6 +107,7 @@ export const CostBenefitAnalysis: React.FC = ({