diff --git a/src/app/features/admin/categories/components/admin-category-form.component.html b/src/app/features/admin/categories/components/admin-category-form.component.html
index 28b0760..05f6c59 100644
--- a/src/app/features/admin/categories/components/admin-category-form.component.html
+++ b/src/app/features/admin/categories/components/admin-category-form.component.html
@@ -27,9 +27,9 @@
}
-
+
-
+
-
-
+
+
+
+ @if (category.icon) {
+
{{ category.icon }}
+ }
+
@@ -73,6 +78,10 @@
@if (activeGroup() === 'seo') {
{{ 'adminProducts.seoExplain' | translate }}
+
+
{{ 'adminProducts.seoGenerate' | translate }}
+
{{ 'adminProducts.seoGenerateHint' | translate }}
+
@@ -97,7 +106,8 @@
@if (activeGroup() === 'navigation') {
- {{ breadcrumb.length > 0 ? breadcrumb.join(' / ') : ('adminCategories.navRoot' | translate) }}
+ {{ 'adminCategories.navBreadcrumbHint' | translate }}
+ {{ breadcrumb.length > 0 ? breadcrumb.join(' › ') : ('adminCategories.navRoot' | translate) }}
@if (children.length === 0) {
diff --git a/src/app/features/admin/categories/components/admin-category-form.component.scss b/src/app/features/admin/categories/components/admin-category-form.component.scss
index b55ba4d..3c86045 100644
--- a/src/app/features/admin/categories/components/admin-category-form.component.scss
+++ b/src/app/features/admin/categories/components/admin-category-form.component.scss
@@ -42,3 +42,8 @@ input[type='checkbox'] { width: auto; }
.seo-preview__title { margin: 0; color: #1a0dab; font-size: 1rem; }
.seo-preview__url { margin: 0; color: #006621; font-size: 0.8rem; }
.seo-preview__description { margin: 0; color: var(--text-secondary, #5f6e6a); font-size: 0.85rem; }
+.icon-field { display: flex; align-items: center; gap: 8px; app-input { flex: 1; } }
+.icon-field__preview { font-size: 1.4rem; line-height: 1; }
+.seo-generate-row { display: flex; align-items: center; gap: 10px; }
+.seo-generate-row__hint { color: var(--text-secondary, #6b7280); font-size: 0.8rem; }
+.breadcrumb-hint { margin: 0 0 4px; color: var(--text-secondary, #6b7280); font-size: 0.85rem; }
diff --git a/src/app/features/admin/categories/components/admin-category-form.component.ts b/src/app/features/admin/categories/components/admin-category-form.component.ts
index f1ad1fa..754f3f8 100644
--- a/src/app/features/admin/categories/components/admin-category-form.component.ts
+++ b/src/app/features/admin/categories/components/admin-category-form.component.ts
@@ -78,6 +78,38 @@ export class AdminCategoryFormComponent {
this.categoryChange.emit({ [key]: value } as Partial);
}
+ /**
+ * Title edits keep the slug and SEO title in sync only while they still hold
+ * their auto-derived value (or are empty). A manually edited slug/SEO title
+ * no longer matches the derived value, so it is never overwritten.
+ */
+ updateTitle(value: string): void {
+ const patch: Partial = { title: value };
+ const previousAutoSlug = this.slugify(this.category.title);
+ if (!this.category.slug || this.category.slug === previousAutoSlug) {
+ patch.slug = this.slugify(value);
+ }
+ if (!this.category.seo.metaTitle || this.category.seo.metaTitle === this.category.title) {
+ patch.seo = { ...this.category.seo, metaTitle: value };
+ }
+ this.categoryChange.emit(patch);
+ }
+
+ /** Fills empty SEO fields from title/description; existing values stay untouched. */
+ generateSeo(): void {
+ this.categoryChange.emit({
+ seo: {
+ metaTitle: this.category.seo.metaTitle || this.category.title,
+ metaDescription: this.category.seo.metaDescription || this.category.description || this.category.title,
+ keywords: this.category.seo.keywords || this.slugify(this.category.title).split('-').filter(Boolean).join(', '),
+ },
+ });
+ }
+
+ private slugify(value: string): string {
+ return value.toLowerCase().trim().replace(/[^a-z0-9а-яёա-ֆ]+/gi, '-').replace(/(^-|-$)/g, '');
+ }
+
updateTranslation(locale: string, field: 'title' | 'description', value: string): void {
this.categoryChange.emit({
translations: {
diff --git a/src/app/i18n/en.ts b/src/app/i18n/en.ts
index fd39939..a6f7d62 100644
--- a/src/app/i18n/en.ts
+++ b/src/app/i18n/en.ts
@@ -1417,6 +1417,8 @@ export const en: Translations = {
groupAdvanced: 'Advanced',
visibilityExplain: 'A hidden category and its products stay out of the storefront navigation and search until you make it visible again.',
navBreadcrumb: 'Breadcrumb',
+ navBreadcrumbHint: 'This is the path customers see above this category, e.g. Home › Electronics › Phones. It comes from the parent category you choose.',
+ iconHint: 'A small emoji or symbol shown next to the category name in menus, e.g. 📱. Leave empty for none.',
navRoot: 'This is a top-level category.',
navChildren: 'Subcategories',
navNoChildren: 'No subcategories yet.',
diff --git a/src/app/i18n/hy.ts b/src/app/i18n/hy.ts
index 147344e..e3e2128 100644
--- a/src/app/i18n/hy.ts
+++ b/src/app/i18n/hy.ts
@@ -1412,6 +1412,8 @@ export const hy: Translations = {
groupAdvanced: 'Լրացուցիչ',
visibilityExplain: 'Թաքցված կատեգորիան և դրա ապրանքները չեն երևում խանութի նավիգացիայում և որոնման մեջ, մինչև այն նորից տեսանելի դարձնեք։',
navBreadcrumb: 'Ուղենիշ',
+ navBreadcrumbHint: 'Սա այն ուղին է, որը գնորդները տեսնում են կատեգորիայի վերևում, օր.՝ Գլխավոր › Էլեկտրոնիկա › Հեռախոսներ։ Այն կառուցվում է ընտրված ծնող կատեգորիայից։',
+ iconHint: 'Փոքր էմոջի կամ նշան կատեգորիայի անվան կողքին մենյուում, օր.՝ 📱։ Թողեք դատարկ, եթե պետք չէ։',
navRoot: 'Սա վերին մակարդակի կատեգորիա է։',
navChildren: 'Ենթակատեգորիաներ',
navNoChildren: 'Ենթակատեգորիաներ դեռ չկան։',
diff --git a/src/app/i18n/ru.ts b/src/app/i18n/ru.ts
index 6f9b254..c2a8540 100644
--- a/src/app/i18n/ru.ts
+++ b/src/app/i18n/ru.ts
@@ -1412,6 +1412,8 @@ export const ru: Translations = {
groupAdvanced: 'Дополнительно',
visibilityExplain: 'Скрытая категория и её товары не отображаются в навигации и поиске магазина, пока вы снова не сделаете её видимой.',
navBreadcrumb: 'Хлебные крошки',
+ navBreadcrumbHint: 'Это путь, который покупатели видят над категорией, напр. Главная › Электроника › Телефоны. Он строится из выбранной родительской категории.',
+ iconHint: 'Небольшой эмодзи или символ рядом с названием категории в меню, напр. 📱. Оставьте пустым, если не нужен.',
navRoot: 'Это категория верхнего уровня.',
navChildren: 'Подкатегории',
navNoChildren: 'Подкатегорий пока нет.',
diff --git a/src/app/i18n/translations.ts b/src/app/i18n/translations.ts
index bb3fb4f..ea6c6f0 100644
--- a/src/app/i18n/translations.ts
+++ b/src/app/i18n/translations.ts
@@ -1424,6 +1424,8 @@ export interface Translations {
groupAdvanced: string;
visibilityExplain: string;
navBreadcrumb: string;
+ navBreadcrumbHint: string;
+ iconHint: string;
navRoot: string;
navChildren: string;
navNoChildren: string;