Implemented page system + signup screen

This commit is contained in:
2024-06-24 12:22:10 +08:00
parent 1b79f5c6c6
commit 5eeaade098
10 changed files with 262 additions and 147 deletions

View File

@@ -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 (
<div>
<div className="flex flex-col p-4 gap-8 relative *:w-max">
<h1 className="text-3xl font-bold ">ecoconnect</h1>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ isValid, dirty }) => (
<Form className="flex flex-col gap-4">
<NextUIFormikInput
label="First Name"
name="firstName"
type="text"
placeholder="John"
labelPlacement="outside"
/>
<NextUIFormikInput
label="Last Name"
name="lastName"
type="text"
placeholder="Doe"
labelPlacement="outside"
/>
<NextUIFormikInput
label="Email"
name="email"
type="email"
placeholder="johndoe@email.com"
labelPlacement="outside"
/>
<NextUIFormikInput
label="Phone number"
name="phoneNumber"
type="text"
placeholder="XXXXXXXX"
labelPlacement="outside"
startContent={
<p className="text-sm pr-2 border-r-2 border-neutral-700">
+65
</p>
}
/>
<NextUIFormikInput
label="New Password"
name="password"
type="password"
placeholder=" "
labelPlacement="outside"
/>
<div>
<Field
name="terms"
type="checkbox"
as={Checkbox}
aria-label="Terms and services agreement checkbox"
>
I have read and agreed to the{" "}
<Link href="#">Terms and Services</Link>
</Field>
<ErrorMessage
name="terms"
component="div"
className="text-red-500"
/>
</div>
<Button
type="submit"
color="primary"
isDisabled={!isValid || !dirty}
>
Sign up
</Button>
</Form>
)}
</Formik>
</div>
</div>
<Routes>
<Route element={<HomePage />} path="/" />
<Route element={<SignUpPage />} path="/signup" />
</Routes>
);
}
export default App;

View File

@@ -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 (
<div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ isValid, dirty }) => (
<Form className="flex flex-col gap-4">
<NextUIFormikInput
label="First Name"
name="firstName"
type="text"
placeholder="John"
labelPlacement="outside"
/>
<NextUIFormikInput
label="Last Name"
name="lastName"
type="text"
placeholder="Doe"
labelPlacement="outside"
/>
<NextUIFormikInput
label="Email"
name="email"
type="email"
placeholder="johndoe@email.com"
labelPlacement="outside"
/>
<NextUIFormikInput
label="Phone number"
name="phoneNumber"
type="text"
placeholder="XXXXXXXX"
labelPlacement="outside"
startContent={
<p className="text-sm pr-2 border-r-2 border-neutral-300 dark:border-neutral-700">
+65
</p>
}
/>
<NextUIFormikInput
label="New Password"
name="password"
type="password"
placeholder=" "
labelPlacement="outside"
/>
<div>
<Field
name="terms"
type="checkbox"
as={Checkbox}
aria-label="Terms and services agreement checkbox"
>
I have read and agreed to the{" "}
<Link href="#">Terms and Services</Link>
</Field>
<ErrorMessage
name="terms"
component="div"
className="text-red-500"
/>
</div>
<Button
type="submit"
color="primary"
isDisabled={!isValid || !dirty}
>
Sign up
</Button>
</Form>
)}
</Formik>
</div>
);
}

View File

@@ -0,0 +1,13 @@
import { Link } from "@nextui-org/react";
export default function SingaporeAgencyStrip() {
return (
<div className="h-8 bg-neutral-200 text-black relative">
<div className="flex flex-row gap-2 *:my-auto pl-16 h-full text-sm">
<img src="../assets/Merlion.svg" alt="Merlion Icon" className="w-6" />
<p>A Singapore Government Agency Website</p>
<Link size="sm">How to identify</Link>
</div>
</div>
);
}

View File

@@ -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(
<React.StrictMode>
<NextUIProvider>
<App />
</NextUIProvider>
<BrowserRouter>
<NextUIProvider>
<App />
</NextUIProvider>
</BrowserRouter>
</React.StrictMode>
);

View File

@@ -0,0 +1,18 @@
import { Button } from "@nextui-org/react";
import { useNavigate } from "react-router-dom";
export default function HomePage() {
const navigate = useNavigate();
return (
<div>
<p>Home</p>
<Button
onPress={() => {
navigate("/signup");
}}
>
Sign up!
</Button>
</div>
);
}

View File

@@ -0,0 +1,37 @@
import SignUpModule from "../components/SignUpModule";
import SingaporeAgencyStrip from "../components/SingaporeAgencyStrip";
export default function SignUpPage() {
return (
<div className="absolute inset-0">
<div className="flex flex-col h-full">
<SingaporeAgencyStrip></SingaporeAgencyStrip>
<div className="flex flex-row h-full">
<div className="w-3/5 relative">
<div className="absolute inset-0">
<div className="w-full h-full relative">
<img
src="../assets/SignupScreenBG.png"
alt="HDB flat"
className="w-full h-full object-cover -z-10"
/>
<div className="absolute inset-0 z-10 flex flex-col justify-center">
<div className="w-full text-right text-white flex flex-col gap-6 p-16">
<p className="text-7xl font-semibold">Welcome!</p>
<p className="text-3xl">
Register a new account to access all of HDB Residence
services.
</p>
</div>
</div>
</div>
</div>
</div>
<div className="w-2/5 p-8">
<SignUpModule></SignUpModule>
</div>
</div>
</div>
</div>
);
}