Forráskód Böngészése

Track selection for events

Lukas Angerer 3 éve
szülő
commit
c71041d60b

+ 10 - 4
src/RunnersMeet.Client/src/app/app.module.ts

@@ -34,12 +34,15 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
 import { MatFormFieldModule } from '@angular/material/form-field';
 import { MatInputModule } from '@angular/material/input';
 import { MatDatepickerModule } from '@angular/material/datepicker';
+import { MatDialogModule } from '@angular/material/dialog';
 import { EventsPageComponent } from './pages/events-page/events-page.component';
 import { EditEventPageComponent } from './pages/edit-event-page/edit-event-page.component';
 import { EventsApiService } from './events-api.service';
 import { EventCreateComponent } from './events/event-create/event-create.component';
 import { EventEditComponent } from './events/event-edit/event-edit.component';
 import { MatNativeDateModule, MAT_DATE_LOCALE } from '@angular/material/core';
+import { TrackPickerComponent } from './tracks/track-picker/track-picker.component';
+import { TrackPickerDialogComponent } from './tracks/track-picker-dialog/track-picker-dialog.component';
 
 @NgModule({
 	declarations: [
@@ -53,10 +56,12 @@ import { MatNativeDateModule, MAT_DATE_LOCALE } from '@angular/material/core';
 		TrackViewComponent,
 		ViewTrackPageComponent,
 		MainMenuComponent,
-  EventsPageComponent,
-  EditEventPageComponent,
-  EventCreateComponent,
-  EventEditComponent,
+		EventsPageComponent,
+		EditEventPageComponent,
+		EventCreateComponent,
+		EventEditComponent,
+		TrackPickerComponent,
+		TrackPickerDialogComponent,
 	],
 	imports: [
 		BrowserModule,
@@ -77,6 +82,7 @@ import { MatNativeDateModule, MAT_DATE_LOCALE } from '@angular/material/core';
 		MatInputModule,
 		MatDatepickerModule,
 		MatNativeDateModule,
+		MatDialogModule,
 	],
 	providers: [
 		ConfigService,

+ 4 - 0
src/RunnersMeet.Client/src/app/events/event-edit/event-edit.component.html

@@ -16,6 +16,10 @@
 		<mat-label>Event start time</mat-label>
 		<input type="time" matInput name="startTime" [(ngModel)]="startTime" required>
 	</mat-form-field>
+	<div>
+		<label>Track</label>
+		<app-track-picker [(track)]="event.track"></app-track-picker>
+	</div>
 	<div class="button-group">
 		<button type="submit" mat-raised-button color="primary">Save</button>
 		<button *ngIf="!isCreateMode" type="submit" mat-raised-button color="warn" (click)="deleteEvent()">Delete</button>

+ 27 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker-dialog/track-picker-dialog.component.html

@@ -0,0 +1,27 @@
+<h1 mat-dialog-title>Choose a Track</h1>
+<div mat-dialog-content>
+	<form class="vertical-form" (ngSubmit)="search($event)">
+		<mat-form-field appearance="fill">
+			<mat-label>Filter text</mat-label>
+			<input matInput name="nameFilter" [(ngModel)]="nameFilter">
+		</mat-form-field>
+
+		<div class="button-group">
+			<button type="submit" mat-raised-button color="accent">Search</button>
+		</div>
+	</form>
+
+	<mat-list role="list">
+		<mat-list-item role="listitem" *ngFor="let track of tracks | async">
+			<div matListItemTitle>{{ track.displayName }}</div>
+			<div matListItemLine>{{ track.distance }}</div>
+			<div matListItemMeta><button type="button" mat-button (click)="onPick(track)">Pick</button></div>
+		</mat-list-item>
+	</mat-list>
+
+	<button mat-raised-button color="accent" [disabled]="!hasMore" (click)="loadMore()">More</button>
+</div>
+<div mat-dialog-actions>
+  <button mat-button (click)="onCancelClick()">Cancel</button>
+  <button mat-button (click)="onPick(null)">None</button>
+</div>

+ 0 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker-dialog/track-picker-dialog.component.scss


+ 23 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker-dialog/track-picker-dialog.component.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TrackPickerDialogComponent } from './track-picker-dialog.component';
+
+describe('TrackPickerDialogComponent', () => {
+  let component: TrackPickerDialogComponent;
+  let fixture: ComponentFixture<TrackPickerDialogComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ TrackPickerDialogComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(TrackPickerDialogComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 59 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker-dialog/track-picker-dialog.component.ts

@@ -0,0 +1,59 @@
+import { Component } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { Subject } from 'rxjs';
+import { TracksApiService } from 'src/app/tracks-api.service';
+import { Track } from '../track';
+import { TrackSearchParams } from '../track-search-params';
+
+@Component({
+	selector: 'app-track-picker-dialog',
+	templateUrl: './track-picker-dialog.component.html',
+	styleUrls: ['./track-picker-dialog.component.scss']
+})
+export class TrackPickerDialogComponent {
+	private _tracks: Track[] = [];
+	public tracks: Subject<Track[]> = new Subject<Track[]>();
+	public hasMore: boolean = false;
+
+	public nameFilter: string = '';
+	private searchParams: TrackSearchParams = new TrackSearchParams();
+
+	constructor(
+		public dialogRef: MatDialogRef<TrackPickerDialogComponent>,
+		private readonly tracksApi: TracksApiService
+	) {
+		this.updateTracks();
+	}
+
+	onCancelClick(): void {
+		this.dialogRef.close();
+	}
+
+	public onPick(track: Track | null): void {
+		this.dialogRef.close(track);
+	}
+
+	public search(event: SubmitEvent): void {
+		this.searchParams = new TrackSearchParams();
+		this.searchParams.filter = this.nameFilter ? this.nameFilter : undefined;
+		this.updateTracks();
+	}
+
+	public loadMore(): void {
+		this.searchParams.page++;
+		this.updateTracks();
+	}
+
+	private updateTracks(): void {
+		this.tracksApi.getTracks(this.searchParams)
+			.then(trackPage => {
+				if (this.searchParams.page === 0) {
+					this._tracks = trackPage.items;
+				} else {
+					this._tracks.push(...trackPage.items);
+				}
+				this.hasMore = trackPage.hasMore;
+				this.tracks.next(this._tracks);
+			});
+	}
+}

+ 2 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker/track-picker.component.html

@@ -0,0 +1,2 @@
+<button type="button" mat-raised-button (click)="pick()">Choose a Track</button>
+{{ track?.displayName ?? 'No Track' }}

+ 4 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker/track-picker.component.scss

@@ -0,0 +1,4 @@
+:host {
+	display: block;
+	margin-bottom: 1em;
+}

+ 23 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker/track-picker.component.spec.ts

@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TrackPickerComponent } from './track-picker.component';
+
+describe('TrackPickerComponent', () => {
+  let component: TrackPickerComponent;
+  let fixture: ComponentFixture<TrackPickerComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ TrackPickerComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(TrackPickerComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 38 - 0
src/RunnersMeet.Client/src/app/tracks/track-picker/track-picker.component.ts

@@ -0,0 +1,38 @@
+import { Component, Input, Output } from '@angular/core';
+import { MatDialog } from '@angular/material/dialog';
+import { Observable, Subject } from 'rxjs';
+import { Track } from '../track';
+import { TrackPickerDialogComponent } from '../track-picker-dialog/track-picker-dialog.component';
+
+@Component({
+	selector: 'app-track-picker',
+	templateUrl: './track-picker.component.html',
+	styleUrls: ['./track-picker.component.scss']
+})
+export class TrackPickerComponent {
+	private readonly trackChangeSubject: Subject<Track | undefined> = new Subject<Track | undefined>();
+
+	@Input()
+	public track: Track | undefined;
+
+	@Output()
+	public trackChange: Observable<Track | undefined> = this.trackChangeSubject.asObservable();
+
+	public constructor(private readonly dialog: MatDialog) {
+	}
+
+	public pick(): void {
+		const dialogRef = this.dialog.open(TrackPickerDialogComponent, {
+			width: '60%',
+			height: '80%',
+		});
+
+		dialogRef.afterClosed().subscribe(result => {
+			console.log('The dialog was closed', result);
+			if (result === null || result) {
+				this.track = result;
+				this.trackChangeSubject.next(result);
+			}
+		});
+	}
+}

+ 1 - 1
src/RunnersMeet.Server/Persistence/EventQuery.cs

@@ -14,6 +14,6 @@ public class EventQuery : IRequestHandler<ObjectId, Event>
 
 	public Event Handle(ObjectId trackId)
 	{
-		return _database.Events.FindById(trackId);
+		return _database.Events.Include(e => e.Track).FindById(trackId);
 	}
 }