Files
marketplaces/src/app/features/admin/shell/admin-layout.component.ts

143 lines
4.7 KiB
TypeScript
Raw Normal View History

import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, HostListener, computed, inject, signal, viewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, NavigationEnd, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { filter } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TranslatePipe } from '../../../i18n/translate.pipe';
import { TranslateService } from '../../../i18n/translate.service';
import { LanguageService } from '../../../services/language.service';
import { AdminAuthService } from '../../../core/admin-auth/admin-auth.service';
import { ADMIN_NAV_BOTTOM, ADMIN_NAV_PRIMARY, AdminBreadcrumbEntry, AdminNavEntry } from './admin-nav.model';
@Component({
selector: 'app-admin-layout',
standalone: true,
imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive, TranslatePipe],
templateUrl: './admin-layout.component.html',
styleUrl: './admin-layout.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AdminLayoutComponent {
private readonly router = inject(Router);
private readonly route = inject(ActivatedRoute);
private readonly destroyRef = inject(DestroyRef);
private readonly translate = inject(TranslateService);
private readonly languageService = inject(LanguageService);
private readonly adminAuth = inject(AdminAuthService);
private readonly drawerEl = viewChild<ElementRef<HTMLElement>>('drawer');
private readonly menuToggleEl = viewChild<ElementRef<HTMLElement>>('menuToggle');
readonly navPrimary: AdminNavEntry[] = ADMIN_NAV_PRIMARY;
readonly navBottom: AdminNavEntry[] = ADMIN_NAV_BOTTOM;
readonly mobileDrawerOpen = signal(false);
readonly notificationsOpen = signal(false);
readonly currentLang = this.languageService.currentLanguage;
readonly displayName = this.adminAuth.displayName;
readonly initials = computed(() => {
const name = this.displayName();
if (!name) {
return '?';
}
return name
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map(part => part[0]?.toUpperCase())
.join('');
});
private readonly breadcrumbSignal = signal<AdminBreadcrumbEntry[]>([]);
private readonly titleKeySignal = signal<string>('adminShell.pages.dashboard.title');
private readonly descriptionKeySignal = signal<string>('adminShell.pages.dashboard.description');
readonly breadcrumb = this.breadcrumbSignal.asReadonly();
readonly pageTitleKey = this.titleKeySignal.asReadonly();
readonly pageDescriptionKey = this.descriptionKeySignal.asReadonly();
constructor() {
this.readRouteData();
this.router.events
.pipe(
filter(event => event instanceof NavigationEnd),
takeUntilDestroyed(this.destroyRef),
)
.subscribe(() => {
this.readRouteData();
this.closeMobileDrawer();
this.notificationsOpen.set(false);
});
}
@HostListener('document:keydown.escape')
onEscape(): void {
if (this.mobileDrawerOpen()) {
this.closeMobileDrawer();
return;
}
if (this.notificationsOpen()) {
this.notificationsOpen.set(false);
}
}
toggleMobileDrawer(): void {
this.mobileDrawerOpen.update(open => !open);
if (this.mobileDrawerOpen()) {
queueMicrotask(() => {
const drawer = this.drawerEl()?.nativeElement;
drawer?.querySelector<HTMLElement>('a, button')?.focus();
});
}
}
closeMobileDrawer(): void {
if (!this.mobileDrawerOpen()) {
return;
}
this.mobileDrawerOpen.set(false);
this.menuToggleEl()?.nativeElement.focus();
}
toggleNotifications(): void {
this.notificationsOpen.update(open => !open);
}
adminLinkFor(entry: Extract<AdminNavEntry, { type: 'link' }>): string[] {
const lang = this.currentLang();
if (entry.absolutePath) {
return ['/', lang, ...entry.absolutePath];
}
return ['/', lang, 'backoffice', ...(entry.path ?? [])];
}
breadcrumbLink(entry: AdminBreadcrumbEntry): string[] {
return ['/', this.currentLang(), 'backoffice', ...(entry.path ?? [])];
}
logout(): void {
this.adminAuth.logout();
}
cardTitle(key: string): string {
return this.translate.t(key);
}
private readRouteData(): void {
let leaf = this.route;
while (leaf.firstChild) {
leaf = leaf.firstChild;
}
const data = leaf.snapshot.data as {
titleKey?: string;
descriptionKey?: string;
breadcrumb?: AdminBreadcrumbEntry[];
};
this.titleKeySignal.set(data.titleKey ?? 'adminShell.pages.dashboard.title');
this.descriptionKeySignal.set(data.descriptionKey ?? 'adminShell.pages.dashboard.description');
this.breadcrumbSignal.set(data.breadcrumb ?? []);
}
}