diff --git a/projects/gameboard-ui/src/app/admin/admin.module.ts b/projects/gameboard-ui/src/app/admin/admin.module.ts
index 635365db0..912dbcba3 100644
--- a/projects/gameboard-ui/src/app/admin/admin.module.ts
+++ b/projects/gameboard-ui/src/app/admin/admin.module.ts
@@ -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';
@@ -141,7 +140,6 @@ import { UserPickerComponent } from '@/standalone/users/user-picker/user-picker.
SiteOverviewStatsComponent,
AdminOverviewComponent,
SupportSettingsComponent,
- FeedbackEditorComponent,
ExtendTeamsModalComponent,
ActiveTeamsModalComponent,
AdminEnrollTeamModalComponent,
diff --git a/projects/gameboard-ui/src/app/admin/components/feedback-editor/feedback-editor.component.html b/projects/gameboard-ui/src/app/admin/components/feedback-editor/feedback-editor.component.html
deleted file mode 100644
index fa727cf1b..000000000
--- a/projects/gameboard-ui/src/app/admin/components/feedback-editor/feedback-editor.component.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
DEPRECATED
-
-
-
-
-
-
diff --git a/projects/gameboard-ui/src/app/admin/components/feedback-editor/feedback-editor.component.ts b/projects/gameboard-ui/src/app/admin/components/feedback-editor/feedback-editor.component.ts
deleted file mode 100644
index 09513d9fa..000000000
--- a/projects/gameboard-ui/src/app/admin/components/feedback-editor/feedback-editor.component.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-import { BehaviorSubject, debounceTime, tap } from 'rxjs';
-import { FeedbackTemplate } from '@/api/feedback-models';
-import { UnsubscriberService } from '@/services/unsubscriber.service';
-import { ModalConfirmService } from '@/services/modal-confirm.service';
-
-@Component({
- selector: 'app-feedback-editor',
- templateUrl: './feedback-editor.component.html',
- styleUrls: ['./feedback-editor.component.scss'],
- providers: [UnsubscriberService]
-})
-export class FeedbackEditorComponent implements OnInit {
- @Input() feedbackConfig = "";
- @Output() templateChange = new EventEmitter();
-
- protected boundYaml = "";
- protected sampleConfig?: string;
- protected validationMessages: string[] = [];
-
- private templateChangeSubject$ = new BehaviorSubject(null);
-
- public constructor(
- private modalService: ModalConfirmService,
- private unsub: UnsubscriberService) {
- this.unsub.add(
- this.templateChangeSubject$
- .pipe(
- debounceTime(1000),
- tap(t => this.templateChange.emit(t))
- )
- .subscribe()
- );
- }
-
- async ngOnInit() {
- this.boundYaml = this.feedbackConfig;
- }
-
- protected handleAboutFeedbackClick() {
- this.modalService.openConfirm({
- bodyContent: `You can use this box to configure questions that will automatically be presented to players upon conclusion of a challenge or game. Enter valid YAML to set these up.
-
-Each question requires these properties at minumum:
-
-- **id:** A unique identifying string for the question.
-- **prompt:** The question that players will answer (e.g. "If you could change one thing about this challenge, what would it be?")
-- **type:** The type of the response you want to collect from players. Valid options are **likert**, **text**, **selectOne**, and **selectMany**.
-
-Depending on the value of **type**, additional configuration may be required. For non-text questions, you'll also need:
-
-- **min:** The minimum numeric value of the question's scale (e.g. 1 on a scale from 1-10)
-- **minLabel:** The label for the lowest value of the question's scale (e.g. "Very Easy")
-- **max:** The maximum numeric value of the question's scale (e.g. 10 on a scale from 10)
-- **maxLabel:** The label for the lowest value of the question's scale (e.g. "Very Hard")`,
- hideCancel: true,
- renderBodyAsMarkdown: true,
- modalClasses: ["modal-xl"],
- title: "About feedback templates"
- });
- }
-}
diff --git a/projects/gameboard-ui/src/app/admin/components/game-center/game-center-settings/game-center-settings.component.html b/projects/gameboard-ui/src/app/admin/components/game-center/game-center-settings/game-center-settings.component.html
index 7462a1fa3..5589c676b 100644
--- a/projects/gameboard-ui/src/app/admin/components/game-center/game-center-settings/game-center-settings.component.html
+++ b/projects/gameboard-ui/src/app/admin/components/game-center/game-center-settings/game-center-settings.component.html
@@ -154,9 +154,6 @@
-
-
@@ -303,7 +300,7 @@
Execution
-
game.gameEnd" class="row form-group">
+
game.gameEnd" class="row form-group">
The game's open date must be less than its close date.
@@ -322,6 +319,18 @@
Execution
total concurrent sessions allowed for game
+
+
+
+
+ If set, shows a warning when the number of currently-available sessions drops below this
+ number
+
+
+
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)
diff --git a/projects/gameboard-ui/src/app/api/feedback.service.ts b/projects/gameboard-ui/src/app/api/feedback.service.ts
index 363694dd3..790606971 100644
--- a/projects/gameboard-ui/src/app/api/feedback.service.ts
+++ b/projects/gameboard-ui/src/app/api/feedback.service.ts
@@ -19,8 +19,11 @@ export class FeedbackService {
private url = '';
private yamlService = inject(YamlService);
- private _deleted$ = new Subject();
- public deleted$ = this._deleted$.asObservable();
+ private _templatesDeleted$ = new Subject();
+ public templatesDeleted$ = this._templatesDeleted$.asObservable();
+
+ private _templatesUpdated$ = new Subject();
+ public templatesUpdated$ = this._templatesUpdated$.asObservable();
constructor(
config: ConfigService,
@@ -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 {
diff --git a/projects/gameboard-ui/src/app/api/game-models.ts b/projects/gameboard-ui/src/app/api/game-models.ts
index 8129cd4eb..c401e17fd 100644
--- a/projects/gameboard-ui/src/app/api/game-models.ts
+++ b/projects/gameboard-ui/src/app/api/game-models.ts
@@ -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";
@@ -36,6 +37,7 @@ export interface GameDetail {
maxTeamSize: number;
sessionMinutes: number;
sessionLimit: number;
+ sessionAvailabilityWarningThreshold?: number;
gamespaceLimitPerSession: number;
isPublished: boolean;
requireSponsoredTeam: boolean;
@@ -169,6 +171,12 @@ export interface UpsertExternalGameHost {
teamExtendedEndpoint?: string;
}
+export interface GameSessionAvailibilityResponse {
+ nextSessionEnd?: DateTime
+ sessionsAvailable: number
+}
+
+
export interface GetExternalTeamDataResponse {
teamId: string;
externalUrl: string;
diff --git a/projects/gameboard-ui/src/app/api/game.service.ts b/projects/gameboard-ui/src/app/api/game.service.ts
index d560ca7e6..fda7bbb53 100644
--- a/projects/gameboard-ui/src/app/api/game.service.ts
+++ b/projects/gameboard-ui/src/app/api/game.service.ts
@@ -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';
@@ -83,6 +83,10 @@ export class GameService {
return firstValueFrom(this.http.post(`${this.url}/game/${gameId}/resources`, { teamIds }));
}
+ public getSessionAvailability(gameId: string): Promise {
+ return firstValueFrom(this.http.get(`${this.url}/game/${gameId}/session-availability`));
+ }
+
public getSyncStartState(gameId: string): Observable {
return this.http.get(`${this.url}/game/${gameId}/ready`);
}
diff --git a/projects/gameboard-ui/src/app/app.module.ts b/projects/gameboard-ui/src/app/app.module.ts
index 958f6998a..b70149f8f 100644
--- a/projects/gameboard-ui/src/app/app.module.ts
+++ b/projects/gameboard-ui/src/app/app.module.ts
@@ -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: [
@@ -54,6 +56,7 @@ import { UserNavItemComponent } from './standalone/user/components/user-nav-item
SystemNotificationsModule,
CoreModule,
UtilityModule,
+ MarkdownModule.forRoot(),
TooltipModule.forRoot(),
TypeaheadModule.forRoot(),
ButtonsModule.forRoot(),
@@ -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,
diff --git a/projects/gameboard-ui/src/app/core/config/marked.config.ts b/projects/gameboard-ui/src/app/core/config/marked.config.ts
index 2e42c6948..56091d151 100644
--- a/projects/gameboard-ui/src/app/core/config/marked.config.ts
+++ b/projects/gameboard-ui/src/app/core/config/marked.config.ts
@@ -7,6 +7,10 @@ export function markedOptionsFactory(): MarkedOptions {
return ``;
};
+ renderer.link = (href, title, text) => {
+ return `${text}`;
+ };
+
renderer.blockquote = (quote) => {
return `