Skip to content

Commit

Permalink
v3.24.0-beta1 (#202)
Browse files Browse the repository at this point in the history
* Fix tracking of active prac challenge

* Minor cleanup and improved console logging

* Bug fixes and challenge report tags filter.

* Fix event horizon, game center teams, enrollment report stat summary, progress widget

* Fix issue with past games not showing ongoing ones

* WIP question sets

* More question set stuff

* Improvements to OIDC experience and support for auto log in/out.

* merge from next

* Add favicon defaults. Update theme

* Fix settings link bug, more thing

* Theme stuff

* Add settings link to user menu

* Theme stuff

* Revert "Theme stuff"

This reverts commit a26c715.

* Revert "Add settings link to user menu"

This reverts commit d640ee8.

* Revert "Theme stuff"

This reverts commit c3e3b69.

* Revert "Fix settings link bug, more thing"

This reverts commit f96dc59.

* Revert "Add favicon defaults. Update theme"

This reverts commit 1b2e5a7.

* Rollback and reimplement oauth changes

* Update oidc settings to match topo

* Cleanup

* Update dev env settings

* Improvements to OIDC experience and support for auto log in/out.

* Restore ux improvements

* More retheme

* Retheming and initial work on name improvements.

* Update gh action versions

* Retheming and name mgmt

* Styling stuff

* Styling stuff

* Move challenge markdown

* Un 'fix' start practice button

* minor name fixes

* Name cleanup

* Finish merge from main

* More theming and fix to GBAPI#502

* More theming and markdown fix

* More style fixes

* Partially address GBAPI#254

* Return question example answers

* Auto-select first available uncompleted tab

* Standard challenge panel sticky enhancements

* Styling

* Roll back login experience changes

* Settings bug fix and hide rename cert

* Minor theme stuff

* Theming
  • Loading branch information
sei-bstein authored Nov 7, 2024
1 parent 3a71ae2 commit 3054a32
Show file tree
Hide file tree
Showing 31 changed files with 240 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ <h3>Warning</h3>
<div class="d-flex align-items-center">
<h4 class="card-title flex-grow w-100">
{{team.name}}
<span *ngIf="team.isReady" class="ml-2 mb-2 badge badge-info">Ready</span>
<span *ngIf="team.isReady" class="ml-2 mb-2 badge badge-success">Ready</span>
</h4>
<app-deployment-admin-team-context-menu class="d-block" [team]="team" [isReady]="team.isReady"
(teamReadyStateToggled)="handleTeamReadyStateChanged(team.id)"></app-deployment-admin-team-context-menu>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</div>
<div secondaryDetail *ngIf="user.activeChallenge">
Playing "{{ user.activeChallenge.name }}" now
<div class="badge badge-info">
<div class="badge badge-success">
<app-countdown [countdownTo]="user.activeChallengeEndTimestamp"></app-countdown>
remaining
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,9 @@
<label for="logo-input">Card Image</label>
<p class="help-text">
Select an image for the game card shown in the public-facing portion of the app. For best
results, use an
image with an aspect ratio of <strong>1:1.44</strong> (e.g. 750x1080).
results, use an image with an aspect ratio of <strong>1:1.44</strong> (e.g. 750x1080).
</p>
<app-dropzone inputId="logo-input" btnClass="btn btn-sm btn-primary"
<app-dropzone inputId="logo-input" btnClass="btn btn-sm btn-succes"
(dropped)="upload($event, 'card')" [clickToLaunchFilePicker]="false">
<button class="btn btn-sm btn-warning mx-2 mb-2" (click)="clearImage()">
<fa-icon [icon]="fa.trash"></fa-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ <h4 class="fs-10" *ngIf="ctx.executionWindow.start && ctx.executionWindow.end">
<div *ngIf="!ctx.isPractice && (ctx.isLive || ctx.isRegistrationActive)"
class="d-flex align-content-center mt-2">
<div *ngIf="!ctx.isPublished" class="badge badge-warning mr-2 text-upper p-1">Unpublished</div>
<div *ngIf="ctx.isRegistrationActive" class="badge badge-info text-upper mr-2 p-1">
<div *ngIf="ctx.isRegistrationActive" class="badge badge-success text-upper mr-2 p-1">
Registration Available
</div>
<div *ngIf="ctx.isLive" class="badge badge-info text-upper p-1">Live</div>
<div *ngIf="ctx.isLive" class="badge badge-success text-upper p-1">Live</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { AdminService } from '@/api/admin.service';
import { Game } from '@/api/game-models';
import { GameService } from '@/api/game.service';
Expand Down Expand Up @@ -29,11 +29,17 @@ export class GameCenterComponent {
private adminService: AdminService,
private appTitle: AppTitleService,
private gameService: GameService,
private localUserService: UserService) {
private localUserService: UserService,
private router: Router) {

unsub.add(this.activatedRoute.paramMap.subscribe(async paramMap => {
const gameId = paramMap.get("gameId") || this.gameCenterCtx?.id;
if (gameId && gameId != this.gameCenterCtx?.id)
await this.load(gameId);

if (this.activatedRoute.firstChild?.routeConfig?.path) {
this.selectedTab = (this.activatedRoute.firstChild.routeConfig.path || "teams") as GameCenterTab;
}
}));

unsub.add(interval(30000).subscribe(async () => {
Expand Down
8 changes: 6 additions & 2 deletions projects/gameboard-ui/src/app/api/certificates.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ export class CertificatesService {
private domSanitizer: DomSanitizer,
private http: HttpClient) { }

getCertificateImage(mode: PlayerMode, userId: string, challengeSpecId: string) {
return this.http.get(this.apiUrl.build(`user/${userId}/certificates/${this.coerceCompetitionModeString(mode)}/${challengeSpecId}`), { responseType: "arraybuffer" }).pipe(
getCertificateImage(mode: PlayerMode, userId: string, challengeSpecId: string, requestedName?: string) {
const queryArgs = requestedName ? `?requestedNameOverride=${requestedName}` : "";

return this.http.get(this.apiUrl.build(`user/${userId}/certificates/${this.coerceCompetitionModeString(mode)}/${challengeSpecId}${queryArgs}`), {
responseType: "arraybuffer"
}).pipe(
map(response => new Blob([response])),
map(blob => window.URL.createObjectURL(blob)),
map(url => this.domSanitizer.bypassSecurityTrustUrl(url))
Expand Down
4 changes: 4 additions & 0 deletions projects/gameboard-ui/src/app/api/settings.models.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export interface GameboardSettings {
nameChangeIsEnabled: boolean;
}

export interface GetGameboardSettingsResponse {
settings: GameboardSettings;
}
7 changes: 4 additions & 3 deletions projects/gameboard-ui/src/app/api/settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import { ApiUrlService } from '@/services/api-url.service';
import { GameboardSettings } from './settings.models';
import { GameboardSettings, GetGameboardSettingsResponse } from './settings.models';

@Injectable({ providedIn: 'root' })
export class SettingsService {
constructor(private apiUrl: ApiUrlService, private http: HttpClient) { }

get(): Promise<GameboardSettings> {
return firstValueFrom(this.http.get<GameboardSettings>(this.apiUrl.build("settings")));
async get(): Promise<GameboardSettings> {
const result = await firstValueFrom(this.http.get<GetGameboardSettingsResponse>(this.apiUrl.build("settings")));
return result.settings;
}
}
26 changes: 16 additions & 10 deletions projects/gameboard-ui/src/app/components/nav/nav.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@
</ng-container>
<a *ngIf="isPracticeModeEnabled" class="btn btn-link text-success mx-1" routerLinkActive="active"
[routerLink]="['practice']">Practice</a>
<ng-container *ngIf="user$ | async as user;">
<ng-container *ngIf="user$ | async as user; else unauthed">
<a class="btn btn-link text-success mx-1" routerLinkActive="active" [routerLink]="['support']">
<span>Support</span>
<app-support-pill></app-support-pill>
</a>

<a class="btn btn-link text-success mx-1" routerLinkActive="text-info" [routerLink]="profileUrl">Profile</a>

<div *appIfHasPermission="'Reports_View'" class="d-flex align-items-center">
<a class="btn btn-link text-success-mx-1" routerLinkActive="active" [routerLink]="['reports']">
Reports
</a>
</div>

<ng-container *ngIf="(user$ | async)?.id">
<a *appIfHasPermission="'Admin_View'" class="btn btn-link text-success mx-1" routerLinkActive="active"
[routerLink]="['/admin']">Admin</a>
</ng-container>
<a *appIfHasPermission="'Admin_View'" class="btn btn-link text-success mx-1" routerLinkActive="active"
[routerLink]="['/admin']">Admin</a>
<a class="btn btn-link text-success mx-1" (click)="handleLogOut()">Log out</a>
</ng-container>

<app-user-nav-item></app-user-nav-item>
</div>
</nav>

Expand All @@ -50,28 +50,34 @@
<li *ngIf="isPracticeModeEnabled">
<a routerLinkActive="active" [routerLink]="['practice']">Practice</a>
</li>
<ng-container *ngIf="user$ | async as user">
<ng-container *ngIf="user$ | async as user; else unauthed">
<li>
<a routerLinkActive="active" [routerLink]="['support']">
<span>Support</span>
<app-support-pill></app-support-pill>
</a>
</li>
<li>
<a routerLinkActive="active" [routerLink]="profileUrl">Profile</a>
</li>
<li *appIfHasPermission="'Reports_View'">
<a routerLinkActive="active" [routerLink]="['/reports']">
Reports
</a>
<span class="badge badge-info">[BETA]</span>
</li>
<li *appIfHasPermission="'Admin_View'">
<a routerLinkActive="active" [routerLink]="['/admin']">
Admin
</a>
</li>
<li>
<app-user-nav-item></app-user-nav-item>
<a class="text-success" (click)=" handleLogOut()">Log out</a>
</li>
</ng-container>
</ul>
</div>
</nav>

<ng-template #unauthed>
<div role="button" class="btn btn-link text-success mx-1" (click)="handleLogIn()">Log in</div>
</ng-template>
12 changes: 11 additions & 1 deletion projects/gameboard-ui/src/app/components/nav/nav.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Component, inject, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { TocFile, TocService } from '@/api/toc.service';
import { ApiUser } from '@/api/user-models';
import { UserService as LocalUser } from '@/utility/user.service';
import { PracticeService } from '@/services/practice.service';
import { UnsubscriberService } from '@/services/unsubscriber.service';
import { Component, inject, OnInit } from '@angular/core';
import { fa } from '@/services/font-awesome.service';
import { AuthService } from '@/utility/auth.service';
import { RouterService } from '@/services/router.service';
Expand All @@ -27,6 +27,7 @@ export class AppNavComponent implements OnInit {
protected fa = fa;
protected isCollapsed = false;
protected isPracticeModeEnabled = false;
protected profileUrl = "";

constructor(
private localUser: LocalUser,
Expand All @@ -38,6 +39,7 @@ export class AppNavComponent implements OnInit {
async ngOnInit(): Promise<void> {
this.user$ = this.localUser.user$;
this.toc$ = this.toc.toc$;
this.profileUrl = this.routerService.getProfileUrl();

this.isPracticeModeEnabled = await this.practiceService.isEnabled();
this.unsub.add(this.practiceService.isEnabled$.subscribe(isEnabled => this.updatePracticeModeEnabled(isEnabled)));
Expand All @@ -46,4 +48,12 @@ export class AppNavComponent implements OnInit {
private updatePracticeModeEnabled(isEnabled: boolean) {
this.isPracticeModeEnabled = isEnabled;
}

protected async handleLogIn() {
await this.authService.login();
}

protected handleLogOut() {
this.authService.logout();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ <h3 class="m-0 p-0 flex-grow-1">{{ctx.canManage ? 'Tickets' : 'My Tickets'}}</h3
</div>
<div class="col-3 align-self-center d-flex flex-column justify-content-between">
<div>
<span class="badge mr-2" style="font-size: 90%" [class.badge-info]="ticket.status == 'Open' || !ticket.status"
<span class="badge mr-2" style="font-size: 90%"
[class.badge-warning]="ticket.status == 'Open' || !ticket.status"
[class.badge-success]="ticket.status == 'In Progress'" [class.badge-dark]="ticket.status == 'Closed'">
{{ticket.status}}
</span>&nbsp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ export class TicketStatusBadgePipe implements PipeTransform {

switch (value.toLowerCase()) {
case "open":
cssClass = "badge-success";
cssClass = "badge-info";
break;
case "in progress":
cssClass = "badge-info";
cssClass = "badge-success";
break;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<ng-container *ngIf="buttonText && buttonUrl">
<a class="btn btn-primary btn-lg" [class.disabled]="!isEnabled" [routerLink]="buttonUrl">
<a class="btn btn-success btn-lg" [class.disabled]="!isEnabled" [routerLink]="buttonUrl">
{{buttonText}}
</a>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ <h2 class="text-muted fw-bold">Your game is starting...</h2>
<h1>{{ctx.game.name}}</h1>
<div class="detail-badges">
<ul>
<li class="badge badge-info">
<li class="badge badge-success">
{{ctx.sessionLengthInMinutes}} minutes
</li>
<li class="badge badge-info">
<li class="badge badge-success">
{{ctx.player.session!.beginDate | friendlyTime}} - {{ctx.player.session!.endDate |
friendlyTime}}
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ <h3 class="font-weight-bold text-muted">To start a challenge, click a circle on
<input type="number" class="form-control" [(ngModel)]="variant">
</div>
</ng-container>
<app-confirm-button btnClass="btn btn-primary" (confirm)="launch(spec)" #startChallengeConfirmButton>
<app-confirm-button btnClass="btn btn-success" (confirm)="launch(spec)" #startChallengeConfirmButton>
<span>Start Challenge</span>
</app-confirm-button>
<div *ngIf="!ctx.isPractice">
Expand Down Expand Up @@ -114,23 +114,26 @@ <h3 *ngIf="spec.instance!.state.challenge?.questions?.length">Challenge Question
<app-gamespace-quiz [challengeId]="spec.instance!.state.id" [spec]="spec!" [session]="ctx.session"
(graded)="graded()"></app-gamespace-quiz>

<div *ngIf="spec.instance?.id" class="text-right m-4">
<div *ngIf="spec.instance?.id" class="d-flex align-items-baseline my-4">
<div class="flex-grow-1">
Support Code:
<span class="text-success m-2 p-2 cursor-pointer" tooltip="Click to copy your challenge support code"
appCopyOnClick>
{{ spec.instance?.id | slice:0:8 }} {{ spec.tag }}
</span>
</div>

<div class="mb-1">
Need Challenge Support?
<span class="m-2 p-2">
<span class="mr-2">
Need Challenge Support?
</span>
<span class="my-2 py-2">
<a class="btn btn-success" target="_blank" [routerLink]="['/support/create']"
[queryParams]="{cid: spec.instance?.id!}">
<span>Create Ticket</span>
</a>
</span>
</div>
<div>
Support Code:
<span class="text-success m-2 p-2 cursor-pointer" tooltip="Click to copy your challenge support code"
appCopyOnClick>
{{ spec.instance?.id | slice:0:8 }} {{ spec.tag }}
</span>
</div>
</div>

<div *ngIf="spec.instance!.state.challenge && ctx.game.feedbackTemplate?.challenge?.length">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="card" *ngIf="ctx">
<div class="card-body">

<div class="text-info card-text mb-2" *ngIf="isRegistrationOpen">
<div class="text-success card-text mb-2" *ngIf="isRegistrationOpen">
<span *ngIf="ctx.game.registration.isBefore">Enrollment opens in </span>
<span *ngIf="ctx.game.registration.isDuring">Enrollment closes in </span>
<span *ngIf="ctx.game.registration.isAfter">Enrollment is closed </span>
Expand Down Expand Up @@ -53,7 +53,7 @@
<div class="d-flex align-items-center justify-content-center mt-5"
*ngIf="ctxDelayed$ | async as delay; else loading">
<div class="text-center" *ngIf="!!ctx.user.name && hasSelectedSponsor">
<app-confirm-button btnClass="btn btn-primary btn-lg mr-4"
<app-confirm-button btnClass="btn btn-success btn-lg mr-4"
[disabled]="!isRegistrationOpen && !canStandardEnroll && !canAdminEnroll" [tooltip]="enrollTooltip"
(confirm)="handleEnroll(ctx.user.id, ctx.game.id)">
<fa-icon [icon]="faEdit"></fa-icon>
Expand All @@ -77,7 +77,7 @@
<div *ngIf="isManager$ | async" class="form-group mb-0">
<ng-container *ngIf="ctx.player | hasPendingName">
<ng-container *ngIf="!ctx.player.nameStatus">
<div class="alert alert-info">
<div class="alert alert-warning">
Your name is pending approval from an administrator. Return later or select "Update" to see if it's
been approved.
</div>
Expand All @@ -96,7 +96,7 @@
<input id="name-input" type="text" class="form-control" [(ngModel)]="ctx.player.name" maxlength="64"
autocomplete="off" placeholder="display name">
<div class="input-group-append">
<button id="name-update-button" class="btn btn-info" (click)="update(ctx.player)"
<button id="name-update-button" class="btn btn-success" (click)="update(ctx.player)"
[disabled]="!ctx.player.name">
<span>Update</span>
</button>
Expand All @@ -121,11 +121,11 @@ <h3>Captains</h3>
<input type="text" disabled id="token-input" class="form-control" [(ngModel)]="code" maxlength="64"
placeholder="Generate a code ->">
<div class="input-group-append gb-button-group">
<button class="btn btn-info" (click)="invite(ctx.player)">
<button class="btn btn-success" (click)="invite(ctx.player)">
<fa-icon [icon]="fa.arrowsSpin"></fa-icon>
</button>

<button *ngIf="code" class="btn btn-info button-group-button" [appCopyOnClick]="code"
<button *ngIf="code" class="btn btn-success button-group-button" [appCopyOnClick]="code"
appCopyOnClickMessage="Copied your invitation code to your clipboard.">
<fa-icon [icon]="fa.copy"></fa-icon>
</button>
Expand All @@ -147,7 +147,7 @@ <h3>Team Up</h3>
<input type="text" id="token-input" class="form-control" [(ngModel)]="token" maxlength="64"
placeholder="Enter your invitation code here to join a team">
<div class="input-group-append">
<button class="btn btn-info" [disabled]="!token" (click)="redeem(ctx.player)">
<button class="btn btn-success" [disabled]="!token" (click)="redeem(ctx.player)">
<fa-icon [icon]="faPaste"></fa-icon>
<span>Join</span>
</button>
Expand Down Expand Up @@ -199,6 +199,6 @@ <h3>Team Up</h3>
<ng-template #invitationGenerated>
<div class="invitation-generated-wrapper">
<span class="text-muted">Your invitation code: </span>
<strong [appCopyOnClick]="code" class="invitation-code text-info fw-bold cursor-pointer">{{code}}</strong>
<strong [appCopyOnClick]="code" class="invitation-code text-success fw-bold cursor-pointer">{{code}}</strong>
</div>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<div class="player-name mr-auto align-middle fs-11 mb-4">
<p class="m-0">{{teamMember.userApprovedName}}</p>
<ul class="player-tags p-0">
<li *ngIf="teamMember.isManager" class="badge badge-info mr-4">Captain</li>
<li *ngIf="teamMember.isManager" class="badge badge-success mr-4">Captain</li>
<li *ngIf="isSyncStartGame && teamMember.isReady" class="badge badge-success mr-4">Ready</li>
<li *ngIf="isSyncStartGame && !teamMember.isReady" class="badge badge-danger mr-4">Not Ready</li>
<li *ngIf="teamMember | hasPendingName" class="badge badge-secondary">
Expand Down
Loading

0 comments on commit 3054a32

Please sign in to comment.