Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] 수강신청 페이지 모달 플로우 추가 #45

Merged
merged 11 commits into from
Aug 22, 2024
2 changes: 1 addition & 1 deletion apps/client/apis/studyApplyApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { StudyListApiResponseDto } from "types/dtos/applyStudy";

export const studyApplyApi = {
getStudyList: async () => {
const response = await fetcher.get<StudyListApiResponseDto[]>(
const response = await fetcher.get<StudyListApiResponseDto>(
apiPath.applyStudy,
{
next: { tags: [tags.studyApply] },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Flex, styled } from "@styled-system/jsx";
import { Space } from "@wow-class/ui";
import Image from "next/image";

import { AssignmentHistory } from "@/(afterLogin)/my-study/my-assignment/_components";

import { AssignmentHistory } from "./_components";
import { AssignmentContent } from "./_components/AssignmentContent";

const MyAssignmentPage = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client";

import { css } from "@styled-system/css";
import { Flex } from "@styled-system/jsx";
import { Modal, Space, Text } from "@wow-class/ui";
import { studyApplyApi } from "apis/studyApplyApi";
import { tags } from "constants/tags";
import { useEffect, useState } from "react";
import { revalidateTagByName } from "utils/revalidateTagByName";
import Button from "wowds-ui/Button";

const StudyApplication = ({ params }: { params: { studyId: number } }) => {
const studyId = params.studyId;

const [applySuccess, setApplySuccess] = useState(false);
const [studyTitle, setStudyTitle] = useState("");

useEffect(() => {
const fetchStudyData = async () => {
const data = await studyApplyApi.getStudyList();
if (!data) return;
const { studyResponses: studyList } = data;

const selectedStudy = studyList.find(
(study) => study.studyId === Number(studyId)
);
if (selectedStudy) {
setStudyTitle(selectedStudy.title);
}
};

fetchStudyData();
}, [studyId]);

const handleClickApplyButton = async () => {
const result = await studyApplyApi.applyStudy(Number(studyId));
if (result.success) {
revalidateTagByName(tags.studyApply);
setApplySuccess(true);
}
};

return (
<Modal>
<Flex direction="column" textAlign="center" width="21rem">
{applySuccess ? (
<Text typo="h1">
<span className={titleStyle}>{studyTitle}</span>
<br />
신청이 완료되었어요.
</Text>
) : (
<>
<Text typo="h1">
<span className={titleStyle}>{studyTitle}</span>을(를) <br />
신청하시겠습니까?
</Text>
<Space height={22} />
<Text color="sub">한 번에 하나의 강의만 수강할 수 있어요.</Text>
<Space height={28} />
<Button onClick={handleClickApplyButton}>수강 신청하기</Button>
</>
)}
</Flex>
</Modal>
);
};

export default StudyApplication;

const titleStyle = css({
color: "primary",
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"use client";

import { css } from "@styled-system/css";
import { Flex } from "@styled-system/jsx";
import { Modal, Space, Text } from "@wow-class/ui";
import { studyApplyApi } from "apis/studyApplyApi";
import { tags } from "constants/tags";
import { useEffect, useState } from "react";
import { revalidateTagByName } from "utils/revalidateTagByName";
import Button from "wowds-ui/Button";

const StudyCancel = ({ params }: { params: { studyId: number } }) => {
const studyId = params.studyId;
const [cancelSucces, setCancelSuccess] = useState(false);
const [studyTitle, setStudyTitle] = useState("");

useEffect(() => {
const fetchStudyData = async () => {
const data = await studyApplyApi.getStudyList();
if (!data) return;
const { studyResponses: studyList } = data;

const selectedStudy = studyList.find(
(study) => study.studyId === Number(studyId)
);
if (selectedStudy) {
setStudyTitle(selectedStudy.title);
}
};

fetchStudyData();
}, [studyId]);

const handleClickCancelButton = async () => {
const result = await studyApplyApi.cancelStudyApplication(Number(studyId));

if (result.success) {
revalidateTagByName(tags.studyApply);
setCancelSuccess(true);
}
};

return (
<Modal>
<Flex direction="column" textAlign="center" width="21rem">
{cancelSucces ? (
<Text typo="h1">
<span className={titleStyle}>{studyTitle}</span>
<br />
수강이 취소되었어요.
</Text>
) : (
<>
<Text typo="h1">
<span className={titleStyle}>{studyTitle}</span>의 <br />
수강을 취소하시겠습니까?
</Text>
<Space height={38} />
<Button onClick={handleClickCancelButton}>신청 취소하기</Button>
</>
)}
</Flex>
</Modal>
);
};

export default StudyCancel;

const titleStyle = css({
color: "primary",
});
5 changes: 5 additions & 0 deletions apps/client/app/(afterLogin)/study-apply/@modal/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const Default = () => {
return null;
};

export default Default;
ghdtjgus76 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
"use client";

import { css } from "@styled-system/css";
import { Flex, styled } from "@styled-system/jsx";
import { Table, Text } from "@wow-class/ui";
import { parseISODate, splitTime } from "@wow-class/utils";
import { studyApplyApi } from "apis/studyApplyApi";
import { padWithZero, parseISODate } from "@wow-class/utils";
import { dayToKorean } from "constants/dayToKorean";
import { routePath } from "constants/routePath";
import Link from "next/link";
import type { ComponentProps } from "react";
import type { StudyListApiResponseDto } from "types/dtos/applyStudy";
import type { StudyList } from "types/dtos/applyStudy";
import type { StudyType } from "types/entities/common/study";
import Button from "wowds-ui/Button";
import Tag from "wowds-ui/Tag";

interface StudyItemProps {
study: StudyListApiResponseDto;
study: StudyList;
appliedStudyId: null | number;
}

const StudyItem = ({ study }: StudyItemProps) => {
const StudyItem = ({ study, appliedStudyId }: StudyItemProps) => {
const {
studyId,
title,
Expand All @@ -25,66 +24,71 @@ const StudyItem = ({ study }: StudyItemProps) => {
mentorName,
studyType,
dayOfWeek,
startTime: startTimeString,
startTime: { hour: startTimeHour, minute: startTimeMinute },
endTime: { hour: endTimeHour, minute: endTimeMinute },
openingDate: openingDateString,
applicationEndDate: endDateString,
totalWeek,
} = study;

const handleClickApplyButton = async () => {
const result = await studyApplyApi.applyStudy(studyId);

if (!result.success) {
console.error("스터디 신청 실패");
} else {
console.log("스터디 신청 성공");
}
};

const handleClickCancelButton = async () => {
const result = await studyApplyApi.cancelStudyApplication(studyId);

if (!result.success) {
console.error("스터디 신청 실패");
} else {
console.log("스터디 취소 성공");
}
};

const startTime = splitTime(startTimeString);
const openingDate = parseISODate(openingDateString);
const studyTime = `${dayToKorean[dayOfWeek.toUpperCase()]} ${startTime.hours}:${startTime.minutes} - ${
Number(startTime.hours) + 1
}:${startTime.minutes}`;
const endDate = parseISODate(endDateString);
const studyTime = `${dayToKorean[dayOfWeek.toUpperCase()]} ${startTimeHour}:${padWithZero(startTimeMinute)} - ${
endTimeHour
}:${padWithZero(endTimeMinute)}`;

const isApplicable = appliedStudyId === null;
const isCancelable = appliedStudyId === studyId;
const isNotApplicable = !isApplicable && !isCancelable;
return (
<Table>
<Flex direction="column" gap="xxs" justifyContent="center">
<Flex gap="xs">
<Flex className={contentStyle} gap="xs">
<Text typo="h3">{title}</Text>
<Tag color={sessionColors[studyType] ?? "green"} variant="solid1">
{studyType}
</Tag>
</Flex>
<Text color="sub" typo="body2">
{`${introduction} -`}
<Link href={notionLink} target="_blank">
<Link href={notionLink ?? ""} target="_blank">
{notionLink}
</Link>
</Text>
</Flex>
<Text className={textCellStyle}>{mentorName}</Text>
<Text className={textCellStyle}>{studyTime}</Text>
<Text className={textCellStyle}>{totalWeek}주 코스</Text>
<Text className={textCellStyle}>
{`${openingDate.month}.${openingDate.day} 개강`}
</Text>
<Flex direction="column" textAlign="center">
<Text className={textCellStyle}>
{`${openingDate.month}.${openingDate.day} 개강`}
</Text>
{isCancelable && (
<Text color="error" typo="body3">
{`${endDate.month}.${endDate.day} 까지 취소 가능`}
</Text>
)}
</Flex>
<styled.div paddingX="24px">
<Button size="sm" variant="solid" onClick={handleClickApplyButton}>
수강 신청
</Button>
<Button size="sm" variant="solid" onClick={handleClickCancelButton}>
신청 취소
</Button>
{isApplicable && (
<Link href={`${routePath["study-application-modal"]}/${studyId}`}>
<Button size="sm" variant="solid">
수강 신청
</Button>
</Link>
)}
{isCancelable && (
<Link href={`${routePath["study-cancellation-modal"]}/${studyId}`}>
<Button size="sm" variant="solid">
신청 취소
</Button>
</Link>
)}
{isNotApplicable && (
<Button disabled size="sm" variant="solid">
신청 불가
</Button>
)}
</styled.div>
</Table>
);
Expand All @@ -94,7 +98,11 @@ const textCellStyle = css({
paddingX: "28px",
});

const sessionColors: Record<string, ComponentProps<typeof Tag>["color"]> = {
const contentStyle = css({
minWidth: "313px",
});

const sessionColors: Record<StudyType, ComponentProps<typeof Tag>["color"]> = {
"과제 스터디": "green",
"온라인 세션": "blue",
"오프라인 세션": "yellow",
Expand Down
5 changes: 5 additions & 0 deletions apps/client/app/(afterLogin)/study-apply/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const Default = () => {
return null;
};

export default Default;
18 changes: 18 additions & 0 deletions apps/client/app/(afterLogin)/study-apply/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const Layout = ({
children,
modal,
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) => {
return (
<>
<main>
{children}
{modal}
</main>
</>
);
};

export default Layout;
12 changes: 10 additions & 2 deletions apps/client/app/(afterLogin)/study-apply/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { studyApplyApi } from "apis/studyApplyApi";
import StudyItem from "./_components/StudyItem";

const StudyApplyPage = async () => {
const studyList = await studyApplyApi.getStudyList();
const data = await studyApplyApi.getStudyList();

if (!data) return null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P5;
피그마 디자인 상에는 존재하지 않지만 null보다는 수강 신청 가능한 스터디가 없어요. 라고 안내문구라도 내보내는 게 좋을 것 같아용!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아니면 redirect 시켜주는 건 어떤가유? my-study 페이지 같은 곳으로..


const { appliedStudyId, studyResponses: studyList } = data;

return (
<>
Expand All @@ -13,7 +17,11 @@ const StudyApplyPage = async () => {
</Text>
<Space height={19} />
{studyList?.map((study) => (
<StudyItem key={study.studyId} study={study} />
<StudyItem
appliedStudyId={appliedStudyId}
key={study.studyId}
study={study}
/>
))}
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { routePath } from "constants/routePath";
import { redirect } from "next/navigation";

const StudyApplicationPage = () => {
return redirect(routePath["study-apply"]);
};

export default StudyApplicationPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { routePath } from "constants/routePath";
import { redirect } from "next/navigation";

const StudyCancelPage = () => {
return redirect(routePath["study-apply"]);
};

export default StudyCancelPage;
Loading
Loading