Skip to content

Commit ab8dd37

Browse files
authored
Merge pull request #27 from CS3219-AY2425S1/ben/profile-page
feat: Implement Profile Page
2 parents 83b47d5 + 2e31cc7 commit ab8dd37

File tree

10 files changed

+543
-141
lines changed

10 files changed

+543
-141
lines changed

apps/frontend/src/app/login/page.tsx

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,83 @@
11
"use client";
22
import Header from "@/components/Header/header";
3-
import {
4-
Button,
5-
Input,
6-
Layout,
7-
message,
8-
Form,
9-
} from "antd";
3+
import { Button, Input, Layout, message, Form } from "antd";
104
import { Content } from "antd/es/layout/layout";
115
import "./styles.scss";
126
import { useEffect, useState } from "react";
137
import Link from "next/link";
148
import TextArea from "antd/es/input/TextArea";
15-
import {loginUser} from '@/app/services/user'
16-
import {setToken} from '@/app/services/login-store'
9+
import { loginUser } from "@/app/services/user";
10+
import { setToken } from "@/app/services/login-store";
1711
import { useRouter } from "next/navigation";
1812

1913
type InputFields = {
20-
email: string
21-
password: string
22-
}
14+
email: string;
15+
password: string;
16+
};
2317

2418
export default function Home() {
25-
2619
const [isLoginFailed, setIsLoginFailed] = useState(false);
2720
const router = useRouter();
2821
const [messageApi, contextHolder] = message.useMessage();
29-
22+
3023
const successMessage = (message: string) => {
3124
messageApi.open({
3225
type: "success",
3326
content: message,
3427
});
3528
};
3629

37-
function submitDetails({email, password}: InputFields): void {
38-
loginUser(email, password).then(jwt => {
39-
successMessage("Login successful")
40-
setToken(jwt);
41-
router.push("/");
42-
43-
}).catch(err => {
44-
console.error(err);
45-
setIsLoginFailed(true);
46-
})
30+
function submitDetails({ email, password }: InputFields): void {
31+
loginUser(email, password)
32+
.then((jwt) => {
33+
successMessage("Login successful");
34+
setToken(jwt);
35+
router.push("/");
36+
})
37+
.catch((err) => {
38+
console.error(err);
39+
setIsLoginFailed(true);
40+
});
4741
}
4842

4943
return (
5044
<>
5145
{contextHolder}
5246
<Layout>
53-
<Header/>
47+
<Header selectedKey={undefined} />
5448
<Content>
5549
<div className="login-card">
5650
<h1>Login</h1>
57-
<Form
58-
name="basic"
59-
onFinish={submitDetails}
60-
>
51+
<Form name="basic" onFinish={submitDetails}>
6152
<Form.Item<InputFields>
62-
name="email"
53+
name="email"
6354
rules={[
6455
{
65-
required: true,
66-
message: "Please provide your email."
56+
required: true,
57+
message: "Please provide your email.",
6758
},
6859
]}
6960
>
70-
<Input
71-
type="email"
72-
placeholder="Email"
73-
/>
61+
<Input type="email" placeholder="Email" />
7462
</Form.Item>
7563

7664
<Form.Item<InputFields>
77-
name="password"
78-
rules={[{
79-
required: true,
80-
message: "Please enter your password."
81-
}]}
65+
name="password"
66+
rules={[
67+
{
68+
required: true,
69+
message: "Please enter your password.",
70+
},
71+
]}
8272
>
83-
<Input.Password
84-
placeholder="Password"
85-
/>
73+
<Input.Password placeholder="Password" />
8674
</Form.Item>
8775

88-
<div
89-
style={{visibility: isLoginFailed ? "visible" : "hidden"}}
90-
className="registration-failed-text">This email/username is not valid
76+
<div
77+
style={{ visibility: isLoginFailed ? "visible" : "hidden" }}
78+
className="registration-failed-text"
79+
>
80+
This email/username is not valid
9181
</div>
9282

9383
<Form.Item>
@@ -97,9 +87,7 @@ export default function Home() {
9787
</Form.Item>
9888
</Form>
9989
<p>
100-
Let me <Link
101-
href="/register"
102-
>register</Link>
90+
Let me <Link href="/register">register</Link>
10391
</p>
10492
</div>
10593
</Content>

apps/frontend/src/app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ export default function Home() {
479479
<div>
480480
{contextHolder}
481481
<Layout className="layout">
482-
<Header />
482+
<Header selectedKey={["0"]} />
483483
<Content className="content">
484484
<div className="content-card">
485485
<div className="content-row-1">
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
"use client";
2+
import Header from "@/components/Header/header";
3+
import {
4+
Avatar,
5+
Button,
6+
Col,
7+
Divider,
8+
Form,
9+
Input,
10+
Layout,
11+
message,
12+
Row,
13+
} from "antd";
14+
import { Content } from "antd/es/layout/layout";
15+
import "./styles.scss";
16+
import { EditOutlined, SaveOutlined } from "@ant-design/icons";
17+
import { useEffect, useState } from "react";
18+
import {
19+
UpdateUser,
20+
ValidateUser,
21+
VerifyTokenResponseType,
22+
} from "../services/user";
23+
24+
interface ProfilePageProps {}
25+
26+
const ProfilePage = (props: ProfilePageProps): JSX.Element => {
27+
const [form] = Form.useForm();
28+
const [id, setId] = useState<string | undefined>(undefined);
29+
const [email, setEmail] = useState<string | undefined>(undefined);
30+
const [username, setUsername] = useState<string | undefined>(undefined);
31+
const [isEditable, setIsEditable] = useState<boolean>(false);
32+
const [refresh, setRefresh] = useState<boolean>(false);
33+
const [messageApi, contextHolder] = message.useMessage();
34+
35+
useEffect(() => {
36+
ValidateUser().then((data: VerifyTokenResponseType) => {
37+
form.setFieldsValue({
38+
username: data.data.username,
39+
password: "",
40+
email: data.data.email,
41+
});
42+
setId(data.data.id);
43+
setEmail(data.data.email);
44+
setUsername(data.data.username);
45+
});
46+
}, [refresh]);
47+
48+
const success = (message: string) => {
49+
messageApi.open({
50+
type: "success",
51+
content: message,
52+
});
53+
};
54+
55+
const error = (message: string) => {
56+
messageApi.open({
57+
type: "error",
58+
content: message,
59+
});
60+
};
61+
62+
return (
63+
<Layout className="layout">
64+
{contextHolder}
65+
<Header selectedKey={undefined} />
66+
<Content className="content">
67+
<div className="content-card1">
68+
<div className="gradient-header"></div>
69+
<div className="profile-header">
70+
<div className="left-header">
71+
<Avatar size={80} className="avatar">
72+
{username?.charAt(0).toUpperCase()}
73+
</Avatar>
74+
<div className="name-container">
75+
<div className="username">{username}</div>
76+
<div className="email">{email}</div>
77+
</div>
78+
</div>
79+
<div className="right-header">
80+
{!isEditable && (
81+
<Button
82+
icon={<EditOutlined />}
83+
className="edit-button"
84+
onClick={() => setIsEditable(true)}
85+
>
86+
Edit
87+
</Button>
88+
)}
89+
</div>
90+
</div>
91+
<div className="profile-form">
92+
<Form
93+
form={form}
94+
onFinish={(values) => {
95+
let data = values;
96+
if (!values.password || values.password.trim() === "") {
97+
data = {
98+
username: values.username,
99+
email: values.email,
100+
};
101+
}
102+
UpdateUser(data, id as string)
103+
.then((value) => {
104+
setIsEditable(false);
105+
setRefresh(!refresh);
106+
success("Profile Updated");
107+
})
108+
.catch((errors: Error) => {
109+
error(errors.message);
110+
});
111+
}}
112+
layout="vertical"
113+
disabled={!isEditable}
114+
>
115+
<Row gutter={16}>
116+
<Col span={12}>
117+
<Form.Item
118+
name="username"
119+
label="Username"
120+
rules={[
121+
{
122+
required: true,
123+
message: "Please enter valid username!",
124+
},
125+
]}
126+
>
127+
<Input name="username" />
128+
</Form.Item>
129+
</Col>
130+
<Col span={12}>
131+
<Form.Item
132+
name="email"
133+
label="Email"
134+
rules={[
135+
{
136+
required: true,
137+
message: "Please enter valid email!",
138+
},
139+
]}
140+
>
141+
<Input name="email" type="email" />
142+
</Form.Item>
143+
</Col>
144+
</Row>
145+
<Row gutter={16}>
146+
<Col span={12}>
147+
<Form.Item name="password" label="Password">
148+
<Input.Password
149+
name="password"
150+
type="password"
151+
placeholder="Enter new password"
152+
/>
153+
</Form.Item>
154+
</Col>
155+
</Row>
156+
<Form.Item
157+
style={{ display: "flex", justifyContent: "flex-end" }}
158+
>
159+
{isEditable && (
160+
<>
161+
<Button
162+
className="cancel-button"
163+
onClick={() => {
164+
form.setFieldValue("username", username);
165+
form.setFieldValue("email", email);
166+
form.setFieldValue("password", undefined);
167+
setIsEditable(false);
168+
}}
169+
>
170+
Cancel
171+
</Button>
172+
<Button
173+
icon={<SaveOutlined />}
174+
type="primary"
175+
className="save-button"
176+
htmlType="submit"
177+
>
178+
Save
179+
</Button>
180+
</>
181+
)}
182+
</Form.Item>
183+
</Form>
184+
</div>
185+
</div>
186+
</Content>
187+
</Layout>
188+
);
189+
};
190+
191+
export default ProfilePage;

0 commit comments

Comments
 (0)