fix(backoffice): merchant-friendly wording in Monitoring

Analytics, Reports, and Diagnostics were already clean (no raw
HTTP/queue-worker strings found on audit). Monitoring had three spots
speaking developer language by default:

- Background queue names ("order-notifications") -> friendly labels
  ("Order notifications").
- Webhook event keys ("order.created") -> friendly labels ("New order
  placed").
- Activity log's "api" category showed the raw HTTP line
  ("GET /api/products responded 200 in 84ms") as the primary message.
  Now shows a plain-language summary by default ("Product data
  refreshed successfully"), with the raw string moved to a collapsed
  "Technical details" <details> per event (api/error/warning rows).
This commit is contained in:
sdarbinyan
2026-07-26 00:17:14 +04:00
parent 3e54e88db7
commit ca343c493f
9 changed files with 58 additions and 6 deletions

View File

@@ -8,6 +8,8 @@ export interface AdminMonitoringEvent {
category: AdminMonitoringCategory;
level: AdminMonitoringLevel;
message: string;
/** Raw technical detail (endpoint, status code, latency, etc.), shown only behind "Technical details". */
technicalDetail?: string;
actor: string;
timestamp: string;
}

View File

@@ -24,7 +24,7 @@
<div class="queue-grid">
@for (queue of facade.queues(); track queue.name) {
<div class="queue-item">
<strong>{{ queue.name }}</strong>
<strong>{{ queueLabel(queue.name) }}</strong>
<span>{{ 'adminMonitoring.depth' | translate }}: {{ queue.depth }}</span>
<app-badge [variant]="queue.status === 'healthy' ? 'success' : queue.status === 'degraded' ? 'warning' : 'danger'">{{ ('adminMonitoring.queueStatus.' + queue.status) | translate }}</app-badge>
</div>
@@ -47,7 +47,7 @@
@for (delivery of facade.webhooks(); track delivery.id) {
<tr>
<th scope="row">{{ delivery.endpoint }}</th>
<td>{{ delivery.event }}</td>
<td>{{ webhookEventLabel(delivery.event) }}</td>
<td><app-badge [variant]="delivery.status === 'delivered' ? 'success' : delivery.status === 'failed' ? 'danger' : 'neutral'">{{ ('adminMonitoring.webhookStatus.' + delivery.status) | translate }}</app-badge></td>
<td>{{ delivery.timestamp | date:'short' }}</td>
</tr>
@@ -97,7 +97,15 @@
<tr>
<td>{{ ('adminMonitoring.categoryValue.' + event.category) | translate }}</td>
<td><app-badge [variant]="event.level === 'error' ? 'danger' : event.level === 'warning' ? 'warning' : 'neutral'">{{ ('adminMonitoring.levelValue.' + event.level) | translate }}</app-badge></td>
<td>{{ event.message }}</td>
<td>
{{ event.message }}
@if (event.technicalDetail) {
<details class="monitoring-technical-detail">
<summary>{{ 'adminMonitoring.technicalDetails' | translate }}</summary>
<code>{{ event.technicalDetail }}</code>
</details>
}
</td>
<td>{{ event.actor }}</td>
<td>{{ event.timestamp | date:'short' }}</td>
</tr>

View File

@@ -13,3 +13,12 @@ select { min-height: 40px; padding: 0 10px; border: 1px solid var(--border-color
// to a plain <td> - only the semantics change, not the visuals.
tbody th[scope='row'] { font-weight: inherit; color: inherit; text-align: left; vertical-align: middle; }
tbody tr:last-child th[scope='row'] { border-bottom: none; }
.monitoring-technical-detail {
margin-top: 4px;
font-size: var(--font-size-sm, 0.8125rem);
color: var(--text-secondary);
summary { cursor: pointer; color: var(--text-secondary); }
code { display: block; margin-top: 4px; word-break: break-all; }
}

View File

@@ -25,7 +25,26 @@ export class AdminMonitoringPageComponent {
readonly categories = ['all', 'audit', 'security', 'login', 'failed_login', 'api', 'error', 'warning'] as const;
private readonly queueLabels: Record<string, string> = {
'order-notifications': 'Order notifications',
'media-processing': 'Media processing',
'webhook-delivery': 'Webhook delivery',
};
private readonly webhookEventLabels: Record<string, string> = {
'order.created': 'New order placed',
'inventory.updated': 'Inventory updated',
};
constructor() {
this.facade.loadAll();
}
queueLabel(name: string): string {
return this.queueLabels[name] ?? name;
}
webhookEventLabel(event: string): string {
return this.webhookEventLabels[event] ?? event;
}
}

View File

@@ -62,6 +62,7 @@ export class AdminMonitoringLocalGateway implements AdminMonitoringGateway {
category,
level,
message: this.messageFor(category, index),
technicalDetail: this.technicalDetailFor(category, index),
actor: ACTORS[index % ACTORS.length],
timestamp: new Date(Date.now() - index * 45 * 60 * 1000).toISOString(),
};
@@ -73,10 +74,19 @@ export class AdminMonitoringLocalGateway implements AdminMonitoringGateway {
case 'security': return `Security policy check passed (#${index})`;
case 'login': return `Successful admin login (#${index})`;
case 'failed_login': return `Failed login attempt (#${index})`;
case 'api': return `GET /api/products responded 200 in ${80 + index}ms`;
case 'error': return `Unhandled exception in checkout flow (#${index})`;
case 'warning': return `Slow query detected (#${index})`;
case 'api': return `Product data refreshed successfully`;
case 'error': return `A background task failed unexpectedly`;
case 'warning': return `A data lookup took longer than usual`;
default: return `Event #${index}`;
}
}
private technicalDetailFor(category: AdminMonitoringCategory, index: number): string | undefined {
switch (category) {
case 'api': return `GET /api/products responded 200 in ${80 + index}ms`;
case 'error': return `Unhandled exception in checkout flow (#${index})`;
case 'warning': return `Slow query detected (#${index})`;
default: return undefined;
}
}
}

View File

@@ -1850,6 +1850,7 @@ export const en: Translations = {
category: 'Category',
level: 'Severity',
message: 'Description',
technicalDetails: 'Technical details',
categoryValue: {
all: 'All categories',
audit: 'Admin activity',

View File

@@ -1845,6 +1845,7 @@ export const hy: Translations = {
category: 'Կատեգորիա',
level: 'Կարևորություն',
message: 'Նկարագրություն',
technicalDetails: 'Տեխնիկական մանրամասներ',
categoryValue: {
all: 'Բոլոր կատեգորիաները',
audit: 'Ադմինի գործողություններ',

View File

@@ -1845,6 +1845,7 @@ export const ru: Translations = {
category: 'Категория',
level: 'Важность',
message: 'Описание',
technicalDetails: 'Технические детали',
categoryValue: {
all: 'Все категории',
audit: 'Действия администраторов',

View File

@@ -1858,6 +1858,7 @@ export interface Translations {
category: string;
level: string;
message: string;
technicalDetails: string;
categoryValue: {
all: string;
audit: string;