Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.9.0 #116

Merged
merged 34 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
41f2b93
Dynamically load team challenge data when it's requested.
sei-bstein Jan 25, 2023
ce8ef61
Dynamically load team challenge data when it's requested.
sei-bstein Jan 25, 2023
d0a7385
Refactor feedback form to reduce dependency on observable inputs. Res…
sei-bstein Mar 10, 2023
f5dbf29
Foundation of UI for managing manual points
sei-bstein Mar 17, 2023
d16618d
Merge branch 'issue/112' into issue/146
sei-bstein Mar 17, 2023
6744452
Add management of bonus points to admin
sei-bstein Mar 17, 2023
49d3522
update oidc and silent-renew
sei-jmattson Mar 20, 2023
ebf5f0c
Finish MVP of manual challenge bonuses
sei-bstein Mar 20, 2023
9c31c7c
Add required field stubs to oidc config in the prod environment.
sei-bstein Mar 20, 2023
e097895
Merge optimizations from previous branch, adjust layout
sei-bstein Mar 20, 2023
be75fde
Wire up player session events on player admin for game.
sei-bstein Mar 20, 2023
928d842
Resolve bug that incorrectly classified sessions as resettable or not.
sei-bstein Mar 20, 2023
deb5d1a
return 'view' button. Add copy actions to context menu.
sei-bstein Mar 20, 2023
72973fc
Usability improvements to player admin
sei-bstein Mar 20, 2023
5866b7e
Refactor gameboard page clock into its own component. Resolve timer i…
sei-bstein Mar 20, 2023
ad1f13f
Merge pull request #114 from cmu-sei/fix-auth-silent-renew
sei-bstein Mar 20, 2023
fca7eed
Merge pull request #113 from cmu-sei/issue/112
sei-bstein Mar 20, 2023
ccb0d81
Merge pull request #115 from cmu-sei/issue/146
sei-bstein Mar 20, 2023
44602b1
Merge next. Resolve clock-related bugs during player session. Resolve…
sei-bstein Mar 20, 2023
cf03b76
enable practice challenges
sei-jmattson Mar 21, 2023
b49166d
Remove logging code.
sei-bstein Mar 21, 2023
d14fa4a
Merge branch 'issue/148' into next
sei-bstein Mar 21, 2023
e0415e3
Fix audit warnings
sei-bstein Mar 21, 2023
392c6a6
Fix issue that prevented feedback form from rendering
sei-bstein Mar 21, 2023
0c019d2
Consolidate cumulative challenge clock into component
sei-bstein Mar 22, 2023
de538be
Merge branch 'feature/practice-challenges' into next
sei-bstein Mar 22, 2023
c9311d2
Update countdown clock on session extend in prac.
sei-bstein Mar 22, 2023
52ddf89
Only allow bonuses for started challenges
sei-bstein Mar 22, 2023
e7cc4a3
Enable testers to admin enroll outside the registration window. Fix b…
sei-bstein Mar 27, 2023
b567d4b
Restore the ability of admins/registrars to unenroll/reset players fr…
sei-bstein Mar 27, 2023
eb1baeb
Clarify player status tooltips.
sei-bstein Mar 27, 2023
602084b
Fix URL timestamping for cubespace
sei-bstein Mar 28, 2023
4edd682
Standardize size of enroll buttons. Allow admin enroll when registrat…
sei-bstein Mar 31, 2023
a92b9e0
Allow admin enroll/unenroll after registration is over.
sei-bstein Mar 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10,788 changes: 7,669 additions & 3,119 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"marked": "^4.2.5",
"ngx-bootstrap": "^10.1.0",
"ngx-markdown": "^15.0.0",
"oidc-client": "^1.11.5",
"oidc-client-ts": "^2.2.2",
"rxjs": "~6.6.0",
"toastify-js": "^1.12.0",
"tslib": "^2.1.0",
Expand All @@ -46,8 +46,8 @@
"@angular/cli": "^15.0.4",
"@angular/compiler-cli": "^15.0.4",
"@types/jasmine": "^4.3.1",
"@types/marked": "^4.0.8",
"@types/luxon": "^3.2.0",
"@types/marked": "^4.0.8",
"@types/node": "^14.18.23",
"@types/toastify-js": "^1.11.1",
"jasmine-core": "~4.5.0",
Expand Down
6 changes: 6 additions & 0 deletions projects/gameboard-ui/src/app/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import { UserApiKeysComponent } from './user-api-keys/user-api-keys.component';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { AlertModule } from 'ngx-bootstrap/alert';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { TeamAdminContextMenuComponent } from './components/team-admin-context-menu/team-admin-context-menu.component';
import { ManageManualChallengeBonusesComponent } from './components/manage-manual-challenge-bonuses/manage-manual-challenge-bonuses.component';
import { ManageManualChallengeBonusesModalComponent } from './components/manage-manual-challenge-bonuses-modal/manage-manual-challenge-bonuses-modal.component';

@NgModule({
declarations: [
Expand Down Expand Up @@ -66,6 +69,9 @@ import { TooltipModule } from 'ngx-bootstrap/tooltip';
PlayerNamesComponent,
ParticipationReportComponent,
UserApiKeysComponent,
TeamAdminContextMenuComponent,
ManageManualChallengeBonusesComponent,
ManageManualChallengeBonusesModalComponent
],
imports: [
CommonModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<div class="manage-manual-challenge-bonuses-modal-component">
<div class="modal-header">
<h2 class="modal-title pull-left">
Manage Bonus Points for
<span class="text-info">{{ playerName }}</span> in
<span class="text-info">"{{ gameName }} "</span>
</h2>
<button type="button" class="btn-close close pull-right" aria-label="Close" (click)="close()">
<span aria-hidden="true" class="visually-hidden">&times;</span>
</button>
</div>
<div class="modal-body">
<app-manage-manual-challenge-bonuses [teamId]="teamId"></app-manage-manual-challenge-bonuses>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" (click)="close()">Done</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component } from '@angular/core';
import { BsModalService } from 'ngx-bootstrap/modal';

@Component({
selector: 'app-manage-manual-challenge-bonuses-modal',
templateUrl: './manage-manual-challenge-bonuses-modal.component.html',
styleUrls: ['./manage-manual-challenge-bonuses-modal.component.scss']
})
export class ManageManualChallengeBonusesModalComponent {
teamId!: string;
playerName!: string;
gameName!: string;

constructor(private bsModalService: BsModalService) { }

close() {
this.bsModalService.hide();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<div class="manage-manual-challenge-bonuses-component m-4" *ngIf="summary">
<table *ngIf="summary.challengeScoreSummaries.length" class="table table-striped border-bottom border-info">
<thead class="h5">
<th scope="col" width="50%">Challenge</th>
<th scope="col" width="10%"></th>
<th scope="col" width="10%">Base </th>
<th scope="col" width="10%">Bonus</th>
<th scope="col" width="15%">Total score</th>
</thead>
<tbody>
<tr *ngFor="let challengeSummary of summary.challengeScoreSummaries">
<td>
<div class="points-breakdown">
<h6>{{challengeSummary.spec.name}}</h6>

<ul *ngIf="challengeSummary.manualBonuses.length" class="mt-2">
<li *ngFor="let manualBonus of challengeSummary.manualBonuses">
<div class="bonus-description">
{{ manualBonus.pointValue > 0 ? "+" : "-" }} {{ manualBonus.pointValue }}
for <strong>{{ manualBonus.description }}</strong>

<em>
({{ manualBonus.enteredBy.name }},
{{ manualBonus.enteredOn | shortdate}} @
{{ manualBonus.enteredOn | friendlyTime }})
</em>
</div>
</li>
</ul>
</div>
</td>
<td>
<div class="challenge-title-spacer" *ngIf="challengeSummary.manualBonuses.length"></div>

<div class="delete-bonus-button-container"
*ngFor="let manualBonus of challengeSummary.manualBonuses">
<button class="btn btn-sm btn-danger" type="button"
(click)="handleDelete(manualBonus.id)">Delete</button>
</div>
</td>
<td>
<h6>{{ challengeSummary.scoreFromChallenge }}</h6>
</td>
<td>
<h6>{{ challengeSummary.scoreFromManualBonuses }}</h6>
</td>
<td>
<h6>{{ challengeSummary.totalScore }}</h6>
</td>
</tr>

<tr class="table-info h6">
<td></td>
<td></td>
<td>{{summary.challengesScore}}</td>
<td>{{summary.manualBonusesScore}}</td>
<td>{{summary.totalScore}}</td>
</tr>
</tbody>
</table>

<ng-template #noBonuses>
<div class="text-secondary">
{{summary.team.name}} hasn't been awarded any bonuses for this game. To give them one, use the form below.
</div>
</ng-template>

<div class="add-bonus-container mt-5">
<h3 class="mt-3 mb-2">Add a bonus</h3>
<form #newBonusForm (submit)="onSubmit()" *ngIf="startedChallenges.length">
<div class="form-group w-100">
<select class="form-select form-select-lg w-100" name="challengeId"
aria-label="Select challenge for bonus" [(ngModel)]="newChallengeBonusModel.challengeId" required>
<option *ngFor="let challenge of startedChallenges" [value]="challenge.id">
{{ challenge.name }}
</option>
</select>
</div>
<div class="form-group">
<label for="bonusDescription">Bonus description</label>
<input type="text" class="form-control" id="bonusDescription" name="bonusDescription"
placeholder="Description for this bonus" required [(ngModel)]="newChallengeBonusModel.description">
</div>
<div class="form-group">
<label for="bonusValue">Points</label>
<input type="number" class="form-control" id="bonusValue" name="pointValue"
placeholder="Points to award" min="1" [(ngModel)]="newChallengeBonusModel.pointValue">
</div>
<button type="submit" class="btn btn-primary"
[disabled]="!newChallengeBonusModel.challengeId || !newChallengeBonusModel.description || !newChallengeBonusModel.pointValue">
Add this bonus
</button>
</form>

<ng-template #noChallengesStarted>
<div class="no-challenges-started-message">Bonuses can only be added after a challenge has begun, and this
player/team hasn't started any yet.</div>
</ng-template>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
li {
line-height: 1.5rem;
list-style-type: circle;
}

.challenge-title-spacer {
height: 1.75rem;
}

.delete-bonus-button-container,
li {
padding: 0.5rem 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Component, Input, OnInit } from '@angular/core';
import { first } from 'rxjs/operators';
import { SimpleEntity } from '../../../api/models';
import { CreateManualChallengeBonus, TeamGameScoreSummary } from '../../../api/scoring-models';
import { ScoringService } from '../../../services/scoring.service';

@Component({
selector: 'app-manage-manual-challenge-bonuses',
templateUrl: './manage-manual-challenge-bonuses.component.html',
styleUrls: ['./manage-manual-challenge-bonuses.component.scss']
})
export class ManageManualChallengeBonusesComponent implements OnInit {
@Input() teamId!: string;

summary?: TeamGameScoreSummary;
startedChallenges: SimpleEntity[] = [];
challengesStarted = 0;

newChallengeBonusModel: CreateManualChallengeBonus = {
description: '',
challengeId: '',
pointValue: 1
}

constructor(private scoresService: ScoringService) { }

ngOnInit(): void {
this.loadSummary(this.teamId);
}

private loadSummary(teamId: string) {
this.scoresService.getTeamGameScore(teamId)
.pipe(first())
.subscribe(summary => {
this.summary = summary;
this.startedChallenges = this.summary.challengeScoreSummaries.filter(s => !!s.challenge).map(s => s.challenge);

this.newChallengeBonusModel = {
description: '',
challengeId: summary.challengeScoreSummaries.length ? summary.challengeScoreSummaries[0].challenge.id : '',
pointValue: 1
}
});
}

handleDelete(manualbonusId: string) {
this.scoresService.deleteManualBonus(manualbonusId)
.pipe(first())
.subscribe(() => this.loadSummary(this.teamId));
}

onSubmit() {
this.scoresService
.createManualChallengeBonus(this.newChallengeBonusModel)
.pipe(first())
.subscribe(_ => {
this.loadSummary(this.teamId);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<div class="btn-group" dropdown>
<button id="button-basic" dropdownToggle type="button" class="btn ctx-menu-button rounded-circle"
aria-controls="dropdown-basic">
<fa-icon [icon]="faService.ellipsisVertical" size="2x"></fa-icon>
</button>
<ul id="dropdown-basic" *dropdownMenu class="dropdown-menu" role="menu" aria-labelledby="button-basic">
<li role="menuitem">
<button class="dropdown-item btn" (click)="onViewRequest.emit(player)">
{{ isViewing ? "Collapse" : "View" }}
</button>
</li>
<li role="menuitem">
<button class="dropdown-item btn" (click)="copy(player.id, 'player ID')">Copy player ID</button>
</li>
<li role="menuitem">
<button class="dropdown-item btn" (click)="copy(player.teamId, 'team ID')">Copy team ID</button>
</li>
<li role="menuitem">
<button class="dropdown-item btn" (click)="onBonusManageRequest.emit(player)">
Manage Challenge Bonuses
</button>
</li>
<li class="divider dropdown-divider"></li>
<li role="menuitem" *ngIf="isResettingSession; else unenroll">
<button class="dropdown-item btn btn-danger" (click)="onSessionResetRequest.emit(player)">Reset
Session</button>
</li>
<ng-template #unenroll>
<li role="menuitem">
<button type="button" class="dropdown-item btn btn-danger"
(click)="onUnenrollRequest.emit(player)">Unenroll</button>
</li>
</ng-template>
</ul>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.ctx-menu-button {
aspect-ratio: 1 / 1;
}

.btn-danger {
font-weight: bold;
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Player, TimeWindow } from '../../../api/player-models';
import { FontAwesomeService } from '../../../services/font-awesome.service';
import { GameSessionService } from '../../../services/game-session.service';
import { ClipboardService } from '../../../utility/services/clipboard.service';
import { ToastService } from '../../../utility/services/toast.service';

@Component({
selector: 'app-team-admin-context-menu',
templateUrl: './team-admin-context-menu.component.html',
styleUrls: ['./team-admin-context-menu.component.scss']
})
export class TeamAdminContextMenuComponent implements OnInit {
@Input() public player!: Player;
@Input() isViewing = false;

@Output() onBonusManageRequest = new EventEmitter<Player>();
@Output() onSessionResetRequest = new EventEmitter<Player>();
@Output() onUnenrollRequest = new EventEmitter<Player>();
@Output() onViewRequest = new EventEmitter<Player>();

isResettingSession = false;

constructor(
private clipboardService: ClipboardService,
protected faService: FontAwesomeService,
private gameSessionService: GameSessionService,
private toastService: ToastService) { }

ngOnInit(): void {
this.isResettingSession = !this.gameSessionService.canUnenroll(this.player);
}

async copy(text: string, description: string) {
await this.clipboardService.copy(text);
this.toastService.showMessage(`Copied ${description} "${text}" to your clipboard.`)
}
}
Loading