feat: 중간 점검 보고 내용은 지시자·담당자만 열람(상태는 전원 노출)
보고 '내용' 열람을 지시자 + 해당 업무 담당자로 제한: - 백엔드 buildDetail: viewerId 기준으로 권한 없으면 checkpointReports 를 빈 배열로 응답(findOne 에 현재 사용자 전달). 점검일에는 reported 플래그를 두어 상태는 전원 공유 - 프론트: checkpointState 는 reported 플래그 기준, '보고 완료' 클릭(모달)은 지시자·담당자만. 그 외는 비클릭 상태 배지 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -107,8 +107,9 @@ export class TaskController {
|
||||
findOne(
|
||||
@Param('projectId') projectId: string,
|
||||
@Param('taskId', ParseIntPipe) taskId: number,
|
||||
@CurrentUser() user: PublicUser,
|
||||
): Promise<TaskDetailResponse> {
|
||||
return this.taskService.findOne(projectId, taskId);
|
||||
return this.taskService.findOne(projectId, taskId, user.id);
|
||||
}
|
||||
|
||||
@Patch(':taskId')
|
||||
|
||||
@@ -73,6 +73,8 @@ export interface ChecklistItemResponse {
|
||||
export interface CheckpointResponse {
|
||||
id: string;
|
||||
checkAt: string;
|
||||
// 보고 접수 여부(상태 표시용) — 내용 열람 권한과 무관하게 전원에게 노출
|
||||
reported: boolean;
|
||||
}
|
||||
|
||||
// 중간 점검 보고 응답(스냅샷 포함)
|
||||
@@ -343,10 +345,14 @@ export class TaskService {
|
||||
}
|
||||
|
||||
// 업무 단건 상세 — project 내 순번(seq)으로 조회
|
||||
async findOne(projectId: string, seq: number): Promise<TaskDetailResponse> {
|
||||
async findOne(
|
||||
projectId: string,
|
||||
seq: number,
|
||||
viewerId: string,
|
||||
): Promise<TaskDetailResponse> {
|
||||
const project = await this.getProjectOrThrow(projectId);
|
||||
const task = await this.getTaskOrThrow(project.id, seq);
|
||||
return this.buildDetail(task, project);
|
||||
return this.buildDetail(task, project, viewerId);
|
||||
}
|
||||
|
||||
// 업무 생성 — admin(지시자) 권한. 담당자는 프로젝트 멤버만 지정 가능
|
||||
@@ -413,6 +419,7 @@ export class TaskService {
|
||||
return this.buildDetail(
|
||||
await this.getTaskOrThrow(project.id, saved.seq),
|
||||
project,
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -510,6 +517,7 @@ export class TaskService {
|
||||
return this.buildDetail(
|
||||
await this.getTaskOrThrow(project.id, seq),
|
||||
project,
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -688,6 +696,7 @@ export class TaskService {
|
||||
return this.buildDetail(
|
||||
await this.getTaskOrThrow(project.id, seq),
|
||||
project,
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -920,6 +929,7 @@ export class TaskService {
|
||||
return this.buildDetail(
|
||||
await this.getTaskOrThrow(project.id, seq),
|
||||
project,
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -937,6 +947,7 @@ export class TaskService {
|
||||
return this.buildDetail(
|
||||
await this.getTaskOrThrow(project.id, seq),
|
||||
project,
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -978,6 +989,7 @@ export class TaskService {
|
||||
return this.buildDetail(
|
||||
await this.getTaskOrThrow(project.id, seq),
|
||||
project,
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1422,6 +1434,7 @@ export class TaskService {
|
||||
return this.buildDetail(
|
||||
await this.getTaskOrThrow(project.id, seq),
|
||||
project,
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1476,6 +1489,7 @@ export class TaskService {
|
||||
private async buildDetail(
|
||||
task: Task,
|
||||
project: Project,
|
||||
viewerId: string,
|
||||
): Promise<TaskDetailResponse> {
|
||||
const [comments, files, activities, checkpointRows, reportRows] =
|
||||
await Promise.all([
|
||||
@@ -1505,23 +1519,35 @@ export class TaskService {
|
||||
workStatus: c.workStatus,
|
||||
}));
|
||||
|
||||
// 점검일별 보고 접수 여부(상태 배지용) — 전원 노출
|
||||
const reportedCheckpointIds = new Set(
|
||||
reportRows
|
||||
.map((r) => r.checkpoint?.id)
|
||||
.filter((id): id is string => !!id),
|
||||
);
|
||||
const checkpoints: CheckpointResponse[] = checkpointRows.map((c) => ({
|
||||
id: c.id,
|
||||
checkAt: c.checkAt.toISOString(),
|
||||
reported: reportedCheckpointIds.has(c.id),
|
||||
}));
|
||||
const checkpointReports: CheckpointReportResponse[] = reportRows.map(
|
||||
(r) => ({
|
||||
id: r.id,
|
||||
checkpointId: r.checkpoint?.id ?? '',
|
||||
checkAt: r.checkpoint?.checkAt
|
||||
? r.checkpoint.checkAt.toISOString()
|
||||
: null,
|
||||
reporter: r.reporter ? UserService.toPublic(r.reporter) : null,
|
||||
note: r.note,
|
||||
items: r.itemsSnapshot ?? [],
|
||||
createdAt: r.createdAt.toISOString(),
|
||||
}),
|
||||
);
|
||||
|
||||
// 보고 '내용'은 지시자 + 해당 업무 담당자만 열람 가능(그 외에는 빈 배열)
|
||||
const canViewReports =
|
||||
task.issuer?.id === viewerId ||
|
||||
(task.assignees ?? []).some((a) => a.id === viewerId);
|
||||
const checkpointReports: CheckpointReportResponse[] = canViewReports
|
||||
? reportRows.map((r) => ({
|
||||
id: r.id,
|
||||
checkpointId: r.checkpoint?.id ?? '',
|
||||
checkAt: r.checkpoint?.checkAt
|
||||
? r.checkpoint.checkAt.toISOString()
|
||||
: null,
|
||||
reporter: r.reporter ? UserService.toPublic(r.reporter) : null,
|
||||
note: r.note,
|
||||
items: r.itemsSnapshot ?? [],
|
||||
createdAt: r.createdAt.toISOString(),
|
||||
}))
|
||||
: [];
|
||||
|
||||
// 승인 정보는 승인 대기(review) 상태이고 요청 메타가 있을 때만 노출
|
||||
const approval: ApprovalInfo | null =
|
||||
|
||||
Reference in New Issue
Block a user