Dasboard und Workspace-Anlage
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
<MudThemeProvider Theme="@_myCustomTheme" IsDarkMode="true" />
|
<MudThemeProvider Theme="@_myCustomTheme" IsDarkMode="true" />
|
||||||
<MudDialogProvider />
|
<MudDialogProvider />
|
||||||
<MudSnackbarProvider />
|
<MudSnackbarProvider />
|
||||||
|
<MudPopoverProvider />
|
||||||
|
|
||||||
<MudLayout>
|
<MudLayout>
|
||||||
<MudMainContent>
|
<MudMainContent>
|
||||||
|
|||||||
+107
-14
@@ -1,40 +1,133 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
|
@using ZahlenAnalyse.Web.Models
|
||||||
|
@using ZahlenAnalyse.Web.Services
|
||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@inject WorkspaceService DbService
|
||||||
@inject AuthenticationStateProvider AuthStateProvider
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
|
||||||
<MudText Typo="Typo.h3" Class="mb-4">Zahlen-Analyse</MudText>
|
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="mb-8">
|
||||||
|
<MudText Typo="Typo.h3">Meine Workspaces</MudText>
|
||||||
|
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<MudText Typo="Typo.body1">Willkommen zurück, @context.User.Identity?.Name!</MudText>
|
<MudButton Href="/workspaces/create"
|
||||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Deine Pocket-ID (Sub): @_userId</MudText>
|
Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
<MudButton Href="/logout" Variant="Variant.Filled" Color="Color.Error" Class="mt-2">
|
StartIcon="@Icons.Material.Filled.Add">
|
||||||
Abmelden
|
Neuer Workspace
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
|
</AuthorizeView>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<AuthorizeView>
|
||||||
|
<Authorized>
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Color="Color.Primary" Indeterminate="true" />
|
||||||
|
}
|
||||||
|
else if (!_workspaces.Any())
|
||||||
|
{
|
||||||
|
<MudPaper Class="pa-8 text-center" Elevation="1">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.FolderOpen" Size="Size.Large" Color="Color.Default" Class="mb-4" />
|
||||||
|
<MudText Typo="Typo.h5" Class="mb-2">Noch keine Workspaces vorhanden</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Color="Color.Secondary" Class="mb-6">
|
||||||
|
Erstelle deinen ersten Workspace (z.B. Urlaubsabrechnung), um mit der Datenanalyse zu beginnen.
|
||||||
|
</MudText>
|
||||||
|
<MudButton Href="/workspaces/create" Variant="Variant.Outlined" Color="Color.Primary">
|
||||||
|
Jetzt erstellen
|
||||||
|
</MudButton>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudGrid>
|
||||||
|
@foreach (var ws in _workspaces)
|
||||||
|
{
|
||||||
|
<MudItem xs="12" sm="6" md="4">
|
||||||
|
<MudCard Elevation="2" Class="h-100">
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudText Typo="Typo.h6">@ws.Name</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">
|
||||||
|
Erstellt am @ws.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy")
|
||||||
|
</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
<CardHeaderActions>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Settings" Color="Color.Default" />
|
||||||
|
</CardHeaderActions>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
@ws.Dimensions.Count Dimensionen konfiguriert
|
||||||
|
</MudText>
|
||||||
|
<MudStack Row="true" Spacing="1" Class="mt-2 flex-wrap">
|
||||||
|
@foreach (var dim in ws.Dimensions.Take(3))
|
||||||
|
{
|
||||||
|
<MudChip T="string" Size="Size.Small" Variant="Variant.Outlined">@dim.Name</MudChip>
|
||||||
|
}
|
||||||
|
@if (ws.Dimensions.Count > 3)
|
||||||
|
{
|
||||||
|
<MudChip T="string" Size="Size.Small" Variant="Variant.Text">+@(ws.Dimensions.Count - 3)</MudChip>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary">
|
||||||
|
Daten erfassen
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Secondary">
|
||||||
|
Auswertung
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
</MudGrid>
|
||||||
|
}
|
||||||
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
<MudText Typo="Typo.body1" Class="mb-2">Bitte melde dich an, um deine Workspaces zu verwalten.</MudText>
|
<MudPaper Class="pa-8 text-center" Elevation="1">
|
||||||
|
<MudText Typo="Typo.h5" Class="mb-4">Willkommen beim Zahlen-Analyse Tool</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-6">Bitte melde dich an, um deine Daten zu verwalten.</MudText>
|
||||||
<MudButton Href="/login" Variant="Variant.Filled" Color="Color.Primary">
|
<MudButton Href="/login" Variant="Variant.Filled" Color="Color.Primary">
|
||||||
Mit Pocket-ID anmelden
|
Mit Pocket-ID anmelden
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudPaper>
|
||||||
</NotAuthorized>
|
</NotAuthorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
</MudContainer>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _userId = string.Empty;
|
private List<Workspace> _workspaces = new();
|
||||||
|
private bool _isLoading = true;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
var user = authState.User;
|
|
||||||
|
|
||||||
if (user.Identity?.IsAuthenticated == true)
|
if (authState.User.Identity?.IsAuthenticated == true)
|
||||||
{
|
{
|
||||||
// Das ist der "sub"-Claim (Subject), den wir als OwnerId in RavenDB nutzen
|
await LoadWorkspaces();
|
||||||
_userId = user.FindFirst(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value
|
}
|
||||||
?? user.FindFirst("sub")?.Value
|
else
|
||||||
?? string.Empty;
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadWorkspaces()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Dank unseres Services reicht hier ein simpler Aufruf!
|
||||||
|
_workspaces = await DbService.GetWorkspacesForUserAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
@using ZahlenAnalyse.Web.Models
|
||||||
|
|
||||||
|
<div class="pl-6 mt-2 border-l-2" style="border-color: var(--mud-palette-primary);">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mb-2">
|
||||||
|
<MudTextField @bind-Value="Node.Name"
|
||||||
|
Placeholder="z.B. Italien oder Maut"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudTooltip Text="Unterkategorie hinzufügen">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.SubdirectoryArrowRight"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Info"
|
||||||
|
OnClick="AddChild" />
|
||||||
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudTooltip Text="Knoten löschen">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Error"
|
||||||
|
OnClick="() => OnRemove.InvokeAsync(Node)" />
|
||||||
|
</MudTooltip>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@foreach (var child in Node.Children.ToList())
|
||||||
|
{
|
||||||
|
<NodeEditor Node="child" OnRemove="RemoveChild" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public DimensionNode Node { get; set; } = default!;
|
||||||
|
[Parameter] public EventCallback<DimensionNode> OnRemove { get; set; }
|
||||||
|
|
||||||
|
private void AddChild()
|
||||||
|
{
|
||||||
|
Node.Children.Add(new DimensionNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveChild(DimensionNode child)
|
||||||
|
{
|
||||||
|
Node.Children.Remove(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
@page "/workspaces/create"
|
||||||
|
@using ZahlenAnalyse.Web.Models
|
||||||
|
@using ZahlenAnalyse.Web.Services
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@inject WorkspaceService DbService
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
@inject NavigationManager NavManager
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Medium" Class="mt-8 mb-8">
|
||||||
|
<MudText Typo="Typo.h4" Class="mb-6">Neuen Workspace erstellen</MudText>
|
||||||
|
|
||||||
|
<MudPaper Class="pa-6 mb-6" Elevation="1">
|
||||||
|
<MudTextField @bind-Value="_workspace.Name"
|
||||||
|
Label="Name des Workspaces (z.B. Urlaubsabrechnung)"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true" />
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h5" Class="mb-4">Analysedimensionen</MudText>
|
||||||
|
|
||||||
|
@foreach (var dim in _workspace.Dimensions.ToList())
|
||||||
|
{
|
||||||
|
<MudCard Class="mb-4" Elevation="1">
|
||||||
|
<MudCardContent>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mb-4">
|
||||||
|
<MudTextField @bind-Value="dim.Name"
|
||||||
|
Label="Name der Dimension (z.B. Ort oder Kostenart)"
|
||||||
|
Variant="Variant.Outlined" />
|
||||||
|
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||||
|
Color="Color.Error"
|
||||||
|
OnClick="() => _workspace.Dimensions.Remove(dim)" />
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.subtitle2" Class="mb-2">Hierarchie-Knoten:</MudText>
|
||||||
|
|
||||||
|
@foreach (var rootNode in dim.Nodes.ToList())
|
||||||
|
{
|
||||||
|
<NodeEditor Node="rootNode" OnRemove="(n) => dim.Nodes.Remove(n)" />
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudButton Variant="Variant.Text"
|
||||||
|
StartIcon="@Icons.Material.Filled.Add"
|
||||||
|
Color="Color.Success"
|
||||||
|
OnClick="() => dim.Nodes.Add(new DimensionNode())"
|
||||||
|
Class="mt-2">
|
||||||
|
Haupt-Knoten hinzufügen
|
||||||
|
</MudButton>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
StartIcon="@Icons.Material.Filled.AddBox"
|
||||||
|
Color="Color.Info"
|
||||||
|
OnClick="() => _workspace.Dimensions.Add(new DimensionDefinition())"
|
||||||
|
Class="mb-8">
|
||||||
|
Neue Dimension hinzufügen
|
||||||
|
</MudButton>
|
||||||
|
|
||||||
|
<MudDivider Class="mb-4" />
|
||||||
|
|
||||||
|
<MudStack Row="true" Justify="Justify.FlexEnd">
|
||||||
|
<MudButton Variant="Variant.Text" Href="/">Abbrechen</MudButton>
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
StartIcon="@Icons.Material.Filled.Save"
|
||||||
|
OnClick="SaveWorkspace">
|
||||||
|
Workspace speichern
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
</MudContainer>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private Workspace _workspace = new();
|
||||||
|
|
||||||
|
private async Task SaveWorkspace()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_workspace.Name))
|
||||||
|
{
|
||||||
|
Snackbar.Add("Bitte gib dem Workspace einen Namen.", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Wir übergeben nur noch das blanke Formular-Objekt.
|
||||||
|
// Der Service kümmert sich um den Auth-Rest!
|
||||||
|
await DbService.SaveWorkspaceAsync(_workspace);
|
||||||
|
|
||||||
|
Snackbar.Add($"Workspace '{_workspace.Name}' erfolgreich gespeichert!", Severity.Success);
|
||||||
|
NavManager.NavigateTo("/");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"Fehler beim Speichern: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace ZahlenAnalyse.Web.Models;
|
||||||
|
|
||||||
|
public record AnalysisFakt: IOwnedEntity, IAuditableEntity
|
||||||
|
{
|
||||||
|
public string? Id { get; set; }
|
||||||
|
public string WorkspaceId { get; set; } = string.Empty;
|
||||||
|
public string OwnerId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CreatedBy { get; set; } = string.Empty;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
|
||||||
|
public DateTime Date { get; set; } = DateTime.UtcNow;
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string> Dimensions { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace ZahlenAnalyse.Web.Models;
|
||||||
|
|
||||||
|
public interface IOwnedEntity
|
||||||
|
{
|
||||||
|
string OwnerId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAuditableEntity
|
||||||
|
{
|
||||||
|
string CreatedBy { get; set; }
|
||||||
|
DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace ZahlenAnalyse.Web.Models;
|
||||||
|
|
||||||
|
public record Workspace: IOwnedEntity, IAuditableEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
public string? Id { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string OwnerId { get; set; } = string.Empty; // Deine Pocket-ID (Sub)
|
||||||
|
|
||||||
|
public string CreatedBy { get; set; } = string.Empty;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
|
||||||
|
public List<DimensionDefinition> Dimensions { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DimensionDefinition
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public List<DimensionNode> Nodes { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DimensionNode
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public List<DimensionNode> Children { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
|||||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using DotNetEnv;
|
using DotNetEnv;
|
||||||
|
using ZahlenAnalyse.Web.Services;
|
||||||
|
|
||||||
Env.Load();
|
Env.Load();
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ var store = new DocumentStore
|
|||||||
};
|
};
|
||||||
store.Initialize();
|
store.Initialize();
|
||||||
builder.Services.AddSingleton<IDocumentStore>(store);
|
builder.Services.AddSingleton<IDocumentStore>(store);
|
||||||
|
builder.Services.AddScoped<WorkspaceService>();
|
||||||
|
|
||||||
builder.Services.AddCascadingAuthenticationState();
|
builder.Services.AddCascadingAuthenticationState();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Raven.Client.Documents;
|
||||||
|
using ZahlenAnalyse.Web.Models;
|
||||||
|
|
||||||
|
namespace ZahlenAnalyse.Web.Services;
|
||||||
|
|
||||||
|
public class WorkspaceService
|
||||||
|
{
|
||||||
|
private readonly IDocumentStore _store;
|
||||||
|
private readonly AuthenticationStateProvider _authStateProvider;
|
||||||
|
|
||||||
|
// Den AuthStateProvider injizieren
|
||||||
|
public WorkspaceService(IDocumentStore store, AuthenticationStateProvider authStateProvider)
|
||||||
|
{
|
||||||
|
_store = store;
|
||||||
|
_authStateProvider = authStateProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetUserIdAsync()
|
||||||
|
{
|
||||||
|
var authState = await _authStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var user = authState.User;
|
||||||
|
var userid = user.FindFirst(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value
|
||||||
|
?? user.FindFirst("sub")?.Value
|
||||||
|
?? string.Empty;
|
||||||
|
return userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Die Magie passiert hier ---
|
||||||
|
private async Task EnrichWithAuditDataAsync(object entity)
|
||||||
|
{
|
||||||
|
// Wenn das Objekt weder IOwnedEntity noch IAuditableEntity ist, können wir abbrechen
|
||||||
|
if (entity is not IOwnedEntity and not IAuditableEntity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var authState = await _authStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var user = authState.User;
|
||||||
|
|
||||||
|
if (entity is IOwnedEntity ownedEntity)
|
||||||
|
{
|
||||||
|
var userid = await GetUserIdAsync();
|
||||||
|
|
||||||
|
// Setzt bei JEDEM Speichern sicherheitshalber den aktuellen User als Owner
|
||||||
|
ownedEntity.OwnerId = userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity is IAuditableEntity auditableEntity)
|
||||||
|
{
|
||||||
|
// WICHTIG: Wir setzen CreatedBy und CreatedAt NUR, wenn sie noch leer sind.
|
||||||
|
// Sonst würden wir bei einem Update (z.B. Namensänderung des Workspaces)
|
||||||
|
// das ursprüngliche Erstellungsdatum und den ursprünglichen Ersteller überschreiben!
|
||||||
|
if (string.IsNullOrWhiteSpace(auditableEntity.CreatedBy))
|
||||||
|
{
|
||||||
|
auditableEntity.CreatedBy = user.FindFirst("name")?.Value ?? user.Identity?.Name ?? "Unbekannt";
|
||||||
|
auditableEntity.CreatedAt = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveWorkspaceAsync(Workspace workspace)
|
||||||
|
{
|
||||||
|
// 1. Audit-Daten automatisch befüllen
|
||||||
|
await EnrichWithAuditDataAsync(workspace);
|
||||||
|
|
||||||
|
// 2. Speichern
|
||||||
|
using var session = _store.OpenAsyncSession();
|
||||||
|
await session.StoreAsync(workspace);
|
||||||
|
await session.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Workspace>> GetWorkspacesForUserAsync()
|
||||||
|
{
|
||||||
|
// Hier können wir jetzt auch die OwnerId direkt aus dem Token ziehen!
|
||||||
|
// Du musst sie nicht mehr von der UI aus übergeben.
|
||||||
|
var authState = await _authStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var ownerId = await GetUserIdAsync();
|
||||||
|
|
||||||
|
using var session = _store.OpenAsyncSession();
|
||||||
|
return await session.Query<Workspace>()
|
||||||
|
.Where(w => w.OwnerId == ownerId)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Workspace?> GetWorkspaceAsync(string id)
|
||||||
|
{
|
||||||
|
var authState = await _authStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var currentUserId = await GetUserIdAsync();
|
||||||
|
|
||||||
|
using var session = _store.OpenAsyncSession();
|
||||||
|
var workspace = await session.LoadAsync<Workspace>(id);
|
||||||
|
|
||||||
|
|
||||||
|
if (workspace != null && workspace.OwnerId != currentUserId)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return workspace;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user