diff --git a/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.ts b/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.ts index 74aac233..e19dc654 100644 --- a/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.ts +++ b/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.ts @@ -9,11 +9,8 @@ import { Player, Team, TimeWindow } from '../../api/player-models'; import { PlayerService } from '../../api/player.service'; import { GameSessionService } from '../../services/game-session.service'; import { TeamAdminContextMenuSessionResetRequest } from '../components/team-admin-context-menu/team-admin-context-menu.component'; -import { TeamService } from '@/api/team.service'; -import { ToastService } from '@/utility/services/toast.service'; import { DateTime } from 'luxon'; import { ModalConfirmService } from '@/services/modal-confirm.service'; -import { FriendlyDatesService } from '@/services/friendly-dates.service'; import { ExtendTeamsModalComponent } from '../components/extend-teams-modal/extend-teams-modal.component'; import { GameService } from '@/api/game.service'; @@ -43,12 +40,9 @@ export class PlayerSessionComponent implements OnInit { constructor( private api: PlayerService, - private friendlyDatesAndTimes: FriendlyDatesService, private gameService: GameService, private sessionService: GameSessionService, private modalService: ModalConfirmService, - private teamService: TeamService, - private toastService: ToastService, ) { } ngOnInit(): void { diff --git a/projects/gameboard-ui/src/app/admin/admin.module.ts b/projects/gameboard-ui/src/app/admin/admin.module.ts index 7331d02f..1e2f1ee7 100644 --- a/projects/gameboard-ui/src/app/admin/admin.module.ts +++ b/projects/gameboard-ui/src/app/admin/admin.module.ts @@ -11,6 +11,7 @@ import { CoreModule } from '../core/core.module'; import { SponsorsModule } from '@/sponsors/sponsors.module'; import { UtilityModule } from '../utility/utility.module'; +import { ActiveChallengesModalComponent } from './components/active-challenges-modal/active-challenges-modal.component'; import { AdminOverviewComponent } from './components/admin-overview/admin-overview.component'; import { AdminPageComponent } from './admin-page/admin-page.component'; import { AnnounceComponent } from './announce/announce.component'; @@ -58,10 +59,11 @@ import { EventHorizonModule } from '@/event-horizon/event-horizon.module'; import { SupportSettingsComponent } from './components/support-settings/support-settings.component'; import { FeedbackEditorComponent } from './components/feedback-editor/feedback-editor.component'; import { ExtendTeamsModalComponent } from './components/extend-teams-modal/extend-teams-modal.component'; - +import { ActiveTeamsModalComponent } from './components/active-teams-modal/active-teams-modal.component'; @NgModule({ declarations: [ + ActiveChallengesModalComponent, AdminPageComponent, AnnounceComponent, ChallengeBrowserComponent, @@ -106,6 +108,7 @@ import { ExtendTeamsModalComponent } from './components/extend-teams-modal/exten SupportSettingsComponent, FeedbackEditorComponent, ExtendTeamsModalComponent, + ActiveTeamsModalComponent, ], imports: [ CommonModule, diff --git a/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.html b/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.html index e7bc207a..9543d72e 100644 --- a/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.html +++ b/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.html @@ -21,7 +21,7 @@

Observe Challenges

Search + aria-label="search term" aria-describedby="search-label" placeholder="term" [ngModel]="searchText">
Consoles
{{row.value.challengeScore | number}}
- +
diff --git a/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.ts b/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.ts index a729b326..6209d53f 100644 --- a/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.ts +++ b/projects/gameboard-ui/src/app/admin/challenge-observer/challenge-observer.component.ts @@ -2,7 +2,7 @@ // Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. import { KeyValue } from '@angular/common'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { faArrowLeft, faSyncAlt, faTv, faExternalLinkAlt, faExpandAlt, faUser, faThLarge, faMinusSquare, faPlusSquare, faCompressAlt, faSortAlphaDown, faSortAmountDownAlt, faAngleDoubleUp, faWindowRestore } from '@fortawesome/free-solid-svg-icons'; import { combineLatest, timer, BehaviorSubject, Observable, Subscription } from 'rxjs'; @@ -12,13 +12,15 @@ import { BoardService } from '../../api/board.service'; import { Game } from '../../api/game-models'; import { GameService } from '../../api/game.service'; import { ConfigService } from '../../utility/config.service'; +import { MatchesTermPipe } from '@/utility/pipes/matches-term.pipe'; @Component({ selector: 'app-challenge-observer', templateUrl: './challenge-observer.component.html', styleUrls: ['./challenge-observer.component.scss'], + providers: [MatchesTermPipe] }) -export class ChallengeObserverComponent implements OnInit, OnDestroy { +export class ChallengeObserverComponent implements OnDestroy { refresh$ = new BehaviorSubject(true); game?: Game; table: Map = new Map(); // table of player challenges to display @@ -47,16 +49,20 @@ export class ChallengeObserverComponent implements OnInit, OnDestroy { faAngleDoubleUp = faAngleDoubleUp; faWindowRestore = faWindowRestore; + protected searchText = ""; + constructor( route: ActivatedRoute, private api: BoardService, private gameApi: GameService, - private conf: ConfigService + private conf: ConfigService, + private matchesTerm: MatchesTermPipe ) { this.mksHost = conf.mkshost; this.gameData = route.params.pipe( switchMap(a => this.gameApi.retrieve(a.id)) ).subscribe(game => this.game = game); + this.tableData = combineLatest([ route.params, this.refresh$, @@ -67,9 +73,14 @@ export class ChallengeObserverComponent implements OnInit, OnDestroy { tap(() => this.isLoading = true), switchMap(() => this.api.consoles(this.gid)), ).subscribe(data => { + const queryTerm = route.snapshot.queryParams?.search?.toLowerCase(); + this.searchText = queryTerm; + this.typing$.next(queryTerm); + this.updateTable(data); this.isLoading = false; }); + this.fetchActors$ = combineLatest([ route.params, this.refresh$, @@ -88,6 +99,7 @@ export class ChallengeObserverComponent implements OnInit, OnDestroy { return map; }, new Map())) ); + this.term$ = this.typing$.pipe( debounceTime(500) ); @@ -105,14 +117,12 @@ export class ChallengeObserverComponent implements OnInit, OnDestroy { } else { this.table.set(updatedChallenge.id, updatedChallenge); } + if (updatedChallenge.gameRank > this.maxRank) this.maxRank = updatedChallenge.gameRank; } } - ngOnInit(): void { - } - ngOnDestroy(): void { this.tableData.unsubscribe(); this.gameData.unsubscribe(); diff --git a/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.html b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.html new file mode 100644 index 00000000..7a27641c --- /dev/null +++ b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.html @@ -0,0 +1,90 @@ + + + +
There aren't any active challenges of this type right now.
+
+ + + + diff --git a/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.scss b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.scss new file mode 100644 index 00000000..5f3fcfc8 --- /dev/null +++ b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.scss @@ -0,0 +1,43 @@ +h3 { + margin-bottom: 0; +} + +h5 { + margin-bottom: 0; + padding-bottom: 0; + text-overflow: ellipsis; +} + +li { + flex-basis: 32%; + min-width: 250px; +} + +// bootstrap overrides (yuck) +.card-footer { + background-color: unset !important; +} + +.spec-container:not(:nth-of-type(1)) { + margin-top: 2rem; +} + +.btn-link { + padding: 0; +} + +.game-link { + font-size: 0.9rem; + font-weight: bold; + margin: 0; + text-transform: uppercase; +} + +.card-footer { + border-top: dashed 1px gray; + + button { + padding-left: 8px; + padding-right: 8px; + } +} diff --git a/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.ts b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.ts new file mode 100644 index 00000000..54f1f6b1 --- /dev/null +++ b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.ts @@ -0,0 +1,54 @@ +import { Component, OnInit } from '@angular/core'; +import { fa } from "@/services/font-awesome.service"; +import { firstValueFrom } from 'rxjs'; +import { PlayerMode } from '@/api/player-models'; +import { AppActiveChallengeSpec } from '@/api/admin.models'; +import { AdminService } from '@/api/admin.service'; +import { ModalConfirmService } from '@/services/modal-confirm.service'; +import { RouterService } from '@/services/router.service'; + +@Component({ + selector: 'app-active-challenges-modal', + templateUrl: './active-challenges-modal.component.html', + styleUrls: ['./active-challenges-modal.component.scss'] +}) +export class ActiveChallengesModalComponent implements OnInit { + playerMode?: PlayerMode; + + protected errors: any[] = []; + protected fa = fa; + protected isPracticeMode = false; + protected isWorking = false; + protected specs: AppActiveChallengeSpec[] = []; + + constructor( + private adminService: AdminService, + private modalService: ModalConfirmService, + private routerService: RouterService) { } + + async ngOnInit(): Promise { + if (!this.playerMode) + throw new Error("Player mode not passed to active challenges modal."); + + await this.load(this.playerMode); + } + + protected close() { + this.modalService.hide(); + } + + private async load(playerMode: PlayerMode): Promise { + this.errors = []; + this.isPracticeMode = playerMode === PlayerMode.practice; + + try { + this.isWorking = true; + const response = await firstValueFrom(this.adminService.getActiveChallenges(playerMode)); + this.specs = response.specs; + this.isWorking = false; + } + catch (err: any) { + this.errors.push(err); + } + } +} diff --git a/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.html b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.html new file mode 100644 index 00000000..c496ba3a --- /dev/null +++ b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.html @@ -0,0 +1,96 @@ + + + +
There aren't any active teams right now.
+
+ + + + diff --git a/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.scss b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.scss new file mode 100644 index 00000000..c196a4dc --- /dev/null +++ b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.scss @@ -0,0 +1,3 @@ +.team-card { + flex-basis: 32%; +} diff --git a/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.ts b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.ts new file mode 100644 index 00000000..3e7b1692 --- /dev/null +++ b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { fa } from '@/services/font-awesome.service'; +import { AppActiveTeam } from '@/api/admin.models'; +import { ModalConfirmService } from '@/services/modal-confirm.service'; +import { AdminService } from '@/api/admin.service'; +import { firstValueFrom } from 'rxjs'; + +@Component({ + selector: 'app-active-teams-modal', + templateUrl: './active-teams-modal.component.html', + styleUrls: ['./active-teams-modal.component.scss'] +}) +export class ActiveTeamsModalComponent implements OnInit { + constructor( + private modalService: ModalConfirmService, + private admin: AdminService) { } + + protected errors: any[] = []; + protected fa = fa; + protected isWorking = false; + protected teams: AppActiveTeam[] = []; + + async ngOnInit(): Promise { + const response = await firstValueFrom(this.admin.getActiveTeams()); + this.teams = response.teams; + } + + protected close() { + this.modalService.hide(); + } +} diff --git a/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.html b/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.html index c67350c6..66da1b8f 100644 --- a/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.html +++ b/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.html @@ -1,14 +1,15 @@
  • - - +
  • - +
  • - +
  • diff --git a/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.ts b/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.ts index 34926939..8c3d2680 100644 --- a/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.ts +++ b/projects/gameboard-ui/src/app/admin/components/site-overview-stats/site-overview-stats.component.ts @@ -1,6 +1,10 @@ import { AdminService } from '@/api/admin.service'; +import { PlayerMode } from '@/api/player-models'; +import { ModalConfirmService } from '@/services/modal-confirm.service'; import { Component, OnInit } from '@angular/core'; import { firstValueFrom } from 'rxjs'; +import { ActiveChallengesModalComponent } from '../active-challenges-modal/active-challenges-modal.component'; +import { ActiveTeamsModalComponent } from '../active-teams-modal/active-teams-modal.component'; @Component({ selector: 'app-site-overview-stats', @@ -8,7 +12,9 @@ import { firstValueFrom } from 'rxjs'; styleUrls: ['./site-overview-stats.component.scss'] }) export class SiteOverviewStatsComponent implements OnInit { - constructor(private adminService: AdminService) { } + constructor( + private adminService: AdminService, + private modalService: ModalConfirmService) { } protected activeCompetitiveChallenges = 0; protected activePracticeChallenges = 0; @@ -23,4 +29,22 @@ export class SiteOverviewStatsComponent implements OnInit { this.activeCompetitiveTeams = stats.activeCompetitiveTeams; this.registeredUsers = stats.registeredUsers; } + + protected showChallengesModal(playerMode: "competitive" | "practice") { + this.modalService.openComponent({ + content: ActiveChallengesModalComponent, + context: { + playerMode: playerMode == "practice" ? PlayerMode.practice : PlayerMode.competition + }, + modalClasses: ["modal-dialog-centered", "modal-xl"] + }); + } + + protected showTeamsModal() { + this.modalService.openComponent({ + content: ActiveTeamsModalComponent, + context: {}, + modalClasses: ["modal-dialog-centered", "modal-xl"] + }); + } } diff --git a/projects/gameboard-ui/src/app/admin/team-observer/team-observer.component.html b/projects/gameboard-ui/src/app/admin/team-observer/team-observer.component.html index 22cfa825..df2f9c32 100644 --- a/projects/gameboard-ui/src/app/admin/team-observer/team-observer.component.html +++ b/projects/gameboard-ui/src/app/admin/team-observer/team-observer.component.html @@ -1,4 +1,3 @@ - Back @@ -19,8 +18,8 @@

    Observe {{game.allowTeam ? 'Teams' : 'Players'}}

    Search
    - +
Cumulative Time

Console

- -
+
- +
- {{row.value.approvedName}} @@ -67,16 +70,17 @@

Console

{{row.value.score | number}}
{{row.value.time | clock }}
-
- +
-
@@ -87,18 +91,21 @@

Console

- - -
-
+
{{user.vm.vmName}} @@ -106,7 +113,7 @@

Console

{{user.vm.challengeName}} -
+