119 lines
3.8 KiB
TypeScript
119 lines
3.8 KiB
TypeScript
import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { EventState, ledBackgroundColor } from '../../../core/services/event-status.store';
|
|
|
|
@Component({
|
|
selector: 'app-shell-header',
|
|
standalone: true,
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
imports: [CommonModule],
|
|
styles: [`
|
|
.led {
|
|
display: inline-block;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
border: 0;
|
|
vertical-align: middle;
|
|
}
|
|
`],
|
|
template: `
|
|
<header class="shell-header" data-testid="shell-header">
|
|
<button
|
|
type="button"
|
|
class="shell-title"
|
|
data-testid="shell-title"
|
|
(click)="titleClick.emit()"
|
|
>
|
|
{{ pageTitle() }}
|
|
</button>
|
|
|
|
<div class="shell-section" data-testid="shell-active-section">
|
|
{{ activeSection() }}
|
|
</div>
|
|
|
|
<div class="shell-right">
|
|
<span
|
|
class="led"
|
|
[class.led-running]="eventState() === 'running'"
|
|
[class.led-countdown]="eventState() === 'countdown'"
|
|
[class.led-stopped]="eventState() === 'stopped'"
|
|
[class.led-unconfigured]="eventState() === 'unconfigured'"
|
|
[style.background-color]="ledColor()"
|
|
data-testid="shell-led"
|
|
[attr.aria-label]="'Event status: ' + eventState()"
|
|
></span>
|
|
<span class="countdown" data-testid="shell-countdown">{{ countdownText() }}</span>
|
|
|
|
<div class="user-menu-wrapper">
|
|
<button
|
|
type="button"
|
|
class="user-menu-trigger"
|
|
data-testid="user-menu-trigger"
|
|
(click)="usernameMenuToggle.emit()"
|
|
>
|
|
{{ username() }} <span aria-hidden="true">▾</span>
|
|
</button>
|
|
|
|
@if (userMenuOpen()) {
|
|
<div class="user-menu" role="menu" data-testid="user-menu">
|
|
<button
|
|
type="button"
|
|
class="user-menu-item"
|
|
data-testid="user-menu-rank"
|
|
(click)="rankClick.emit()"
|
|
>
|
|
{{ rankText() }}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="user-menu-item"
|
|
data-testid="user-menu-change-password"
|
|
(click)="changePasswordClick.emit()"
|
|
>
|
|
Change password
|
|
</button>
|
|
@if (canAccessAdmin()) {
|
|
<button
|
|
type="button"
|
|
class="user-menu-item"
|
|
data-testid="user-menu-admin"
|
|
(click)="adminClick.emit()"
|
|
>
|
|
Admin area
|
|
</button>
|
|
}
|
|
<button
|
|
type="button"
|
|
class="user-menu-item"
|
|
data-testid="user-menu-logout"
|
|
(click)="logoutClick.emit()"
|
|
>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
`,
|
|
})
|
|
export class ShellHeaderComponent {
|
|
readonly pageTitle = input<string>('HIPCTF');
|
|
readonly activeSection = input<string>('Challenges');
|
|
readonly eventState = input<EventState>('unconfigured');
|
|
readonly countdownText = input<string>('');
|
|
readonly username = input<string>('');
|
|
readonly rankText = input<string>('');
|
|
readonly canAccessAdmin = input<boolean>(false);
|
|
readonly userMenuOpen = input<boolean>(false);
|
|
|
|
readonly titleClick = output<void>();
|
|
readonly usernameMenuToggle = output<void>();
|
|
readonly rankClick = output<void>();
|
|
readonly changePasswordClick = output<void>();
|
|
readonly adminClick = output<void>();
|
|
readonly logoutClick = output<void>();
|
|
|
|
readonly ledColor = computed(() => ledBackgroundColor(this.eventState()));
|
|
} |