Introduction of town council
This commit is contained in:
6272
client/pnpm-lock.yaml
generated
6272
client/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
46
client/src/components/NextUIFormikSelect.tsx
Normal file
46
client/src/components/NextUIFormikSelect.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Select, SelectItem } from "@nextui-org/react";
|
||||
import { useField, useFormikContext } from "formik";
|
||||
|
||||
interface NextUIFormikSelectProps {
|
||||
label: string;
|
||||
name: string;
|
||||
placeholder: string;
|
||||
labelPlacement?: "inside" | "outside";
|
||||
options: Array<{ key: string; label: string }>;
|
||||
}
|
||||
|
||||
const NextUIFormikSelect = ({
|
||||
label,
|
||||
name,
|
||||
placeholder,
|
||||
labelPlacement = "outside",
|
||||
options,
|
||||
}: NextUIFormikSelectProps) => {
|
||||
const [field, meta] = useField(name);
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setFieldValue(name, e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
{...field}
|
||||
label={label}
|
||||
labelPlacement={labelPlacement}
|
||||
placeholder={placeholder}
|
||||
selectedKeys={[field.value]}
|
||||
onChange={handleChange}
|
||||
isInvalid={meta.touched && !!meta.error}
|
||||
errorMessage={meta.touched && meta.error ? meta.error : ""}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<SelectItem key={option.key} value={option.key}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
export default NextUIFormikSelect;
|
||||
@@ -5,8 +5,10 @@ import config from "../config";
|
||||
import NextUIFormikInput from "./NextUIFormikInput";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { popErrorToast } from "../utilities";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import instance from "../security/http";
|
||||
import axios from "axios";
|
||||
import NextUIFormikSelect from "./NextUIFormikSelect";
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
firstName: Yup.string()
|
||||
@@ -31,6 +33,7 @@ const validationSchema = Yup.object({
|
||||
.matches(/^[0-9]+$/, "Phone number must contain only numerical characters")
|
||||
.length(8, "Phone number must be 8 digits")
|
||||
.required("Phone number is required"),
|
||||
townCouncil: Yup.string().trim().max(30).required(),
|
||||
password: Yup.string()
|
||||
.trim()
|
||||
.min(8, "Password must be at least 8 characters")
|
||||
@@ -49,12 +52,14 @@ const validationSchema = Yup.object({
|
||||
export default function SignUpModule() {
|
||||
const navigate = useNavigate();
|
||||
const [step, setStep] = useState(1);
|
||||
const [townCouncils, setTownCouncils] = useState<string[]>([]);
|
||||
|
||||
const initialValues = {
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
phoneNumber: "",
|
||||
townCouncil: "",
|
||||
password: "",
|
||||
terms: false,
|
||||
};
|
||||
@@ -75,6 +80,14 @@ export default function SignUpModule() {
|
||||
const nextStep = () => setStep((prev) => prev + 1);
|
||||
const prevStep = () => setStep((prev) => prev - 1);
|
||||
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(`${config.serverAddress}/users/town-councils-metadata`)
|
||||
.then((values) => {
|
||||
setTownCouncils(JSON.parse(values.data).townCouncils);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-16">
|
||||
<Formik
|
||||
@@ -132,6 +145,18 @@ export default function SignUpModule() {
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
{townCouncils.length > 0 && (
|
||||
<NextUIFormikSelect
|
||||
label="Town council"
|
||||
name="townCouncil"
|
||||
placeholder="Choose the town council you belong to"
|
||||
labelPlacement="outside"
|
||||
options={townCouncils.map((townCouncil) => ({
|
||||
key: townCouncil,
|
||||
label: townCouncil,
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
<div className="flex justify-between">
|
||||
<Button onClick={prevStep} variant="light">
|
||||
Back
|
||||
|
||||
@@ -20,10 +20,13 @@ import { useNavigate } from "react-router-dom";
|
||||
import UserProfilePicture from "./UserProfilePicture";
|
||||
import { popErrorToast, popToast } from "../utilities";
|
||||
import instance from "../security/http";
|
||||
import axios from "axios";
|
||||
import NextUIFormikSelect from "./NextUIFormikSelect";
|
||||
|
||||
export default function UpdateAccountModule() {
|
||||
const navigate = useNavigate();
|
||||
const [userInformation, setUserInformation] = useState<any>();
|
||||
const [townCouncils, setTownCouncils] = useState<string[]>([]);
|
||||
|
||||
const {
|
||||
isOpen: isArchiveDialogOpen,
|
||||
@@ -41,6 +44,11 @@ export default function UpdateAccountModule() {
|
||||
retrieveUserInformation()
|
||||
.then((response) => {
|
||||
setUserInformation(response);
|
||||
axios
|
||||
.get(`${config.serverAddress}/users/town-councils-metadata`)
|
||||
.then((values) => {
|
||||
setTownCouncils(JSON.parse(values.data).townCouncils);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
navigate("/signin");
|
||||
@@ -73,6 +81,7 @@ export default function UpdateAccountModule() {
|
||||
)
|
||||
.length(8, "Phone number must be 8 digits")
|
||||
.required("Phone number is required"),
|
||||
townCouncil: Yup.string().trim().max(30).required(),
|
||||
});
|
||||
|
||||
const handleSubmit = async (values: any) => {
|
||||
@@ -95,6 +104,7 @@ export default function UpdateAccountModule() {
|
||||
lastName: userInformation.lastName || "",
|
||||
email: userInformation.email || "",
|
||||
phoneNumber: userInformation.phoneNumber || "",
|
||||
townCouncil: userInformation.townCouncil || "",
|
||||
}
|
||||
: {
|
||||
id: "",
|
||||
@@ -102,6 +112,7 @@ export default function UpdateAccountModule() {
|
||||
lastName: "",
|
||||
email: "",
|
||||
phoneNumber: "",
|
||||
townCouncil: "",
|
||||
};
|
||||
|
||||
const archiveAccount = () => {
|
||||
@@ -137,7 +148,7 @@ export default function UpdateAccountModule() {
|
||||
return (
|
||||
<div>
|
||||
{userInformation && (
|
||||
<div>
|
||||
<div className="max-w-[800px] mx-auto">
|
||||
<div className="flex flex-col gap-16">
|
||||
<div>
|
||||
<Formik
|
||||
@@ -170,8 +181,8 @@ export default function UpdateAccountModule() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-8">
|
||||
<div className="flex-grow flex sm:flex-row flex-col gap-4 *:w-full *:flex *:flex-col *:gap-4 *:my-auto">
|
||||
<div className="flex flex-row *:my-auto">
|
||||
<div className="w-full *:w-full *:flex *:flex-col *:gap-4 *:my-auto">
|
||||
<div>
|
||||
<NextUIFormikInput
|
||||
label="First Name"
|
||||
@@ -187,8 +198,6 @@ export default function UpdateAccountModule() {
|
||||
placeholder="Doe"
|
||||
labelPlacement="outside"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<NextUIFormikInput
|
||||
label="Email"
|
||||
name="email"
|
||||
@@ -208,13 +217,32 @@ export default function UpdateAccountModule() {
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
{townCouncils.length > 0 && (
|
||||
<NextUIFormikSelect
|
||||
label="Town council"
|
||||
name="townCouncil"
|
||||
placeholder="Choose the town council you belong to"
|
||||
labelPlacement="outside"
|
||||
options={townCouncils.map((townCouncil) => ({
|
||||
key: townCouncil,
|
||||
label: townCouncil,
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<UserProfilePicture
|
||||
userId={userInformation.id}
|
||||
editable={true}
|
||||
/>
|
||||
<div className="w-full flex flex-row justify-center">
|
||||
<div className="flex flex-col gap-8 text-center">
|
||||
<UserProfilePicture
|
||||
userId={userInformation.id}
|
||||
editable={true}
|
||||
/>
|
||||
<p className="font-bold opacity-50 text-sm">
|
||||
Click to select a new
|
||||
<br />
|
||||
profile picture
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -51,7 +51,13 @@ export default function SpringboardPage() {
|
||||
{greeting}, {userInformation.firstName}.
|
||||
</p>
|
||||
<p>
|
||||
Resident of <Link>Bishan-Toa Payoh Town Council</Link>
|
||||
Resident of{" "}
|
||||
<Link>
|
||||
{userInformation.townCouncil.length > 0
|
||||
? userInformation.townCouncil
|
||||
: "Unknown"}{" "}
|
||||
Town Council
|
||||
</Link>
|
||||
</p>
|
||||
<Button
|
||||
className="w-max"
|
||||
|
||||
Reference in New Issue
Block a user