Implemented page system + signup screen
This commit is contained in:
1
client/assets/Merlion.svg
Normal file
1
client/assets/Merlion.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="sg-crest"><title>sg-crest</title><path fill="#C52F19" d="M5.896 11.185c0 0-0.949 1.341 0.294 3.075 0 0 0.196-0.883 2.159-0.883h2.356c2.225 0 3.893-2.126 2.846-4.319 0 0 1.57 0.164 2.095-0.818 0.523-0.981-0.033-1.374-0.818-1.374h-3.959c0 0.704-1.341 0.802-1.341 0h-2.225c0 0-1.669 0-1.701 1.407 0 0 0.377-0.229 0.752-0.261v0.375c0 0-0.458 0.082-0.671 0.197-0.212 0.114-0.523 0.425-0.228 1.227 0.294 0.801 0.409 1.079 0.409 1.079s0.475-0.41 1.244-0.41h0.9c1.602 0 1.308 1.554-0.295 1.554s-1.815-0.85-1.815-0.85z"></path><path fill="#C52F19" d="M14.255 9.566c0 0 0.54 0.033 0.932-0.31 0 0 3.55 2.765-1.717 8.326-5.268 5.562-1.195 9.162-1.195 9.162s-0.948 0.915-0.409 2.699c0 0-2.191-1.237-3.867-3.338-2.422-3.036-3.902-7.681 2.749-11.386 0 0 4.389-2.208 3.506-5.153z"></path><path fill="#C52F19" d="M8.829 6.343c0 0 0.709-1.265 2.355-1.265 1.298 0 1.594-0.666 1.594-0.666s0.566-1.079 3.424-1.079c2.619 0 4.384 0.873 5.812 2.039 0 0-3.85-2.388-7.645 0.971h-5.54z"></path><path fill="#C52F19" d="M24.839 14.348c-0.109-3.948-3.163-8.179-9.728-7.939 6.413-5.431 17.537 6.695 8.375 13.066 0 0 1.533-2.186 1.353-5.126z"></path><path fill="#C52F19" d="M16.093 6.845c8.005-0.24 10.863 9.357 5.693 13.676l-5.191 2.509c0 0-0.676-2.181 1.833-4.734 2.509-2.551 4.929-7.328-2.006-10.469 0 0 0.131-0.654-0.327-0.981z"></path><path fill="#C52F19" d="M15.678 9.004c0 0 0.393-0.371 0.524-0.676 5.954 2.486 5.017 6.697 1.461 10.23-2.181 2.246-1.505 4.668-1.505 4.668s-2.66 1.657-3.577 3.097c0 0-3.852-3.28 1.483-8.724 5.235-5.344 1.614-8.594 1.614-8.594z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
client/assets/SignupScreenBG.png
Normal file
BIN
client/assets/SignupScreenBG.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
@@ -17,6 +17,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
|
"react-router-dom": "^6.23.1",
|
||||||
"yup": "^1.4.0"
|
"yup": "^1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
31
client/pnpm-lock.yaml
generated
31
client/pnpm-lock.yaml
generated
@@ -26,6 +26,9 @@ dependencies:
|
|||||||
react-hot-toast:
|
react-hot-toast:
|
||||||
specifier: ^2.4.1
|
specifier: ^2.4.1
|
||||||
version: 2.4.1(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.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:
|
yup:
|
||||||
specifier: ^1.4.0
|
specifier: ^1.4.0
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
@@ -3092,6 +3095,11 @@ packages:
|
|||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
dev: false
|
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:
|
/@rollup/rollup-android-arm-eabi@4.18.0:
|
||||||
resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==}
|
resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
@@ -4717,6 +4725,29 @@ packages:
|
|||||||
use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1)
|
use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1)
|
||||||
dev: false
|
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):
|
/react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|||||||
@@ -1,148 +1,14 @@
|
|||||||
import { Button, Checkbox, Link } from "@nextui-org/react";
|
import { Route, Routes } from "react-router-dom";
|
||||||
import { Formik, Form, Field, ErrorMessage } from "formik";
|
import HomePage from "./pages/HomePage";
|
||||||
import * as Yup from "yup";
|
import SignUpPage from "./pages/SignUpPage";
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Routes>
|
||||||
<div className="flex flex-col p-4 gap-8 relative *:w-max">
|
<Route element={<HomePage />} path="/" />
|
||||||
<h1 className="text-3xl font-bold ">ecoconnect</h1>
|
<Route element={<SignUpPage />} path="/signup" />
|
||||||
<Formik
|
</Routes>
|
||||||
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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|||||||
145
client/src/components/SignUpModule.tsx
Normal file
145
client/src/components/SignUpModule.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
13
client/src/components/SingaporeAgencyStrip.tsx
Normal file
13
client/src/components/SingaporeAgencyStrip.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { NextUIProvider } from "@nextui-org/react";
|
import { NextUIProvider } from "@nextui-org/react";
|
||||||
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<NextUIProvider>
|
<BrowserRouter>
|
||||||
<App />
|
<NextUIProvider>
|
||||||
</NextUIProvider>
|
<App />
|
||||||
|
</NextUIProvider>
|
||||||
|
</BrowserRouter>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|||||||
18
client/src/pages/HomePage.tsx
Normal file
18
client/src/pages/HomePage.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
37
client/src/pages/SignUpPage.tsx
Normal file
37
client/src/pages/SignUpPage.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user