import { Type } from 'class-transformer'; import { IsArray, IsIn, IsInt, IsOptional, IsString, Max, Min, ValidateNested, } from 'class-validator'; /** * One row of the XTable filter bar. * * Mirrors the object legacy's XFilterRow pushed into `query.filters`: * `{ id, field, op, value, value2? }`. `id` is a client-side react key * (a random float) and is accepted but ignored. */ export class TableFilterDto { @IsOptional() id?: number | string; @IsOptional() @IsString() field?: string; /** * Legacy operators, kept verbatim. `between` uses value + value2 and is what * the date-range filter emits. */ @IsOptional() @IsIn(['=', '!=', '>', '>=', '<', '<=', 'like', 'in', 'between', 'null', 'notnull']) op?: string; @IsOptional() value?: unknown; @IsOptional() value2?: unknown; } /** * The request body XTable POSTs, ported from its requestData(): * * { ...query, select, titles, report_title, op? } * * where query = { search, filters, total, limit, page }. */ export class TableQueryDto { @IsOptional() @IsString() search?: string; @IsOptional() @IsArray() @ValidateNested({ each: true }) @Type(() => TableFilterDto) filters?: TableFilterDto[]; @IsOptional() @Type(() => Number) @IsInt() @Min(1) page?: number; @IsOptional() @Type(() => Number) @IsInt() @Min(1) @Max(500) limit?: number; /** Sort spec: '+field' ascending, '-field' descending (legacy `order`). */ @IsOptional() @IsString() order?: string; /** Columns currently visible; drives the export payload. */ @IsOptional() @IsArray() select?: string[]; /** key -> Farsi column heading, used for export headers. */ @IsOptional() titles?: Record; @IsOptional() @IsString() report_title?: string; /** * Export mode. Legacy expected `{ url }` back and did `window.open(data.url)`. * Exports are not implemented yet; the controller rejects these explicitly * rather than returning a broken url. */ @IsOptional() @IsIn(['excel', 'json', 'print']) op?: 'excel' | 'json' | 'print'; /** Echoed back inside `query`; the client owns this value. */ @IsOptional() total?: number; }