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
9 changes: 8 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
reactCompiler: true,
images: {
remotePatterns: [
{
protocol: "https",
hostname: "ditda-public-bucket.s3.ap-northeast-2.amazonaws.com",
},
],
},
turbopack: {
rules: {
"*.svg": {
Expand Down
10 changes: 8 additions & 2 deletions src/app/instructor/my/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { cookies } from "next/headers";

import { CommissionsHistorySection, MyInfoSection } from "@/widgets/instructor/my";

const page = () => {
const page = async () => {
const cookieStore = await cookies();
const name = cookieStore.get("userName")?.value ?? "";
const profileImageUrl = cookieStore.get("userProfileImageUrl")?.value;

return (
<div className="mx-auto flex w-212.75 flex-col items-center pt-15">
<h1 className="text-title2-sb w-full pb-10 text-left text-black">마이페이지</h1>
<div className="flex flex-col gap-8">
<MyInfoSection />
<MyInfoSection name={name} profileImageUrl={profileImageUrl} />
<CommissionsHistorySection />
</div>
</div>
Expand Down
9 changes: 7 additions & 2 deletions src/app/instructor/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { cookies } from "next/headers";

import {
DraftSubmissionStatusSection,
MatchingCommissionsSection,
ModifyingCommissionsSection,
} from "@/widgets/instructor/home";

const page = () => {
const page = async () => {
const cookieStore = await cookies();
const name = cookieStore.get("userName")?.value ?? "";

return (
<div className="mx-auto flex w-275 flex-col items-center gap-10 pt-10 pb-14">
<h1 className="text-gray-90 text-heading1-sb w-full text-left">다현님, 어서오세요!</h1>
<h1 className="text-gray-90 text-heading1-sb w-full text-left">{name}님, 어서오세요!</h1>
<DraftSubmissionStatusSection />
<div className="flex w-full flex-row gap-5">
<MatchingCommissionsSection />
Expand Down
2 changes: 1 addition & 1 deletion src/app/instructor/revision/[commissionId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { MAX_SELECTABLE_COUNT } from "@/widgets/instructor/revision/config/revis
import {
draftFilesData,
draftRevisionDetailData,
} from "@/widgets/instructor/revision/model/revision";
} from "@/widgets/instructor/revision/model/revisionMock";

const Page = () => {
const router = useRouter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import {
getApiResponseMessage,
toApiError,
} from "@/shared/api/client";
import type { ApiResponse } from "@/shared/api/types";
import type { ApiResponse } from "@/shared/api/commonType";

export const logout = async () => {
// 로그아웃
export const postLogout = async () => {
try {
const response = await api
.post(createApiPath("/api/v1/auth/logout"))
Expand Down
2 changes: 1 addition & 1 deletion src/features/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { logout } from "./api/logout";
export { postLogout } from "./api/auth";
export { useLogout } from "./model/useLogout";
export { default as LogoutSidebarMenu } from "./ui/LogoutSidebarMenu";
4 changes: 2 additions & 2 deletions src/features/auth/model/useLogout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useRouter } from "next/navigation";
import { useState } from "react";

import { logout } from "@/features/auth/api/logout";
import { postLogout } from "@/features/auth/api/auth";
import { clearClientAuth } from "@/shared/lib/auth/client";

export const useLogout = () => {
Expand All @@ -16,7 +16,7 @@ export const useLogout = () => {
setIsLoggingOut(true);

try {
await logout();
await postLogout();
} catch {
// 클라이언트 세션 정리는 로그아웃 API 실패 여부와 무관하게 진행합니다.
} finally {
Expand Down
4 changes: 2 additions & 2 deletions src/features/instructor/choose/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export type { CommissionWithDrafts, Draft, DraftDetail } from "./model/choose";
export { commissionDraftsData, draftDetailsData } from "./model/choose";
export type { CommissionWithDrafts, Draft, DraftDetail } from "./model/chooseMock";
export { commissionDraftsData, draftDetailsData } from "./model/chooseMock";
export { default as DraftCard } from "./ui/DraftCard";
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type CommissionWithDrafts = {

export const commissionDraftsData: CommissionWithDrafts[] = [
{
commissionId: 11,
commissionId: 4,
title: "해커스톡 왕초보 영어 - 기초 문법편",
drafts: [
{ draftId: 41, thumbnailUrl: "/images/thumbnail_mock.jpg" },
Expand All @@ -22,7 +22,7 @@ export const commissionDraftsData: CommissionWithDrafts[] = [
],
},
{
commissionId: 15,
commissionId: 6,
title: "고등 국어 문학 - 현대시 집중",
drafts: [
{ draftId: 51, thumbnailUrl: "/images/thumbnail_mock.jpg" },
Expand Down
37 changes: 37 additions & 0 deletions src/features/instructor/home/api/home.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type {
DraftSubmissionItem,
GetDraftSubmissionsResult,
GetMatchingCommissionsResult,
GetRevisionsResult,
MatchingItem,
ModifyingItem,
} from "@/features/instructor/home/api/homeTypes";
import { api, createApiPath } from "@/shared/api/client";
import type { ApiResponse } from "@/shared/api/commonType";

// 시안 제출 현황 조회
export const getDraftSubmissions = async (): Promise<DraftSubmissionItem[]> => {
const response = await api
.get(createApiPath("/api/v1/instructors/dashboards/draft-submissions"))
.json<ApiResponse<GetDraftSubmissionsResult>>();

return response.result?.commissions ?? [];
};

// 매칭 중인 외주 조회
export const getMatchingCommissions = async (): Promise<MatchingItem[]> => {
const response = await api
.get(createApiPath("/api/v1/instructors/dashboards/matchings"))
.json<ApiResponse<GetMatchingCommissionsResult>>();

return response.result?.commissions ?? [];
};

// 수정 중인 외주 조회
export const getRevisions = async (): Promise<ModifyingItem[]> => {
const response = await api
.get(createApiPath("/api/v1/instructors/dashboards/revisions"))
.json<ApiResponse<GetRevisionsResult>>();

return response.result?.commissions ?? [];
};
Comment on lines +13 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle success: false explicitly instead of returning empty lists.

Line 18, Line 27, and Line 36 currently collapse API failure payloads into [], so real backend errors become indistinguishable from “no data”.

Suggested fix
 export const getDraftSubmissions = async (): Promise<DraftSubmissionItem[]> => {
   const response = await api
     .get(createApiPath("/api/v1/instructors/dashboards/draft-submissions"))
     .json<ApiResponse<GetDraftSubmissionsResult>>();
+  if (!response.success) {
+    throw new Error(response.message);
+  }

   return response.result?.commissions ?? [];
 };

 export const getMatchingCommissions = async (): Promise<MatchingItem[]> => {
   const response = await api
     .get(createApiPath("/api/v1/instructors/dashboards/matchings"))
     .json<ApiResponse<GetMatchingCommissionsResult>>();
+  if (!response.success) {
+    throw new Error(response.message);
+  }

   return response.result?.commissions ?? [];
 };

 export const getRevisions = async (): Promise<ModifyingItem[]> => {
   const response = await api
     .get(createApiPath("/api/v1/instructors/dashboards/revisions"))
     .json<ApiResponse<GetRevisionsResult>>();
+  if (!response.success) {
+    throw new Error(response.message);
+  }

   return response.result?.commissions ?? [];
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/instructor/home/api/home.ts` around lines 13 - 37, The three
functions getDraftSubmissions, getMatchingCommissions, and getRevisions all use
the fallback operator to return an empty array when response.result is
undefined, which masks API failures where success is false. Modify each function
to explicitly check the success property of the ApiResponse before accessing the
result. If success is false, throw an error or handle the failure explicitly
instead of defaulting to an empty array, so that backend errors are
distinguishable from cases where data is genuinely unavailable.

45 changes: 45 additions & 0 deletions src/features/instructor/home/api/homeTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export type DraftSubmissionItem = {
commissionId: number;
title: string;
category: string;
draftSubmission: {
submitted: number;
total: number;
};
isViewable: boolean;
firstDraftDeadline: string;
};

export type MatchingItem = {
commissionId: number;
title: string;
applicationDeadline: string;
matching: {
matched: number;
total: number;
};
};

export type ModifyingItem = {
commissionId: number;
title: string;
isSubmitted: boolean;
hasUpdated: boolean;
finalDeadline: string;
};

export const CATEGORY_DISPLAY_MAP: Record<string, string> = {
FLYER_TEXTBOOK_COVER_INNER: "교재 외지/내지",
};

export type GetDraftSubmissionsResult = {
commissions: DraftSubmissionItem[];
};

export type GetMatchingCommissionsResult = {
commissions: MatchingItem[];
};

export type GetRevisionsResult = {
commissions: ModifyingItem[];
};
10 changes: 3 additions & 7 deletions src/features/instructor/home/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
export { getDraftSubmissions, getMatchingCommissions, getRevisions } from "./api/home";
export type { DraftSubmissionItem, MatchingItem, ModifyingItem } from "./api/homeTypes";
export { CATEGORY_DISPLAY_MAP } from "./api/homeTypes";
export { getDDay } from "./lib/getDDay";
export type { DraftSubmissionItem, MatchingItem, ModifyingItem } from "./model/home";
export {
CATEGORY_DISPLAY_MAP,
draftSubmissionStatusData,
matchingStatusData,
modifyingStatusData,
} from "./model/home";
export { default as CommissionsHeader } from "./ui/CommissionsHeader";
export { default as DraftSubmissionStatusRow } from "./ui/DraftSubmissionStatusRow";
export { default as MatchingCommissionsRow } from "./ui/MatchingCommissionsRow";
Expand Down
144 changes: 0 additions & 144 deletions src/features/instructor/home/model/home.ts

This file was deleted.

9 changes: 6 additions & 3 deletions src/features/instructor/home/ui/DraftSubmissionStatusRow.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { useRouter } from "next/navigation";

import {
CATEGORY_DISPLAY_MAP,
DraftSubmissionItem,
} from "@/features/instructor/home/api/homeTypes";
import { getDDay } from "@/features/instructor/home/lib/getDDay";
import { CATEGORY_DISPLAY_MAP, DraftSubmissionItem } from "@/features/instructor/home/model/home";
import { ArrowRightIcon, MatchingOffIcon, MatchingOnIcon } from "@/shared/assets/icons";
import Button from "@/shared/ui/Button";
import Tag from "@/shared/ui/Tag";

const DraftSubmissionStatusRow = ({ item }: { item: DraftSubmissionItem }) => {
const { commissionId, title, category, draftSubmission, firstDraftDeadline } = item;
const { commissionId, title, category, draftSubmission, isViewable, firstDraftDeadline } = item;
const { submitted, total } = draftSubmission;
const categoryLabel = CATEGORY_DISPLAY_MAP[category] ?? category;
const router = useRouter();
Expand Down Expand Up @@ -37,7 +40,7 @@ const DraftSubmissionStatusRow = ({ item }: { item: DraftSubmissionItem }) => {
(<span className="text-main-main">{submitted}</span>/{total})
</p>
</div>
{submitted === total ? (
{isViewable ? (
<Button
variant="small_primary"
className="w-fit"
Expand Down
Loading
Loading