Add multi_value technical fields and FA category AI forms.

Support duplicatable free-text lists, always append سایر ویژگی‌ها on AI category forms, and fill leftover product specs into that field.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-19 13:50:04 +03:30
co-authored by Cursor
parent ac04e51c8b
commit 00d0284454
8 changed files with 181 additions and 26 deletions
+52 -1
View File
@@ -10,6 +10,10 @@ import { PermissionsService } from '../auth/permissions.service';
import { CategoryTechnicalFormService } from '../categories/category-technical-form.service';
import { PrismaService } from '../prisma/prisma.service';
import { ReplaceProductTechnicalInfoDto } from './dto/product-technical-info.dto';
import {
decodeMultiValueText,
encodeMultiValueText,
} from '../common/technical-multi-value.util';
type FormField = {
id: string;
@@ -159,6 +163,22 @@ export class ProductTechnicalInfoService {
continue;
}
if (field.type === 'multi_value') {
const items = Array.isArray(entry.value)
? entry.value.map((item) => String(item).trim()).filter(Boolean)
: [];
if (!items.length) continue;
await tx.productTechnicalFieldValue.create({
data: {
businessId,
productId: product.id,
fieldId,
textValue: encodeMultiValueText(items),
},
});
continue;
}
if (field.type === 'text' || field.type === 'textarea') {
await tx.productTechnicalFieldValue.create({
data: {
@@ -185,6 +205,9 @@ export class ProductTechnicalInfoService {
}
const selectedValues = entry.value as string[];
if (!Array.isArray(selectedValues) || !selectedValues.length) {
continue;
}
const fieldValue = await tx.productTechnicalFieldValue.create({
data: {
businessId,
@@ -230,6 +253,24 @@ export class ProductTechnicalInfoService {
return;
}
if (field.type === 'multi_value') {
if (!Array.isArray(value)) {
throw new BadRequestException(
`Field "${field.label}" expects an array of text values`,
);
}
const items = value.map((item) => String(item).trim()).filter(Boolean);
if (!items.length) {
if (field.isRequired) {
throw new BadRequestException(
`Required field "${field.label}" cannot be empty`,
);
}
return;
}
return;
}
if (field.type === 'select') {
if (typeof value !== 'string') {
throw new BadRequestException(
@@ -303,7 +344,17 @@ export class ProductTechnicalInfoService {
fieldKey: field.key,
fieldLabel: field.label,
type: field.type,
value: field.type === 'multi_select' ? [] : null,
value:
field.type === 'multi_select' || field.type === 'multi_value' ? [] : null,
};
}
if (field.type === 'multi_value') {
return {
fieldKey: field.key,
fieldLabel: field.label,
type: field.type,
value: decodeMultiValueText(record.textValue),
};
}