forked from Yara724/api
203 lines
7.0 KiB
TypeScript
203 lines
7.0 KiB
TypeScript
import {
|
||
Body,
|
||
Controller,
|
||
Get,
|
||
HttpException,
|
||
InternalServerErrorException,
|
||
Param,
|
||
Post,
|
||
Put,
|
||
Query,
|
||
UseGuards,
|
||
} from "@nestjs/common";
|
||
import {
|
||
ApiBearerAuth,
|
||
ApiBody,
|
||
ApiOperation,
|
||
ApiParam,
|
||
ApiResponse,
|
||
ApiTags,
|
||
} from "@nestjs/swagger";
|
||
import { ExpertFileAssignResultDto } from "src/common/dto/expert-file-assign-result.dto";
|
||
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
||
import { RolesGuard } from "src/auth/guards/role.guard";
|
||
import { Roles } from "src/decorators/roles.decorator";
|
||
import { CurrentUser } from "src/decorators/user.decorator";
|
||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||
import { ExpertBlameService } from "./expert-blame.service";
|
||
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||
import { AllRequestDtoRsV2 } from "./dto/all-request.dto";
|
||
import { SubmitReplyDto } from "./dto/reply.dto";
|
||
import { ResendRequestDto } from "./dto/resend.dto";
|
||
|
||
@ApiTags("expert-blame-panel (v2)")
|
||
@Controller("v2/expert-blame")
|
||
@ApiBearerAuth()
|
||
@UseGuards(LocalActorAuthGuard, RolesGuard)
|
||
@Roles(RoleEnum.EXPERT, RoleEnum.FIELD_EXPERT)
|
||
export class ExpertBlameV2Controller {
|
||
constructor(private readonly expertBlameService: ExpertBlameService) {}
|
||
|
||
@Get()
|
||
@ApiOperation({
|
||
summary: "List blame cases for expert review (V2)",
|
||
description:
|
||
"Damage experts (`expert`): tenant-scoped **DISAGREEMENT** queue (available, locked, or decided by you). " +
|
||
"Field experts (`field_expert`): all expert-initiated blame files they created (LINK + IN_PERSON). Use expert-claim for claims after blame completion. " +
|
||
"Optional query: `search`, `sortBy` (publicId | createdAt | requestNo | status), `sortOrder`, `page`, `limit`.",
|
||
})
|
||
async findAll(
|
||
@CurrentUser() actor: any,
|
||
@Query() query: ListQueryV2Dto,
|
||
): Promise<AllRequestDtoRsV2> {
|
||
try {
|
||
return await this.expertBlameService.findAllV2(actor, query);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error ? error.message : "Failed to list blame cases",
|
||
);
|
||
}
|
||
}
|
||
|
||
@Get("report/status-counts")
|
||
@ApiOperation({
|
||
summary: "Count blame cases by grouped status bucket (this field expert)",
|
||
description:
|
||
"Counts only files in the acting expert’s portfolio: expertFileActivities (checked or handled), workflow lock, expert decision, or expert-initiated blame. IN_PROGRESS groups OPEN and WAITING_FOR_SECOND_PARTY. Other keys match CaseStatus. Does not include the open WAITING_FOR_EXPERT queue unless this expert has touched the file.",
|
||
})
|
||
async getStatusReportBucketsV2(@CurrentUser() actor: any) {
|
||
try {
|
||
return await this.expertBlameService.getStatusReportBucketsV2(actor);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error
|
||
? error.message
|
||
: "Failed to load blame status report",
|
||
);
|
||
}
|
||
}
|
||
|
||
@Get(":id")
|
||
@ApiParam({ name: "id", description: "Blame case request id" })
|
||
async findOne(@Param("id") id: string, @CurrentUser() actor: any) {
|
||
try {
|
||
return await this.expertBlameService.findOneV2(id, actor);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error
|
||
? error.message
|
||
: "Failed to get blame case details",
|
||
);
|
||
}
|
||
}
|
||
|
||
@Post(":id/assign")
|
||
@ApiOperation({
|
||
summary: "Check availability and assign blame case to this expert",
|
||
description:
|
||
"Call before opening a case from the list. Returns `assigned` when the case was free and is now locked to you, `already_assigned_to_you` when you already hold the lock, or **409** with `status=locked` when another expert is reviewing. Does not replace PUT lock — use this for list UI availability.",
|
||
})
|
||
@ApiParam({ name: "id", description: "Blame case request id" })
|
||
@ApiResponse({ status: 200, type: ExpertFileAssignResultDto })
|
||
@ApiResponse({
|
||
status: 409,
|
||
description: "Another expert is processing this case",
|
||
type: ExpertFileAssignResultDto,
|
||
})
|
||
async assignForReview(@Param("id") id: string, @CurrentUser() actor: any) {
|
||
try {
|
||
return await this.expertBlameService.assignBlameCaseForReviewV2(
|
||
id,
|
||
actor,
|
||
);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error ? error.message : "Failed to assign blame case",
|
||
);
|
||
}
|
||
}
|
||
|
||
@Put("lock/:id")
|
||
@ApiOperation({
|
||
summary: "Lock blame case for review (legacy)",
|
||
description:
|
||
"Same assignment logic as POST `:id/assign`, but returns the legacy `{ _id, lock }` shape and maps conflicts to 400 instead of 409.",
|
||
deprecated: true,
|
||
})
|
||
@ApiParam({ name: "id", description: "Blame case request id" })
|
||
async lockRequest(@Param("id") id: string, @CurrentUser() actor: any) {
|
||
try {
|
||
return await this.expertBlameService.lockRequestV2(id, actor);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error ? error.message : "Failed to lock blame case",
|
||
);
|
||
}
|
||
}
|
||
|
||
@Put("reply/resend/:id")
|
||
@ApiParam({ name: "id", description: "Blame case request id" })
|
||
@ApiBody({ type: ResendRequestDto })
|
||
async resendRequest(
|
||
@Param("id") id: string,
|
||
@Body() body: ResendRequestDto,
|
||
@CurrentUser() actor: any,
|
||
) {
|
||
try {
|
||
return await this.expertBlameService.resendRequestV2(id, body, actor);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error
|
||
? error.message
|
||
: "Failed to request document resend",
|
||
);
|
||
}
|
||
}
|
||
|
||
@Put("reply/submit/:id")
|
||
@ApiParam({ name: "id", description: "Blame case request id" })
|
||
@ApiBody({ type: SubmitReplyDto })
|
||
async submitReply(
|
||
@Param("id") id: string,
|
||
@Body() body: SubmitReplyDto,
|
||
@CurrentUser() actor: any,
|
||
) {
|
||
try {
|
||
return await this.expertBlameService.replyRequestV2(id, body, actor);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error
|
||
? error.message
|
||
: "Failed to submit expert reply",
|
||
);
|
||
}
|
||
}
|
||
|
||
@Put("reply/inPerson/:id")
|
||
@ApiParam({ name: "id", description: "Blame case request id" })
|
||
@ApiBody({ type: SubmitReplyDto })
|
||
async submitInPerson(
|
||
@Param("id") id: string,
|
||
@Body() body: SubmitReplyDto,
|
||
@CurrentUser() actor: any,
|
||
) {
|
||
try {
|
||
return await this.expertBlameService.submitInPerson(id, actor.sub);
|
||
} catch (error) {
|
||
if (error instanceof HttpException) throw error;
|
||
throw new InternalServerErrorException(
|
||
error instanceof Error
|
||
? error.message
|
||
: "Failed to submit expert reply for in person",
|
||
);
|
||
}
|
||
}
|
||
}
|