From 5c3ff526895a7131cc177a0625467aba8fc85892 Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Tue, 4 Aug 2026 08:24:57 +0200 Subject: [PATCH] feat: add CashboxExportDialog --- .../cashbox-export-dialog.spec.ts | 49 ++++++++++++ .../cashbox-export-dialog.ts | 79 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.spec.ts create mode 100644 myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.ts diff --git a/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.spec.ts b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.spec.ts new file mode 100644 index 0000000..05dcdb1 --- /dev/null +++ b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.spec.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { of } from 'rxjs'; +import { CashboxExportApi } from '../../../../core/team/cashbox-export-api'; +import { FileDownloadService } from '../../../../shared/file-download/file-download.service'; +import { CashboxExportDialog } from './cashbox-export-dialog'; + +describe('CashboxExportDialog', () => { + let fixture: ComponentFixture; + let exportCashbox: ReturnType; + let save: ReturnType; + let dialogRef: { close: ReturnType }; + + beforeEach(async () => { + exportCashbox = vi.fn(() => of(new Blob(['csv content']))); + save = vi.fn(); + dialogRef = { close: vi.fn() }; + + await TestBed.configureTestingModule({ + imports: [CashboxExportDialog], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: { teamId: 5 } }, + { provide: MatDialogRef, useValue: dialogRef }, + { provide: CashboxExportApi, useValue: { exportCashbox } }, + { provide: FileDownloadService, useValue: { save } }, + ], + }).compileComponents(); + fixture = TestBed.createComponent(CashboxExportDialog); + fixture.detectChanges(); + }); + + it('keeps the download disabled until from <= to', () => { + fixture.componentInstance['form'].setValue({ from: '2026-08-31', to: '2026-08-01', format: 'csv' }); + expect(fixture.componentInstance['form'].invalid).toBe(true); + + fixture.componentInstance['download'](); + expect(exportCashbox).not.toHaveBeenCalled(); + }); + + it('downloads the file and closes the dialog on success', () => { + fixture.componentInstance['form'].setValue({ from: '2026-08-01', to: '2026-08-31', format: 'csv' }); + + fixture.componentInstance['download'](); + + expect(exportCashbox).toHaveBeenCalledWith(5, '2026-08-01', '2026-08-31', 'csv'); + expect(save).toHaveBeenCalledWith(expect.any(Blob), 'kassenbuch_2026-08-01_2026-08-31.csv'); + expect(dialogRef.close).toHaveBeenCalled(); + }); +}); diff --git a/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.ts b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.ts new file mode 100644 index 0000000..2bc86c2 --- /dev/null +++ b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-dialog/cashbox-export-dialog.ts @@ -0,0 +1,79 @@ +import { Component, inject } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { CashboxExportApi } from '../../../../core/team/cashbox-export-api'; +import { CashboxExportFormat } from '../../../../models/cashbox-export.model'; +import { FileDownloadService } from '../../../../shared/file-download/file-download.service'; + +const rangeValid: ValidatorFn = (group): ValidationErrors | null => { + const from = group.get('from')?.value; + const to = group.get('to')?.value; + return from && to && from > to ? { rangeInvalid: true } : null; +}; + +@Component({ + selector: 'app-cashbox-export-dialog', + imports: [ + ReactiveFormsModule, + MatButtonModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + ], + template: ` +

Kassenbuch exportieren

+
+ + + Von + + + + Bis + + + + Format + + CSV + PDF + + + + + + + +
+ `, +}) +export class CashboxExportDialog { + protected readonly dialogRef = inject(MatDialogRef); + private readonly data = inject<{ teamId: number }>(MAT_DIALOG_DATA); + private readonly formBuilder = inject(FormBuilder); + private readonly api = inject(CashboxExportApi); + private readonly fileDownload = inject(FileDownloadService); + + protected readonly form = this.formBuilder.nonNullable.group( + { + from: ['', Validators.required], + to: ['', Validators.required], + format: ['csv' as CashboxExportFormat, Validators.required], + }, + { validators: rangeValid }, + ); + + protected download(): void { + if (this.form.invalid) return; + const { from, to, format } = this.form.getRawValue(); + this.api.exportCashbox(this.data.teamId, from, to, format).subscribe((blob) => { + this.fileDownload.save(blob, `kassenbuch_${from}_${to}.${format}`); + this.dialogRef.close(); + }); + } +}