Skip to content

Commit

Permalink
v3.26.3 (#213)
Browse files Browse the repository at this point in the history
* Add game session availability warning and enrollment trend by game

* Allow viewing of the game screen for practice mode games

* Improve performance of enrollment trend line

* Fix link rendering in markdown

* Fix onn-dismissible notification creation
  • Loading branch information
sei-bstein authored Dec 16, 2024
1 parent 82c8c12 commit a717b12
Show file tree
Hide file tree
Showing 29 changed files with 288 additions and 158 deletions.
2 changes: 0 additions & 2 deletions projects/gameboard-ui/src/app/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { ExternalGameAdminTeamContextMenuComponent } from './components/external
import { ExternalGameAdminComponent } from './components/external-game-admin/external-game-admin.component';
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 { FeedbackEditorComponent } from './components/feedback-editor/feedback-editor.component';
import { GameBonusesConfigComponent } from './components/game-bonuses-config/game-bonuses-config.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';
Expand Down Expand Up @@ -141,7 +140,6 @@ import { UserPickerComponent } from '@/standalone/users/user-picker/user-picker.
SiteOverviewStatsComponent,
AdminOverviewComponent,
SupportSettingsComponent,
FeedbackEditorComponent,
ExtendTeamsModalComponent,
ActiveTeamsModalComponent,
AdminEnrollTeamModalComponent,
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,6 @@
<app-feedback-template-picker labelText="Challenges Feedback Template"
[(templateId)]="game.challengesFeedbackTemplateId"
(select)="handleChallengesFeedbackTemplateChanged($event)"></app-feedback-template-picker>

<app-feedback-editor *ngIf="game.feedbackConfig"
[feedbackConfig]="game.feedbackConfig"></app-feedback-editor>
</div>

<div class="col-12 mt-5">
Expand Down Expand Up @@ -303,7 +300,7 @@ <h4>Execution</h4>

</div>

<div *ngIf="game.gameStart > game.gameEnd" class="row form-group">
<div *ngIf="game.gameEnd && game.gameStart > game.gameEnd" class="row form-group">
<alert type="warning" class="col-12">The game's open date must be less than its close date.</alert>
</div>

Expand All @@ -322,6 +319,18 @@ <h4>Execution</h4>
<small>total concurrent sessions allowed for game</small>
</div>

<div class="form-group pb-0 pt-1">
<label for="sessionAvailabilityWarningThreshold-input">Session Availability Warning
Threshold</label>
<input type="number" class="form-control" id="sessionLimit-input"
name="sessionAvailabilityWarningThreshold"
[(ngModel)]="game.sessionAvailabilityWarningThreshold">
<small>
If set, shows a warning when the number of currently-available sessions drops below this
number
</small>
</div>

<div class="form-group pb-0 pt-1">
<label for="gamespaceLimit-input">Gamespace Limit</label>
<input type="number" class="form-control" id="gamespaceLimit-input" name="gamespaceLimit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import { KeyValue } from '@angular/common';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { FormGroup, NgForm } from '@angular/forms';
import { debounceTime, filter, firstValueFrom, switchMap, tap } from 'rxjs';
import { FeedbackTemplate } from '@/api/feedback-models';
import { ExternalGameHost, Game, GameEngineMode, GameRegistrationType } from '@/api/game-models';
import { GameService } from '@/api/game.service';
import { fa } from '@/services/font-awesome.service';
import { PracticeService } from '@/services/practice.service';
import { UnsubscriberService } from '@/services/unsubscriber.service';
import { YamlService } from '@/services/yaml.service';
import { ToastService } from '@/utility/services/toast.service';
import { ActivatedRoute } from '@angular/router';
import { FeedbackTemplateView } from '@/feedback/feedback.models';
Expand Down Expand Up @@ -167,7 +165,7 @@ export class GameCenterSettingsComponent implements AfterViewInit {
if (game.minTeamSize > game.maxTeamSize)
return false;

if (game.gameStart > game.gameEnd)
if (game.gameEnd && new Date(game.gameEnd).getFullYear() > 1 && game.gameStart > game.gameEnd)
return false;

if (game.registrationType == GameRegistrationType.open && game.registrationOpen > game.registrationClose)
Expand Down
9 changes: 6 additions & 3 deletions projects/gameboard-ui/src/app/api/feedback.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ export class FeedbackService {
private url = '';
private yamlService = inject(YamlService);

private _deleted$ = new Subject<string>();
public deleted$ = this._deleted$.asObservable();
private _templatesDeleted$ = new Subject<string>();
public templatesDeleted$ = this._templatesDeleted$.asObservable();

private _templatesUpdated$ = new Subject<void>();
public templatesUpdated$ = this._templatesUpdated$.asObservable();

constructor(
config: ConfigService,
Expand All @@ -47,7 +50,7 @@ export class FeedbackService {

public async deleteTemplate(template: FeedbackTemplateView) {
await firstValueFrom(this.http.delete(`${this.url}/feedback/template/${template.id}`));
this._deleted$.next(template.id);
this._templatesDeleted$.next(template.id);
}

public async getTemplate(templateId: string): Promise<FeedbackTemplateView> {
Expand Down
8 changes: 8 additions & 0 deletions projects/gameboard-ui/src/app/api/game-models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.

import { DateTime } from "luxon";
import { FeedbackTemplate } from "./feedback-models";
import { SimpleEntity } from "./models";
import { Player, PlayerMode, TimeWindow } from "./player-models";
Expand Down Expand Up @@ -36,6 +37,7 @@ export interface GameDetail {
maxTeamSize: number;
sessionMinutes: number;
sessionLimit: number;
sessionAvailabilityWarningThreshold?: number;
gamespaceLimitPerSession: number;
isPublished: boolean;
requireSponsoredTeam: boolean;
Expand Down Expand Up @@ -169,6 +171,12 @@ export interface UpsertExternalGameHost {
teamExtendedEndpoint?: string;
}

export interface GameSessionAvailibilityResponse {
nextSessionEnd?: DateTime
sessionsAvailable: number
}


export interface GetExternalTeamDataResponse {
teamId: string;
externalUrl: string;
Expand Down
6 changes: 5 additions & 1 deletion projects/gameboard-ui/src/app/api/game.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { map, tap } from 'rxjs/operators';
import { SyncStartGameState } from '../game/game.models';
import { ConfigService } from '../utility/config.service';
import { ChallengeGate } from './board-models';
import { ChangedGame, Game, GameGroup, NewGame, SessionForecast, UploadedFile } from './game-models';
import { ChangedGame, Game, GameGroup, GameSessionAvailibilityResponse, NewGame, SessionForecast, UploadedFile } from './game-models';
import { TimeWindow } from './player-models';
import { Spec } from './spec-models';
import { YamlService } from '@/services/yaml.service';
Expand Down Expand Up @@ -83,6 +83,10 @@ export class GameService {
return firstValueFrom(this.http.post(`${this.url}/game/${gameId}/resources`, { teamIds }));
}

public getSessionAvailability(gameId: string): Promise<GameSessionAvailibilityResponse> {
return firstValueFrom(this.http.get<GameSessionAvailibilityResponse>(`${this.url}/game/${gameId}/session-availability`));
}

public getSyncStartState(gameId: string): Observable<SyncStartGameState> {
return this.http.get<SyncStartGameState>(`${this.url}/game/${gameId}/ready`);
}
Expand Down
7 changes: 7 additions & 0 deletions projects/gameboard-ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import { SignalRService } from './services/signalR/signalr.service';
import { LogService } from './services/log.service';
import { SystemNotificationsModule } from './system-notifications/system-notifications.module';
import { UserNavItemComponent } from './standalone/user/components/user-nav-item/user-nav-item.component';
import { markedOptionsFactory } from './core/config/marked.config';
import { MarkdownModule, MarkedOptions } from 'ngx-markdown';

@NgModule({
declarations: [
Expand All @@ -54,6 +56,7 @@ import { UserNavItemComponent } from './standalone/user/components/user-nav-item
SystemNotificationsModule,
CoreModule,
UtilityModule,
MarkdownModule.forRoot(),
TooltipModule.forRoot(),
TypeaheadModule.forRoot(),
ButtonsModule.forRoot(),
Expand All @@ -76,6 +79,10 @@ import { UserNavItemComponent } from './standalone/user/components/user-nav-item
useFactory: (config: ConfigService, log: LogService, userService: UserService) => new SignalRService(config, log, userService),
deps: [ConfigService, LogService, UserService],
},
{
provide: MarkedOptions,
useFactory: markedOptionsFactory
},
{
provide: [NotificationService],
useFactory: () => NotificationService,
Expand Down
4 changes: 4 additions & 0 deletions projects/gameboard-ui/src/app/core/config/marked.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export function markedOptionsFactory(): MarkedOptions {
return `<div class="text-center"><img class="img-fluid rounded" src=${href} alt="${text}" /></div>`;
};

renderer.link = (href, title, text) => {
return `<a role="link" target="_blank" href="${href}" rel="nofollow noopener noreferrer">${text}</a>`;
};

renderer.blockquote = (quote) => {
return `<blockquote class="blockquote">${quote}</blockquote>`;
};
Expand Down
15 changes: 3 additions & 12 deletions projects/gameboard-ui/src/app/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NgModule } from '@angular/core';
import { ModuleWithProviders, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
Expand All @@ -19,7 +19,6 @@ import { TypeaheadModule } from 'ngx-bootstrap/typeahead';

// other 3rd party modules
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { MarkdownModule, MarkedOptions } from 'ngx-markdown';
import { NgChartsModule } from 'ng2-charts';
// import luxon adapter for chartjs
import 'chartjs-adapter-luxon';
Expand Down Expand Up @@ -120,9 +119,9 @@ import { ObserverConsoleComponent } from './components/observer-console/observer
import { TicketLabelPickerComponent } from './components/ticket-label-picker/ticket-label-picker.component';
import { GameMapImageUrlPipe } from './pipes/game-map-image-url.pipe';
import { SpinnerComponent } from '@/standalone/core/components/spinner/spinner.component';
import { ErrorDivComponent } from '@/standalone/core/components/error-div/error-div.component';
import { IfHasPermissionDirective } from '@/standalone/directives/if-has-permission.directive';
import { ToSupportCodePipe } from '@/standalone/core/pipes/to-support-code.pipe';
import { MarkdownModule } from 'ngx-markdown';

const PUBLIC_DECLARATIONS = [
AbsoluteValuePipe,
Expand Down Expand Up @@ -260,19 +259,11 @@ const RELAYED_MODULES = [
HttpClientModule,
ProgressbarModule,
TooltipModule,
MarkdownModule.forRoot({
loader: HttpClient,
markedOptions: {
provide: MarkedOptions,
useFactory: markedOptionsFactory,
},
}),
PopoverModule.forRoot(),
TypeaheadModule.forRoot(),
...RELAYED_MODULES,

// standalones
ErrorDivComponent,
IfHasPermissionDirective,
SpinnerComponent,
ToSupportCodePipe
Expand All @@ -281,7 +272,7 @@ const RELAYED_MODULES = [
CommonModule,
...RELAYED_MODULES,
...PUBLIC_DECLARATIONS,
IfHasPermissionDirective,
IfHasPermissionDirective
]
})
export class CoreModule { }
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
[disabled]="feedbackForm.submitted! || isPreview" id="check-{{q.id}}-{{option}}"
[name]="q.id" [value]="option">

{{option}}{{q.specify && q.specify.key ==
<span [innerHTML]="option | markdown"></span>
{{q.specify && q.specify.key ==
option ? ": " +
(q.specify.prompt ? q.specify.prompt : "") : "" }}
</label>
Expand All @@ -87,10 +88,10 @@
[checked]="q.answer && q.answer!.indexOf(option) > -1"
[disabled]="feedbackForm.submitted!" id="check-{{q.id}}-{{option}}" [name]="option"
[value]="option">
<label class="form-check-label" for="{{option}}">{{option}}{{q.specify && q.specify.key
==
option ? ": " +
(q.specify.prompt ? q.specify.prompt : "") : "" }}</label>
<label class="form-check-label" for="{{option}}">
<span [innerHTML]="option | markdown"></span>
{{q.specify && q.specify.key == option ? ": " + (q.specify.prompt ?
q.specify.prompt : "") : "" }}</label>
<br>
<textarea *ngIf="q.specify && q.specify.key == option" rows="1" type="text"
[disabled]="feedbackForm.submitted!"
Expand Down Expand Up @@ -146,3 +147,7 @@
<ng-template #noResponse>
<em>(no response)</em>
</ng-template>

<ng-template #markdownOption let-content>
<span [innerHTML]="content | markdown"></span>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Component, inject, Input, OnDestroy, OnInit, ViewChild } from '@angular
import { CommonModule } from '@angular/common';
import { FormGroup, NgForm } from '@angular/forms';
import { DateTime } from 'luxon';
import { catchError, debounceTime, delay, filter, Observable, of, Subscription, tap } from 'rxjs';
import { catchError, debounceTime, delay, filter, of, Subscription, tap } from 'rxjs';
import { fa } from '@/services/font-awesome.service';
import { UserService as LocalUserService } from '@/utility/user.service';
import { FeedbackSubmissionAttachedEntity, FeedbackSubmissionView, FeedbackTemplateView } from '@/feedback/feedback.models';
Expand All @@ -12,6 +12,8 @@ import { ErrorDivComponent } from '@/standalone/core/components/error-div/error-
import { CoreModule } from '@/core/core.module';
import { FeedbackQuestion } from '@/api/feedback-models';
import { SpinnerComponent } from '@/standalone/core/components/spinner/spinner.component';
import { MarkedOptions } from 'ngx-markdown';
import { markedOptionsFactory } from '@/core/config/marked.config';

@Component({
selector: 'app-feedback-submission-form',
Expand All @@ -25,6 +27,9 @@ import { SpinnerComponent } from '@/standalone/core/components/spinner/spinner.c
ErrorDivComponent,
SpinnerComponent
],
providers: [
{ provide: MarkedOptions, useFactory: markedOptionsFactory }
],
templateUrl: './feedback-submission-form.component.html',
styleUrls: ['./feedback-submission-form.component.scss']
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@
tooltip="Preview this feedback template">
<fa-icon [icon]="fa.eye"></fa-icon>
</button>
<button type="button" class="btn btn-success btn-sm" (click)="handleEdit(selectedTemplate)">
<button *ngIf="selectedTemplate.responseCount == 0" type="button" class="btn btn-success btn-sm"
(click)="handleEdit(selectedTemplate)">
<fa-icon [icon]="fa.edit"></fa-icon>
</button>
<button type="button" class="btn btn-success btn-sm" [appCopyOnClick]="selectedTemplate.content"
tooltip="Copy this template's YAML configuration"
appCopyOnClickMessage="Copied this template's YAML to your clipboard.">
<fa-icon [icon]="fa.copy"></fa-icon>
</button>
<button type="button" class="btn btn-danger btn-sm" (click)="handleDelete(selectedTemplate)">
<fa-icon [icon]="fa.trash"></fa-icon>
</button>
Expand Down
Loading

0 comments on commit a717b12

Please sign in to comment.