From 7a44ba70d46bec41c0f6a0d60292ef1a97943d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Danyi?= Date: Fri, 11 May 2018 18:22:59 +0200 Subject: [PATCH] * most non-image based admin stuff is working now * lacks authentication tho --- src/app/app-routing.module.ts | 31 +++++- src/app/app.module.ts | 17 ++- .../awardee-editor.component.html | 12 +-- .../awardee-editor.component.ts | 34 +++++- .../awardee-list-table-datasource.ts | 55 +++------- .../awardee-list-table.component.css | 4 + .../awardee-list-table.component.html | 53 +++++---- .../awardee-list-table.component.ts | 39 ++++++- .../awardee-list/awardee-list.component.ts | 14 ++- .../confirm-dialog.component.css | 0 .../confirm-dialog.component.html | 6 ++ .../confirm-dialog.component.spec.ts | 25 +++++ .../confirm-dialog.component.ts | 15 +++ .../judge-editor/judge-editor.component.css | 10 +- .../judge-editor/judge-editor.component.html | 102 ++++++++++-------- .../judge-editor/judge-editor.component.ts | 56 ++++------ .../judge-list-table-datasource.ts | 3 +- .../judge-list-table.component.css | 4 + .../judge-list-table.component.html | 57 ++++++---- .../judge-list-table.component.ts | 40 ++++++- src/app/judge-list/judge-list.component.ts | 15 +-- .../shared/awardee-resolver.service.spec.ts | 15 +++ src/app/shared/awardee-resolver.service.ts | 21 ++++ src/app/shared/awardee.service.spec.ts | 15 +++ src/app/shared/awardee.service.ts | 59 ++++++++++ src/app/shared/awardee.ts | 7 ++ src/app/shared/judge-resolver.service.spec.ts | 15 +++ src/app/shared/judge-resolver.service.ts | 21 ++++ src/app/shared/judge-title.ts | 6 +- src/app/shared/judge.service.ts | 33 +++++- src/app/shared/judge.ts | 7 +- src/app/shared/judged-year.ts | 4 - src/app/shared/year-resolver.service.spec.ts | 15 +++ src/app/shared/year-resolver.service.ts | 19 ++++ 34 files changed, 625 insertions(+), 204 deletions(-) create mode 100644 src/app/confirm-dialog/confirm-dialog.component.css create mode 100644 src/app/confirm-dialog/confirm-dialog.component.html create mode 100644 src/app/confirm-dialog/confirm-dialog.component.spec.ts create mode 100644 src/app/confirm-dialog/confirm-dialog.component.ts create mode 100644 src/app/shared/awardee-resolver.service.spec.ts create mode 100644 src/app/shared/awardee-resolver.service.ts create mode 100644 src/app/shared/awardee.service.spec.ts create mode 100644 src/app/shared/awardee.service.ts create mode 100644 src/app/shared/awardee.ts create mode 100644 src/app/shared/judge-resolver.service.spec.ts create mode 100644 src/app/shared/judge-resolver.service.ts delete mode 100644 src/app/shared/judged-year.ts create mode 100644 src/app/shared/year-resolver.service.spec.ts create mode 100644 src/app/shared/year-resolver.service.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c391fcb..bc5bccc 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -6,15 +6,33 @@ import { JudgeListComponent } from "./judge-list/judge-list.component"; import { AwardeeEditorComponent } from "./awardee-editor/awardee-editor.component"; import { JudgeEditorComponent } from "./judge-editor/judge-editor.component"; 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 = [ { path: 'awardees', - component: AwardeeListComponent + component: AwardeeListComponent, + resolve: { + awardees: AwardeeService, + } // canActivate: [AuthGuardService, RoleGuardService], }, { 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], }, { path: 'judges', @@ -25,7 +43,14 @@ const routes: Routes = [ // canActivate: [AuthGuardService, RoleGuardService], }, { path: 'judge/new', - component: JudgeEditorComponent + component: JudgeEditorComponent, + // canActivate: [AuthGuardService, RoleGuardService], + }, { + path: 'judge/edit/:id', + component: JudgeEditorComponent, + resolve: { + judge: JudgeResolverService, + } // canActivate: [AuthGuardService, RoleGuardService], } ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e8778a2..c6ad88b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -17,6 +17,11 @@ import { MatInputModule, MatSelectModule, MatOptionModule, + MatCardModule, + MatRadioModule, + MatExpansionModule, + MatTooltipModule, + MatDialogModule, } from '@angular/material'; 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 { AwardeeEditorComponent } from './awardee-editor/awardee-editor.component'; import { JudgeEditorComponent } from './judge-editor/judge-editor.component'; +import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component'; @NgModule({ declarations: [ @@ -38,7 +44,11 @@ import { JudgeEditorComponent } from './judge-editor/judge-editor.component'; JudgeListTableComponent, AwardeeListTableComponent, AwardeeEditorComponent, - JudgeEditorComponent + JudgeEditorComponent, + ConfirmDialogComponent, + ], + entryComponents: [ + ConfirmDialogComponent, ], imports: [ BrowserModule, @@ -59,6 +69,11 @@ import { JudgeEditorComponent } from './judge-editor/judge-editor.component'; MatInputModule, MatSelectModule, MatOptionModule, + MatCardModule, + MatRadioModule, + MatExpansionModule, + MatTooltipModule, + MatDialogModule, ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/awardee-editor/awardee-editor.component.html b/src/app/awardee-editor/awardee-editor.component.html index 0625fd8..1cc0ff6 100644 --- a/src/app/awardee-editor/awardee-editor.component.html +++ b/src/app/awardee-editor/awardee-editor.component.html @@ -1,6 +1,6 @@ -
+ - + - + {{ year }} - +
diff --git a/src/app/awardee-editor/awardee-editor.component.ts b/src/app/awardee-editor/awardee-editor.component.ts index 35de04a..5d463a9 100644 --- a/src/app/awardee-editor/awardee-editor.component.ts +++ b/src/app/awardee-editor/awardee-editor.component.ts @@ -1,6 +1,9 @@ import { Component, OnInit } from '@angular/core'; -import { YearService } from "../shared/year.service"; import { Title } from "@angular/platform-browser"; +import { ActivatedRoute, Router } from "@angular/router"; + +import { Awardee } from "../shared/awardee"; +import { AwardeeService } from "../shared/awardee.service"; @Component({ selector: 'app-awardee-editor', @@ -9,17 +12,38 @@ import { Title } from "@angular/platform-browser"; }) export class AwardeeEditorComponent implements OnInit { + public years: Array = []; + public awardee: Awardee; + constructor( - private yearProvider: YearService, - private titleService: Title + private titleService: Title, + private route: ActivatedRoute, + private router: Router, + private awardeeService: AwardeeService, ) {} ngOnInit() { this.titleService.setTitle('Edit awardee'); + this.route.data.subscribe((data: { + years: Array, + awardee: Awardee, + }) => { + this.years = data.years ? data.years : []; + this.awardee = data.awardee ? data.awardee : new Awardee(); + }); } - get years(): Array { - return this.yearProvider.years; + public saveAwardee() { + 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; + } } diff --git a/src/app/awardee-list-table/awardee-list-table-datasource.ts b/src/app/awardee-list-table/awardee-list-table-datasource.ts index 4bdf537..82bcadb 100644 --- a/src/app/awardee-list-table/awardee-list-table-datasource.ts +++ b/src/app/awardee-list-table/awardee-list-table-datasource.ts @@ -2,46 +2,21 @@ import { DataSource } from '@angular/cdk/collections'; import { MatPaginator, MatSort } from '@angular/material'; import { map } from 'rxjs/operators'; import { Observable, of as observableOf, merge } from 'rxjs'; - -// TODO: Replace this with your own data model type -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'}, -]; +import { AwardeeService } from "../shared/awardee.service"; +import { Awardee } from "../shared/awardee"; /** * Data source for the AwardeeListTable view. This class should * encapsulate all logic for fetching and manipulating the displayed data * (including sorting, pagination, and filtering). */ -export class AwardeeListTableDataSource extends DataSource { - data: AwardeeListTableItem[] = EXAMPLE_DATA; +export class AwardeeListTableDataSource extends DataSource { - constructor(private paginator: MatPaginator, private sort: MatSort) { + constructor( + private paginator: MatPaginator, + private sort: MatSort, + private awardeeService: AwardeeService + ) { super(); } @@ -50,20 +25,21 @@ export class AwardeeListTableDataSource extends DataSource * the returned stream emits new items. * @returns A stream of the items to be rendered. */ - connect(): Observable { + connect(): Observable { // Combine everything that affects the rendered data into one update // stream for the data-table to consume. const dataMutations = [ - observableOf(this.data), + observableOf(this.awardeeService.awardees), + this.awardeeService.changed, this.paginator.page, this.sort.sortChange ]; // Set the paginators length - this.paginator.length = this.data.length; + this.paginator.length = this.awardeeService.awardees.length; 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 * Paginate the data (client-side). If you're using server-side pagination, * 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; return data.splice(startIndex, this.paginator.pageSize); } @@ -86,7 +62,7 @@ export class AwardeeListTableDataSource extends DataSource * Sort the data (client-side). If you're using server-side sorting, * 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 === '') { return data; } @@ -95,7 +71,6 @@ export class AwardeeListTableDataSource extends DataSource const isAsc = this.sort.direction === 'asc'; switch (this.sort.active) { case 'name': return compare(a.name, b.name, isAsc); - case 'id': return compare(+a.id, +b.id, isAsc); default: return 0; } }); diff --git a/src/app/awardee-list-table/awardee-list-table.component.css b/src/app/awardee-list-table/awardee-list-table.component.css index d4a9b17..d3f73aa 100644 --- a/src/app/awardee-list-table/awardee-list-table.component.css +++ b/src/app/awardee-list-table/awardee-list-table.component.css @@ -2,3 +2,7 @@ display: block; margin-top: 20px; } + +.mat-column-buttons { + flex: 0 0 100px; +} diff --git a/src/app/awardee-list-table/awardee-list-table.component.html b/src/app/awardee-list-table/awardee-list-table.component.html index 1105442..050e493 100644 --- a/src/app/awardee-list-table/awardee-list-table.component.html +++ b/src/app/awardee-list-table/awardee-list-table.component.html @@ -1,26 +1,39 @@
- + + + + + + + + + + - - - Id - {{row.id}} - + + + Name + {{row.name}} + - - - Name - {{row.name}} - + + + Year + {{row.year}} + - - - + + + - - + +
\ No newline at end of file diff --git a/src/app/awardee-list-table/awardee-list-table.component.ts b/src/app/awardee-list-table/awardee-list-table.component.ts index d4aa2e2..239d48d 100644 --- a/src/app/awardee-list-table/awardee-list-table.component.ts +++ b/src/app/awardee-list-table/awardee-list-table.component.ts @@ -1,6 +1,9 @@ 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 { AwardeeService } from "../shared/awardee.service"; +import { Awardee } from "../shared/awardee"; +import { ConfirmDialogComponent } from "../confirm-dialog/confirm-dialog.component"; @Component({ selector: 'awardee-list-table', @@ -13,9 +16,39 @@ export class AwardeeListTableComponent implements OnInit { dataSource: AwardeeListTableDataSource; /** 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() { - this.dataSource = new AwardeeListTableDataSource(this.paginator, this.sort); + this.dataSource = new AwardeeListTableDataSource(this.paginator, this.sort, this.awardeeService); + } + + get awardees(): Array { + 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 + ) } } diff --git a/src/app/awardee-list/awardee-list.component.ts b/src/app/awardee-list/awardee-list.component.ts index 9df6664..26e4de0 100644 --- a/src/app/awardee-list/awardee-list.component.ts +++ b/src/app/awardee-list/awardee-list.component.ts @@ -1,5 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { Title } from "@angular/platform-browser"; +import { ActivatedRoute } from "@angular/router"; + +import { Awardee } from "../shared/awardee"; +import { AwardeeService } from "../shared/awardee.service"; @Component({ selector: 'app-awardee-list', @@ -9,11 +13,17 @@ import { Title } from "@angular/platform-browser"; export class AwardeeListComponent implements OnInit { constructor( - private titleService: Title + private titleService: Title, + private route: ActivatedRoute, + private awardeeService: AwardeeService, ) { } ngOnInit() { this.titleService.setTitle('Awardee list'); + this.route.data.subscribe((data: { + awardees: Array, + }) => { + this.awardeeService.awardees = data.awardees; + }); } - } diff --git a/src/app/confirm-dialog/confirm-dialog.component.css b/src/app/confirm-dialog/confirm-dialog.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/confirm-dialog/confirm-dialog.component.html b/src/app/confirm-dialog/confirm-dialog.component.html new file mode 100644 index 0000000..08b6433 --- /dev/null +++ b/src/app/confirm-dialog/confirm-dialog.component.html @@ -0,0 +1,6 @@ +

Are you sure?

+{{data.question}} + + + + diff --git a/src/app/confirm-dialog/confirm-dialog.component.spec.ts b/src/app/confirm-dialog/confirm-dialog.component.spec.ts new file mode 100644 index 0000000..ccea433 --- /dev/null +++ b/src/app/confirm-dialog/confirm-dialog.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ConfirmDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfirmDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/confirm-dialog/confirm-dialog.component.ts b/src/app/confirm-dialog/confirm-dialog.component.ts new file mode 100644 index 0000000..d057f35 --- /dev/null +++ b/src/app/confirm-dialog/confirm-dialog.component.ts @@ -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, + @Inject(MAT_DIALOG_DATA) public data: {question: string} + ) {} +} diff --git a/src/app/judge-editor/judge-editor.component.css b/src/app/judge-editor/judge-editor.component.css index 1cd63ef..936f0c2 100644 --- a/src/app/judge-editor/judge-editor.component.css +++ b/src/app/judge-editor/judge-editor.component.css @@ -17,11 +17,15 @@ margin-right: 20px; } -button + mat-divider { +mat-divider { margin-top: 10px; margin-bottom: 10px; } -.title-table { - margin-bottom: 20px; +.save-button { + margin-top: 20px; +} + +.mat-radio-button + .mat-radio-button { + margin-left: 20px; } diff --git a/src/app/judge-editor/judge-editor.component.html b/src/app/judge-editor/judge-editor.component.html index 26f634d..0a70c91 100644 --- a/src/app/judge-editor/judge-editor.component.html +++ b/src/app/judge-editor/judge-editor.component.html @@ -1,49 +1,67 @@ -
- - - - - + + + + + + None + Dr + Prof. + + - - - - - - {{ year }} - - - + + + + + Assign judege to year + Type the year and the title of the judge + - - - - - + + + + + + + + - - Year - {{row.year}} - + + No records + + + + + + + + - - - Title - {{row.title}} - + + Year + {{row.year}} + - - - + + + Title + {{row.title}} + - + + + + +
diff --git a/src/app/judge-editor/judge-editor.component.ts b/src/app/judge-editor/judge-editor.component.ts index 6191730..9405e99 100644 --- a/src/app/judge-editor/judge-editor.component.ts +++ b/src/app/judge-editor/judge-editor.component.ts @@ -1,11 +1,10 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { YearService } from "../shared/year.service"; import { Title } from "@angular/platform-browser"; -import { JudgedYear } from "../shared/judged-year"; import { MatTable } from "@angular/material"; -import { ActivatedRoute } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { Judge } from "../shared/judge"; import { JudgeService } from "../shared/judge.service"; +import { JudgeTitle } from "../shared/judge-title"; @Component({ selector: 'app-judge-editor', @@ -13,63 +12,54 @@ import { JudgeService } from "../shared/judge.service"; styleUrls: ['./judge-editor.component.css'] }) export class JudgeEditorComponent implements OnInit { - @ViewChild(MatTable) private table; + @ViewChild(MatTable) private table: MatTable>; - public judgedYearInput: JudgedYear = new JudgedYear(); + public judgedYearInput: JudgeTitle = new JudgeTitle(); public displayedColumns = ['buttons', 'year', 'title']; - public judge = { - yearlyData: [ - { - year: 2013, - title: 'Something something dark side', - } - ] - }; + public judge: Judge = new Judge(); constructor( - private yearProvider: YearService, private judgeService: JudgeService, private titleService: Title, - private route: ActivatedRoute + private route: ActivatedRoute, + private router: Router ) {} ngOnInit() { this.titleService.setTitle('Edit judge'); this.route.data.subscribe((data: { - judges: Array, + judge: Judge, }) => { - this.judges = data.judges; + this.judge = data.judge ? data.judge : new Judge(); }); } - get judges(): Array { - return this.judgeService.judges; - } - - set judges(judges: Array) { - this.judgeService.judges = judges; - } - - get years(): Array { - return this.yearProvider.years; - } - get canAdd(): boolean { return this.judgedYearInput.year != null && this.judgedYearInput.title.trim().length > 0; } + get canSave(): boolean { + return this.judge.name.trim().length > 0; + } + public addToYear() { let appendable = Object.assign({}, this.judgedYearInput); - this.judge.yearlyData.push(appendable); - this.judgedYearInput = new JudgedYear(); - this.table.renderRows(); + this.judge.titles.push(appendable); + this.judgedYearInput = new JudgeTitle(); + if (this.table) { this.table.renderRows(); } } public removeFromYear(year: number) { - this.judge.yearlyData = this.judge.yearlyData.filter( + this.judge.titles = this.judge.titles.filter( row => row.year !== year ); } + + public saveJudge() { + if (this.canSave) { + this.judgeService.persist(this.judge).subscribe(() => this.router.navigate(['/judges'])); + } + } } diff --git a/src/app/judge-list-table/judge-list-table-datasource.ts b/src/app/judge-list-table/judge-list-table-datasource.ts index ae65ed1..fb0950d 100644 --- a/src/app/judge-list-table/judge-list-table-datasource.ts +++ b/src/app/judge-list-table/judge-list-table-datasource.ts @@ -5,7 +5,6 @@ import { Observable, of as observableOf, merge } from 'rxjs'; import { JudgeService } from "../shared/judge.service"; import { Judge } from "../shared/judge"; - /** * Data source for the JudgeListTable view. This class should * encapsulate all logic for fetching and manipulating the displayed data @@ -31,6 +30,7 @@ export class JudgeListTableDataSource extends DataSource { // stream for the data-table to consume. const dataMutations = [ observableOf(this.judgeService.judges), + this.judgeService.changed, this.paginator.page, this.sort.sortChange ]; @@ -71,7 +71,6 @@ export class JudgeListTableDataSource extends DataSource { const isAsc = this.sort.direction === 'asc'; switch (this.sort.active) { case 'name': return compare(a.name, b.name, isAsc); - case 'id': return compare(+a.id, +b.id, isAsc); default: return 0; } }); diff --git a/src/app/judge-list-table/judge-list-table.component.css b/src/app/judge-list-table/judge-list-table.component.css index d4a9b17..d3f73aa 100644 --- a/src/app/judge-list-table/judge-list-table.component.css +++ b/src/app/judge-list-table/judge-list-table.component.css @@ -2,3 +2,7 @@ display: block; margin-top: 20px; } + +.mat-column-buttons { + flex: 0 0 100px; +} diff --git a/src/app/judge-list-table/judge-list-table.component.html b/src/app/judge-list-table/judge-list-table.component.html index f023ccc..8c93e66 100644 --- a/src/app/judge-list-table/judge-list-table.component.html +++ b/src/app/judge-list-table/judge-list-table.component.html @@ -1,26 +1,43 @@
- + + + + + + + + + + - - - Id - {{row.id}} - + + + Name + {{row.prefix}}{{row.name}} + - - - Name - {{row.name}} - + + Active years + {{getJudgedYears(row.titles)}} + - - - + + Latest title + {{getLatestTitle(row.titles)}} + - - + + + + + +
diff --git a/src/app/judge-list-table/judge-list-table.component.ts b/src/app/judge-list-table/judge-list-table.component.ts index e844ff4..91fa9ed 100644 --- a/src/app/judge-list-table/judge-list-table.component.ts +++ b/src/app/judge-list-table/judge-list-table.component.ts @@ -1,8 +1,10 @@ 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 { JudgeService } from "../shared/judge.service"; import { Judge } from "../shared/judge"; +import { JudgeTitle } from "../shared/judge-title"; +import { ConfirmDialogComponent } from "../confirm-dialog/confirm-dialog.component"; @Component({ selector: 'judge-list-table', @@ -15,9 +17,12 @@ export class JudgeListTableComponent implements OnInit { dataSource: JudgeListTableDataSource; /** 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() { this.dataSource = new JudgeListTableDataSource(this.paginator, this.sort, this.judgeService); @@ -26,4 +31,33 @@ export class JudgeListTableComponent implements OnInit { get judges(): Array { return this.judgeService.judges; } + + public getJudgedYears(titles: Array): string { + return titles.map(title => title.year).join(', '); + } + + public getLatestTitle(titles: Array): 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 + ) + } } diff --git a/src/app/judge-list/judge-list.component.ts b/src/app/judge-list/judge-list.component.ts index 98a36a3..d67dea6 100644 --- a/src/app/judge-list/judge-list.component.ts +++ b/src/app/judge-list/judge-list.component.ts @@ -1,7 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { Title } from "@angular/platform-browser"; -import { Judge } from "../shared/judge"; import { ActivatedRoute } from "@angular/router"; + +import { Judge } from "../shared/judge"; import { JudgeService } from "../shared/judge.service"; @Component({ @@ -14,15 +15,15 @@ export class JudgeListComponent implements OnInit { constructor( private judgeService: JudgeService, private titleService: Title, - private route: ActivatedRoute + private route: ActivatedRoute, ) { } ngOnInit() { this.titleService.setTitle('Judge list'); - this.route.data.subscribe((data: { - judges: Array, - }) => { - this.judgeService.judges = data.judges; - }); + this.route.data.subscribe((data: { + judges: Array, + }) => { + this.judgeService.judges = data.judges; + }); } } diff --git a/src/app/shared/awardee-resolver.service.spec.ts b/src/app/shared/awardee-resolver.service.spec.ts new file mode 100644 index 0000000..108b895 --- /dev/null +++ b/src/app/shared/awardee-resolver.service.spec.ts @@ -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(); + })); +}); diff --git a/src/app/shared/awardee-resolver.service.ts b/src/app/shared/awardee-resolver.service.ts new file mode 100644 index 0000000..2f9f463 --- /dev/null +++ b/src/app/shared/awardee-resolver.service.ts @@ -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 { + + private apiEndPoint = `${environment.apiUrl}/awardee`; + + constructor(private httpClient: HttpClient) {} + + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.httpClient.get(`${this.apiEndPoint}/${route.params['id']}`); + } +} diff --git a/src/app/shared/awardee.service.spec.ts b/src/app/shared/awardee.service.spec.ts new file mode 100644 index 0000000..7811cbb --- /dev/null +++ b/src/app/shared/awardee.service.spec.ts @@ -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(); + })); +}); diff --git a/src/app/shared/awardee.service.ts b/src/app/shared/awardee.service.ts new file mode 100644 index 0000000..8efde7c --- /dev/null +++ b/src/app/shared/awardee.service.ts @@ -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> { + + private apiEndPoint = `${environment.apiUrl}/awardee`; + private cachedAwardees: Array = []; + + public changed: EventEmitter> = new EventEmitter>(); + + constructor(private httpClient: HttpClient) {} + + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise> { + return this.getJudges().toPromise(); + } + + public getJudges(): Observable> { + return this.httpClient.get>(this.apiEndPoint); + } + + get awardees(): Array { + return this.cachedAwardees; + } + + set awardees(judges: Array) { + this.cachedAwardees = judges; + this.triggerChange(); + } + + public triggerChange() { + this.changed.emit(this.cachedAwardees); + } + + public persist(judge: Awardee): Observable { + return judge.id === null + ? this.create(judge) + : this.update(judge); + } + + public create(awardee: Awardee): Observable { + return this.httpClient.post(this.apiEndPoint, awardee); + } + + public update(awardee: Awardee): Observable { + return this.httpClient.put(`${this.apiEndPoint}/${awardee.id}`, awardee); + } + + public delete(id: number): Observable { + return this.httpClient.delete(`${this.apiEndPoint}/${id}`); + } +} diff --git a/src/app/shared/awardee.ts b/src/app/shared/awardee.ts new file mode 100644 index 0000000..5f9cb7b --- /dev/null +++ b/src/app/shared/awardee.ts @@ -0,0 +1,7 @@ +export class Awardee { + public id: number = null; + public year: number = null; + public name: string = ''; + public text: string = ''; + public imageLabel: string = ''; +} diff --git a/src/app/shared/judge-resolver.service.spec.ts b/src/app/shared/judge-resolver.service.spec.ts new file mode 100644 index 0000000..f890c41 --- /dev/null +++ b/src/app/shared/judge-resolver.service.spec.ts @@ -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(); + })); +}); diff --git a/src/app/shared/judge-resolver.service.ts b/src/app/shared/judge-resolver.service.ts new file mode 100644 index 0000000..f94b868 --- /dev/null +++ b/src/app/shared/judge-resolver.service.ts @@ -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 { + + private apiEndPoint = `${environment.apiUrl}/judge`; + + constructor(private httpClient: HttpClient) {} + + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.httpClient.get(`${this.apiEndPoint}/${route.params['id']}`); + } +} diff --git a/src/app/shared/judge-title.ts b/src/app/shared/judge-title.ts index e15ff6d..1870d93 100644 --- a/src/app/shared/judge-title.ts +++ b/src/app/shared/judge-title.ts @@ -1,5 +1,5 @@ export class JudgeTitle { - id: number; - year: number; - title: string; + id: number = null; + year: number = null; + title: string = ''; } diff --git a/src/app/shared/judge.service.ts b/src/app/shared/judge.service.ts index 84069f0..1dc0dcf 100644 --- a/src/app/shared/judge.service.ts +++ b/src/app/shared/judge.service.ts @@ -1,11 +1,11 @@ -import { Injectable } from '@angular/core'; +import { EventEmitter, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from "@angular/router"; import { HttpClient } from "@angular/common/http"; - -import { Judge } from "./judge"; -import { environment } from '../../environments/environment'; import { Observable } from "rxjs/internal/Observable"; +import { environment } from '../../environments/environment'; +import { Judge } from "./judge"; + @Injectable({ providedIn: 'root' }) @@ -14,6 +14,8 @@ export class JudgeService implements Resolve> { private apiEndPoint = `${environment.apiUrl}/judge`; private cachedJudges: Array = []; + public changed: EventEmitter> = new EventEmitter>(); + constructor(private httpClient: HttpClient) {} public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise> { @@ -30,5 +32,28 @@ export class JudgeService implements Resolve> { set judges(judges: Array) { this.cachedJudges = judges; + this.triggerChange(); + } + + public triggerChange() { + this.changed.emit(this.cachedJudges); + } + + public persist(judge: Judge): Observable { + return judge.id === null + ? this.create(judge) + : this.update(judge); + } + + public create(judge: Judge): Observable { + return this.httpClient.post(this.apiEndPoint, judge); + } + + public update(judge: Judge): Observable { + return this.httpClient.put(`${this.apiEndPoint}/${judge.id}`, judge); + } + + public delete(id: number): Observable { + return this.httpClient.delete(`${this.apiEndPoint}/${id}`); } } diff --git a/src/app/shared/judge.ts b/src/app/shared/judge.ts index ec3b41c..58eb113 100644 --- a/src/app/shared/judge.ts +++ b/src/app/shared/judge.ts @@ -1,7 +1,8 @@ import { JudgeTitle } from "./judge-title"; export class Judge { - public id: number; - public name: string; - public titles: Array; + public id: number = null; + public prefix: string = null; + public name: string = ''; + public titles: Array = []; } diff --git a/src/app/shared/judged-year.ts b/src/app/shared/judged-year.ts deleted file mode 100644 index 9d5ac02..0000000 --- a/src/app/shared/judged-year.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class JudgedYear { - public year: number = null; - public title = ''; -} diff --git a/src/app/shared/year-resolver.service.spec.ts b/src/app/shared/year-resolver.service.spec.ts new file mode 100644 index 0000000..4263a8e --- /dev/null +++ b/src/app/shared/year-resolver.service.spec.ts @@ -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(); + })); +}); diff --git a/src/app/shared/year-resolver.service.ts b/src/app/shared/year-resolver.service.ts new file mode 100644 index 0000000..90ba544 --- /dev/null +++ b/src/app/shared/year-resolver.service.ts @@ -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> { + private apiEndPoint = `${environment.apiUrl}/years`; + + constructor(private httpClient: HttpClient) {} + + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { + return this.httpClient.get>(this.apiEndPoint); + } +}