Improve dashboard charts, category tree selects, and product AI name fill.

Fix RTL chart ordering with Y-axis and hover values; aggregate website views; refine category picker tree borders and spacing; add one-click FA→EN name translation on add product.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-28 20:39:43 +03:30
co-authored by Cursor
parent 7b2ed7c7b0
commit a412cb82fc
31 changed files with 1222 additions and 226 deletions
+147
View File
@@ -0,0 +1,147 @@
/** Canonical color presets for category variations (English name stored in API). */
export const COLOR_PRESETS = [
{ name: 'Red', hex: '#EF4444' },
{ name: 'Dark Red', hex: '#B91C1C' },
{ name: 'Crimson', hex: '#DC143C' },
{ name: 'Maroon', hex: '#7F1D1D' },
{ name: 'Rose', hex: '#F43F5E' },
{ name: 'Pink', hex: '#EC4899' },
{ name: 'Hot Pink', hex: '#DB2777' },
{ name: 'Magenta', hex: '#D946EF' },
{ name: 'Coral', hex: '#FB7185' },
{ name: 'Salmon', hex: '#FDA4AF' },
{ name: 'Orange', hex: '#F97316' },
{ name: 'Dark Orange', hex: '#C2410C' },
{ name: 'Amber', hex: '#F59E0B' },
{ name: 'Yellow', hex: '#EAB308' },
{ name: 'Mustard', hex: '#CA8A04' },
{ name: 'Gold', hex: '#D4AF37' },
{ name: 'Bronze', hex: '#B45309' },
{ name: 'Copper', hex: '#B87333' },
{ name: 'Brown', hex: '#92400E' },
{ name: 'Dark Brown', hex: '#5C3A21' },
{ name: 'Tan', hex: '#D2B48C' },
{ name: 'Beige', hex: '#D4C4A8' },
{ name: 'Cream', hex: '#FFF7ED' },
{ name: 'Ivory', hex: '#FFFFF0' },
{ name: 'Khaki', hex: '#C3B091' },
{ name: 'Olive', hex: '#808000' },
{ name: 'Lime', hex: '#84CC16' },
{ name: 'Green', hex: '#22C55E' },
{ name: 'Dark Green', hex: '#166534' },
{ name: 'Forest', hex: '#14532D' },
{ name: 'Emerald', hex: '#059669' },
{ name: 'Mint', hex: '#6EE7B7' },
{ name: 'Teal', hex: '#14B8A6' },
{ name: 'Turquoise', hex: '#2DD4BF' },
{ name: 'Cyan', hex: '#06B6D4' },
{ name: 'Sky Blue', hex: '#38BDF8' },
{ name: 'Light Blue', hex: '#93C5FD' },
{ name: 'Blue', hex: '#3B82F6' },
{ name: 'Royal Blue', hex: '#1D4ED8' },
{ name: 'Navy', hex: '#1E3A5F' },
{ name: 'Indigo', hex: '#4F46E5' },
{ name: 'Purple', hex: '#A855F7' },
{ name: 'Violet', hex: '#7C3AED' },
{ name: 'Lavender', hex: '#C4B5FD' },
{ name: 'Lilac', hex: '#D8B4FE' },
{ name: 'Burgundy', hex: '#9F1239' },
{ name: 'Wine', hex: '#881337' },
{ name: 'Gray', hex: '#6B7280' },
{ name: 'Light Gray', hex: '#D1D5DB' },
{ name: 'Dark Gray', hex: '#374151' },
{ name: 'Charcoal', hex: '#1F2937' },
{ name: 'Silver', hex: '#C0C0C0' },
{ name: 'Black', hex: '#111827' },
{ name: 'White', hex: '#FFFFFF' },
] as const
export type ColorPresetName = (typeof COLOR_PRESETS)[number]['name']
export const COLOR_OPTIONS: ColorPresetName[] = COLOR_PRESETS.map((c) => c.name)
/** Legacy / Farsi labels → English preset name (for edit + search). */
export const COLOR_ALIASES: Record<string, ColorPresetName> = {
قرمز: 'Red',
'قرمز تیره': 'Dark Red',
آلبالویی: 'Crimson',
عنابی: 'Maroon',
صورتی: 'Pink',
'صورتی تند': 'Hot Pink',
سرخابی: 'Magenta',
مرجانی: 'Coral',
سالمون: 'Salmon',
نارنجی: 'Orange',
کهربایی: 'Amber',
زرد: 'Yellow',
خردلی: 'Mustard',
طلایی: 'Gold',
طلائی: 'Gold',
برنزی: 'Bronze',
مسی: 'Copper',
قهوهای: 'Brown',
'قهوه ای': 'Brown',
قهوه‌ای: 'Brown',
'قهوه‌ای تیره': 'Dark Brown',
خاکی: 'Tan',
بژ: 'Beige',
کرم: 'Cream',
عاجی: 'Ivory',
زیتونی: 'Olive',
لیمویی: 'Lime',
سبز: 'Green',
'سبز تیره': 'Dark Green',
زمردی: 'Emerald',
نعنایی: 'Mint',
سبزآبی: 'Teal',
فیروزهای: 'Turquoise',
'فیروزه ای': 'Turquoise',
فیروزه‌ای: 'Turquoise',
آبی: 'Blue',
آسمانی: 'Sky Blue',
'آبی روشن': 'Light Blue',
'آبی سلطنتی': 'Royal Blue',
سرمهای: 'Navy',
'سرمه ای': 'Navy',
سرمه‌ای: 'Navy',
نیلی: 'Indigo',
بنفش: 'Purple',
یاسی: 'Lilac',
اسطوخودوسی: 'Lavender',
بادمجانی: 'Burgundy',
خاکستری: 'Gray',
طوسی: 'Gray',
'خاکستری روشن': 'Light Gray',
'خاکستری تیره': 'Dark Gray',
ذغالی: 'Charcoal',
نقرهای: 'Silver',
'نقره ای': 'Silver',
نقره‌ای: 'Silver',
مشکی: 'Black',
سیاه: 'Black',
سفید: 'White',
}
export function getColorHex(name: string): string | null {
return COLOR_PRESETS.find((c) => c.name === name)?.hex ?? null
}
export function normalizeColorName(value: string): string {
const trimmed = value.trim()
if (!trimmed) return trimmed
if ((COLOR_OPTIONS as string[]).includes(trimmed)) return trimmed
const alias = COLOR_ALIASES[trimmed] ?? COLOR_ALIASES[trimmed.replace(/\u200c/g, '')]
return alias ?? trimmed
}
export function normalizeColorNames(values: string[]): string[] {
const seen = new Set<string>()
const result: string[] = []
for (const value of values) {
const name = normalizeColorName(value)
if (!name || seen.has(name)) continue
seen.add(name)
result.push(name)
}
return result
}