feat: 수정 요청 메시지를 선택 사항으로 변경

수정 요청 시 공용 메시지 입력을 필수에서 선택으로 완화한다.

- 백엔드: ApprovalChangesDto.note 를 선택값으로 변경, requestChanges 가
  빈 메시지면 changesNote 를 null 로 저장
- 프론트: 제출 버튼 비활성(메시지 필수) 해제, 빈 메시지는 미전송,
  안내/라벨 문구를 '선택'으로 정리

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 19:42:37 +09:00
parent 70d75cf88d
commit 3b78574855
6 changed files with 15 additions and 19 deletions
+5 -5
View File
@@ -2,7 +2,6 @@ import {
ArrayMaxSize,
IsArray,
IsBoolean,
IsNotEmpty,
IsOptional,
IsString,
IsUUID,
@@ -45,16 +44,17 @@ export class ChecklistReviewDto {
note?: string;
}
// 수정 요청(지시자 → 담당자) — 보완 내용 필수 + 항목별 완료 체크(선택)
// 수정 요청(지시자 → 담당자) — 보완 메시지(선택) + 항목별 완료 체크(선택)
export class ApprovalChangesDto {
@ApiProperty({
description: '수정 요청 메시지(보완이 필요한 내용)',
description: '수정 요청 메시지(선택)',
required: false,
example: '세로형 카피 가독성이 낮으니 자간과 대비를 조정해 주세요.',
})
@IsOptional()
@IsString()
@IsNotEmpty({ message: '수정 요청 내용을 입력해 주세요.' })
@MaxLength(2000)
note!: string;
note?: string;
@ApiProperty({
description: '항목별 완료 검토 결과(체크/X). 생략 시 체크리스트 변경 없음',
+2 -2
View File
@@ -892,14 +892,14 @@ export class TaskService {
projectId: string,
actingUserId: string,
seq: number,
note: string,
note: string | undefined,
reviews?: { id: string; done: boolean; note?: string }[],
): Promise<TaskDetailResponse> {
const project = await this.getProjectOrThrow(projectId);
const membership = await this.assertMember(project.id, actingUserId);
const task = await this.getTaskOrThrow(project.id, seq);
task.changesNote = note;
task.changesNote = note?.trim() ? note.trim() : null;
task.approvalNote = null;
task.approvalRequestedAt = null;
+1 -1
View File
@@ -140,7 +140,7 @@ export function useTask() {
async function requestChanges(
projectId: string,
taskId: number,
note: string,
note: string | undefined,
checklist?: ChecklistReviewItem[],
): Promise<ApiTaskDetail> {
return (await api.post(`/projects/${projectId}/tasks/${taskId}/approval/changes`, {
+4 -9
View File
@@ -154,10 +154,6 @@ const reviewPct = computed(() =>
)
// 보완 항목이 없으면 곧바로 승인 가능
const reviewAllDone = computed(() => reviewTodoCount.value === 0)
// 제출 가능 여부 — 보완 항목이 있으면 메시지 필수
const reviewSubmittable = computed(
() => reviewAllDone.value || changesNote.value.trim().length > 0,
)
// 담당자 표기(푸터)
const assigneeNames = computed(() => {
const names = task.value?.assignees.map((a) => a.name) ?? []
@@ -205,7 +201,7 @@ function groupClass(cat: ChecklistCategory): string {
}
// 검토 제출 — 전부 완료면 승인, 보완 항목이 있으면 수정 요청
async function submitChanges() {
if (!task.value || !reviewSubmittable.value) return
if (!task.value) return
try {
if (reviewAllDone.value) {
task.value = await taskStore.approve(projectId.value, task.value.id)
@@ -222,7 +218,7 @@ async function submitChanges() {
task.value = await taskStore.requestChanges(
projectId.value,
task.value.id,
changesNote.value.trim(),
changesNote.value.trim() || undefined,
checklist.length ? checklist : undefined,
)
}
@@ -1452,12 +1448,12 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
모든 항목이 완료되었습니다. 메시지 없이 바로 승인할 있어요.
</span>
<span v-else>
<b>{{ reviewTodoCount }}</b> 항목에 보완이 필요합니다. 무엇을 수정해야 하는지 적어 주세요.
<b>{{ reviewTodoCount }}</b> 항목에 보완이 필요합니다. 필요하면 보완 내용을 적어 주세요.
</span>
</div>
<div class="rev-label">
수정 요청 메시지
<span class="opt">{{ reviewAllDone ? '· 선택' : '· 보완 항목이 있어 필수' }}</span>
<span class="opt">· 선택</span>
</div>
<textarea
v-model="changesNote"
@@ -1492,7 +1488,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
class="btn"
:class="reviewAllDone ? 'approve' : 'danger'"
type="button"
:disabled="!reviewSubmittable"
@click="submitChanges"
>
<svg
+1 -1
View File
@@ -369,7 +369,7 @@ export const useTaskStore = defineStore('task', () => {
async function requestChanges(
projectId: string,
taskId: number,
note: string,
note: string | undefined,
checklist?: ChecklistReviewItem[],
): Promise<TaskDetailView> {
const view = toTaskDetailView(
+2 -1
View File
@@ -146,7 +146,8 @@ export interface ChecklistReviewItem {
note?: string
}
export interface ApprovalChangesPayload {
note: string
// 수정 요청 메시지(선택)
note?: string
// 항목별 완료 검토 결과(생략 시 체크리스트 변경 없음)
checklist?: ChecklistReviewItem[]
}