feat(admin): sprint 19 admin dashboard, routing, i18n
- Add admin dashboard feature (models/gateway/facade/components/page) - Wire admin/products routes and backoffice coming-soon placeholders - Add lastPublishedAt to ProjectEditorFacade/state - Add dashboard i18n keys (en/ru/hy) and docs/ADMIN.md Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateService } from '../../../../i18n/translate.service';
|
||||
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||
import { LanguageService } from '../../../../services/language.service';
|
||||
import { AdminDashboardFacade } from '../facade/admin-dashboard.facade';
|
||||
import { AdminDashboardCardComponent } from '../components/admin-dashboard-card.component';
|
||||
import { AdminDashboardQuickActionsComponent } from '../components/admin-dashboard-quick-actions.component';
|
||||
import { AdminDashboardActivityComponent, AdminDashboardActivityViewEntry } from '../components/admin-dashboard-activity.component';
|
||||
import { AdminDashboardHealthComponent } from '../components/admin-dashboard-health.component';
|
||||
import { AdminDashboardCardStatus, AdminDashboardQuickAction } from '../models/admin-dashboard.model';
|
||||
|
||||
interface DashboardCardViewModel {
|
||||
id: string;
|
||||
titleKey: string;
|
||||
status: AdminDashboardCardStatus;
|
||||
value: string | null;
|
||||
subtitle: string | null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-dashboard-page',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
TranslatePipe,
|
||||
AdminDashboardCardComponent,
|
||||
AdminDashboardQuickActionsComponent,
|
||||
AdminDashboardActivityComponent,
|
||||
AdminDashboardHealthComponent,
|
||||
],
|
||||
templateUrl: './admin-dashboard-page.component.html',
|
||||
styleUrls: ['./admin-dashboard-page.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AdminDashboardPageComponent {
|
||||
readonly facade = inject(AdminDashboardFacade);
|
||||
private readonly translate = inject(TranslateService);
|
||||
private readonly languageService = inject(LanguageService);
|
||||
|
||||
readonly quickActions = computed<AdminDashboardQuickAction[]>(() => {
|
||||
const lang = this.languageService.currentLanguage();
|
||||
return this.facade.quickActions.map(action => ({
|
||||
...action,
|
||||
route: ['/', lang, ...action.route.filter(segment => segment !== '')],
|
||||
}));
|
||||
});
|
||||
|
||||
readonly activityEntries = computed<AdminDashboardActivityViewEntry[]>(() =>
|
||||
this.facade.activityEntries().map(entry => ({
|
||||
id: entry.id,
|
||||
labelKey: entry.type === 'published' ? 'dashboard.activityPublished' : 'dashboard.activityDraftSaved',
|
||||
timeText: new Date(entry.timestamp).toLocaleString(),
|
||||
})),
|
||||
);
|
||||
|
||||
readonly healthChecks = this.facade.healthChecks;
|
||||
|
||||
readonly cards = computed<DashboardCardViewModel[]>(() => {
|
||||
const bootstrap = this.facade.bootstrap();
|
||||
const bootstrapLoading: AdminDashboardCardStatus = bootstrap ? 'ready' : 'loading';
|
||||
const metrics = this.facade.metrics();
|
||||
const locales = bootstrap?.localization.supportedLocales ?? [];
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'marketplace-status',
|
||||
titleKey: 'dashboard.cardMarketplaceStatus',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap ? this.translate.t(this.facade.status() === 'published' ? 'builder.statusPublished' : 'builder.statusDraft') : null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'project-name',
|
||||
titleKey: 'dashboard.cardProjectName',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap?.tenant.name ?? null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'current-theme',
|
||||
titleKey: 'dashboard.cardCurrentTheme',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap?.theme.themeId ?? null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'languages',
|
||||
titleKey: 'dashboard.cardLanguages',
|
||||
status: bootstrapLoading === 'loading' ? 'loading' : (locales.length === 0 ? 'empty' : 'ready'),
|
||||
value: locales.length ? locales.join(', ').toUpperCase() : null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'categories-count',
|
||||
titleKey: 'dashboard.cardCategoriesCount',
|
||||
status: metrics.status,
|
||||
value: metrics.value ? String(metrics.value.categoriesCount) : null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'products-count',
|
||||
titleKey: 'dashboard.cardProductsCount',
|
||||
status: metrics.status,
|
||||
value: metrics.value ? String(metrics.value.productsCount) : null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'orders',
|
||||
titleKey: 'dashboard.cardOrders',
|
||||
status: 'pending-backend',
|
||||
value: null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'revenue',
|
||||
titleKey: 'dashboard.cardRevenue',
|
||||
status: 'pending-backend',
|
||||
value: null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'last-publish',
|
||||
titleKey: 'dashboard.cardLastPublish',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap ? this.formatTimestamp(this.facade.lastPublishedAt()) : null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'last-draft-save',
|
||||
titleKey: 'dashboard.cardLastDraftSave',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap ? this.formatTimestamp(this.facade.lastSavedAt()) : null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'bootstrap-version',
|
||||
titleKey: 'dashboard.cardBootstrapVersion',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap?.schemaVersion ?? null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'active-layout',
|
||||
titleKey: 'dashboard.cardActiveLayout',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap ? (bootstrap.layout?.type ?? 'default') : null,
|
||||
subtitle: null,
|
||||
},
|
||||
{
|
||||
id: 'enabled-widgets',
|
||||
titleKey: 'dashboard.cardEnabledWidgets',
|
||||
status: bootstrapLoading,
|
||||
value: bootstrap ? String(this.facade.enabledWidgetsCount()) : null,
|
||||
subtitle: null,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
constructor() {
|
||||
this.facade.ensureLoaded();
|
||||
}
|
||||
|
||||
cardTitle(titleKey: string): string {
|
||||
return this.translate.t(titleKey);
|
||||
}
|
||||
|
||||
private formatTimestamp(timestamp: number | null): string {
|
||||
return timestamp ? new Date(timestamp).toLocaleString() : this.translate.t('dashboard.never');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user