diff --git a/client/assets/Merlion.svg b/client/assets/Merlion.svg new file mode 100644 index 0000000..d56d885 --- /dev/null +++ b/client/assets/Merlion.svg @@ -0,0 +1 @@ +sg-crest \ No newline at end of file diff --git a/client/assets/SignupScreenBG.png b/client/assets/SignupScreenBG.png new file mode 100644 index 0000000..f9cc3c2 Binary files /dev/null and b/client/assets/SignupScreenBG.png differ diff --git a/client/package.json b/client/package.json index 861189a..25c1b2a 100644 --- a/client/package.json +++ b/client/package.json @@ -17,6 +17,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", + "react-router-dom": "^6.23.1", "yup": "^1.4.0" }, "devDependencies": { diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index c24058c..8fa88d7 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -26,6 +26,9 @@ dependencies: react-hot-toast: specifier: ^2.4.1 version: 2.4.1(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1) + react-router-dom: + specifier: ^6.23.1 + version: 6.23.1(react-dom@18.3.1)(react@18.3.1) yup: specifier: ^1.4.0 version: 1.4.0 @@ -3092,6 +3095,11 @@ packages: react: 18.3.1 dev: false + /@remix-run/router@1.16.1: + resolution: {integrity: sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==} + engines: {node: '>=14.0.0'} + dev: false + /@rollup/rollup-android-arm-eabi@4.18.0: resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} cpu: [arm] @@ -4717,6 +4725,29 @@ packages: use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1) dev: false + /react-router-dom@6.23.1(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + '@remix-run/router': 1.16.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.23.1(react@18.3.1) + dev: false + + /react-router@6.23.1(react@18.3.1): + resolution: {integrity: sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + dependencies: + '@remix-run/router': 1.16.1 + react: 18.3.1 + dev: false + /react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1): resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} diff --git a/client/src/App.tsx b/client/src/App.tsx index c9b12cb..38e735e 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,148 +1,14 @@ -import { Button, Checkbox, Link } from "@nextui-org/react"; -import { Formik, Form, Field, ErrorMessage } from "formik"; -import * as Yup from "yup"; -import axios from "axios"; -import config from "./config"; -import NextUIFormikInput from "./components/NextUIFormikInput"; - -const validationSchema = Yup.object({ - firstName: Yup.string() - .trim() - .min(1) - .max(100) - .required("First Name is required"), - lastName: Yup.string() - .trim() - .min(1) - .max(100) - .required("Last Name is required"), - email: Yup.string() - .trim() - .lowercase() - .min(5) - .max(69) - .email("Invalid email format") - .required("Email is required"), - phoneNumber: Yup.string() - .trim() - .matches(/^[0-9]+$/, "Phone number must contain only numerical characters") - .length(8, "Phone number must be 8 digits") - .required("Phone number is required"), - password: Yup.string() - .trim() - .min(8, "Password must be at least 8 characters") - .max(69, "Password must be at most 69 characters") - .matches( - /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,}$/, - "Password needs to contain both letters and numbers" - ) - .required("Password is required"), - terms: Yup.boolean().oneOf( - [true], - "You must accept the terms and conditions" - ), -}); - -export default function App() { - const initialValues = { - firstName: "", - lastName: "", - email: "", - phoneNumber: "", - password: "", - terms: false, - }; - - const handleSubmit = async (values: any) => { - try { - const response = await axios.post( - config.serverAddress + "/users/register", - values - ); - console.log("User created successfully:", response.data); - } catch (error) { - console.error("Error creating user:", error); - } - }; +import { Route, Routes } from "react-router-dom"; +import HomePage from "./pages/HomePage"; +import SignUpPage from "./pages/SignUpPage"; +function App() { return ( -
-
-

ecoconnect

- - {({ isValid, dirty }) => ( -
- - - - - +65 -

- } - /> - -
- - I have read and agreed to the{" "} - Terms and Services - - -
- - - )} -
-
-
+ + } path="/" /> + } path="/signup" /> + ); } + +export default App; diff --git a/client/src/components/SignUpModule.tsx b/client/src/components/SignUpModule.tsx new file mode 100644 index 0000000..8159ed6 --- /dev/null +++ b/client/src/components/SignUpModule.tsx @@ -0,0 +1,145 @@ +import { Button, Checkbox, Link } from "@nextui-org/react"; +import { Formik, Form, Field, ErrorMessage } from "formik"; +import * as Yup from "yup"; +import axios from "axios"; +import config from "../config"; +import NextUIFormikInput from "./NextUIFormikInput"; + +const validationSchema = Yup.object({ + firstName: Yup.string() + .trim() + .min(1) + .max(100) + .required("First Name is required"), + lastName: Yup.string() + .trim() + .min(1) + .max(100) + .required("Last Name is required"), + email: Yup.string() + .trim() + .lowercase() + .min(5) + .max(69) + .email("Invalid email format") + .required("Email is required"), + phoneNumber: Yup.string() + .trim() + .matches(/^[0-9]+$/, "Phone number must contain only numerical characters") + .length(8, "Phone number must be 8 digits") + .required("Phone number is required"), + password: Yup.string() + .trim() + .min(8, "Password must be at least 8 characters") + .max(69, "Password must be at most 69 characters") + .matches( + /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,}$/, + "Password needs to contain both letters and numbers" + ) + .required("Password is required"), + terms: Yup.boolean().oneOf( + [true], + "You must accept the terms and conditions" + ), +}); + +export default function SignUpModule() { + const initialValues = { + firstName: "", + lastName: "", + email: "", + phoneNumber: "", + password: "", + terms: false, + }; + + const handleSubmit = async (values: any) => { + try { + const response = await axios.post( + config.serverAddress + "/users/register", + values + ); + console.log("User created successfully:", response.data); + } catch (error) { + console.error("Error creating user:", error); + } + }; + + return ( +
+ + {({ isValid, dirty }) => ( +
+ + + + + +65 +

+ } + /> + +
+ + I have read and agreed to the{" "} + Terms and Services + + +
+ + + )} +
+
+ ); +} diff --git a/client/src/components/SingaporeAgencyStrip.tsx b/client/src/components/SingaporeAgencyStrip.tsx new file mode 100644 index 0000000..431e095 --- /dev/null +++ b/client/src/components/SingaporeAgencyStrip.tsx @@ -0,0 +1,13 @@ +import { Link } from "@nextui-org/react"; + +export default function SingaporeAgencyStrip() { + return ( +
+
+ Merlion Icon +

A Singapore Government Agency Website

+ How to identify +
+
+ ); +} diff --git a/client/src/main.tsx b/client/src/main.tsx index 1a95ad6..9ccd80e 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -1,13 +1,16 @@ import React from "react"; import ReactDOM from "react-dom/client"; import { NextUIProvider } from "@nextui-org/react"; +import { BrowserRouter } from "react-router-dom"; import App from "./App.tsx"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root")!).render( - - - + + + + + ); diff --git a/client/src/pages/HomePage.tsx b/client/src/pages/HomePage.tsx new file mode 100644 index 0000000..4be06d6 --- /dev/null +++ b/client/src/pages/HomePage.tsx @@ -0,0 +1,18 @@ +import { Button } from "@nextui-org/react"; +import { useNavigate } from "react-router-dom"; + +export default function HomePage() { + const navigate = useNavigate(); + return ( +
+

Home

+ +
+ ); +} diff --git a/client/src/pages/SignUpPage.tsx b/client/src/pages/SignUpPage.tsx new file mode 100644 index 0000000..dc8ece4 --- /dev/null +++ b/client/src/pages/SignUpPage.tsx @@ -0,0 +1,37 @@ +import SignUpModule from "../components/SignUpModule"; +import SingaporeAgencyStrip from "../components/SingaporeAgencyStrip"; + +export default function SignUpPage() { + return ( +
+
+ +
+
+
+
+ HDB flat +
+
+

Welcome!

+

+ Register a new account to access all of HDB Residence + services. +

+
+
+
+
+
+
+ +
+
+
+
+ ); +}