Skip to content
76 changes: 74 additions & 2 deletions src/apis/workspace/workspace.queries.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient, keepPreviousData } from '@tanstack/react-query';
import { useSession } from 'next-auth/react';
import { workspaceService } from './workspace.service';
import { InviteWorkspaceRequest } from './workspace.type';
import { CreateWorkspaceRequest, InviteWorkspaceRequest } from './workspace.type';

export const workspaceKeys = {
all: ['workspaces'] as const,
my: ['workspaces', 'my'] as const,
detail: (workspaceId: number) => ['workspaces', workspaceId] as const,
invitations: ['workspaces', 'invitations'] as const,
adminName: (workspaceId: number) => ['workspaces', workspaceId, 'admin-name'] as const,
};

export const useGetMyWorkspaces = () => {
Expand All @@ -17,6 +19,21 @@ export const useGetMyWorkspaces = () => {
queryKey: workspaceKeys.my,
queryFn: () => workspaceService.getMyWorkspaces(accessToken!),
enabled: !!accessToken,
staleTime: 1000 * 60,
placeholderData: keepPreviousData,
});
};

export const useGetInvitations = () => {
const { data: session } = useSession();
const accessToken = session?.accessToken as string | undefined;

return useQuery({
queryKey: workspaceKeys.invitations,
queryFn: () => workspaceService.getInvitations(accessToken!),
enabled: !!accessToken,
staleTime: 1000 * 60,
placeholderData: keepPreviousData,
});
};

Expand All @@ -31,6 +48,32 @@ export const useGetWorkspace = (workspaceId: number) => {
});
};

export const useGetAdminName = (workspaceId: number) => {
const { data: session } = useSession();
const accessToken = session?.accessToken as string | undefined;

return useQuery({
queryKey: workspaceKeys.adminName(workspaceId),
queryFn: () => workspaceService.getAdminName(workspaceId, accessToken!),
enabled: !!accessToken && !!workspaceId,
staleTime: 1000 * 60 * 5,
});
};

export const useCreateWorkspace = () => {
const { data: session } = useSession();
const accessToken = session?.accessToken as string | undefined;
const queryClient = useQueryClient();

return useMutation({
mutationFn: (body: CreateWorkspaceRequest) =>
workspaceService.createWorkspace(body, accessToken!),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.my });
},
});
};

export const useInviteWorkspace = (workspaceId: number) => {
const { data: session } = useSession();
const accessToken = session?.accessToken as string | undefined;
Expand All @@ -44,3 +87,32 @@ export const useInviteWorkspace = (workspaceId: number) => {
},
});
};

export const useAcceptInvitation = () => {
const { data: session } = useSession();
const accessToken = session?.accessToken as string | undefined;
const queryClient = useQueryClient();

return useMutation({
mutationFn: (membershipId: number) =>
workspaceService.acceptInvitation(membershipId, accessToken!),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.my });
queryClient.invalidateQueries({ queryKey: workspaceKeys.invitations });
},
});
};

export const useRejectInvitation = () => {
const { data: session } = useSession();
const accessToken = session?.accessToken as string | undefined;
const queryClient = useQueryClient();

return useMutation({
mutationFn: (membershipId: number) =>
workspaceService.rejectInvitation(membershipId, accessToken!),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.invitations });
},
});
};
72 changes: 67 additions & 5 deletions src/apis/workspace/workspace.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ import {
WorkspaceDetailResponse,
InviteWorkspaceRequest,
InviteWorkspaceResponse,
CreateWorkspaceRequest,
CreateWorkspaceResponse,
InvitationListResponse,
AcceptRejectInvitationResponse,
AdminNameResponse,
} from './workspace.type';

const BASE_URL = process.env.NEXT_PUBLIC_API_URL;

export const workspaceService = {
getMyWorkspaces: async (accessToken: string): Promise<WorkspaceListResponse> => {
const res = await fetch(`${BASE_URL}/api/v1/workspaces/my`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
headers: { Authorization: `Bearer ${accessToken}` },
});

if (!res.ok) throw new Error('워크스페이스 목록 조회 실패');
Expand All @@ -24,13 +27,28 @@ export const workspaceService = {
accessToken: string,
): Promise<WorkspaceDetailResponse> => {
const res = await fetch(`${BASE_URL}/api/v1/workspaces/${workspaceId}`, {
headers: { Authorization: `Bearer ${accessToken}` },
});

if (res.status === 404) throw new Error('워크스페이스를 찾을 수 없습니다.');
if (!res.ok) throw new Error('워크스페이스 조회 실패');
return res.json();
},

createWorkspace: async (
body: CreateWorkspaceRequest,
accessToken: string,
): Promise<CreateWorkspaceResponse> => {
const res = await fetch(`${BASE_URL}/api/v1/workspaces`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(body),
});

if (res.status === 404) throw new Error('워크스페이스를 찾을 수 없습니다.');
if (!res.ok) throw new Error('워크스페이스 조회 실패');
if (!res.ok) throw new Error('워크스페이스 생성 실패');
return res.json();
},

Expand All @@ -53,4 +71,48 @@ export const workspaceService = {
if (!res.ok) throw new Error('초대에 실패했습니다. 이메일을 확인해 주세요.');
return res.json();
},

getInvitations: async (accessToken: string): Promise<InvitationListResponse> => {
const res = await fetch(`${BASE_URL}/api/v1/workspaces/invitations`, {
headers: { Authorization: `Bearer ${accessToken}` },
});

if (!res.ok) throw new Error('초대 목록 조회 실패');
return res.json();
},

acceptInvitation: async (
membershipId: number,
accessToken: string,
): Promise<AcceptRejectInvitationResponse> => {
const res = await fetch(`${BASE_URL}/api/v1/workspaces/invitations/${membershipId}/accept`, {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}` },
});

if (!res.ok) throw new Error('초대 수락 실패');
return res.json();
},

rejectInvitation: async (
membershipId: number,
accessToken: string,
): Promise<AcceptRejectInvitationResponse> => {
const res = await fetch(`${BASE_URL}/api/v1/workspaces/invitations/${membershipId}/reject`, {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}` },
});

if (!res.ok) throw new Error('초대 거절 실패');
return res.json();
},

getAdminName: async (workspaceId: number, accessToken: string): Promise<AdminNameResponse> => {
const res = await fetch(`${BASE_URL}/api/v1/workspaces/${workspaceId}/admin-name`, {
headers: { Authorization: `Bearer ${accessToken}` },
});

if (!res.ok) throw new Error('어드민 이름 조회 실패');
return res.json();
},
};
53 changes: 53 additions & 0 deletions src/apis/workspace/workspace.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,56 @@ export interface InviteWorkspaceResponse {
traceId: string;
};
}

export interface CreateWorkspaceRequest {
name: string;
color: string;
}

export interface CreateWorkspaceResponse {
success: boolean;
data: number;
meta: {
timestamp: string;
traceId: string;
};
}

export interface InvitationItem {
color: string;
membershipId: number;
role: WorkspaceRole;
workspaceId: number;
workspaceName: string;
}

export interface InvitationListResponse {
success: boolean;
totalCount: number;
nextCursor: number | null;
data: InvitationItem[];
meta: {
timestamp: string;
traceId: string;
};
}

export interface AcceptRejectInvitationResponse {
success: boolean;
data: null;
meta: {
timestamp: string;
traceId: string;
};
}

export interface AdminNameResponse {
success: boolean;
data: {
adminName: string;
};
meta: {
timestamp: string;
traceId: string;
};
}
Loading
Loading