Skip to content

Commit 2410c20

Browse files
Merge pull request #380 from code-zero-to-one/fix/study
토스트 컴포넌트 추가, 피어리뷰 스레드 댓글 UI 수정
2 parents 327ca4d + b5e0a19 commit 2410c20

25 files changed

Lines changed: 155 additions & 48 deletions

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ CI 파이프라인: lint → typecheck → prettier → build → build-storyboo
3434

3535
### API 레이어
3636

37+
**백엔드 API 문서 (Swagger):**
38+
39+
- 스테이징: https://test-api.zeroone.it.kr/v3/api-docs
40+
- Swagger UI: https://test-api.zeroone.it.kr/swagger-ui/index.html
41+
3742
두 가지 통신 패턴이 공존:
3843

3944
1. **레거시 axios** (`src/api/client/axios.ts`): baseURL `/api/v1/`, 토큰 갱신 큐 구현 (AUTH001 에러 시 갱신 트리거). 커스텀 엔드포인트에 사용.

src/app/(service)/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { Metadata } from 'next';
77
import localFont from 'next/font/local';
88
import React from 'react';
99
import PageViewTracker from '@/components/analytics/page-view-tracker';
10+
import GlobalToast from '@/components/ui/global-toast';
1011
import MainProvider from '@/providers';
1112
import Header from '@/widgets/home/header';
1213

@@ -41,6 +42,7 @@ export default function ServiceLayout({
4142
<head>{GTM_ID && <GoogleTagManager gtmId={GTM_ID} />}</head>
4243
<body className={clsx(pretendard.className, 'min-h-screen w-screen')}>
4344
<MainProvider>
45+
<GlobalToast />
4446
<PageViewTracker />
4547
<div className="flex min-h-screen w-full flex-col overflow-x-auto">
4648
<Header />

src/components/contents/homework-detail-content.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ function EvaluationResult({ evaluation }: { evaluation: EvaluationResponse }) {
192192
</div>
193193
<div className="flex flex-col gap-100">
194194
<span className="font-designer-14b text-text-default">평가 코멘트</span>
195-
<p className="text-text-default font-designer-14r">
195+
<p className="text-text-default font-designer-14r wrap-anywhere whitespace-pre-wrap">
196196
{evaluation.comment}
197197
</p>
198198
</div>
@@ -432,7 +432,9 @@ function PeerReviewItem({ review, homeworkId }: PeerReviewItemProps) {
432432
</div>
433433
</div>
434434
) : (
435-
<p className="text-text-default font-designer-14r">{review.comment}</p>
435+
<p className="text-text-default font-designer-14r wrap-anywhere whitespace-pre-wrap">
436+
{review.comment}
437+
</p>
436438
)}
437439
</div>
438440
);

src/components/modals/create-evaluation-modal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
useCreateEvaluation,
1212
useGetMissionEvaluationGrades,
1313
} from '@/hooks/queries/evaluation-api';
14+
import { useToastStore } from '@/stores/use-toast-store';
1415
import { TextAreaInput } from '../ui/input';
1516

1617
const CreateEvaluationFormSchema = z.object({
@@ -89,6 +90,7 @@ function CreateEvaluationForm({
8990

9091
const { data: grades } = useGetMissionEvaluationGrades();
9192
const { mutate: createEvaluation } = useCreateEvaluation();
93+
const showToast = useToastStore((state) => state.showToast);
9294

9395
const onValidSubmit = (values: CreateEvaluationFormValues) => {
9496
createEvaluation(
@@ -98,11 +100,11 @@ function CreateEvaluationForm({
98100
},
99101
{
100102
onSuccess: () => {
101-
alert('평가가 성공적으로 제출되었습니다!');
103+
showToast('평가가 성공적으로 제출되었습니다!');
102104
onClose();
103105
},
104106
onError: () => {
105-
alert('평가 제출에 실패했습니다. 다시 시도해주세요.');
107+
showToast('평가 제출에 실패했습니다. 다시 시도해주세요.', 'error');
106108
},
107109
},
108110
);

src/components/modals/create-mission-modal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { BaseInput, TextAreaInput } from '@/components/ui/input';
1111
import { Modal } from '@/components/ui/modal';
1212
import { useGroupStudyDetailQuery } from '@/features/study/group/model/use-study-query';
1313
import { useCreateMission, useGetMissions } from '@/hooks/queries/mission-api';
14+
import { useToastStore } from '@/stores/use-toast-store';
1415
import {
1516
createDisabledDateMatcherForMission,
1617
MissionPeriod,
@@ -129,6 +130,7 @@ function CreateMissionForm({
129130
const { handleSubmit, formState, control } = methods;
130131

131132
const { mutate: createMission } = useCreateMission();
133+
const showToast = useToastStore((state) => state.showToast);
132134

133135
const onValidSubmit = (values: CreateMissionFormValues) => {
134136
const startDate = dayjs(values.dateRange.from).format('YYYY-MM-DD');
@@ -148,11 +150,11 @@ function CreateMissionForm({
148150
},
149151
{
150152
onSuccess: () => {
151-
alert('미션이 성공적으로 생성되었습니다!');
153+
showToast('미션이 성공적으로 생성되었습니다!');
152154
onClose();
153155
},
154156
onError: () => {
155-
alert('미션 생성에 실패했습니다. 다시 시도해주세요.');
157+
showToast('미션 생성에 실패했습니다. 다시 시도해주세요.', 'error');
156158
},
157159
},
158160
);

src/components/modals/delete-evaluation-modal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState } from 'react';
22
import { useDeleteEvaluation } from '@/hooks/queries/evaluation-api';
3+
import { useToastStore } from '@/stores/use-toast-store';
34
import Button from '../ui/button';
45
import { Modal } from '../ui/modal';
56

@@ -14,15 +15,16 @@ export default function DeleteEvaluationModal({
1415
const [open, setOpen] = useState<boolean>(false);
1516

1617
const { mutate: deleteEvaluation } = useDeleteEvaluation();
18+
const showToast = useToastStore((state) => state.showToast);
1719

1820
const handleDelete = () => {
1921
deleteEvaluation(evaluationId, {
2022
onSuccess: () => {
21-
alert('평가가 삭제되었습니다.');
23+
showToast('평가가 삭제되었습니다.');
2224
setOpen(false);
2325
},
2426
onError: () => {
25-
alert('평가 삭제에 실패했습니다. 다시 시도해주세요.');
27+
showToast('평가 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
2628
},
2729
});
2830
};

src/components/modals/delete-homework-modal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState } from 'react';
22
import { useDeleteHomework } from '@/hooks/queries/group-study-homework-api';
3+
import { useToastStore } from '@/stores/use-toast-store';
34
import Button from '../ui/button';
45
import { Modal } from '../ui/modal';
56

@@ -16,16 +17,17 @@ export default function DeleteHomeworkModal({
1617
const [open, setOpen] = useState<boolean>(false);
1718

1819
const { mutate: deleteHomework } = useDeleteHomework();
20+
const showToast = useToastStore((state) => state.showToast);
1921

2022
const handleDelete = () => {
2123
deleteHomework(homeworkId, {
2224
onSuccess: () => {
23-
alert('과제가 성공적으로 삭제되었습니다!');
25+
showToast('과제가 성공적으로 삭제되었습니다!');
2426
setOpen(false);
2527
onSuccess?.();
2628
},
2729
onError: () => {
28-
alert('과제 삭제에 실패했습니다. 다시 시도해주세요.');
30+
showToast('과제 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
2931
},
3032
});
3133
};

src/components/modals/delete-mission-modal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState } from 'react';
22

33
import { MissionListResponse } from '@/api/openapi';
44
import { useDeleteMission } from '@/hooks/queries/mission-api';
5+
import { useToastStore } from '@/stores/use-toast-store';
56
import Button from '../ui/button';
67
import { Modal } from '../ui/modal';
78

@@ -20,18 +21,19 @@ export default function DeleteMissionModal({
2021
const [open, setOpen] = useState<boolean>(false);
2122

2223
const { mutate: deleteMission } = useDeleteMission();
24+
const showToast = useToastStore((state) => state.showToast);
2325

2426
const handleDelete = () => {
2527
deleteMission(
2628
{ missionId, groupStudyId },
2729
{
2830
onSuccess: () => {
29-
alert('미션이 성공적으로 삭제되었습니다!');
31+
showToast('미션이 성공적으로 삭제되었습니다!');
3032
setOpen(false);
3133
onSuccess?.();
3234
},
3335
onError: () => {
34-
alert('미션 삭제에 실패했습니다. 다시 시도해주세요.');
36+
showToast('미션 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
3537
},
3638
},
3739
);

src/components/modals/delete-peer-review-modal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useDeletePeerReview } from '@/hooks/queries/peer-review-api';
2+
import { useToastStore } from '@/stores/use-toast-store';
23
import Button from '../ui/button';
34
import { Modal } from '../ui/modal';
45

@@ -15,15 +16,16 @@ export default function DeletePeerReviewModal({
1516
onOpenChange,
1617
}: DeletePeerReviewModalProps) {
1718
const { mutate: deletePeerReview } = useDeletePeerReview();
19+
const showToast = useToastStore((state) => state.showToast);
1820

1921
const handleDelete = () => {
2022
deletePeerReview(peerReviewId, {
2123
onSuccess: () => {
22-
alert('피어 리뷰가 삭제되었습니다!');
24+
showToast('피어 리뷰가 삭제되었습니다!');
2325
onOpenChange(false);
2426
},
2527
onError: () => {
26-
alert('피어 리뷰 삭제에 실패했습니다. 다시 시도해주세요.');
28+
showToast('피어 리뷰 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
2729
},
2830
});
2931
};

src/components/modals/discretionary-evaluation-modal.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Button from '@/components/ui/button';
77
import FormField from '@/components/ui/form/form-field';
88
import { Modal } from '@/components/ui/modal';
99
import { useUpdateMemberDiscretion } from '@/hooks/queries/group-study-member-api';
10+
import { useToastStore } from '@/stores/use-toast-store';
1011
import { TextAreaInput } from '../ui/input';
1112

1213
const DiscretionaryEvaluationFormSchema = z.object({
@@ -89,8 +90,8 @@ function DiscretionaryEvaluationForm({
8990

9091
const { handleSubmit, formState } = methods;
9192

92-
// TODO: API hook 연결 필요
9393
const { mutate: updateMemberDiscretion } = useUpdateMemberDiscretion();
94+
const showToast = useToastStore((state) => state.showToast);
9495

9596
const onValidSubmit = (values: DiscretionaryEvaluationFormValues) => {
9697
updateMemberDiscretion(
@@ -103,11 +104,14 @@ function DiscretionaryEvaluationForm({
103104
},
104105
{
105106
onSuccess: () => {
106-
alert('재량 평가가 성공적으로 제출되었습니다!');
107+
showToast('재량 평가가 성공적으로 제출되었습니다!');
107108
onClose();
108109
},
109110
onError: () => {
110-
alert('재량 평가 제출에 실패했습니다. 다시 시도해주세요.');
111+
showToast(
112+
'재량 평가 제출에 실패했습니다. 다시 시도해주세요.',
113+
'error',
114+
);
111115
},
112116
},
113117
);

0 commit comments

Comments
 (0)