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
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This file is a reference guide for Claude Code (claude.ai/code) when working wit
- **Limit exploration to 2–3 files max.** Do not spend the session on exploration or planning. Once you know the file path and API contract, start writing code immediately.
- **Never fabricate API endpoints.** Do not invent endpoints that don't exist. Always verify real APIs in `src/hooks/queries/`, `src/api/`, `src/api/openapi/` before using them. If not found, leave a TODO placeholder and inform the user.
- **Fix all issues in a single pass during code review.** Do not make multiple passes on the same file. Handle all discovered issues in one pass.
- **Verify frontend ↔ backend alignment before finalizing any API-touching change.** Cross-check against the backend repo at `../study-platform-mvp/`. Specifically confirm: (1) endpoint path and HTTP method, (2) query/path param names and types, (3) response DTO field names, types, and optionality, (4) enum values. Report any mismatch to the user before committing.

### Completion Criteria

Expand Down
345 changes: 184 additions & 161 deletions PROJECT_INDEX.md

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions src/app/(service)/(my)/my-study/completed/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const GroupStudyFormModal = dynamic(
);

interface MemberGroupStudyList extends MemberStudyItem {
type: 'GROUP_STUDY';
type: 'GROUP_STUDY' | 'MENTOR_STUDY';
status: 'COMPLETED';
}

Expand All @@ -25,13 +25,15 @@ export default function CompletedPage() {
const [page, setPage] = useState<number>(1);
const { data, isLoading } = useMemberStudyListV2Query({
memberId,
studyType: 'GROUP_STUDY',
studyType: 'BOTH',
studyStatus: 'COMPLETED',
page,
});

// status가 "COMPLETED"인 스터디 목록
const completedStudyList = (data?.content || []) as MemberGroupStudyList[];
// status가 "COMPLETED"인 스터디 목록 (ONE_ON_ONE_STUDY 제외)
const completedStudyList = (data?.content || []).filter(
(list) => list.type === 'GROUP_STUDY' || list.type === 'MENTOR_STUDY',
) as MemberGroupStudyList[];

if (isLoading) {
return null;
Expand Down
10 changes: 6 additions & 4 deletions src/app/(service)/(my)/my-study/not-completed/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const GroupStudyFormModal = dynamic(
);

interface MemberGroupStudyList extends MemberStudyItem {
type: 'GROUP_STUDY';
type: 'GROUP_STUDY' | 'MENTOR_STUDY';
status: 'RECRUITING' | 'IN_PROGRESS';
}

Expand All @@ -25,13 +25,15 @@ export default function NotCompletedPage() {
const [page, setPage] = useState<number>(1);
const { data, isLoading } = useMemberStudyListV2Query({
memberId,
studyType: 'GROUP_STUDY',
studyType: 'BOTH',
studyStatus: 'NOT_COMPLETED',
page,
});

// status가 "IN_PROGRESS" 또는 "RECRUITMENT"인 스터디 목록
const notCompletedStudyList = (data?.content || []) as MemberGroupStudyList[];
// status가 "IN_PROGRESS" 또는 "RECRUITMENT"인 스터디 목록 (ONE_ON_ONE_STUDY 제외)
const notCompletedStudyList = (data?.content || []).filter(
(s) => s.type === 'GROUP_STUDY' || s.type === 'MENTOR_STUDY',
) as MemberGroupStudyList[];

if (isLoading) {
return null;
Expand Down
15 changes: 8 additions & 7 deletions src/app/(service)/(my)/my-study/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const GroupStudyFormModal = dynamic(
const PREVIEW_LIMIT = 9;

interface MemberGroupStudyList extends MemberStudyItem {
type: 'GROUP_STUDY';
type: 'GROUP_STUDY' | 'MENTOR_STUDY';
}

export default function MyStudy() {
Expand All @@ -28,15 +28,15 @@ export default function MyStudy() {
const { data: notCompletedData, isLoading: isLoadingNotCompleted } =
useMemberStudyListV2Query({
memberId,
studyType: 'GROUP_STUDY',
studyType: 'BOTH',
studyStatus: 'NOT_COMPLETED',
pageSize: 50,
});

const { data: completedData, isLoading: isLoadingCompleted } =
useMemberStudyListV2Query({
memberId,
studyType: 'GROUP_STUDY',
studyType: 'BOTH',
studyStatus: 'COMPLETED',
pageSize: 9,
});
Expand All @@ -51,18 +51,19 @@ export default function MyStudy() {
study.status === 'COMPLETED' ||
(study.endTime && new Date(study.endTime) < now);

const isGroupStudy = (
const isGroupOrMentorStudy = (
study: MemberStudyItem,
): study is MemberGroupStudyList => study.type === 'GROUP_STUDY';
): study is MemberGroupStudyList =>
study.type === 'GROUP_STUDY' || study.type === 'MENTOR_STUDY';

const groupStudies = allNotCompleted.filter(isGroupStudy);
const groupStudies = allNotCompleted.filter(isGroupOrMentorStudy);
const active = groupStudies.filter((s) => !isEnded(s));
const ended = groupStudies.filter(isEnded);

return {
notCompletedStudyList: active.slice(0, PREVIEW_LIMIT),
completedStudyList: [
...(completedData?.content ?? []).filter(isGroupStudy),
...(completedData?.content ?? []).filter(isGroupOrMentorStudy),
...ended,
].slice(0, PREVIEW_LIMIT),
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/lists/completed-group-study-list.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import MyStudyInfoCard from '@/components/my-page/my-study-info-card';
import { MemberStudyItem } from '@/types/api/group-study.types';
import type { MemberStudyItem } from '@/types/api/group-study.types';

interface MemberGroupStudyList extends MemberStudyItem {
type: 'GROUP_STUDY';
type: 'GROUP_STUDY' | 'MENTOR_STUDY';
}

interface CompletedGroupStudyListProps {
Expand Down
4 changes: 2 additions & 2 deletions src/components/lists/not-completed-group-study-list.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import MyStudyInfoCard from '@/components/my-page/my-study-info-card';
import { MemberStudyItem } from '@/types/api/group-study.types';
import type { MemberStudyItem } from '@/types/api/group-study.types';

interface MemberGroupStudyList extends MemberStudyItem {
type: 'GROUP_STUDY';
type: 'GROUP_STUDY' | 'MENTOR_STUDY';
}

interface NotCompletedGroupStudyListProps {
Expand Down
12 changes: 7 additions & 5 deletions src/components/my-page/my-study-info-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import Button from '@/components/common/ui/button';
import type { MemberStudyItem } from '@/types/api/group-study.types';

interface MyStudyInfoCardProps extends MemberStudyItem {
type: 'GROUP_STUDY';
type: 'GROUP_STUDY' | 'MENTOR_STUDY';
}

export default function MyStudyInfoCard({
studyId,
type,
thumbnail,
status,
startTime,
Expand All @@ -24,13 +25,14 @@ export default function MyStudyInfoCard({
}: MyStudyInfoCardProps) {
const startDate = dayjs(startTime).format('YYYY.MM.DD');
const endDate = endTime ? dayjs(endTime).format('YYYY.MM.DD') : null;
const studyHref =
type === 'MENTOR_STUDY'
? `/premium-study/${studyId}`
: `/group-study/${studyId}`;

return (
<li className="flex w-full flex-col gap-100">
<Link
href={`/group-study/${studyId}`}
className="flex w-full flex-col gap-100"
>
<Link href={studyHref} className="flex w-full flex-col gap-100">
<div className="relative">
<Image
src={thumbnail?.resizedImages[0]?.resizedImageUrl ?? undefined}
Expand Down
Loading