feat: user menu

This commit is contained in:
2026-02-19 15:44:23 -08:00
parent 603398b0e1
commit b6ac3cbb99
@@ -35,17 +35,30 @@ import { ApiService } from '../../services/api.service';
<div class="auth-section"> <div class="auth-section">
@if (authService.user(); as user) { @if (authService.user(); as user) {
<span class="welcome">Welcome, {{ user.username }}!</span> <div class="user-menu">
@if (!user.isAdmin) { @if (user.avatar) {
<a routerLink="/my-suggestions" class="user-link">My Suggestions</a> <img
} [src]="user.avatar"
<a routerLink="/my-likes" class="user-link">My Likes</a> [alt]="user.username"
@if (user.isAdmin) { class="user-avatar"
<a routerLink="/admin/users" class="admin-badge">Users</a> (click)="toggleDropdown()"
<a routerLink="/admin/audit" class="admin-badge">Audit</a> />
<a routerLink="/admin/suggestions" class="admin-badge">Suggestions</a> }
} @if (showDropdown()) {
<button (click)="logout()" class="btn btn-secondary">Logout</button> <div class="dropdown-menu">
@if (!user.isAdmin) {
<a routerLink="/my-suggestions" class="dropdown-item" (click)="closeDropdown()">My Suggestions</a>
}
<a routerLink="/my-likes" class="dropdown-item" (click)="closeDropdown()">My Likes</a>
@if (user.isAdmin) {
<a routerLink="/admin/users" class="dropdown-item" (click)="closeDropdown()">Users</a>
<a routerLink="/admin/audit" class="dropdown-item" (click)="closeDropdown()">Audit</a>
<a routerLink="/admin/suggestions" class="dropdown-item" (click)="closeDropdown()">Suggestions</a>
}
<button (click)="logout()" class="dropdown-item logout-btn">Logout</button>
</div>
}
</div>
} @else { } @else {
<button (click)="login()" class="btn btn-primary">Login with Discord</button> <button (click)="login()" class="btn btn-primary">Login with Discord</button>
} }
@@ -122,6 +135,75 @@ import { ApiService } from '../../services/api.service';
color: var(--witch-lavender); color: var(--witch-lavender);
} }
.user-menu {
position: relative;
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
border: 2px solid var(--witch-lavender);
transition: all 0.3s;
cursor: pointer;
}
.user-avatar:hover {
border-color: var(--witch-moon);
transform: scale(1.1);
}
.dropdown-menu {
position: absolute;
top: 50px;
right: 0;
background-color: var(--witch-purple);
border: 2px solid var(--witch-lavender);
border-radius: 8px;
padding: 0.5rem 0;
min-width: 180px;
box-shadow: 0 4px 12px var(--witch-shadow);
z-index: 1000;
animation: fadeIn 0.2s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.dropdown-item {
display: block;
width: 100%;
padding: 0.75rem 1rem;
color: var(--witch-lavender);
text-decoration: none;
background: none;
border: none;
text-align: left;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s;
}
.dropdown-item:hover {
background-color: var(--witch-plum);
color: var(--witch-moon);
}
.logout-btn {
border-top: 1px solid var(--witch-lavender);
margin-top: 0.5rem;
padding-top: 0.75rem;
font-weight: 500;
}
.admin-badge { .admin-badge {
background-color: var(--witch-rose); background-color: var(--witch-rose);
color: var(--witch-moon); color: var(--witch-moon);
@@ -190,6 +272,7 @@ export class HeaderComponent implements OnInit {
authService = inject(AuthService); authService = inject(AuthService);
private apiService = inject(ApiService); private apiService = inject(ApiService);
version = signal<string | null>(null); version = signal<string | null>(null);
showDropdown = signal<boolean>(false);
ngOnInit() { ngOnInit() {
this.apiService.get<{ version: string }>('/version').subscribe({ this.apiService.get<{ version: string }>('/version').subscribe({
@@ -198,11 +281,20 @@ export class HeaderComponent implements OnInit {
}); });
} }
toggleDropdown() {
this.showDropdown.update(v => !v);
}
closeDropdown() {
this.showDropdown.set(false);
}
login() { login() {
this.authService.login(); this.authService.login();
} }
logout() { logout() {
this.closeDropdown();
this.authService.logout().subscribe(); this.authService.logout().subscribe();
} }
} }