Skip to content

Commit e509f9a

Browse files
authored
Merge branch 'staging' into ben/frontend-docker
2 parents d473232 + 8b00680 commit e509f9a

File tree

16 files changed

+640
-9
lines changed

16 files changed

+640
-9
lines changed

apps/frontend/.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# Replace with the corresponding url, credentials and endpoint
2-
NEXT_PUBLIC_API_URL="http://localhost:8080/"
2+
NEXT_PUBLIC_API_URL="http://localhost:8080/"
3+
NEXT_PUBLIC_USER_SERVICE_URL="http://localhost:3001/"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"use client";
2+
import Header from "@/components/Header/header";
3+
import {
4+
Button,
5+
Input,
6+
Layout,
7+
message,
8+
Form,
9+
} from "antd";
10+
import { Content } from "antd/es/layout/layout";
11+
import "./styles.scss";
12+
import { useEffect, useState } from "react";
13+
import Link from "next/link";
14+
import TextArea from "antd/es/input/TextArea";
15+
import {loginUser} from '@/app/services/user'
16+
import {setToken} from '@/app/services/login-store'
17+
import { useRouter } from "next/navigation";
18+
19+
type InputFields = {
20+
email: string
21+
password: string
22+
}
23+
24+
export default function Home() {
25+
26+
const [isLoginFailed, setIsLoginFailed] = useState(false);
27+
const router = useRouter();
28+
const [messageApi, contextHolder] = message.useMessage();
29+
30+
const successMessage = (message: string) => {
31+
messageApi.open({
32+
type: "success",
33+
content: message,
34+
});
35+
};
36+
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+
})
47+
}
48+
49+
return (
50+
<>
51+
{contextHolder}
52+
<Layout>
53+
<Header/>
54+
<Content>
55+
<div className="login-card">
56+
<h1>Login</h1>
57+
<Form
58+
name="basic"
59+
onFinish={submitDetails}
60+
>
61+
<Form.Item<InputFields>
62+
name="email"
63+
rules={[
64+
{
65+
required: true,
66+
message: "Please provide your email."
67+
},
68+
]}
69+
>
70+
<Input
71+
type="email"
72+
placeholder="Email"
73+
/>
74+
</Form.Item>
75+
76+
<Form.Item<InputFields>
77+
name="password"
78+
rules={[{
79+
required: true,
80+
message: "Please enter your password."
81+
}]}
82+
>
83+
<Input.Password
84+
placeholder="Password"
85+
/>
86+
</Form.Item>
87+
88+
<div
89+
style={{visibility: isLoginFailed ? "visible" : "hidden"}}
90+
className="registration-failed-text">This email/username is not valid
91+
</div>
92+
93+
<Form.Item>
94+
<Button type="primary" htmlType="submit">
95+
Login
96+
</Button>
97+
</Form.Item>
98+
</Form>
99+
<p>
100+
Let me <Link
101+
href="/register"
102+
>register</Link>
103+
</p>
104+
</div>
105+
</Content>
106+
</Layout>
107+
</>
108+
);
109+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
.layout {
2+
background: white;
3+
}
4+
5+
.content {
6+
background: white;
7+
}
8+
9+
.content-card {
10+
margin: 4rem 8rem;
11+
padding: 2rem 4rem;
12+
background-color: white;
13+
min-height: 100vh;
14+
border-radius: 30px;
15+
box-shadow: 0px 10px 60px rgba(226, 236, 249, 0.5);
16+
}
17+
18+
.login-card {
19+
/* Rectangle 6698 */
20+
margin: 4rem auto;
21+
padding: 2rem 4rem;
22+
box-sizing: border-box;
23+
max-width: 30rem;
24+
background: #FFFFFF;
25+
border: 2px solid #8A817C;
26+
box-shadow: 3px 4px 4px rgba(0, 0, 0, 0.25);
27+
border-radius: 15px;
28+
}
29+
30+
.registration-failed-text {
31+
color: #f02849;
32+
font-size: 13px;
33+
}
34+
35+
.content-row-1 {
36+
display: flex;
37+
flex-direction: row;
38+
justify-content: space-between;
39+
}
40+
41+
.content-title {
42+
// font-family: "Poppins";
43+
// font-style: normal;
44+
font-weight: 600;
45+
font-size: 22px;
46+
line-height: 33px;
47+
letter-spacing: -0.01em;
48+
color: #000000;
49+
}
50+
51+
.content-filter {
52+
margin: 1.5rem 0;
53+
}
54+
55+
.edit-button {
56+
margin-right: 0.5rem;
57+
}
58+
59+
.filter-button {
60+
margin-left: 8px;
61+
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.02) !important;
62+
}
63+
64+
.clear-button {
65+
width: 100%;
66+
}
67+
68+
.categories-multi-select,
69+
.difficulty-select,
70+
.order-select {
71+
width: 100%;
72+
}
73+
74+
.create-title,
75+
.new-problem-categories-multi-select,
76+
.new-problem-difficulty-select,
77+
.create-description,
78+
.create-problem-id {
79+
width: 100%;
80+
margin: 5px;
81+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
"use client";
2+
import Header from "@/components/Header/header";
3+
import {
4+
Button,
5+
Input,
6+
Layout,
7+
message,
8+
Form,
9+
} from "antd";
10+
import { Content } from "antd/es/layout/layout";
11+
import "./styles.scss";
12+
import { useEffect, useState } from "react";
13+
import Link from "next/link";
14+
import TextArea from "antd/es/input/TextArea";
15+
import { createUser } from '@/app/services/user'
16+
import { useRouter } from "next/navigation";
17+
18+
type InputFields = {
19+
username: string
20+
email: string
21+
password: string
22+
confirmPassword: string
23+
}
24+
25+
export default function Home() {
26+
27+
const [isRegistrationFailed, setIsRegistrationFailed] = useState(false);
28+
const router = useRouter();
29+
const [messageApi, contextHolder] = message.useMessage();
30+
31+
const successMessage = (message: string) => {
32+
messageApi.open({
33+
type: "success",
34+
content: message,
35+
});
36+
};
37+
38+
function submitDetails({username, email, password}: InputFields): void {
39+
createUser(username, email, password)
40+
.then(() => {
41+
successMessage("Account successfully created")
42+
router.push("/login");
43+
}).catch(err => {
44+
console.log(err);
45+
setIsRegistrationFailed(true);
46+
})
47+
}
48+
return (
49+
<>
50+
{contextHolder}
51+
<Layout>
52+
<Header/>
53+
<Content>
54+
<div className="login-card">
55+
<h1>Register</h1>
56+
57+
<Form
58+
name="basic"
59+
style={{ margin: "auto" }}
60+
onFinish={submitDetails}
61+
>
62+
63+
<Form.Item<InputFields>
64+
name="username"
65+
rules={[
66+
{
67+
required: true,
68+
message: "You must provide a username."
69+
},
70+
{
71+
pattern: /^\S+$/,
72+
message: "Please provide a valid username."
73+
},
74+
]}
75+
>
76+
<Input
77+
placeholder="Username"
78+
/>
79+
</Form.Item>
80+
81+
<Form.Item<InputFields>
82+
name="email"
83+
rules={[
84+
{
85+
required: true,
86+
message: "You must provide an email."
87+
},
88+
{
89+
type: "email",
90+
message: "Please provide a valid email address."
91+
},
92+
]}
93+
>
94+
<Input
95+
type="email"
96+
placeholder="Email"
97+
/>
98+
</Form.Item>
99+
100+
<Form.Item<InputFields>
101+
name="password"
102+
rules={[{
103+
required: true,
104+
message: "You must provide a password."
105+
}]}
106+
>
107+
<Input.Password
108+
placeholder="Password"
109+
/>
110+
</Form.Item>
111+
112+
<Form.Item<InputFields>
113+
name="confirmPassword"
114+
rules={[
115+
{
116+
required: true,
117+
message: "Please confirm your password."
118+
},
119+
({getFieldValue}) => ({
120+
validator: async (r, confirmPassword) => {
121+
if (!!confirmPassword && getFieldValue("password") !== confirmPassword) {
122+
throw new Error("Passwords do not match")
123+
}
124+
}
125+
})
126+
]}
127+
>
128+
<Input.Password
129+
placeholder="Confirm Password"
130+
/>
131+
</Form.Item>
132+
133+
<div
134+
style={{visibility: isRegistrationFailed ? "visible" : "hidden"}}
135+
className="registration-failed-text">A user with the same email/username already exists
136+
</div>
137+
138+
<Form.Item>
139+
<Button type="primary" htmlType="submit">
140+
Register
141+
</Button>
142+
</Form.Item>
143+
</Form>
144+
<p>
145+
Let me <Link
146+
href="/login"
147+
>login</Link>
148+
</p>
149+
</div>
150+
</Content>
151+
</Layout>
152+
</>
153+
);
154+
}

0 commit comments

Comments
 (0)