feat(admin): sprint 19 admin dashboard, routing, i18n

- Add admin dashboard feature (models/gateway/facade/components/page)
- Wire admin/products routes and backoffice coming-soon placeholders
- Add lastPublishedAt to ProjectEditorFacade/state
- Add dashboard i18n keys (en/ru/hy) and docs/ADMIN.md

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-14 12:21:33 +04:00
parent 6aec2ebcb2
commit 325dc17911
36 changed files with 1386 additions and 5 deletions

View File

@@ -512,6 +512,8 @@ export const en: Translations = {
validationMissingWidget: 'A homepage section has a widget with no type.',
validationDuplicateNavLinks: 'Two or more header navigation links are duplicates.',
validationInvalidColors: 'One or more theme colors are not valid hex colors.',
validationMissingTranslations: 'Some content is missing translations for a supported language.',
validationInvalidLayouts: 'One or more layouts reference an unknown layout type.',
statusDraft: 'Draft',
statusPublished: 'Published',
unsavedChanges: 'Unsaved changes',
@@ -530,6 +532,52 @@ export const en: Translations = {
confirmResetDraft: 'This discards all unpublished changes and restores the last published configuration. Continue?',
confirmResetSection: 'This discards unpublished changes in this section only. Continue?',
},
dashboard: {
title: 'Dashboard',
subtitle: 'Overview of your marketplace',
cardMarketplaceStatus: 'Marketplace Status',
cardProjectName: 'Project Name',
cardCurrentTheme: 'Current Theme',
cardLanguages: 'Languages',
cardCategoriesCount: 'Categories',
cardProductsCount: 'Products',
cardOrders: 'Orders',
cardRevenue: 'Revenue',
cardLastPublish: 'Last Publish',
cardLastDraftSave: 'Last Draft Save',
cardBootstrapVersion: 'Bootstrap Version',
cardActiveLayout: 'Active Layout',
cardEnabledWidgets: 'Enabled Widgets',
stateLoading: 'Loading...',
stateEmpty: 'No data yet',
stateError: 'Could not load this card',
statePendingBackend: 'Awaiting backend integration',
never: 'Never',
quickActionsTitle: 'Quick Actions',
actionEditProject: 'Edit Project',
actionCategories: 'Categories',
actionProducts: 'Products',
actionStaticPages: 'Static Pages',
actionTransactions: 'Transactions',
actionOrders: 'Orders',
actionMediaLibrary: 'Media Library',
actionPreviewMarketplace: 'Preview Marketplace',
activityTitle: 'Recent Activity',
activityEmpty: 'No activity yet. Save or publish a change to see it here.',
activityDraftSaved: 'Draft saved',
activityPublished: 'Published',
healthTitle: 'System Health',
healthAllClear: 'All checks passed',
healthBootstrapValid: 'Bootstrap valid',
healthConfigurationValid: 'Configuration valid',
healthMissingTranslations: 'Missing translations',
healthInvalidColors: 'Invalid colors',
healthInvalidWidgetReferences: 'Invalid widget references',
healthInvalidLayouts: 'Invalid layouts',
comingSoonTitle: 'Coming soon',
comingSoonDescription: 'This section is not built yet. Check back in a future update.',
backToDashboard: 'Back to dashboard',
},
staticPages: {
notFound: 'Page not found',
backHome: 'Back to home',

View File

@@ -512,6 +512,8 @@ export const hy: Translations = {
validationMissingWidget: 'Գլխավոր էջի սեկցիաներից մեկն ունի վիջեթ առանց տիպի։',
validationDuplicateNavLinks: 'Վերնագրի նավիգացիայում կան կրկնվող հղումներ։',
validationInvalidColors: 'Թեմայի գույներից մեկը կամ մի քանիսը վավեր hex գույն չեն։',
validationMissingTranslations: 'Որոշ բովանդակություն թարգմանված չէ սատարվող լեզուներից մեկով։',
validationInvalidLayouts: 'Մեկ կամ մի քանի դասավորություններ հղում են անհայտ տեսակի։',
statusDraft: 'Սևագիր',
statusPublished: 'Հրապարակված',
unsavedChanges: 'Չպահված փոփոխություններ',
@@ -530,6 +532,52 @@ export const hy: Translations = {
confirmResetDraft: 'Սա կչեղարկի բոլոր չհրապարակված փոփոխությունները և կվերականգնի վերջին հրապարակված կոնֆիգուրացիան։ Շարունակե՞լ։',
confirmResetSection: 'Սա կչեղարկի չհրապարակված փոփոխությունները միայն այս սեկցիայում։ Շարունակե՞լ։',
},
dashboard: {
title: 'Կառավարման վահանակ',
subtitle: 'Ձեր մարքեթփլեյսի ընդհանուր տեսք',
cardMarketplaceStatus: 'Մարքեթփլեյսի կարգավիճակ',
cardProjectName: 'Նախագծի անվանում',
cardCurrentTheme: 'Ընթացիկ թեմա',
cardLanguages: 'Լեզուներ',
cardCategoriesCount: 'Կատեգորիաներ',
cardProductsCount: 'Ապրանքներ',
cardOrders: 'Պատվերներ',
cardRevenue: 'Եկամուտ',
cardLastPublish: 'Վերջին հրապարակում',
cardLastDraftSave: 'Վերջին սևագրի պահպանում',
cardBootstrapVersion: 'Bootstrap-ի տարբերակ',
cardActiveLayout: 'Ակտիվ դասավորություն',
cardEnabledWidgets: 'Միացված վիջեթներ',
stateLoading: 'Բեռնվում է...',
stateEmpty: 'Դեռ տվյալներ չկան',
stateError: 'Չհաջողվեց բեռնել այս քարտը',
statePendingBackend: 'Սպասվում է backend ինտեգրման',
never: 'Երբեք',
quickActionsTitle: 'Արագ գործողություններ',
actionEditProject: 'Խմբագրել նախագիծը',
actionCategories: 'Կատեգորիաներ',
actionProducts: 'Ապրանքներ',
actionStaticPages: 'Ստատիկ էջեր',
actionTransactions: 'Գործարքներ',
actionOrders: 'Պատվերներ',
actionMediaLibrary: 'Մեդիագրադարան',
actionPreviewMarketplace: 'Դիտել մարքեթփլեյսը',
activityTitle: 'Վերջին ակտիվություն',
activityEmpty: 'Դեռ ակտիվություն չկա։ Պահպանեք կամ հրապարակեք փոփոխություն՝ այն այստեղ տեսնելու համար։',
activityDraftSaved: 'Սևագիրը պահպանվեց',
activityPublished: 'Հրապարակվեց',
healthTitle: 'Համակարգի վիճակ',
healthAllClear: 'Բոլոր ստուգումներն անցել են',
healthBootstrapValid: 'Bootstrap-ը վավեր է',
healthConfigurationValid: 'Կոնֆիգուրացիան վավեր է',
healthMissingTranslations: 'Բացակայող թարգմանություններ',
healthInvalidColors: 'Անվավեր գույներ',
healthInvalidWidgetReferences: 'Անվավեր վիջեթ հղումներ',
healthInvalidLayouts: 'Անվավեր դասավորություններ',
comingSoonTitle: 'Շուտով',
comingSoonDescription: 'Այս բաժինը դեռ կառուցված չէ։ Ստուգեք ավելի ուշ։',
backToDashboard: 'Վերադառնալ վահանակ',
},
staticPages: {
notFound: 'Էջը չի գտնվել',
backHome: 'Վերադառնալ գլխավոր',

View File

@@ -512,6 +512,8 @@ export const ru: Translations = {
validationMissingWidget: 'В секции главной страницы есть виджет без типа.',
validationDuplicateNavLinks: 'Две или более ссылки в навигации шапки дублируются.',
validationInvalidColors: 'Один или несколько цветов темы указаны некорректно.',
validationMissingTranslations: 'Часть контента не переведена на один из поддерживаемых языков.',
validationInvalidLayouts: 'Один или несколько макетов ссылаются на неизвестный тип раскладки.',
statusDraft: 'Черновик',
statusPublished: 'Опубликовано',
unsavedChanges: 'Есть несохранённые изменения',
@@ -530,6 +532,52 @@ export const ru: Translations = {
confirmResetDraft: 'Это отменит все неопубликованные изменения и восстановит последнюю опубликованную конфигурацию. Продолжить?',
confirmResetSection: 'Это отменит неопубликованные изменения только в этой секции. Продолжить?',
},
dashboard: {
title: 'Панель управления',
subtitle: 'Обзор вашего маркетплейса',
cardMarketplaceStatus: 'Статус маркетплейса',
cardProjectName: 'Название проекта',
cardCurrentTheme: 'Текущая тема',
cardLanguages: 'Языки',
cardCategoriesCount: 'Категории',
cardProductsCount: 'Товары',
cardOrders: 'Заказы',
cardRevenue: 'Выручка',
cardLastPublish: 'Последняя публикация',
cardLastDraftSave: 'Последнее сохранение черновика',
cardBootstrapVersion: 'Версия конфигурации',
cardActiveLayout: 'Активный макет',
cardEnabledWidgets: 'Активные виджеты',
stateLoading: 'Загрузка...',
stateEmpty: 'Пока нет данных',
stateError: 'Не удалось загрузить карточку',
statePendingBackend: 'Ожидает интеграции с бэкендом',
never: 'Никогда',
quickActionsTitle: 'Быстрые действия',
actionEditProject: 'Редактировать проект',
actionCategories: 'Категории',
actionProducts: 'Товары',
actionStaticPages: 'Статические страницы',
actionTransactions: 'Транзакции',
actionOrders: 'Заказы',
actionMediaLibrary: 'Медиатека',
actionPreviewMarketplace: 'Просмотр маркетплейса',
activityTitle: 'Недавняя активность',
activityEmpty: 'Пока нет активности. Сохраните или опубликуйте изменение, чтобы увидеть его здесь.',
activityDraftSaved: 'Черновик сохранён',
activityPublished: 'Опубликовано',
healthTitle: 'Состояние системы',
healthAllClear: 'Все проверки пройдены',
healthBootstrapValid: 'Bootstrap корректен',
healthConfigurationValid: 'Конфигурация корректна',
healthMissingTranslations: 'Отсутствуют переводы',
healthInvalidColors: 'Некорректные цвета',
healthInvalidWidgetReferences: 'Некорректные ссылки виджетов',
healthInvalidLayouts: 'Некорректные макеты',
comingSoonTitle: 'Скоро',
comingSoonDescription: 'Этот раздел ещё не реализован. Загляните позже.',
backToDashboard: 'Вернуться на панель управления',
},
staticPages: {
notFound: 'Страница не найдена',
backHome: 'На главную',

View File

@@ -510,6 +510,8 @@ export interface Translations {
validationMissingWidget: string;
validationDuplicateNavLinks: string;
validationInvalidColors: string;
validationMissingTranslations: string;
validationInvalidLayouts: string;
statusDraft: string;
statusPublished: string;
unsavedChanges: string;
@@ -528,6 +530,52 @@ export interface Translations {
confirmResetDraft: string;
confirmResetSection: string;
};
dashboard: {
title: string;
subtitle: string;
cardMarketplaceStatus: string;
cardProjectName: string;
cardCurrentTheme: string;
cardLanguages: string;
cardCategoriesCount: string;
cardProductsCount: string;
cardOrders: string;
cardRevenue: string;
cardLastPublish: string;
cardLastDraftSave: string;
cardBootstrapVersion: string;
cardActiveLayout: string;
cardEnabledWidgets: string;
stateLoading: string;
stateEmpty: string;
stateError: string;
statePendingBackend: string;
never: string;
quickActionsTitle: string;
actionEditProject: string;
actionCategories: string;
actionProducts: string;
actionStaticPages: string;
actionTransactions: string;
actionOrders: string;
actionMediaLibrary: string;
actionPreviewMarketplace: string;
activityTitle: string;
activityEmpty: string;
activityDraftSaved: string;
activityPublished: string;
healthTitle: string;
healthAllClear: string;
healthBootstrapValid: string;
healthConfigurationValid: string;
healthMissingTranslations: string;
healthInvalidColors: string;
healthInvalidWidgetReferences: string;
healthInvalidLayouts: string;
comingSoonTitle: string;
comingSoonDescription: string;
backToDashboard: string;
};
staticPages: {
notFound: string;
backHome: string;