From 5303b2e92eeb0f1da6b90e060a049be3ce82596e Mon Sep 17 00:00:00 2001 From: TtiPo Date: Tue, 21 Jul 2026 23:08:50 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=95=A0=20=EC=9D=BC=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=ED=95=AD=EB=AA=A9=20=EC=A0=95=EB=A6=AC=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 오래 쓴 라벨에 완료 항목이 계속 쌓여 카드가 길어지는 문제를 해소한다. - 목록 상단에 '완료 항목 숨기기' 토글 — 화면에서만 걸러내고 데이터는 유지 - 라벨 메뉴에 '완료 항목 N개 삭제' — 확인 후 일괄 삭제(DELETE labels/:id/done-items) - 완료 항목이 없으면 두 진입점 모두 노출하지 않는다 - 숨김 상태에서도 진행 집계는 전체 기준을 유지한다 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/src/modules/todo/todo.controller.ts | 12 +++++++++ backend/src/modules/todo/todo.service.ts | 11 ++++++++ .../src/components/todo/TodoLabelCard.vue | 9 +++++++ frontend/src/composables/useTodo.ts | 8 ++++++ frontend/src/pages/relay/TodoPage.vue | 26 +++++++++++++++++++ frontend/src/stores/todo.store.ts | 25 ++++++++++++++++-- 6 files changed, 89 insertions(+), 2 deletions(-) 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 { > 라벨 편집 + +