* most non-image based admin stuff is working now
* lacks authentication tho
This commit is contained in:
parent
b3d87e3f9a
commit
7a44ba70d4
@ -6,15 +6,33 @@ import { JudgeListComponent } from "./judge-list/judge-list.component";
|
|||||||
import { AwardeeEditorComponent } from "./awardee-editor/awardee-editor.component";
|
import { AwardeeEditorComponent } from "./awardee-editor/awardee-editor.component";
|
||||||
import { JudgeEditorComponent } from "./judge-editor/judge-editor.component";
|
import { JudgeEditorComponent } from "./judge-editor/judge-editor.component";
|
||||||
import { JudgeService } from "./shared/judge.service";
|
import { JudgeService } from "./shared/judge.service";
|
||||||
|
import { JudgeResolverService } from "./shared/judge-resolver.service";
|
||||||
|
import { AwardeeService } from "./shared/awardee.service";
|
||||||
|
import { AwardeeResolverService } from "./shared/awardee-resolver.service";
|
||||||
|
import { YearResolverService } from "./shared/year-resolver.service";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'awardees',
|
path: 'awardees',
|
||||||
component: AwardeeListComponent
|
component: AwardeeListComponent,
|
||||||
|
resolve: {
|
||||||
|
awardees: AwardeeService,
|
||||||
|
}
|
||||||
// canActivate: [AuthGuardService, RoleGuardService],
|
// canActivate: [AuthGuardService, RoleGuardService],
|
||||||
}, {
|
}, {
|
||||||
path: 'awardee/new',
|
path: 'awardee/new',
|
||||||
component: AwardeeEditorComponent
|
component: AwardeeEditorComponent,
|
||||||
|
resolve: {
|
||||||
|
years: YearResolverService,
|
||||||
|
}
|
||||||
|
// canActivate: [AuthGuardService, RoleGuardService],
|
||||||
|
}, {
|
||||||
|
path: 'awardee/edit/:id',
|
||||||
|
component: AwardeeEditorComponent,
|
||||||
|
resolve: {
|
||||||
|
awardee: AwardeeResolverService,
|
||||||
|
years: YearResolverService,
|
||||||
|
}
|
||||||
// canActivate: [AuthGuardService, RoleGuardService],
|
// canActivate: [AuthGuardService, RoleGuardService],
|
||||||
}, {
|
}, {
|
||||||
path: 'judges',
|
path: 'judges',
|
||||||
@ -25,7 +43,14 @@ const routes: Routes = [
|
|||||||
// canActivate: [AuthGuardService, RoleGuardService],
|
// canActivate: [AuthGuardService, RoleGuardService],
|
||||||
}, {
|
}, {
|
||||||
path: 'judge/new',
|
path: 'judge/new',
|
||||||
component: JudgeEditorComponent
|
component: JudgeEditorComponent,
|
||||||
|
// canActivate: [AuthGuardService, RoleGuardService],
|
||||||
|
}, {
|
||||||
|
path: 'judge/edit/:id',
|
||||||
|
component: JudgeEditorComponent,
|
||||||
|
resolve: {
|
||||||
|
judge: JudgeResolverService,
|
||||||
|
}
|
||||||
// canActivate: [AuthGuardService, RoleGuardService],
|
// canActivate: [AuthGuardService, RoleGuardService],
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@ -17,6 +17,11 @@ import {
|
|||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
MatOptionModule,
|
MatOptionModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatRadioModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
MatDialogModule,
|
||||||
} from '@angular/material';
|
} from '@angular/material';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
@ -28,6 +33,7 @@ import { JudgeListTableComponent } from './judge-list-table/judge-list-table.com
|
|||||||
import { AwardeeListTableComponent } from './awardee-list-table/awardee-list-table.component';
|
import { AwardeeListTableComponent } from './awardee-list-table/awardee-list-table.component';
|
||||||
import { AwardeeEditorComponent } from './awardee-editor/awardee-editor.component';
|
import { AwardeeEditorComponent } from './awardee-editor/awardee-editor.component';
|
||||||
import { JudgeEditorComponent } from './judge-editor/judge-editor.component';
|
import { JudgeEditorComponent } from './judge-editor/judge-editor.component';
|
||||||
|
import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -38,7 +44,11 @@ import { JudgeEditorComponent } from './judge-editor/judge-editor.component';
|
|||||||
JudgeListTableComponent,
|
JudgeListTableComponent,
|
||||||
AwardeeListTableComponent,
|
AwardeeListTableComponent,
|
||||||
AwardeeEditorComponent,
|
AwardeeEditorComponent,
|
||||||
JudgeEditorComponent
|
JudgeEditorComponent,
|
||||||
|
ConfirmDialogComponent,
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
ConfirmDialogComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -59,6 +69,11 @@ import { JudgeEditorComponent } from './judge-editor/judge-editor.component';
|
|||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
MatOptionModule,
|
MatOptionModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatRadioModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
MatDialogModule,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<form class="awardee-form">
|
<form class="awardee-form" (ngSubmit)="saveAwardee()">
|
||||||
<mat-form-field class="full-width">
|
<mat-form-field class="full-width">
|
||||||
<input type="text" matInput placeholder="Display name">
|
<input name="name" type="text" matInput placeholder="Display name" [(ngModel)]="awardee.name">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button type="button" mat-raised-button>
|
<button type="button" mat-raised-button>
|
||||||
<i class="far fa-image"></i>
|
<i class="far fa-image"></i>
|
||||||
@ -8,13 +8,13 @@
|
|||||||
</button>
|
</button>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<mat-form-field class="full-width">
|
<mat-form-field class="full-width">
|
||||||
<mat-select placeholder="Year">
|
<mat-select name="year" placeholder="Year" [(ngModel)]="awardee.year">
|
||||||
<mat-option *ngFor="let year of years" [value]="year">{{ year }}</mat-option>
|
<mat-option *ngFor="let year of years" [value]="year">{{ year }}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field class="full-width">
|
<mat-form-field class="full-width">
|
||||||
<textarea matInput placeholder="Article text" rows="10"></textarea>
|
<textarea name="text" matInput placeholder="Article text" rows="10" [(ngModel)]="awardee.text"></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button type="button" mat-raised-button>
|
<button type="button" mat-raised-button>
|
||||||
<i class="far fa-image"></i> Upload article image
|
<i class="far fa-image"></i> Upload article image
|
||||||
@ -22,10 +22,10 @@
|
|||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
<mat-form-field class="full-width">
|
<mat-form-field class="full-width">
|
||||||
<input type="text" matInput placeholder="Image label">
|
<input name="imageLabel" type="text" matInput placeholder="Image label" [(ngModel)]="awardee.imageLabel">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<button type="submit" mat-raised-button color="accent">
|
<button type="submit" mat-raised-button color="accent" [disabled]="!canSave">
|
||||||
<i class="fas fa-save"></i> Save
|
<i class="fas fa-save"></i> Save
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { YearService } from "../shared/year.service";
|
|
||||||
import { Title } from "@angular/platform-browser";
|
import { Title } from "@angular/platform-browser";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
|
import { Awardee } from "../shared/awardee";
|
||||||
|
import { AwardeeService } from "../shared/awardee.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-awardee-editor',
|
selector: 'app-awardee-editor',
|
||||||
@ -9,17 +12,38 @@ import { Title } from "@angular/platform-browser";
|
|||||||
})
|
})
|
||||||
export class AwardeeEditorComponent implements OnInit {
|
export class AwardeeEditorComponent implements OnInit {
|
||||||
|
|
||||||
|
public years: Array<number> = [];
|
||||||
|
public awardee: Awardee;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private yearProvider: YearService,
|
private titleService: Title,
|
||||||
private titleService: Title
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private awardeeService: AwardeeService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleService.setTitle('Edit awardee');
|
this.titleService.setTitle('Edit awardee');
|
||||||
|
this.route.data.subscribe((data: {
|
||||||
|
years: Array<number>,
|
||||||
|
awardee: Awardee,
|
||||||
|
}) => {
|
||||||
|
this.years = data.years ? data.years : [];
|
||||||
|
this.awardee = data.awardee ? data.awardee : new Awardee();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get years(): Array<number> {
|
public saveAwardee() {
|
||||||
return this.yearProvider.years;
|
if (this.canSave) {
|
||||||
|
this.awardeeService.persist(this.awardee).subscribe(() => this.router.navigate(['/awardees']));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get canSave(): boolean {
|
||||||
|
return [
|
||||||
|
this.awardee.name,
|
||||||
|
this.awardee.text,
|
||||||
|
this.awardee.imageLabel].every(textField => textField.length > 0)
|
||||||
|
&& this.awardee.year !== null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,46 +2,21 @@ import { DataSource } from '@angular/cdk/collections';
|
|||||||
import { MatPaginator, MatSort } from '@angular/material';
|
import { MatPaginator, MatSort } from '@angular/material';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { Observable, of as observableOf, merge } from 'rxjs';
|
import { Observable, of as observableOf, merge } from 'rxjs';
|
||||||
|
import { AwardeeService } from "../shared/awardee.service";
|
||||||
// TODO: Replace this with your own data model type
|
import { Awardee } from "../shared/awardee";
|
||||||
export interface AwardeeListTableItem {
|
|
||||||
name: string;
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: replace this with real data from your application
|
|
||||||
const EXAMPLE_DATA: AwardeeListTableItem[] = [
|
|
||||||
{id: 1, name: 'Hydrogen'},
|
|
||||||
{id: 2, name: 'Helium'},
|
|
||||||
{id: 3, name: 'Lithium'},
|
|
||||||
{id: 4, name: 'Beryllium'},
|
|
||||||
{id: 5, name: 'Boron'},
|
|
||||||
{id: 6, name: 'Carbon'},
|
|
||||||
{id: 7, name: 'Nitrogen'},
|
|
||||||
{id: 8, name: 'Oxygen'},
|
|
||||||
{id: 9, name: 'Fluorine'},
|
|
||||||
{id: 10, name: 'Neon'},
|
|
||||||
{id: 11, name: 'Sodium'},
|
|
||||||
{id: 12, name: 'Magnesium'},
|
|
||||||
{id: 13, name: 'Aluminum'},
|
|
||||||
{id: 14, name: 'Silicon'},
|
|
||||||
{id: 15, name: 'Phosphorus'},
|
|
||||||
{id: 16, name: 'Sulfur'},
|
|
||||||
{id: 17, name: 'Chlorine'},
|
|
||||||
{id: 18, name: 'Argon'},
|
|
||||||
{id: 19, name: 'Potassium'},
|
|
||||||
{id: 20, name: 'Calcium'},
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data source for the AwardeeListTable view. This class should
|
* Data source for the AwardeeListTable view. This class should
|
||||||
* encapsulate all logic for fetching and manipulating the displayed data
|
* encapsulate all logic for fetching and manipulating the displayed data
|
||||||
* (including sorting, pagination, and filtering).
|
* (including sorting, pagination, and filtering).
|
||||||
*/
|
*/
|
||||||
export class AwardeeListTableDataSource extends DataSource<AwardeeListTableItem> {
|
export class AwardeeListTableDataSource extends DataSource<Awardee> {
|
||||||
data: AwardeeListTableItem[] = EXAMPLE_DATA;
|
|
||||||
|
|
||||||
constructor(private paginator: MatPaginator, private sort: MatSort) {
|
constructor(
|
||||||
|
private paginator: MatPaginator,
|
||||||
|
private sort: MatSort,
|
||||||
|
private awardeeService: AwardeeService
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,20 +25,21 @@ export class AwardeeListTableDataSource extends DataSource<AwardeeListTableItem>
|
|||||||
* the returned stream emits new items.
|
* the returned stream emits new items.
|
||||||
* @returns A stream of the items to be rendered.
|
* @returns A stream of the items to be rendered.
|
||||||
*/
|
*/
|
||||||
connect(): Observable<AwardeeListTableItem[]> {
|
connect(): Observable<Awardee[]> {
|
||||||
// Combine everything that affects the rendered data into one update
|
// Combine everything that affects the rendered data into one update
|
||||||
// stream for the data-table to consume.
|
// stream for the data-table to consume.
|
||||||
const dataMutations = [
|
const dataMutations = [
|
||||||
observableOf(this.data),
|
observableOf(this.awardeeService.awardees),
|
||||||
|
this.awardeeService.changed,
|
||||||
this.paginator.page,
|
this.paginator.page,
|
||||||
this.sort.sortChange
|
this.sort.sortChange
|
||||||
];
|
];
|
||||||
|
|
||||||
// Set the paginators length
|
// Set the paginators length
|
||||||
this.paginator.length = this.data.length;
|
this.paginator.length = this.awardeeService.awardees.length;
|
||||||
|
|
||||||
return merge(...dataMutations).pipe(map(() => {
|
return merge(...dataMutations).pipe(map(() => {
|
||||||
return this.getPagedData(this.getSortedData([...this.data]));
|
return this.getPagedData(this.getSortedData([...this.awardeeService.awardees]));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +53,7 @@ export class AwardeeListTableDataSource extends DataSource<AwardeeListTableItem>
|
|||||||
* Paginate the data (client-side). If you're using server-side pagination,
|
* Paginate the data (client-side). If you're using server-side pagination,
|
||||||
* this would be replaced by requesting the appropriate data from the server.
|
* this would be replaced by requesting the appropriate data from the server.
|
||||||
*/
|
*/
|
||||||
private getPagedData(data: AwardeeListTableItem[]) {
|
private getPagedData(data: Awardee[]) {
|
||||||
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
|
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
|
||||||
return data.splice(startIndex, this.paginator.pageSize);
|
return data.splice(startIndex, this.paginator.pageSize);
|
||||||
}
|
}
|
||||||
@ -86,7 +62,7 @@ export class AwardeeListTableDataSource extends DataSource<AwardeeListTableItem>
|
|||||||
* Sort the data (client-side). If you're using server-side sorting,
|
* Sort the data (client-side). If you're using server-side sorting,
|
||||||
* this would be replaced by requesting the appropriate data from the server.
|
* this would be replaced by requesting the appropriate data from the server.
|
||||||
*/
|
*/
|
||||||
private getSortedData(data: AwardeeListTableItem[]) {
|
private getSortedData(data: Awardee[]) {
|
||||||
if (!this.sort.active || this.sort.direction === '') {
|
if (!this.sort.active || this.sort.direction === '') {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -95,7 +71,6 @@ export class AwardeeListTableDataSource extends DataSource<AwardeeListTableItem>
|
|||||||
const isAsc = this.sort.direction === 'asc';
|
const isAsc = this.sort.direction === 'asc';
|
||||||
switch (this.sort.active) {
|
switch (this.sort.active) {
|
||||||
case 'name': return compare(a.name, b.name, isAsc);
|
case 'name': return compare(a.name, b.name, isAsc);
|
||||||
case 'id': return compare(+a.id, +b.id, isAsc);
|
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,3 +2,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-column-buttons {
|
||||||
|
flex: 0 0 100px;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,26 +1,39 @@
|
|||||||
<div class="mat-elevation-z8">
|
<div class="mat-elevation-z8">
|
||||||
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
||||||
|
<ng-container matColumnDef="buttons">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="controls"></mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="controls">
|
||||||
|
<button type="button" mat-icon-button (click)="deleteAwardee(row)" color="warn"
|
||||||
|
matTooltip="Delete this entry">
|
||||||
|
<mat-icon fontSet="fas" fontIcon="fa-trash"></mat-icon>
|
||||||
|
</button>
|
||||||
|
<a [routerLink]="['/awardee/edit',row.id]" class="mat-icon-button mat-primary"
|
||||||
|
matTooltip="Edit this entry">
|
||||||
|
<mat-icon fontSet="far" fontIcon="fa-edit"></mat-icon>
|
||||||
|
</a>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<!-- Id Column -->
|
<!-- Name Column -->
|
||||||
<ng-container matColumnDef="id">
|
<ng-container matColumnDef="name">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Id</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let row">{{row.id}}</mat-cell>
|
<mat-cell *matCellDef="let row">{{row.name}}</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Name Column -->
|
<!-- Year Column -->
|
||||||
<ng-container matColumnDef="name">
|
<ng-container matColumnDef="year">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Year</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let row">{{row.name}}</mat-cell>
|
<mat-cell *matCellDef="let row">{{row.year}}</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||||
</mat-table>
|
</mat-table>
|
||||||
|
|
||||||
<mat-paginator #paginator
|
<mat-paginator #paginator
|
||||||
[length]="dataSource.data.length"
|
[length]="awardees.length"
|
||||||
[pageIndex]="0"
|
[pageIndex]="0"
|
||||||
[pageSize]="50"
|
[pageSize]="50"
|
||||||
[pageSizeOptions]="[25, 50, 100, 250]">
|
[pageSizeOptions]="[25, 50, 100, 250]">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
@ -1,6 +1,9 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { MatPaginator, MatSort } from '@angular/material';
|
import { MatDialog, MatPaginator, MatSort } from '@angular/material';
|
||||||
import { AwardeeListTableDataSource } from './awardee-list-table-datasource';
|
import { AwardeeListTableDataSource } from './awardee-list-table-datasource';
|
||||||
|
import { AwardeeService } from "../shared/awardee.service";
|
||||||
|
import { Awardee } from "../shared/awardee";
|
||||||
|
import { ConfirmDialogComponent } from "../confirm-dialog/confirm-dialog.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'awardee-list-table',
|
selector: 'awardee-list-table',
|
||||||
@ -13,9 +16,39 @@ export class AwardeeListTableComponent implements OnInit {
|
|||||||
dataSource: AwardeeListTableDataSource;
|
dataSource: AwardeeListTableDataSource;
|
||||||
|
|
||||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||||
displayedColumns = ['id', 'name'];
|
displayedColumns = ['buttons', 'name', 'year'];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private awardeeService: AwardeeService,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.dataSource = new AwardeeListTableDataSource(this.paginator, this.sort);
|
this.dataSource = new AwardeeListTableDataSource(this.paginator, this.sort, this.awardeeService);
|
||||||
|
}
|
||||||
|
|
||||||
|
get awardees(): Array<Awardee> {
|
||||||
|
return this.awardeeService.awardees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteAwardee(awardee: Awardee) {
|
||||||
|
let dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
width: '300px',
|
||||||
|
data: {
|
||||||
|
question: `This will delete ${awardee.name}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let idx = this.awardees.indexOf(awardee);
|
||||||
|
dialogRef.afterClosed().subscribe(
|
||||||
|
dialogResult => dialogResult ? this.awardeeService.delete(awardee.id).subscribe(
|
||||||
|
result => {
|
||||||
|
if (result) {
|
||||||
|
this.awardees.splice(idx, 1);
|
||||||
|
this.awardeeService.triggerChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) : false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Title } from "@angular/platform-browser";
|
import { Title } from "@angular/platform-browser";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
|
import { Awardee } from "../shared/awardee";
|
||||||
|
import { AwardeeService } from "../shared/awardee.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-awardee-list',
|
selector: 'app-awardee-list',
|
||||||
@ -9,11 +13,17 @@ import { Title } from "@angular/platform-browser";
|
|||||||
export class AwardeeListComponent implements OnInit {
|
export class AwardeeListComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private titleService: Title
|
private titleService: Title,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private awardeeService: AwardeeService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleService.setTitle('Awardee list');
|
this.titleService.setTitle('Awardee list');
|
||||||
|
this.route.data.subscribe((data: {
|
||||||
|
awardees: Array<Awardee>,
|
||||||
|
}) => {
|
||||||
|
this.awardeeService.awardees = data.awardees;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/app/confirm-dialog/confirm-dialog.component.css
Normal file
0
src/app/confirm-dialog/confirm-dialog.component.css
Normal file
6
src/app/confirm-dialog/confirm-dialog.component.html
Normal file
6
src/app/confirm-dialog/confirm-dialog.component.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h3 mat-dialog-title>Are you sure?</h3>
|
||||||
|
<mat-dialog-content>{{data.question}}</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-raised-button type="button" color="primary" [mat-dialog-close]="false">No</button>
|
||||||
|
<button mat-raised-button type="button" color="accent" [mat-dialog-close]="true">Yes</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
25
src/app/confirm-dialog/confirm-dialog.component.spec.ts
Normal file
25
src/app/confirm-dialog/confirm-dialog.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConfirmDialogComponent } from './confirm-dialog.component';
|
||||||
|
|
||||||
|
describe('ConfirmDialogComponent', () => {
|
||||||
|
let component: ConfirmDialogComponent;
|
||||||
|
let fixture: ComponentFixture<ConfirmDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ConfirmDialogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ConfirmDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
15
src/app/confirm-dialog/confirm-dialog.component.ts
Normal file
15
src/app/confirm-dialog/confirm-dialog.component.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-confirm-dialog',
|
||||||
|
templateUrl: './confirm-dialog.component.html',
|
||||||
|
styleUrls: ['./confirm-dialog.component.css']
|
||||||
|
})
|
||||||
|
export class ConfirmDialogComponent{
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<ConfirmDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: {question: string}
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@ -17,11 +17,15 @@
|
|||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button + mat-divider {
|
mat-divider {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-table {
|
.save-button {
|
||||||
margin-bottom: 20px;
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-radio-button + .mat-radio-button {
|
||||||
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,49 +1,67 @@
|
|||||||
<form class="judge-form" name="judge-form">
|
<form class="judge-form" name="judge-form" (ngSubmit)="saveJudge()" >
|
||||||
<mat-form-field class="full-width">
|
<mat-form-field class="full-width">
|
||||||
<input name="name" type="text" matInput placeholder="Display name">
|
<input name="name" type="text" matInput placeholder="Display name" [(ngModel)]="judge.name">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button type="button" mat-raised-button>
|
<mat-radio-group [(ngModel)]="judge.prefix" name="prefix">
|
||||||
<i class="far fa-image"></i>
|
<mat-radio-button [value]="null">None</mat-radio-button>
|
||||||
Upload profile image
|
<mat-radio-button value="Dr ">Dr</mat-radio-button>
|
||||||
</button>
|
<mat-radio-button value="Prof. ">Prof.</mat-radio-button>
|
||||||
<mat-divider></mat-divider>
|
</mat-radio-group>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
<mat-form-field class="full-width">
|
<button type="button" mat-raised-button>
|
||||||
<input name="title" type="text" matInput placeholder="Title" [(ngModel)]="judgedYearInput.title">
|
<i class="far fa-image"></i>
|
||||||
</mat-form-field>
|
Upload profile image
|
||||||
<mat-form-field class="year-select">
|
</button>
|
||||||
<mat-select name="year" placeholder="Year" [(ngModel)]="judgedYearInput.year">
|
<mat-divider></mat-divider>
|
||||||
<mat-option *ngFor="let year of years" [value]="year">{{ year }}</mat-option>
|
<mat-expansion-panel>
|
||||||
</mat-select>
|
<mat-expansion-panel-header>
|
||||||
</mat-form-field>
|
<mat-panel-title>Assign judege to year</mat-panel-title>
|
||||||
<button name="add_to_year" type="button" mat-raised-button color="primary" [disabled]="!canAdd" (click)="addToYear()">
|
<mat-panel-description>Type the year and the title of the judge</mat-panel-description>
|
||||||
<i class="fas fa-plus"></i> Add
|
</mat-expansion-panel-header>
|
||||||
</button>
|
|
||||||
|
|
||||||
<mat-table [dataSource]="judge.yearlyData" class="title-table">
|
<mat-form-field class="full-width">
|
||||||
<ng-container matColumnDef="buttons">
|
<input name="title" type="text" matInput placeholder="Title" [(ngModel)]="judgedYearInput.title">
|
||||||
<mat-header-cell *matHeaderCellDef class="controls"></mat-header-cell>
|
</mat-form-field>
|
||||||
<mat-cell *matCellDef="let row" class="controls"><button type="button" mat-icon-button (click)="removeFromYear(row.year)">
|
<mat-form-field class="year-select">
|
||||||
<mat-icon fontSet="fas" fontIcon="fa-trash"></mat-icon>
|
<input name="year" type="number" matInput placeholder="Year" [(ngModel)]="judgedYearInput.year"
|
||||||
</button></mat-cell>
|
min="2013" max="2100">
|
||||||
</ng-container>
|
</mat-form-field>
|
||||||
|
<button name="add_to_year" type="button" mat-raised-button color="primary" [disabled]="!canAdd"
|
||||||
|
(click)="addToYear()">
|
||||||
|
<i class="fas fa-plus"></i> Add
|
||||||
|
</button>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
|
||||||
<ng-container matColumnDef="year">
|
<mat-card *ngIf="!judge.titles?.length">
|
||||||
<mat-header-cell *matHeaderCellDef>Year</mat-header-cell>
|
<i>No records</i>
|
||||||
<mat-cell *matCellDef="let row">{{row.year}}</mat-cell>
|
</mat-card>
|
||||||
</ng-container>
|
<mat-table [dataSource]="judge.titles" class="title-table" *ngIf="judge.titles?.length">
|
||||||
|
<ng-container matColumnDef="buttons">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="controls"></mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="controls">
|
||||||
|
<button type="button" mat-icon-button (click)="removeFromYear(row.year)">
|
||||||
|
<mat-icon fontSet="fas" fontIcon="fa-trash"></mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<!-- Name Column -->
|
<ng-container matColumnDef="year">
|
||||||
<ng-container matColumnDef="title">
|
<mat-header-cell *matHeaderCellDef>Year</mat-header-cell>
|
||||||
<mat-header-cell *matHeaderCellDef>Title</mat-header-cell>
|
<mat-cell *matCellDef="let row">{{row.year}}</mat-cell>
|
||||||
<mat-cell *matCellDef="let row">{{row.title}}</mat-cell>
|
</ng-container>
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
<!-- Name Column -->
|
||||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
<ng-container matColumnDef="title">
|
||||||
</mat-table>
|
<mat-header-cell *matHeaderCellDef>Title</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">{{row.title}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<button type="submit" mat-raised-button color="accent">
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
<i class="fas fa-save"></i> Save
|
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||||
</button>
|
</mat-table>
|
||||||
|
|
||||||
|
<button type="submit" mat-raised-button color="accent" class="save-button" [disabled]="!canSave">
|
||||||
|
<i class="fas fa-save"></i> Save
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { YearService } from "../shared/year.service";
|
|
||||||
import { Title } from "@angular/platform-browser";
|
import { Title } from "@angular/platform-browser";
|
||||||
import { JudgedYear } from "../shared/judged-year";
|
|
||||||
import { MatTable } from "@angular/material";
|
import { MatTable } from "@angular/material";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { Judge } from "../shared/judge";
|
import { Judge } from "../shared/judge";
|
||||||
import { JudgeService } from "../shared/judge.service";
|
import { JudgeService } from "../shared/judge.service";
|
||||||
|
import { JudgeTitle } from "../shared/judge-title";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-judge-editor',
|
selector: 'app-judge-editor',
|
||||||
@ -13,63 +12,54 @@ import { JudgeService } from "../shared/judge.service";
|
|||||||
styleUrls: ['./judge-editor.component.css']
|
styleUrls: ['./judge-editor.component.css']
|
||||||
})
|
})
|
||||||
export class JudgeEditorComponent implements OnInit {
|
export class JudgeEditorComponent implements OnInit {
|
||||||
@ViewChild(MatTable) private table;
|
@ViewChild(MatTable) private table: MatTable<Array<JudgeTitle>>;
|
||||||
|
|
||||||
public judgedYearInput: JudgedYear = new JudgedYear();
|
public judgedYearInput: JudgeTitle = new JudgeTitle();
|
||||||
public displayedColumns = ['buttons', 'year', 'title'];
|
public displayedColumns = ['buttons', 'year', 'title'];
|
||||||
|
|
||||||
public judge = {
|
public judge: Judge = new Judge();
|
||||||
yearlyData: [
|
|
||||||
{
|
|
||||||
year: 2013,
|
|
||||||
title: 'Something something dark side',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private yearProvider: YearService,
|
|
||||||
private judgeService: JudgeService,
|
private judgeService: JudgeService,
|
||||||
private titleService: Title,
|
private titleService: Title,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute,
|
||||||
|
private router: Router
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleService.setTitle('Edit judge');
|
this.titleService.setTitle('Edit judge');
|
||||||
this.route.data.subscribe((data: {
|
this.route.data.subscribe((data: {
|
||||||
judges: Array<Judge>,
|
judge: Judge,
|
||||||
}) => {
|
}) => {
|
||||||
this.judges = data.judges;
|
this.judge = data.judge ? data.judge : new Judge();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get judges(): Array<Judge> {
|
|
||||||
return this.judgeService.judges;
|
|
||||||
}
|
|
||||||
|
|
||||||
set judges(judges: Array<Judge>) {
|
|
||||||
this.judgeService.judges = judges;
|
|
||||||
}
|
|
||||||
|
|
||||||
get years(): Array<number> {
|
|
||||||
return this.yearProvider.years;
|
|
||||||
}
|
|
||||||
|
|
||||||
get canAdd(): boolean {
|
get canAdd(): boolean {
|
||||||
return this.judgedYearInput.year != null
|
return this.judgedYearInput.year != null
|
||||||
&& this.judgedYearInput.title.trim().length > 0;
|
&& this.judgedYearInput.title.trim().length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get canSave(): boolean {
|
||||||
|
return this.judge.name.trim().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public addToYear() {
|
public addToYear() {
|
||||||
let appendable = Object.assign({}, this.judgedYearInput);
|
let appendable = Object.assign({}, this.judgedYearInput);
|
||||||
this.judge.yearlyData.push(appendable);
|
this.judge.titles.push(appendable);
|
||||||
this.judgedYearInput = new JudgedYear();
|
this.judgedYearInput = new JudgeTitle();
|
||||||
this.table.renderRows();
|
if (this.table) { this.table.renderRows(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeFromYear(year: number) {
|
public removeFromYear(year: number) {
|
||||||
this.judge.yearlyData = this.judge.yearlyData.filter(
|
this.judge.titles = this.judge.titles.filter(
|
||||||
row => row.year !== year
|
row => row.year !== year
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public saveJudge() {
|
||||||
|
if (this.canSave) {
|
||||||
|
this.judgeService.persist(this.judge).subscribe(() => this.router.navigate(['/judges']));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { Observable, of as observableOf, merge } from 'rxjs';
|
|||||||
import { JudgeService } from "../shared/judge.service";
|
import { JudgeService } from "../shared/judge.service";
|
||||||
import { Judge } from "../shared/judge";
|
import { Judge } from "../shared/judge";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data source for the JudgeListTable view. This class should
|
* Data source for the JudgeListTable view. This class should
|
||||||
* encapsulate all logic for fetching and manipulating the displayed data
|
* encapsulate all logic for fetching and manipulating the displayed data
|
||||||
@ -31,6 +30,7 @@ export class JudgeListTableDataSource extends DataSource<Judge> {
|
|||||||
// stream for the data-table to consume.
|
// stream for the data-table to consume.
|
||||||
const dataMutations = [
|
const dataMutations = [
|
||||||
observableOf(this.judgeService.judges),
|
observableOf(this.judgeService.judges),
|
||||||
|
this.judgeService.changed,
|
||||||
this.paginator.page,
|
this.paginator.page,
|
||||||
this.sort.sortChange
|
this.sort.sortChange
|
||||||
];
|
];
|
||||||
@ -71,7 +71,6 @@ export class JudgeListTableDataSource extends DataSource<Judge> {
|
|||||||
const isAsc = this.sort.direction === 'asc';
|
const isAsc = this.sort.direction === 'asc';
|
||||||
switch (this.sort.active) {
|
switch (this.sort.active) {
|
||||||
case 'name': return compare(a.name, b.name, isAsc);
|
case 'name': return compare(a.name, b.name, isAsc);
|
||||||
case 'id': return compare(+a.id, +b.id, isAsc);
|
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,3 +2,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-column-buttons {
|
||||||
|
flex: 0 0 100px;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,26 +1,43 @@
|
|||||||
<div class="mat-elevation-z8">
|
<div class="mat-elevation-z8">
|
||||||
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
||||||
|
<ng-container matColumnDef="buttons">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="controls"></mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="controls">
|
||||||
|
<button type="button" mat-icon-button (click)="deleteJudge(row)" color="warn"
|
||||||
|
matTooltip="Delete this entry">
|
||||||
|
<mat-icon fontSet="fas" fontIcon="fa-trash"></mat-icon>
|
||||||
|
</button>
|
||||||
|
<a [routerLink]="['/judge/edit',row.id]" class="mat-icon-button mat-primary"
|
||||||
|
matTooltip="Edit this entry">
|
||||||
|
<mat-icon fontSet="far" fontIcon="fa-edit"></mat-icon>
|
||||||
|
</a>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<!-- Id Column -->
|
<!-- Name Column -->
|
||||||
<ng-container matColumnDef="id">
|
<ng-container matColumnDef="name">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Id</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let row">{{row.id}}</mat-cell>
|
<mat-cell *matCellDef="let row">{{row.prefix}}{{row.name}}</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Name Column -->
|
<ng-container matColumnDef="years">
|
||||||
<ng-container matColumnDef="name">
|
<mat-header-cell *matHeaderCellDef>Active years</mat-header-cell>
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
|
<mat-cell *matCellDef="let row">{{getJudgedYears(row.titles)}}</mat-cell>
|
||||||
<mat-cell *matCellDef="let row">{{row.name}}</mat-cell>
|
</ng-container>
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
<ng-container matColumnDef="title">
|
||||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
<mat-header-cell *matHeaderCellDef>Latest title</mat-header-cell>
|
||||||
</mat-table>
|
<mat-cell *matCellDef="let row">{{getLatestTitle(row.titles)}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<mat-paginator #paginator
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
[length]="judges.length"
|
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||||
[pageIndex]="0"
|
</mat-table>
|
||||||
[pageSize]="15"
|
|
||||||
[pageSizeOptions]="[15, 25, 50]">
|
<mat-paginator #paginator
|
||||||
</mat-paginator>
|
[length]="judges.length"
|
||||||
|
[pageIndex]="0"
|
||||||
|
[pageSize]="15"
|
||||||
|
[pageSizeOptions]="[15, 25, 50]">
|
||||||
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { MatPaginator, MatSort } from '@angular/material';
|
import { MatDialog, MatPaginator, MatSort } from '@angular/material';
|
||||||
import { JudgeListTableDataSource } from './judge-list-table-datasource';
|
import { JudgeListTableDataSource } from './judge-list-table-datasource';
|
||||||
import { JudgeService } from "../shared/judge.service";
|
import { JudgeService } from "../shared/judge.service";
|
||||||
import { Judge } from "../shared/judge";
|
import { Judge } from "../shared/judge";
|
||||||
|
import { JudgeTitle } from "../shared/judge-title";
|
||||||
|
import { ConfirmDialogComponent } from "../confirm-dialog/confirm-dialog.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'judge-list-table',
|
selector: 'judge-list-table',
|
||||||
@ -15,9 +17,12 @@ export class JudgeListTableComponent implements OnInit {
|
|||||||
dataSource: JudgeListTableDataSource;
|
dataSource: JudgeListTableDataSource;
|
||||||
|
|
||||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||||
displayedColumns = ['id', 'name'];
|
displayedColumns = ['buttons', 'name', 'years', 'title'];
|
||||||
|
|
||||||
constructor(private judgeService: JudgeService) {}
|
constructor(
|
||||||
|
private judgeService: JudgeService,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.dataSource = new JudgeListTableDataSource(this.paginator, this.sort, this.judgeService);
|
this.dataSource = new JudgeListTableDataSource(this.paginator, this.sort, this.judgeService);
|
||||||
@ -26,4 +31,33 @@ export class JudgeListTableComponent implements OnInit {
|
|||||||
get judges(): Array<Judge> {
|
get judges(): Array<Judge> {
|
||||||
return this.judgeService.judges;
|
return this.judgeService.judges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getJudgedYears(titles: Array<JudgeTitle>): string {
|
||||||
|
return titles.map(title => title.year).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLatestTitle(titles: Array<JudgeTitle>): string {
|
||||||
|
return titles.length ? titles.reduce((item, last) => item.year > last.year ? item : last).title : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteJudge(judge: Judge) {
|
||||||
|
let dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
width: '300px',
|
||||||
|
data: {
|
||||||
|
question: `This will delete ${judge.name}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let idx = this.judges.indexOf(judge);
|
||||||
|
dialogRef.afterClosed().subscribe(
|
||||||
|
dialogResult => dialogResult ? this.judgeService.delete(judge.id).subscribe(
|
||||||
|
result => {
|
||||||
|
if (result) {
|
||||||
|
this.judgeService.judges.splice(idx, 1);
|
||||||
|
this.judgeService.triggerChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) : false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Title } from "@angular/platform-browser";
|
import { Title } from "@angular/platform-browser";
|
||||||
import { Judge } from "../shared/judge";
|
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
|
import { Judge } from "../shared/judge";
|
||||||
import { JudgeService } from "../shared/judge.service";
|
import { JudgeService } from "../shared/judge.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -14,15 +15,15 @@ export class JudgeListComponent implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private judgeService: JudgeService,
|
private judgeService: JudgeService,
|
||||||
private titleService: Title,
|
private titleService: Title,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleService.setTitle('Judge list');
|
this.titleService.setTitle('Judge list');
|
||||||
this.route.data.subscribe((data: {
|
this.route.data.subscribe((data: {
|
||||||
judges: Array<Judge>,
|
judges: Array<Judge>,
|
||||||
}) => {
|
}) => {
|
||||||
this.judgeService.judges = data.judges;
|
this.judgeService.judges = data.judges;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/app/shared/awardee-resolver.service.spec.ts
Normal file
15
src/app/shared/awardee-resolver.service.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AwardeeResolverService } from './awardee-resolver.service';
|
||||||
|
|
||||||
|
describe('AwardeeResolverService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [AwardeeResolverService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([AwardeeResolverService], (service: AwardeeResolverService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
21
src/app/shared/awardee-resolver.service.ts
Normal file
21
src/app/shared/awardee-resolver.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from "@angular/router";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs/internal/Observable";
|
||||||
|
|
||||||
|
import { environment } from "../../environments/environment";
|
||||||
|
import { Awardee } from "./awardee";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AwardeeResolverService implements Resolve<Awardee> {
|
||||||
|
|
||||||
|
private apiEndPoint = `${environment.apiUrl}/awardee`;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
|
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Awardee> {
|
||||||
|
return this.httpClient.get<Awardee>(`${this.apiEndPoint}/${route.params['id']}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/app/shared/awardee.service.spec.ts
Normal file
15
src/app/shared/awardee.service.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AwardeeService } from './awardee.service';
|
||||||
|
|
||||||
|
describe('AwardeeService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [AwardeeService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([AwardeeService], (service: AwardeeService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
59
src/app/shared/awardee.service.ts
Normal file
59
src/app/shared/awardee.service.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { EventEmitter, Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from "@angular/router";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs/internal/Observable";
|
||||||
|
|
||||||
|
import { environment } from "../../environments/environment";
|
||||||
|
import { Awardee } from "./awardee";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AwardeeService implements Resolve<Array<Awardee>> {
|
||||||
|
|
||||||
|
private apiEndPoint = `${environment.apiUrl}/awardee`;
|
||||||
|
private cachedAwardees: Array<Awardee> = [];
|
||||||
|
|
||||||
|
public changed: EventEmitter<Array<Awardee>> = new EventEmitter<Array<Awardee>>();
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
|
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Array<Awardee>> {
|
||||||
|
return this.getJudges().toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getJudges(): Observable<Array<Awardee>> {
|
||||||
|
return this.httpClient.get<Array<Awardee>>(this.apiEndPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
get awardees(): Array<Awardee> {
|
||||||
|
return this.cachedAwardees;
|
||||||
|
}
|
||||||
|
|
||||||
|
set awardees(judges: Array<Awardee>) {
|
||||||
|
this.cachedAwardees = judges;
|
||||||
|
this.triggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public triggerChange() {
|
||||||
|
this.changed.emit(this.cachedAwardees);
|
||||||
|
}
|
||||||
|
|
||||||
|
public persist(judge: Awardee): Observable<Awardee> {
|
||||||
|
return judge.id === null
|
||||||
|
? this.create(judge)
|
||||||
|
: this.update(judge);
|
||||||
|
}
|
||||||
|
|
||||||
|
public create(awardee: Awardee): Observable<Awardee> {
|
||||||
|
return this.httpClient.post<Awardee>(this.apiEndPoint, awardee);
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(awardee: Awardee): Observable<Awardee> {
|
||||||
|
return this.httpClient.put<Awardee>(`${this.apiEndPoint}/${awardee.id}`, awardee);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(id: number): Observable<boolean> {
|
||||||
|
return this.httpClient.delete<boolean>(`${this.apiEndPoint}/${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/app/shared/awardee.ts
Normal file
7
src/app/shared/awardee.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export class Awardee {
|
||||||
|
public id: number = null;
|
||||||
|
public year: number = null;
|
||||||
|
public name: string = '';
|
||||||
|
public text: string = '';
|
||||||
|
public imageLabel: string = '';
|
||||||
|
}
|
||||||
15
src/app/shared/judge-resolver.service.spec.ts
Normal file
15
src/app/shared/judge-resolver.service.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { JudgeResolverService } from './judge-resolver.service';
|
||||||
|
|
||||||
|
describe('JudgeResolverService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [JudgeResolverService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([JudgeResolverService], (service: JudgeResolverService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
21
src/app/shared/judge-resolver.service.ts
Normal file
21
src/app/shared/judge-resolver.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from "@angular/router";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs/internal/Observable";
|
||||||
|
|
||||||
|
import { environment } from "../../environments/environment";
|
||||||
|
import { Judge } from "./judge";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class JudgeResolverService implements Resolve<Judge> {
|
||||||
|
|
||||||
|
private apiEndPoint = `${environment.apiUrl}/judge`;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
|
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Judge> {
|
||||||
|
return this.httpClient.get<Judge>(`${this.apiEndPoint}/${route.params['id']}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
export class JudgeTitle {
|
export class JudgeTitle {
|
||||||
id: number;
|
id: number = null;
|
||||||
year: number;
|
year: number = null;
|
||||||
title: string;
|
title: string = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { EventEmitter, Injectable } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from "@angular/router";
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from "@angular/router";
|
||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
|
||||||
import { Judge } from "./judge";
|
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
import { Observable } from "rxjs/internal/Observable";
|
import { Observable } from "rxjs/internal/Observable";
|
||||||
|
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import { Judge } from "./judge";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
@ -14,6 +14,8 @@ export class JudgeService implements Resolve<Array<Judge>> {
|
|||||||
private apiEndPoint = `${environment.apiUrl}/judge`;
|
private apiEndPoint = `${environment.apiUrl}/judge`;
|
||||||
private cachedJudges: Array<Judge> = [];
|
private cachedJudges: Array<Judge> = [];
|
||||||
|
|
||||||
|
public changed: EventEmitter<Array<Judge>> = new EventEmitter<Array<Judge>>();
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient) {}
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Array<Judge>> {
|
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Array<Judge>> {
|
||||||
@ -30,5 +32,28 @@ export class JudgeService implements Resolve<Array<Judge>> {
|
|||||||
|
|
||||||
set judges(judges: Array<Judge>) {
|
set judges(judges: Array<Judge>) {
|
||||||
this.cachedJudges = judges;
|
this.cachedJudges = judges;
|
||||||
|
this.triggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public triggerChange() {
|
||||||
|
this.changed.emit(this.cachedJudges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public persist(judge: Judge): Observable<Judge> {
|
||||||
|
return judge.id === null
|
||||||
|
? this.create(judge)
|
||||||
|
: this.update(judge);
|
||||||
|
}
|
||||||
|
|
||||||
|
public create(judge: Judge): Observable<Judge> {
|
||||||
|
return this.httpClient.post<Judge>(this.apiEndPoint, judge);
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(judge: Judge): Observable<Judge> {
|
||||||
|
return this.httpClient.put<Judge>(`${this.apiEndPoint}/${judge.id}`, judge);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(id: number): Observable<boolean> {
|
||||||
|
return this.httpClient.delete<boolean>(`${this.apiEndPoint}/${id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { JudgeTitle } from "./judge-title";
|
import { JudgeTitle } from "./judge-title";
|
||||||
|
|
||||||
export class Judge {
|
export class Judge {
|
||||||
public id: number;
|
public id: number = null;
|
||||||
public name: string;
|
public prefix: string = null;
|
||||||
public titles: Array<JudgeTitle>;
|
public name: string = '';
|
||||||
|
public titles: Array<JudgeTitle> = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
export class JudgedYear {
|
|
||||||
public year: number = null;
|
|
||||||
public title = '';
|
|
||||||
}
|
|
||||||
15
src/app/shared/year-resolver.service.spec.ts
Normal file
15
src/app/shared/year-resolver.service.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { YearResolverService } from './year-resolver.service';
|
||||||
|
|
||||||
|
describe('YearResolverService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [YearResolverService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([YearResolverService], (service: YearResolverService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
19
src/app/shared/year-resolver.service.ts
Normal file
19
src/app/shared/year-resolver.service.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from "@angular/router";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs/internal/Observable";
|
||||||
|
|
||||||
|
import { environment } from "../../environments/environment";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class YearResolverService implements Resolve<Array<number>> {
|
||||||
|
private apiEndPoint = `${environment.apiUrl}/years`;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
|
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Array<number>> {
|
||||||
|
return this.httpClient.get<Array<number>>(this.apiEndPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user