Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions src/app/lectures/[lectureId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import {
} from "@/shared/lib/enrollmentWindow";
import {
getUserGrade,
isGradeCapacityBlocked as checkGradeCapacityBlocked,
hasNoGradeSeat as checkNoGradeSeat,
usesGradeCapacity,
} from "@/shared/lib/gradeCapacity";
import { getApiErrorMessage } from "@/shared/lib/getApiErrorMessage";
Expand Down Expand Up @@ -207,7 +207,8 @@ export default function LectureDetailPage() {
const isSpeaker =
lecture.speakers?.some((speaker) => speaker.userId === user?.userId) ??
false;
const canDecide = isCreator || isAdmin;
// 대기자를 신청자로 올리는 판단은 학생회 몫입니다. 개설자가 누르면 서버가 403입니다.
const canDecide = isAdmin;
const displayStatus = getDisplayLectureStatus(lecture);

// 학생회 승인 전후. 아직 공개되지 않은 강연은 신청도 마감도 의미가 없어서,
Expand All @@ -229,8 +230,8 @@ export default function LectureDetailPage() {
lecture.totalCapacity,
lecture.capacityByGrade,
);
// 내 학년에 애초에 배정된 자리가 있는지. 자리가 0이면 대기도 받지 않습니다.
const isGradeCapacityBlocked = checkGradeCapacityBlocked({
// 내 학년에 배정된 자리가 아예 없는 강연인지. 신청을 막지는 않고 대기로 받습니다.
const hasNoGradeSeat = checkNoGradeSeat({
totalCapacity: lecture.totalCapacity,
capacityByGrade: lecture.capacityByGrade,
studentNumber: user?.studentNumber,
Expand All @@ -253,9 +254,13 @@ export default function LectureDetailPage() {
});
// 지금 누르면 신청자가 아니라 대기자로 들어가는 상태.
// 마감이 지나도 신청은 받습니다. 다만 무조건 대기로 서고 담당자가 수락해야 확정됩니다.
const isWaitlistOnly = isFull || isMyGradeTaken;
const isWaitlistOnly = isFull || isMyGradeTaken || hasNoGradeSeat;
const myGrade = getUserGrade(user?.studentNumber);
const isPast = displayStatus === "CLOSED" || displayStatus === "UNCONFIRMED";
// 신청을 닫는 것은 끝난 강연뿐입니다. 정원이 덜 찬 채로 마감된 강연(개설
// 불확정)도 대기 신청은 계속 받습니다.
const isLectureEnded = displayStatus === "CLOSED";
// 지난 강연처럼 조용히 보여야 하는 자리(게이지·카운트다운)는 개설 불확정도 함께 묶습니다.
const isPast = isLectureEnded || displayStatus === "UNCONFIRMED";

// 신청은 7교시가 끝나는 16:20부터 받습니다. 개설한 날이 아니라 학생회가
// 수락한 날이 기준입니다.
Expand Down Expand Up @@ -311,13 +316,9 @@ export default function LectureDetailPage() {
<Button variant="waiting" disabled className="w-full py-3">
{isCreator ? "내가 개설한 강연입니다" : "내가 진행하는 강연입니다"}
</Button>
) : isGradeCapacityBlocked ? (
) : isLectureEnded ? (
<Button variant="waiting" disabled className="w-full py-3">
다른 학년만 신청할 수 있습니다
</Button>
) : isPast ? (
<Button variant="waiting" disabled className="w-full py-3">
{displayStatus === "UNCONFIRMED" ? "개설 불확정" : "강연 종료"}
강연 종료
</Button>
) : isBeforeEnrollmentOpen && enrollmentOpenAt ? (
<Button variant="waiting" disabled className="w-full py-3">
Expand Down Expand Up @@ -356,7 +357,7 @@ export default function LectureDetailPage() {
)}
{enrollStatus === "WAITING" && (
<p className="text-center text-xs text-gray-500">
개설자나 학생회가 수락하면 신청이 확정됩니다.
학생회가 수락하면 신청이 확정됩니다.
</p>
)}
</>
Expand Down Expand Up @@ -550,8 +551,11 @@ export default function LectureDetailPage() {
{/* 남은 자리가 있는데 왜 대기로 가는지 버튼만으로는 알 수 없어서 적어 둡니다. */}
{!enrollStatus && isAfterEnrollmentDeadline ? (
<p className="text-center text-xs text-gray-500">
마감 뒤 신청은 대기자로 등록되고, 개설자나 학생회가 수락해야
확정됩니다.
마감 뒤 신청은 대기자로 등록되고, 학생회가 수락해야 확정됩니다.
</p>
) : !enrollStatus && hasNoGradeSeat ? (
<p className="text-center text-xs text-gray-500">
{myGrade}학년에 배정된 자리가 없어 대기자로 등록됩니다.
</p>
) : (
isMyGradeTaken &&
Expand Down
26 changes: 16 additions & 10 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
} from "@/shared/lib/enrollmentWindow";
import {
getUserGrade,
isGradeCapacityBlocked as checkGradeCapacityBlocked,
hasNoGradeSeat as checkNoGradeSeat,
usesGradeCapacity,
} from "@/shared/lib/gradeCapacity";
import {
Expand Down Expand Up @@ -226,8 +226,8 @@ export default function ThisWeekPage() {
lecture.totalCapacity,
lecture.capacityByGrade,
);
// 상세 화면과 같은 규칙으로 내 학년에 배정된 자리가 있는지 봅니다.
const isGradeCapacityBlocked = checkGradeCapacityBlocked({
// 상세 화면과 같은 규칙입니다. 자리가 없어도 막지 않고 대기로 받습니다.
const hasNoGradeSeat = checkNoGradeSeat({
totalCapacity: lecture.totalCapacity,
capacityByGrade: lecture.capacityByGrade,
studentNumber: user?.studentNumber,
Expand All @@ -248,14 +248,15 @@ export default function ThisWeekPage() {
studentNumber: user?.studentNumber,
});
// 지금 누르면 신청자가 아니라 대기자로 들어가는 상태.
const isWaitlistOnly = isFull || isMyGradeTaken;
const isWaitlistOnly = isFull || isMyGradeTaken || hasNoGradeSeat;
const myGrade = getUserGrade(user?.studentNumber);
const isCreator = user?.userId === lecture.creatorId;
// 연사자는 자기 강연에 신청할 수 없습니다. 서버도 403으로 막습니다.
const isSpeaker =
lecture.speakers?.some((speaker) => speaker.userId === user?.userId) ??
false;
const canDecide = isCreator || isAdmin;
// 상세 화면과 같습니다. 수락·거절은 학생회만 할 수 있습니다.
const canDecide = isAdmin;
const otherCount = lectures.length - 1;

// 상세 페이지와 같은 규칙입니다. 신청은 개설한 날이 아니라 학생회가 수락한 날
Expand Down Expand Up @@ -414,9 +415,11 @@ export default function ThisWeekPage() {
<Button variant="waiting" disabled className="w-full py-3">
{isCreator ? "내가 개설한 강연입니다" : "내가 진행하는 강연입니다"}
</Button>
) : isGradeCapacityBlocked ? (
) : displayStatus === "CLOSED" ? (
// 화면을 열어 둔 채 강연 시각을 넘기면 고른 강연은 그대로 남습니다.
// 서버는 종료된 강연의 신청을 받지 않으므로 여기서도 닫습니다.
<Button variant="waiting" disabled className="w-full py-3">
다른 학년만 신청할 수 있습니다
강연 종료
</Button>
) : isBeforeEnrollmentOpen && enrollmentOpenAt ? (
<Button variant="waiting" disabled className="w-full py-3">
Expand Down Expand Up @@ -454,7 +457,7 @@ export default function ThisWeekPage() {
)}
{enrollStatus === "WAITING" && (
<p className="text-center text-xs text-gray-500">
개설자나 학생회가 수락하면 신청이 확정됩니다.
학생회가 수락하면 신청이 확정됩니다.
</p>
)}
</>
Expand All @@ -475,8 +478,11 @@ export default function ThisWeekPage() {
{/* 남은 자리가 있는데 왜 대기로 가는지 버튼만으로는 알 수 없어서 적어 둡니다. */}
{!enrollStatus && isAfterEnrollmentDeadline ? (
<p className="text-center text-xs text-gray-500">
마감 뒤 신청은 대기자로 등록되고, 개설자나 학생회가 수락해야
확정됩니다.
마감 뒤 신청은 대기자로 등록되고, 학생회가 수락해야 확정됩니다.
</p>
) : !enrollStatus && hasNoGradeSeat ? (
<p className="text-center text-xs text-gray-500">
{myGrade}학년에 배정된 자리가 없어 대기자로 등록됩니다.
</p>
) : (
isMyGradeTaken &&
Expand Down
2 changes: 1 addition & 1 deletion src/components/lecture/LectureForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ export default function LectureForm({
받습니다.
</p>
<p className="text-xs text-gray-500">
마감 뒤에 들어온 신청은 대기자로 서고, 개설자나 학생회가 수락해야
마감 뒤에 들어온 신청은 대기자로 서고, 학생회가 수락해야
확정됩니다.
</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/entities/lecture/model/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type RawLectureStatusType = LectureStatusType | "CLOSE";

/**
* 내 신청 상태. 신청하지 않았으면 서버가 null로 내려줍니다.
* REJECTED는 대기자였다가 개설자나 학생회가 거절한 경우입니다.
* REJECTED는 대기자였다가 학생회가 거절한 경우입니다.
*/
export type EnrollmentStatusType = "ENROLLED" | "WAITING" | "REJECTED";

Expand Down
7 changes: 4 additions & 3 deletions src/shared/lib/gradeCapacity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ interface GradeCapacityBlockParams {
/**
* 내 학년에는 애초에 자리를 배정하지 않은 강연인지.
*
* 자리가 배정돼 있는데 같은 학년이 다 채운 경우는 여기서 막지 않습니다.
* 그때는 그 학년 대기로 신청할 수 있습니다.
* 자리가 없다고 신청을 막지는 않습니다. 서버도 대기로 받아 두고, 마감 뒤에
* 학생회가 수락하면 그때 신청자가 됩니다. 자동 승급 대상만 아닐 뿐입니다.
* 자리가 배정돼 있는데 같은 학년이 다 채운 경우는 isMyGradeFull이 봅니다.
*/
export const isGradeCapacityBlocked = ({
export const hasNoGradeSeat = ({
totalCapacity,
capacityByGrade,
studentNumber,
Expand Down
Loading