DMS Layout mit Fehlern
All checks were successful
Build & Deploy PLDpro.Web Test to 192.168.1.100 / build-and-deploy (push) Successful in 1m15s

This commit is contained in:
2026-02-09 21:50:24 +01:00
parent f9fb791dca
commit cae77ef1e3
12 changed files with 709 additions and 1 deletions

View File

@@ -0,0 +1,167 @@
@page "/dms"
@using MudBlazor
@using Pldpro.Web.UI.Models
@inject Pldpro.Web.UI.Services.IDocumentClient Client
@inject NavigationManager Nav
<PageTitle>DMS</PageTitle>
<MudStack Spacing="3">
<!-- Kopfzeile: Titel, Bucket-Auswahl und Aktionen -->
<MudStack Row="true" AlignItems=AlignItems.Center Spacing="2">
<MudText Typo="Typo.h4">Dokumenten-Management</MudText>
<MudSpacer />
<MudSelect T="string" @bind-Value="_bucket" Label="Bucket" Dense="true" Style="min-width:240px">
@foreach (var b in _buckets)
{
<MudSelectItem Value="@b">@b</MudSelectItem>
}
</MudSelect>
<MudButton Variant="Variant.Outlined" OnClick="Reload" StartIcon="@Icons.Material.Filled.Refresh">
Aktualisieren
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Primary"
OnClick="@(() => Nav.NavigateTo("/dms/upload"))"
StartIcon="@Icons.Material.Filled.CloudUpload">
Upload
</MudButton>
<MudButton Variant="Variant.Outlined"
OnClick="@(() => Nav.NavigateTo("/dms/list"))"
StartIcon="@Icons.Material.Filled.List">
Zur Liste
</MudButton>
</MudStack>
<!-- Kennzahlen -->
<MudGrid>
<MudItem xs="12" md="3">
<MudPaper Class="pa-4">
<MudText Typo="Typo.subtitle1">Gesamt</MudText>
<MudText Typo="Typo.h5">@_total.ToString("N0")</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" md="3">
<MudPaper Class="pa-4">
<MudText Typo="Typo.subtitle1">Eingegangen</MudText>
<MudText Typo="Typo.h5">@_eingegangen.ToString("N0")</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" md="3">
<MudPaper Class="pa-4">
<MudText Typo="Typo.subtitle1">Freigegeben</MudText>
<MudText Typo="Typo.h5">@_freigegeben.ToString("N0")</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" md="3">
<MudPaper Class="pa-4">
<MudText Typo="Typo.subtitle1">Bezahlt</MudText>
<MudText Typo="Typo.h5">@_bezahlt.ToString("N0")</MudText>
</MudPaper>
</MudItem>
</MudGrid>
<!-- Zuletzt hinzugefügt + Filter -->
<MudPaper Class="pa-4">
<MudStack Row="true" AlignItems=AlignItems.Center Spacing="2">
<MudText Typo="Typo.h6">Zuletzt hinzugefügt</MudText>
<MudSpacer />
<MudTextField @bind-Value="_query"
Placeholder="Suche (optional)"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
Immediate="true" />
<MudTextField @bind-Value="_prefix" Placeholder="Pfad-Prefix (optional, z.B. rechnungen/2026)" />
<MudButton Variant="Variant.Outlined" OnClick="Reload" StartIcon="@Icons.Material.Filled.Search">
Filtern
</MudButton>
</MudStack>
<MudTable Items="_latest" Dense="true" Hover="true" Class="mt-3">
<HeaderContent>
<MudTh>Datei</MudTh>
<MudTh>Pfad</MudTh>
<MudTh>Größe</MudTh>
<MudTh>Geändert</MudTh>
<MudTh></MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@context.FileName</MudTd>
<MudTd>@context.PathPrefix</MudTd>
<MudTd>@(context.Size?.ToString("N0"))</MudTd>
<MudTd>@context.LastModified</MudTd>
<MudTd Align="TableCellAlign.Right">
<MudTooltip Text="Details">
<MudIconButton Icon="@Icons.Material.Filled.Description"
OnClick="@(() => Nav.NavigateTo($"/dms/detail/{Uri.EscapeDataString(context.Bucket)}/{EncodeKeyForPath(context.Key)}"))" />
</MudTooltip>
<MudTooltip Text="Download">
<MudIconButton Icon="@Icons.Material.Filled.Download"
OnClick="@(() => Download(context))" />
</MudTooltip>
</MudTd>
</RowTemplate>
</MudTable>
</MudPaper>
</MudStack>
@code {
private List<string> _buckets = new();
private string? _bucket;
private string? _query;
private string? _prefix;
private int _total;
private int _eingegangen;
private int _freigegeben;
private int _bezahlt;
private List<DocumentListItem> _latest = new();
protected override async Task OnInitializedAsync()
{
_buckets = (await Client.ListBucketsAsync()).ToList();
_bucket = _buckets.FirstOrDefault();
await Reload();
}
private async Task Reload()
{
if (string.IsNullOrWhiteSpace(_bucket))
{
_total = _eingegangen = _freigegeben = _bezahlt = 0;
_latest = new();
StateHasChanged();
return;
}
// Kennzahlen: einmal komplette Liste (für kleine Datenmengen ok)
var (allItems, total) = await Client.SearchAsync(_bucket!, _query, _prefix, 0, int.MaxValue);
_total = total;
// Aktuell sind Status nur UI-intern; wenn später persistiert, hier echte Counts laden.
_eingegangen = allItems.Count(i => i.Status == DocumentStatus.Eingegangen);
_freigegeben = allItems.Count(i => i.Status == DocumentStatus.Freigegeben);
_bezahlt = allItems.Count(i => i.Status == DocumentStatus.Bezahlt);
// Letzte 10
var (latest, _) = await Client.SearchAsync(_bucket!, _query, _prefix, 0, 10);
_latest = latest
.OrderByDescending(i => i.LastModified ?? DateTime.MinValue)
.Take(10)
.ToList();
StateHasChanged();
}
private void Download(DocumentListItem item)
{
var url = Client.GetDownloadUrl(item.Bucket, item.Key);
Nav.NavigateTo(url, forceLoad: true);
}
private static string EncodeKeyForPath(string key)
=> string.Join("/", (key ?? string.Empty)
.Split('/', StringSplitOptions.RemoveEmptyEntries)
.Select(Uri.EscapeDataString));
}