From bb6b045a2a10b2024e3ba7e170e75e5c9ed0ebbb Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Mon, 3 Aug 2026 16:04:31 +0200 Subject: [PATCH] feat: add CreateTeamDialog component --- .../create-team-dialog.spec.ts | 72 +++++++++++++++++++ .../create-team-dialog/create-team-dialog.ts | 59 +++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.spec.ts create mode 100644 myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.ts diff --git a/myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.spec.ts b/myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.spec.ts new file mode 100644 index 0000000..c5f6d85 --- /dev/null +++ b/myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.spec.ts @@ -0,0 +1,72 @@ +import { TestBed } from '@angular/core/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; +import { MatDialogRef } from '@angular/material/dialog'; +import { CreateTeamDialog } from './create-team-dialog'; +import { environment } from '../../../../environments/environment'; + +describe('CreateTeamDialog', () => { + let dialogRef: { close: ReturnType }; + let httpMock: HttpTestingController; + + beforeEach(async () => { + dialogRef = { close: vi.fn() }; + await TestBed.configureTestingModule({ + imports: [CreateTeamDialog], + providers: [ + provideHttpClient(), + provideHttpClientTesting(), + { provide: MatDialogRef, useValue: dialogRef }, + ], + }).compileComponents(); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + it('keeps the submit button disabled until a team name is entered', () => { + const fixture = TestBed.createComponent(CreateTeamDialog); + fixture.detectChanges(); + + expect(fixture.componentInstance['form'].invalid).toBe(true); + + fixture.componentInstance['form'].controls.name.setValue('1. Herren'); + expect(fixture.componentInstance['form'].invalid).toBe(false); + }); + + it('creates the team and closes the dialog with the created team', () => { + const fixture = TestBed.createComponent(CreateTeamDialog); + fixture.detectChanges(); + + fixture.componentInstance['form'].controls.name.setValue('1. Herren'); + fixture.componentInstance['submit'](); + + const request = httpMock.expectOne(`${environment.apiUrl}teams`); + expect(request.request.body).toEqual({ name: '1. Herren' }); + request.flush({ id: 9, name: '1. Herren', alias: 'a', balance: 0 }); + + expect(dialogRef.close).toHaveBeenCalledWith({ + id: 9, + name: '1. Herren', + alias: 'a', + balance: 0, + }); + }); + + it('re-enables the form and keeps the dialog open when the request fails', () => { + const fixture = TestBed.createComponent(CreateTeamDialog); + fixture.detectChanges(); + + fixture.componentInstance['form'].controls.name.setValue('1. Herren'); + fixture.componentInstance['submit'](); + + httpMock + .expectOne(`${environment.apiUrl}teams`) + .flush('error', { status: 500, statusText: 'Server Error' }); + + expect(dialogRef.close).not.toHaveBeenCalled(); + expect(fixture.componentInstance['saving']()).toBe(false); + }); +}); diff --git a/myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.ts b/myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.ts new file mode 100644 index 0000000..a453f62 --- /dev/null +++ b/myteamwallet_frontend_modern/src/app/features/team-select/create-team-dialog/create-team-dialog.ts @@ -0,0 +1,59 @@ +import { Component, inject, signal } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { Team } from '../../../models/team.model'; +import { TeamsApi } from '../../../core/team/teams-api'; + +@Component({ + selector: 'app-create-team-dialog', + imports: [ReactiveFormsModule, MatButtonModule, MatDialogModule, MatFormFieldModule, MatInputModule], + template: ` +

Team erstellen

+
+ + + Teamname + + + + + + + +
+ `, + styles: [ + ` + .full-width { + width: 100%; + } + `, + ], +}) +export class CreateTeamDialog { + protected readonly dialogRef = inject(MatDialogRef); + private readonly formBuilder = inject(FormBuilder); + private readonly teamsApi = inject(TeamsApi); + + protected readonly saving = signal(false); + protected readonly form = this.formBuilder.nonNullable.group({ + name: ['', Validators.required], + }); + + protected submit(): void { + if (this.form.invalid || this.saving()) return; + this.saving.set(true); + this.teamsApi.createTeam(this.form.getRawValue()).subscribe({ + next: (team) => { + this.saving.set(false); + this.dialogRef.close(team); + }, + error: () => this.saving.set(false), + }); + } +}