Neue Benutzerübersicht
This commit is contained in:
parent
2927131f9e
commit
0c2a83a31f
@ -5,7 +5,7 @@
|
||||
|
||||
<div class="container py-4 text-center fade-in">
|
||||
<h4 class="mb-4 text-primary">
|
||||
<i class="bi bi-shield-lock me-2"></i> Willkommen im Adminbereich
|
||||
<i class="bi bi-shield-lock me-2"></i> Adminbereich
|
||||
</h4>
|
||||
|
||||
<div class="row g-4 justify-content-center">
|
||||
@ -19,10 +19,10 @@
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="card shadow-sm border-0 admin-tile" @onclick="@(() => Nav.NavigateTo("/admin/filters"))">
|
||||
<div class="card shadow-sm border-0 admin-tile" @onclick="@(() => Nav.NavigateTo("/admin/users"))">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-funnel display-6 text-primary mb-3"></i>
|
||||
<h5 class="fw-semibold">Filter verwalten</h5>
|
||||
<i class="bi bi-person display-6 text-primary mb-3"></i>
|
||||
<h5 class="fw-semibold">Benutzer verwalten</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
308
FilterCair.Client/Pages/Admin/Users.razor
Normal file
308
FilterCair.Client/Pages/Admin/Users.razor
Normal file
@ -0,0 +1,308 @@
|
||||
@page "/admin/users"
|
||||
@layout AdminLayout
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<PageTitle>Benutzerverwaltung</PageTitle>
|
||||
|
||||
<style>
|
||||
/* Responsives Verhalten optimieren */
|
||||
.users-table-wrapper {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.users-table {
|
||||
min-width: 900px; /* verhindert hässliches Zusammenstauchen auf kleinen Bildschirmen */
|
||||
}
|
||||
|
||||
@@media (max-width: 992px) {
|
||||
.users-table th:nth-child(2),
|
||||
.users-table td:nth-child(2),
|
||||
.users-table th:nth-child(3),
|
||||
.users-table td:nth-child(3) {
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container py-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3">
|
||||
<h4 class="text-primary mb-0">
|
||||
<i class="bi bi-people me-2"></i> Benutzerverwaltung
|
||||
</h4>
|
||||
<button class="btn btn-success rounded-pill" @onclick="ShowCreateModal">
|
||||
<i class="bi bi-plus-lg me-1"></i> Neuer Benutzer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (isLoading)
|
||||
{
|
||||
<div class="text-center py-5 text-muted">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
<p class="mt-3">Lade Benutzer...</p>
|
||||
</div>
|
||||
}
|
||||
else if (users.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info text-center">
|
||||
Keine Benutzer vorhanden. Lege den ersten Benutzer an!
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="users-table-wrapper">
|
||||
<table class="table table-striped align-middle shadow-sm users-table">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th>Benutzername</th>
|
||||
<th>Vorname</th>
|
||||
<th>Nachname</th>
|
||||
<th>E-Mail</th>
|
||||
<th>Rolle</th>
|
||||
<th style="width: 80px;">Aktiv</th>
|
||||
<th style="width: 140px; text-align: end;">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var u in users)
|
||||
{
|
||||
<tr>
|
||||
<td><strong>@u.UserName</strong></td>
|
||||
<td>@u.FirstName</td>
|
||||
<td>@u.LastName</td>
|
||||
<td>@u.Email</td>
|
||||
<td>
|
||||
<span class="badge @(u.Role == "Administrator" ? "bg-danger" : "bg-secondary")">
|
||||
@u.Role
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<i class="bi @(u.IsActive ? "bi-check-circle-fill text-success fs-5" : "bi-x-circle-fill text-danger fs-5")"></i>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-outline-primary btn-sm rounded-pill me-1"
|
||||
@onclick="() => ShowEditModal(u)"
|
||||
title="Bearbeiten">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-danger btn-sm rounded-pill"
|
||||
@onclick="() => DeleteUser(u)"
|
||||
title="Löschen">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Modal mit Backdrop-Click & ESC-Support -->
|
||||
@if (showModal)
|
||||
{
|
||||
<div class="modal fade show d-block" tabindex="-1"
|
||||
style="background-color: rgba(0,0,0,0.5);"
|
||||
@onkeydown="OnModalKeyDown"
|
||||
@onkeydown:preventDefault="true"
|
||||
@onkeydown:stopPropagation="true"
|
||||
@onclick="CloseModal"
|
||||
>
|
||||
|
||||
<!-- Backdrop-Click schließt Modal -->
|
||||
<div class="modal-dialog modal-dialog-centered" @onclick:stopPropagation>
|
||||
<div class="modal-content border-0 shadow-lg rounded-4">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title">
|
||||
<i class="me-2 @(isEditMode ? "bi bi-pencil" : "bi bi-person-plus")"></i>
|
||||
@(isEditMode ? "Benutzer bearbeiten" : "Neuer Benutzer")
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" @onclick="CloseModal" aria-label="Schließen"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<EditForm Model="@currentUser" OnValidSubmit="SaveUser">
|
||||
<DataAnnotationsValidator />
|
||||
ge
|
||||
<ValidationSummary class="text-danger mb-3" />
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Benutzername *</label>
|
||||
<InputText class="form-control" @bind-Value="currentUser.UserName" required />
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Vorname</label>
|
||||
<InputText class="form-control" @bind-Value="currentUser.FirstName" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Nachname</label>
|
||||
<InputText class="form-control" @bind-Value="currentUser.LastName" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 mt-3">
|
||||
<label class="form-label">E-Mail *</label>
|
||||
<InputText type="email" class="form-control" @bind-Value="currentUser.Email" required />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">
|
||||
Passwort @(isEditMode ? "(leer = unverändert)" : "*")
|
||||
</label>
|
||||
<InputText type="password"
|
||||
class="form-control"
|
||||
@bind-Value="currentUser.Password"
|
||||
placeholder="@(isEditMode ? "Leer lassen = Passwort bleibt gleich" : "Pflichtfeld beim Anlegen")"
|
||||
required="@(!isEditMode)" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rolle</label>
|
||||
<InputSelect class="form-select" @bind-Value="currentUser.Role">
|
||||
<option value="User">User</option>
|
||||
<option value="Administrator">Administrator</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-4">
|
||||
<InputCheckbox class="form-check-input" @bind-Value="currentUser.IsActive" />
|
||||
<label class="form-check-label">Account aktiv</label>
|
||||
</div>
|
||||
|
||||
<div class="text-end">
|
||||
<button type="button" class="btn btn-outline-secondary me-2 rounded-pill" @onclick="CloseModal">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success rounded-pill">
|
||||
@(isEditMode ? "Aktualisieren" : "Anlegen")
|
||||
</button>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@code {
|
||||
private List<AppUser> users = new();
|
||||
private AppUser currentUser = new();
|
||||
private bool showModal = false;
|
||||
private bool isEditMode = false;
|
||||
private bool isLoading = true;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Demo-Daten direkt beim Start
|
||||
users = new List<AppUser>
|
||||
{
|
||||
new AppUser { Id = 1, UserName = "admin", FirstName = "Max", LastName = "Mustermann", Email = "admin@filtercair.de", Role = "Administrator", IsActive = true },
|
||||
new AppUser { Id = 2, UserName = "peter", FirstName = "Peter", LastName = "Parker", Email = "peter@company.com", Role = "User", IsActive = true },
|
||||
new AppUser { Id = 3, UserName = "susi", FirstName = "Susanne", LastName = "Müller", Email = "susi@company.com", Role = "User", IsActive = false },
|
||||
new AppUser { Id = 4, UserName = "tom", FirstName = "Tom", LastName = "Tester", Email = "tom@test.de", Role = "User", IsActive = true }
|
||||
};
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private void ShowCreateModal()
|
||||
{
|
||||
currentUser = new AppUser { IsActive = true, Role = "User" };
|
||||
isEditMode = false;
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
private void ShowEditModal(AppUser user)
|
||||
{
|
||||
currentUser = new AppUser
|
||||
{
|
||||
Id = user.Id,
|
||||
UserName = user.UserName,
|
||||
FirstName = user.FirstName,
|
||||
LastName = user.LastName,
|
||||
Email = user.Email,
|
||||
Role = user.Role,
|
||||
IsActive = user.IsActive,
|
||||
Password = "" // bleibt leer → wird beim Edit nicht überschrieben
|
||||
};
|
||||
isEditMode = true;
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
private void CloseModal()
|
||||
{
|
||||
showModal = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void OnBackdropClick(MouseEventArgs args)
|
||||
{
|
||||
CloseModal();
|
||||
}
|
||||
|
||||
private void OnModalKeyDown(KeyboardEventArgs args)
|
||||
{
|
||||
if (args.Key == "Escape")
|
||||
{
|
||||
CloseModal();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveUser()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(currentUser.UserName) || string.IsNullOrWhiteSpace(currentUser.Email) ||
|
||||
(!isEditMode && string.IsNullOrWhiteSpace(currentUser.Password)))
|
||||
{
|
||||
await JS.InvokeVoidAsync("showToast", "Bitte alle Pflichtfelder ausfüllen!", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEditMode)
|
||||
{
|
||||
var existing = users.First(x => x.Id == currentUser.Id);
|
||||
existing.UserName = currentUser.UserName;
|
||||
existing.FirstName = currentUser.FirstName;
|
||||
existing.LastName = currentUser.LastName;
|
||||
existing.Email = currentUser.Email;
|
||||
existing.Role = currentUser.Role;
|
||||
existing.IsActive = currentUser.IsActive;
|
||||
// Passwort wird nur geändert, wenn was eingetragen ist
|
||||
if (!string.IsNullOrWhiteSpace(currentUser.Password))
|
||||
existing.PasswordHash = currentUser.Password; // hier einfach als Klartext für Demozwecke
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUser.Id = users.Max(x => x.Id) + 1;
|
||||
currentUser.PasswordHash = currentUser.Password; // Dummy-Hash
|
||||
users.Add(currentUser);
|
||||
}
|
||||
|
||||
CloseModal();
|
||||
await JS.InvokeVoidAsync("showToast", isEditMode ? "✅ Benutzer aktualisiert!" : "✅ Benutzer angelegt!");
|
||||
}
|
||||
|
||||
private async Task DeleteUser(AppUser user)
|
||||
{
|
||||
var confirmed = await JS.InvokeAsync<bool>("confirm", $"Soll der Benutzer '{user.UserName}' wirklich gelöscht werden?");
|
||||
if (confirmed)
|
||||
{
|
||||
users.Remove(user);
|
||||
await JS.InvokeVoidAsync("showToast", "🗑️ Benutzer gelöscht");
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy-User-Klasse nur für diese Seite
|
||||
public class AppUser
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
public string FirstName { get; set; } = string.Empty;
|
||||
public string LastName { get; set; } = string.Empty;
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty; // nur im Modal
|
||||
public string? PasswordHash { get; set; } // wird später mal richtig
|
||||
public string Role { get; set; } = "User";
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user