diff --git a/src/app/api/dev/login/route.ts b/src/app/api/dev/login/route.ts new file mode 100644 index 00000000..b6192963 --- /dev/null +++ b/src/app/api/dev/login/route.ts @@ -0,0 +1,20 @@ +import { NextResponse } from "next/server"; + +import { setTokenCookies } from "@/shared/lib/auth/cookies"; + +export function GET() { + if (process.env.NODE_ENV === "production") { + return NextResponse.json({ error: "Not allowed" }, { status: 403 }); + } + + const response = NextResponse.redirect( + new URL( + "/taskmate", + process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3000", + ), + ); + + setTokenCookies(response, "dev-access-token", "dev-refresh-token"); + + return response; +} diff --git a/src/features/trash/mock/trash.ts b/src/features/trash/mock/trash.ts new file mode 100644 index 00000000..27b7ada5 --- /dev/null +++ b/src/features/trash/mock/trash.ts @@ -0,0 +1,120 @@ +import { HttpResponse } from "msw"; + +import { apiMock } from "@/shared/mock/apiMock"; + +const mockPersonalTrashItems = [ + { + itemType: "GOAL", + id: 1, + deletedAt: new Date().toISOString(), + goalName: "이번 주 운동", + todoTitle: null, + }, + { + itemType: "TODO", + id: 10, + deletedAt: new Date().toISOString(), + goalName: "이번 주 운동", + todoTitle: "러닝 30분", + }, + { + itemType: "TODO", + id: 11, + deletedAt: new Date().toISOString(), + goalName: "독서", + todoTitle: "클린코드 1장", + }, +]; + +const mockTeamTrashItems = [ + { + itemType: "GOAL", + id: 2, + deletedAt: new Date().toISOString(), + teamName: "디자인팀", + goalName: "스프린트 목표", + todoTitle: null, + }, + { + itemType: "TODO", + id: 20, + deletedAt: new Date().toISOString(), + teamName: "디자인팀", + goalName: "스프린트 목표", + todoTitle: "와이어프레임", + }, +]; + +export const trashHandlers = [ + // 개인 휴지통 조회 + apiMock.get("/api/trash/personal", ({ request }: { request: Request }) => { + const url = new URL(request.url); + const page = Number(url.searchParams.get("page") ?? 0); + const size = Number(url.searchParams.get("size") ?? 20); + const content = mockPersonalTrashItems.slice( + page * size, + (page + 1) * size, + ); + + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "개인 휴지통 조회에 성공했습니다.", + data: { + content, + page, + size, + totalElements: mockPersonalTrashItems.length, + totalPages: Math.ceil(mockPersonalTrashItems.length / size), + }, + timestamp: new Date().toISOString(), + }); + }), + + // 팀 휴지통 조회 + apiMock.get( + "/api/trash/teams/:teamId", + ({ request }: { request: Request }) => { + const url = new URL(request.url); + const page = Number(url.searchParams.get("page") ?? 0); + const size = Number(url.searchParams.get("size") ?? 20); + const content = mockTeamTrashItems.slice(page * size, (page + 1) * size); + + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "팀 휴지통 조회에 성공했습니다.", + data: { + content, + page, + size, + totalElements: mockTeamTrashItems.length, + totalPages: Math.ceil(mockTeamTrashItems.length / size), + }, + timestamp: new Date().toISOString(), + }); + }, + ), + + // 휴지통 복구 + apiMock.post("/api/trash/restore", async () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "휴지통 복구에 성공했습니다.", + data: null, + timestamp: new Date().toISOString(), + }); + }), + + // 휴지통 영구 삭제 + apiMock.delete("/api/trash", async () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "휴지통 영구 삭제에 성공했습니다.", + data: null, + timestamp: new Date().toISOString(), + }); + }), +]; diff --git a/src/features/user/mock/user.ts b/src/features/user/mock/user.ts new file mode 100644 index 00000000..30ef00b3 --- /dev/null +++ b/src/features/user/mock/user.ts @@ -0,0 +1,54 @@ +import { HttpResponse } from "msw"; + +import { apiMock } from "@/shared/mock/apiMock"; + +const mockUserInfo = { + id: 101, + email: "leader@example.com", + nickname: "팀장", + profileImageUrl: null, + provider: "LOCAL", + createdAt: new Date().toISOString(), +}; + +export const userHandlers = [ + // 내 정보 수정 + apiMock.put("/api/users", async ({ request }: { request: Request }) => { + const body = (await request.json()) as { nickname?: string }; + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "내 정보 수정에 성공했습니다.", + data: { + ...mockUserInfo, + nickname: body.nickname ?? mockUserInfo.nickname, + }, + timestamp: new Date().toISOString(), + }); + }), + + // 프로필 이미지 업로드 + apiMock.put("/api/users/image", async () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "프로필 이미지 업로드에 성공했습니다.", + data: { + ...mockUserInfo, + profileImageUrl: "https://placehold.co/100x100", + }, + timestamp: new Date().toISOString(), + }); + }), + + // 회원 탈퇴 + apiMock.delete("/api/users/me", () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "회원 탈퇴에 성공했습니다.", + data: null, + timestamp: new Date().toISOString(), + }); + }), +]; diff --git a/src/shared/mock/handlers.ts b/src/shared/mock/handlers.ts index 103c67c2..77ed98e9 100644 --- a/src/shared/mock/handlers.ts +++ b/src/shared/mock/handlers.ts @@ -4,6 +4,8 @@ import { managementHandler } from "@/features/management/mock/management"; import { notificationHandler } from "@/features/notification/mock/notification"; import { invitationsHandlers, teamsHandlers } from "@/features/team/mock"; import { todosHandlers } from "@/features/todo/mock/todos"; +import { trashHandlers } from "@/features/trash/mock/trash"; +import { userHandlers } from "@/features/user/mock/user"; import { homeHandler } from "@/widgets/home/mock/home"; export const handlers = [ @@ -15,4 +17,6 @@ export const handlers = [ ...todosHandlers, ...managementHandler, ...notificationHandler, + ...trashHandlers, + ...userHandlers, ];