2026-02-26 23:09:20 +04:00
|
|
|
export interface Translations {
|
|
|
|
|
header: {
|
|
|
|
|
home: string;
|
|
|
|
|
search: string;
|
|
|
|
|
about: string;
|
|
|
|
|
contacts: string;
|
|
|
|
|
searchPlaceholder: string;
|
|
|
|
|
catalog: string;
|
2026-07-09 01:13:54 +04:00
|
|
|
wishlist: string;
|
|
|
|
|
compare: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
openMenu: string;
|
|
|
|
|
closeMenu: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
};
|
|
|
|
|
footer: {
|
|
|
|
|
description: string;
|
|
|
|
|
company: string;
|
|
|
|
|
aboutUs: string;
|
|
|
|
|
contacts: string;
|
|
|
|
|
requisites: string;
|
|
|
|
|
support: string;
|
|
|
|
|
faq: string;
|
|
|
|
|
delivery: string;
|
|
|
|
|
guarantee: string;
|
|
|
|
|
legal: string;
|
|
|
|
|
offer: string;
|
|
|
|
|
privacy: string;
|
|
|
|
|
returns: string;
|
|
|
|
|
info: string;
|
|
|
|
|
aboutCompany: string;
|
|
|
|
|
documents: string;
|
|
|
|
|
paymentRules: string;
|
|
|
|
|
returnPolicy: string;
|
|
|
|
|
publicOffer: string;
|
|
|
|
|
help: string;
|
|
|
|
|
payment: string;
|
|
|
|
|
allRightsReserved: string;
|
|
|
|
|
};
|
|
|
|
|
home: {
|
|
|
|
|
welcomeTo: string;
|
|
|
|
|
subtitle: string;
|
|
|
|
|
startSearch: string;
|
|
|
|
|
loading: string;
|
|
|
|
|
errorTitle: string;
|
|
|
|
|
retry: string;
|
|
|
|
|
categoriesTitle: string;
|
|
|
|
|
categoriesSubtitle: string;
|
|
|
|
|
categoriesEmpty: string;
|
|
|
|
|
categoriesEmptyDesc: string;
|
|
|
|
|
dexarHeroTitle: string;
|
|
|
|
|
dexarHeroSubtitle: string;
|
|
|
|
|
dexarHeroTagline: string;
|
|
|
|
|
goToCatalog: string;
|
|
|
|
|
findProduct: string;
|
|
|
|
|
loadingDexar: string;
|
|
|
|
|
catalogTitle: string;
|
|
|
|
|
emptyCategoriesDexar: string;
|
|
|
|
|
categoriesSoonDexar: string;
|
|
|
|
|
itemsCount: string;
|
|
|
|
|
};
|
|
|
|
|
cart: {
|
|
|
|
|
title: string;
|
|
|
|
|
clear: string;
|
|
|
|
|
empty: string;
|
|
|
|
|
emptyDesc: string;
|
|
|
|
|
goShopping: string;
|
|
|
|
|
total: string;
|
|
|
|
|
items: string;
|
|
|
|
|
deliveryLabel: string;
|
2026-06-21 23:13:01 +04:00
|
|
|
deliveryMethod: string;
|
|
|
|
|
selectDelivery: string;
|
|
|
|
|
deliveryPlace: string;
|
|
|
|
|
deliveryTime: string;
|
|
|
|
|
digitalDelivery: string;
|
|
|
|
|
deliveryRequired: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
toPay: string;
|
|
|
|
|
agreeWith: string;
|
|
|
|
|
publicOffer: string;
|
|
|
|
|
returnPolicy: string;
|
|
|
|
|
guaranteeTerms: string;
|
|
|
|
|
privacyPolicy: string;
|
|
|
|
|
and: string;
|
|
|
|
|
checkout: string;
|
2026-06-29 00:06:18 +04:00
|
|
|
payViaQr: string;
|
|
|
|
|
payViaCard: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
close: string;
|
|
|
|
|
creatingPayment: string;
|
|
|
|
|
waitFewSeconds: string;
|
|
|
|
|
scanQr: string;
|
|
|
|
|
amountToPay: string;
|
|
|
|
|
waitingPayment: string;
|
|
|
|
|
copied: string;
|
|
|
|
|
copyLink: string;
|
|
|
|
|
openNewTab: string;
|
|
|
|
|
paymentSuccess: string;
|
|
|
|
|
paymentSuccessDesc: string;
|
|
|
|
|
sending: string;
|
|
|
|
|
send: string;
|
2026-06-02 00:57:36 +04:00
|
|
|
paymentError: string;
|
|
|
|
|
paymentErrorDesc: string;
|
|
|
|
|
retryPayment: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
paymentTimeout: string;
|
|
|
|
|
paymentTimeoutDesc: string;
|
|
|
|
|
autoClose: string;
|
|
|
|
|
confirmClear: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
removeItem: string;
|
|
|
|
|
paymentQrAlt: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
acceptTerms: string;
|
|
|
|
|
copyError: string;
|
|
|
|
|
emailSuccess: string;
|
|
|
|
|
emailError: string;
|
|
|
|
|
phoneRequired: string;
|
|
|
|
|
phoneMoreDigits: string;
|
|
|
|
|
phoneTooMany: string;
|
|
|
|
|
emailRequired: string;
|
|
|
|
|
emailTooShort: string;
|
|
|
|
|
emailTooLong: string;
|
|
|
|
|
emailNeedsAt: string;
|
|
|
|
|
emailNeedsDomain: string;
|
|
|
|
|
emailInvalid: string;
|
2026-03-24 00:09:11 +04:00
|
|
|
loginRequired: string;
|
|
|
|
|
loginRequiredDesc: string;
|
|
|
|
|
loginWithTelegram: string;
|
|
|
|
|
orScanQr: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
};
|
|
|
|
|
search: {
|
|
|
|
|
title: string;
|
|
|
|
|
placeholder: string;
|
2026-07-10 13:25:31 +04:00
|
|
|
searchBarAria: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
resultsCount: string;
|
|
|
|
|
searching: string;
|
|
|
|
|
retry: string;
|
|
|
|
|
noResults: string;
|
|
|
|
|
noResultsFor: string;
|
|
|
|
|
noResultsHint: string;
|
2026-07-10 13:25:31 +04:00
|
|
|
emptyResultsAria: string;
|
|
|
|
|
popularCategories: string;
|
|
|
|
|
recommendedProducts: string;
|
|
|
|
|
aiSuggestionHint: string;
|
|
|
|
|
suggestionType: {
|
|
|
|
|
product: string;
|
|
|
|
|
category: string;
|
|
|
|
|
brand: string;
|
|
|
|
|
collection: string;
|
|
|
|
|
seller: string;
|
|
|
|
|
'static-page': string;
|
|
|
|
|
ai: string;
|
|
|
|
|
};
|
2026-02-26 23:09:20 +04:00
|
|
|
addToCart: string;
|
|
|
|
|
loadingMore: string;
|
|
|
|
|
allLoaded: string;
|
|
|
|
|
emptyState: string;
|
2026-07-05 01:10:56 +04:00
|
|
|
minLength: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
of: string;
|
|
|
|
|
};
|
|
|
|
|
category: {
|
|
|
|
|
retry: string;
|
|
|
|
|
addToCart: string;
|
|
|
|
|
loadingMore: string;
|
|
|
|
|
allLoaded: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDesc: string;
|
|
|
|
|
goHome: string;
|
|
|
|
|
loading: string;
|
|
|
|
|
};
|
2026-07-05 01:36:21 +04:00
|
|
|
catalog: {
|
|
|
|
|
title: string;
|
|
|
|
|
breadcrumb: string;
|
|
|
|
|
allCategories: string;
|
|
|
|
|
products: string;
|
|
|
|
|
loading: string;
|
|
|
|
|
loadingProducts: string;
|
|
|
|
|
errorTitle: string;
|
|
|
|
|
error: string;
|
|
|
|
|
retry: string;
|
|
|
|
|
categoryHint: string;
|
|
|
|
|
productHint: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyCategories: string;
|
|
|
|
|
emptyProducts: string;
|
|
|
|
|
addToCart: string;
|
|
|
|
|
categoriesCount: string;
|
|
|
|
|
productsCount: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
entriesCount: string;
|
|
|
|
|
categoryBannerPlaceholder: string;
|
|
|
|
|
navigationPlaceholder: string;
|
|
|
|
|
removeSavedSearch: string;
|
|
|
|
|
resetFilters: string;
|
|
|
|
|
filtersTitle: string;
|
2026-07-09 02:57:48 +04:00
|
|
|
from: string;
|
|
|
|
|
to: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
minValue: string;
|
|
|
|
|
maxValue: string;
|
|
|
|
|
enabled: string;
|
|
|
|
|
searchPlaceholder: string;
|
|
|
|
|
searchSubmit: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
clearSearch: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
suggestionsTitle: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
popularSearchesTitle: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
recentSearchesTitle: string;
|
|
|
|
|
searchHistoryTitle: string;
|
|
|
|
|
clearHistory: string;
|
|
|
|
|
searchNoResultsHint: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
searchProductsFound: string;
|
|
|
|
|
searchResultsSummary: string;
|
2026-07-10 15:55:38 +04:00
|
|
|
loadMore: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
sortBy: string;
|
|
|
|
|
sortRelevance: string;
|
|
|
|
|
sortLatest: string;
|
|
|
|
|
sortPriceAsc: string;
|
|
|
|
|
sortPriceDesc: string;
|
|
|
|
|
sortRating: string;
|
|
|
|
|
sortPopular: string;
|
|
|
|
|
sortDiscount: string;
|
|
|
|
|
layoutGrid: string;
|
|
|
|
|
layoutLargeGrid: string;
|
|
|
|
|
layoutCompactGrid: string;
|
2026-07-10 15:55:38 +04:00
|
|
|
layoutGrid2: string;
|
|
|
|
|
layoutGrid3: string;
|
|
|
|
|
layoutGrid4: string;
|
|
|
|
|
layoutCompact: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
layoutList: string;
|
|
|
|
|
noResults: string;
|
|
|
|
|
itemsCount: string;
|
|
|
|
|
emptyResultsTitle: string;
|
|
|
|
|
emptyResultsDescription: string;
|
|
|
|
|
previousPage: string;
|
|
|
|
|
nextPage: string;
|
|
|
|
|
pageOf: string;
|
2026-07-09 01:46:32 +04:00
|
|
|
emptyCategoryTitle: string;
|
|
|
|
|
emptyCategoryMessage: string;
|
2026-07-10 15:55:38 +04:00
|
|
|
goToParentCategory: string;
|
2026-07-09 01:46:32 +04:00
|
|
|
browseCategories: string;
|
2026-07-10 15:55:38 +04:00
|
|
|
offlineTitle: string;
|
|
|
|
|
offlineMessage: string;
|
2026-07-09 01:46:32 +04:00
|
|
|
noFilterMatchTitle: string;
|
|
|
|
|
noFilterMatchMessage: string;
|
|
|
|
|
clearFilters: string;
|
|
|
|
|
openFilters: string;
|
|
|
|
|
applyFilters: string;
|
|
|
|
|
close: string;
|
|
|
|
|
filtersDrawerAria: string;
|
|
|
|
|
sortSheetAria: string;
|
|
|
|
|
mobileToolbarAria: string;
|
|
|
|
|
gridCycle: string;
|
|
|
|
|
filtersPanelAria: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
filterPrice: string;
|
|
|
|
|
filterAvailability: string;
|
|
|
|
|
filterInStock: string;
|
|
|
|
|
filterLowStock: string;
|
|
|
|
|
filterOutOfStock: string;
|
|
|
|
|
filterRating: string;
|
|
|
|
|
filterStars: string;
|
|
|
|
|
filterBrand: string;
|
|
|
|
|
filterCategory: string;
|
|
|
|
|
filterCategoryValue: string;
|
|
|
|
|
filterSubcategory: string;
|
|
|
|
|
filterDiscount: string;
|
|
|
|
|
filterNew: string;
|
|
|
|
|
filterColor: string;
|
|
|
|
|
filterSize: string;
|
|
|
|
|
filterAttributes: string;
|
|
|
|
|
};
|
|
|
|
|
productDetails: {
|
|
|
|
|
loading: string;
|
|
|
|
|
errorTitle: string;
|
|
|
|
|
retry: string;
|
|
|
|
|
missingTitle: string;
|
|
|
|
|
missingDescription: string;
|
|
|
|
|
backToCatalog: string;
|
|
|
|
|
tabsAria: string;
|
|
|
|
|
mediaAria: string;
|
|
|
|
|
ratingStarsAria: string;
|
|
|
|
|
selectRatingAria: string;
|
|
|
|
|
reviewsEmpty: string;
|
|
|
|
|
questionsEmpty: string;
|
|
|
|
|
askQuestion: string;
|
|
|
|
|
hideQuestionForm: string;
|
|
|
|
|
questionFormTitle: string;
|
|
|
|
|
questionFormPlaceholder: string;
|
|
|
|
|
questionFormAnonymous: string;
|
|
|
|
|
submitQuestion: string;
|
|
|
|
|
reviewFormTitle: string;
|
|
|
|
|
reviewRatingLabel: string;
|
|
|
|
|
reviewTitleLabel: string;
|
|
|
|
|
reviewTitlePlaceholder: string;
|
|
|
|
|
reviewTextLabel: string;
|
|
|
|
|
reviewTextPlaceholder: string;
|
|
|
|
|
reviewAnonymous: string;
|
|
|
|
|
reviewUploadPlaceholder: string;
|
2026-07-10 13:10:35 +04:00
|
|
|
reviewRatingRequired: string;
|
|
|
|
|
reviewTextRequired: string;
|
|
|
|
|
reviewSuccess: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
submitReview: string;
|
|
|
|
|
submitting: string;
|
2026-07-10 13:10:35 +04:00
|
|
|
loadMore: string;
|
|
|
|
|
showingOf: string;
|
|
|
|
|
questionsSubmissionFuture: string;
|
|
|
|
|
specificationsGeneral: string;
|
|
|
|
|
actionsAria: string;
|
|
|
|
|
buyNow: string;
|
|
|
|
|
wishlist: string;
|
|
|
|
|
compare: string;
|
|
|
|
|
share: string;
|
|
|
|
|
notifyMe: string;
|
|
|
|
|
zoom: string;
|
|
|
|
|
fullscreen: string;
|
|
|
|
|
pdfDocument: string;
|
|
|
|
|
manualDocument: string;
|
|
|
|
|
warrantyDocument: string;
|
|
|
|
|
openDocument: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
specificationsEmpty: string;
|
|
|
|
|
warrantyTitle: string;
|
|
|
|
|
warrantyItem1: string;
|
|
|
|
|
warrantyItem2: string;
|
|
|
|
|
warrantyItem3: string;
|
|
|
|
|
verifiedPurchase: string;
|
|
|
|
|
officialSeller: string;
|
|
|
|
|
acceptedAnswer: string;
|
|
|
|
|
like: string;
|
|
|
|
|
dislike: string;
|
|
|
|
|
reviewsCountLabel: string;
|
|
|
|
|
previous: string;
|
|
|
|
|
next: string;
|
|
|
|
|
pageOf: string;
|
2026-07-05 01:36:21 +04:00
|
|
|
};
|
2026-02-26 23:09:20 +04:00
|
|
|
subcategories: {
|
|
|
|
|
loading: string;
|
|
|
|
|
retry: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDesc: string;
|
|
|
|
|
goHome: string;
|
2026-03-24 00:09:11 +04:00
|
|
|
itemsInCategory: string;
|
2026-07-05 01:09:55 +04:00
|
|
|
childrenCount: string;
|
|
|
|
|
productsCount: string;
|
|
|
|
|
includesProducts: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
};
|
|
|
|
|
itemDetail: {
|
|
|
|
|
loading: string;
|
|
|
|
|
loadingDexar: string;
|
|
|
|
|
back: string;
|
|
|
|
|
backHome: string;
|
|
|
|
|
noImage: string;
|
|
|
|
|
stock: string;
|
|
|
|
|
inStock: string;
|
|
|
|
|
lowStock: string;
|
|
|
|
|
lastItems: string;
|
|
|
|
|
mediumStock: string;
|
|
|
|
|
addToCart: string;
|
|
|
|
|
description: string;
|
2026-02-28 16:13:14 +04:00
|
|
|
specifications: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
reviews: string;
|
|
|
|
|
yourReview: string;
|
|
|
|
|
leaveReview: string;
|
|
|
|
|
rating: string;
|
|
|
|
|
reviewPlaceholder: string;
|
|
|
|
|
reviewPlaceholderDexar: string;
|
|
|
|
|
anonymous: string;
|
|
|
|
|
submitting: string;
|
|
|
|
|
submit: string;
|
|
|
|
|
reviewSuccess: string;
|
|
|
|
|
reviewError: string;
|
|
|
|
|
defaultUser: string;
|
|
|
|
|
defaultUserDexar: string;
|
|
|
|
|
noReviews: string;
|
|
|
|
|
qna: string;
|
|
|
|
|
photo: string;
|
|
|
|
|
reviewsCount: string;
|
|
|
|
|
today: string;
|
|
|
|
|
yesterday: string;
|
|
|
|
|
daysAgo: string;
|
|
|
|
|
weeksAgo: string;
|
2026-03-24 00:09:11 +04:00
|
|
|
colour: string;
|
|
|
|
|
size: string;
|
2026-07-05 01:07:53 +04:00
|
|
|
relatedProducts: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
};
|
|
|
|
|
app: {
|
|
|
|
|
connecting: string;
|
|
|
|
|
serverUnavailable: string;
|
|
|
|
|
serverError: string;
|
|
|
|
|
retryConnection: string;
|
|
|
|
|
pageTitle: string;
|
|
|
|
|
};
|
2026-07-09 02:29:12 +04:00
|
|
|
platform: {
|
|
|
|
|
loadingPage: string;
|
|
|
|
|
emptyConfig: string;
|
|
|
|
|
};
|
|
|
|
|
backoffice: {
|
|
|
|
|
title: string;
|
|
|
|
|
subtitle: string;
|
|
|
|
|
loading: string;
|
|
|
|
|
products: string;
|
|
|
|
|
categories: string;
|
|
|
|
|
otherDomains: string;
|
|
|
|
|
sku: string;
|
|
|
|
|
titleColumn: string;
|
|
|
|
|
price: string;
|
|
|
|
|
status: string;
|
|
|
|
|
itemsCount: string;
|
|
|
|
|
orders: string;
|
|
|
|
|
customers: string;
|
|
|
|
|
inventory: string;
|
|
|
|
|
media: string;
|
|
|
|
|
settings: string;
|
|
|
|
|
notAvailable: string;
|
|
|
|
|
};
|
|
|
|
|
builder: {
|
|
|
|
|
title: string;
|
|
|
|
|
subtitle: string;
|
2026-07-10 13:43:53 +04:00
|
|
|
general: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
branding: string;
|
2026-07-10 13:43:53 +04:00
|
|
|
theme: string;
|
|
|
|
|
header: string;
|
|
|
|
|
footer: string;
|
|
|
|
|
homepage: string;
|
|
|
|
|
widgets: string;
|
2026-07-10 13:52:01 +04:00
|
|
|
staticPages: string;
|
2026-07-10 13:43:53 +04:00
|
|
|
marketplaceFeatures: string;
|
|
|
|
|
preview: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
brandName: string;
|
2026-07-10 13:43:53 +04:00
|
|
|
marketplaceName: string;
|
|
|
|
|
domain: string;
|
|
|
|
|
descriptionLabel: string;
|
|
|
|
|
defaultLanguage: string;
|
|
|
|
|
supportedLanguages: string;
|
|
|
|
|
logo: string;
|
|
|
|
|
smallLogo: string;
|
|
|
|
|
favicon: string;
|
2026-07-17 15:22:45 +04:00
|
|
|
socialImage: string;
|
|
|
|
|
gallery: string;
|
|
|
|
|
addGalleryImage: string;
|
2026-07-17 15:29:55 +04:00
|
|
|
headerLayout: string;
|
|
|
|
|
headerLayoutDefault: string;
|
|
|
|
|
headerLayoutCentered: string;
|
|
|
|
|
headerSticky: string;
|
2026-07-10 13:43:53 +04:00
|
|
|
marketplaceTitle: string;
|
|
|
|
|
primaryColor: string;
|
|
|
|
|
secondaryColor: string;
|
|
|
|
|
backgroundColor: string;
|
|
|
|
|
surfaceColor: string;
|
|
|
|
|
textColor: string;
|
|
|
|
|
successColor: string;
|
|
|
|
|
warningColor: string;
|
|
|
|
|
dangerColor: string;
|
|
|
|
|
showLogo: string;
|
|
|
|
|
showSearch: string;
|
|
|
|
|
showCategories: string;
|
|
|
|
|
showLanguages: string;
|
|
|
|
|
showCart: string;
|
|
|
|
|
showProfile: string;
|
|
|
|
|
showWishlist: string;
|
|
|
|
|
showCompare: string;
|
|
|
|
|
showRegion: string;
|
|
|
|
|
companyName: string;
|
|
|
|
|
address: string;
|
|
|
|
|
phone: string;
|
|
|
|
|
email: string;
|
|
|
|
|
copyright: string;
|
|
|
|
|
paymentIcons: string;
|
|
|
|
|
socialLinks: string;
|
2026-07-16 02:06:26 +04:00
|
|
|
footerLogo: string;
|
|
|
|
|
addPaymentIcon: string;
|
|
|
|
|
removePaymentIcon: string;
|
|
|
|
|
iconAltLabel: string;
|
|
|
|
|
addSocialLink: string;
|
|
|
|
|
removeSocialLink: string;
|
|
|
|
|
socialLinkLabel: string;
|
|
|
|
|
socialLinkUrl: string;
|
|
|
|
|
invalidUrl: string;
|
2026-07-10 13:52:01 +04:00
|
|
|
createPage: string;
|
|
|
|
|
deletePage: string;
|
|
|
|
|
pageId: string;
|
|
|
|
|
slug: string;
|
|
|
|
|
iconLabel: string;
|
|
|
|
|
showInFooter: string;
|
|
|
|
|
showInHeader: string;
|
|
|
|
|
showInSitemap: string;
|
|
|
|
|
requiresAuthentication: string;
|
|
|
|
|
footerGroup: string;
|
|
|
|
|
orderLabel: string;
|
|
|
|
|
translationTitle: string;
|
|
|
|
|
htmlPreview: string;
|
2026-07-15 04:11:18 +04:00
|
|
|
seoSection: string;
|
|
|
|
|
seoTitle: string;
|
|
|
|
|
seoDescription: string;
|
|
|
|
|
seoKeywords: string;
|
|
|
|
|
seoCanonical: string;
|
|
|
|
|
seoOgTitle: string;
|
|
|
|
|
seoOgDescription: string;
|
|
|
|
|
seoOgImage: string;
|
2026-07-10 13:43:53 +04:00
|
|
|
visible: string;
|
|
|
|
|
layoutLabel: string;
|
|
|
|
|
columns: string;
|
|
|
|
|
height: string;
|
|
|
|
|
overlay: string;
|
|
|
|
|
autoplay: string;
|
|
|
|
|
cardsPerRow: string;
|
|
|
|
|
filtersLabel: string;
|
|
|
|
|
showBadges: string;
|
|
|
|
|
showRating: string;
|
|
|
|
|
showPrice: string;
|
|
|
|
|
widgetJson: string;
|
|
|
|
|
reviews: string;
|
|
|
|
|
questions: string;
|
|
|
|
|
comments: string;
|
|
|
|
|
recommendations: string;
|
|
|
|
|
recentlyViewed: string;
|
|
|
|
|
searchSuggestions: string;
|
|
|
|
|
searchHistory: string;
|
|
|
|
|
livePreview: string;
|
2026-07-17 09:00:47 +04:00
|
|
|
previewChangesTitle: string;
|
|
|
|
|
previewNoIssues: string;
|
|
|
|
|
previewNoChanges: string;
|
|
|
|
|
previewChangeField: string;
|
|
|
|
|
previewChangeBefore: string;
|
|
|
|
|
previewChangeAfter: string;
|
2026-07-13 08:10:00 +04:00
|
|
|
languagesTab: string;
|
|
|
|
|
addLanguage: string;
|
|
|
|
|
removeLanguage: string;
|
|
|
|
|
setDefaultLanguage: string;
|
|
|
|
|
defaultLanguageLabel: string;
|
2026-07-13 08:57:42 +04:00
|
|
|
navigationTab: string;
|
|
|
|
|
navigationHeader: string;
|
|
|
|
|
navigationFooter: string;
|
|
|
|
|
navigationFooterGrouped: string;
|
|
|
|
|
addLink: string;
|
2026-07-17 10:18:08 +04:00
|
|
|
insertPageLink: string;
|
|
|
|
|
linkedToPage: string;
|
2026-07-13 08:57:42 +04:00
|
|
|
removeLink: string;
|
|
|
|
|
linkLabel: string;
|
|
|
|
|
linkUrl: string;
|
2026-07-10 13:43:53 +04:00
|
|
|
exportBootstrap: string;
|
|
|
|
|
importBootstrap: string;
|
|
|
|
|
exportedBootstrap: string;
|
|
|
|
|
importSource: string;
|
|
|
|
|
importError: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
featureFlags: string;
|
2026-07-13 10:51:05 +04:00
|
|
|
validationMissingLogo: string;
|
|
|
|
|
validationNoLanguages: string;
|
2026-07-17 19:00:17 +04:00
|
|
|
validationDefaultLocaleNotSupported: string;
|
2026-07-13 10:51:05 +04:00
|
|
|
validationInvalidUrl: string;
|
|
|
|
|
validationDuplicateSlugs: string;
|
|
|
|
|
validationEmptyHomepage: string;
|
|
|
|
|
validationMissingWidget: string;
|
|
|
|
|
validationDuplicateNavLinks: string;
|
|
|
|
|
validationInvalidColors: string;
|
2026-07-14 12:21:33 +04:00
|
|
|
validationMissingTranslations: string;
|
|
|
|
|
validationInvalidLayouts: string;
|
feat(project-editor): centralize schema-driven validation
Milestone 2 of the Configuration Engine sprint.
- Add schema/validators/primitives: pure isValidHexColor/HttpUrl/Email,
validateJson, validateCss, extractStyleBlocks, normalizeRoute. One function
per concern, no duplicated validator logic.
- Refactor ProjectValidator to compose the primitives and tag every issue with
section + fieldKey + severity ('error' blocks publish, 'warning' advisory).
Preserves all existing codes/messages; adds duplicate-routes, invalid-css,
invalid-widget-config checks.
- Facade: issuesByField, issuesBySection, blockingIssues, hasBlockingIssues;
publish() now gates on severity==='error' instead of any issue.
- i18n: add validationInvalidJson/Css/DuplicateRoutes/InvalidWidgetConfig to
the Translations interface + en/ru/hy.
- Specs: primitives + ProjectValidator (25 passing total).
Gate: tsc --noEmit, npm test (25/25), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:58:36 +04:00
|
|
|
validationInvalidJson: string;
|
|
|
|
|
validationInvalidCss: string;
|
|
|
|
|
validationDuplicateRoutes: string;
|
|
|
|
|
validationInvalidWidgetConfig: string;
|
2026-07-17 16:57:43 +04:00
|
|
|
validationInvalidContactEmail: string;
|
|
|
|
|
validationInvalidSocialLinkUrl: string;
|
|
|
|
|
validationIncompletePaymentIcon: string;
|
2026-07-17 02:13:34 +04:00
|
|
|
undo: string;
|
|
|
|
|
redo: string;
|
2026-07-13 15:00:03 +04:00
|
|
|
statusDraft: string;
|
|
|
|
|
statusPublished: string;
|
|
|
|
|
unsavedChanges: string;
|
|
|
|
|
save: string;
|
|
|
|
|
publish: string;
|
2026-07-13 15:57:54 +04:00
|
|
|
confirmLeaveUnsaved: string;
|
|
|
|
|
promptLinkUrl: string;
|
|
|
|
|
promptImageUrl: string;
|
2026-07-17 10:00:33 +04:00
|
|
|
promptEmbedUrl: string;
|
|
|
|
|
htmlEditorInvalidHtml: string;
|
2026-07-13 15:57:54 +04:00
|
|
|
htmlEditorCode: string;
|
|
|
|
|
htmlEditorPreview: string;
|
2026-07-14 09:50:03 +04:00
|
|
|
lastSaved: string;
|
|
|
|
|
draftRestored: string;
|
|
|
|
|
dismiss: string;
|
|
|
|
|
resetDraft: string;
|
|
|
|
|
resetSection: string;
|
|
|
|
|
confirmResetDraft: string;
|
|
|
|
|
confirmResetSection: string;
|
2026-07-14 16:05:09 +04:00
|
|
|
marketplaceNameDesc: string;
|
|
|
|
|
domainDesc: string;
|
|
|
|
|
descriptionFieldDesc: string;
|
|
|
|
|
defaultLanguageDesc: string;
|
|
|
|
|
supportedLanguagesDesc: string;
|
|
|
|
|
logoDesc: string;
|
|
|
|
|
smallLogoDesc: string;
|
|
|
|
|
faviconDesc: string;
|
2026-07-17 15:22:45 +04:00
|
|
|
socialImageDesc: string;
|
|
|
|
|
galleryDesc: string;
|
2026-07-17 15:29:55 +04:00
|
|
|
headerLayoutDesc: string;
|
|
|
|
|
headerStickyDesc: string;
|
2026-07-14 16:05:09 +04:00
|
|
|
marketplaceTitleDesc: string;
|
|
|
|
|
primaryColorDesc: string;
|
|
|
|
|
secondaryColorDesc: string;
|
|
|
|
|
backgroundColorDesc: string;
|
|
|
|
|
surfaceColorDesc: string;
|
|
|
|
|
textColorDesc: string;
|
|
|
|
|
successColorDesc: string;
|
|
|
|
|
warningColorDesc: string;
|
|
|
|
|
dangerColorDesc: string;
|
|
|
|
|
themeModeLabel: string;
|
|
|
|
|
themeModeDesc: string;
|
|
|
|
|
themeModeLight: string;
|
|
|
|
|
themeModeLightDesc: string;
|
|
|
|
|
themeModeDark: string;
|
|
|
|
|
themeModeDarkDesc: string;
|
|
|
|
|
themeModeSystem: string;
|
|
|
|
|
themeModeSystemDesc: string;
|
|
|
|
|
siteLayoutLabel: string;
|
|
|
|
|
siteLayoutDesc: string;
|
|
|
|
|
siteLayoutDefault: string;
|
|
|
|
|
siteLayoutDefaultDesc: string;
|
|
|
|
|
siteLayoutSidebarLeft: string;
|
|
|
|
|
siteLayoutSidebarLeftDesc: string;
|
|
|
|
|
siteLayoutCarouselHome: string;
|
|
|
|
|
siteLayoutCarouselHomeDesc: string;
|
|
|
|
|
siteLayoutMinimal: string;
|
|
|
|
|
siteLayoutMinimalDesc: string;
|
|
|
|
|
showLogoDesc: string;
|
|
|
|
|
showSearchDesc: string;
|
|
|
|
|
showCategoriesDesc: string;
|
|
|
|
|
showLanguagesDesc: string;
|
|
|
|
|
showCartDesc: string;
|
|
|
|
|
showProfileDesc: string;
|
|
|
|
|
showWishlistDesc: string;
|
|
|
|
|
showCompareDesc: string;
|
|
|
|
|
showRegionDesc: string;
|
|
|
|
|
companyNameDesc: string;
|
|
|
|
|
addressDesc: string;
|
|
|
|
|
phoneDesc: string;
|
|
|
|
|
emailDesc: string;
|
|
|
|
|
copyrightDesc: string;
|
|
|
|
|
paymentIconsDesc: string;
|
|
|
|
|
socialLinksDesc: string;
|
2026-07-16 02:06:26 +04:00
|
|
|
footerLogoDesc: string;
|
|
|
|
|
iconAltLabelDesc: string;
|
|
|
|
|
socialLinkUrlDesc: string;
|
2026-07-14 16:05:09 +04:00
|
|
|
staticPagesFieldDesc: string;
|
|
|
|
|
sectionVisibleDesc: string;
|
|
|
|
|
layoutStrategyLabel: string;
|
|
|
|
|
layoutStrategyDesc: string;
|
|
|
|
|
layoutStrategyStack: string;
|
|
|
|
|
layoutStrategyStackDesc: string;
|
|
|
|
|
layoutStrategyGrid: string;
|
|
|
|
|
layoutStrategyGridDesc: string;
|
|
|
|
|
layoutStrategyHero: string;
|
|
|
|
|
layoutStrategyHeroDesc: string;
|
|
|
|
|
layoutStrategyCarousel: string;
|
|
|
|
|
layoutStrategyCarouselDesc: string;
|
|
|
|
|
layoutStrategySplit: string;
|
|
|
|
|
layoutStrategySplitDesc: string;
|
|
|
|
|
sectionColumnsDesc: string;
|
|
|
|
|
widgetLayoutDesc: string;
|
|
|
|
|
widgetHeightDesc: string;
|
|
|
|
|
widgetOverlayDesc: string;
|
|
|
|
|
widgetAutoplayDesc: string;
|
|
|
|
|
widgetColumnsDesc: string;
|
|
|
|
|
widgetCardsPerRowDesc: string;
|
|
|
|
|
widgetFiltersDesc: string;
|
|
|
|
|
widgetShowBadgesDesc: string;
|
|
|
|
|
widgetShowRatingDesc: string;
|
|
|
|
|
widgetShowPriceDesc: string;
|
|
|
|
|
widgetJsonDesc: string;
|
2026-07-17 18:14:58 +04:00
|
|
|
widgetJsonInvalid: string;
|
2026-07-14 16:05:09 +04:00
|
|
|
wishlistFeatureDesc: string;
|
|
|
|
|
compareFeatureDesc: string;
|
|
|
|
|
reviewsFeatureDesc: string;
|
|
|
|
|
questionsFeatureDesc: string;
|
|
|
|
|
commentsFeatureDesc: string;
|
|
|
|
|
recommendationsFeatureDesc: string;
|
|
|
|
|
recentlyViewedFeatureDesc: string;
|
|
|
|
|
searchSuggestionsDesc: string;
|
|
|
|
|
searchHistoryDesc: string;
|
|
|
|
|
catalogNavigationModeLabel: string;
|
|
|
|
|
catalogNavigationModeDesc: string;
|
|
|
|
|
catalogNavDefault: string;
|
|
|
|
|
catalogNavDefaultDesc: string;
|
|
|
|
|
catalogNavLeftCategory: string;
|
|
|
|
|
catalogNavLeftCategoryDesc: string;
|
|
|
|
|
catalogNavMegaCategory: string;
|
|
|
|
|
catalogNavMegaCategoryDesc: string;
|
|
|
|
|
catalogNavTopCarousel: string;
|
|
|
|
|
catalogNavTopCarouselDesc: string;
|
|
|
|
|
newLanguageDesc: string;
|
2026-07-17 18:31:01 +04:00
|
|
|
languageAlreadyAdded: string;
|
2026-07-14 16:05:09 +04:00
|
|
|
languagesTabDesc: string;
|
|
|
|
|
linkLabelDesc: string;
|
|
|
|
|
linkUrlDesc: string;
|
|
|
|
|
linkVisibleDesc: string;
|
feat(builder): redesign marketplace builder experience and navigation
Renamed the experience consistently to Marketplace Builder everywhere
visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick
action/shortcut copy) - internal ProjectEditor class/selector names
kept as-is to avoid regressions. builder.title/subtitle no longer say
'bootstrap-config editing surface' to end users.
New business-oriented IA (builder-groups.model.ts): 12 existing
ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding
and Design, Homepage, Content, Languages, Marketplace Features,
Navigation and Search, Preview) - every existing section mapped to
exactly one group, none dropped, no group points at a page that
doesn't exist.
New Builder landing page at /edit (was a redirect straight into the
General form): readiness percent, recommended next step, one overview
card per group (purpose + real complete/in-progress/not-started/unknown
status, icon+text never color-alone), a Marketplace Readiness
checklist, quick links. All derived from real facade/schema signals
(required-field fill state, modifiedSections, staticPages, catalog
counts via BackofficeDataService) or explicitly marked unknown
(the "preview reviewed" check has no tracking - shown as unknown,
never guessed).
Section pages (/edit/:section) now share one consistent header:
breadcrumb (Builder > Group > Section), draft/published + modified
badges, and a contextual help panel (what is this / where visible /
what happens if you skip it) sourced per group. Sidebar nav rewritten
as grouped, icon-led sections with per-section and per-group status
indicators; layout converted from a top pill-tab bar to a responsive
sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with
Escape-to-close), matching the Sprint 1 admin shell pattern. Section
form components themselves untouched.
Routes: 'edit' is now its own landing route instead of redirecting to
'edit/general'; /builder and /project-editor redirect to 'edit'.
Sprint 1/2 destinations that pointed at edit/general now point at the
new edit landing page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
|
|
|
appName: string;
|
2026-07-19 08:25:29 +04:00
|
|
|
backToDashboard: string;
|
2026-07-19 08:48:22 +04:00
|
|
|
htmlToolBold: string;
|
|
|
|
|
htmlToolItalic: string;
|
|
|
|
|
htmlToolUnderline: string;
|
|
|
|
|
htmlToolHeading: string;
|
|
|
|
|
htmlToolSubheading: string;
|
|
|
|
|
htmlToolBulletList: string;
|
|
|
|
|
htmlToolNumberedList: string;
|
|
|
|
|
htmlToolLink: string;
|
|
|
|
|
htmlToolImage: string;
|
|
|
|
|
htmlToolTable: string;
|
|
|
|
|
htmlToolDivider: string;
|
|
|
|
|
htmlToolCodeBlock: string;
|
|
|
|
|
htmlToolEmbed: string;
|
|
|
|
|
|
feat(builder): redesign marketplace builder experience and navigation
Renamed the experience consistently to Marketplace Builder everywhere
visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick
action/shortcut copy) - internal ProjectEditor class/selector names
kept as-is to avoid regressions. builder.title/subtitle no longer say
'bootstrap-config editing surface' to end users.
New business-oriented IA (builder-groups.model.ts): 12 existing
ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding
and Design, Homepage, Content, Languages, Marketplace Features,
Navigation and Search, Preview) - every existing section mapped to
exactly one group, none dropped, no group points at a page that
doesn't exist.
New Builder landing page at /edit (was a redirect straight into the
General form): readiness percent, recommended next step, one overview
card per group (purpose + real complete/in-progress/not-started/unknown
status, icon+text never color-alone), a Marketplace Readiness
checklist, quick links. All derived from real facade/schema signals
(required-field fill state, modifiedSections, staticPages, catalog
counts via BackofficeDataService) or explicitly marked unknown
(the "preview reviewed" check has no tracking - shown as unknown,
never guessed).
Section pages (/edit/:section) now share one consistent header:
breadcrumb (Builder > Group > Section), draft/published + modified
badges, and a contextual help panel (what is this / where visible /
what happens if you skip it) sourced per group. Sidebar nav rewritten
as grouped, icon-led sections with per-section and per-group status
indicators; layout converted from a top pill-tab bar to a responsive
sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with
Escape-to-close), matching the Sprint 1 admin shell pattern. Section
form components themselves untouched.
Routes: 'edit' is now its own landing route instead of redirecting to
'edit/general'; /builder and /project-editor redirect to 'edit'.
Sprint 1/2 destinations that pointed at edit/general now point at the
new edit landing page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
|
|
|
breadcrumbRoot: string;
|
|
|
|
|
statusComplete: string;
|
|
|
|
|
statusInProgress: string;
|
|
|
|
|
statusNotStarted: string;
|
|
|
|
|
statusUnknown: string;
|
|
|
|
|
draftBadge: string;
|
|
|
|
|
publishedBadge: string;
|
|
|
|
|
modifiedBadge: string;
|
|
|
|
|
helpToggleShow: string;
|
|
|
|
|
helpToggleHide: string;
|
|
|
|
|
helpWhatIsThis: string;
|
|
|
|
|
helpWhySkip: string;
|
|
|
|
|
helpWhereVisible: string;
|
|
|
|
|
helpWhatIfSkip: string;
|
|
|
|
|
helpWhenReturn: string;
|
|
|
|
|
openSidebar: string;
|
|
|
|
|
closeSidebar: string;
|
|
|
|
|
overviewTitle: string;
|
|
|
|
|
overviewSubtitle: string;
|
|
|
|
|
overviewReadinessLabel: string;
|
|
|
|
|
overviewReadinessHint: string;
|
|
|
|
|
overviewNextStepLabel: string;
|
|
|
|
|
overviewNextStepFallback: string;
|
|
|
|
|
overviewQuickLinksTitle: string;
|
|
|
|
|
overviewSectionsTitle: string;
|
|
|
|
|
overviewOpenSection: string;
|
|
|
|
|
overviewReadinessTitle: string;
|
|
|
|
|
readinessLogoUploaded: string;
|
|
|
|
|
readinessHomepageConfigured: string;
|
|
|
|
|
readinessThemeConfigured: string;
|
|
|
|
|
readinessLanguagesConfigured: string;
|
|
|
|
|
readinessContentPagesAdded: string;
|
|
|
|
|
readinessPreviewReviewed: string;
|
|
|
|
|
readinessPublished: string;
|
|
|
|
|
readinessCategoriesExist: string;
|
|
|
|
|
readinessProductsExist: string;
|
|
|
|
|
groupMarketplaceLabel: string;
|
|
|
|
|
groupMarketplaceDescription: string;
|
|
|
|
|
groupMarketplacePurpose: string;
|
|
|
|
|
groupMarketplaceWhere: string;
|
|
|
|
|
groupMarketplaceSkip: string;
|
|
|
|
|
groupBrandingLabel: string;
|
|
|
|
|
groupBrandingDescription: string;
|
|
|
|
|
groupBrandingPurpose: string;
|
|
|
|
|
groupBrandingWhere: string;
|
|
|
|
|
groupBrandingSkip: string;
|
|
|
|
|
groupHomepageLabel: string;
|
|
|
|
|
groupHomepageDescription: string;
|
|
|
|
|
groupHomepagePurpose: string;
|
|
|
|
|
groupHomepageWhere: string;
|
|
|
|
|
groupHomepageSkip: string;
|
|
|
|
|
groupContentLabel: string;
|
|
|
|
|
groupContentDescription: string;
|
|
|
|
|
groupContentPurpose: string;
|
|
|
|
|
groupContentWhere: string;
|
|
|
|
|
groupContentSkip: string;
|
|
|
|
|
groupLanguagesLabel: string;
|
|
|
|
|
groupLanguagesDescription: string;
|
|
|
|
|
groupLanguagesPurpose: string;
|
|
|
|
|
groupLanguagesWhere: string;
|
|
|
|
|
groupLanguagesSkip: string;
|
|
|
|
|
groupFeaturesLabel: string;
|
|
|
|
|
groupFeaturesDescription: string;
|
|
|
|
|
groupFeaturesPurpose: string;
|
|
|
|
|
groupFeaturesWhere: string;
|
|
|
|
|
groupFeaturesSkip: string;
|
|
|
|
|
groupNavigationLabel: string;
|
|
|
|
|
groupNavigationDescription: string;
|
|
|
|
|
groupNavigationPurpose: string;
|
|
|
|
|
groupNavigationWhere: string;
|
|
|
|
|
groupNavigationSkip: string;
|
|
|
|
|
groupPreviewLabel: string;
|
|
|
|
|
groupPreviewDescription: string;
|
|
|
|
|
groupPreviewPurpose: string;
|
|
|
|
|
groupPreviewWhere: string;
|
|
|
|
|
groupPreviewSkip: string;
|
feat(builder): implement professional brand management experience
New BrandOverviewComponent, mounted at the top of the existing
Branding section page (no route/schema/business-logic changes):
- Completion ring + recommended next step + last-modified timestamp,
computed from real facade signals (branding.logoUrl/faviconUrl/
socialImageUrl, theme.palette, theme.typography, lastSavedAt) -
nothing fabricated.
- Brand Health checklist (6 items: logo, favicon, colors, typography,
social image, accessibility) - icon+text, never color alone.
- Color palette preview: 10 swatches from the real ThemePaletteConfig
(primary/secondary/accent/success/warning/danger/backgrounds/text/
border), each with a readable label.
- Real WCAG contrast check (contrast.util.ts, relative-luminance
formula) between configured text and background colors - shows an
inline warning when below 4.5:1, never blocks publishing.
- Typography live preview using the configured heading/body font
families and base size.
- Social share preview card (Open Graph-style) using the existing
socialImageUrl + seo.default.title/description, with an explicit
empty state when no image is set yet.
Scope cut from the full brief given this session's cost already well
over budget entering this sprint - see Known limitations in the final
report (no multi-variant logo/favicon management, no live responsive
device preview, no new media library/asset picker; all reuse the
existing single-logo/single-favicon fields and image-field upload
component as-is).
Could not verify live in-browser this sprint: port 4200 is held by
another chat's dev server. Verified via `tsc --noEmit` (clean) and
manual template/binding review only; one binding (`--pct` custom-
property style binding) was replaced with a plain computed string to
remove an unverifiable risk rather than ship it unverified.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 10:13:35 +04:00
|
|
|
brandOverviewTitle: string;
|
|
|
|
|
brandOverviewSubtitle: string;
|
|
|
|
|
brandOverviewComplete: string;
|
|
|
|
|
brandLastModified: string;
|
|
|
|
|
brandFaviconUploaded: string;
|
|
|
|
|
brandColorsSelected: string;
|
|
|
|
|
brandTypographyConfigured: string;
|
|
|
|
|
brandSocialImageUploaded: string;
|
|
|
|
|
brandAccessibilityPassed: string;
|
|
|
|
|
brandColorsPreviewTitle: string;
|
|
|
|
|
brandTypographyPreviewTitle: string;
|
|
|
|
|
brandSocialPreviewTitle: string;
|
|
|
|
|
brandTypeSampleHeading: string;
|
|
|
|
|
brandTypeSampleBody: string;
|
|
|
|
|
brandContrastWarning: string;
|
|
|
|
|
brandSocialEmpty: string;
|
|
|
|
|
brandAccentColor: string;
|
|
|
|
|
brandBorderColor: string;
|
feat(builder): implement visual homepage builder
New HomepageOverviewComponent (Tasks 1+8), mounted at the top of the
existing Homepage section page: completion ring, active/hidden section
counts, recommended next step, last-modified timestamp, and a 6-item
Homepage Health checklist (hero/categories/products/promotion/
newsletter/any-sections) - all derived from the real page.sections/
widgets data already in the facade, nothing fabricated.
Existing page-level drag-and-drop reordering (homepage-section's
CdkDragDrop over page.sections) was already implemented pre-Sprint 5 -
left as-is per "build on top of, do not replace."
Widgets section (Task 2) rewritten from a bare list into visual cards:
per-widget icon + humanized type label (hero/categories/product-
collection), a visible/hidden toggle and badge, up/down move buttons
(keyboard-accessible reorder within a section - see Known limitations
for why this replaces pointer drag for widgets specifically), duplicate,
and remove (with confirm). The existing per-type field editors (hero/
categories/product-collection) are unchanged; the raw-JSON fallback for
unknown widget types now sits behind a collapsed "Advanced settings"
disclosure instead of being the default view.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 10:24:13 +04:00
|
|
|
homepageOverviewTitle: string;
|
|
|
|
|
homepageOverviewSubtitle: string;
|
|
|
|
|
homepageOverviewComplete: string;
|
|
|
|
|
homepageActiveSections: string;
|
|
|
|
|
homepageDisabledSections: string;
|
|
|
|
|
homepageHealthHero: string;
|
|
|
|
|
homepageHealthCategories: string;
|
|
|
|
|
homepageHealthProducts: string;
|
|
|
|
|
homepageHealthPromotion: string;
|
|
|
|
|
homepageHealthNewsletter: string;
|
|
|
|
|
homepageHealthAnySections: string;
|
|
|
|
|
widgetTypeHero: string;
|
|
|
|
|
widgetTypeCategories: string;
|
|
|
|
|
widgetTypeProducts: string;
|
|
|
|
|
widgetHidden: string;
|
|
|
|
|
widgetVisible: string;
|
|
|
|
|
widgetMoveUp: string;
|
|
|
|
|
widgetMoveDown: string;
|
|
|
|
|
widgetDuplicate: string;
|
|
|
|
|
widgetRemove: string;
|
|
|
|
|
widgetRemoveConfirm: string;
|
|
|
|
|
widgetAdvancedSettings: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
};
|
2026-07-14 12:21:33 +04:00
|
|
|
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;
|
2026-07-15 20:25:12 +04:00
|
|
|
actionUsers: string;
|
|
|
|
|
actionMonitoring: string;
|
|
|
|
|
actionAnalytics: string;
|
2026-07-14 12:21:33 +04:00
|
|
|
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;
|
feat(admin): redesign dashboard into professional SaaS homepage
Replaced the placeholder dashboard with a real business homepage built
on 6 new reusable widget components (DashboardSection, DashboardCard,
DashboardMetric, DashboardStatusRow, DashboardTimeline,
DashboardShortcutCard), all wired through shared app-card/app-skeleton/
app-empty-state.
Sections: Welcome (tenant, env badge, current user, last publish/save -
all real facade signals, never blank), Quick Actions (large cards: add
product, create category, open builder, edit homepage, media, orders),
Draft Status (real dirty/modifiedFields/publish/discard from
ProjectEditorFacade), Marketplace Health (9 checks - config, product/
category counts, missing translations, draft, static-pages-unpublished,
homepage/theme configured, all real; images-without-alt shown as
'not tracked yet' rather than fabricated), Recent Activity (existing
localStorage-backed history service, loading/empty states), Useful
Shortcuts, Documentation (honest coming-soon list, no dead links).
12-column responsive grid: 3-across cards on desktop, 2-column on
tablet, single column on mobile, no horizontal scroll. Keyboard/focus/
ARIA per shortcut card and status row; health status never conveyed by
color alone (icon + text every time).
AdminDashboardHealthCheck (boolean 'healthy' shape) and its facade
signal are untouched - AdminMonitoringPageComponent also consumes them.
New richer status data lives in a separate homeHealthChecks signal/
AdminDashboardHomeHealthCheck type instead of widening the shared one.
Added ~50 new dashboard.* / kept existing i18n strings across ru/en/hy;
no raw keys.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 08:59:29 +04:00
|
|
|
welcomeEyebrow: string;
|
|
|
|
|
welcomeFallbackTenant: string;
|
|
|
|
|
envProduction: string;
|
|
|
|
|
envDevelopment: string;
|
|
|
|
|
factCurrentUser: string;
|
|
|
|
|
factTenant: string;
|
|
|
|
|
quickActionsDescription: string;
|
|
|
|
|
actionAddProduct: string;
|
|
|
|
|
actionAddProductDescription: string;
|
|
|
|
|
actionCreateCategory: string;
|
|
|
|
|
actionCreateCategoryDescription: string;
|
|
|
|
|
actionEditProjectDescription: string;
|
|
|
|
|
actionEditHomepage: string;
|
|
|
|
|
actionEditHomepageDescription: string;
|
|
|
|
|
actionMediaLibraryDescription: string;
|
|
|
|
|
actionOrdersDescription: string;
|
|
|
|
|
draftStatusTitle: string;
|
|
|
|
|
draftStatusHasChanges: string;
|
|
|
|
|
draftStatusAllPublished: string;
|
|
|
|
|
draftStatusMoreFields: string;
|
|
|
|
|
draftOpenBuilder: string;
|
|
|
|
|
draftPublish: string;
|
|
|
|
|
draftDiscard: string;
|
|
|
|
|
healthProductsCount: string;
|
|
|
|
|
healthCategoriesCount: string;
|
|
|
|
|
healthDraftExists: string;
|
|
|
|
|
healthImagesWithoutAlt: string;
|
|
|
|
|
healthStaticPagesUnpublished: string;
|
|
|
|
|
healthHomepageConfigured: string;
|
|
|
|
|
healthThemeConfigured: string;
|
|
|
|
|
statusHealthy: string;
|
|
|
|
|
statusAttention: string;
|
|
|
|
|
statusUnhealthy: string;
|
|
|
|
|
statusUnknown: string;
|
|
|
|
|
activityEmptyHint: string;
|
|
|
|
|
activityUnavailable: string;
|
|
|
|
|
activityUnavailableHint: string;
|
|
|
|
|
activityErrorHint: string;
|
|
|
|
|
shortcutsTitle: string;
|
|
|
|
|
shortcutsDescription: string;
|
|
|
|
|
shortcutMarketplaceBuilder: string;
|
|
|
|
|
shortcutSettings: string;
|
|
|
|
|
shortcutContent: string;
|
|
|
|
|
docTitle: string;
|
|
|
|
|
docDocumentation: string;
|
|
|
|
|
docKeyboardShortcuts: string;
|
|
|
|
|
docReportIssue: string;
|
|
|
|
|
docReleaseNotes: string;
|
2026-07-14 12:21:33 +04:00
|
|
|
};
|
2026-07-10 13:52:01 +04:00
|
|
|
staticPages: {
|
|
|
|
|
notFound: string;
|
|
|
|
|
backHome: string;
|
|
|
|
|
duplicateSlug: string;
|
|
|
|
|
emptyTitle: string;
|
feat(static-pages): CRUD completion + search/filter/bulk actions
Milestone 2 of the Static Pages Module sprint.
- StaticPagesEditorComponent: duplicate page, confirm-before-delete/bulk-
delete (matches the resetDraft confirm pattern), route/enabled/customTemplate/
media(hero/thumbnail/gallery) fields wired into the card, per-page publish/
unpublish action, status + duplicate-route/invalid-html/invalid-seo badges.
- Search (id/slug/route/title across all locales), filter by status
(draft/published) and by locale (hides pages missing a translation for the
selected locale) - all local computed() filters, no new service.
- Bulk selection (per-row + select-all-visible checkboxes) with bulk delete/
enable/disable/publish/unpublish, one updateBootstrap() call each.
- Correctness note: introduced `allPages` (unfiltered) vs `pages` (filtered
view) computeds. Every mutation (create/duplicate/delete/move/bulk) reads
from allPages(), never the filtered pages() - reading from the filtered
view would have silently deleted whatever an active search/filter hid on
the next persist(). Documented inline on persist() as a guardrail for
future edits.
- Fixed a template compile error found by the build gate: Angular templates
don't support inline arrow functions in binding expressions
((ngModelChange)="...map(v => v.trim())..." failed to parse) - moved the
gallery CSV-parsing into a component method (updateGallery).
- SEO robots field added to the page card (validated against a known-token
set from M1).
- i18n: staticPages.* extended (search/filter/bulk/route/enabled/status/
media/robots/disabled labels) across the interface + en/ru/hy.
Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:55:21 +04:00
|
|
|
duplicateRoute: string;
|
|
|
|
|
invalidHtml: string;
|
|
|
|
|
invalidSeo: string;
|
|
|
|
|
searchPlaceholder: string;
|
|
|
|
|
filterAll: string;
|
|
|
|
|
filterAllLocales: string;
|
|
|
|
|
confirmDeletePage: string;
|
|
|
|
|
confirmBulkDelete: string;
|
|
|
|
|
duplicatePage: string;
|
|
|
|
|
selectAll: string;
|
|
|
|
|
selectedCount: string;
|
|
|
|
|
bulkDelete: string;
|
|
|
|
|
bulkEnable: string;
|
|
|
|
|
bulkDisable: string;
|
|
|
|
|
bulkPublishAction: string;
|
|
|
|
|
bulkUnpublish: string;
|
|
|
|
|
routeLabel: string;
|
|
|
|
|
enabledLabel: string;
|
|
|
|
|
statusLabel: string;
|
|
|
|
|
publishPageAction: string;
|
|
|
|
|
unpublishPageAction: string;
|
|
|
|
|
heroImageLabel: string;
|
|
|
|
|
thumbnailLabel: string;
|
|
|
|
|
galleryLabel: string;
|
|
|
|
|
customTemplateLabel: string;
|
|
|
|
|
seoRobotsLabel: string;
|
|
|
|
|
disabledBadge: string;
|
2026-07-17 10:11:31 +04:00
|
|
|
previewDesktop: string;
|
|
|
|
|
previewTablet: string;
|
|
|
|
|
previewMobile: string;
|
|
|
|
|
previewToggle: string;
|
2026-07-10 13:52:01 +04:00
|
|
|
};
|
2026-07-18 11:02:26 +04:00
|
|
|
contentManagement: {
|
|
|
|
|
publishedPages: string;
|
|
|
|
|
draftPages: string;
|
|
|
|
|
missingLegalPages: string;
|
|
|
|
|
seoHealth: string;
|
|
|
|
|
lastEdited: string;
|
|
|
|
|
lastEditedNever: string;
|
|
|
|
|
completionLabel: string;
|
|
|
|
|
recommendedNext: string;
|
|
|
|
|
recommendAddTitles: string;
|
|
|
|
|
recommendFixSeo: string;
|
|
|
|
|
recommendPublish: string;
|
|
|
|
|
recommendNone: string;
|
|
|
|
|
openAction: string;
|
|
|
|
|
backToPages: string;
|
|
|
|
|
groupContent: string;
|
|
|
|
|
groupSeo: string;
|
|
|
|
|
groupSharing: string;
|
|
|
|
|
groupAdvanced: string;
|
|
|
|
|
groupAdvancedHtml: string;
|
|
|
|
|
seoExplainDesc: string;
|
|
|
|
|
seoTitleHelp: string;
|
|
|
|
|
seoDescriptionHelp: string;
|
|
|
|
|
seoCanonicalHelp: string;
|
|
|
|
|
seoRobotsHelp: string;
|
|
|
|
|
sharingExplain: string;
|
|
|
|
|
mediaSelect: string;
|
|
|
|
|
mediaReplace: string;
|
|
|
|
|
mediaRemove: string;
|
|
|
|
|
mediaAlt: string;
|
|
|
|
|
mediaCaption: string;
|
|
|
|
|
emptyStateGuideTitle: string;
|
|
|
|
|
emptyStateGuideBody: string;
|
|
|
|
|
seoStatusGood: string;
|
|
|
|
|
seoStatusPartial: string;
|
|
|
|
|
seoStatusWarning: string;
|
|
|
|
|
seoStatusMissing: string;
|
|
|
|
|
needsAttention: string;
|
|
|
|
|
legal: {
|
|
|
|
|
about: { label: string; why: string };
|
|
|
|
|
contacts: { label: string; why: string };
|
|
|
|
|
privacy: { label: string; why: string };
|
|
|
|
|
terms: { label: string; why: string };
|
|
|
|
|
cookies: { label: string; why: string };
|
|
|
|
|
delivery: { label: string; why: string };
|
|
|
|
|
returns: { label: string; why: string };
|
|
|
|
|
faq: { label: string; why: string };
|
|
|
|
|
};
|
|
|
|
|
};
|
2026-07-09 02:29:12 +04:00
|
|
|
widgets: {
|
|
|
|
|
unavailable: string;
|
|
|
|
|
};
|
2026-02-26 23:09:20 +04:00
|
|
|
carousel: {
|
|
|
|
|
loading: string;
|
|
|
|
|
addToCart: string;
|
|
|
|
|
};
|
|
|
|
|
common: {
|
|
|
|
|
retry: string;
|
|
|
|
|
loading: string;
|
2026-07-17 15:22:45 +04:00
|
|
|
remove: string;
|
2026-02-26 23:09:20 +04:00
|
|
|
};
|
2026-02-28 17:18:24 +04:00
|
|
|
location: {
|
|
|
|
|
allRegions: string;
|
|
|
|
|
chooseRegion: string;
|
|
|
|
|
detectAuto: string;
|
|
|
|
|
};
|
|
|
|
|
auth: {
|
|
|
|
|
loginRequired: string;
|
|
|
|
|
loginDescription: string;
|
|
|
|
|
checking: string;
|
|
|
|
|
loginWithTelegram: string;
|
|
|
|
|
orScanQr: string;
|
|
|
|
|
loginNote: string;
|
2026-03-25 15:32:50 +04:00
|
|
|
qrExpired: string;
|
2026-06-01 00:47:26 +04:00
|
|
|
qrError: string;
|
2026-02-28 17:18:24 +04:00
|
|
|
};
|
2026-07-09 01:13:54 +04:00
|
|
|
ux: {
|
|
|
|
|
items: string;
|
|
|
|
|
wishlistTitle: string;
|
|
|
|
|
wishlistEmptyTitle: string;
|
|
|
|
|
wishlistEmptyDescription: string;
|
|
|
|
|
clearWishlist: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
wishlistCleared: string;
|
2026-07-09 01:13:54 +04:00
|
|
|
compareTitle: string;
|
|
|
|
|
compareEmptyTitle: string;
|
|
|
|
|
compareEmptyDescription: string;
|
|
|
|
|
clearCompare: string;
|
|
|
|
|
hideIdentical: string;
|
|
|
|
|
highlightDifferences: string;
|
|
|
|
|
clearRecentlyViewed: string;
|
|
|
|
|
recentlyViewedEmpty: string;
|
|
|
|
|
saveSearch: string;
|
|
|
|
|
goToCatalog: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
wishlistAdded: string;
|
|
|
|
|
wishlistRemoved: string;
|
|
|
|
|
compareAdded: string;
|
|
|
|
|
compareRemoved: string;
|
|
|
|
|
compareLimitReached: string;
|
|
|
|
|
productShared: string;
|
|
|
|
|
productLinkCopied: string;
|
|
|
|
|
sharingUnsupported: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
addedToCart: string;
|
2026-07-09 01:40:22 +04:00
|
|
|
enterQueryBeforeSave: string;
|
|
|
|
|
savedSearchNamed: string;
|
2026-07-09 02:29:12 +04:00
|
|
|
recentlyViewedTitle: string;
|
|
|
|
|
compareAttribute: string;
|
|
|
|
|
comparePrice: string;
|
|
|
|
|
compareRating: string;
|
|
|
|
|
compareStock: string;
|
|
|
|
|
compareDiscount: string;
|
|
|
|
|
compareColor: string;
|
|
|
|
|
compareSize: string;
|
|
|
|
|
compareUnknown: string;
|
2026-07-09 01:13:54 +04:00
|
|
|
};
|
2026-07-10 13:34:25 +04:00
|
|
|
diagnostics: {
|
|
|
|
|
title: string;
|
|
|
|
|
disabledTitle: string;
|
|
|
|
|
disabledDescription: string;
|
|
|
|
|
generatedAt: string;
|
|
|
|
|
healthScore: string;
|
|
|
|
|
summary: string;
|
|
|
|
|
passedChecks: string;
|
|
|
|
|
warnings: string;
|
|
|
|
|
errors: string;
|
|
|
|
|
critical: string;
|
|
|
|
|
healthChecks: string;
|
|
|
|
|
results: string;
|
|
|
|
|
noIssues: string;
|
|
|
|
|
ok: string;
|
|
|
|
|
failed: string;
|
|
|
|
|
affectedComponent: string;
|
|
|
|
|
suggestedResolution: string;
|
|
|
|
|
severity: {
|
|
|
|
|
info: string;
|
|
|
|
|
warning: string;
|
|
|
|
|
error: string;
|
|
|
|
|
critical: string;
|
|
|
|
|
};
|
|
|
|
|
checks: {
|
|
|
|
|
bootstrapLoaded: string;
|
|
|
|
|
tenantResolved: string;
|
|
|
|
|
runtimeInitialized: string;
|
|
|
|
|
themeLoaded: string;
|
|
|
|
|
widgetManifestLoaded: string;
|
|
|
|
|
sectionEngineInitialized: string;
|
|
|
|
|
configurationEngineInitialized: string;
|
|
|
|
|
translationResourcesAvailable: string;
|
|
|
|
|
requiredAssetsAvailable: string;
|
|
|
|
|
};
|
|
|
|
|
bootstrapMissingTitle: string;
|
|
|
|
|
bootstrapMissingDescription: string;
|
|
|
|
|
bootstrapMissingResolution: string;
|
|
|
|
|
requiredPropertyMissingTitle: string;
|
|
|
|
|
requiredPropertyMissingDescription: string;
|
|
|
|
|
requiredPropertyMissingResolution: string;
|
|
|
|
|
unknownWidgetTypeTitle: string;
|
|
|
|
|
unknownWidgetTypeDescription: string;
|
|
|
|
|
unknownWidgetTypeResolution: string;
|
|
|
|
|
duplicateIdTitle: string;
|
|
|
|
|
duplicatePageIdDescription: string;
|
|
|
|
|
duplicateSectionIdDescription: string;
|
|
|
|
|
duplicateWidgetIdDescription: string;
|
|
|
|
|
duplicateIdResolution: string;
|
|
|
|
|
unknownLayoutTitle: string;
|
|
|
|
|
unknownLayoutDescription: string;
|
|
|
|
|
unknownLayoutResolution: string;
|
|
|
|
|
invalidFeatureFlagTitle: string;
|
|
|
|
|
invalidFeatureFlagDescription: string;
|
|
|
|
|
invalidFeatureFlagResolution: string;
|
|
|
|
|
brokenPageDefinitionTitle: string;
|
|
|
|
|
brokenPageDefinitionDescription: string;
|
|
|
|
|
brokenPageDefinitionResolution: string;
|
|
|
|
|
invalidNavigationTargetTitle: string;
|
|
|
|
|
invalidNavigationTargetDescription: string;
|
|
|
|
|
invalidNavigationTargetResolution: string;
|
|
|
|
|
missingTranslationTitle: string;
|
|
|
|
|
missingTranslationDescription: string;
|
|
|
|
|
missingTranslationResolution: string;
|
|
|
|
|
missingMediaTitle: string;
|
|
|
|
|
missingMediaDescription: string;
|
|
|
|
|
missingMediaResolution: string;
|
|
|
|
|
runtimeNotInitializedTitle: string;
|
|
|
|
|
runtimeNotInitializedDescription: string;
|
|
|
|
|
runtimeNotInitializedResolution: string;
|
|
|
|
|
configFallbackUsageTitle: string;
|
|
|
|
|
configFallbackUsageDescription: string;
|
|
|
|
|
configFallbackUsageResolution: string;
|
|
|
|
|
missingDatasourceTitle: string;
|
|
|
|
|
missingDatasourceDescription: string;
|
|
|
|
|
missingDatasourceResolution: string;
|
|
|
|
|
widgetRenderFailureTitle: string;
|
|
|
|
|
widgetRenderFailureDescription: string;
|
|
|
|
|
widgetRenderFailureResolution: string;
|
|
|
|
|
brokenRouteTitle: string;
|
|
|
|
|
brokenRouteDescription: string;
|
|
|
|
|
brokenRouteResolution: string;
|
|
|
|
|
imageLoadFailedTitle: string;
|
|
|
|
|
imageLoadFailedDescription: string;
|
|
|
|
|
imageLoadFailedResolution: string;
|
|
|
|
|
optionalDataMissingTitle: string;
|
|
|
|
|
optionalDataMissingDescription: string;
|
|
|
|
|
optionalDataMissingResolution: string;
|
|
|
|
|
};
|
feat(admin): implement review and moderation center
New Reviews & Moderation feature (frontend only): moderation dashboard (real pending/approved/rejected/reported/spam counts, average rating, recent activity, moderation-health%, computed from the full review queue); reviews list with table/cards, density, saved column visibility, search/status/rating filters, bulk approve/reject/spam/hide/export; review detail shows customer/product/rating/text/photos/timeline/moderator-notes with a real moderation workflow (approve/reject/spam/hide/restore/pin/feature, feature disabled with an explanation unless the review is approved); reusable ReviewHealthWidget (rating/text/media/moderated/report-status/visible + completion%); reports queue lists reports against products/reviews/customers/categories with resolve/dismiss, and honestly renders 'Not available yet' rather than fabricating a value wherever a target can't be resolved (e.g. photos, customer-target reports). Backed by a new in-memory AdminModerationLocalGateway seeded from real product data - the same mock-gateway pattern already used by every other admin feature in this app (orders/products/categories), since no review/report backend exists to reuse. Replaced the 'Reviews' comingSoon nav placeholder with a working link; added full adminModeration i18n coverage.
2026-07-18 22:01:16 +04:00
|
|
|
adminModeration: {
|
|
|
|
|
search: string;
|
|
|
|
|
allStatuses: string;
|
|
|
|
|
allRatings: string;
|
|
|
|
|
rating: string;
|
|
|
|
|
customer: string;
|
|
|
|
|
product: string;
|
|
|
|
|
reviewText: string;
|
|
|
|
|
photos: string;
|
|
|
|
|
workflow: string;
|
|
|
|
|
approve: string;
|
|
|
|
|
reject: string;
|
|
|
|
|
markSpam: string;
|
|
|
|
|
hide: string;
|
|
|
|
|
restore: string;
|
|
|
|
|
pin: string;
|
|
|
|
|
unpin: string;
|
|
|
|
|
feature: string;
|
|
|
|
|
unfeature: string;
|
|
|
|
|
featureDisabledHint: string;
|
|
|
|
|
moderatorNotes: string;
|
|
|
|
|
notAvailable: string;
|
|
|
|
|
changeStatus: string;
|
|
|
|
|
hideSelected: string;
|
|
|
|
|
archiveSelected: string;
|
|
|
|
|
reportsQueue: string;
|
|
|
|
|
reportsColumn: string;
|
|
|
|
|
reportTarget: string;
|
|
|
|
|
reportType: string;
|
|
|
|
|
reportReason: string;
|
|
|
|
|
resolve: string;
|
|
|
|
|
dismiss: string;
|
|
|
|
|
reportsEmptyTitle: string;
|
|
|
|
|
reportsEmptyDescription: string;
|
|
|
|
|
reportedCount: string;
|
|
|
|
|
averageRating: string;
|
|
|
|
|
moderationHealth: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
|
|
|
|
emptyGuide: string;
|
|
|
|
|
healthRating: string;
|
|
|
|
|
healthText: string;
|
|
|
|
|
healthMedia: string;
|
|
|
|
|
healthModeration: string;
|
|
|
|
|
healthReportStatus: string;
|
|
|
|
|
healthVisibility: string;
|
|
|
|
|
health: string;
|
|
|
|
|
column_product: string;
|
|
|
|
|
column_rating: string;
|
|
|
|
|
column_status: string;
|
|
|
|
|
column_reports: string;
|
|
|
|
|
column_created: string;
|
|
|
|
|
status: {
|
|
|
|
|
pending: string;
|
|
|
|
|
approved: string;
|
|
|
|
|
rejected: string;
|
|
|
|
|
spam: string;
|
|
|
|
|
};
|
|
|
|
|
targetType: {
|
|
|
|
|
product: string;
|
|
|
|
|
review: string;
|
|
|
|
|
customer: string;
|
|
|
|
|
category: string;
|
|
|
|
|
unknown: string;
|
|
|
|
|
};
|
|
|
|
|
reportStatus: {
|
|
|
|
|
open: string;
|
|
|
|
|
resolved: string;
|
|
|
|
|
dismissed: string;
|
|
|
|
|
};
|
|
|
|
|
};
|
2026-07-18 21:47:54 +04:00
|
|
|
adminOrders: {
|
|
|
|
|
back: string;
|
|
|
|
|
search: string;
|
|
|
|
|
export: string;
|
|
|
|
|
view: string;
|
|
|
|
|
orderNumber: string;
|
|
|
|
|
customer: string;
|
|
|
|
|
payment: string;
|
|
|
|
|
delivery: string;
|
|
|
|
|
trackingNumber: string;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
items: string;
|
|
|
|
|
timeline: string;
|
|
|
|
|
notes: string;
|
|
|
|
|
internalNotes: string;
|
|
|
|
|
addNote: string;
|
|
|
|
|
changeStatus: string;
|
|
|
|
|
applyStatus: string;
|
|
|
|
|
archiveSelected: string;
|
|
|
|
|
printSelected: string;
|
|
|
|
|
printInvoice: string;
|
|
|
|
|
requestRefund: string;
|
|
|
|
|
cancelOrder: string;
|
|
|
|
|
confirmCancel: string;
|
|
|
|
|
confirmRefund: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
|
|
|
|
column_customer: string;
|
|
|
|
|
column_status: string;
|
|
|
|
|
column_payment: string;
|
|
|
|
|
column_total: string;
|
|
|
|
|
column_created: string;
|
|
|
|
|
status: {
|
|
|
|
|
pending: string;
|
|
|
|
|
processing: string;
|
|
|
|
|
shipped: string;
|
|
|
|
|
delivered: string;
|
|
|
|
|
cancelled: string;
|
|
|
|
|
refunded: string;
|
|
|
|
|
};
|
|
|
|
|
paymentStatus: {
|
|
|
|
|
unpaid: string;
|
|
|
|
|
paid: string;
|
|
|
|
|
refund_requested: string;
|
|
|
|
|
refunded: string;
|
|
|
|
|
};
|
|
|
|
|
ordersToday: string;
|
|
|
|
|
paidCount: string;
|
|
|
|
|
customersCount: string;
|
|
|
|
|
returningCustomers: string;
|
|
|
|
|
averageOrder: string;
|
|
|
|
|
recentActivity: string;
|
|
|
|
|
noRecentActivity: string;
|
|
|
|
|
alertStuckPending: string;
|
|
|
|
|
alertRefundRequested: string;
|
|
|
|
|
};
|
|
|
|
|
adminCustomers: {
|
|
|
|
|
search: string;
|
|
|
|
|
name: string;
|
|
|
|
|
email: string;
|
|
|
|
|
phone: string;
|
|
|
|
|
orderCount: string;
|
|
|
|
|
lifetimeValue: string;
|
|
|
|
|
returning: string;
|
|
|
|
|
profile: string;
|
|
|
|
|
statistics: string;
|
|
|
|
|
firstOrder: string;
|
|
|
|
|
lastOrder: string;
|
|
|
|
|
addresses: string;
|
|
|
|
|
notes: string;
|
|
|
|
|
notesUnavailable: string;
|
|
|
|
|
orders: string;
|
|
|
|
|
activity: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
|
|
|
|
};
|
feat(media): add Media Manager UI (grid, upload, delete)
MediaLibraryPageComponent replaces the coming-soon placeholder at
/backoffice/media. Built entirely on Sprint 6 Design System primitives
(app-card, app-button, app-input, app-empty-state, app-dialog, app-pagination,
app-skeleton) and Sprint 4 Task 2's MediaRepository/MockMediaRepository.
- Grid view with per-tile filename/size and delete action
- Hidden native file input triggered by an app-button, uploads via
MediaLibraryFacade -> MediaRepository.upload()
- Delete requires confirmation through app-dialog (destructive action)
- Search + pagination wired to MockMediaRepository's list() params
- Loading state shows app-skeleton tiles; empty state shows app-empty-state
- New mediaLibrary.* translation namespace across en/ru/hy
Verified in browser (via a temporary unguarded route, reverted before
commit - /backoffice/media itself requires Telegram QR admin auth not
available in this session): empty state renders correctly with translated
copy, search input and upload button present, no console errors.
2026-07-15 07:14:16 +04:00
|
|
|
mediaLibrary: {
|
|
|
|
|
title: string;
|
|
|
|
|
upload: string;
|
|
|
|
|
uploading: string;
|
|
|
|
|
searchPlaceholder: string;
|
|
|
|
|
delete: string;
|
|
|
|
|
deleteConfirmTitle: string;
|
|
|
|
|
deleteConfirmBody: string;
|
|
|
|
|
cancel: string;
|
|
|
|
|
confirm: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
feat(builder): implement reusable media library
Media dashboard (real total/images/SVG/logos/unused/storage/alt-coverage, computed from actual assets + a bootstrap usage scan, no fabricated stats); gallery gets grid/list toggle, type filter, sort, drag-and-drop + multi-file upload with cancel/retry and friendly error mapping, lazy thumbnails, multi-select with bulk delete/download/export-metadata; asset details drawer shows real dimensions/size/format/date/usage locations (walks bootstrap config for exact URL matches, reports not-used rather than guessing) plus editable alt text/caption/description/decorative flag with missing-alt warning; shared MediaPickerComponent (already the one reusable picker used by content management) gains type filter, a recent shortcut, and keyboard grid navigation.
2026-07-18 16:09:47 +04:00
|
|
|
allFolders: string;
|
|
|
|
|
newFolder: string;
|
|
|
|
|
editTags: string;
|
|
|
|
|
totalAssets: string;
|
|
|
|
|
logos: string;
|
|
|
|
|
unused: string;
|
|
|
|
|
storageUsed: string;
|
|
|
|
|
altCoverage: string;
|
|
|
|
|
allTypes: string;
|
|
|
|
|
typeImage: string;
|
|
|
|
|
typeSvg: string;
|
|
|
|
|
typePdf: string;
|
|
|
|
|
sortRecent: string;
|
|
|
|
|
sortName: string;
|
|
|
|
|
sortSize: string;
|
|
|
|
|
viewMode: string;
|
|
|
|
|
viewGrid: string;
|
|
|
|
|
viewList: string;
|
|
|
|
|
dropHint: string;
|
|
|
|
|
cancelUpload: string;
|
|
|
|
|
retry: string;
|
|
|
|
|
errorTooLarge: string;
|
|
|
|
|
errorUnsupportedType: string;
|
|
|
|
|
errorGeneric: string;
|
|
|
|
|
selectedCount: string;
|
|
|
|
|
download: string;
|
|
|
|
|
exportMetadata: string;
|
|
|
|
|
bulkDelete: string;
|
|
|
|
|
bulkDeleteConfirm: string;
|
|
|
|
|
clearSelection: string;
|
|
|
|
|
guideCopy: string;
|
|
|
|
|
missingAlt: string;
|
|
|
|
|
usageCount: string;
|
|
|
|
|
detailsTitle: string;
|
|
|
|
|
filename: string;
|
|
|
|
|
format: string;
|
|
|
|
|
fileSize: string;
|
|
|
|
|
dimensions: string;
|
|
|
|
|
uploadDate: string;
|
|
|
|
|
lastUsed: string;
|
|
|
|
|
usageCountLabel: string;
|
|
|
|
|
usedIn: string;
|
|
|
|
|
notUsed: string;
|
|
|
|
|
unknownValue: string;
|
|
|
|
|
altText: string;
|
|
|
|
|
altTextHelp: string;
|
|
|
|
|
decorative: string;
|
|
|
|
|
missingAltWarning: string;
|
|
|
|
|
caption: string;
|
|
|
|
|
description: string;
|
|
|
|
|
usage: {
|
|
|
|
|
brand: string;
|
|
|
|
|
theme: string;
|
|
|
|
|
header: string;
|
|
|
|
|
footer: string;
|
|
|
|
|
staticPage: string;
|
|
|
|
|
homepage: string;
|
|
|
|
|
catalog: string;
|
|
|
|
|
productPage: string;
|
|
|
|
|
widget: string;
|
|
|
|
|
company: string;
|
|
|
|
|
seo: string;
|
|
|
|
|
navigation: string;
|
|
|
|
|
other: string;
|
|
|
|
|
};
|
feat(media): add Media Manager UI (grid, upload, delete)
MediaLibraryPageComponent replaces the coming-soon placeholder at
/backoffice/media. Built entirely on Sprint 6 Design System primitives
(app-card, app-button, app-input, app-empty-state, app-dialog, app-pagination,
app-skeleton) and Sprint 4 Task 2's MediaRepository/MockMediaRepository.
- Grid view with per-tile filename/size and delete action
- Hidden native file input triggered by an app-button, uploads via
MediaLibraryFacade -> MediaRepository.upload()
- Delete requires confirmation through app-dialog (destructive action)
- Search + pagination wired to MockMediaRepository's list() params
- Loading state shows app-skeleton tiles; empty state shows app-empty-state
- New mediaLibrary.* translation namespace across en/ru/hy
Verified in browser (via a temporary unguarded route, reverted before
commit - /backoffice/media itself requires Telegram QR admin auth not
available in this session): empty state renders correctly with translated
copy, search input and upload button present, no console errors.
2026-07-15 07:14:16 +04:00
|
|
|
};
|
2026-07-16 00:19:41 +04:00
|
|
|
// Sprint 28: only the new empty-state copy this sprint's skeleton/empty-
|
|
|
|
|
// state consistency fix introduces - NOT the full adminProducts/
|
|
|
|
|
// adminUsers/adminMonitoring/adminAnalytics translation surface, which is
|
|
|
|
|
// still missing entirely (see docs/KNOWN-ISSUES.md, deferred to Sprint 29).
|
fix(static-pages): backfill enabled/status in export, fix missing i18n key
Independent review pass over the Static Pages Module sprint (M1-M7),
including live browser E2E per the task's own verification checklist.
- ProjectEditorFacade.normalize() now backfills enabled/status on
record-format static pages (defaulting missing values to enabled+published,
same rule ContentPageService.normalizePage applies for display - mirrored
rather than imported, to avoid a project-editor <-> content-management
circular dependency since ContentManagementFacade already depends on this
facade). Found live: exporting a page that predates this sprint and was
never touched/re-saved in the current session produced JSON missing
enabled/status entirely - the editor UI and storefront resolver both
normalize-on-read so nothing was actually broken live, but Export/Import
fidelity should match what the editor shows. Verified fixed live (export
now includes "enabled":true,"status":"published" for an untouched legacy
page) and via the full gate.
- Added the missing adminCategories.chooseImage i18n key (interface +
en/ru/hy). Found live: the media-picker "choose image" button rendered as
the literal string "adminCategories.chooseImage" - a pre-existing,
repo-wide bug (5 templates reference this key; none of the locale files
ever defined it) that I propagated into a 3rd/4th/5th... well, 2 new
occurrences by copying the existing branding-section/footer-section
pattern into static-pages-editor. Fixed the actual defect (missing
translation) rather than renaming the key, which would have required
touching 2 unrelated admin components outside this sprint's scope.
Live-verified this pass: Static Pages editor renders with all new fields;
create page works (page count 4->confirmed); device preview toggles
desktop/tablet/mobile widths correctly; navigation "Insert page link"
creates a real type:'staticPage' nav item end-to-end (confirmed in the
exported JSON); export includes all Sprint X+2 fields after the fix; no
console errors throughout.
Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:27:29 +04:00
|
|
|
// Shared across branding/footer/static-pages/admin-product/admin-category media
|
|
|
|
|
// pickers - despite the namespace name, this is a generic "choose image" label,
|
|
|
|
|
// not category-specific. Renaming is a separate cleanup outside this sprint's
|
|
|
|
|
// scope; the key was previously referenced by 5 templates but never defined
|
|
|
|
|
// anywhere, so it rendered as the literal string "adminCategories.chooseImage".
|
|
|
|
|
adminCategories: {
|
|
|
|
|
chooseImage: string;
|
2026-07-17 15:22:45 +04:00
|
|
|
replaceImage: string;
|
2026-07-18 17:04:11 +04:00
|
|
|
search: string;
|
|
|
|
|
showDeleted: string;
|
|
|
|
|
create: string;
|
|
|
|
|
edit: string;
|
|
|
|
|
title: string;
|
|
|
|
|
slug: string;
|
|
|
|
|
slugTaken: string;
|
|
|
|
|
parent: string;
|
|
|
|
|
noParent: string;
|
|
|
|
|
icon: string;
|
|
|
|
|
image: string;
|
|
|
|
|
imageHint: string;
|
|
|
|
|
description: string;
|
|
|
|
|
items: string;
|
|
|
|
|
status: { draft: string; published: string };
|
|
|
|
|
restore: string;
|
|
|
|
|
saveDraft: string;
|
|
|
|
|
publish: string;
|
|
|
|
|
confirmDelete: string;
|
|
|
|
|
confirmLeaveUnsaved: string;
|
|
|
|
|
deleteBlocked: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
|
|
|
|
emptyGuide: string;
|
|
|
|
|
viewTree: string;
|
|
|
|
|
viewCards: string;
|
|
|
|
|
expandAll: string;
|
|
|
|
|
collapseAll: string;
|
|
|
|
|
expandNode: string;
|
|
|
|
|
collapseNode: string;
|
|
|
|
|
jumpToParent: string;
|
|
|
|
|
deletedBadge: string;
|
|
|
|
|
column_slug: string;
|
|
|
|
|
column_items: string;
|
|
|
|
|
column_status: string;
|
|
|
|
|
column_visibility: string;
|
|
|
|
|
column_updated: string;
|
|
|
|
|
bulkAssignParent: string;
|
|
|
|
|
bulkAssignImage: string;
|
|
|
|
|
groupGeneral: string;
|
|
|
|
|
groupMedia: string;
|
|
|
|
|
groupSeo: string;
|
|
|
|
|
groupVisibility: string;
|
|
|
|
|
groupNavigation: string;
|
|
|
|
|
groupAttributes: string;
|
|
|
|
|
groupAdvanced: string;
|
|
|
|
|
visibilityExplain: string;
|
|
|
|
|
navBreadcrumb: string;
|
2026-07-19 08:35:18 +04:00
|
|
|
navBreadcrumbHint: string;
|
|
|
|
|
iconHint: string;
|
2026-07-18 17:04:11 +04:00
|
|
|
navRoot: string;
|
|
|
|
|
navChildren: string;
|
|
|
|
|
navNoChildren: string;
|
|
|
|
|
navAppearsIn: string;
|
|
|
|
|
navAppearsVisible: string;
|
|
|
|
|
navAppearsHidden: string;
|
|
|
|
|
itemsCountLabel: string;
|
|
|
|
|
createdAtLabel: string;
|
|
|
|
|
healthImage: string;
|
|
|
|
|
healthSeo: string;
|
|
|
|
|
healthDescription: string;
|
|
|
|
|
healthParent: string;
|
|
|
|
|
healthVisibility: string;
|
|
|
|
|
healthProducts: string;
|
|
|
|
|
totalCategories: string;
|
|
|
|
|
visibleCount: string;
|
|
|
|
|
hiddenCount: string;
|
|
|
|
|
emptyCount: string;
|
|
|
|
|
rootCount: string;
|
|
|
|
|
subCount: string;
|
|
|
|
|
missingImagesCount: string;
|
|
|
|
|
missingSeoCount: string;
|
|
|
|
|
lastModified: string;
|
|
|
|
|
lastModifiedNever: string;
|
|
|
|
|
completionLabel: string;
|
|
|
|
|
recommendedNext: string;
|
|
|
|
|
recommendAddImage: string;
|
|
|
|
|
recommendAddSeo: string;
|
|
|
|
|
recommendFillEmpty: string;
|
|
|
|
|
recommendNone: string;
|
fix(static-pages): backfill enabled/status in export, fix missing i18n key
Independent review pass over the Static Pages Module sprint (M1-M7),
including live browser E2E per the task's own verification checklist.
- ProjectEditorFacade.normalize() now backfills enabled/status on
record-format static pages (defaulting missing values to enabled+published,
same rule ContentPageService.normalizePage applies for display - mirrored
rather than imported, to avoid a project-editor <-> content-management
circular dependency since ContentManagementFacade already depends on this
facade). Found live: exporting a page that predates this sprint and was
never touched/re-saved in the current session produced JSON missing
enabled/status entirely - the editor UI and storefront resolver both
normalize-on-read so nothing was actually broken live, but Export/Import
fidelity should match what the editor shows. Verified fixed live (export
now includes "enabled":true,"status":"published" for an untouched legacy
page) and via the full gate.
- Added the missing adminCategories.chooseImage i18n key (interface +
en/ru/hy). Found live: the media-picker "choose image" button rendered as
the literal string "adminCategories.chooseImage" - a pre-existing,
repo-wide bug (5 templates reference this key; none of the locale files
ever defined it) that I propagated into a 3rd/4th/5th... well, 2 new
occurrences by copying the existing branding-section/footer-section
pattern into static-pages-editor. Fixed the actual defect (missing
translation) rather than renaming the key, which would have required
touching 2 unrelated admin components outside this sprint's scope.
Live-verified this pass: Static Pages editor renders with all new fields;
create page works (page count 4->confirmed); device preview toggles
desktop/tablet/mobile widths correctly; navigation "Insert page link"
creates a real type:'staticPage' nav item end-to-end (confirmed in the
exported JSON); export includes all Sprint X+2 fields after the fix; no
console errors throughout.
Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:27:29 +04:00
|
|
|
};
|
2026-07-16 00:19:41 +04:00
|
|
|
adminProducts: {
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
feat(products): implement professional product management experience
Products dashboard (real total/published/drafts/out-of-stock/hidden/missing-images/missing-SEO/low-quality counts, recently-edited list, recommended next action); list gains table/grid view toggle, density, saved column visibility and saved sort (persisted via LocalStorageService), plus real bulk assign-category/assign-tags/duplicate/CSV-export alongside existing publish/hide/delete; per-row and per-editor reusable ProductHealthWidget (images/SEO/price/category/description/inventory checklist + completion %); editor reorganized into General/Media/Pricing/Inventory/Categories/Attributes/SEO/Visibility/Advanced tabs, media now uses the shared MediaPickerComponent/ImageFieldComponent (primary image + reorderable gallery) instead of raw URL textareas, specifications/attributes/variants moved off pipe-delimited textareas onto the shared KeyValueEditorComponent, SEO tab explains fields in plain language with a live search-result preview, inventory relabeled in business language, toggles/badges use the shared Toggle/Badge components. Filled in the adminProducts i18n namespace (previously ~98% missing, rendering raw translation keys) across en/ru/hy.
2026-07-18 16:34:06 +04:00
|
|
|
emptyGuide: string;
|
|
|
|
|
search: string;
|
|
|
|
|
category: string;
|
|
|
|
|
allCategories: string;
|
|
|
|
|
visibility: string;
|
|
|
|
|
allVisibility: string;
|
|
|
|
|
visible: string;
|
|
|
|
|
hidden: string;
|
|
|
|
|
stockStatus: string;
|
|
|
|
|
allStock: string;
|
|
|
|
|
inStock: string;
|
|
|
|
|
lowStock: string;
|
|
|
|
|
outOfStock: string;
|
|
|
|
|
sortTitle: string;
|
|
|
|
|
sortPrice: string;
|
|
|
|
|
sortPriority: string;
|
|
|
|
|
sortStock: string;
|
|
|
|
|
sortUpdated: string;
|
|
|
|
|
showArchived: string;
|
|
|
|
|
infiniteScroll: string;
|
|
|
|
|
viewMode: string;
|
|
|
|
|
viewTable: string;
|
|
|
|
|
viewGrid: string;
|
|
|
|
|
density: string;
|
|
|
|
|
densityComfortable: string;
|
|
|
|
|
densityCompact: string;
|
|
|
|
|
columns: string;
|
|
|
|
|
column_sku: string;
|
|
|
|
|
column_brand: string;
|
|
|
|
|
column_price: string;
|
|
|
|
|
column_stock: string;
|
|
|
|
|
column_visibility: string;
|
|
|
|
|
column_updated: string;
|
|
|
|
|
create: string;
|
|
|
|
|
edit: string;
|
|
|
|
|
duplicate: string;
|
|
|
|
|
selectedCount: string;
|
|
|
|
|
bulkShow: string;
|
|
|
|
|
bulkHide: string;
|
|
|
|
|
bulkAssignCategory: string;
|
|
|
|
|
bulkAssignTags: string;
|
|
|
|
|
bulkDuplicateAction: string;
|
|
|
|
|
bulkExportAction: string;
|
|
|
|
|
bulkDelete: string;
|
|
|
|
|
tagsPlaceholder: string;
|
|
|
|
|
name: string;
|
|
|
|
|
brand: string;
|
|
|
|
|
healthColumn: string;
|
|
|
|
|
actions: string;
|
|
|
|
|
archive: string;
|
|
|
|
|
delete: string;
|
|
|
|
|
items: string;
|
|
|
|
|
loadMore: string;
|
|
|
|
|
noImage: string;
|
|
|
|
|
slug: string;
|
|
|
|
|
barcode: string;
|
|
|
|
|
priority: string;
|
|
|
|
|
shortDescription: string;
|
|
|
|
|
translations: string;
|
|
|
|
|
primaryImage: string;
|
|
|
|
|
primaryImageHint: string;
|
|
|
|
|
gallery: string;
|
|
|
|
|
galleryHint: string;
|
|
|
|
|
moveLeft: string;
|
|
|
|
|
moveRight: string;
|
|
|
|
|
videos: string;
|
|
|
|
|
discount: string;
|
|
|
|
|
currency: string;
|
|
|
|
|
stockQuantity: string;
|
|
|
|
|
stockQuantityHint: string;
|
|
|
|
|
availabilityStatus: string;
|
|
|
|
|
availabilityStatusHint: string;
|
|
|
|
|
availabilityNote: string;
|
|
|
|
|
availabilityNoteHint: string;
|
|
|
|
|
reservedNote: string;
|
|
|
|
|
relatedProducts: string;
|
|
|
|
|
specifications: string;
|
|
|
|
|
attributes: string;
|
|
|
|
|
addRow: string;
|
|
|
|
|
removeRow: string;
|
|
|
|
|
rowKey: string;
|
|
|
|
|
rowValue: string;
|
|
|
|
|
addVariant: string;
|
|
|
|
|
variantName: string;
|
|
|
|
|
variantsHint: string;
|
2026-07-18 22:44:39 +04:00
|
|
|
variants: string;
|
|
|
|
|
archived: string;
|
feat(admin): product editor auto-slug, SKU helper, one-click SEO fill
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
P0 user feedback: admins did not know what SKU means, had to hand-write slugs, and left SEO empty.
- Name edits now derive the URL slug automatically (supports Cyrillic/Armenian characters); the derivation stops the moment the slug no longer matches the auto value, so a manually edited slug is never overwritten - verified in browser
- SEO title mirrors the name under the same only-while-untouched rule
- SKU field explains itself (what a stock keeping unit is, that any unique text works) and gains a Generate button producing readable codes like WIR-GAM-7K2P from the product name
- SEO tab gains 'Fill from product details': fills only empty title/description/keywords from name and short description, existing text untouched
- New adminProducts keys (slugHint, skuHint, generate, seoGenerate, seoGenerateHint) in en/ru/hy
2026-07-19 08:30:49 +04:00
|
|
|
slugHint: string;
|
|
|
|
|
skuHint: string;
|
|
|
|
|
generate: string;
|
|
|
|
|
seoGenerate: string;
|
|
|
|
|
seoGenerateHint: string;
|
2026-07-19 08:40:30 +04:00
|
|
|
translationsHint: string;
|
2026-07-19 08:44:20 +04:00
|
|
|
badgesHint: string;
|
|
|
|
|
addBadge: string;
|
|
|
|
|
removeBadge: string;
|
|
|
|
|
customBadgePlaceholder: string;
|
feat(products): implement professional product management experience
Products dashboard (real total/published/drafts/out-of-stock/hidden/missing-images/missing-SEO/low-quality counts, recently-edited list, recommended next action); list gains table/grid view toggle, density, saved column visibility and saved sort (persisted via LocalStorageService), plus real bulk assign-category/assign-tags/duplicate/CSV-export alongside existing publish/hide/delete; per-row and per-editor reusable ProductHealthWidget (images/SEO/price/category/description/inventory checklist + completion %); editor reorganized into General/Media/Pricing/Inventory/Categories/Attributes/SEO/Visibility/Advanced tabs, media now uses the shared MediaPickerComponent/ImageFieldComponent (primary image + reorderable gallery) instead of raw URL textareas, specifications/attributes/variants moved off pipe-delimited textareas onto the shared KeyValueEditorComponent, SEO tab explains fields in plain language with a live search-result preview, inventory relabeled in business language, toggles/badges use the shared Toggle/Badge components. Filled in the adminProducts i18n namespace (previously ~98% missing, rendering raw translation keys) across en/ru/hy.
2026-07-18 16:34:06 +04:00
|
|
|
seoExplain: string;
|
|
|
|
|
searchTitle: string;
|
|
|
|
|
searchTitleHint: string;
|
|
|
|
|
searchDescription: string;
|
|
|
|
|
searchDescriptionHint: string;
|
|
|
|
|
seoMissingTitle: string;
|
|
|
|
|
seoMissingDescription: string;
|
|
|
|
|
keywords: string;
|
|
|
|
|
featured: string;
|
|
|
|
|
recommended: string;
|
|
|
|
|
new: string;
|
|
|
|
|
bestseller: string;
|
|
|
|
|
badges: string;
|
|
|
|
|
htmlDescription: string;
|
|
|
|
|
customer: string;
|
|
|
|
|
reviewsReadonly: string;
|
|
|
|
|
noReviews: string;
|
|
|
|
|
questionsReadonly: string;
|
|
|
|
|
noQuestions: string;
|
|
|
|
|
save: string;
|
|
|
|
|
groupGeneral: string;
|
|
|
|
|
groupMedia: string;
|
|
|
|
|
groupPricing: string;
|
|
|
|
|
groupInventory: string;
|
|
|
|
|
groupCategories: string;
|
|
|
|
|
groupAttributes: string;
|
|
|
|
|
groupSeo: string;
|
|
|
|
|
groupVisibility: string;
|
|
|
|
|
groupAdvanced: string;
|
|
|
|
|
healthImages: string;
|
|
|
|
|
healthSeo: string;
|
|
|
|
|
healthPrice: string;
|
|
|
|
|
healthCategory: string;
|
|
|
|
|
healthDescription: string;
|
|
|
|
|
healthInventory: string;
|
|
|
|
|
totalProducts: string;
|
|
|
|
|
publishedCount: string;
|
|
|
|
|
draftsCount: string;
|
|
|
|
|
outOfStockCount: string;
|
|
|
|
|
hiddenCount: string;
|
|
|
|
|
missingImagesCount: string;
|
|
|
|
|
missingSeoCount: string;
|
|
|
|
|
lowQualityCount: string;
|
|
|
|
|
recentlyEdited: string;
|
|
|
|
|
recommendedNext: string;
|
|
|
|
|
openAction: string;
|
|
|
|
|
recommendAddImages: string;
|
|
|
|
|
recommendAddSeo: string;
|
|
|
|
|
recommendRestock: string;
|
|
|
|
|
recommendNone: string;
|
2026-07-16 00:19:41 +04:00
|
|
|
};
|
|
|
|
|
adminUsers: {
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
|
|
|
|
invitationsEmptyTitle: string;
|
2026-07-18 22:44:39 +04:00
|
|
|
title: string;
|
|
|
|
|
name: string;
|
|
|
|
|
email: string;
|
|
|
|
|
scope: string;
|
|
|
|
|
role: string;
|
|
|
|
|
roles: string;
|
|
|
|
|
lastLogin: string;
|
|
|
|
|
sessions: string;
|
|
|
|
|
currentSession: string;
|
|
|
|
|
invite: string;
|
|
|
|
|
sendInvite: string;
|
|
|
|
|
revoke: string;
|
|
|
|
|
passwordlessHint: string;
|
|
|
|
|
confirmSuspend: string;
|
|
|
|
|
scopeValue: {
|
|
|
|
|
marketplace: string;
|
|
|
|
|
office: string;
|
|
|
|
|
};
|
|
|
|
|
statusValue: {
|
|
|
|
|
active: string;
|
|
|
|
|
invited: string;
|
|
|
|
|
suspended: string;
|
|
|
|
|
};
|
|
|
|
|
invitationStatus: {
|
|
|
|
|
pending: string;
|
|
|
|
|
accepted: string;
|
|
|
|
|
expired: string;
|
|
|
|
|
revoked: string;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
adminTransactions: {
|
|
|
|
|
search: string;
|
|
|
|
|
type: string;
|
|
|
|
|
method: string;
|
|
|
|
|
fraud: string;
|
|
|
|
|
flagged: string;
|
2026-07-19 08:25:29 +04:00
|
|
|
flag: string;
|
|
|
|
|
clearFlag: string;
|
2026-07-18 22:44:39 +04:00
|
|
|
retry: string;
|
|
|
|
|
audit: string;
|
|
|
|
|
emptyTitle: string;
|
|
|
|
|
emptyDescription: string;
|
|
|
|
|
status: {
|
|
|
|
|
all: string;
|
|
|
|
|
pending: string;
|
|
|
|
|
success: string;
|
|
|
|
|
failed: string;
|
|
|
|
|
retried: string;
|
|
|
|
|
};
|
|
|
|
|
typeValue: {
|
|
|
|
|
all: string;
|
|
|
|
|
payment: string;
|
|
|
|
|
refund: string;
|
|
|
|
|
qr_payment: string;
|
|
|
|
|
};
|
2026-07-16 00:19:41 +04:00
|
|
|
};
|
|
|
|
|
adminMonitoring: {
|
|
|
|
|
eventsEmptyTitle: string;
|
|
|
|
|
eventsEmptyDescription: string;
|
2026-07-18 22:44:39 +04:00
|
|
|
health: string;
|
|
|
|
|
queues: string;
|
|
|
|
|
depth: string;
|
|
|
|
|
webhooks: string;
|
|
|
|
|
endpoint: string;
|
|
|
|
|
event: string;
|
|
|
|
|
events: string;
|
|
|
|
|
category: string;
|
|
|
|
|
level: string;
|
|
|
|
|
message: string;
|
|
|
|
|
categoryValue: {
|
|
|
|
|
all: string;
|
|
|
|
|
audit: string;
|
|
|
|
|
security: string;
|
|
|
|
|
login: string;
|
|
|
|
|
failed_login: string;
|
|
|
|
|
api: string;
|
|
|
|
|
error: string;
|
|
|
|
|
warning: string;
|
|
|
|
|
};
|
|
|
|
|
queueStatus: {
|
|
|
|
|
healthy: string;
|
|
|
|
|
degraded: string;
|
|
|
|
|
down: string;
|
|
|
|
|
};
|
|
|
|
|
webhookStatus: {
|
|
|
|
|
delivered: string;
|
|
|
|
|
failed: string;
|
|
|
|
|
pending: string;
|
|
|
|
|
};
|
|
|
|
|
levelValue: {
|
|
|
|
|
info: string;
|
|
|
|
|
warning: string;
|
|
|
|
|
error: string;
|
|
|
|
|
};
|
feat(admin): implement analytics and monitoring center
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Business Dashboard: revenue/orders/customers/AOV/top products/low stock/recent activity/warnings/completion% (Overview tab)
- Marketplace Health: real completion checks (images, SEO, categories, reviews, orders, translations, homepage, backend, performance) with clickable recommendations
- Product Analytics: top selling/most reviewed/worst rated/hidden/archived counts; top-viewed honestly marked Unknown (no view tracking exists)
- Customer Analytics: new/returning customers, average spend, retention - derived from real order data
- Search & Traffic tabs: honest 'Unknown - available after backend integration' placeholders, no fabricated numbers
- Recommendations engine: actionable cards (missing images/SEO/category, empty categories, incomplete homepage, unpublished static pages) linking to the relevant admin page
- System Monitoring: added real draft/publish/sync/backend-connectivity state from AdminDashboardFacade
- CSV export extended to top products and health checks; added print action
- New adminAnalytics.* and adminMarketplaceHealth.* i18n namespaces (en/ru/hy), no raw i18n keys
- Reused existing shared UI (app-table/app-badge/app-skeleton/app-empty-state) and content-health-widget completion-meter pattern - no new backend APIs, no storage changes
2026-07-18 22:33:36 +04:00
|
|
|
systemState: string;
|
|
|
|
|
backendConnectivity: string;
|
|
|
|
|
connected: string;
|
|
|
|
|
loading: string;
|
|
|
|
|
draftState: string;
|
|
|
|
|
unsavedChanges: string;
|
|
|
|
|
noDraft: string;
|
|
|
|
|
lastSaved: string;
|
|
|
|
|
lastPublished: string;
|
|
|
|
|
syncStatus: string;
|
2026-07-16 00:19:41 +04:00
|
|
|
};
|
|
|
|
|
adminAnalytics: {
|
|
|
|
|
topProductsEmptyTitle: string;
|
|
|
|
|
topProductsEmptyDescription: string;
|
2026-07-18 22:44:39 +04:00
|
|
|
topProducts: string;
|
feat(admin): implement analytics and monitoring center
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Business Dashboard: revenue/orders/customers/AOV/top products/low stock/recent activity/warnings/completion% (Overview tab)
- Marketplace Health: real completion checks (images, SEO, categories, reviews, orders, translations, homepage, backend, performance) with clickable recommendations
- Product Analytics: top selling/most reviewed/worst rated/hidden/archived counts; top-viewed honestly marked Unknown (no view tracking exists)
- Customer Analytics: new/returning customers, average spend, retention - derived from real order data
- Search & Traffic tabs: honest 'Unknown - available after backend integration' placeholders, no fabricated numbers
- Recommendations engine: actionable cards (missing images/SEO/category, empty categories, incomplete homepage, unpublished static pages) linking to the relevant admin page
- System Monitoring: added real draft/publish/sync/backend-connectivity state from AdminDashboardFacade
- CSV export extended to top products and health checks; added print action
- New adminAnalytics.* and adminMarketplaceHealth.* i18n namespaces (en/ru/hy), no raw i18n keys
- Reused existing shared UI (app-table/app-badge/app-skeleton/app-empty-state) and content-health-widget completion-meter pattern - no new backend APIs, no storage changes
2026-07-18 22:33:36 +04:00
|
|
|
print: string;
|
|
|
|
|
tabsLabel: string;
|
|
|
|
|
tab: {
|
|
|
|
|
overview: string;
|
|
|
|
|
health: string;
|
|
|
|
|
products: string;
|
|
|
|
|
customers: string;
|
|
|
|
|
search: string;
|
|
|
|
|
traffic: string;
|
|
|
|
|
recommendations: string;
|
|
|
|
|
};
|
|
|
|
|
revenue: string;
|
|
|
|
|
orders: string;
|
|
|
|
|
customers: string;
|
|
|
|
|
avgOrderValue: string;
|
|
|
|
|
conversion: string;
|
|
|
|
|
completionPercent: string;
|
|
|
|
|
unknown: string;
|
|
|
|
|
salesChart: string;
|
|
|
|
|
quantitySold: string;
|
|
|
|
|
lowStock: string;
|
|
|
|
|
lowStockEmptyTitle: string;
|
|
|
|
|
quantity: string;
|
|
|
|
|
status: string;
|
|
|
|
|
recentActivity: string;
|
|
|
|
|
recentActivityEmptyTitle: string;
|
|
|
|
|
activityDraftSaved: string;
|
|
|
|
|
activityPublished: string;
|
|
|
|
|
warnings: string;
|
|
|
|
|
topViewed: string;
|
|
|
|
|
hiddenProducts: string;
|
|
|
|
|
archivedProducts: string;
|
|
|
|
|
mostReviewed: string;
|
|
|
|
|
worstRated: string;
|
|
|
|
|
noReviewedProducts: string;
|
|
|
|
|
reviewCount: string;
|
|
|
|
|
avgRating: string;
|
|
|
|
|
newCustomers: string;
|
|
|
|
|
returningCustomers: string;
|
|
|
|
|
averageSpend: string;
|
|
|
|
|
retention: string;
|
|
|
|
|
searchAnalytics: string;
|
|
|
|
|
trafficAnalytics: string;
|
|
|
|
|
availableAfterBackend: string;
|
|
|
|
|
pendingBackend: string;
|
|
|
|
|
pendingBackendHint: string;
|
|
|
|
|
funnelsHeatmaps: string;
|
|
|
|
|
visitors: string;
|
|
|
|
|
products: string;
|
|
|
|
|
categories: string;
|
|
|
|
|
};
|
|
|
|
|
adminMarketplaceHealth: {
|
|
|
|
|
title: string;
|
|
|
|
|
complete: string;
|
|
|
|
|
recommendationsTitle: string;
|
|
|
|
|
recommendNone: string;
|
|
|
|
|
checkImages: string;
|
|
|
|
|
checkSeo: string;
|
|
|
|
|
checkCategories: string;
|
|
|
|
|
checkReviews: string;
|
|
|
|
|
checkOrders: string;
|
|
|
|
|
checkTranslations: string;
|
|
|
|
|
checkHomepage: string;
|
|
|
|
|
checkBackend: string;
|
|
|
|
|
checkPerformance: string;
|
|
|
|
|
recommendImages: string;
|
|
|
|
|
recommendImagesCount: string;
|
|
|
|
|
recommendSeo: string;
|
|
|
|
|
recommendSeoCount: string;
|
|
|
|
|
recommendCategory: string;
|
|
|
|
|
recommendCategoryCount: string;
|
|
|
|
|
recommendEmptyCategories: string;
|
|
|
|
|
recommendEmptyCategoriesCount: string;
|
|
|
|
|
recommendHomepage: string;
|
|
|
|
|
recommendStaticPages: string;
|
|
|
|
|
severity: {
|
|
|
|
|
info: string;
|
|
|
|
|
warning: string;
|
|
|
|
|
critical: string;
|
|
|
|
|
};
|
2026-07-16 00:19:41 +04:00
|
|
|
};
|
feat(admin): build shared admin shell
Sidebar (Dashboard/Catalog group/Products/Categories/Orders/Transactions/
Reviews/Reports/Content/Media/Marketplace Builder/Users/Settings/
Monitoring/Analytics + Documentation/Help/Logout), sticky topbar
(breadcrumbs, page title/description, search, notifications, tenant
selector and quick-publish placeholders, current user), reserved
right-rail slot, scrollable content area. Desktop 280px sidebar, tablet
icon rail, mobile drawer with focus management and Escape-to-close.
Nav items without a built page (Reviews, Reports, Settings, Docs, Help)
render disabled with a coming-soon badge instead of dead links; Content
and Marketplace Builder route to the existing project-editor pages
(static-pages / general) rather than duplicating them.
All 15 /backoffice/** routes now render through AdminLayoutComponent;
the public storefront header/back-button/footer no longer render on
admin routes (app.ts/app.html gate on a new isAdminRoute signal).
Added the adminShell i18n namespace (ru/en/hy) for every new shell
string so this doesn't add to the existing untranslated-admin-UI gap
tracked in KNOWN-ISSUES.md.
Colors/type sizes follow DESIGN.md tokens; the two rgba() modal-scrim
values are a documented, intentional exception (neutral overlay,
not a themed token).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 00:12:19 +04:00
|
|
|
adminShell: {
|
|
|
|
|
skipToContent: string;
|
|
|
|
|
openSidebar: string;
|
|
|
|
|
closeSidebar: string;
|
|
|
|
|
collapseSidebar: string;
|
|
|
|
|
expandSidebar: string;
|
|
|
|
|
nav: {
|
|
|
|
|
dashboard: string;
|
|
|
|
|
catalogGroup: string;
|
|
|
|
|
products: string;
|
|
|
|
|
categories: string;
|
|
|
|
|
orders: string;
|
2026-07-18 21:47:54 +04:00
|
|
|
customers: string;
|
feat(admin): build shared admin shell
Sidebar (Dashboard/Catalog group/Products/Categories/Orders/Transactions/
Reviews/Reports/Content/Media/Marketplace Builder/Users/Settings/
Monitoring/Analytics + Documentation/Help/Logout), sticky topbar
(breadcrumbs, page title/description, search, notifications, tenant
selector and quick-publish placeholders, current user), reserved
right-rail slot, scrollable content area. Desktop 280px sidebar, tablet
icon rail, mobile drawer with focus management and Escape-to-close.
Nav items without a built page (Reviews, Reports, Settings, Docs, Help)
render disabled with a coming-soon badge instead of dead links; Content
and Marketplace Builder route to the existing project-editor pages
(static-pages / general) rather than duplicating them.
All 15 /backoffice/** routes now render through AdminLayoutComponent;
the public storefront header/back-button/footer no longer render on
admin routes (app.ts/app.html gate on a new isAdminRoute signal).
Added the adminShell i18n namespace (ru/en/hy) for every new shell
string so this doesn't add to the existing untranslated-admin-UI gap
tracked in KNOWN-ISSUES.md.
Colors/type sizes follow DESIGN.md tokens; the two rgba() modal-scrim
values are a documented, intentional exception (neutral overlay,
not a themed token).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 00:12:19 +04:00
|
|
|
transactions: string;
|
|
|
|
|
reviews: string;
|
feat(admin): implement review and moderation center
New Reviews & Moderation feature (frontend only): moderation dashboard (real pending/approved/rejected/reported/spam counts, average rating, recent activity, moderation-health%, computed from the full review queue); reviews list with table/cards, density, saved column visibility, search/status/rating filters, bulk approve/reject/spam/hide/export; review detail shows customer/product/rating/text/photos/timeline/moderator-notes with a real moderation workflow (approve/reject/spam/hide/restore/pin/feature, feature disabled with an explanation unless the review is approved); reusable ReviewHealthWidget (rating/text/media/moderated/report-status/visible + completion%); reports queue lists reports against products/reviews/customers/categories with resolve/dismiss, and honestly renders 'Not available yet' rather than fabricating a value wherever a target can't be resolved (e.g. photos, customer-target reports). Backed by a new in-memory AdminModerationLocalGateway seeded from real product data - the same mock-gateway pattern already used by every other admin feature in this app (orders/products/categories), since no review/report backend exists to reuse. Replaced the 'Reviews' comingSoon nav placeholder with a working link; added full adminModeration i18n coverage.
2026-07-18 22:01:16 +04:00
|
|
|
moderation: string;
|
feat(admin): build shared admin shell
Sidebar (Dashboard/Catalog group/Products/Categories/Orders/Transactions/
Reviews/Reports/Content/Media/Marketplace Builder/Users/Settings/
Monitoring/Analytics + Documentation/Help/Logout), sticky topbar
(breadcrumbs, page title/description, search, notifications, tenant
selector and quick-publish placeholders, current user), reserved
right-rail slot, scrollable content area. Desktop 280px sidebar, tablet
icon rail, mobile drawer with focus management and Escape-to-close.
Nav items without a built page (Reviews, Reports, Settings, Docs, Help)
render disabled with a coming-soon badge instead of dead links; Content
and Marketplace Builder route to the existing project-editor pages
(static-pages / general) rather than duplicating them.
All 15 /backoffice/** routes now render through AdminLayoutComponent;
the public storefront header/back-button/footer no longer render on
admin routes (app.ts/app.html gate on a new isAdminRoute signal).
Added the adminShell i18n namespace (ru/en/hy) for every new shell
string so this doesn't add to the existing untranslated-admin-UI gap
tracked in KNOWN-ISSUES.md.
Colors/type sizes follow DESIGN.md tokens; the two rgba() modal-scrim
values are a documented, intentional exception (neutral overlay,
not a themed token).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 00:12:19 +04:00
|
|
|
reports: string;
|
|
|
|
|
content: string;
|
|
|
|
|
mediaLibrary: string;
|
|
|
|
|
marketplaceBuilder: string;
|
|
|
|
|
users: string;
|
|
|
|
|
settings: string;
|
|
|
|
|
monitoring: string;
|
|
|
|
|
analytics: string;
|
|
|
|
|
documentation: string;
|
|
|
|
|
help: string;
|
|
|
|
|
logout: string;
|
|
|
|
|
comingSoonBadge: string;
|
|
|
|
|
};
|
|
|
|
|
topbar: {
|
|
|
|
|
searchLabel: string;
|
|
|
|
|
searchPlaceholder: string;
|
|
|
|
|
notifications: string;
|
|
|
|
|
notificationsEmpty: string;
|
|
|
|
|
account: string;
|
|
|
|
|
tenantSelector: string;
|
|
|
|
|
tenantSelectorComingSoon: string;
|
|
|
|
|
quickPublish: string;
|
|
|
|
|
quickPublishComingSoon: string;
|
|
|
|
|
};
|
|
|
|
|
breadcrumbHome: string;
|
|
|
|
|
pages: {
|
|
|
|
|
dashboard: { title: string; description: string };
|
|
|
|
|
products: { title: string; description: string };
|
|
|
|
|
productCreate: { title: string; description: string };
|
|
|
|
|
productEdit: { title: string; description: string };
|
|
|
|
|
productDuplicate: { title: string; description: string };
|
|
|
|
|
categories: { title: string; description: string };
|
|
|
|
|
categoryCreate: { title: string; description: string };
|
|
|
|
|
categoryEdit: { title: string; description: string };
|
|
|
|
|
orders: { title: string; description: string };
|
|
|
|
|
orderDetail: { title: string; description: string };
|
2026-07-18 21:47:54 +04:00
|
|
|
customers: { title: string; description: string };
|
|
|
|
|
customerDetail: { title: string; description: string };
|
feat(admin): implement review and moderation center
New Reviews & Moderation feature (frontend only): moderation dashboard (real pending/approved/rejected/reported/spam counts, average rating, recent activity, moderation-health%, computed from the full review queue); reviews list with table/cards, density, saved column visibility, search/status/rating filters, bulk approve/reject/spam/hide/export; review detail shows customer/product/rating/text/photos/timeline/moderator-notes with a real moderation workflow (approve/reject/spam/hide/restore/pin/feature, feature disabled with an explanation unless the review is approved); reusable ReviewHealthWidget (rating/text/media/moderated/report-status/visible + completion%); reports queue lists reports against products/reviews/customers/categories with resolve/dismiss, and honestly renders 'Not available yet' rather than fabricating a value wherever a target can't be resolved (e.g. photos, customer-target reports). Backed by a new in-memory AdminModerationLocalGateway seeded from real product data - the same mock-gateway pattern already used by every other admin feature in this app (orders/products/categories), since no review/report backend exists to reuse. Replaced the 'Reviews' comingSoon nav placeholder with a working link; added full adminModeration i18n coverage.
2026-07-18 22:01:16 +04:00
|
|
|
moderation: { title: string; description: string };
|
|
|
|
|
reviewDetail: { title: string; description: string };
|
|
|
|
|
reportsQueue: { title: string; description: string };
|
feat(admin): build shared admin shell
Sidebar (Dashboard/Catalog group/Products/Categories/Orders/Transactions/
Reviews/Reports/Content/Media/Marketplace Builder/Users/Settings/
Monitoring/Analytics + Documentation/Help/Logout), sticky topbar
(breadcrumbs, page title/description, search, notifications, tenant
selector and quick-publish placeholders, current user), reserved
right-rail slot, scrollable content area. Desktop 280px sidebar, tablet
icon rail, mobile drawer with focus management and Escape-to-close.
Nav items without a built page (Reviews, Reports, Settings, Docs, Help)
render disabled with a coming-soon badge instead of dead links; Content
and Marketplace Builder route to the existing project-editor pages
(static-pages / general) rather than duplicating them.
All 15 /backoffice/** routes now render through AdminLayoutComponent;
the public storefront header/back-button/footer no longer render on
admin routes (app.ts/app.html gate on a new isAdminRoute signal).
Added the adminShell i18n namespace (ru/en/hy) for every new shell
string so this doesn't add to the existing untranslated-admin-UI gap
tracked in KNOWN-ISSUES.md.
Colors/type sizes follow DESIGN.md tokens; the two rgba() modal-scrim
values are a documented, intentional exception (neutral overlay,
not a themed token).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 00:12:19 +04:00
|
|
|
transactions: { title: string; description: string };
|
|
|
|
|
media: { title: string; description: string };
|
|
|
|
|
users: { title: string; description: string };
|
|
|
|
|
monitoring: { title: string; description: string };
|
|
|
|
|
analytics: { title: string; description: string };
|
|
|
|
|
};
|
|
|
|
|
};
|
2026-02-26 23:09:20 +04:00
|
|
|
}
|