Files
registry/ui/src/app/shared/components/layout/layout.component.ts

130 lines
6.5 KiB
TypeScript
Raw Normal View History

import { Component, computed, inject } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
import { AuthService } from '../../../core/services/auth.service';
@Component({
selector: 'app-layout',
standalone: true,
imports: [RouterOutlet, RouterLink, RouterLinkActive],
template: `
<div class="min-h-screen flex">
<!-- Sidebar -->
2025-11-28 10:24:10 +00:00
<aside class="w-64 bg-background border-r border-border flex flex-col">
<!-- Logo -->
2025-11-28 10:24:10 +00:00
<div class="h-16 flex items-center px-6 border-b border-border">
<a routerLink="/" class="flex items-center gap-3">
<div class="w-8 h-8 bg-primary flex items-center justify-center">
<svg class="w-5 h-5 text-primary-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
</div>
2025-11-28 10:24:10 +00:00
<span class="font-mono font-bold text-lg uppercase tracking-wider text-foreground">Stack.Gallery</span>
</a>
</div>
<!-- Navigation -->
<nav class="flex-1 p-4 space-y-1">
2025-11-28 10:24:10 +00:00
<a routerLink="/dashboard" routerLinkActive="bg-primary/10 text-primary"
class="nav-link">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
Dashboard
</a>
2025-11-28 10:24:10 +00:00
<a routerLink="/organizations" routerLinkActive="bg-primary/10 text-primary"
class="nav-link">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
Organizations
</a>
2025-11-28 10:24:10 +00:00
<a routerLink="/packages" routerLinkActive="bg-primary/10 text-primary"
class="nav-link">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
Packages
</a>
2025-11-28 10:24:10 +00:00
<a routerLink="/tokens" routerLinkActive="bg-primary/10 text-primary"
class="nav-link">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
</svg>
API Tokens
</a>
2025-11-28 10:24:10 +00:00
<a routerLink="/settings" routerLinkActive="bg-primary/10 text-primary"
class="nav-link">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
Settings
</a>
<!-- Admin Section -->
@if (isAdmin()) {
<div class="pt-4 mt-4 border-t border-border">
<p class="px-3 mb-2 font-mono text-xs text-muted-foreground uppercase tracking-wider">Administration</p>
<a routerLink="/admin/auth" routerLinkActive="bg-primary/10 text-primary"
class="nav-link">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
Authentication
</a>
</div>
}
</nav>
<!-- User section -->
2025-11-28 10:24:10 +00:00
<div class="p-4 border-t border-border">
<div class="flex items-center gap-3">
2025-11-28 10:24:10 +00:00
<div class="w-8 h-8 bg-muted flex items-center justify-center">
<span class="font-mono text-sm font-medium text-muted-foreground">
{{ userInitial() }}
</span>
</div>
<div class="flex-1 min-w-0">
2025-11-28 10:24:10 +00:00
<p class="font-mono text-sm font-medium text-foreground truncate">
{{ userName() }}
</p>
2025-11-28 10:24:10 +00:00
<p class="font-mono text-xs text-muted-foreground truncate">
{{ userEmail() }}
</p>
</div>
2025-11-28 10:24:10 +00:00
<button (click)="logout()" class="p-1.5 text-muted-foreground hover:text-foreground hover:bg-muted/30 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
</button>
</div>
</div>
</aside>
<!-- Main content -->
2025-11-28 10:24:10 +00:00
<main class="flex-1 bg-background overflow-auto">
<router-outlet />
</main>
</div>
`,
})
export class LayoutComponent {
private authService = inject(AuthService);
userName = computed(() => this.authService.user()?.displayName || 'User');
userEmail = computed(() => this.authService.user()?.email || '');
userInitial = computed(() => {
const name = this.authService.user()?.displayName || 'U';
return name.charAt(0).toUpperCase();
});
isAdmin = computed(() => this.authService.isAdmin());
logout(): void {
this.authService.logout();
}
}