Skip to content

Commit b5607c6

Browse files
authored
Merge pull request #263 from LellowMellow/feature/231211-add-api-to-mypage
Feature(#77): ๋งˆ์ด ํŽ˜์ด์ง€ API ์—ฐ๋™ ๊ด€๋ จ ๊ธฐ๋ณธ ๋กœ์ง ์ž‘์—…
2 parents e4427f6 + 02cbb2f commit b5607c6

File tree

5 files changed

+121
-144
lines changed

5 files changed

+121
-144
lines changed

โ€Žfrontend/src/hooks/useAuth.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import axios from "axios";
12
import { useCallback } from "react";
23
import { useNavigate } from "react-router-dom";
3-
import axios from "axios";
4+
45
import { useToast } from "@/components/Toast/useToast";
56

67
const useAuth = () => {
@@ -23,11 +24,11 @@ const useAuth = () => {
2324
}
2425
})
2526
.then((response) => {
26-
localStorage.setItem("username", response.data.username);
27-
localStorage.setItem("email", response.data.email);
27+
const { username, email } = response.data;
28+
localStorage.setItem("username", username);
29+
localStorage.setItem("email", email);
2830
})
2931
.catch((error) => {
30-
console.log(error.response?.status);
3132
if (error.response?.status === 401) {
3233
showToast({ message: "๋กœ๊ทธ์ธ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์–ด์š”.", type: "alert" });
3334
navigate("/userauth");

โ€Žfrontend/src/pages/MyPage/MyPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const MyPage = () => {
55
return (
66
<>
77
<Header type="main" />
8-
<MyPageSection profileImage="" />
8+
<MyPageSection />
99
</>
1010
);
1111
};

โ€Žfrontend/src/pages/MyPage/components/MyPageSection.tsx

Lines changed: 95 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,188 +1,159 @@
1-
import { useState } from "react";
1+
import axios from "axios";
2+
import { useNavigate } from "react-router-dom";
3+
import { useState, useEffect } from "react";
4+
5+
import useAuth from "@/hooks/useAuth";
26
import Button from "@/components/Button/Button";
3-
import ProfileBig from "@/assets/imgs/profileBig.png";
4-
import EnterIcon from "@/assets/svgs/enter.svg?react";
5-
import { useToast } from "@/components/Toast/useToast";
67
import ReplayLectureCard from "./ReplayLectureCard";
8+
import { useToast } from "@/components/Toast/useToast";
79

8-
interface MyPageSectionProps {
9-
profileImage: string;
10-
}
10+
import EnterIcon from "@/assets/svgs/enter.svg?react";
11+
import ProfileBig from "@/assets/imgs/profileBig.png";
12+
import SubLogoOriginal from "@/assets/imgs/subLogoOriginal.png";
1113

12-
const NICKNAME_REGEXP = /^(?![0-9-_.]+$)[๊ฐ€-ํžฃA-Za-z0-9-_.]{1,10}$/;
14+
const NICKNAME_REGEXP = /^[a-zA-Z0-9๊ฐ€-ํžฃ]{3,15}$/;
1315

14-
const DUMMY_DATA = [
15-
{
16-
profileImage: "",
17-
duration: "12:34",
18-
user: "๋ณผ๋ก์ด",
19-
date: "2023.11.11",
20-
title: "ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ์ดˆ",
21-
description: "์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ๋ฐฐ์šฐ๋Š” ์ž…๋ฌธ ๊ฐ•์ขŒ. ์–ธ์–ด ์„ ํƒ๋ถ€ํ„ฐ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ๊นŒ์ง€."
22-
},
23-
{
24-
profileImage: "",
25-
duration: "12:34",
26-
user: "๋ณผ๋ก์ด",
27-
date: "2023.11.12",
28-
title: "์š”๋ฆฌ์˜ ๊ธฐ์ดˆ",
29-
description: "๊ธฐ๋ณธ์ ์ธ ์š”๋ฆฌ ๊ธฐ์ˆ ๊ณผ ์š”๋ฆฌ๋ฒ•์„ ๋ฐฐ์šฐ๋Š” ๊ฐ•์ขŒ. ์ดˆ๋ณด์ž๋ฅผ ์œ„ํ•œ ์‰ฌ์šด ๋ ˆ์‹œํ”ผ์™€ ์š”๋ฆฌ ํŒ ์ œ๊ณต."
30-
},
31-
{
32-
profileImage: "",
33-
duration: "12:34",
34-
user: "๋ณผ๋ก์ด",
35-
date: "2023.11.13",
36-
title: "๋””์ง€ํ„ธ ๋งˆ์ผ€ํŒ…",
37-
description: "๋””์ง€ํ„ธ ๋งˆ์ผ€ํŒ… ์ „๋žต๊ณผ ์†Œ์…œ ๋ฏธ๋””์–ด ํ™œ์šฉ ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ๋Š” ์‹ค์šฉ์ ์ธ ๊ฐ•์ขŒ."
38-
},
39-
{
40-
profileImage: "",
41-
duration: "12:34",
42-
user: "๋ณผ๋ก์ด",
43-
date: "2023.11.14",
44-
title: "์ˆ˜ํ•™์˜ ์ดํ•ด",
45-
description: "๊ธฐ๋ณธ์ ์ธ ์ˆ˜ํ•™ ๊ฐœ๋…๊ณผ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ธฐ์ˆ ์„ ๋ฐฐ์šฐ๋Š” ๊ฐ•์ขŒ. ์ˆ˜ํ•™์— ๋Œ€ํ•œ ๋‘๋ ค์›€์„ ๊ทน๋ณตํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์›€."
46-
},
47-
{
48-
profileImage: "",
49-
duration: "12:34",
50-
user: "๋ณผ๋ก์ด",
51-
date: "2023.11.15",
52-
title: "์—ญ์‚ฌ ํƒํ—˜",
53-
description: "์„ธ๊ณ„ ์—ญ์‚ฌ์˜ ์ฃผ์š” ์‚ฌ๊ฑด๊ณผ ์ธ๋ฌผ์„ ํƒ๊ตฌํ•˜๋Š” ๊ฐ•์ขŒ. ์—ญ์‚ฌ๋ฅผ ํ†ตํ•ด ํ˜„์žฌ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€."
54-
},
55-
{
56-
profileImage: "",
57-
duration: "12:34",
58-
user: "๋ณผ๋ก์ด",
59-
date: "2023.11.16",
60-
title: "์ฐฝ์˜์  ๊ธ€์“ฐ๊ธฐ",
61-
description: "์ฐฝ์˜์ ์ธ ๊ธ€์“ฐ๊ธฐ ๊ธฐ์ˆ ๊ณผ ์•„์ด๋””์–ด ๋ฐœ์ƒ๋ฒ•์„ ๋ฐฐ์šฐ๋Š” ๊ฐ•์ขŒ. ๊ธ€์“ฐ๊ธฐ๋ฅผ ํ†ตํ•œ ์ž๊ธฐ ํ‘œํ˜„ ๋ฐฉ๋ฒ• ํƒ๊ตฌ."
62-
},
63-
{
64-
profileImage: "",
65-
duration: "12:34",
66-
user: "๋ณผ๋ก์ด",
67-
date: "2023.11.17",
68-
title: "์˜ํ™” ๋ถ„์„",
69-
description: "์˜ํ™”์˜ ๊ธฐ์ˆ ์  ์š”์†Œ์™€ ์˜ˆ์ˆ ์  ๊ฐ€์น˜๋ฅผ ๋ถ„์„ํ•˜๋Š” ๊ฐ•์ขŒ. ์˜ํ™”๋ฅผ ๊นŠ์ด ์žˆ๊ฒŒ ์ดํ•ดํ•˜๋Š” ๋ฐฉ๋ฒ• ์ œ๊ณต."
70-
},
71-
{
72-
profileImage: "",
73-
duration: "12:34",
74-
user: "๋ณผ๋ก์ด",
75-
date: "2023.11.18",
76-
title: "ํ”ผํŠธ๋‹ˆ์Šค ๊ฐ€์ด๋“œ",
77-
description: "๊ฑด๊ฐ•ํ•˜๊ณ  ํšจ์œจ์ ์ธ ์šด๋™ ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ๋Š” ๊ฐ•์ขŒ. ๊ฐœ์ธ๋ณ„ ๋งž์ถค ์šด๋™ ๊ณ„ํš ์„ค์ • ๋ฐฉ๋ฒ• ์ œ๊ณต."
78-
},
79-
{
80-
profileImage: "",
81-
duration: "12:34",
82-
user: "๋ณผ๋ก์ด",
83-
date: "2023.11.19",
84-
title: "์‚ฌ์ง„์ˆ  ์ž…๋ฌธ",
85-
description: "๊ธฐ์ดˆ ์‚ฌ์ง„ ๊ธฐ์ˆ ๊ณผ ๊ตฌ๋„, ์กฐ๋ช… ํ™œ์šฉ๋ฒ•์„ ๋ฐฐ์šฐ๋Š” ๊ฐ•์ขŒ. ์‚ฌ์ง„์„ ํ†ตํ•ด ์˜ˆ์ˆ ์  ๊ฐ๊ฐ ํ–ฅ์ƒ."
86-
},
87-
{
88-
profileImage: "",
89-
duration: "12:34",
90-
user: "๋ณผ๋ก์ด",
91-
date: "2023.11.20",
92-
title: "ํ™˜๊ฒฝ ๋ณดํ˜ธ",
93-
description: "ํ™˜๊ฒฝ ๋ณดํ˜ธ์™€ ์ง€์† ๊ฐ€๋Šฅํ•œ ์ƒํ™œ ๋ฐฉ์‹์— ๋Œ€ํ•ด ๋ฐฐ์šฐ๋Š” ๊ฐ•์ขŒ. ์ผ์ƒ์—์„œ ์‹ค์ฒœํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ๋ณดํ˜ธ ํ™œ๋™ ์†Œ๊ฐœ."
94-
}
95-
];
16+
type Lecture = {
17+
date: string;
18+
duration: string;
19+
user: string;
20+
title: string;
21+
description: string;
22+
};
9623

97-
const MyPageSection = ({ profileImage }: MyPageSectionProps) => {
24+
const MyPageSection = () => {
9825
const showToast = useToast();
99-
const [nickname, setNickname] = useState("๋ณผ๋ก์ด");
100-
const [isNicknameEdit, setIsNicknameEdit] = useState(false);
26+
const navigate = useNavigate();
27+
const { checkAuth } = useAuth();
28+
const [username, setUsername] = useState(localStorage.getItem("username") || "");
29+
const [isUsernameEdit, setIsUsernameEdit] = useState(false);
10130
const [isValid, setIsValid] = useState(true);
31+
const [lectureList, setLectureList] = useState<Lecture[]>([]);
32+
33+
useEffect(() => {
34+
checkAuth();
35+
axios
36+
.get(`${import.meta.env.VITE_API_SERVER_URL}/lecture/list`, {
37+
headers: { Authorization: localStorage.getItem("token") }
38+
})
39+
.then((result) => {
40+
setLectureList(result.data);
41+
})
42+
.catch((error) => {
43+
if (error.response.status === 401) {
44+
showToast({ message: "๋กœ๊ทธ์ธ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์–ด์š”.", type: "alert" });
45+
navigate("/userauth");
46+
} else showToast({ message: "์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”.", type: "alert" });
47+
});
48+
}, []);
10249

10350
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
104-
const newNickname = event.target.value;
105-
setNickname(newNickname);
106-
setIsValid(NICKNAME_REGEXP.test(newNickname));
51+
const newUsername = event.target.value;
52+
setUsername(newUsername);
53+
setIsValid(NICKNAME_REGEXP.test(newUsername));
10754
};
10855

10956
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
11057
if (e.key === "Enter") handleEditButtonClicked();
11158
};
11259

11360
const handleEditButtonClicked = () => {
114-
if (isNicknameEdit) {
61+
if (isUsernameEdit) {
11562
if (isValid) {
63+
axios
64+
.post(
65+
`${import.meta.env.VITE_API_SERVER_URL}/profile`,
66+
{ username },
67+
{
68+
headers: { Authorization: localStorage.getItem("token") }
69+
}
70+
)
71+
.then((response) => {
72+
const { username, email } = response.data;
73+
localStorage.setItem("username", username);
74+
localStorage.setItem("email", email);
75+
setUsername(username);
76+
})
77+
.catch((error) => {
78+
if (error.response.status === 401) {
79+
showToast({ message: "๋กœ๊ทธ์ธ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์–ด์š”.", type: "alert" });
80+
navigate("/userauth");
81+
} else showToast({ message: "์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”.", type: "alert" });
82+
});
11683
showToast({ message: "๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝ์„ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.", type: "success" });
117-
setIsNicknameEdit(false);
84+
setIsUsernameEdit(false);
11885
} else {
11986
showToast({ message: "์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ๋‹‰๋„ค์ž„์ž…๋‹ˆ๋‹ค.", type: "alert" });
12087
}
12188
} else {
12289
showToast({ message: "๋‹‰๋„ค์ž„์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.", type: "default" });
123-
setIsNicknameEdit(true);
90+
setIsUsernameEdit(true);
12491
}
12592
};
12693

12794
return (
12895
<div className="flex flex-col items-center my-32 sm:mt-36">
12996
<section className="flex relative w-11/12 max-w-3xl p-6 flex-col items-center gap-6 rounded-2xl border-default shadow-xl">
130-
<img
131-
src={profileImage ? profileImage : ProfileBig}
132-
alt="ํ”„๋กœํ•„ ์ด๋ฏธ์ง€"
133-
className="absolute -top-20 w-40 h-40 sm:w-56 sm:h-56 sm:-top-28"
134-
/>
97+
<img src={ProfileBig} alt="ํ”„๋กœํ•„ ์ด๋ฏธ์ง€" className="absolute -top-20 w-40 h-40 sm:w-56 sm:h-56 sm:-top-28" />
13598

13699
<input
137100
type="text"
138-
value={nickname}
101+
value={username}
139102
onChange={handleChange}
140103
onKeyDown={handleKeyDown}
141-
size={nickname.length + 1 || 3}
104+
size={username.length + 1 || 3}
142105
placeholder="๋‹‰๋„ค์ž„"
143106
maxLength={10}
144-
disabled={!isNicknameEdit}
107+
disabled={!isUsernameEdit}
145108
className="mt-20 sm:mt-28 semibold-32 text-center border-b-2 border-grayscale-gray focus:border-grayscale-black outline-none transition duration-200 rounded-none disabled:border-none disabled:text-grayscale-black disabled:bg-grayscale-white"
146109
/>
147110

148111
<div className="flex flex-col gap-1 justify-center items-center">
149-
{isNicknameEdit ? (
112+
{isUsernameEdit ? (
150113
<>
151114
{" "}
152115
<p className="semibold-18 text-grayscale-darkgray">์‚ฌ์šฉํ•  ๋‹‰๋„ค์ž„์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.</p>
153116
<p className={`medium-12 ${isValid ? "text-boarlog-100" : "text-alert-100"}`}>
154-
ํ•œ๊ธ€, ์˜๋ฌธ, ์ˆซ์ž, -, _, ., ์ด 10์ž ์ด๋‚ด
117+
ํ•œ๊ธ€, ์˜๋ฌธ, ์ˆซ์ž ์ด 10์ž ์ด๋‚ด
155118
</p>
156119
</>
157120
) : (
158-
<p className="semibold-16 text-grayscale-darkgray">[email protected]</p>
121+
<p className="semibold-16 text-grayscale-darkgray">{localStorage.getItem("email")}</p>
159122
)}
160123
</div>
161124

162125
<div className="w-full max-w-sm">
163126
<Button
164127
type="full"
165-
buttonStyle={isNicknameEdit && isValid ? "blue" : "black"}
128+
buttonStyle={isUsernameEdit && isValid ? "blue" : "black"}
166129
onClick={handleEditButtonClicked}
167130
>
168131
<EnterIcon className="fill-grayscale-white" />
169-
{isNicknameEdit ? "๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝ ์™„๋ฃŒํ•˜๊ธฐ" : "๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝํ•˜๊ธฐ"}
132+
{isUsernameEdit ? "๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝ ์™„๋ฃŒํ•˜๊ธฐ" : "๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝํ•˜๊ธฐ"}
170133
</Button>
171134
</div>
172135

173136
<h3 className="mt-12 semibold-32">๊ฐ•์˜ ๋‹ค์‹œ๋ณด๊ธฐ</h3>
174-
<div className="flex flex-col w-full gap-6">
175-
{DUMMY_DATA.map((value, index) => (
176-
<ReplayLectureCard
177-
key={index}
178-
profileImage={value.profileImage}
179-
date={value.date}
180-
duration={value.duration}
181-
user={value.user}
182-
title={value.title}
183-
description={value.description}
184-
/>
185-
))}
137+
<div className="flex flex-col w-full gap-6 items-center">
138+
{lectureList.length ? (
139+
lectureList.map((value, index) => (
140+
<ReplayLectureCard
141+
key={index}
142+
date={value.date}
143+
duration={value.duration}
144+
user={value.user}
145+
title={value.title}
146+
description={value.description}
147+
onClick={() => navigate("/")}
148+
/>
149+
))
150+
) : (
151+
<>
152+
<img className="max-w-sm w-full" src={SubLogoOriginal} />
153+
<p className="semibold-16 text-grayscale-darkgray -mb-4">์•„์ง ์ˆ˜๊ฐ•ํ•œ ๊ฐ•์˜๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์•„์š”.</p>
154+
<p className="semibold-16 text-grayscale-darkgray mb-6">์ƒˆ๋กœ์šด ๊ฐ•์˜๋ฅผ ์ˆ˜๊ฐ•ํ•˜๋Ÿฌ ๊ฐ€๋ณผ๊นŒ์š”?</p>
155+
</>
156+
)}
186157
</div>
187158
</section>
188159
</div>

โ€Žfrontend/src/pages/MyPage/components/ReplayLectureCard.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@ import CalendarIcon from "@/assets/svgs/calendar.svg?react";
44
import PlayIcon from "@/assets/svgs/play.svg?react";
55

66
interface ReplayLectureCardProps {
7-
profileImage: string;
87
date: string;
98
duration: string;
109
user: string;
1110
title: string;
1211
description: string;
12+
onClick: () => void;
1313
}
1414

15-
const ReplayLectureCard = ({ profileImage, date, duration, user, title, description }: ReplayLectureCardProps) => {
15+
const ReplayLectureCard = ({ date, duration, user, title, description, onClick }: ReplayLectureCardProps) => {
1616
return (
17-
<div className="flex flex-col w-full p-6 gap-4 justify-between bg-grayscale-white border-default rounded-xl hover:shadow-xl duration-500 cursor-pointer break-keep">
17+
<div
18+
className="flex flex-col w-full p-6 gap-4 justify-between bg-grayscale-white border-default rounded-xl hover:shadow-xl duration-500 cursor-pointer break-keep"
19+
onClick={onClick}
20+
>
1821
<div className="flex flex-row gap-3 w-full items-center">
19-
<img src={profileImage ? profileImage : ProfileMedium} alt="๊ฐ•์˜ ์ง„ํ–‰์ž ํ”„๋กœํ•„" className="w-12 h-12" />
22+
<img src={ProfileMedium} alt="๊ฐ•์˜ ์ง„ํ–‰์ž ํ”„๋กœํ•„" className="w-12 h-12" />
2023
<div className="flex flex-col w-full gap-1 justify-center items-left semibold-20">
2124
{title}
2225
<div className="flex flex-row gap-1 items-center medium-16 text-grayscale-darkgray">

0 commit comments

Comments
ย (0)