From f8b865b0aa2592fd385f22fe01971a122ab408db Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Sat, 19 Sep 2026 15:43:18 +0330 Subject: [PATCH] Skip technical form fill on AI product create. Co-authored-by: Cursor --- src/products/product-ai.service.ts | 61 ++---------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/src/products/product-ai.service.ts b/src/products/product-ai.service.ts index 02af906..18de73b 100644 --- a/src/products/product-ai.service.ts +++ b/src/products/product-ai.service.ts @@ -5,7 +5,6 @@ import { import { ConfigService } from '@nestjs/config'; import { ContentStatus, TechnicalFieldType } from '@prisma/client'; import { AuthUser } from '../auth/auth.types'; -import { CategoryTechnicalFormService } from '../categories/category-technical-form.service'; import { PrismaService } from '../prisma/prisma.service'; import { CreateProductByAiDto, @@ -37,7 +36,6 @@ type AiProductDraft = { summary: string; descriptionHtml: string; tags: string[]; - technicalValues: Record; }; @Injectable() @@ -47,7 +45,6 @@ export class ProductAiService { private readonly prisma: PrismaService, private readonly products: ProductsService, private readonly technicalInfo: ProductTechnicalInfoService, - private readonly technicalFormService: CategoryTechnicalFormService, ) {} async createFromAi( @@ -70,16 +67,10 @@ export class ProductAiService { throw new BadRequestException('Product category not found'); } - const form = await this.technicalFormService.getFormForCategory( - businessId, - categoryId, - ); - const draft = await this.generateDraft({ categoryName: category.name, productName: dto.name.trim(), language: dto.language, - fields: form?.fields ?? [], }); const created = await this.products.create( @@ -96,26 +87,8 @@ export class ProductAiService { actor, ); - if (form && Object.keys(draft.technicalValues).length > 0) { - const values = Object.entries(draft.technicalValues) - .filter(([, value]) => { - if (Array.isArray(value)) return value.length > 0; - return value !== ''; - }) - .map(([fieldKey, value]) => ({ fieldKey, value })); - - if (values.length > 0) { - await this.technicalInfo.replaceForProduct( - businessIdRaw, - created.product.id, - { values }, - actor, - ); - } - } - return { - message: 'Product created with AI. Add images and variations next.', + message: 'Product created with AI. Add images, variations, and technical info next.', product: created.product, }; } @@ -445,23 +418,11 @@ Rules: categoryName: string; productName: string; language: ProductAiLanguage; - fields: TechnicalFormField[]; }): Promise { const provider = resolveAiProvider(this.config); const referencePages = await loadReferencePagesFromText(input.productName); const usableReferences = referencePages.filter((page) => page.text); - const technicalSchema = input.fields.map((field) => ({ - key: field.key, - label: field.label, - type: field.type, - required: field.isRequired, - options: - field.type === 'select' || field.type === 'multi_select' - ? field.options.map((option) => option.value) - : undefined, - })); - const inputLanguage = input.language === ProductAiLanguage.fa ? 'Persian (Farsi)' : 'English'; @@ -472,24 +433,18 @@ Return ONLY valid JSON with this shape: "nameFa": "Persian product name", "summary": "short listing summary", "descriptionHtml": "HTML description using

and

  • only", - "tags": ["tag1", "tag2"], - "technicalValues": { "fieldKey": "value" } + "tags": ["tag1", "tag2"] } Rules: - The user input is a research brief in ${inputLanguage}. It may include a model/product name, a product page URL, and extra notes — not a final storefront title. - Infer the real product from the brief (and any URL/model clues). Do not copy URLs, markdown, or raw notes into title/nameFa. - Write clean storefront titles: "title" in English, "nameFa" in Persian. Keep brand/model accurate. -- When referencePages are provided, treat them as the source of truth for specs, capacity, power, dimensions, and description facts. +- When referencePages are provided, treat them as the source of truth for capacity, power, dimensions, and description facts. - Prefer the main product specs near the product title; ignore conflicting related-product / upsell blocks. - Do NOT invent specs from memory when a reference page was provided. - Be factual and realistic for the identified product in the given category. -- If technical form fields are provided, fill technicalValues using exact field keys. -- For select fields use one allowed option value exactly. -- For multi_select fields use an array of allowed option values. -- For multi_value fields use an array of free-text strings. -- If a multi_value field is labeled «سایر ویژگی‌ها» (or similar “other features”), put any leftover real product attributes there that do not belong in the other fields — one short string per item; do not repeat values already used elsewhere. -- For text/textarea fields use plain strings. +- Do NOT return technical form fields — product content only (title, names, summary, description, tags). - Prefer the input language for summary/description tone, but always return both title and nameFa. - descriptionHtml should be 2-4 paragraphs with key specs. - tags: 3-8 relevant lowercase tags.`; @@ -498,7 +453,6 @@ Rules: category: input.categoryName, researchBrief: input.productName, inputLanguage: input.language, - technicalFields: technicalSchema, referencePages: usableReferences.map((page) => ({ url: page.url, text: page.text, @@ -531,7 +485,6 @@ Rules: input: { productName: string; language: ProductAiLanguage; - fields: TechnicalFormField[]; }, ): AiProductDraft { const fallbackName = this.shortProductLabel(input.productName); @@ -554,11 +507,6 @@ Rules: throw new BadRequestException('AI draft is missing a Persian product name'); } - const technicalValues = this.normalizeTechnicalValues( - (draft.technicalValues ?? {}) as Record, - input.fields, - ); - return { title, nameFa, @@ -567,7 +515,6 @@ Rules: descriptionHtml || `

    ${summary || `${title} product description.`}

    `, tags, - technicalValues, }; }