-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
- Loading branch information
1 parent
a51a5b0
commit a97b3cf
Showing
69 changed files
with
1,796 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
...ameboard-ui/src/app/admin/components/create-users-modal/create-users-modal.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<app-modal-content title="Create Users" (confirm)="confirm()" [confirmDisabled]="!userIds.length" | ||
*ngIf="!isWorking; else loading"> | ||
<div class="mb-4"> | ||
Enter space-delimited user GUIDs (globally unique identifiers) in the textbox below to create a new | ||
{{appName}} user account for each. You can use the settings below to control some initial settings for | ||
the created users. | ||
</div> | ||
|
||
<div class="input-controls"> | ||
<textarea appAutofocus class="w-100 font-monospace form-control" rows="10" (input)="handleTextInput()" | ||
[placeholder]="placeholder" [(ngModel)]="rawText"></textarea> | ||
|
||
<alert *ngIf="invalidIds.length" type="danger" class="mt-3"> | ||
{{appName}} user IDs may only contain the letters <strong>A through F</strong> (in upper or lowercase), | ||
<strong>hyphens</strong>, and <strong>digits</strong>. The following IDs can't be used: | ||
|
||
<ul class="mt-3"> | ||
<li class="li-style-type-circle ml-4" *ngFor="let invalidId of invalidIds">{{invalidId}}</li> | ||
</ul> | ||
</alert> | ||
|
||
<div class="settings mt-4"> | ||
<h2 class=fs-12>Settings</h2> | ||
<div class="form-check ml-2"> | ||
<input type="checkbox" class="form-check-input" name="allow-subset-creation" | ||
[(ngModel)]="allowSubsetCreation"> | ||
<label for="allow-subset-creation">Show an error if one of these IDs already exists</label> | ||
</div> | ||
|
||
<div class="form-check ml-2"> | ||
<input type="checkbox" class="form-check-input" name="unset-default-sponsor-flag" | ||
[(ngModel)]="unsetDefaultSponsorFlag"> | ||
<label for="unset-default-sponsor-flag">Don't force users to select their sponsor before playing</label> | ||
</div> | ||
|
||
<div class="my-3"> | ||
<label for="createWithSponsorId">Create users with sponsor</label> | ||
<select class="form-control" name="createWithSponsorId" [(ngModel)]="createWithSponsorId"> | ||
<option [ngValue]="undefined">[the default sponsor]</option> | ||
<ng-container *ngFor="let parent of sponsors"> | ||
<option [value]="parent.id" class="group-parent">{{ parent.name }}</option> | ||
<option *ngFor="let child of parent.childSponsors" class="group-child" [value]="child.id"> | ||
- | ||
{{ child.name }} | ||
</option> | ||
</ng-container> | ||
</select> | ||
</div> | ||
|
||
<div class="my-3"> | ||
<label for="enrollInGameId">Enroll users in game</label> | ||
<select class="form-control" name="enrollInGameId" [(ngModel)]="enrollInGameId"> | ||
<option [ngValue]="undefined">[don't enroll them]</option> | ||
<option *ngFor="let game of games" [value]="game.id">{{ game.name }}</option> | ||
</select> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div footer> | ||
<div *ngIf="(userIds.length - invalidIds.length) > 0"> | ||
<strong class="text-info">{{userIds.length}}</strong> | ||
{{ "user" | pluralizer:userIds.length - invalidIds.length }} will be created. | ||
</div> | ||
</div> | ||
</app-modal-content> | ||
|
||
<ng-template #loading> | ||
<app-spinner>Loading...</app-spinner> | ||
</ng-template> |
Empty file.
84 changes: 84 additions & 0 deletions
84
.../gameboard-ui/src/app/admin/components/create-users-modal/create-users-modal.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { Component } from '@angular/core'; | ||
import { UserService } from '@/api/user.service'; | ||
import { ConfigService } from '@/utility/config.service'; | ||
import { TryCreateUsersResponse } from '@/api/user-models'; | ||
import { SponsorService } from '@/api/sponsor.service'; | ||
import { SponsorWithChildSponsors } from '@/api/sponsor-models'; | ||
import { firstValueFrom } from 'rxjs'; | ||
import { GameService } from '@/api/game.service'; | ||
import { SimpleEntity } from '@/api/models'; | ||
|
||
@Component({ | ||
selector: 'app-create-users-modal', | ||
templateUrl: './create-users-modal.component.html', | ||
styleUrls: ['./create-users-modal.component.scss'] | ||
}) | ||
export class CreateUsersModalComponent { | ||
onCreated?: (response: TryCreateUsersResponse) => void | Promise<void>; | ||
|
||
protected allowSubsetCreation = false; | ||
protected createWithSponsorId?: string; | ||
protected unsetDefaultSponsorFlag = false; | ||
|
||
protected appName: string; | ||
protected enrollInGameId?: string; | ||
protected games: SimpleEntity[] = []; | ||
protected hasInvalidIds = false; | ||
protected invalidIds: string[] = []; | ||
protected isWorking = false; | ||
protected placeholder: string; | ||
protected rawText: string = ""; | ||
protected sponsors: SponsorWithChildSponsors[] = []; | ||
protected userIds: string[] = []; | ||
|
||
private invalidIdsRegex = /[a-fA-F0-9-]{2,}/; | ||
private onePerLineRegex = /\s+/gm; | ||
|
||
constructor( | ||
config: ConfigService, | ||
private gameService: GameService, | ||
private sponsorService: SponsorService, | ||
private usersService: UserService) { | ||
this.appName = config.appName; | ||
this.placeholder = "// one ID per line, e.g.:\n\n3496da07-d19e-440d-a246-e35f7b7bfcac\n9a53d8cd-ef88-44c0-96b2-fc8766b518dd\n\n//and so on"; | ||
} | ||
|
||
async ngOnInit() { | ||
this.isWorking = true; | ||
this.games = (await firstValueFrom(this.gameService.list({ "orderBy": "name" }))).map(game => ({ | ||
id: game.id, | ||
name: game.name | ||
})); | ||
this.sponsors = await firstValueFrom(this.sponsorService.listWithChildren()); | ||
this.isWorking = false; | ||
} | ||
|
||
async confirm() { | ||
this.isWorking = true; | ||
const result = await this.usersService.tryCreateMany({ | ||
allowSubsetCreation: this.allowSubsetCreation, | ||
enrollInGameId: this.enrollInGameId, | ||
sponsorId: this.createWithSponsorId, | ||
unsetDefaultSponsorFlag: this.unsetDefaultSponsorFlag, | ||
userIds: this.userIds | ||
}); | ||
this.isWorking = false; | ||
|
||
if (this.onCreated) | ||
this.onCreated(result); | ||
} | ||
|
||
protected handleTextInput() { | ||
this.userIds = this.rawText | ||
.split(this.onePerLineRegex) | ||
.map(entry => entry.trim()) | ||
.filter(entry => entry.length > 2); | ||
|
||
this.invalidIds = []; | ||
for (const id of this.userIds) { | ||
if (!id.match(this.invalidIdsRegex)) { | ||
this.invalidIds.push(id); | ||
} | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...c/app/admin/components/game-center/game-center-players/game-center-players.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<ng-container *ngIf="!isLoading || !results || !game; else loading"> | ||
<ul *ngIf="results?.teams?.items?.length"> | ||
<li *ngFor="let team of results?.teams?.items"> | ||
<div class="card my-2"> | ||
<div class="card-body"> | ||
<div class="d-flex"> | ||
<div class="d-flex flex-grow-1"> | ||
<app-avatar *ngIf="team.players.length == 1" [tooltip]="team.captain.sponsor.name" | ||
[imageUrl]="team.captain.sponsor | sponsorToLogoUri" size="small" | ||
class="mr-2"></app-avatar> | ||
<div> | ||
<h5 class="card-title my-1">{{ team.name }}</h5> | ||
<h6 class="card-subtitle mb-2 text-muted"> {{ team.captain.sponsor.name }}</h6> | ||
</div> | ||
</div> | ||
|
||
<app-game-center-team-context-menu *ngIf="game" [team]="team" | ||
[game]="{ id: game.id, name: game.name, isSyncStart: game.requireSynchronizedStart}"></app-game-center-team-context-menu> | ||
</div> | ||
|
||
<ul class="d-flex align-items-center" *ngIf="team.players.length > 1"> | ||
<li class="mr-3" *ngTemplateOutlet="playerAvatar; context: { $implicit: team.captain}"> | ||
</li> | ||
</ul> | ||
</div> | ||
</div> | ||
</li> | ||
</ul> | ||
</ng-container> | ||
|
||
<ng-template #playerAvatar let-player> | ||
<app-avatar [imageUrl]="player.sponsor | sponsorToLogoUri" size="tiny" [tooltip]="player.name"></app-avatar> | ||
</ng-template> | ||
|
||
<ng-template #noMatches> | ||
<p class="my-2 text-muted text-center">No players match your search.</p> | ||
</ng-template> | ||
|
||
<ng-template #loading> | ||
<app-spinner>Finding players...</app-spinner> | ||
</ng-template> |
Empty file.
37 changes: 37 additions & 0 deletions
37
...src/app/admin/components/game-center/game-center-players/game-center-players.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Component, Input, OnInit } from '@angular/core'; | ||
import { GameCenterTeamsResults } from '@/api/admin.models'; | ||
import { AdminService } from '@/api/admin.service'; | ||
import { Game } from '@/api/game-models'; | ||
import { GameService } from '@/api/game.service'; | ||
import { firstValueFrom } from 'rxjs'; | ||
|
||
@Component({ | ||
selector: 'app-game-center-players', | ||
templateUrl: './game-center-players.component.html', | ||
styleUrls: ['./game-center-players.component.scss'] | ||
}) | ||
export class GameCenterPlayersComponent implements OnInit { | ||
@Input() gameId?: string; | ||
|
||
protected game?: Game; | ||
protected isLoading = false; | ||
protected results?: GameCenterTeamsResults; | ||
|
||
constructor( | ||
private adminService: AdminService, | ||
private gameService: GameService) { } | ||
|
||
async ngOnInit(): Promise<void> { | ||
if (!this.gameId) | ||
throw new Error("Component requires a gameId"); | ||
|
||
this.game = await firstValueFrom(this.gameService.retrieve(this.gameId)); | ||
await this.load(); | ||
} | ||
|
||
private async load() { | ||
this.isLoading = true; | ||
this.results = await this.adminService.getGameCenterTeams(this.gameId!); | ||
this.isLoading = false; | ||
} | ||
} |
Oops, something went wrong.