From 68e4e1aa4b5f38d59ae986b20854f60235df199b Mon Sep 17 00:00:00 2001 From: troogs Date: Tue, 31 Mar 2026 18:05:45 +0200 Subject: [PATCH] Add club settings and use club name in PDF exports --- src/Duempelkas.App/Pages/Dashboard.razor | 11 +++- src/Duempelkas.App/Pages/Settings.razor | 50 +++++++++++++++++ .../Services/ISettingsService.cs | 7 +++ .../DependencyInjection.cs | 1 + .../Services/PdfStatementService.cs | 7 ++- .../Services/SettingsService.cs | 55 +++++++++++++++++++ 6 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 src/Duempelkas.App/Pages/Settings.razor create mode 100644 src/Duempelkas.App/Services/ISettingsService.cs create mode 100644 src/Duempelkas.Infrastructure/Services/SettingsService.cs diff --git a/src/Duempelkas.App/Pages/Dashboard.razor b/src/Duempelkas.App/Pages/Dashboard.razor index f246c26..6ae53b6 100644 --- a/src/Duempelkas.App/Pages/Dashboard.razor +++ b/src/Duempelkas.App/Pages/Dashboard.razor @@ -4,9 +4,14 @@

Übersicht

- +
+ + + Einstellungen + +
@if (accounts == null) diff --git a/src/Duempelkas.App/Pages/Settings.razor b/src/Duempelkas.App/Pages/Settings.razor new file mode 100644 index 0000000..243e76b --- /dev/null +++ b/src/Duempelkas.App/Pages/Settings.razor @@ -0,0 +1,50 @@ +@page "/settings" +@inject ISettingsService SettingsService + +
+
+

Einstellungen

+ + Zurück + +
+ +
+
+
Verein
+
+ + +
Wird im PDF-Auszug als Kopfzeile verwendet.
+
+ + @if (saved) + { + Gespeichert + } +
+
+
+ +@code { + private string clubName = string.Empty; + private bool saving; + private bool saved; + + protected override async Task OnInitializedAsync() + { + clubName = await SettingsService.GetClubNameAsync() ?? string.Empty; + } + + private async Task Save() + { + saving = true; + saved = false; + await SettingsService.SetClubNameAsync(clubName); + saved = true; + saving = false; + } +} diff --git a/src/Duempelkas.App/Services/ISettingsService.cs b/src/Duempelkas.App/Services/ISettingsService.cs new file mode 100644 index 0000000..38ca199 --- /dev/null +++ b/src/Duempelkas.App/Services/ISettingsService.cs @@ -0,0 +1,7 @@ +namespace Duempelkas.App.Services; + +public interface ISettingsService +{ + Task GetClubNameAsync(); + Task SetClubNameAsync(string? clubName); +} diff --git a/src/Duempelkas.Infrastructure/DependencyInjection.cs b/src/Duempelkas.Infrastructure/DependencyInjection.cs index 0ef170c..32e4fae 100644 --- a/src/Duempelkas.Infrastructure/DependencyInjection.cs +++ b/src/Duempelkas.Infrastructure/DependencyInjection.cs @@ -18,6 +18,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddSingleton(); return services; } diff --git a/src/Duempelkas.Infrastructure/Services/PdfStatementService.cs b/src/Duempelkas.Infrastructure/Services/PdfStatementService.cs index b611982..ab58b69 100644 --- a/src/Duempelkas.Infrastructure/Services/PdfStatementService.cs +++ b/src/Duempelkas.Infrastructure/Services/PdfStatementService.cs @@ -15,13 +15,15 @@ public class PdfStatementService : IPdfStatementService private readonly FinanceDbContext _db; private readonly IEntryService _entryService; private readonly IBalanceQueryService _balanceQueryService; + private readonly ISettingsService _settingsService; private static readonly CultureInfo DeLocale = new("de-DE"); - public PdfStatementService(FinanceDbContext db, IEntryService entryService, IBalanceQueryService balanceQueryService) + public PdfStatementService(FinanceDbContext db, IEntryService entryService, IBalanceQueryService balanceQueryService, ISettingsService settingsService) { _db = db; _entryService = entryService; _balanceQueryService = balanceQueryService; + _settingsService = settingsService; } public async Task GenerateStatementAsync(int accountId, bool currentYearOnly) @@ -31,6 +33,7 @@ public class PdfStatementService : IPdfStatementService var entries = await _entryService.GetEntriesAsync(accountId, currentYearOnly); var balance = await _balanceQueryService.GetAccountBalanceAsync(accountId); + var clubName = await _settingsService.GetClubNameAsync() ?? "Mein Verein"; var title = currentYearOnly ? $"{account.Name} – Auszug {DateTime.Now.Year}" @@ -47,7 +50,7 @@ public class PdfStatementService : IPdfStatementService page.Header().Column(col => { - col.Item().Text(account.Name).Bold().FontSize(20); + col.Item().Text(clubName).Bold().FontSize(20); col.Item().Text(title).FontSize(14).FontColor(Colors.Grey.Darken1); col.Item().PaddingTop(5).Text($"Erstellt am: {DateTime.Now:dd.MM.yyyy}").FontSize(8).FontColor(Colors.Grey.Medium); col.Item().PaddingTop(10).LineHorizontal(1).LineColor(Colors.Grey.Lighten2); diff --git a/src/Duempelkas.Infrastructure/Services/SettingsService.cs b/src/Duempelkas.Infrastructure/Services/SettingsService.cs new file mode 100644 index 0000000..78da1ab --- /dev/null +++ b/src/Duempelkas.Infrastructure/Services/SettingsService.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using Duempelkas.App.Services; + +namespace Duempelkas.Infrastructure.Services; + +public class SettingsService : ISettingsService +{ + private static readonly string SettingsPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Duempelkas", "settings.json"); + + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + public async Task GetClubNameAsync() + { + var settings = await LoadAsync(); + return settings.ClubName; + } + + public async Task SetClubNameAsync(string? clubName) + { + var settings = await LoadAsync(); + settings.ClubName = string.IsNullOrWhiteSpace(clubName) ? null : clubName.Trim(); + await SaveAsync(settings); + } + + private static async Task LoadAsync() + { + if (!File.Exists(SettingsPath)) + return new AppSettings(); + + try + { + var json = await File.ReadAllTextAsync(SettingsPath); + return JsonSerializer.Deserialize(json) ?? new AppSettings(); + } + catch + { + return new AppSettings(); + } + } + + private static async Task SaveAsync(AppSettings settings) + { + var dir = Path.GetDirectoryName(SettingsPath)!; + Directory.CreateDirectory(dir); + var json = JsonSerializer.Serialize(settings, JsonOptions); + await File.WriteAllTextAsync(SettingsPath, json); + } + + private class AppSettings + { + public string? ClubName { get; set; } + } +}