130 lines
6.5 KiB
TypeScript
130 lines
6.5 KiB
TypeScript
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 -->
|
|
<aside class="w-64 bg-background border-r border-border flex flex-col">
|
|
<!-- Logo -->
|
|
<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>
|
|
<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">
|
|
<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>
|
|
|
|
<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>
|
|
|
|
<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>
|
|
|
|
<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>
|
|
|
|
<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 -->
|
|
<div class="p-4 border-t border-border">
|
|
<div class="flex items-center gap-3">
|
|
<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">
|
|
<p class="font-mono text-sm font-medium text-foreground truncate">
|
|
{{ userName() }}
|
|
</p>
|
|
<p class="font-mono text-xs text-muted-foreground truncate">
|
|
{{ userEmail() }}
|
|
</p>
|
|
</div>
|
|
<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 -->
|
|
<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();
|
|
}
|
|
}
|