Accept custom color hexes when saving category color variations.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
00d0284454
commit
a1e73d1c9e
@@ -131,6 +131,7 @@ export class CategoryTechnicalFormAiService {
|
|||||||
- multi_value یک لیست تکراری از مقادیر متنی آزاد است (بدون گزینه) برای مقادیر کوتاه چندتایی.
|
- multi_value یک لیست تکراری از مقادیر متنی آزاد است (بدون گزینه) برای مقادیر کوتاه چندتایی.
|
||||||
- text برای مقادیر کوتاه (ابعاد، وزن، سال مدل).
|
- text برای مقادیر کوتاه (ابعاد، وزن، سال مدل).
|
||||||
- textarea برای مشخصات طولانیتر (ویژگیها، دستورالعمل نگهداری).
|
- textarea برای مشخصات طولانیتر (ویژگیها، دستورالعمل نگهداری).
|
||||||
|
- اگر برای این دسته مناسب است، فیلدهای «ابعاد» و «وزن» را بهصورت text اضافه کن (واحد متریک رایج ایران را در برچسب بیاور، مثلاً سانتیمتر / کیلوگرم). اگر برای دسته بیمعناست، اضافه نکن.
|
||||||
- مشخصات مهم خریدار را الزامی علامت بزن.
|
- مشخصات مهم خریدار را الزامی علامت بزن.
|
||||||
- برچسبها باید یکتا و خوانا باشند.
|
- برچسبها باید یکتا و خوانا باشند.
|
||||||
- همیشه برچسبها و گزینهها را به فارسی بنویس.
|
- همیشه برچسبها و گزینهها را به فارسی بنویس.
|
||||||
|
|||||||
@@ -54,6 +54,31 @@ function uniqueSlug(base: string, used: Set<string>, fallbackIndex: number): str
|
|||||||
return next;
|
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 = {
|
type ExistingVariation = {
|
||||||
id: bigint;
|
id: bigint;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -126,6 +151,10 @@ export class CategoryVariationsService {
|
|||||||
const uniqueValues = [
|
const uniqueValues = [
|
||||||
...new Set(variation.values.map((v) => v.trim()).filter(Boolean)),
|
...new Set(variation.values.map((v) => v.trim()).filter(Boolean)),
|
||||||
];
|
];
|
||||||
|
const hexByLabel = buildColorHexByLabel(
|
||||||
|
variation.values,
|
||||||
|
variation.colorHexes,
|
||||||
|
);
|
||||||
|
|
||||||
if (current) {
|
if (current) {
|
||||||
matchedExistingIds.add(current.id);
|
matchedExistingIds.add(current.id);
|
||||||
@@ -137,7 +166,7 @@ export class CategoryVariationsService {
|
|||||||
sortOrder: index,
|
sortOrder: index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await this.syncOptions(tx, current, variation.type, uniqueValues);
|
await this.syncOptions(tx, current, variation.type, uniqueValues, hexByLabel);
|
||||||
} else {
|
} else {
|
||||||
const created = await tx.categoryVariation.create({
|
const created = await tx.categoryVariation.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -148,7 +177,13 @@ export class CategoryVariationsService {
|
|||||||
sortOrder: index,
|
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,
|
current: ExistingVariation,
|
||||||
type: CategoryVariationInputDto['type'],
|
type: CategoryVariationInputDto['type'],
|
||||||
labels: string[],
|
labels: string[],
|
||||||
|
hexByLabel: Map<string, string> = new Map(),
|
||||||
) {
|
) {
|
||||||
const usedSlugs = new Set<string>();
|
const usedSlugs = new Set<string>();
|
||||||
const matchedOptionIds = new Set<bigint>();
|
const matchedOptionIds = new Set<bigint>();
|
||||||
@@ -215,7 +251,8 @@ export class CategoryVariationsService {
|
|||||||
|
|
||||||
const colorHex =
|
const colorHex =
|
||||||
type === 'color'
|
type === 'color'
|
||||||
? resolveColorHex(label) ??
|
? hexByLabel.get(label) ??
|
||||||
|
resolveColorHex(label) ??
|
||||||
existingOption?.colorHex ??
|
existingOption?.colorHex ??
|
||||||
(existingOption && isHexColor(existingOption.value)
|
(existingOption && isHexColor(existingOption.value)
|
||||||
? existingOption.value.trim()
|
? existingOption.value.trim()
|
||||||
@@ -270,6 +307,7 @@ export class CategoryVariationsService {
|
|||||||
variationId: bigint,
|
variationId: bigint,
|
||||||
type: CategoryVariationInputDto['type'],
|
type: CategoryVariationInputDto['type'],
|
||||||
labels: string[],
|
labels: string[],
|
||||||
|
hexByLabel: Map<string, string> = new Map(),
|
||||||
) {
|
) {
|
||||||
const usedSlugs = new Set<string>();
|
const usedSlugs = new Set<string>();
|
||||||
await tx.categoryVariationOption.createMany({
|
await tx.categoryVariationOption.createMany({
|
||||||
@@ -277,7 +315,10 @@ export class CategoryVariationsService {
|
|||||||
variationId,
|
variationId,
|
||||||
label,
|
label,
|
||||||
value: uniqueSlug(slugifyValue(label), usedSlugs, optionIndex + 1),
|
value: uniqueSlug(slugifyValue(label), usedSlugs, optionIndex + 1),
|
||||||
colorHex: type === 'color' ? resolveColorHex(label) : null,
|
colorHex:
|
||||||
|
type === 'color'
|
||||||
|
? hexByLabel.get(label) ?? resolveColorHex(label)
|
||||||
|
: null,
|
||||||
sortOrder: optionIndex,
|
sortOrder: optionIndex,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
ArrayMinSize,
|
ArrayMinSize,
|
||||||
IsArray,
|
IsArray,
|
||||||
IsIn,
|
IsIn,
|
||||||
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
MinLength,
|
MinLength,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
@@ -21,6 +22,11 @@ export class CategoryVariationInputDto {
|
|||||||
@ArrayMinSize(1)
|
@ArrayMinSize(1)
|
||||||
@IsString({ each: true })
|
@IsString({ each: true })
|
||||||
values!: string[];
|
values!: string[];
|
||||||
|
|
||||||
|
/** Optional parallel list of hex colors for `color` variations (same order as values). */
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
colorHexes?: (string | null)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReplaceCategoryVariationsDto {
|
export class ReplaceCategoryVariationsDto {
|
||||||
|
|||||||
Reference in New Issue
Block a user