diff --git a/backend/src/modules/todo/todo.controller.ts b/backend/src/modules/todo/todo.controller.ts index 4754a52..99bd3bd 100644 --- a/backend/src/modules/todo/todo.controller.ts +++ b/backend/src/modules/todo/todo.controller.ts @@ -84,6 +84,18 @@ export class TodoController { return this.todoService.createFromTask(dto, user.id); } + @Delete('labels/:id/done-items') + @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: '라벨의 완료된 할 일 일괄 삭제' }) + @ApiResponse({ status: 200, description: '삭제 성공' }) + @ApiResponse({ status: 404, description: '라벨 없음 (RES_001)' }) + clearDoneItems( + @CurrentUser() user: PublicUser, + @Param('id', ParseUUIDPipe) id: string, + ): Promise { + return this.todoService.clearDoneItems(id, user.id); + } + // ----- 항목 ----- @Post('labels/:labelId/items') diff --git a/backend/src/modules/todo/todo.service.ts b/backend/src/modules/todo/todo.service.ts index 0bded63..e8c5c12 100644 --- a/backend/src/modules/todo/todo.service.ts +++ b/backend/src/modules/todo/todo.service.ts @@ -220,6 +220,17 @@ export class TodoService { return this.findLabelOrThrow(label.id, ownerId); } + // 라벨의 완료 항목 일괄 삭제 — 오래 쓴 라벨에 완료 항목이 쌓이는 것을 정리한다. + // 남길 항목이 없어도 라벨 자체는 유지한다(사용자가 명시적으로 지운 게 아니므로). + async clearDoneItems( + labelId: string, + ownerId: string, + ): Promise { + await this.assertLabelOwned(labelId, ownerId); + await this.itemRepo.delete({ label: { id: labelId }, done: true }); + return this.findLabelOrThrow(labelId, ownerId); + } + // ----- 항목 ----- async createItem( diff --git a/frontend/src/components/todo/TodoLabelCard.vue b/frontend/src/components/todo/TodoLabelCard.vue index 3caf8c5..fb88fe0 100644 --- a/frontend/src/components/todo/TodoLabelCard.vue +++ b/frontend/src/components/todo/TodoLabelCard.vue @@ -18,6 +18,7 @@ const emit = defineEmits<{ (e: 'remove', item: ApiTodoItem): void (e: 'edit-label'): void (e: 'delete-label'): void + (e: 'clear-done'): void }>() // 라벨 메뉴(편집/삭제) 드롭다운 @@ -155,6 +156,14 @@ function cancelEdit(): void { > 라벨 편집 + +