Skip to content

Commit

Permalink
Refactor of enrollment report to deal with weird paging issues.
Browse files Browse the repository at this point in the history
  • Loading branch information
sei-bstein committed Nov 30, 2023
1 parent 92eb039 commit 18ca491
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 270 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { EnrollmentReportByGameRecord, EnrollmentReportByGameSponsor, EnrollmentReportFlatParameters } from '@/reports/components/reports/enrollment-report/enrollment-report.models';
import { ReportGame, ReportResults } from '@/reports/reports-models';
import { EnrollmentReportService } from '@/reports/components/reports/enrollment-report/enrollment-report.service';
Expand All @@ -13,17 +13,21 @@ import { RouterService } from '@/services/router.service';
templateUrl: './enrollment-report-by-game.component.html',
styleUrls: ['./enrollment-report-by-game.component.scss']
})
export class EnrollmentReportByGameComponent implements OnInit {
export class EnrollmentReportByGameComponent implements OnChanges {
@Input() parameters!: EnrollmentReportFlatParameters | null;

protected isLoading = false;
protected results: ReportResults<EnrollmentReportByGameRecord> | null = null;

constructor(
private enrollmentReportService: EnrollmentReportService,
private modalService: ModalConfirmService,
private routerService: RouterService) { }

async ngOnInit(): Promise<void> {
async ngOnChanges(changes: SimpleChanges): Promise<void> {
if (!changes.parameters)
return;

await this.loadData(this.parameters);
}

Expand All @@ -43,6 +47,8 @@ export class EnrollmentReportByGameComponent implements OnInit {
}

private async loadData(parameters: EnrollmentReportFlatParameters | null) {
this.isLoading = true;
this.results = await firstValueFrom(this.enrollmentReportService.getByGameData(parameters));
this.isLoading = false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<ng-container *ngIf="ctx">
<div class="results">
<table class="report-table table-hover gameboard-report">
<colgroup span="3"></colgroup>
<colgroup span="5"></colgroup>

<thead class="thead-light">
<tr class="headers super-headers-row">
<th scope="col" colSpan="3" class="group-header">Enrollment</th>
<th scope="col" colSpan="5" class="group-header">Performance</th>
</tr>
</thead>

<tbody>
<tr class="headers headers-row">
<td>Player</td>
<td>Enroll Date</td>
<td>Game</td>
<td>Cumulative Time</td>
<td>Attempted</td>
<td>Partially Solved</td>
<td>Completely Solved</td>
<td>Score</td>
</tr>

<tr class="data-row" *ngFor="let record of ctx.results.records">
<td>
<div class="d-flex align-items-center">
<app-player-field class="d-block"
[player]="{ userId: record.player.id, name: record.player.name, sponsor: record.player.sponsor, team: record.game.isTeamGame ? record.team : undefined }"></app-player-field>
</div>
</td>
<td class="report-field date">
<ng-container *ngIf="record.player.enrollDate else noValue">
<p>{{ record.player.enrollDate | shortdate }}</p>
<p class="subtle-info">{{ record.player.enrollDate | friendlyTime }}</p>
</ng-container>
</td>
<td>
<p class="game-name m-0">{{ record.game.name }}</p>
<p class="game-info subtle-info">
<span *ngIf="record.game.series">
<app-parameter-change-link
[config]="{ parameterChange: { series: record.game.series } }">
{{ record.game.series }}
</app-parameter-change-link>
::
</span>
<span *ngIf="record.game.season">
<app-parameter-change-link
[config]="{ parameterChange: { seasons: record.game.season }}">
{{ record.game.season }}
</app-parameter-change-link>
::
</span>
<span *ngIf="record.game.track; else noValue">
<app-parameter-change-link [config]="{ parameterChange: { tracks: record.game.track }}">
{{ record.game.track }}
</app-parameter-change-link>
</span>
</p>
</td>
<td class="report-field numerical">
<p class="report-field" *ngIf="record.playTime.durationMs; else noValue">
{{ record.playTime.durationMs | msToDuration }}
</p>
</td>
<td class="report-field numerical">
<p class="challenges-deployed">
<span [class.tooltipped-value]="record.challenges.length"
(click)="record.challenges && showChallengesDetail(record, 'deployed')">
{{ record.challenges | arrayToCount }}
</span>
</p>
</td>
<td class="report-field numerical">
<p class="count challenges-partial">
<span [class.tooltipped-value]="record.challengesPartiallySolvedCount"
(click)="record.challengesPartiallySolvedCount && showChallengesDetail(record, 'partial')">
{{ record.challengesPartiallySolvedCount }}
</span>
</p>
</td>
<td class="report-field numerical">
<p class="count challenges-complete">
<span [class.tooltipped-value]="record.challengesCompletelySolvedCount"
(click)="record.challengesCompletelySolvedCount && showChallengesDetail(record, 'complete')">
{{ record.challengesCompletelySolvedCount }}
</span>
</p>
</td>
<td class="report-field numerical">
<p>
<span [class.tooltipped-value]="(record.score || 0) > 0"
(click)="(record.score || 0) && showScoreBreakdown(record)">
{{ record.score }}
</span>
</p>
</td>
</tr>
</tbody>
</table>

<div class="pager-container d-flex justify-content-end mt-4">
<div class="flex-grow-1"></div>
<app-select-pager [itemCount]="ctx.results.paging.itemCount" [pageSize]="ctx.results.paging.pageSize || 20"
(change)="handlePagingChange($event)"></app-select-pager>
</div>
</div>
</ng-container>

<ng-template #noRecords>
<app-no-report-records recordDescription="enrollments"></app-no-report-records>
</ng-template>

<ng-template #noValue>
<app-report-field-no-value></app-report-field-no-value>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { EnrollmentReportFlatParameters, EnrollmentReportRecord } from '../enrollment-report.models';
import { MarkdownHelpersService } from '@/services/markdown-helpers.service';
import { ModalConfirmService } from '@/services/modal-confirm.service';
import { ReportResults } from '@/reports/reports-models';
import { PagingArgs } from '@/api/models';
import { RouterService } from '@/services/router.service';
import { EnrollmentReportService } from '../enrollment-report.service';
import { firstValueFrom } from 'rxjs';

interface EnrollmentReportSummaryContext {
results: ReportResults<EnrollmentReportRecord>;
}

@Component({
selector: 'app-enrollment-report-summary',
templateUrl: './enrollment-report-summary.component.html',
styleUrls: ['./enrollment-report-summary.component.scss']
})
export class EnrollmentReportSummaryComponent implements OnChanges {
@Input() parameters: EnrollmentReportFlatParameters | null = null;

protected ctx?: EnrollmentReportSummaryContext;
protected isLoading = false;

constructor(
private enrollmentReportService: EnrollmentReportService,
private markdownHelpersService: MarkdownHelpersService,
private modalService: ModalConfirmService,
private routerService: RouterService
) { }

async ngOnChanges(changes: SimpleChanges) {
if (!changes.parameters)
return;

this.isLoading = true;
this.ctx = {
results: await firstValueFrom(this.enrollmentReportService.getReportData(this.parameters || {}))
};
this.isLoading = false;
}

protected handlePagingChange(request: PagingArgs) {
this.routerService.updateQueryParams({ parameters: { ...request } });
}

protected showChallengesDetail(record: EnrollmentReportRecord, challengeStatus: "deployed" | "partial" | "complete") {
let challenges: { name: string, score?: number, maxPossiblePoints: number }[] = [];

switch (challengeStatus) {
case "partial":
challenges = record.challenges.filter(c => c.result == "partial");
break;
case "complete":
challenges = record.challenges.filter(c => c.result == "success");
break;
default:
challenges = record.challenges;
}

this.modalService.open({
bodyContent: this
.markdownHelpersService
.arrayToBulletList(challenges.map(c => `${c.name} (${c.score || "--"}/${c.maxPossiblePoints}) possible points`)),
renderBodyAsMarkdown: true,
title: `${record.player.name}: Challenges ${challengeStatus.substring(0, 1).toUpperCase()}${challengeStatus.substring(1)}`,
});
}

protected showScoreBreakdown(record: EnrollmentReportRecord) {
const scoreItems: { points: number, source: string }[] = [];

for (const challenge of record.challenges) {
if (challenge.score)
scoreItems.push({ points: challenge.score, source: `**${challenge.name}**, ${challenge.result.toString()} solve` });

if (challenge.manualChallengeBonuses?.length) {
for (const bonus of challenge.manualChallengeBonuses) {
scoreItems.push({ points: bonus.points, source: `**Manual bonus**, ${bonus.description}` });
}
}
}

this.modalService.open({
bodyContent: this
.markdownHelpersService
.arrayToBulletList(scoreItems.map(i => `${i.points} (${i.source})`)),
renderBodyAsMarkdown: true,
title: `${record.player.name}: Score Breakdown`
});
}
}
Loading

0 comments on commit 18ca491

Please sign in to comment.