Files
edr-platform/apps/edr-passenger-api/src/modules/support/support.dto.ts
2026-07-18 09:44:14 +00:00

194 lines
4.2 KiB
TypeScript

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
IsEmail,
IsEnum,
IsInt,
IsOptional,
IsString,
Length,
Max,
MaxLength,
Min,
MinLength,
} from 'class-validator';
export enum SupportStatusDto {
OPEN = 'OPEN',
RESOLVED = 'RESOLVED',
CLOSED = 'CLOSED',
}
export class CreateConversationDto {
@ApiProperty({ description: 'Short subject / topic of the request.' })
@IsString()
@Length(3, 200)
subject!: string;
@ApiProperty({ description: 'The first message body.' })
@IsString()
@MinLength(1)
@MaxLength(4000)
initialMessage!: string;
}
/**
* Text is optional across the send DTOs because a message may be nothing but
* attachments. "Neither text nor files" is rejected in the service rather than
* here — the validator can't see the multipart file parts.
*/
export class SendMessageDto {
@ApiPropertyOptional({
description: 'Message text. Optional only when attachments are present.',
})
@IsOptional()
@IsString()
@MaxLength(4000)
text?: string;
}
export class CreateGuestConversationDto {
@ApiProperty({ description: 'Client-generated anonymous id (localStorage).' })
@IsString()
@Length(8, 120)
guestId!: string;
@ApiProperty({ description: 'Guest full name.' })
@IsString()
@Length(1, 120)
name!: string;
@ApiProperty({ description: 'Guest email for follow-up.' })
@IsEmail()
email!: string;
@ApiPropertyOptional({ description: 'Guest phone (optional).' })
@IsOptional()
@IsString()
@MaxLength(40)
phone?: string;
@ApiProperty()
@IsString()
@Length(3, 200)
subject!: string;
@ApiProperty()
@IsString()
@MinLength(1)
@MaxLength(4000)
initialMessage!: string;
}
export class GuestSendMessageDto {
@ApiProperty({ description: 'The guest id that owns the conversation.' })
@IsString()
@Length(8, 120)
guestId!: string;
@ApiPropertyOptional({
description: 'Message text. Optional only when attachments are present.',
})
@IsOptional()
@IsString()
@MaxLength(4000)
text?: string;
}
export class GuestIdBodyDto {
@ApiProperty()
@IsString()
@Length(8, 120)
guestId!: string;
}
export class DeviceSendMessageDto {
@ApiProperty({ description: 'Client device id (localStorage).' })
@IsString()
@Length(8, 120)
deviceId!: string;
@ApiPropertyOptional({
description: 'Message text. Optional only when attachments are present.',
})
@IsOptional()
@IsString()
@MaxLength(4000)
text?: string;
}
export class DeviceIdBodyDto {
@ApiProperty()
@IsString()
@Length(8, 120)
deviceId!: string;
}
export class UpdateStatusDto {
@ApiProperty({ enum: SupportStatusDto })
@IsEnum(SupportStatusDto)
status!: SupportStatusDto;
}
export class ListConversationsQueryDto {
@ApiPropertyOptional({ enum: SupportStatusDto })
@IsOptional()
@IsEnum(SupportStatusDto)
status?: SupportStatusDto;
@ApiPropertyOptional({ description: 'Search subject / passenger name.' })
@IsOptional()
@IsString()
search?: string;
@ApiPropertyOptional({ minimum: 1, default: 1 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
@ApiPropertyOptional({ minimum: 1, maximum: 100, default: 100 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(100)
limit?: number;
}
/** Default page size for a thread — roughly two screens of bubbles. */
export const SUPPORT_MESSAGES_DEFAULT_LIMIT = 30;
export const SUPPORT_MESSAGES_MAX_LIMIT = 100;
export class ListMessagesQueryDto {
@ApiPropertyOptional({
description:
"Opaque cursor from a previous response's `nextCursor`. Returns the page " +
'of messages immediately OLDER than the cursor. Omit for the newest page.',
})
@IsOptional()
@IsString()
before?: string;
@ApiPropertyOptional({
minimum: 1,
maximum: SUPPORT_MESSAGES_MAX_LIMIT,
default: SUPPORT_MESSAGES_DEFAULT_LIMIT,
})
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(SUPPORT_MESSAGES_MAX_LIMIT)
limit?: number;
}
/** Query form of {@link ListMessagesQueryDto} for the device-scoped thread. */
export class DeviceThreadQueryDto extends ListMessagesQueryDto {
@ApiProperty({ description: 'Client device id (localStorage).' })
@IsString()
@Length(8, 120)
deviceId!: string;
}