Skip to content

Commit

Permalink
fix: client, server 사이 interface 통일
Browse files Browse the repository at this point in the history
  • Loading branch information
PMtHk committed Nov 25, 2024
1 parent 3b8f8b0 commit 1339e50
Show file tree
Hide file tree
Showing 18 changed files with 78 additions and 68 deletions.
2 changes: 1 addition & 1 deletion apps/client/src/features/project/label/labelSchema.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod';

export const labelFormSchema = z.object({
name: z.string().min(1, 'Label name is required'),
name: z.string().min(1, 'Label name is required').max(10, 'Label name is too long'),
description: z.string().min(1, 'Label description is required'),
color: z.string().regex(/^#[0-9A-F]{6}$/i, 'Must be a valid hex color'),
});
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/features/project/sprint/sprintSchema.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod';

export const sprintFormSchema = z.object({
name: z.string().min(1, 'Sprint name is required'),
name: z.string().min(1, 'Sprint name is required').max(10, 'Sprint name is too long'),
dateRange: z
.object({
from: z.date(),
Expand Down
14 changes: 14 additions & 0 deletions apps/client/src/features/project/useUsersQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useQuery } from '@tanstack/react-query';
import { projectAPI } from '@/features/project/api.ts';

export const useUsersQuery = (projectId: number) => {
return useQuery({
queryKey: ['users'],
queryFn: async () => {
const { result } = await projectAPI.getMembers(projectId);

return result;
},
retry: 0,
});
};
8 changes: 4 additions & 4 deletions apps/client/src/features/task/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ export const taskAPI = {
return data;
},

updateAssignees: async (taskId: number, userIds: number[] = []) => {
updateAssignees: async (taskId: number, assignees: number[] = []) => {
const { data } = await axiosInstance.put<BaseResponse>(`/task/${taskId}/assignees`, {
userIds,
assignees,
});

return data;
},

updateLabels: async (taskId: number, labelIds: number[] = []) => {
updateLabels: async (taskId: number, labels: number[] = []) => {
const { data } = await axiosInstance.put<BaseResponse>(`/task/${taskId}/labels`, {
labelIds,
labels,
});

return data;
Expand Down
6 changes: 3 additions & 3 deletions apps/client/src/features/task/components/Assignees.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import { Input } from '@/components/ui/input';
import { cn } from '@/lib/utils';
import { Assignee } from '@/features/types';
import { useTaskMutations } from '@/features/task/useTaskMutations';
import { useUsersQuery } from '@/features/project/useUsersQuery.ts';

interface AssigneesProps {
initialAssignees: Assignee[];
}

export default function Assignees({ initialAssignees }: AssigneesProps) {
const { taskId } = useLoaderData({
const { taskId, projectId } = useLoaderData({
from: '/_auth/$project/board/$taskId',
});
// const { data: members = [] } = useProjectMembersQuery(projectId);
const members: Assignee[] = [];
const { data: members = [] } = useUsersQuery(projectId);
const { updateAssignees } = useTaskMutations(taskId);

const [selectedAssignees, setSelectedAssignees] = useState<Assignee[]>(initialAssignees);
Expand Down
1 change: 1 addition & 0 deletions apps/client/src/features/task/components/Estimate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default function Estimate({ initialEstimate }: EstimateProps) {
onChange={(e) => handleEstimateChange(Number(e.target.value))}
min={0}
step={1}
max={99}
/>
</div>
</PopoverContent>
Expand Down
2 changes: 2 additions & 0 deletions apps/client/src/features/task/components/TaskDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export function TaskDescription({ initialDescription = '' }: TaskDescriptionProp

const handleSave = () => {
updateDescription.mutate(description);
prevDescriptionRef.current = description;
setIsEdit(false);
};

const handleCancel = () => {
Expand Down
20 changes: 7 additions & 13 deletions apps/client/src/features/task/subtask/api.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import { axiosInstance } from '@/lib/axios.ts';
import { BaseResponse } from '@/features/types.ts';
import {
CreateSubtaskResult,
DeleteSubtaskResult,
CreateSubtaskResponse,
UpdateSubtaskResponse,
UpdateSubtaskDto,
UpdateSubtaskResult,
} from '@/features/task/subtask/types.ts';

export const subtaskAPI = {
create: async (taskId: number) => {
const { data } = await axiosInstance.post<BaseResponse<CreateSubtaskResult>>(
`/task/${taskId}/subtask`,
{
title: 'New Subtask',
}
);
const { data } = await axiosInstance.post<CreateSubtaskResponse>(`/task/${taskId}/subtask`, {
content: 'New Subtask',
});

return data;
},

update: async (subtaskId: number, updateSubtaskDto: UpdateSubtaskDto) => {
const { data } = await axiosInstance.patch<BaseResponse<UpdateSubtaskResult>>(
const { data } = await axiosInstance.patch<UpdateSubtaskResponse>(
`/subtask/${subtaskId}`,
updateSubtaskDto
);
Expand All @@ -29,9 +25,7 @@ export const subtaskAPI = {
},

delete: async (subtaskId: number) => {
const { data } = await axiosInstance.delete<BaseResponse<DeleteSubtaskResult>>(
`/subtask/${subtaskId}`
);
const { data } = await axiosInstance.delete<BaseResponse>(`/subtask/${subtaskId}`);

return data;
},
Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/features/task/subtask/components/Subtasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export function Subtasks({ initialSubtasks = [] }: SubtasksProps) {
});

const deleteMutation = deleteSubtask({
onSuccess: (deletedSubtask: Subtask) => {
setSubtasks((prev) => prev.filter((subtask) => subtask.id !== deletedSubtask.id));
onSuccess: (_, subtaskId) => {
setSubtasks((prev) => prev.filter((subtask) => subtask.id !== subtaskId));
},
});

Expand Down
18 changes: 4 additions & 14 deletions apps/client/src/features/task/subtask/types.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
import { BaseResponse } from '@/features/types.ts';

export type Subtask = {
id: number;
content: string;
completed: boolean;
};

// create
export interface CreateSubtaskResult {
subtask: {
id: number;
content: string;
completed: boolean;
};
}
export type CreateSubtaskResponse = BaseResponse<Subtask>;

// update
export interface UpdateSubtaskDto {
content?: string;
completed?: boolean;
}

export interface UpdateSubtaskResult {
subtask: {
id: number;
content: string;
completed: boolean;
};
}
export type UpdateSubtaskResponse = BaseResponse<Subtask>;

// delete
export interface DeleteSubtaskResult {
Expand Down
19 changes: 8 additions & 11 deletions apps/client/src/features/task/subtask/useSubtaskMutations.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { MutationOptions, useMutation, useQueryClient } from '@tanstack/react-query';
import { subtaskAPI } from '@/features/task/subtask/api';
import { Subtask, UpdateSubtaskDto } from '@/features/task/subtask/types';
import { BaseResponse } from '@/features/types.ts';

export const useSubtaskMutations = (taskId: number) => {
const queryClient = useQueryClient();

const invalidateTask = () => {
queryClient.invalidateQueries({
queryKey: ['task', taskId],
refetchType: 'none',
});
};

return {
create: (options?: MutationOptions<Subtask, Error, void>) =>
useMutation({
mutationFn: async () => {
const data = await subtaskAPI.create(taskId);
const { subtask } = data.result;
const { result } = await subtaskAPI.create(taskId);

return subtask;
return result;
},
onSuccess: (data, variables, context) => {
invalidateTask();
Expand Down Expand Up @@ -48,10 +47,9 @@ export const useSubtaskMutations = (taskId: number) => {
subtaskId: number;
updateSubtaskDto: UpdateSubtaskDto;
}) => {
const data = await subtaskAPI.update(subtaskId, updateSubtaskDto);
const { subtask } = data.result;
const { result } = await subtaskAPI.update(subtaskId, updateSubtaskDto);

return subtask;
return result;
},
onSuccess: (data, variables, context) => {
invalidateTask();
Expand All @@ -62,13 +60,12 @@ export const useSubtaskMutations = (taskId: number) => {
},
}),

delete: (options?: MutationOptions<Subtask, Error, number>) =>
delete: (options?: MutationOptions<BaseResponse, Error, number>) =>
useMutation({
mutationFn: async (subtaskId: number) => {
const data = await subtaskAPI.delete(subtaskId);
const { subtask } = data.result;
const response = await subtaskAPI.delete(subtaskId);

return subtask;
return response;
},
onSuccess: (data, variables, context) => {
invalidateTask();
Expand Down
6 changes: 3 additions & 3 deletions apps/client/src/features/task/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { Assignee, Label, Sprint } from '@/features/types.ts';

export interface UpdateTaskDto {
description?: string;
priority?: number;
sprintId?: number;
estimate?: number;
priority?: number | null;
sprintId?: number | null;
estimate?: number | null;
}

export interface DetailedTask {
Expand Down
10 changes: 5 additions & 5 deletions apps/client/src/features/task/useTaskMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@ export const useTaskMutations = (taskId: number) => {
}),

updatePriority: useMutation({
mutationFn: (priority?: number) => taskAPI.update(taskId, { priority }),
mutationFn: (priority?: number) => taskAPI.update(taskId, { priority: priority ?? null }),
onSuccess: invalidateTask,
}),

updateSprint: useMutation({
mutationFn: (sprintId?: number) => taskAPI.update(taskId, { sprintId }),
mutationFn: (sprintId?: number) => taskAPI.update(taskId, { sprintId: sprintId ?? null }),
onSuccess: invalidateTask,
}),

updateEstimate: useMutation({
mutationFn: (estimate?: number) => taskAPI.update(taskId, { estimate }),
mutationFn: (estimate?: number) => taskAPI.update(taskId, { estimate: estimate ?? null }),
onSuccess: invalidateTask,
}),

updateAssignees: useMutation({
mutationFn: (userIds?: number[]) => taskAPI.updateAssignees(taskId, userIds),
mutationFn: (assignees?: number[]) => taskAPI.updateAssignees(taskId, assignees),
onSuccess: invalidateTask,
}),

updateLabels: useMutation({
mutationFn: (labelIds?: number[]) => taskAPI.updateLabels(taskId, labelIds),
mutationFn: (labels?: number[]) => taskAPI.updateLabels(taskId, labels),
onSuccess: invalidateTask,
}),
};
Expand Down
2 changes: 0 additions & 2 deletions apps/client/src/pages/Board.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Suspense } from 'react';
import { Outlet } from '@tanstack/react-router';
import KanbanBoard from '@/components/KanbanBoard.tsx';

export default function Board() {
Expand All @@ -8,7 +7,6 @@ export default function Board() {
<Suspense fallback={<LoadingFallback />}>
<KanbanBoard />
</Suspense>
<Outlet />
</div>
);
}
Expand Down
13 changes: 7 additions & 6 deletions apps/client/src/pages/TaskDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import Estimate from '@/features/task/components/Estimate.tsx';

export function TaskDetail() {
const { taskId } = useLoaderData({ from: '/_auth/$project/board/$taskId' });
const navigate = useNavigate({ from: '/$project/board/$taskId' });
const navigate = useNavigate({
from: '/$project/board/$taskId',
});
const [isClosing, setIsClosing] = useState(false);

const { data: task } = useTaskQuery(taskId);

if (!task) {
Expand All @@ -33,7 +34,7 @@ export function TaskDetail() {
return () => {
document.body.style.overflow = 'unset';
};
}, []);
}, [isClosing]);

const handleClose = useCallback(async () => {
setIsClosing(true);
Expand All @@ -43,7 +44,7 @@ export function TaskDetail() {
}, [navigate]);

return (
<AnimatePresence mode="wait" onExitComplete={() => setIsClosing(false)}>
<AnimatePresence key={taskId} mode="wait" onExitComplete={() => setIsClosing(false)}>
{!isClosing && (
<>
<motion.div
Expand Down Expand Up @@ -71,15 +72,15 @@ export function TaskDetail() {
<Card className="h-full rounded-none border-none">
<CardHeader className="bg-blue sticky top-0 z-40 h-[100px] backdrop-blur">
<div className="flex h-full items-center justify-between">
<h2 className="text-3xl font-semibold">Make Project Bigger</h2>
<h2 className="text-3xl font-semibold">{task.title}</h2>
<Button variant="ghost" size="icon" onClick={handleClose}>
<X className="h-4 w-4" />
</Button>
</div>
</CardHeader>
<CardContent className="mt-4 grid grid-cols-3 gap-6">
<div className="col-span-2 space-y-6 pr-4">
<TaskDescription initialDescription="" />
<TaskDescription initialDescription={task.description} />
<Subtasks initialSubtasks={task.subtasks} />
</div>

Expand Down
9 changes: 7 additions & 2 deletions apps/client/src/routes/_auth.$project.board.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createFileRoute } from '@tanstack/react-router';
import { createFileRoute, Outlet } from '@tanstack/react-router';
import Board from '@/pages/Board.tsx';
import { TasksResponse } from '@/components/KanbanBoard.tsx';
import { axiosInstance } from '@/lib/axios.ts';
Expand All @@ -17,5 +17,10 @@ export const Route = createFileRoute('/_auth/$project/board')({
onError: (error) => {
console.error(error);
},
component: Board,
component: () => (
<>
<Board />
<Outlet />
</>
),
});
6 changes: 6 additions & 0 deletions apps/server/src/task/controller/task.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export class TaskController {
return this.taskService.get(user.id, id);
}

@Get(':id/detail')
@ResponseMessage('태스크 상세 정보 조회 완료했습니다.')
async getTaskDetail(@AuthUser() user: Account, @Param('id') id: number) {
return this.taskService.getTaskDetail(user.id, id);
}

@Patch(':id')
@ResponseMessage('태스크 상세 정보 수정 완료했습니다.')
async updateDetails(
Expand Down
4 changes: 3 additions & 1 deletion apps/server/src/task/service/task.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,9 @@ export class TaskService {
await this.validateUserRole(userId, task.section.project.id);

const result = new TaskDetailsResponse(task);
const sprint = await this.sprintRepository.findOneBy({ id: task.sprintId });
const sprint = task.sprintId
? await this.sprintRepository.findOneBy({ id: task.sprintId })
: null;
result.setSprint(sprint ? new SprintDetailsResponse(sprint) : null);

const taskAssigneeRecords = await this.taskAssigneeRepository
Expand Down

0 comments on commit 1339e50

Please sign in to comment.