Add club settings and use club name in PDF exports
This commit is contained in:
@@ -4,9 +4,14 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2>Übersicht</h2>
|
<h2>Übersicht</h2>
|
||||||
<button class="btn btn-primary" @onclick="() => showAddAccount = true">
|
<div class="d-flex gap-2">
|
||||||
<i class="bi bi-plus-lg"></i> Neues Konto
|
<button class="btn btn-primary" @onclick="() => showAddAccount = true">
|
||||||
</button>
|
<i class="bi bi-plus-lg"></i> Neues Konto
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-outline-secondary" href="/settings">
|
||||||
|
<i class="bi bi-gear"></i> Einstellungen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (accounts == null)
|
@if (accounts == null)
|
||||||
|
|||||||
50
src/Duempelkas.App/Pages/Settings.razor
Normal file
50
src/Duempelkas.App/Pages/Settings.razor
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
@page "/settings"
|
||||||
|
@inject ISettingsService SettingsService
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h2>Einstellungen</h2>
|
||||||
|
<a class="btn btn-outline-secondary" href="/">
|
||||||
|
<i class="bi bi-arrow-left"></i> Zurück
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3">Verein</h5>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Name des Vereins</label>
|
||||||
|
<input type="text" class="form-control" @bind="clubName" @bind:event="oninput"
|
||||||
|
placeholder="Mein Verein" />
|
||||||
|
<div class="form-text">Wird im PDF-Auszug als Kopfzeile verwendet.</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" @onclick="Save" disabled="@saving">
|
||||||
|
<i class="bi bi-check-lg"></i> Speichern
|
||||||
|
</button>
|
||||||
|
@if (saved)
|
||||||
|
{
|
||||||
|
<span class="text-success ms-2"><i class="bi bi-check-circle"></i> Gespeichert</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/Duempelkas.App/Services/ISettingsService.cs
Normal file
7
src/Duempelkas.App/Services/ISettingsService.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Duempelkas.App.Services;
|
||||||
|
|
||||||
|
public interface ISettingsService
|
||||||
|
{
|
||||||
|
Task<string?> GetClubNameAsync();
|
||||||
|
Task SetClubNameAsync(string? clubName);
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ public static class DependencyInjection
|
|||||||
services.AddScoped<IBalanceQueryService, BalanceQueryService>();
|
services.AddScoped<IBalanceQueryService, BalanceQueryService>();
|
||||||
services.AddScoped<IPdfStatementService, PdfStatementService>();
|
services.AddScoped<IPdfStatementService, PdfStatementService>();
|
||||||
services.AddScoped<IFileSaveService, FileSaveService>();
|
services.AddScoped<IFileSaveService, FileSaveService>();
|
||||||
|
services.AddSingleton<ISettingsService, SettingsService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,15 @@ public class PdfStatementService : IPdfStatementService
|
|||||||
private readonly FinanceDbContext _db;
|
private readonly FinanceDbContext _db;
|
||||||
private readonly IEntryService _entryService;
|
private readonly IEntryService _entryService;
|
||||||
private readonly IBalanceQueryService _balanceQueryService;
|
private readonly IBalanceQueryService _balanceQueryService;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
private static readonly CultureInfo DeLocale = new("de-DE");
|
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;
|
_db = db;
|
||||||
_entryService = entryService;
|
_entryService = entryService;
|
||||||
_balanceQueryService = balanceQueryService;
|
_balanceQueryService = balanceQueryService;
|
||||||
|
_settingsService = settingsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]> GenerateStatementAsync(int accountId, bool currentYearOnly)
|
public async Task<byte[]> GenerateStatementAsync(int accountId, bool currentYearOnly)
|
||||||
@@ -31,6 +33,7 @@ public class PdfStatementService : IPdfStatementService
|
|||||||
|
|
||||||
var entries = await _entryService.GetEntriesAsync(accountId, currentYearOnly);
|
var entries = await _entryService.GetEntriesAsync(accountId, currentYearOnly);
|
||||||
var balance = await _balanceQueryService.GetAccountBalanceAsync(accountId);
|
var balance = await _balanceQueryService.GetAccountBalanceAsync(accountId);
|
||||||
|
var clubName = await _settingsService.GetClubNameAsync() ?? "Mein Verein";
|
||||||
|
|
||||||
var title = currentYearOnly
|
var title = currentYearOnly
|
||||||
? $"{account.Name} – Auszug {DateTime.Now.Year}"
|
? $"{account.Name} – Auszug {DateTime.Now.Year}"
|
||||||
@@ -47,7 +50,7 @@ public class PdfStatementService : IPdfStatementService
|
|||||||
|
|
||||||
page.Header().Column(col =>
|
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().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(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);
|
col.Item().PaddingTop(10).LineHorizontal(1).LineColor(Colors.Grey.Lighten2);
|
||||||
|
|||||||
55
src/Duempelkas.Infrastructure/Services/SettingsService.cs
Normal file
55
src/Duempelkas.Infrastructure/Services/SettingsService.cs
Normal file
@@ -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<string?> 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<AppSettings> LoadAsync()
|
||||||
|
{
|
||||||
|
if (!File.Exists(SettingsPath))
|
||||||
|
return new AppSettings();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = await File.ReadAllTextAsync(SettingsPath);
|
||||||
|
return JsonSerializer.Deserialize<AppSettings>(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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user