Skip to content

Commit bd12241

Browse files
authored
[Chore] 나의 스터디 페이지 QA 반영 (#128)
* fix: 출석체크 에러 상태 반영 못하는 부분 수정 * fix: 출석 번호 trim 추가 * fix: 출석번호 textarea 줄바꿈 금지 * chore: 메타데이터 타이틀 수정 * design: 제출한 과제 확인 버튼 줄바꿈 안 되도록 수정 * chore: 오늘의 할 일 과제 박스 버튼 비활성화 상태 삭제 * chore: 오늘의 할 일 맨 마지막에 도달 시 캐러셀 버튼 안 보이도록 수정 * chore: 출석체크 모달에서 api fetch 대신 query parameter로 처리하도록 수정 * design: 출석번호 textfield placeholder ui 변경 * chore: 출석 성공한 경우 error 상태 리셋 * chore: 나의 과제 페이지로 이동 시에는 외부 탭으로 열리지 않도록 수정 * chore: 스터디 기본 정보 api 호출 시 cache 속성 no-store로 가져오게 수정
1 parent ae4405a commit bd12241

File tree

14 files changed

+125
-94
lines changed

14 files changed

+125
-94
lines changed

apps/client/apis/myStudyApi.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const myStudyApi = {
2727
`${apiPath.basicStudyInfo}/${studyId}`,
2828
{
2929
next: { tags: [tags.basicStudyInfo] },
30-
cache: "force-cache",
30+
cache: "no-store",
3131
}
3232
);
3333

@@ -44,9 +44,9 @@ export const myStudyApi = {
4444

4545
return response.data;
4646
},
47-
checkAttendance: async (studyId: number, attendanceNumber: string) => {
47+
checkAttendance: async (studyDetailId: number, attendanceNumber: string) => {
4848
const response = await fetcher.post(
49-
`${apiPath.studyDetails}/${studyId}/${apiPath.attend}`,
49+
`${apiPath.studyDetails}/${studyDetailId}/${apiPath.attend}`,
5050
{
5151
attendanceNumber,
5252
}

apps/client/app/(afterLogin)/my-page/layout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Metadata } from "next";
22

33
export const metadata: Metadata = {
4-
title: "마이페이지",
4+
title: "마이 페이지",
55
};
66

77
const Layout = ({

apps/client/app/(afterLogin)/my-study/@modal/(.)attendance-check/page.tsx

+24-7
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import { useModalRoute } from "@wow-class/ui/hooks";
77
import { parseISODate } from "@wow-class/utils";
88
import { myStudyApi } from "apis/myStudyApi";
99
import { tags } from "constants/tags";
10-
import useFetchAttendanceCheckModalInfoData from "hooks/useFetchAttendanceCheckModalInfoData";
10+
import useAttendanceCheckSearchParams from "hooks/useAttendanceCheckSearchParams";
1111
import Image from "next/image";
12+
import type { KeyboardEventHandler } from "react";
1213
import { useState } from "react";
1314
import { revalidateTagByName } from "utils/revalidateTagByName";
1415
import { validateAttendanceNumber } from "utils/validateAttendanceNumber";
@@ -22,9 +23,8 @@ const AttendanceCheckModal = () => {
2223

2324
const { onClose } = useModalRoute();
2425

25-
const {
26-
studyInfo: { currentWeek, studyDetailId, studyName, deadLine },
27-
} = useFetchAttendanceCheckModalInfoData();
26+
const { studyDetailId, studyName, deadLine, currentWeek } =
27+
useAttendanceCheckSearchParams();
2828

2929
const { year, month, day, hours, minutes } = parseISODate(deadLine);
3030

@@ -48,11 +48,17 @@ const AttendanceCheckModal = () => {
4848
};
4949

5050
const handleClickAttendanceCheckButton = async () => {
51-
if (!isAttendanceNumberValid(attendanceNumber)) {
51+
const trimmedAttendanceNumber = attendanceNumber.trim();
52+
53+
if (!isAttendanceNumberValid(trimmedAttendanceNumber)) {
5254
return setError(true);
5355
}
5456

55-
const success = await checkAttendance(studyDetailId, attendanceNumber);
57+
const success = await checkAttendance(
58+
+studyDetailId,
59+
trimmedAttendanceNumber
60+
);
61+
5662
if (!success) {
5763
return setError(true);
5864
}
@@ -62,12 +68,19 @@ const AttendanceCheckModal = () => {
6268

6369
const handleAttendanceSuccess = () => {
6470
setAttended(true);
71+
setError(false);
6572
revalidateTagByName(tags.dailyTask);
6673
setTimeout(() => {
6774
onClose();
6875
}, 500);
6976
};
7077

78+
const handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = (event) => {
79+
if (event.key === "Enter" || event.key === " ") {
80+
event.preventDefault();
81+
}
82+
};
83+
7184
return (
7285
<Modal>
7386
{attended ? (
@@ -136,9 +149,12 @@ const AttendanceCheckModal = () => {
136149
error={error}
137150
helperText={error ? textfieldHelperText : ""}
138151
label="출결번호 입력"
139-
placeholder="Ex. 0000"
152+
placeholder="ex) 0000"
140153
style={textfieldStyle}
141154
value={attendanceNumber}
155+
textareaProps={{
156+
onKeyDown: handleKeyDown,
157+
}}
142158
onChange={handleChangeAttendanceNumber}
143159
/>
144160
<Button
@@ -172,6 +188,7 @@ const attendanceCheckDescriptionStyle = css({
172188
const textfieldStyle = {
173189
height: "89px",
174190
marginBottom: "20px",
191+
whiteSpace: "nowrap",
175192
};
176193

177194
const attendanceCompleteTitleStyle = css({

apps/client/app/(afterLogin)/my-study/_components/AssignmentStatusBox.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ const AssignmentStatusBox = ({
6565
</Flex>
6666
<Button
6767
asProp={Link}
68-
disabled={assignmentSubmissionStatus !== "NOT_SUBMITTED"}
6968
href={routePath["my-assignment"]}
7069
size="lg"
7170
style={assignmentButtonStyle}

apps/client/app/(afterLogin)/my-study/_components/AttendanceStatusBox.tsx

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { css } from "@styled-system/css";
22
import { Flex } from "@styled-system/jsx";
33
import { Text } from "@wow-class/ui";
44
import { padWithZero, parseISODate } from "@wow-class/utils";
5+
import { myStudyApi } from "apis/myStudyApi";
56
import { attendanceStatusMap } from "constants/attendanceStatusMap";
67
import { routePath } from "constants/routePath";
78
import Link from "next/link";
@@ -12,15 +13,28 @@ import Tag from "wowds-ui/Tag";
1213

1314
interface AttendanceStatusBoxProps {
1415
week: number;
16+
studyDetailId: number;
1517
attendanceStatus: AttendanceStatusType;
1618
deadLine: string;
1719
}
1820

19-
const AttendanceStatusBox = ({
21+
const AttendanceStatusBox = async ({
2022
week,
2123
attendanceStatus,
24+
studyDetailId,
2225
deadLine,
2326
}: AttendanceStatusBoxProps) => {
27+
const myOngoingStudyInfoResponse = await myStudyApi.getMyOngoingStudyInfo();
28+
29+
if (!myOngoingStudyInfoResponse?.studyId) {
30+
return null;
31+
}
32+
33+
const basicStudyInfoResponse = await myStudyApi.getBasicStudyInfo(
34+
myOngoingStudyInfoResponse?.studyId
35+
);
36+
const studyName = basicStudyInfoResponse?.title;
37+
2438
const {
2539
year: startYear,
2640
month: startMonth,
@@ -63,7 +77,7 @@ const AttendanceStatusBox = ({
6377
<Button
6478
asProp={Link}
6579
disabled={attendanceStatus === "ATTENDED"}
66-
href={`${routePath["attendance-check"]}`}
80+
href={`${routePath["attendance-check"]}?study-detail-id=${studyDetailId}&week=${week}&study-name=${studyName}&deadline=${deadLine}`}
6781
size="lg"
6882
style={attendanceButtonStyle}
6983
>

apps/client/app/(afterLogin)/my-study/_components/DailyTaskCarousel.tsx

+3-15
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,16 @@
33
import { css } from "@styled-system/css";
44
import { Flex } from "@styled-system/jsx";
55
import useHorizontalScroll from "hooks/useHorizontalScroll";
6+
import useScrollCarouselButtonVisibility from "hooks/useScrollCarouselButtonVisibility";
67
import Image from "next/image";
7-
import { type PropsWithChildren, useEffect, useState } from "react";
8+
import { type PropsWithChildren } from "react";
89

910
const DailyTaskCarousel = ({ children }: PropsWithChildren) => {
10-
const [showRightButton, setShowRightButton] = useState(false);
11-
1211
const itemWidth = 386;
1312

1413
const { containerRef, handleScroll } = useHorizontalScroll();
1514

16-
useEffect(() => {
17-
if (containerRef.current) {
18-
const containerWidth = containerRef.current.offsetWidth;
19-
const totalChildrenWidth = Array.from(containerRef.current.children)
20-
.map((child) => (child as HTMLElement).offsetWidth)
21-
.reduce((acc, width) => acc + width, 0);
22-
23-
if (totalChildrenWidth > containerWidth) {
24-
setShowRightButton(true);
25-
}
26-
}
27-
}, [containerRef]);
15+
const showRightButton = useScrollCarouselButtonVisibility(containerRef);
2816

2917
const handleClickScrollRightButton = () => {
3018
if (containerRef.current) {

apps/client/app/(afterLogin)/my-study/_components/DailyTaskItem.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ const DailyTaskItem = ({
1717
attendanceStatus,
1818
assignmentTitle,
1919
assignmentSubmissionStatus,
20+
studyDetailId,
2021
} = dailyTask;
2122

2223
return todoType === "ATTENDANCE" ? (
2324
<AttendanceStatusBox
2425
attendanceStatus={attendanceStatus || "NOT_ATTENDED"}
2526
deadLine={deadLine}
2627
key={index}
28+
studyDetailId={studyDetailId}
2729
week={week}
2830
/>
2931
) : (

apps/client/app/(afterLogin)/my-study/_components/StudyCurriculum.tsx

+14-10
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,19 @@ const StudyCurriculum = async () => {
5555
curriculumStatus === "CANCELLED" ? "ATTENDED" : attendanceStatus
5656
];
5757

58-
const assignmentButtonText =
59-
assignmentSubmissionStatus === "SUCCESS"
60-
? "제출한 과제 확인"
61-
: "과제 제출하기";
58+
const isAssignmentSubmissionSuccess =
59+
assignmentSubmissionStatus === "SUCCESS";
60+
const assignmentButtonText = isAssignmentSubmissionSuccess
61+
? "제출한 과제 확인"
62+
: "과제 제출하기";
6263
const assignmentButtonHref =
6364
submissionLink ?? routePath["my-assignment"] ?? "";
65+
const assignmentButtonTargetProp = isAssignmentSubmissionSuccess
66+
? "_blank"
67+
: "_self";
68+
const assignmentButtonVariant = isAssignmentSubmissionSuccess
69+
? "outline"
70+
: "solid";
6471

6572
const isCurrentWeek = getIsCurrentWeek(startDate, endDate);
6673

@@ -143,12 +150,8 @@ const StudyCurriculum = async () => {
143150
href={assignmentButtonHref}
144151
size="sm"
145152
style={assignmentButtonStyle}
146-
target="_blank"
147-
variant={
148-
assignmentSubmissionStatus === "SUCCESS"
149-
? "outline"
150-
: "solid"
151-
}
153+
target={assignmentButtonTargetProp}
154+
variant={assignmentButtonVariant}
152155
>
153156
{assignmentButtonText}
154157
</Button>
@@ -198,6 +201,7 @@ const rightColStyle = css({
198201
const assignmentButtonStyle = {
199202
minWidth: "131px",
200203
margin: "21px 25px",
204+
whiteSpace: "nowrap",
201205
};
202206

203207
const weekContainerStyle = css({

apps/client/app/(afterLogin)/my-study/my-assignment/layout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Metadata } from "next";
22

33
export const metadata: Metadata = {
4-
title: "나의 과제 | GDSC Hongik 스터디 서비스, 와우클래스",
4+
title: "나의 과제 | 와우클래스",
55
};
66

77
const Layout = ({ children }: { children: React.ReactNode }) => {

apps/client/app/layout.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ import { JotaiProvider } from "../components/JotaiProvider";
1010

1111
export const metadata: Metadata = {
1212
title: {
13-
default: "와우 클래스 | GDSC Hongik 스터디 서비스",
14-
template: "%s | 와우 클래스",
13+
default: "와우클래스 | GDSC Hongik 스터디 서비스",
14+
template: "%s | 와우클래스",
1515
},
1616
description:
1717
"와우클래스는 GDSC Hongik이 제공하는 스터디 관리 플랫폼입니다. 이 서비스는 정규 스터디 과제 제출, 출석 체크 등 전반적인 스터디 활동을 효율적으로 관리할 수 있는 기능을 제공합니다.",
1818
openGraph: {
19-
title: "와우 클래스 | GDSC Hongik 스터디 서비스",
19+
title: "와우클래스 | GDSC Hongik 스터디 서비스",
2020
description:
2121
"와우클래스는 GDSC Hongik이 제공하는 스터디 관리 플랫폼입니다. 이 서비스는 정규 스터디 과제 제출, 출석 체크 등 전반적인 스터디 활동을 효율적으로 관리할 수 있는 기능을 제공합니다.",
2222
images: ["/images/og-image.png"],
23-
siteName: "와우 클래스 | GDSC Hongik 스터디 서비스",
23+
siteName: "와우클래스 | GDSC Hongik 스터디 서비스",
2424
type: "website",
2525
},
2626
twitter: {
2727
card: "summary_large_image",
28-
title: "와우 클래스 | GDSC Hongik 스터디 서비스",
28+
title: "와우클래스 | GDSC Hongik 스터디 서비스",
2929
description:
3030
"와우클래스는 GDSC Hongik이 제공하는 스터디 관리 플랫폼입니다. 이 서비스는 정규 스터디 과제 제출, 출석 체크 등 전반적인 스터디 활동을 효율적으로 관리할 수 있는 기능을 제공합니다.",
3131
images: ["/images/og-image.png"],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useSearchParams } from "next/navigation";
2+
3+
const useAttendanceCheckSearchParams = () => {
4+
const searchParams = useSearchParams();
5+
6+
const studyDetailId = searchParams.get("study-detail-id") || "0";
7+
const studyName = searchParams.get("study-name");
8+
const deadLine = searchParams.get("deadline") || "";
9+
const currentWeek = searchParams.get("week");
10+
11+
return { studyDetailId, studyName, deadLine, currentWeek };
12+
};
13+
14+
export default useAttendanceCheckSearchParams;

apps/client/hooks/useFetchAttendanceCheckModalInfoData.ts

-49
This file was deleted.

0 commit comments

Comments
 (0)