Enhanced archived account signin experience

This commit is contained in:
2024-06-26 15:00:48 +08:00
parent 161fa3dd1d
commit d3838100e0
6 changed files with 130 additions and 93 deletions

View File

@@ -1,7 +1,7 @@
import { Button, Link } from "@nextui-org/react"; import { Button, Link } from "@nextui-org/react";
import { Formik, Form } from "formik"; import { Formik, Form } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import axios from "axios"; import axios, { AxiosError } from "axios";
import config from "../config"; import config from "../config";
import NextUIFormikInput from "./NextUIFormikInput"; import NextUIFormikInput from "./NextUIFormikInput";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@@ -33,16 +33,15 @@ export default function SignInModule() {
password: "", password: "",
}; };
const handleSubmit = async (values: any) => { const handleSubmit = (values: any) => {
try { axios
const response = await axios.post( .post(config.serverAddress + "/users/login", values)
config.serverAddress + "/users/login", .then((response) => {
values navigate("/springboard/" + response.data.accessToken);
); })
navigate("/springboard/" + response.data.accessToken); .catch((error) => {
} catch (error) { throw ((error as AxiosError).response?.data as any).message;
console.error("Error logging in:", error); });
}
}; };
return ( return (

View File

@@ -1,7 +1,7 @@
import { Button, Checkbox, Link } from "@nextui-org/react"; import { Button, Checkbox, Link } from "@nextui-org/react";
import { Formik, Form, Field, ErrorMessage } from "formik"; import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import axios from "axios"; import axios, { AxiosError } from "axios";
import config from "../config"; import config from "../config";
import NextUIFormikInput from "./NextUIFormikInput"; import NextUIFormikInput from "./NextUIFormikInput";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@@ -64,7 +64,7 @@ export default function SignUpModule() {
); );
console.log("User created successfully:", response.data); console.log("User created successfully:", response.data);
} catch (error) { } catch (error) {
console.error("Error creating user:", error); throw ((error as AxiosError).response?.data as any).message;
} }
}; };

View File

@@ -1,4 +1,4 @@
import axios from "axios"; import axios, { AxiosError } from "axios";
import * as Yup from "yup"; import * as Yup from "yup";
import config from "../config"; import config from "../config";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@@ -18,7 +18,13 @@ export default function UpdateAccountModule({
let [userInformation, setUserInformation] = useState<any>(); let [userInformation, setUserInformation] = useState<any>();
useEffect(() => { useEffect(() => {
retrieveUserInformation(accessToken!, setUserInformation); retrieveUserInformation(accessToken!)
.then((response) => {
setUserInformation(response);
})
.catch(() => {
navigate("/springboard/" + accessToken);
});
}, [accessToken]); }, [accessToken]);
const validationSchema = Yup.object({ const validationSchema = Yup.object({
@@ -63,7 +69,7 @@ export default function UpdateAccountModule({
console.log("User updated successfully:", response.data); console.log("User updated successfully:", response.data);
navigate("/springboard/" + accessToken); navigate("/springboard/" + accessToken);
} catch (error) { } catch (error) {
console.error("Error updating user:", error); throw ((error as AxiosError).response?.data as any).message;
} }
}; };
@@ -95,7 +101,7 @@ export default function UpdateAccountModule({
} }
) )
.then(() => { .then(() => {
navigate("/login"); navigate("/signin");
}) })
.catch((err) => { .catch((err) => {
console.log("Archive failed: " + err); console.log("Archive failed: " + err);

View File

@@ -10,6 +10,7 @@ import { retrieveUserInformation } from "../security/users";
export default function SpringboardPage() { export default function SpringboardPage() {
let { accessToken } = useParams<string>(); // TODO: Replace AT from props with AT from localstorage let { accessToken } = useParams<string>(); // TODO: Replace AT from props with AT from localstorage
let [userInformation, setUserInformation] = useState<any>(); let [userInformation, setUserInformation] = useState<any>();
let [accountUnavailable, setAccountUnavaliable] = useState(false);
let timeOfDay = getTimeOfDay(); let timeOfDay = getTimeOfDay();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -23,63 +24,102 @@ export default function SpringboardPage() {
greeting = "Good evening"; greeting = "Good evening";
} }
useEffect( useEffect(() => {
() => retrieveUserInformation(accessToken!, setUserInformation), retrieveUserInformation(accessToken!)
[] .then((response) => {
); setUserInformation(response);
})
.catch((error) => {
setAccountUnavaliable(true);
});
return;
}, []);
return ( return (
<DefaultLayout> <DefaultLayout>
{userInformation && ( <div>
<div className="flex flex-col w-full"> {userInformation && (
<div className="flex flex-row justify-between p-8 *:my-auto"> <div className="flex flex-col w-full">
<div className="flex flex-col gap-4"> <div className="flex flex-row justify-between p-8 *:my-auto">
<p className="text-3xl font-bold"> <div className="flex flex-col gap-4">
{greeting}, {userInformation.firstName}. <p className="text-3xl font-bold">
</p> {greeting}, {userInformation.firstName}.
<p> </p>
Resident of <Link>Bishan-Toa Payoh Town Council</Link> <p>
</p> Resident of <Link>Bishan-Toa Payoh Town Council</Link>
<Button </p>
className="w-max" <Button
size="sm" className="w-max"
variant="flat" size="sm"
color="primary" variant="flat"
startContent={ color="primary"
<div className="scale-80"> startContent={
<PencilSquareIcon /> <div className="scale-80">
</div> <PencilSquareIcon />
} </div>
onPress={() => { }
navigate("/manage-account/" + accessToken); onPress={() => {
}} navigate("/manage-account/" + accessToken);
> }}
Manage your account >
</Button> Manage your account
</Button>
</div>
<div className="bg-red-500 w-40 h-40 rounded-full"></div>
</div> </div>
<div className="bg-red-500 w-40 h-40 rounded-full"></div> <div className="flex flex-row justify-stretch *:w-full *:h-56 w-full p-4 pt-0 gap-4">
<SpringboardButton
title="Community Forums"
subtitle="Be involved in discussions among your neighbourhood"
></SpringboardButton>
<SpringboardButton
title="Events"
subtitle="Participate in exciting upcoming events around Singapore"
></SpringboardButton>
<SpringboardButton
title="Home Bill Contest"
subtitle="Save resources, win vouchers!"
></SpringboardButton>
<SpringboardButton
title="Karang Guni Scheduling"
subtitle="Arrange doorstep sales for your old gears with Karang Guni"
></SpringboardButton>
</div>
<div className="w-full h-[600px] bg-red-500"></div>
</div> </div>
<div className="flex flex-row justify-stretch *:w-full *:h-56 w-full p-4 pt-0 gap-4"> )}
<SpringboardButton </div>
title="Community Forums" <div>
subtitle="Be involved in discussions among your neighbourhood" {accountUnavailable && (
></SpringboardButton> <div className="flex flex-col">
<SpringboardButton <div className="flex flex-col gap-8 p-4 justify-center bg-red-500 text-center text-white h-96">
title="Events" <p className="text-9xl">🔒</p>
subtitle="Participate in exciting upcoming events around Singapore" <div className="flex flex-col gap-4">
></SpringboardButton> <p className="text-4xl font-bold">Account unavailable.</p>
<SpringboardButton <div className="flex flex-col">
title="Home Bill Contest" <p className="text-xl">
subtitle="Save resources, win vouchers!" This account seems to have been archived.
></SpringboardButton> </p>
<SpringboardButton <p>
title="Karang Guni Scheduling" In order to recover the account, please{" "}
subtitle="Arrange doorstep sales for your old gears with Karang Guni" <Link className="text-white px-1 rounded-md bg-red-400">
></SpringboardButton> contact us
</Link>
</p>
</div>
<div className="w-min mx-auto"></div>
</div>
</div>
<div className="flex flex-col justify-center p-8">
<div className="w-min mx-auto">
<Button color="primary" onPress={() => navigate("/signin")}>
Sign in with a different account
</Button>
</div>
</div>
</div> </div>
<div className="w-full h-[600px] bg-red-500"></div> )}
</div> </div>
)}
</DefaultLayout> </DefaultLayout>
); );
} }

View File

@@ -1,31 +1,23 @@
import axios from "axios"; import axios, { AxiosError } from "axios";
import config from "../config"; import config from "../config";
export function retrieveUserInformation( export async function retrieveUserInformation(accessToken: string) {
accessToken: string, try {
andThen: React.Dispatch<any> let userId = await axios.get(`${config.serverAddress}/users/auth`, {
) {
axios
.get(`${config.serverAddress}/users/auth`, {
headers: { headers: {
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
}, },
})
.then((response) => {
axios
.get(`${config.serverAddress}/users/individual/${response.data.id}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
andThen(response.data);
})
.catch((error) => {
console.error("Error retrieving user information:", error);
});
})
.catch((error) => {
console.error("Error retrieving user ID:", error);
}); });
let userInformation = await axios.get(
`${config.serverAddress}/users/individual/${userId.data.id}`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
return userInformation.data;
} catch (error) {
throw ((error as AxiosError).response?.data as any).message;
}
} }

View File

@@ -96,7 +96,7 @@ router.get("/individual/:id", validateToken, async (req, res) => {
if (user.isArchived) { if (user.isArchived) {
res.status(400).json({ res.status(400).json({
message: `Account ${id} is archived.`, message: `ERR_ACC_IS_ARCHIVED`,
}); });
} else { } else {
res.json(user); res.json(user);