Add public user-products storefront API and website docs.

Expose published customer listings under /tenants/:host/user-products (list, search, details, technical-info) and document them in the website API pack.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-10 00:19:13 +03:30
co-authored by Cursor
parent 158523df7b
commit 953b87b616
32 changed files with 3326 additions and 138 deletions
+176
View File
@@ -0,0 +1,176 @@
import { Transform, Type } from 'class-transformer';
import {
ArrayUnique,
IsArray,
IsBoolean,
IsEnum,
IsIn,
IsInt,
IsNumber,
IsOptional,
IsString,
Min,
MinLength,
ValidateNested,
} from 'class-validator';
import { ContentStatus, UserProductCondition } from '@prisma/client';
export const USER_PRODUCT_PRICE_CURRENCIES = ['IRT', 'USD', 'EUR', 'AED'] as const;
export type UserProductPriceCurrency =
(typeof USER_PRODUCT_PRICE_CURRENCIES)[number];
export class ListMyUserProductsDto {
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
pageSize?: number;
}
export class ListAdminUserProductsDto extends ListMyUserProductsDto {
@IsOptional()
@IsEnum(ContentStatus)
status?: ContentStatus;
}
export class ListPublicUserProductsDto {
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
pageSize?: number;
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
q?: string;
@IsOptional()
@IsString()
categoryId?: string;
@IsOptional()
@IsString()
cityId?: string;
@IsOptional()
@IsString()
countryId?: string;
@IsOptional()
@IsEnum(UserProductCondition)
condition?: UserProductCondition;
@IsOptional()
@Transform(({ value }) => value === 'true' || value === true)
@IsBoolean()
promoted?: boolean;
}
export class UserProductTechnicalValueDto {
@IsString()
@MinLength(1)
fieldId!: string;
@IsOptional()
@IsString()
textValue?: string;
@IsOptional()
@IsString()
optionId?: string;
@IsOptional()
@IsArray()
@ArrayUnique()
@IsString({ each: true })
optionIds?: string[];
}
export class CreateUserProductDto {
@IsString()
@MinLength(1)
titleFa!: string;
@IsOptional()
@IsString()
titleEn?: string;
@IsOptional()
@IsString()
description?: string;
@IsString()
@MinLength(1)
categoryId!: string;
@IsOptional()
@Type(() => Number)
@IsNumber({ maxDecimalPlaces: 2 })
@Min(0)
price?: number;
@IsOptional()
@IsIn(USER_PRODUCT_PRICE_CURRENCIES)
priceCurrency?: UserProductPriceCurrency;
@IsOptional()
@IsBoolean()
priceByExpert?: boolean;
@IsString()
@MinLength(1)
countryId!: string;
@IsString()
@MinLength(1)
cityId!: string;
@IsOptional()
@IsString()
deliveryNote?: string;
@IsEnum(UserProductCondition)
condition!: UserProductCondition;
@IsOptional()
@IsString()
technicalNotes?: string;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => UserProductTechnicalValueDto)
technicalValues?: UserProductTechnicalValueDto[];
@IsOptional()
@IsString()
featuredMediaId?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
galleryMediaIds?: string[];
}
export class UpdateUserProductDto extends CreateUserProductDto {}
export class UpdateUserProductStatusDto {
@IsEnum(ContentStatus)
status!: ContentStatus;
}