Various improvements to game management interface

This commit is contained in:
grimsi
2022-08-05 18:34:24 +02:00
parent 22c8e99f38
commit 98197fc4a6
23 changed files with 446 additions and 170 deletions
@@ -0,0 +1,50 @@
<div class="mat-elevation-z8">
<table mat-table matSort matTableFilter [dataSource]="dataSource" [exampleEntity]="filter" [debounceTime]="0">
<!-- Path column -->
<ng-container matColumnDef="path">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Path</th>
<td mat-cell *matCellDef="let element"> {{element.path}} </td>
</ng-container>
<!-- Game column -->
<ng-container matColumnDef="game">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Game</th>
<td mat-cell *matCellDef="let element"> {{element.title}} ({{getFullYearFromTimestamp(element.releaseDate)}})</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>
<button mat-icon-button (click)="toggleShowOnlyUnconfirmedMatches()">
<mat-icon *ngIf="showOnlyUnconfirmedMatches" matTooltip="Show all game mappings" matTooltipPosition="below" color="warn">playlist_add_check_circle</mat-icon>
<mat-icon *ngIf="!showOnlyUnconfirmedMatches" matTooltip="Show only unconfirmed game mappings" matTooltipPosition="below" fontSet="material-icons-outlined">playlist_add_check_circle</mat-icon>
</button>
<button mat-icon-button (click)="refreshMappedGamesList()">
<mat-icon matTooltip="Refresh game list" matTooltipPosition="below">refresh</mat-icon>
</button>
</th>
<!-- Action column -->
<td mat-cell *matCellDef="let element">
<button mat-icon-button (click)="toggleConfirmGameMapping(element)" [color]="element.confirmedMatch ? 'primary' : 'warn'">
<mat-icon [matTooltip]="element.confirmedMatch ? 'Unconfirm match':'Confirm match'" matTooltipPosition="below">check</mat-icon>
</button>
<button mat-icon-button (click)="openCorrectMappingDialog(element)">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button (click)="deleteGameMapping(element)">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator
[length]="dataSource?.data?.length"
[pageIndex]="0"
[pageSize]="15"
[pageSizeOptions]="[10, 15, 25, 50]">
</mat-paginator>
</div>
@@ -0,0 +1,10 @@
table {
min-width: 50vw;
}
.mat-column-path {
width: 50%;
}
.mat-column-game {
width: 35%;
}
@@ -0,0 +1,34 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MappedGamesTableComponent } from './mapped-games-table.component';
describe('MappedGamesTableComponent', () => {
let component: MappedGamesTableComponent;
let fixture: ComponentFixture<MappedGamesTableComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ MappedGamesTableComponent ],
imports: [
NoopAnimationsModule,
MatPaginatorModule,
MatSortModule,
MatTableModule,
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MappedGamesTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should compile', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,87 @@
import {AfterViewInit, Component, Input, OnChanges, SimpleChanges, ViewChild} from '@angular/core';
import {MatPaginator} from '@angular/material/paginator';
import {MatSort} from '@angular/material/sort';
import {MatTable, MatTableDataSource} from '@angular/material/table';
import {DetectedGameDto} from "../../models/dtos/DetectedGameDto";
import {GamesService} from "../../services/games.service";
import {LibraryManagementService} from "../../services/library-management.service";
import {DialogService} from "../../services/dialog.service";
@Component({
selector: 'mapped-games-table',
templateUrl: './mapped-games-table.component.html',
styleUrls: ['./mapped-games-table.component.scss']
})
export class MappedGamesTableComponent implements AfterViewInit, OnChanges {
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatTable) table!: MatTable<DetectedGameDto>;
@Input() mappedGames!: DetectedGameDto[];
dataSource: MatTableDataSource<DetectedGameDto> = new MatTableDataSource();
displayedColumns: string[] = ["path", "game", "actions"];
showOnlyUnconfirmedMatches: boolean = false;
filter: DetectedGameDto = new DetectedGameDto();
constructor(private gamesService: GamesService,
private libraryManagementService: LibraryManagementService,
private dialogService: DialogService) {
}
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.sortingDataAccessor = (item: DetectedGameDto, property: string) => {
if (property === 'game') {
return item.title;
}
return (item as any)[property];
};
this.dataSource.paginator = this.paginator;
}
ngOnChanges(changes: SimpleChanges): void {
this.refreshData(changes['mappedGames'].currentValue);
}
refreshMappedGamesList(): void {
this.gamesService.getAllGames(true).subscribe(games => this.refreshData(games));
}
toggleShowOnlyUnconfirmedMatches() {
this.showOnlyUnconfirmedMatches = !this.showOnlyUnconfirmedMatches;
this.filter.confirmedMatch = this.showOnlyUnconfirmedMatches ? false : undefined;
}
getFullYearFromTimestamp(timestamp: number): number {
return new Date(timestamp).getFullYear();
}
toggleConfirmGameMapping(mappedGame: DetectedGameDto): void {
this.libraryManagementService.confirmGameMapping(mappedGame.slug, !mappedGame.confirmedMatch).subscribe(() => {
mappedGame.confirmedMatch = !mappedGame.confirmedMatch;
this.refreshData(this.dataSource.data);
});
}
deleteGameMapping(mappedGame: DetectedGameDto): void {
this.libraryManagementService.deleteGame(mappedGame.slug).subscribe(
() => this.refreshData(this.dataSource.data.filter(game => game !== mappedGame))
);
}
openCorrectMappingDialog(mappedGame: DetectedGameDto): void {
this.dialogService.correctGameMappingDialog(mappedGame);
}
private refreshData(newData: DetectedGameDto[]): void {
this.dataSource.data = newData;
// Dirty hack to force a re-render
// Did not find a better solution
this.paginator?._changePageSize(this.paginator?.pageSize);
}
}