145 lines
5.3 KiB
Plaintext
145 lines
5.3 KiB
Plaintext
@page "/"
|
|
@rendermode InteractiveServer
|
|
@using TodoTicketApp.Models
|
|
@using TodoTicketApp.Services
|
|
@inject ITicketService TicketService
|
|
@inject NavigationManager Nav
|
|
|
|
<PageTitle>Ticket Dashboard</PageTitle>
|
|
|
|
<div class="dashboard-container">
|
|
<div class="quick-entry">
|
|
<input class="form-control form-control-lg"
|
|
placeholder="Ticket-Titel eingeben..."
|
|
@bind="newTicketTitle"
|
|
@onkeyup="HandleKeyUp" />
|
|
</div>
|
|
|
|
<div class="ticket-list">
|
|
@{
|
|
bool dividerShown = false;
|
|
}
|
|
@foreach (var ticket in TicketService.GetPendingTickets())
|
|
{
|
|
// Sobald das erste wartende Ticket auftaucht und der Trenner noch nicht da ist:
|
|
@if (ticket.IsWaitingForFeedback && !dividerShown)
|
|
{
|
|
<div class="waiting-separator">
|
|
<hr />
|
|
<span>Wartende Tickets</span>
|
|
<hr />
|
|
</div>
|
|
dividerShown = true; // Trenner wurde gezeichnet, nicht nochmal anzeigen
|
|
}
|
|
|
|
<div class="ticket-card priority-@ticket.Priority.ToString().ToLower() @(ticket.IsWaitingForFeedback ? "is-waiting" : "")">
|
|
<div class="ticket-header" @onclick="() => GoToDetails(ticket.Id)">
|
|
<span class="prio-tag">@ticket.Priority</span>
|
|
<h5>@ticket.Title</h5>
|
|
<small>@ticket.CreatedAt.ToLocalTime().ToString("g")</small>
|
|
</div>
|
|
|
|
<div class="ticket-body" @onclick="() => GoToDetails(ticket.Id)">
|
|
<p>@(ticket.Description.Length > 200 ? ticket.Description.Substring(0, 200) + "..." : ticket.Description)</p>
|
|
</div>
|
|
|
|
<div class="ticket-footer">
|
|
<span class="comments-info">
|
|
<i class="bi bi-chat-left-text"></i> @ticket.Comments.Count Kommentare
|
|
</span>
|
|
<div class="ticket-actions">
|
|
<button class="btn btn-sm @(ticket.IsWaitingForFeedback ? "btn-secondary" : "btn-outline-warning")"
|
|
@onclick="() => ToggleWaitingStatus(ticket)"
|
|
@onclick:stopPropagation="true">
|
|
@(ticket.IsWaitingForFeedback ? "Warten aufheben" : "Auf Rückmeldung warten")
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-success"
|
|
@onclick="() => Complete(ticket.Id)"
|
|
@onclick:stopPropagation="true">
|
|
Erledigen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
@code {
|
|
private string newTicketTitle = "";
|
|
|
|
private async Task HandleKeyUp(KeyboardEventArgs e)
|
|
{
|
|
if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(newTicketTitle))
|
|
{
|
|
var newTicket = new Ticket { Title = newTicketTitle };
|
|
TicketService.AddTicket(newTicket);
|
|
newTicketTitle = "";
|
|
// Hier könnten wir direkt auf die Detailseite navigieren:
|
|
// Nav.NavigateTo($"/ticket/edit/{newTicket.Id}");
|
|
}
|
|
}
|
|
|
|
private void ToggleWaitingStatus(Ticket ticket)
|
|
{
|
|
// Status umkehren
|
|
ticket.IsWaitingForFeedback = !ticket.IsWaitingForFeedback;
|
|
|
|
// In der Datenbank speichern
|
|
TicketService.UpdateTicket(ticket);
|
|
|
|
// UI explizit anweisen, sich neu zu sortieren
|
|
StateHasChanged();
|
|
}
|
|
|
|
private void Complete(Guid id)
|
|
{
|
|
TicketService.CompleteTicket(id);
|
|
StateHasChanged();
|
|
}
|
|
private void GoToDetails(Guid id) => Nav.NavigateTo($"/ticket/edit/{id}");
|
|
}
|
|
|
|
<style>
|
|
.dashboard-container { max-width: 800px; margin: 2rem auto; }
|
|
.quick-entry { margin-bottom: 2rem; }
|
|
.ticket-card {
|
|
background: #f8f9fa; border-radius: 8px; padding: 1rem; margin-bottom: 1rem;
|
|
border-left: 5px solid #ccc; cursor: pointer; transition: transform 0.1s;
|
|
}
|
|
.is-waiting {
|
|
opacity: 0.6;
|
|
background-color: #e9ecef;
|
|
border-left-color: #6c757d !important; /* Überschreibt die Prio-Farbe mit Grau */
|
|
}
|
|
.ticket-actions {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
.ticket-card:hover { transform: scale(1.01); }
|
|
.priority-critical { border-left-color: #dc3545; }
|
|
.priority-high { border-left-color: #fd7e14; }
|
|
.ticket-header { display: flex; justify-content: space-between; align-items: center; }
|
|
.prio-tag { font-size: 0.7rem; font-weight: bold; text-transform: uppercase; }
|
|
.ticket-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 1rem; }
|
|
.waiting-separator {
|
|
display: flex;
|
|
align-items: center;
|
|
text-align: center;
|
|
margin: 2.5rem 0 1.5rem 0;
|
|
color: #6c757d;
|
|
font-size: 0.85rem;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
.waiting-separator hr {
|
|
flex-grow: 1;
|
|
border: none;
|
|
border-top: 2px dashed #dee2e6;
|
|
margin: 0 1rem;
|
|
opacity: 1;
|
|
}
|
|
</style>
|