Skip to content

Commit

Permalink
v3.20.0-beta0 (#189)
Browse files Browse the repository at this point in the history
* Add validation to game editor.

* Don't serve host API key in external host responses.

* Initial work on game center

* Finish batch user create

* Add observe links to support tickets. Fix enroll bugs (Admin enroll and loading indicator on error)

* Allow VM console urls to be built without a name, since they get a default one now.

* Add work for 3.19.3

* Fix decimal precision for questionw eight

* Fixed an issue that prevented the challenge observe link in Support Tools from working. Observe links now only appears when the session is live.

* Allow game end to be nullish. Show ongoing games on the landing.

* Fixed a bug that caused the enrollment report line chart to fail to refresh on param changes.

* Add players modal to practice mode -> challenges report

* Add hours of play to site usage. Resolves GBAPI#459. Also center all app dialogs by default.

* More work on game center

* MVP of game center

* Add player registrar to game center (until new player list is finished)

* Draft of game center

* Finish game center draft

* NPM audit
  • Loading branch information
sei-bstein authored Jul 25, 2024
1 parent 250c731 commit 4da0059
Show file tree
Hide file tree
Showing 121 changed files with 2,050 additions and 700 deletions.
223 changes: 151 additions & 72 deletions package-lock.json

Large diffs are not rendered by default.

46 changes: 0 additions & 46 deletions patch-it

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,18 @@ <h4 class="mb-2 px-3">Other tools</h4>
(click)="onManageManualBonusesRequest.emit(team.teamId)">Manage Challenge Bonuses</button>
<app-confirm-button btnClass="btn btn-sm btn-danger" *ngIf="canUnenroll"
(confirm)="onUnenrollRequest.emit(team.teamId)">Unenroll</app-confirm-button>
<app-confirm-button btnClass="btn btn-sm btn-danger mr-2" *ngIf="!canUnenroll"
(confirm)="onResetSessionRequest.emit({ player, resetType: 'preserveChallenges' })">
<!-- <app-confirm-button btnClass="btn btn-sm btn-danger mr-2" *ngIf="!canUnenroll"
(confirm)="onResetSessionRequest.emit({ teamId: player.teamId, resetType: 'preserveChallenges' })">
Reset Session (Preserve Challenges)
</app-confirm-button>
<app-confirm-button btnClass="btn btn-sm btn-danger mr-2" *ngIf="!canUnenroll"
(confirm)="onResetSessionRequest.emit({ player, resetType: 'archiveChallenges' })">
(confirm)="onResetSessionRequest.emit({ teamId: player.teamId, resetType: 'archiveChallenges' })">
Reset Session
</app-confirm-button>
<app-confirm-button btnClass="btn btn-sm btn-danger" *ngIf="!canUnenroll"
(confirm)="onResetSessionRequest.emit({ player, resetType: 'unenrollAndArchiveChallenges' })">Reset Session
&amp;
Unenroll</app-confirm-button>
(confirm)="onResetSessionRequest.emit({ teamId: player.teamId })">
Reset Session &amp; Unenroll
</app-confirm-button> -->
</div>
</div>

Expand Down
38 changes: 25 additions & 13 deletions projects/gameboard-ui/src/app/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ 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 { AdminSystemNotificationsComponent } from '@/system-notifications/components/admin-system-notifications/admin-system-notifications.component';
import { AnnounceComponent } from './announce/announce.component';
import { ChallengeBrowserComponent } from './challenge-browser/challenge-browser.component';
import { ChallengeObserverComponent } from './challenge-observer/challenge-observer.component';
Expand All @@ -33,6 +34,14 @@ import { ExternalGamePlayerStatusToFriendlyPipe } from './pipes/external-game-pl
import { FeedbackReportComponent } from './feedback-report/feedback-report.component';
import { GameBonusesConfigComponent } from './components/game-bonuses-config/game-bonuses-config.component';
import { GameCenterComponent } from './components/game-center/game-center.component';
import { GameCenterDeployComponent } from './components/game-center/game-center-deploy/game-center-deploy.component';
import { GameCenterObserveComponent } from './components/game-center/game-center-observe/game-center-observe.component';
import { GameCenterPracticePlayerDetailComponent } from './components/game-center/game-center-practice-player-detail/game-center-practice-player-detail.component';
import { GameCenterPracticeComponent } from './components/game-center/game-center-practice/game-center-practice.component';
import { GameCenterSettingsComponent } from './components/game-center/game-center-settings/game-center-settings.component';
import { GameCenterTeamContextMenuComponent } from './components/game-center/game-center-team-context-menu/game-center-team-context-menu.component';
import { GameCenterTeamsComponent } from './components/game-center/game-center-teams/game-center-teams.component';
import { GameCenterTicketsComponent } from './components/game-center/game-center-tickets/game-center-tickets.component';
import { GameClassificationToStringPipe } from './pipes/game-classification-to-string.pipe';
import { GameDesignerComponent } from './game-designer/game-designer.component';
import { GameEditorComponent } from './game-editor/game-editor.component';
Expand All @@ -54,11 +63,11 @@ import { SupportReportLegacyComponent } from './support-report-legacy/support-re
import { SyncStartTeamPlayerReadyCountPipe } from './pipes/sync-start-team-player-ready-count.pipe';
import { SystemNotificationsModule } from '@/system-notifications/system-notifications.module';
import { TeamAdminContextMenuComponent } from './components/team-admin-context-menu/team-admin-context-menu.component';
import { TeamListCardComponent } from './components/team-list-card/team-list-card.component';
import { TeamObserverComponent } from './team-observer/team-observer.component';
import { UserApiKeysComponent } from './user-api-keys/user-api-keys.component';
import { UserRegistrarComponent } from './user-registrar/user-registrar.component';
import { UserReportComponent } from './user-report/user-report.component';
import { AdminSystemNotificationsComponent } from '@/system-notifications/components/admin-system-notifications/admin-system-notifications.component';
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';
Expand All @@ -70,10 +79,6 @@ import { SyncStartGameStateDescriptionPipe } from './pipes/sync-start-game-state
import { ExternalGameHostPickerComponent } from './components/external-game-host-picker/external-game-host-picker.component';
import { ExternalHostEditorComponent } from './components/external-host-editor/external-host-editor.component';
import { DeleteExternalGameHostModalComponent } from './components/delete-external-game-host-modal/delete-external-game-host-modal.component';
import { GameCenterPlayersComponent } from './components/game-center/game-center-players/game-center-players.component';
import { GameCenterTeamContextMenuComponent } from './components/game-center/game-center-team-context-menu/game-center-team-context-menu.component';
import { GameCenterSettingsComponent } from './components/game-center/game-center-settings/game-center-settings.component';
import { GameCenterTicketsComponent } from './components/game-center/game-center-tickets/game-center-tickets.component';

@NgModule({
declarations: [
Expand All @@ -96,6 +101,14 @@ import { GameCenterTicketsComponent } from './components/game-center/game-center
ExternalTeamChallengesToIsPredeployablePipe,
FeedbackReportComponent,
GameCenterComponent,
GameCenterDeployComponent,
GameCenterPracticeComponent,
GameCenterPracticePlayerDetailComponent,
GameCenterSettingsComponent,
GameCenterObserveComponent,
GameCenterTeamContextMenuComponent,
GameCenterTeamsComponent,
GameCenterTicketsComponent,
GameClassificationToStringPipe,
GameDesignerComponent,
GameEditorComponent,
Expand Down Expand Up @@ -132,10 +145,7 @@ import { GameCenterTicketsComponent } from './components/game-center/game-center
ExternalGameHostPickerComponent,
ExternalHostEditorComponent,
DeleteExternalGameHostModalComponent,
GameCenterPlayersComponent,
GameCenterTeamContextMenuComponent,
GameCenterSettingsComponent,
GameCenterTicketsComponent,
TeamListCardComponent,
],
imports: [
CommonModule,
Expand All @@ -147,12 +157,14 @@ import { GameCenterTicketsComponent } from './components/game-center/game-center
{ path: '', pathMatch: 'full', redirectTo: 'dashboard' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'designer/:id', component: GameEditorComponent },
{
path: "game/:gameId/:selectedTab",
component: GameCenterComponent
},
{
path: 'game/:gameId',
component: GameCenterComponent,
children: [
{ path: "teams", component: PlayerRegistrarComponent }
]
pathMatch: 'full'
},
{
path: "game/:gameId/external", pathMatch: 'full', component: ExternalGameAdminComponent
Expand All @@ -177,7 +189,7 @@ import { GameCenterTicketsComponent } from './components/game-center/game-center
{ path: 'report/support', component: SupportReportLegacyComponent },
{ path: 'report/participation', component: ParticipationReportComponent },
{ path: "notifications", component: AdminSystemNotificationsComponent },
{ path: "support/settings", component: SupportSettingsComponent },
{ path: "support/settings", component: SupportSettingsComponent, title: "Admin | Support" },
{ path: 'support', component: ChallengeBrowserComponent }
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
<!-- Copyright 2021 Carnegie Mellon University. All Rights Reserved. -->
<!-- Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. -->

<a class="btn btn-link mr-2" routerLink="/admin">
<a class="btn btn-link mr-2" *ngIf="isLegacyMode" routerLink="/admin">
<fa-icon [icon]="faArrowLeft"></fa-icon>
<span>Back</span>
</a>
<button class="btn btn-link mr-2" (click)="refresh$.next(true)">
<fa-icon [icon]="faSync"></fa-icon>
<span>Refresh</span>
</button>
<div class="d-flex">

<div *ngIf="isLegacyMode" class="d-flex">
<h2>Observe Challenges</h2>
<a *ngIf="!!game" class="btn btn-link mr-2 align-self-center" [routerLink]="['/admin/observer/teams', gid]">
<span>Observe {{game.allowTeam ? 'Teams' : 'Players'}} </span>
</a>
</div>

<div class="row">
<div class="input-group mt-2 mb-3 col-5">
<div class="input-group-prepend">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// 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 } from '@angular/core';
import { Component, Input, OnDestroy, OnInit } 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';
import { debounceTime, tap, switchMap, map } from 'rxjs/operators';
import { debounceTime, tap, switchMap, map, filter } from 'rxjs/operators';
import { ConsoleActor, ObserveChallenge, ObserveVM } from '../../api/board-models';
import { BoardService } from '../../api/board.service';
import { Game } from '../../api/game-models';
Expand All @@ -20,7 +20,9 @@ import { MatchesTermPipe } from '@/utility/pipes/matches-term.pipe';
styleUrls: ['./challenge-observer.component.scss'],
providers: [MatchesTermPipe]
})
export class ChallengeObserverComponent implements OnDestroy {
export class ChallengeObserverComponent implements OnInit, OnDestroy {
@Input() gameId?: string;

refresh$ = new BehaviorSubject<boolean>(true);
game?: Game;
table: Map<string, ObserveChallenge> = new Map<string, ObserveChallenge>(); // table of player challenges to display
Expand Down Expand Up @@ -49,17 +51,18 @@ export class ChallengeObserverComponent implements OnDestroy {
faAngleDoubleUp = faAngleDoubleUp;
faWindowRestore = faWindowRestore;

protected isLegacyMode = false;
protected searchText = "";

constructor(
route: ActivatedRoute,
private api: BoardService,
private gameApi: GameService,
private conf: ConfigService,
private matchesTerm: MatchesTermPipe
private conf: ConfigService
) {
this.mksHost = conf.mkshost;
this.gameData = route.params.pipe(
filter(a => !!a.id),
switchMap(a => this.gameApi.retrieve(a.id))
).subscribe(game => this.game = game);

Expand All @@ -69,7 +72,7 @@ export class ChallengeObserverComponent implements OnDestroy {
timer(0, 60_000) // *every 60 sec* refresh challenge data (score/duration updates and new deploys)
]).pipe(
debounceTime(500),
tap(([a, b, c]) => this.gid = a.id),
tap(([a, b, c]) => this.gid = this.gameId || a.id),
tap(() => this.isLoading = true),
switchMap(() => this.api.consoles(this.gid)),
).subscribe(data => {
Expand All @@ -87,7 +90,7 @@ export class ChallengeObserverComponent implements OnDestroy {
timer(0, 10_000) // *every 10 sec* refresh which users are one which consoles
]).pipe(
debounceTime(500),
tap(([a, b, c]) => this.gid = a.id),
tap(([a, b, c]) => this.gid = this.gameId || a.id),
switchMap(() => this.api.consoleActors(this.gid)),
map(data => data.reduce((map, item) => {
const key = `${item.challengeId}#${item.vmName}`;
Expand All @@ -105,6 +108,10 @@ export class ChallengeObserverComponent implements OnDestroy {
);
}

public ngOnInit() {
this.isLegacyMode == !this.gameId;
}

updateTable(data: ObserveChallenge[]) {
for (let updatedChallenge of data) {
if (this.table.has(updatedChallenge.id)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
<div class="modal-content" *ngIf="game">
<div class="modal-header">
<h2 class="modal-title">Add {{isTeamGame ? "Team" : "Player"}}</h2>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="close()">
<span aria-hidden="true">&times;</span>
</button>
</div>

<div class="modal-body my-2" *ngIf="!isWorking; else loading">
<app-modal-content *ngIf="game" title="Add {{isTeamGame ? 'Team' : 'Player'}}" [subtitle]="game.name"
(confirm)="handleAddClick()"
[confirmDisabled]="selectedUsers.length === 0 || (!!game.minTeamSize && (selectedUsers.length < game.minTeamSize)) || (!!game.maxTeamSize && (selectedUsers.length > game.maxTeamSize))">
<ng-container *ngIf="!isWorking; else loading">
<app-error-div [errors]="errors"></app-error-div>
<div class="input-group">
<input [(ngModel)]="searchTerm" appAutofocus [typeahead]="typeaheadSearch$" [typeaheadAsync]="true"
Expand All @@ -32,21 +27,8 @@ <h2 class="modal-title">Add {{isTeamGame ? "Team" : "Player"}}</h2>
</li>
</ul>
</div>
</div>

<div class="modal-footer">
<!-- <div *ngIf="this.selectedUsers.length >= 2 && isTeamGame" class="form-group form-check flex-grow-1">
<input type="checkbox" class="form-check-input" id="addAsTeam">
<label class="form-check-label" for="addAsTeam">Add these users as a team</label>
</div> -->

<button type="button" class="btn link-button" (click)="close()">Cancel</button>
<button type="button" class="btn btn-success" (click)="handleAddClick()"
[disabled]="selectedUsers.length === 0 || (game.minTeamSize && (selectedUsers.length < game.minTeamSize)) || (game.maxTeamSize && (selectedUsers.length > game.maxTeamSize))">
{{ (game.maxTeamSize || 0) > 1 ? "Create this team" : "Add this player" }}
</button>
</div>
</div>
</ng-container>
</app-modal-content>

<ng-template #loading>
<app-spinner></app-spinner>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ <h2 class="m-0 p-0 fs-11">{{ spec.name }}</h2>
<label for="guide-show-in-competitive-{{spec.id}}">Show Solution Guide in Competitive Mode</label>
</div>


<div class="form-check mt-2">
<input type="checkbox" class="form-check-input" id="disabled-input-{{spec.id}}" name="disabled"
[(ngModel)]="spec.disabled" (ngModelChange)="handleSpecUpdated(spec)">
Expand All @@ -44,7 +43,7 @@ <h2 class="m-0 p-0 fs-11">{{ spec.name }}</h2>
<input type="checkbox" class="form-check-input" id="hidden-input-{{spec.id}}" name="disabled"
[(ngModel)]="spec.isHidden" (ngModelChange)="handleSpecUpdated(spec)">
<app-whats-this
whatItIs="Hidden challenge specs are invisible to players and don't contribute to their score.">
whatItIs="Hidden challenge specs are invisible to players and don't contribute to their score. They also don't appear in reports.">
<label for="hidden-input-{{spec.id}}">Hidden</label>
</app-whats-this>
</div>
Expand Down
Loading

0 comments on commit 4da0059

Please sign in to comment.