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
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ CI 파이프라인: lint → typecheck → prettier → build → build-storyboo

### API 레이어

**백엔드 API 문서 (Swagger):**

- 스테이징: https://test-api.zeroone.it.kr/v3/api-docs
- Swagger UI: https://test-api.zeroone.it.kr/swagger-ui/index.html

두 가지 통신 패턴이 공존:

1. **레거시 axios** (`src/api/client/axios.ts`): baseURL `/api/v1/`, 토큰 갱신 큐 구현 (AUTH001 에러 시 갱신 트리거). 커스텀 엔드포인트에 사용.
Expand Down
2 changes: 2 additions & 0 deletions src/app/(service)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Metadata } from 'next';
import localFont from 'next/font/local';
import React from 'react';
import PageViewTracker from '@/components/analytics/page-view-tracker';
import GlobalToast from '@/components/ui/global-toast';
import MainProvider from '@/providers';
import Header from '@/widgets/home/header';

Expand Down Expand Up @@ -41,6 +42,7 @@ export default function ServiceLayout({
<head>{GTM_ID && <GoogleTagManager gtmId={GTM_ID} />}</head>
<body className={clsx(pretendard.className, 'min-h-screen w-screen')}>
<MainProvider>
<GlobalToast />
<PageViewTracker />
<div className="flex min-h-screen w-full flex-col overflow-x-auto">
<Header />
Expand Down
6 changes: 4 additions & 2 deletions src/components/contents/homework-detail-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function EvaluationResult({ evaluation }: { evaluation: EvaluationResponse }) {
</div>
<div className="flex flex-col gap-100">
<span className="font-designer-14b text-text-default">평가 코멘트</span>
<p className="text-text-default font-designer-14r">
<p className="text-text-default font-designer-14r wrap-anywhere whitespace-pre-wrap">
{evaluation.comment}
</p>
</div>
Expand Down Expand Up @@ -432,7 +432,9 @@ function PeerReviewItem({ review, homeworkId }: PeerReviewItemProps) {
</div>
</div>
) : (
<p className="text-text-default font-designer-14r">{review.comment}</p>
<p className="text-text-default font-designer-14r wrap-anywhere whitespace-pre-wrap">
{review.comment}
</p>
)}
</div>
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/create-evaluation-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
useCreateEvaluation,
useGetMissionEvaluationGrades,
} from '@/hooks/queries/evaluation-api';
import { useToastStore } from '@/stores/use-toast-store';
import { TextAreaInput } from '../ui/input';

const CreateEvaluationFormSchema = z.object({
Expand Down Expand Up @@ -89,6 +90,7 @@ function CreateEvaluationForm({

const { data: grades } = useGetMissionEvaluationGrades();
const { mutate: createEvaluation } = useCreateEvaluation();
const showToast = useToastStore((state) => state.showToast);

const onValidSubmit = (values: CreateEvaluationFormValues) => {
createEvaluation(
Expand All @@ -98,11 +100,11 @@ function CreateEvaluationForm({
},
{
onSuccess: () => {
alert('평가가 성공적으로 제출되었습니다!');
showToast('평가가 성공적으로 제출되었습니다!');
onClose();
},
onError: () => {
alert('평가 제출에 실패했습니다. 다시 시도해주세요.');
showToast('평가 제출에 실패했습니다. 다시 시도해주세요.', 'error');
},
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/create-mission-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BaseInput, TextAreaInput } from '@/components/ui/input';
import { Modal } from '@/components/ui/modal';
import { useGroupStudyDetailQuery } from '@/features/study/group/model/use-study-query';
import { useCreateMission, useGetMissions } from '@/hooks/queries/mission-api';
import { useToastStore } from '@/stores/use-toast-store';
import {
createDisabledDateMatcherForMission,
MissionPeriod,
Expand Down Expand Up @@ -129,6 +130,7 @@ function CreateMissionForm({
const { handleSubmit, formState, control } = methods;

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

const onValidSubmit = (values: CreateMissionFormValues) => {
const startDate = dayjs(values.dateRange.from).format('YYYY-MM-DD');
Expand All @@ -148,11 +150,11 @@ function CreateMissionForm({
},
{
onSuccess: () => {
alert('미션이 성공적으로 생성되었습니다!');
showToast('미션이 성공적으로 생성되었습니다!');
onClose();
},
onError: () => {
alert('미션 생성에 실패했습니다. 다시 시도해주세요.');
showToast('미션 생성에 실패했습니다. 다시 시도해주세요.', 'error');
},
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/delete-evaluation-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { useDeleteEvaluation } from '@/hooks/queries/evaluation-api';
import { useToastStore } from '@/stores/use-toast-store';
import Button from '../ui/button';
import { Modal } from '../ui/modal';

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

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

const handleDelete = () => {
deleteEvaluation(evaluationId, {
onSuccess: () => {
alert('평가가 삭제되었습니다.');
showToast('평가가 삭제되었습니다.');
setOpen(false);
},
onError: () => {
alert('평가 삭제에 실패했습니다. 다시 시도해주세요.');
showToast('평가 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
},
});
};
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/delete-homework-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { useDeleteHomework } from '@/hooks/queries/group-study-homework-api';
import { useToastStore } from '@/stores/use-toast-store';
import Button from '../ui/button';
import { Modal } from '../ui/modal';

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

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

const handleDelete = () => {
deleteHomework(homeworkId, {
onSuccess: () => {
alert('과제가 성공적으로 삭제되었습니다!');
showToast('과제가 성공적으로 삭제되었습니다!');
setOpen(false);
onSuccess?.();
},
onError: () => {
alert('과제 삭제에 실패했습니다. 다시 시도해주세요.');
showToast('과제 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
},
});
};
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/delete-mission-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState } from 'react';

import { MissionListResponse } from '@/api/openapi';
import { useDeleteMission } from '@/hooks/queries/mission-api';
import { useToastStore } from '@/stores/use-toast-store';
import Button from '../ui/button';
import { Modal } from '../ui/modal';

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

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

const handleDelete = () => {
deleteMission(
{ missionId, groupStudyId },
{
onSuccess: () => {
alert('미션이 성공적으로 삭제되었습니다!');
showToast('미션이 성공적으로 삭제되었습니다!');
setOpen(false);
onSuccess?.();
},
onError: () => {
alert('미션 삭제에 실패했습니다. 다시 시도해주세요.');
showToast('미션 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
},
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/delete-peer-review-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useDeletePeerReview } from '@/hooks/queries/peer-review-api';
import { useToastStore } from '@/stores/use-toast-store';
import Button from '../ui/button';
import { Modal } from '../ui/modal';

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

const handleDelete = () => {
deletePeerReview(peerReviewId, {
onSuccess: () => {
alert('피어 리뷰가 삭제되었습니다!');
showToast('피어 리뷰가 삭제되었습니다!');
onOpenChange(false);
},
onError: () => {
alert('피어 리뷰 삭제에 실패했습니다. 다시 시도해주세요.');
showToast('피어 리뷰 삭제에 실패했습니다. 다시 시도해주세요.', 'error');
},
});
};
Expand Down
10 changes: 7 additions & 3 deletions src/components/modals/discretionary-evaluation-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Button from '@/components/ui/button';
import FormField from '@/components/ui/form/form-field';
import { Modal } from '@/components/ui/modal';
import { useUpdateMemberDiscretion } from '@/hooks/queries/group-study-member-api';
import { useToastStore } from '@/stores/use-toast-store';
import { TextAreaInput } from '../ui/input';

const DiscretionaryEvaluationFormSchema = z.object({
Expand Down Expand Up @@ -89,8 +90,8 @@ function DiscretionaryEvaluationForm({

const { handleSubmit, formState } = methods;

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

const onValidSubmit = (values: DiscretionaryEvaluationFormValues) => {
updateMemberDiscretion(
Expand All @@ -103,11 +104,14 @@ function DiscretionaryEvaluationForm({
},
{
onSuccess: () => {
alert('재량 평가가 성공적으로 제출되었습니다!');
showToast('재량 평가가 성공적으로 제출되었습니다!');
onClose();
},
onError: () => {
alert('재량 평가 제출에 실패했습니다. 다시 시도해주세요.');
showToast(
'재량 평가 제출에 실패했습니다. 다시 시도해주세요.',
'error',
);
},
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/edit-evaluation-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
useGetMissionEvaluationGrades,
useUpdateEvaluation,
} from '@/hooks/queries/evaluation-api';
import { useToastStore } from '@/stores/use-toast-store';
import { TextAreaInput } from '../ui/input';

const EditEvaluationFormSchema = z.object({
Expand Down Expand Up @@ -99,6 +100,7 @@ function EditEvaluationForm({

const { data: grades } = useGetMissionEvaluationGrades();
const { mutate: updateEvaluation } = useUpdateEvaluation();
const showToast = useToastStore((state) => state.showToast);

const onValidSubmit = (values: EditEvaluationFormValues) => {
updateEvaluation(
Expand All @@ -108,11 +110,11 @@ function EditEvaluationForm({
},
{
onSuccess: () => {
alert('평가가 성공적으로 수정되었습니다!');
showToast('평가가 성공적으로 수정되었습니다!');
onClose();
},
onError: () => {
alert('평가 수정에 실패했습니다. 다시 시도해주세요.');
showToast('평가 수정에 실패했습니다. 다시 시도해주세요.', 'error');
},
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/edit-homework-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import FormField from '@/components/ui/form/form-field';
import { Modal } from '@/components/ui/modal';

import { useEditHomework } from '@/hooks/queries/group-study-homework-api';
import { useToastStore } from '@/stores/use-toast-store';
import { BaseInput, TextAreaInput } from '../ui/input';

const EditHomeworkFormSchema = z.object({
Expand Down Expand Up @@ -92,6 +93,7 @@ function EditHomeworkForm({
const { handleSubmit, formState } = methods;

const { mutate: editHomework } = useEditHomework();
const showToast = useToastStore((state) => state.showToast);

const onValidSubmit = (values: EditHomeworkFormValues) => {
editHomework(
Expand All @@ -104,12 +106,12 @@ function EditHomeworkForm({
},
{
onSuccess: async () => {
alert('과제가 성공적으로 수정되었습니다!');
showToast('과제가 성공적으로 수정되었습니다!');
onClose();
onSuccess?.();
},
onError: () => {
alert('과제 수정에 실패했습니다. 다시 시도해주세요.');
showToast('과제 수정에 실패했습니다. 다시 시도해주세요.', 'error');
},
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/edit-mission-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
useGetMissions,
useUpdateMission,
} from '@/hooks/queries/mission-api';
import { useToastStore } from '@/stores/use-toast-store';
import {
createDisabledDateMatcherForMission,
MissionPeriod,
Expand Down Expand Up @@ -165,6 +166,7 @@ function EditMissionForm({
}, [missionData, reset]);

const { mutate: updateMission } = useUpdateMission();
const showToast = useToastStore((state) => state.showToast);

const onValidSubmit = (values: EditMissionFormValues) => {
const startDate = dayjs(values.dateRange.from).format('YYYY-MM-DD');
Expand All @@ -184,11 +186,11 @@ function EditMissionForm({
},
{
onSuccess: () => {
alert('미션이 성공적으로 수정되었습니다!');
showToast('미션이 성공적으로 수정되었습니다!');
onClose();
},
onError: () => {
alert('미션 수정에 실패했습니다. 다시 시도해주세요.');
showToast('미션 수정에 실패했습니다. 다시 시도해주세요.', 'error');
},
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/submit-homework-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import FormField from '@/components/ui/form/form-field';
import { Modal } from '@/components/ui/modal';

import { useSubmitHomework } from '@/hooks/queries/group-study-homework-api';
import { useToastStore } from '@/stores/use-toast-store';
import { BaseInput, TextAreaInput } from '../ui/input';

const SubmitHomeworkFormSchema = z.object({
Expand Down Expand Up @@ -87,6 +88,7 @@ function SubmitHomeworkForm({
const { handleSubmit, formState } = methods;

const { mutate: submitHomework } = useSubmitHomework();
const showToast = useToastStore((state) => state.showToast);

const onValidSubmit = (values: SubmitHomeworkFormValues) => {
submitHomework(
Expand All @@ -99,12 +101,12 @@ function SubmitHomeworkForm({
},
{
onSuccess: async () => {
alert('과제가 성공적으로 제출되었습니다!');
showToast('과제가 성공적으로 제출되었습니다!');
onClose();
onSuccess?.();
},
onError: () => {
alert('과제 제출에 실패했습니다. 다시 시도해주세요.');
showToast('과제 제출에 실패했습니다. 다시 시도해주세요.', 'error');
},
},
);
Expand Down
Loading
Loading