Accept custom color hexes when saving category color variations.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-19 15:28:14 +03:30
co-authored by Cursor
parent 00d0284454
commit a1e73d1c9e
3 changed files with 52 additions and 4 deletions
@@ -131,6 +131,7 @@ export class CategoryTechnicalFormAiService {
- multi_value یک لیست تکراری از مقادیر متنی آزاد است (بدون گزینه) برای مقادیر کوتاه چندتایی.
- text برای مقادیر کوتاه (ابعاد، وزن، سال مدل).
- textarea برای مشخصات طولانی‌تر (ویژگی‌ها، دستورالعمل نگهداری).
- اگر برای این دسته مناسب است، فیلدهای «ابعاد» و «وزن» را به‌صورت text اضافه کن (واحد متریک رایج ایران را در برچسب بیاور، مثلاً سانتی‌متر / کیلوگرم). اگر برای دسته بی‌معناست، اضافه نکن.
- مشخصات مهم خریدار را الزامی علامت بزن.
- برچسب‌ها باید یکتا و خوانا باشند.
- همیشه برچسب‌ها و گزینه‌ها را به فارسی بنویس.
+45 -4
View File
@@ -54,6 +54,31 @@ function uniqueSlug(base: string, used: Set<string>, fallbackIndex: number): str
return next;
}
function normalizeHex(value: string | null | undefined): string | null {
if (value == null) return null;
const trimmed = value.trim();
if (!isHexColor(trimmed)) return null;
return trimmed.toUpperCase();
}
/** Map value labels → provided hex (later entries win for duplicate labels). */
function buildColorHexByLabel(
values: string[],
colorHexes: (string | null)[] | undefined,
): Map<string, string> {
const map = new Map<string, string>();
if (!colorHexes?.length) return map;
values.forEach((raw, index) => {
const label = raw.trim();
if (!label) return;
const hex = normalizeHex(colorHexes[index] ?? null);
if (hex) map.set(label, hex);
});
return map;
}
type ExistingVariation = {
id: bigint;
name: string;
@@ -126,6 +151,10 @@ export class CategoryVariationsService {
const uniqueValues = [
...new Set(variation.values.map((v) => v.trim()).filter(Boolean)),
];
const hexByLabel = buildColorHexByLabel(
variation.values,
variation.colorHexes,
);
if (current) {
matchedExistingIds.add(current.id);
@@ -137,7 +166,7 @@ export class CategoryVariationsService {
sortOrder: index,
},
});
await this.syncOptions(tx, current, variation.type, uniqueValues);
await this.syncOptions(tx, current, variation.type, uniqueValues, hexByLabel);
} else {
const created = await tx.categoryVariation.create({
data: {
@@ -148,7 +177,13 @@ export class CategoryVariationsService {
sortOrder: index,
},
});
await this.createOptions(tx, created.id, variation.type, uniqueValues);
await this.createOptions(
tx,
created.id,
variation.type,
uniqueValues,
hexByLabel,
);
}
}
@@ -203,6 +238,7 @@ export class CategoryVariationsService {
current: ExistingVariation,
type: CategoryVariationInputDto['type'],
labels: string[],
hexByLabel: Map<string, string> = new Map(),
) {
const usedSlugs = new Set<string>();
const matchedOptionIds = new Set<bigint>();
@@ -215,7 +251,8 @@ export class CategoryVariationsService {
const colorHex =
type === 'color'
? resolveColorHex(label) ??
? hexByLabel.get(label) ??
resolveColorHex(label) ??
existingOption?.colorHex ??
(existingOption && isHexColor(existingOption.value)
? existingOption.value.trim()
@@ -270,6 +307,7 @@ export class CategoryVariationsService {
variationId: bigint,
type: CategoryVariationInputDto['type'],
labels: string[],
hexByLabel: Map<string, string> = new Map(),
) {
const usedSlugs = new Set<string>();
await tx.categoryVariationOption.createMany({
@@ -277,7 +315,10 @@ export class CategoryVariationsService {
variationId,
label,
value: uniqueSlug(slugifyValue(label), usedSlugs, optionIndex + 1),
colorHex: type === 'color' ? resolveColorHex(label) : null,
colorHex:
type === 'color'
? hexByLabel.get(label) ?? resolveColorHex(label)
: null,
sortOrder: optionIndex,
})),
});
@@ -3,6 +3,7 @@ import {
ArrayMinSize,
IsArray,
IsIn,
IsOptional,
IsString,
MinLength,
ValidateNested,
@@ -21,6 +22,11 @@ export class CategoryVariationInputDto {
@ArrayMinSize(1)
@IsString({ each: true })
values!: string[];
/** Optional parallel list of hex colors for `color` variations (same order as values). */
@IsOptional()
@IsArray()
colorHexes?: (string | null)[];
}
export class ReplaceCategoryVariationsDto {