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.
This commit is contained in:
@@ -1140,6 +1140,77 @@ export const en: Translations = {
|
||||
optionalDataMissingDescription: 'Optional configuration data is absent.',
|
||||
optionalDataMissingResolution: 'Add optional value if UX depends on it.',
|
||||
},
|
||||
adminModeration: {
|
||||
search: 'Search by customer, product, or review text…',
|
||||
allStatuses: 'All statuses',
|
||||
allRatings: 'All ratings',
|
||||
rating: 'Rating',
|
||||
customer: 'Customer',
|
||||
product: 'Product',
|
||||
reviewText: 'Review',
|
||||
photos: 'Photos',
|
||||
workflow: 'Actions',
|
||||
approve: 'Approve',
|
||||
reject: 'Reject',
|
||||
markSpam: 'Mark as spam',
|
||||
hide: 'Hide',
|
||||
restore: 'Restore',
|
||||
pin: 'Pin',
|
||||
unpin: 'Unpin',
|
||||
feature: 'Feature',
|
||||
unfeature: 'Unfeature',
|
||||
featureDisabledHint: 'Only approved reviews can be featured.',
|
||||
moderatorNotes: 'Moderator notes',
|
||||
notAvailable: 'Not available yet',
|
||||
changeStatus: 'Change status',
|
||||
hideSelected: 'Hide',
|
||||
archiveSelected: 'Archive',
|
||||
reportsQueue: 'Reports queue',
|
||||
reportsColumn: 'Reports',
|
||||
reportTarget: 'Reported item',
|
||||
reportType: 'Type',
|
||||
reportReason: 'Reason',
|
||||
resolve: 'Resolve',
|
||||
dismiss: 'Dismiss',
|
||||
reportsEmptyTitle: 'No reports',
|
||||
reportsEmptyDescription: 'Customer reports about products, reviews, or accounts will show up here.',
|
||||
reportedCount: 'Reported',
|
||||
averageRating: 'Average rating',
|
||||
moderationHealth: 'Moderation health',
|
||||
emptyTitle: 'No reviews yet',
|
||||
emptyDescription: 'Reviews will appear here once customers start leaving feedback.',
|
||||
emptyGuide: 'Customers leave a star rating and text after purchase. New reviews start as Pending until a moderator approves, rejects, or marks them as spam — approved reviews become visible on the storefront. Aim to clear the pending queue regularly.',
|
||||
healthRating: 'Rating',
|
||||
healthText: 'Text',
|
||||
healthMedia: 'Media',
|
||||
healthModeration: 'Moderated',
|
||||
healthReportStatus: 'No open reports',
|
||||
healthVisibility: 'Visible',
|
||||
health: 'Health',
|
||||
column_product: 'Product',
|
||||
column_rating: 'Rating',
|
||||
column_status: 'Status',
|
||||
column_reports: 'Reports',
|
||||
column_created: 'Submitted',
|
||||
status: {
|
||||
pending: 'Pending',
|
||||
approved: 'Approved',
|
||||
rejected: 'Rejected',
|
||||
spam: 'Spam',
|
||||
},
|
||||
targetType: {
|
||||
product: 'Product',
|
||||
review: 'Review',
|
||||
customer: 'Customer',
|
||||
category: 'Category',
|
||||
unknown: 'Unknown',
|
||||
},
|
||||
reportStatus: {
|
||||
open: 'Open',
|
||||
resolved: 'Resolved',
|
||||
dismissed: 'Dismissed',
|
||||
},
|
||||
},
|
||||
adminOrders: {
|
||||
back: 'Back to orders',
|
||||
search: 'Search by order number, name, email, or phone…',
|
||||
@@ -1542,6 +1613,7 @@ export const en: Translations = {
|
||||
orders: 'Orders',
|
||||
customers: 'Customers',
|
||||
transactions: 'Transactions',
|
||||
moderation: 'Reviews & Moderation',
|
||||
reviews: 'Reviews',
|
||||
reports: 'Reports',
|
||||
content: 'Content',
|
||||
@@ -1581,6 +1653,9 @@ export const en: Translations = {
|
||||
orderDetail: { title: 'Order', description: 'Order details and status' },
|
||||
customers: { title: 'Customers', description: 'Everyone who has placed an order' },
|
||||
customerDetail: { title: 'Customer', description: 'Customer profile, orders, and activity' },
|
||||
moderation: { title: 'Reviews & Moderation', description: 'Review and moderate customer feedback' },
|
||||
reviewDetail: { title: 'Review', description: 'Review details and moderation actions' },
|
||||
reportsQueue: { title: 'Reports queue', description: 'Content reported by customers' },
|
||||
transactions: { title: 'Transactions', description: 'Payments, refunds, and fraud review' },
|
||||
media: { title: 'Media Library', description: 'Images and files for products and pages' },
|
||||
users: { title: 'Users', description: 'Admin team members and permissions' },
|
||||
|
||||
@@ -1135,6 +1135,77 @@ export const hy: Translations = {
|
||||
optionalDataMissingDescription: 'Optional config արժեքը բացակայում է։',
|
||||
optionalDataMissingResolution: 'Ավելացրեք արժեքը, եթե UX-ը կախված է դրանից։',
|
||||
},
|
||||
adminModeration: {
|
||||
search: 'Փնտրել ըստ հաճախորդի, ապրանքի կամ կարծիքի տեքստի…',
|
||||
allStatuses: 'Բոլոր կարգավիճակները',
|
||||
allRatings: 'Բոլոր գնահատականները',
|
||||
rating: 'Գնահատական',
|
||||
customer: 'Հաճախորդ',
|
||||
product: 'Ապրանք',
|
||||
reviewText: 'Կարծիք',
|
||||
photos: 'Լուսանկարներ',
|
||||
workflow: 'Գործողություններ',
|
||||
approve: 'Հաստատել',
|
||||
reject: 'Մերժել',
|
||||
markSpam: 'Նշել որպես սպամ',
|
||||
hide: 'Թաքցնել',
|
||||
restore: 'Վերականգնել',
|
||||
pin: 'Ամրացնել',
|
||||
unpin: 'Հանել ամրացումը',
|
||||
feature: 'Առանձնացնել',
|
||||
unfeature: 'Հանել առանձնացումը',
|
||||
featureDisabledHint: 'Առանձնացնել կարելի է միայն հաստատված կարծիքները։',
|
||||
moderatorNotes: 'Մոդերատորի նշումներ',
|
||||
notAvailable: 'Դեռ հասանելի չէ',
|
||||
changeStatus: 'Փոխել կարգավիճակը',
|
||||
hideSelected: 'Թաքցնել',
|
||||
archiveSelected: 'Արխիվացնել',
|
||||
reportsQueue: 'Բողոքների հերթ',
|
||||
reportsColumn: 'Բողոքներ',
|
||||
reportTarget: 'Բողոքարկված օբյեկտ',
|
||||
reportType: 'Տեսակ',
|
||||
reportReason: 'Պատճառ',
|
||||
resolve: 'Լուծված',
|
||||
dismiss: 'Մերժել',
|
||||
reportsEmptyTitle: 'Բողոքներ չկան',
|
||||
reportsEmptyDescription: 'Հաճախորդների բողոքները ապրանքների, կարծիքների կամ հաշիվների վերաբերյալ կհայտնվեն այստեղ։',
|
||||
reportedCount: 'Բողոքարկված',
|
||||
averageRating: 'Միջին գնահատական',
|
||||
moderationHealth: 'Մոդերացիայի վիճակ',
|
||||
emptyTitle: 'Կարծիքներ դեռ չկան',
|
||||
emptyDescription: 'Կարծիքները կհայտնվեն այստեղ հաճախորդների առաջին գնահատականներից հետո։',
|
||||
emptyGuide: 'Հաճախորդները թողնում են աստղանիշ գնահատական և տեքստ գնումից հետո։ Նոր կարծիքները ստանում են «Սպասման մեջ» կարգավիճակ, մինչև մոդերատորը հաստատի, մերժի կամ նշի որպես սպամ․ հաստատված կարծիքները դառնում են տեսանելի խանութում։ Փորձեք պարբերաբար մշակել սպասման հերթը։',
|
||||
healthRating: 'Գնահատական',
|
||||
healthText: 'Տեքստ',
|
||||
healthMedia: 'Մեդիա',
|
||||
healthModeration: 'Մոդերացված',
|
||||
healthReportStatus: 'Բաց բողոքներ չկան',
|
||||
healthVisibility: 'Տեսանելի է',
|
||||
health: 'Վիճակ',
|
||||
column_product: 'Ապրանք',
|
||||
column_rating: 'Գնահատական',
|
||||
column_status: 'Կարգավիճակ',
|
||||
column_reports: 'Բողոքներ',
|
||||
column_created: 'Ուղարկված է',
|
||||
status: {
|
||||
pending: 'Սպասման մեջ',
|
||||
approved: 'Հաստատված',
|
||||
rejected: 'Մերժված',
|
||||
spam: 'Սպամ',
|
||||
},
|
||||
targetType: {
|
||||
product: 'Ապրանք',
|
||||
review: 'Կարծիք',
|
||||
customer: 'Հաճախորդ',
|
||||
category: 'Կատեգորիա',
|
||||
unknown: 'Անհայտ',
|
||||
},
|
||||
reportStatus: {
|
||||
open: 'Բաց',
|
||||
resolved: 'Լուծված',
|
||||
dismissed: 'Մերժված',
|
||||
},
|
||||
},
|
||||
adminOrders: {
|
||||
back: 'Վերադառնալ պատվերներին',
|
||||
search: 'Փնտրել ըստ պատվերի համարի, անվան, էլ. փոստի կամ հեռախոսի…',
|
||||
@@ -1536,6 +1607,7 @@ export const hy: Translations = {
|
||||
categories: 'Կատեգորիաներ',
|
||||
orders: 'Պատվերներ',
|
||||
customers: 'Հաճախորդներ',
|
||||
moderation: 'Կարծիքներ և մոդերացիա',
|
||||
transactions: 'Գործարքներ',
|
||||
reviews: 'Կարծիքներ',
|
||||
reports: 'Հաշվետվություններ',
|
||||
@@ -1576,6 +1648,9 @@ export const hy: Translations = {
|
||||
orderDetail: { title: 'Պատվեր', description: 'Պատվերի մանրամասներն ու կարգավիճակը' },
|
||||
customers: { title: 'Հաճախորդներ', description: 'Բոլորը, ովքեր պատվեր են կատարել' },
|
||||
customerDetail: { title: 'Հաճախորդ', description: 'Հաճախորդի պրոֆիլ, պատվերներ և ակտիվություն' },
|
||||
moderation: { title: 'Կարծիքներ և մոդերացիա', description: 'Դիտեք և մոդերացրեք հաճախորդների կարծիքները' },
|
||||
reviewDetail: { title: 'Կարծիք', description: 'Կարծիքի մանրամասներ և մոդերացիայի գործողություններ' },
|
||||
reportsQueue: { title: 'Բողոքների հերթ', description: 'Հաճախորդների կողմից բողոքարկված բովանդակություն' },
|
||||
transactions: { title: 'Գործարքներ', description: 'Վճարումներ, վերադարձներ և կեղծիքի ստուգում' },
|
||||
media: { title: 'Մեդիադարան', description: 'Ապրանքների և էջերի պատկերներ ու ֆայլեր' },
|
||||
users: { title: 'Օգտատերեր', description: 'Ադմինիստրատորների թիմը և իրավունքները' },
|
||||
|
||||
@@ -1135,6 +1135,77 @@ export const ru: Translations = {
|
||||
optionalDataMissingDescription: 'Необязательное конфигурационное значение отсутствует.',
|
||||
optionalDataMissingResolution: 'Добавьте значение, если оно нужно UX.',
|
||||
},
|
||||
adminModeration: {
|
||||
search: 'Поиск по клиенту, товару или тексту отзыва…',
|
||||
allStatuses: 'Все статусы',
|
||||
allRatings: 'Все оценки',
|
||||
rating: 'Оценка',
|
||||
customer: 'Клиент',
|
||||
product: 'Товар',
|
||||
reviewText: 'Отзыв',
|
||||
photos: 'Фото',
|
||||
workflow: 'Действия',
|
||||
approve: 'Одобрить',
|
||||
reject: 'Отклонить',
|
||||
markSpam: 'Отметить как спам',
|
||||
hide: 'Скрыть',
|
||||
restore: 'Восстановить',
|
||||
pin: 'Закрепить',
|
||||
unpin: 'Открепить',
|
||||
feature: 'Выделить',
|
||||
unfeature: 'Убрать выделение',
|
||||
featureDisabledHint: 'Выделять можно только одобренные отзывы.',
|
||||
moderatorNotes: 'Заметки модератора',
|
||||
notAvailable: 'Пока недоступно',
|
||||
changeStatus: 'Изменить статус',
|
||||
hideSelected: 'Скрыть',
|
||||
archiveSelected: 'В архив',
|
||||
reportsQueue: 'Очередь жалоб',
|
||||
reportsColumn: 'Жалобы',
|
||||
reportTarget: 'Объект жалобы',
|
||||
reportType: 'Тип',
|
||||
reportReason: 'Причина',
|
||||
resolve: 'Решено',
|
||||
dismiss: 'Отклонить',
|
||||
reportsEmptyTitle: 'Жалоб нет',
|
||||
reportsEmptyDescription: 'Жалобы клиентов на товары, отзывы или аккаунты появятся здесь.',
|
||||
reportedCount: 'С жалобами',
|
||||
averageRating: 'Средняя оценка',
|
||||
moderationHealth: 'Состояние модерации',
|
||||
emptyTitle: 'Отзывов пока нет',
|
||||
emptyDescription: 'Отзывы появятся здесь после первых оценок от клиентов.',
|
||||
emptyGuide: 'Клиенты оставляют оценку и текст после покупки. Новые отзывы получают статус «Ожидает» до одобрения, отклонения или пометки как спам модератором — одобренные отзывы становятся видны на витрине. Старайтесь регулярно разбирать очередь ожидающих отзывов.',
|
||||
healthRating: 'Оценка',
|
||||
healthText: 'Текст',
|
||||
healthMedia: 'Медиа',
|
||||
healthModeration: 'Промодерирован',
|
||||
healthReportStatus: 'Нет открытых жалоб',
|
||||
healthVisibility: 'Виден',
|
||||
health: 'Состояние',
|
||||
column_product: 'Товар',
|
||||
column_rating: 'Оценка',
|
||||
column_status: 'Статус',
|
||||
column_reports: 'Жалобы',
|
||||
column_created: 'Отправлен',
|
||||
status: {
|
||||
pending: 'Ожидает',
|
||||
approved: 'Одобрен',
|
||||
rejected: 'Отклонён',
|
||||
spam: 'Спам',
|
||||
},
|
||||
targetType: {
|
||||
product: 'Товар',
|
||||
review: 'Отзыв',
|
||||
customer: 'Клиент',
|
||||
category: 'Категория',
|
||||
unknown: 'Неизвестно',
|
||||
},
|
||||
reportStatus: {
|
||||
open: 'Открыта',
|
||||
resolved: 'Решена',
|
||||
dismissed: 'Отклонена',
|
||||
},
|
||||
},
|
||||
adminOrders: {
|
||||
back: 'Назад к заказам',
|
||||
search: 'Поиск по номеру заказа, имени, email или телефону…',
|
||||
@@ -1536,6 +1607,7 @@ export const ru: Translations = {
|
||||
categories: 'Категории',
|
||||
orders: 'Заказы',
|
||||
customers: 'Клиенты',
|
||||
moderation: 'Отзывы и модерация',
|
||||
transactions: 'Транзакции',
|
||||
reviews: 'Отзывы',
|
||||
reports: 'Отчёты',
|
||||
@@ -1576,6 +1648,9 @@ export const ru: Translations = {
|
||||
orderDetail: { title: 'Заказ', description: 'Детали и статус заказа' },
|
||||
customers: { title: 'Клиенты', description: 'Все, кто оформлял заказ' },
|
||||
customerDetail: { title: 'Клиент', description: 'Профиль клиента, заказы и активность' },
|
||||
moderation: { title: 'Отзывы и модерация', description: 'Проверяйте и модерируйте отзывы клиентов' },
|
||||
reviewDetail: { title: 'Отзыв', description: 'Детали отзыва и действия модерации' },
|
||||
reportsQueue: { title: 'Очередь жалоб', description: 'Контент, на который пожаловались клиенты' },
|
||||
transactions: { title: 'Транзакции', description: 'Платежи, возвраты и проверка мошенничества' },
|
||||
media: { title: 'Медиатека', description: 'Изображения и файлы для товаров и страниц' },
|
||||
users: { title: 'Пользователи', description: 'Команда администраторов и права доступа' },
|
||||
|
||||
@@ -1138,6 +1138,77 @@ export interface Translations {
|
||||
optionalDataMissingDescription: string;
|
||||
optionalDataMissingResolution: string;
|
||||
};
|
||||
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;
|
||||
};
|
||||
};
|
||||
adminOrders: {
|
||||
back: string;
|
||||
search: string;
|
||||
@@ -1550,6 +1621,7 @@ export interface Translations {
|
||||
customers: string;
|
||||
transactions: string;
|
||||
reviews: string;
|
||||
moderation: string;
|
||||
reports: string;
|
||||
content: string;
|
||||
mediaLibrary: string;
|
||||
@@ -1588,6 +1660,9 @@ export interface Translations {
|
||||
orderDetail: { title: string; description: string };
|
||||
customers: { title: string; description: string };
|
||||
customerDetail: { title: string; description: string };
|
||||
moderation: { title: string; description: string };
|
||||
reviewDetail: { title: string; description: string };
|
||||
reportsQueue: { title: string; description: string };
|
||||
transactions: { title: string; description: string };
|
||||
media: { title: string; description: string };
|
||||
users: { title: string; description: string };
|
||||
|
||||
Reference in New Issue
Block a user