218 lines
15 KiB
Plaintext
218 lines
15 KiB
Plaintext
@model watcher_monitoring.Models.User
|
|
|
|
@{
|
|
ViewData["Title"] = "Benutzerdetails";
|
|
}
|
|
|
|
<div class="container-fluid px-4">
|
|
<div class="row">
|
|
<div class="col-lg-10">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="section-title mb-0">Benutzerdetails</h1>
|
|
<div>
|
|
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn" style="background-color: var(--warning); color: #fff; margin-right: 0.5rem;">Bearbeiten</a>
|
|
<a asp-action="Index" class="btn" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary);">Zurück</a>
|
|
</div>
|
|
</div>
|
|
|
|
@if (TempData["Success"] != null)
|
|
{
|
|
<div class="alert" style="background-color: rgba(63, 185, 80, 0.15); border: 1px solid var(--success); color: var(--success); border-radius: 6px; padding: 1rem; margin-bottom: 1rem; position: relative;">
|
|
@TempData["Success"]
|
|
@if (TempData["NewApiKey"] != null)
|
|
{
|
|
<hr style="border-color: var(--border-color); margin: 1rem 0;">
|
|
<strong>API-Key (wird nur einmal angezeigt!):</strong>
|
|
<div class="input-group mt-2" style="display: flex; gap: 0.5rem;">
|
|
<input type="text" class="form-control font-monospace" value="@TempData["NewApiKey"]" id="newApiKey" readonly style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px; flex: 1;">
|
|
<button class="btn" type="button" onclick="copyApiKey()" style="background-color: var(--accent-primary); color: #fff; border: none; padding: 0.75rem 1rem; border-radius: 6px;">
|
|
Kopieren
|
|
</button>
|
|
</div>
|
|
}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" style="position: absolute; top: 1rem; right: 1rem; color: var(--success);"></button>
|
|
</div>
|
|
}
|
|
|
|
@if (TempData["Error"] != null)
|
|
{
|
|
<div class="alert" style="background-color: rgba(248, 81, 73, 0.15); border: 1px solid var(--danger); color: var(--danger); border-radius: 6px; padding: 1rem; margin-bottom: 1rem; position: relative;">
|
|
@TempData["Error"]
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" style="position: absolute; top: 1rem; right: 1rem; color: var(--danger);"></button>
|
|
</div>
|
|
}
|
|
|
|
<div class="card mb-4">
|
|
<h2 class="card-title">Benutzerinformationen</h2>
|
|
<div style="color: var(--text-secondary);">
|
|
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
|
|
<div class="col-sm-4"><strong style="color: var(--text-primary);">Benutzername:</strong></div>
|
|
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.Username</div>
|
|
</div>
|
|
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
|
|
<div class="col-sm-4"><strong style="color: var(--text-primary);">E-Mail:</strong></div>
|
|
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.Email</div>
|
|
</div>
|
|
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
|
|
<div class="col-sm-4"><strong style="color: var(--text-primary);">Status:</strong></div>
|
|
<div class="col-sm-8">
|
|
@if (Model.IsActive)
|
|
{
|
|
<span class="status-badge status-online">Aktiv</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="status-badge status-offline">Inaktiv</span>
|
|
}
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
|
|
<div class="col-sm-4"><strong style="color: var(--text-primary);">Erstellt am:</strong></div>
|
|
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.CreatedAt.ToString("dd.MM.yyyy HH:mm:ss")</div>
|
|
</div>
|
|
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
|
|
<div class="col-sm-4"><strong style="color: var(--text-primary);">Letzter Login:</strong></div>
|
|
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.LastLogin.ToString("dd.MM.yyyy HH:mm:ss")</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-12">
|
|
<form asp-action="ToggleActive" asp-route-id="@Model.Id" method="post" class="d-inline">
|
|
@Html.AntiForgeryToken()
|
|
<button type="submit" class="btn btn-sm" style="background-color: @(Model.IsActive ? "var(--warning)" : "var(--success)"); color: #fff; margin-right: 0.5rem;">
|
|
@(Model.IsActive ? "Deaktivieren" : "Aktivieren")
|
|
</button>
|
|
</form>
|
|
<form asp-action="Delete" asp-route-id="@Model.Id" method="post" class="d-inline" onsubmit="return confirm('Möchten Sie diesen Benutzer wirklich löschen? Alle API-Keys werden ebenfalls gelöscht.');">
|
|
@Html.AntiForgeryToken()
|
|
<button type="submit" class="btn btn-sm" style="background-color: var(--danger); color: #fff;">Löschen</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h2 class="card-title mb-0">API-Keys (@Model.ApiKeys.Count)</h2>
|
|
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#createApiKeyModal">
|
|
Neuer API-Key
|
|
</button>
|
|
</div>
|
|
@if (Model.ApiKeys.Any())
|
|
{
|
|
<div class="table-responsive">
|
|
<table class="table table-hover" style="color: var(--text-primary); margin-bottom: 0;">
|
|
<thead style="border-bottom: 1px solid var(--border-color);">
|
|
<tr>
|
|
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Name</th>
|
|
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Beschreibung</th>
|
|
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Status</th>
|
|
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Erstellt</th>
|
|
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Läuft ab</th>
|
|
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Zuletzt verwendet</th>
|
|
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var key in Model.ApiKeys.OrderByDescending(k => k.CreatedAt))
|
|
{
|
|
<tr style="border-bottom: 1px solid var(--border-color);">
|
|
<td style="padding: 1rem;"><strong style="color: var(--text-primary);">@key.Name</strong></td>
|
|
<td style="padding: 1rem; color: var(--text-secondary);">@(key.Description ?? "-")</td>
|
|
<td style="padding: 1rem;">
|
|
@if (key.IsExpired)
|
|
{
|
|
<span class="status-badge status-offline">Abgelaufen</span>
|
|
}
|
|
else if (!key.IsActive)
|
|
{
|
|
<span class="status-badge" style="background-color: rgba(139, 148, 158, 0.15); color: var(--text-secondary); border: 1px solid var(--text-secondary);">Inaktiv</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="status-badge status-online">Aktiv</span>
|
|
}
|
|
</td>
|
|
<td style="padding: 1rem; color: var(--text-secondary);">@key.CreatedAt.ToString("dd.MM.yyyy")</td>
|
|
<td style="padding: 1rem; color: var(--text-secondary);">@(key.ExpiresAt?.ToString("dd.MM.yyyy") ?? "Nie")</td>
|
|
<td style="padding: 1rem; color: var(--text-secondary);">@(key.LastUsedAt?.ToString("dd.MM.yyyy HH:mm") ?? "Nie")</td>
|
|
<td style="padding: 1rem;">
|
|
<form asp-action="ToggleApiKey" asp-route-userId="@Model.Id" asp-route-keyId="@key.Id" method="post" class="d-inline">
|
|
@Html.AntiForgeryToken()
|
|
<button type="submit" class="btn btn-sm" style="background-color: @(key.IsActive ? "var(--warning)" : "var(--success)"); color: #fff; margin-right: 0.5rem;">
|
|
@(key.IsActive ? "Deaktivieren" : "Aktivieren")
|
|
</button>
|
|
</form>
|
|
<form asp-action="DeleteApiKey" asp-route-userId="@Model.Id" asp-route-keyId="@key.Id" method="post" class="d-inline" onsubmit="return confirm('Möchten Sie diesen API-Key wirklich löschen?');">
|
|
@Html.AntiForgeryToken()
|
|
<button type="submit" class="btn btn-sm" style="background-color: var(--danger); color: #fff;">Löschen</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<p style="color: var(--text-muted); margin-bottom: 0;">Noch keine API-Keys vorhanden.</p>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal für neuen API-Key -->
|
|
<div class="modal fade" id="createApiKeyModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content" style="background-color: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary);">
|
|
<form asp-action="GenerateApiKey" asp-route-id="@Model.Id" method="post">
|
|
@Html.AntiForgeryToken()
|
|
<div class="modal-header" style="border-bottom: 1px solid var(--border-color);">
|
|
<h5 class="modal-title" style="color: var(--text-primary);">Neuen API-Key erstellen</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" style="filter: invert(1);"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="keyName" class="form-label" style="color: var(--text-primary); font-weight: 500;">Name *</label>
|
|
<input type="text" class="form-control" id="keyName" name="keyName" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;" required>
|
|
<div style="color: var(--text-muted); font-size: 0.875rem; margin-top: 0.25rem;">Ein eindeutiger Name für diesen API-Key</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="description" class="form-label" style="color: var(--text-primary); font-weight: 500;">Beschreibung</label>
|
|
<textarea class="form-control" id="description" name="description" rows="2" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;"></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="expiresAt" class="form-label" style="color: var(--text-primary); font-weight: 500;">Ablaufdatum (optional)</label>
|
|
<input type="datetime-local" class="form-control" id="expiresAt" name="expiresAt" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;">
|
|
<div style="color: var(--text-muted); font-size: 0.875rem; margin-top: 0.25rem;">Leer lassen für unbegrenzte Gültigkeit</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer" style="border-top: 1px solid var(--border-color);">
|
|
<button type="button" class="btn" data-bs-dismiss="modal" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary);">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">API-Key erstellen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@section Scripts {
|
|
<script>
|
|
function copyApiKey() {
|
|
var copyText = document.getElementById("newApiKey");
|
|
copyText.select();
|
|
copyText.setSelectionRange(0, 99999);
|
|
navigator.clipboard.writeText(copyText.value);
|
|
|
|
var btn = event.target;
|
|
var originalText = btn.textContent;
|
|
btn.textContent = "Kopiert!";
|
|
setTimeout(function() {
|
|
btn.textContent = originalText;
|
|
}, 2000);
|
|
}
|
|
</script>
|
|
}
|