forked from Yara724/api
Merge pull request 'Fix SMS, and added pagination+sorting+searching for GETs' (#77) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#77
This commit is contained in:
@@ -1,12 +1,16 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty, IsOptional, IsString, MaxLength } from "class-validator";
|
||||||
import { UserModel } from "src/users/entities/schema/user.schema";
|
import { UserModel } from "src/users/entities/schema/user.schema";
|
||||||
|
|
||||||
export class UserLoginDto {
|
export class UserLoginDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: "09226187419",
|
example: "09226187419",
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "User login dto",
|
description: "Mobile number (username for OTP login)",
|
||||||
})
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(20)
|
||||||
mobile: string;
|
mobile: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
@@ -14,6 +18,9 @@ export class UserLoginDto {
|
|||||||
type: "string",
|
type: "string",
|
||||||
description: "Raw token from linked SMS URL (?token=...).",
|
description: "Raw token from linked SMS URL (?token=...).",
|
||||||
})
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(128)
|
||||||
linkToken?: string;
|
linkToken?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
@@ -21,6 +28,9 @@ export class UserLoginDto {
|
|||||||
type: "string",
|
type: "string",
|
||||||
description: "Optional route/context hint for linked SMS login.",
|
description: "Optional route/context hint for linked SMS login.",
|
||||||
})
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(64)
|
||||||
linkContext?: string;
|
linkContext?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty, IsOptional, IsString, MaxLength } from "class-validator";
|
||||||
|
|
||||||
export class UserVerifyOtp {
|
export class UserVerifyOtp {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: "09226187419",
|
example: "09226187419",
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "User login dto",
|
description: "Mobile number (same value sent to send-otp)",
|
||||||
})
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(20)
|
||||||
username: string;
|
username: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: "258567",
|
example: "258567",
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "User login verify dto",
|
description: "OTP code from SMS",
|
||||||
})
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(16)
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
@@ -20,6 +27,9 @@ export class UserVerifyOtp {
|
|||||||
type: "string",
|
type: "string",
|
||||||
description: "Raw token from linked SMS URL (?token=...).",
|
description: "Raw token from linked SMS URL (?token=...).",
|
||||||
})
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(128)
|
||||||
linkToken?: string;
|
linkToken?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
@@ -27,5 +37,8 @@ export class UserVerifyOtp {
|
|||||||
type: "string",
|
type: "string",
|
||||||
description: "Optional route/context hint for linked SMS login.",
|
description: "Optional route/context hint for linked SMS login.",
|
||||||
})
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(64)
|
||||||
linkContext?: string;
|
linkContext?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ import {
|
|||||||
VideoCaptureV2ResponseDto,
|
VideoCaptureV2ResponseDto,
|
||||||
} from "./dto/capture-part-v2.dto";
|
} from "./dto/capture-part-v2.dto";
|
||||||
import { GetMyClaimsV2ResponseDto, ClaimListItemV2Dto } from "./dto/my-claims-v2.dto";
|
import { GetMyClaimsV2ResponseDto, ClaimListItemV2Dto } from "./dto/my-claims-v2.dto";
|
||||||
|
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
|
import { applyListQueryV2 } from "src/helpers/list-query-v2";
|
||||||
import { ClaimDetailsV2ResponseDto } from "./dto/claim-details-v2.dto";
|
import { ClaimDetailsV2ResponseDto } from "./dto/claim-details-v2.dto";
|
||||||
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
||||||
import { ClaimRequiredDocumentType, CarAngle } from "src/Types&Enums/claim-request-management/required-document-type.enum";
|
import { ClaimRequiredDocumentType, CarAngle } from "src/Types&Enums/claim-request-management/required-document-type.enum";
|
||||||
@@ -6009,6 +6011,7 @@ export class ClaimRequestManagementService {
|
|||||||
async getMyClaimsV2(
|
async getMyClaimsV2(
|
||||||
currentUserId: string,
|
currentUserId: string,
|
||||||
actor?: { sub: string; role?: string },
|
actor?: { sub: string; role?: string },
|
||||||
|
query: ListQueryV2Dto = {},
|
||||||
): Promise<GetMyClaimsV2ResponseDto> {
|
): Promise<GetMyClaimsV2ResponseDto> {
|
||||||
try {
|
try {
|
||||||
let claims: any[];
|
let claims: any[];
|
||||||
@@ -6050,7 +6053,25 @@ export class ClaimRequestManagementService {
|
|||||||
createdAt: c.createdAt,
|
createdAt: c.createdAt,
|
||||||
blameRequestId: c.blameRequestId?.toString(),
|
blameRequestId: c.blameRequestId?.toString(),
|
||||||
})) as ClaimListItemV2Dto[];
|
})) as ClaimListItemV2Dto[];
|
||||||
return { list, total: list.length };
|
|
||||||
|
const paged = applyListQueryV2(list, {
|
||||||
|
publicId: (r) => r.publicId,
|
||||||
|
createdAt: (r) => r.createdAt,
|
||||||
|
requestNo: (r) => r.requestNo,
|
||||||
|
status: (r) => r.status,
|
||||||
|
searchExtras: (r) =>
|
||||||
|
[r.claimRequestId, r.claimStatus, r.currentStep, r.blameRequestId].filter(
|
||||||
|
Boolean,
|
||||||
|
) as string[],
|
||||||
|
}, query);
|
||||||
|
|
||||||
|
return {
|
||||||
|
list: paged.list,
|
||||||
|
total: paged.total,
|
||||||
|
page: paged.page,
|
||||||
|
limit: paged.limit,
|
||||||
|
totalPages: paged.totalPages,
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) throw error;
|
if (error instanceof HttpException) throw error;
|
||||||
this.logger.error('getMyClaimsV2 failed', error);
|
this.logger.error('getMyClaimsV2 failed', error);
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
VideoCaptureV2ResponseDto,
|
VideoCaptureV2ResponseDto,
|
||||||
} from "./dto/capture-part-v2.dto";
|
} from "./dto/capture-part-v2.dto";
|
||||||
import { GetMyClaimsV2ResponseDto } from "./dto/my-claims-v2.dto";
|
import { GetMyClaimsV2ResponseDto } from "./dto/my-claims-v2.dto";
|
||||||
|
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
import { ClaimDetailsV2ResponseDto } from "./dto/claim-details-v2.dto";
|
import { ClaimDetailsV2ResponseDto } from "./dto/claim-details-v2.dto";
|
||||||
import { UserObjectionV2Dto } from "./dto/user-objection-v2.dto";
|
import { UserObjectionV2Dto } from "./dto/user-objection-v2.dto";
|
||||||
import { UserRatingDto } from "./dto/user-rating.dto";
|
import { UserRatingDto } from "./dto/user-rating.dto";
|
||||||
@@ -60,16 +61,24 @@ export class ClaimRequestManagementV2Controller {
|
|||||||
@Get("requests")
|
@Get("requests")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Get My Claims (V2)",
|
summary: "Get My Claims (V2)",
|
||||||
description: "Get list of all claim requests for the current user.",
|
description:
|
||||||
|
"Claims for the current user (or field-expert in-person files). Optional query: `search`, `sortBy` (publicId | createdAt | requestNo | status), `sortOrder`, `page`, `limit`. Without `page`/`limit`, returns the full filtered list.",
|
||||||
})
|
})
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: "List of user claims",
|
description: "List of user claims",
|
||||||
type: GetMyClaimsV2ResponseDto,
|
type: GetMyClaimsV2ResponseDto,
|
||||||
})
|
})
|
||||||
async getMyClaims(@CurrentUser() user: any): Promise<GetMyClaimsV2ResponseDto> {
|
async getMyClaims(
|
||||||
|
@CurrentUser() user: any,
|
||||||
|
@Query() query: ListQueryV2Dto,
|
||||||
|
): Promise<GetMyClaimsV2ResponseDto> {
|
||||||
try {
|
try {
|
||||||
return await this.claimRequestManagementService.getMyClaimsV2(user.sub, user);
|
return await this.claimRequestManagementService.getMyClaimsV2(
|
||||||
|
user.sub,
|
||||||
|
user,
|
||||||
|
query,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) throw error;
|
if (error instanceof HttpException) throw error;
|
||||||
throw new InternalServerErrorException(
|
throw new InternalServerErrorException(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
export class ClaimListItemV2Dto {
|
export class ClaimListItemV2Dto {
|
||||||
@ApiProperty({ description: 'Claim case ID', example: '507f1f77bcf86cd799439011' })
|
@ApiProperty({ description: 'Claim case ID', example: '507f1f77bcf86cd799439011' })
|
||||||
@@ -34,6 +34,17 @@ export class GetMyClaimsV2ResponseDto {
|
|||||||
@ApiProperty({ description: 'List of user claims', type: [ClaimListItemV2Dto] })
|
@ApiProperty({ description: 'List of user claims', type: [ClaimListItemV2Dto] })
|
||||||
list: ClaimListItemV2Dto[];
|
list: ClaimListItemV2Dto[];
|
||||||
|
|
||||||
@ApiProperty({ description: 'Total count', example: 5 })
|
@ApiProperty({ description: 'Total count after search filter', example: 5 })
|
||||||
total: number;
|
total: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Current page when `page` or `limit` query params were sent',
|
||||||
|
})
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Page size when paginating' })
|
||||||
|
limit?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Total pages when paginating' })
|
||||||
|
totalPages?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
78
src/common/dto/list-query-v2.dto.ts
Normal file
78
src/common/dto/list-query-v2.dto.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import {
|
||||||
|
IsIn,
|
||||||
|
IsInt,
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
Max,
|
||||||
|
MaxLength,
|
||||||
|
Min,
|
||||||
|
} from "class-validator";
|
||||||
|
|
||||||
|
/** Allowed sort keys for V2 list endpoints (claims / blame). */
|
||||||
|
export const LIST_SORT_BY_V2 = [
|
||||||
|
"publicId",
|
||||||
|
"createdAt",
|
||||||
|
"requestNo",
|
||||||
|
"status",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type ListSortByV2 = (typeof LIST_SORT_BY_V2)[number];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional query params for V2 lists. If neither `page` nor `limit` is sent,
|
||||||
|
* the full filtered+sorted list is returned (backward compatible).
|
||||||
|
*/
|
||||||
|
export class ListQueryV2Dto {
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: "1-based page index (use together with `limit` for pagination).",
|
||||||
|
example: 1,
|
||||||
|
minimum: 1,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: "Page size (max 100). If only `page` is set, defaults to 20.",
|
||||||
|
example: 20,
|
||||||
|
minimum: 1,
|
||||||
|
maximum: 100,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(100)
|
||||||
|
limit?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
enum: LIST_SORT_BY_V2,
|
||||||
|
description: "Sort field. Default: `createdAt`.",
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsIn([...LIST_SORT_BY_V2])
|
||||||
|
sortBy?: ListSortByV2;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
enum: ["asc", "desc"],
|
||||||
|
description: "Sort direction. Default: `desc` for dates, `asc` for publicId/requestNo.",
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsIn(["asc", "desc"])
|
||||||
|
sortOrder?: "asc" | "desc";
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
"Case-insensitive substring match on `publicId` and `requestNo` (and other endpoint-specific fields).",
|
||||||
|
example: "A142",
|
||||||
|
maxLength: 64,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(64)
|
||||||
|
search?: string;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { RequestManagementModel } from "src/request-management/entities/schema/request-management.schema";
|
import { RequestManagementModel } from "src/request-management/entities/schema/request-management.schema";
|
||||||
|
|
||||||
export class AllRequestDto {
|
export class AllRequestDto {
|
||||||
@@ -122,7 +123,30 @@ export class AllRequestDtoV2 {
|
|||||||
|
|
||||||
export class AllRequestDtoRsV2 {
|
export class AllRequestDtoRsV2 {
|
||||||
data: AllRequestDtoV2[];
|
data: AllRequestDtoV2[];
|
||||||
constructor(items: AllRequestDtoV2[]) {
|
|
||||||
this.data = items;
|
@ApiProperty({ description: "Total rows after search filter" })
|
||||||
|
total: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
limit?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
totalPages?: number;
|
||||||
|
|
||||||
|
constructor(payload: {
|
||||||
|
data: AllRequestDtoV2[];
|
||||||
|
total: number;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
totalPages?: number;
|
||||||
|
}) {
|
||||||
|
this.data = payload.data;
|
||||||
|
this.total = payload.total;
|
||||||
|
this.page = payload.page;
|
||||||
|
this.limit = payload.limit;
|
||||||
|
this.totalPages = payload.totalPages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import {
|
|||||||
expertPortfolioFileIdsFromActivityEvents,
|
expertPortfolioFileIdsFromActivityEvents,
|
||||||
objectIdsFromStringSet,
|
objectIdsFromStringSet,
|
||||||
} from "src/helpers/expert-portfolio";
|
} from "src/helpers/expert-portfolio";
|
||||||
|
import { applyListQueryV2 } from "src/helpers/list-query-v2";
|
||||||
|
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service";
|
import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service";
|
||||||
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
||||||
import {
|
import {
|
||||||
@@ -275,7 +277,10 @@ export class ExpertBlameService {
|
|||||||
return buckets;
|
return buckets;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAllV2(actor: any): Promise<AllRequestDtoRsV2> {
|
async findAllV2(
|
||||||
|
actor: any,
|
||||||
|
query: ListQueryV2Dto = {},
|
||||||
|
): Promise<AllRequestDtoRsV2> {
|
||||||
try {
|
try {
|
||||||
requireActorClientKey(actor);
|
requireActorClientKey(actor);
|
||||||
const expertId = actor.sub;
|
const expertId = actor.sub;
|
||||||
@@ -354,10 +359,41 @@ export class ExpertBlameService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const items: AllRequestDtoV2[] = visibleCases.map(
|
const paged = applyListQueryV2(visibleCases, {
|
||||||
(doc) => this.mapBlameRequestToListItemV2(doc),
|
publicId: (doc) => String((doc as { publicId?: string }).publicId ?? ""),
|
||||||
|
createdAt: (doc) => (doc as { createdAt?: Date }).createdAt,
|
||||||
|
requestNo: (doc) =>
|
||||||
|
String(
|
||||||
|
(doc as { requestNo?: string }).requestNo ??
|
||||||
|
(doc as { publicId?: string }).publicId ??
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
status: (doc) => String((doc as { status?: string }).status ?? ""),
|
||||||
|
searchExtras: (doc) => {
|
||||||
|
const d = doc as {
|
||||||
|
_id?: unknown;
|
||||||
|
blameStatus?: string;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
return [
|
||||||
|
String(d._id ?? ""),
|
||||||
|
d.blameStatus,
|
||||||
|
d.type,
|
||||||
|
].filter(Boolean) as string[];
|
||||||
|
},
|
||||||
|
}, query);
|
||||||
|
|
||||||
|
const items: AllRequestDtoV2[] = paged.list.map((doc) =>
|
||||||
|
this.mapBlameRequestToListItemV2(doc),
|
||||||
);
|
);
|
||||||
return new AllRequestDtoRsV2(items);
|
|
||||||
|
return new AllRequestDtoRsV2({
|
||||||
|
data: items,
|
||||||
|
total: paged.total,
|
||||||
|
page: paged.page,
|
||||||
|
limit: paged.limit,
|
||||||
|
totalPages: paged.totalPages,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) throw error;
|
if (error instanceof HttpException) throw error;
|
||||||
this.logger.error("findAllV2 failed", error instanceof Error ? error.stack : String(error));
|
this.logger.error("findAllV2 failed", error instanceof Error ? error.stack : String(error));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
Param,
|
Param,
|
||||||
Put,
|
Put,
|
||||||
|
Query,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from "@nestjs/swagger";
|
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from "@nestjs/swagger";
|
||||||
@@ -15,6 +16,8 @@ import { Roles } from "src/decorators/roles.decorator";
|
|||||||
import { CurrentUser } from "src/decorators/user.decorator";
|
import { CurrentUser } from "src/decorators/user.decorator";
|
||||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||||
import { ExpertBlameService } from "./expert-blame.service";
|
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 { SubmitReplyDto } from "./dto/reply.dto";
|
||||||
import { ResendRequestDto } from "./dto/resend.dto";
|
import { ResendRequestDto } from "./dto/resend.dto";
|
||||||
|
|
||||||
@@ -27,9 +30,17 @@ export class ExpertBlameV2Controller {
|
|||||||
constructor(private readonly expertBlameService: ExpertBlameService) { }
|
constructor(private readonly expertBlameService: ExpertBlameService) { }
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async findAll(@CurrentUser() actor: any) {
|
@ApiOperation({
|
||||||
|
summary: "List blame cases for field expert (V2)",
|
||||||
|
description:
|
||||||
|
"Optional query: `search`, `sortBy` (publicId | createdAt | requestNo | status), `sortOrder`, `page`, `limit`. Without `page`/`limit`, returns the full filtered list.",
|
||||||
|
})
|
||||||
|
async findAll(
|
||||||
|
@CurrentUser() actor: any,
|
||||||
|
@Query() query: ListQueryV2Dto,
|
||||||
|
): Promise<AllRequestDtoRsV2> {
|
||||||
try {
|
try {
|
||||||
return await this.expertBlameService.findAllV2(actor);
|
return await this.expertBlameService.findAllV2(actor, query);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) throw error;
|
if (error instanceof HttpException) throw error;
|
||||||
throw new InternalServerErrorException(
|
throw new InternalServerErrorException(
|
||||||
|
|||||||
@@ -64,6 +64,15 @@ export class GetClaimListV2ResponseDto {
|
|||||||
@ApiProperty({ type: [ClaimListItemV2Dto] })
|
@ApiProperty({ type: [ClaimListItemV2Dto] })
|
||||||
list: ClaimListItemV2Dto[];
|
list: ClaimListItemV2Dto[];
|
||||||
|
|
||||||
@ApiProperty({ description: 'Total count' })
|
@ApiProperty({ description: 'Total count after search filter' })
|
||||||
total: number;
|
total: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Current page when paginating' })
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Page size when paginating' })
|
||||||
|
limit?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Total pages when paginating' })
|
||||||
|
totalPages?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ import {
|
|||||||
|
|
||||||
/** Maximum sum of line `totalPayment` across the claim (Toman; priced parts + factor lines after validation). */
|
/** Maximum sum of line `totalPayment` across the claim (Toman; priced parts + factor lines after validation). */
|
||||||
import { CLAIM_V2_TOTAL_PAYMENT_CAP_TOMAN } from "src/constants/repair-amount-limits";
|
import { CLAIM_V2_TOTAL_PAYMENT_CAP_TOMAN } from "src/constants/repair-amount-limits";
|
||||||
|
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
|
import { applyListQueryV2 } from "src/helpers/list-query-v2";
|
||||||
|
|
||||||
const CLAIM_V2_TOTAL_PAYMENT_CAP = CLAIM_V2_TOTAL_PAYMENT_CAP_TOMAN;
|
const CLAIM_V2_TOTAL_PAYMENT_CAP = CLAIM_V2_TOTAL_PAYMENT_CAP_TOMAN;
|
||||||
|
|
||||||
@@ -3082,7 +3084,10 @@ export class ExpertClaimService {
|
|||||||
* 2. Locked by this expert (in-progress)
|
* 2. Locked by this expert (in-progress)
|
||||||
* 3. EXPERT_VALIDATING_REPAIR_FACTORS (or legacy WAITING_FOR_INSURER_APPROVAL) + UNDER_REVIEW at EXPERT_COST_EVALUATION (factor validation queue)
|
* 3. EXPERT_VALIDATING_REPAIR_FACTORS (or legacy WAITING_FOR_INSURER_APPROVAL) + UNDER_REVIEW at EXPERT_COST_EVALUATION (factor validation queue)
|
||||||
*/
|
*/
|
||||||
async getClaimListV2(actor: any): Promise<GetClaimListV2ResponseDto> {
|
async getClaimListV2(
|
||||||
|
actor: any,
|
||||||
|
query: ListQueryV2Dto = {},
|
||||||
|
): Promise<GetClaimListV2ResponseDto> {
|
||||||
requireActorClientKey(actor);
|
requireActorClientKey(actor);
|
||||||
const actorId = actor.sub;
|
const actorId = actor.sub;
|
||||||
const clientKey = actor.clientKey as string;
|
const clientKey = actor.clientKey as string;
|
||||||
@@ -3216,7 +3221,28 @@ export class ExpertClaimService {
|
|||||||
};
|
};
|
||||||
}) as ClaimListItemV2Dto[];
|
}) as ClaimListItemV2Dto[];
|
||||||
|
|
||||||
return { list, total: list.length };
|
const paged = applyListQueryV2(list, {
|
||||||
|
publicId: (r) => r.publicId,
|
||||||
|
createdAt: (r) => r.createdAt,
|
||||||
|
requestNo: (r) => r.publicId,
|
||||||
|
status: (r) => r.status,
|
||||||
|
searchExtras: (r) =>
|
||||||
|
[
|
||||||
|
r.claimRequestId,
|
||||||
|
r.currentStep,
|
||||||
|
r.vehicle?.carName,
|
||||||
|
r.vehicle?.carModel,
|
||||||
|
r.blameRequestType,
|
||||||
|
].filter(Boolean) as string[],
|
||||||
|
}, query);
|
||||||
|
|
||||||
|
return {
|
||||||
|
list: paged.list,
|
||||||
|
total: paged.total,
|
||||||
|
page: paged.page,
|
||||||
|
limit: paged.limit,
|
||||||
|
totalPages: paged.totalPages,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import { Roles } from "src/decorators/roles.decorator";
|
|||||||
import { CurrentUser } from "src/decorators/user.decorator";
|
import { CurrentUser } from "src/decorators/user.decorator";
|
||||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||||
import { ExpertClaimService } from "./expert-claim.service";
|
import { ExpertClaimService } from "./expert-claim.service";
|
||||||
|
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
|
import { GetClaimListV2ResponseDto } from "./dto/claim-list-v2.dto";
|
||||||
import { ClaimSubmitResendV2Dto, SubmitExpertReplyV2Dto } from "./dto/expert-claim-v2.dto";
|
import { ClaimSubmitResendV2Dto, SubmitExpertReplyV2Dto } from "./dto/expert-claim-v2.dto";
|
||||||
import { UpdateClaimDamagedPartsV2Dto } from "./dto/update-claim-damaged-parts-v2.dto";
|
import { UpdateClaimDamagedPartsV2Dto } from "./dto/update-claim-damaged-parts-v2.dto";
|
||||||
import {
|
import {
|
||||||
@@ -96,10 +98,14 @@ export class ExpertClaimV2Controller {
|
|||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "List available claim requests for damage expert",
|
summary: "List available claim requests for damage expert",
|
||||||
description:
|
description:
|
||||||
"Returns claims that are WAITING_FOR_DAMAGE_EXPERT and not locked by another expert, this expert's locked/in-progress claims, and claims awaiting factor validation (`status=EXPERT_VALIDATING_REPAIR_FACTORS` or legacy `WAITING_FOR_INSURER_APPROVAL` with UNDER_REVIEW at EXPERT_COST_EVALUATION).",
|
"Returns claims that are WAITING_FOR_DAMAGE_EXPERT and not locked by another expert, this expert's locked/in-progress claims, and claims awaiting factor validation. Optional query: `search`, `sortBy` (publicId | createdAt | requestNo | status), `sortOrder`, `page`, `limit`. Without `page`/`limit`, returns the full filtered list.",
|
||||||
})
|
})
|
||||||
async getClaimListV2(@CurrentUser() actor) {
|
@ApiResponse({ status: 200, type: GetClaimListV2ResponseDto })
|
||||||
return await this.expertClaimService.getClaimListV2(actor);
|
async getClaimListV2(
|
||||||
|
@CurrentUser() actor,
|
||||||
|
@Query() query: ListQueryV2Dto,
|
||||||
|
): Promise<GetClaimListV2ResponseDto> {
|
||||||
|
return await this.expertClaimService.getClaimListV2(actor, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("request/:claimRequestId")
|
@Get("request/:claimRequestId")
|
||||||
|
|||||||
51
src/helpers/list-query-v2.spec.ts
Normal file
51
src/helpers/list-query-v2.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { applyListQueryV2 } from "./list-query-v2";
|
||||||
|
|
||||||
|
describe("applyListQueryV2", () => {
|
||||||
|
const rows = [
|
||||||
|
{ publicId: "B100", createdAt: "2026-01-01", status: "OPEN" },
|
||||||
|
{ publicId: "A200", createdAt: "2026-02-01", status: "DONE" },
|
||||||
|
{ publicId: "C050", createdAt: "2026-03-01", status: "OPEN" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
publicId: (r: (typeof rows)[0]) => r.publicId,
|
||||||
|
createdAt: (r: (typeof rows)[0]) => r.createdAt,
|
||||||
|
status: (r: (typeof rows)[0]) => r.status,
|
||||||
|
};
|
||||||
|
|
||||||
|
it("returns all rows when no pagination query", () => {
|
||||||
|
const r = applyListQueryV2(rows, getters, {});
|
||||||
|
expect(r.total).toBe(3);
|
||||||
|
expect(r.list).toHaveLength(3);
|
||||||
|
expect(r.page).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("filters by search on publicId", () => {
|
||||||
|
const r = applyListQueryV2(rows, getters, { search: "a200" });
|
||||||
|
expect(r.total).toBe(1);
|
||||||
|
expect(r.list[0].publicId).toBe("A200");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sorts by publicId ascending", () => {
|
||||||
|
const r = applyListQueryV2(rows, getters, {
|
||||||
|
sortBy: "publicId",
|
||||||
|
sortOrder: "asc",
|
||||||
|
});
|
||||||
|
expect(r.list.map((x) => x.publicId)).toEqual(["A200", "B100", "C050"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("paginates when page and limit are set", () => {
|
||||||
|
const r = applyListQueryV2(rows, getters, {
|
||||||
|
sortBy: "publicId",
|
||||||
|
sortOrder: "asc",
|
||||||
|
page: 2,
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
expect(r.total).toBe(3);
|
||||||
|
expect(r.list).toHaveLength(1);
|
||||||
|
expect(r.list[0].publicId).toBe("B100");
|
||||||
|
expect(r.page).toBe(2);
|
||||||
|
expect(r.limit).toBe(1);
|
||||||
|
expect(r.totalPages).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
116
src/helpers/list-query-v2.ts
Normal file
116
src/helpers/list-query-v2.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import type { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
|
|
||||||
|
const MAX_LIMIT = 100;
|
||||||
|
const DEFAULT_LIMIT_WHEN_PAGE_SET = 20;
|
||||||
|
|
||||||
|
export type ListItemGetters<T> = {
|
||||||
|
publicId: (row: T) => string | undefined;
|
||||||
|
createdAt: (row: T) => Date | string | number | undefined;
|
||||||
|
requestNo?: (row: T) => string | undefined;
|
||||||
|
status: (row: T) => string | undefined;
|
||||||
|
/** Extra strings to match against `search` (e.g. claimRequestId, vehicle). */
|
||||||
|
searchExtras?: (row: T) => string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizeSearch(raw?: string): string | undefined {
|
||||||
|
const t = raw?.trim().toLowerCase();
|
||||||
|
return t || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toTime(v: Date | string | number | undefined): number {
|
||||||
|
if (v == null) return 0;
|
||||||
|
const n = v instanceof Date ? v.getTime() : new Date(v as string | number).getTime();
|
||||||
|
return Number.isFinite(n) ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter, sort, and optionally paginate in-memory list results.
|
||||||
|
* Pagination applies only when `query.page` or `query.limit` is set.
|
||||||
|
*/
|
||||||
|
export function applyListQueryV2<T>(
|
||||||
|
rows: T[],
|
||||||
|
getters: ListItemGetters<T>,
|
||||||
|
query: ListQueryV2Dto,
|
||||||
|
): {
|
||||||
|
list: T[];
|
||||||
|
total: number;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
totalPages?: number;
|
||||||
|
} {
|
||||||
|
const search = normalizeSearch(query.search);
|
||||||
|
let filtered = rows;
|
||||||
|
if (search) {
|
||||||
|
filtered = rows.filter((row) => {
|
||||||
|
const chunks: string[] = [];
|
||||||
|
const pid = getters.publicId(row);
|
||||||
|
if (pid) chunks.push(pid);
|
||||||
|
const rno = getters.requestNo?.(row);
|
||||||
|
if (rno) chunks.push(rno);
|
||||||
|
const st = getters.status(row);
|
||||||
|
if (st) chunks.push(st);
|
||||||
|
const extras = getters.searchExtras?.(row) ?? [];
|
||||||
|
for (const e of extras) {
|
||||||
|
if (e) chunks.push(e);
|
||||||
|
}
|
||||||
|
const hay = chunks.join(" ").toLowerCase();
|
||||||
|
return hay.includes(search);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortBy = query.sortBy ?? "createdAt";
|
||||||
|
let sortOrder = query.sortOrder;
|
||||||
|
if (!sortOrder) {
|
||||||
|
sortOrder =
|
||||||
|
sortBy === "publicId" || sortBy === "requestNo" ? "asc" : "desc";
|
||||||
|
}
|
||||||
|
const dir = sortOrder === "asc" ? 1 : -1;
|
||||||
|
|
||||||
|
const sorted = [...filtered].sort((a, b) => {
|
||||||
|
switch (sortBy) {
|
||||||
|
case "publicId": {
|
||||||
|
const va = String(getters.publicId(a) ?? "");
|
||||||
|
const vb = String(getters.publicId(b) ?? "");
|
||||||
|
return dir * va.localeCompare(vb, "en", { numeric: true, sensitivity: "base" });
|
||||||
|
}
|
||||||
|
case "requestNo": {
|
||||||
|
const va = String(
|
||||||
|
getters.requestNo?.(a) ?? getters.publicId(a) ?? "",
|
||||||
|
);
|
||||||
|
const vb = String(
|
||||||
|
getters.requestNo?.(b) ?? getters.publicId(b) ?? "",
|
||||||
|
);
|
||||||
|
return dir * va.localeCompare(vb, "en", { numeric: true, sensitivity: "base" });
|
||||||
|
}
|
||||||
|
case "status": {
|
||||||
|
const va = String(getters.status(a) ?? "");
|
||||||
|
const vb = String(getters.status(b) ?? "");
|
||||||
|
return dir * va.localeCompare(vb, "en", { sensitivity: "base" });
|
||||||
|
}
|
||||||
|
case "createdAt":
|
||||||
|
default: {
|
||||||
|
const ta = toTime(getters.createdAt(a));
|
||||||
|
const tb = toTime(getters.createdAt(b));
|
||||||
|
return dir * (ta - tb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const total = sorted.length;
|
||||||
|
const wantsPage = query.page != null;
|
||||||
|
const wantsLimit = query.limit != null;
|
||||||
|
if (!wantsPage && !wantsLimit) {
|
||||||
|
return { list: sorted, total };
|
||||||
|
}
|
||||||
|
|
||||||
|
const limit = Math.min(
|
||||||
|
MAX_LIMIT,
|
||||||
|
Math.max(1, query.limit ?? (wantsPage ? DEFAULT_LIMIT_WHEN_PAGE_SET : MAX_LIMIT)),
|
||||||
|
);
|
||||||
|
const page = Math.max(1, query.page ?? 1);
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
const list = sorted.slice(skip, skip + limit);
|
||||||
|
const totalPages = Math.max(1, Math.ceil(total / limit));
|
||||||
|
|
||||||
|
return { list, total, page, limit, totalPages };
|
||||||
|
}
|
||||||
23
src/request-management/dto/blame-list-user-v2.dto.ts
Normal file
23
src/request-management/dto/blame-list-user-v2.dto.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
/** User / field-expert blame list (v2 GET /v2/blame-request-management). */
|
||||||
|
export class GetUserBlameListV2ResponseDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: "array",
|
||||||
|
description: "Blame requests for the current user (parties stripped)",
|
||||||
|
items: { type: "object", additionalProperties: true },
|
||||||
|
})
|
||||||
|
list: Record<string, unknown>[];
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Count after `search` filter" })
|
||||||
|
total: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
limit?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
totalPages?: number;
|
||||||
|
}
|
||||||
@@ -42,6 +42,9 @@ import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
|
|||||||
import { ExpertDbService } from "src/users/entities/db-service/expert.db.service";
|
import { ExpertDbService } from "src/users/entities/db-service/expert.db.service";
|
||||||
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
||||||
import { parseIranLocalDateTime } from "src/helpers/iran-datetime";
|
import { parseIranLocalDateTime } from "src/helpers/iran-datetime";
|
||||||
|
import { applyListQueryV2 } from "src/helpers/list-query-v2";
|
||||||
|
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
|
import { GetUserBlameListV2ResponseDto } from "src/request-management/dto/blame-list-user-v2.dto";
|
||||||
import { AutoCloseRequestService } from "src/utils/cron/cron.service";
|
import { AutoCloseRequestService } from "src/utils/cron/cron.service";
|
||||||
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
|
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
|
||||||
import {
|
import {
|
||||||
@@ -4147,7 +4150,10 @@ export class RequestManagementService {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllBlameRequestsV2(user: any): Promise<any> {
|
async getAllBlameRequestsV2(
|
||||||
|
user: any,
|
||||||
|
query: ListQueryV2Dto = {},
|
||||||
|
): Promise<GetUserBlameListV2ResponseDto> {
|
||||||
try {
|
try {
|
||||||
const userIdFilter =
|
const userIdFilter =
|
||||||
user?.sub && Types.ObjectId.isValid(user.sub)
|
user?.sub && Types.ObjectId.isValid(user.sub)
|
||||||
@@ -4158,13 +4164,16 @@ export class RequestManagementService {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const filters = [userIdFilter, phoneFilter].filter(Boolean);
|
const filters = [userIdFilter, phoneFilter].filter(Boolean);
|
||||||
if (filters.length === 0) return [];
|
if (filters.length === 0) {
|
||||||
|
return { list: [], total: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
const requests = await this.blameRequestDbService.find(
|
const requests = await this.blameRequestDbService.find(
|
||||||
filters.length === 1 ? (filters[0] as any) : ({ $or: filters } as any),
|
filters.length === 1 ? (filters[0] as any) : ({ $or: filters } as any),
|
||||||
{
|
{
|
||||||
select: "requestNo type status blameStatus createdAt updatedAt parties",
|
select:
|
||||||
}
|
"publicId requestNo type status blameStatus createdAt updatedAt parties",
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const enriched = requests.map((req: any) => {
|
const enriched = requests.map((req: any) => {
|
||||||
@@ -4184,7 +4193,33 @@ export class RequestManagementService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return enriched;
|
const paged = applyListQueryV2(enriched, {
|
||||||
|
publicId: (r) => String((r as { publicId?: string }).publicId ?? ""),
|
||||||
|
createdAt: (r) => (r as { createdAt?: Date }).createdAt,
|
||||||
|
requestNo: (r) => String((r as { requestNo?: string }).requestNo ?? ""),
|
||||||
|
status: (r) => String((r as { status?: string }).status ?? ""),
|
||||||
|
searchExtras: (r) => {
|
||||||
|
const row = r as {
|
||||||
|
blameStatus?: string;
|
||||||
|
type?: string;
|
||||||
|
userSide?: string;
|
||||||
|
};
|
||||||
|
return [
|
||||||
|
row.blameStatus,
|
||||||
|
row.type,
|
||||||
|
row.userSide,
|
||||||
|
String((r as { _id?: unknown })._id ?? ""),
|
||||||
|
].filter(Boolean) as string[];
|
||||||
|
},
|
||||||
|
}, query);
|
||||||
|
|
||||||
|
return {
|
||||||
|
list: paged.list,
|
||||||
|
total: paged.total,
|
||||||
|
page: paged.page,
|
||||||
|
limit: paged.limit,
|
||||||
|
totalPages: paged.totalPages,
|
||||||
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error("Error: ", err);
|
this.logger.error("Error: ", err);
|
||||||
throw new InternalServerErrorException("Something Went Wrong");
|
throw new InternalServerErrorException("Something Went Wrong");
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
|
Query,
|
||||||
Req,
|
Req,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
UploadedFiles,
|
UploadedFiles,
|
||||||
@@ -39,6 +40,8 @@ import {
|
|||||||
CarBodyFormDto,
|
CarBodyFormDto,
|
||||||
} from "./dto/create-request-management.dto";
|
} from "./dto/create-request-management.dto";
|
||||||
import { RequestManagementService } from "./request-management.service";
|
import { RequestManagementService } from "./request-management.service";
|
||||||
|
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||||
|
import { GetUserBlameListV2ResponseDto } from "./dto/blame-list-user-v2.dto";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2 API surface for the redesigned `BlameRequest` model.
|
* V2 API surface for the redesigned `BlameRequest` model.
|
||||||
@@ -71,10 +74,16 @@ export class RequestManagementV2Controller {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Roles(RoleEnum.USER, RoleEnum.FIELD_EXPERT)
|
@Roles(RoleEnum.USER, RoleEnum.FIELD_EXPERT)
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "List my blame requests (V2)",
|
||||||
|
description:
|
||||||
|
"All blame files for the current user. Optional query: `search`, `sortBy` (publicId | createdAt | requestNo | status), `sortOrder`, `page`, `limit`. Without `page`/`limit`, returns the full filtered list.",
|
||||||
|
})
|
||||||
async getAllBlameRequestsV2(
|
async getAllBlameRequestsV2(
|
||||||
@CurrentUser() user: any,
|
@CurrentUser() user: any,
|
||||||
) {
|
@Query() query: ListQueryV2Dto,
|
||||||
return this.requestManagementService.getAllBlameRequestsV2(user);
|
): Promise<GetUserBlameListV2ResponseDto> {
|
||||||
|
return this.requestManagementService.getAllBlameRequestsV2(user, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export class SmsGatewayService {
|
|||||||
|
|
||||||
async verifyLookUp(data: VerifyLookUpMessage) {
|
async verifyLookUp(data: VerifyLookUpMessage) {
|
||||||
try {
|
try {
|
||||||
|
console.log(data)
|
||||||
const body = await this.sender.verifyLookup(data);
|
const body = await this.sender.verifyLookup(data);
|
||||||
if (!isKavenegarSuccess(body)) {
|
if (!isKavenegarSuccess(body)) {
|
||||||
const normalized = normalizeKavenegarBody(body);
|
const normalized = normalizeKavenegarBody(body);
|
||||||
|
|||||||
Reference in New Issue
Block a user