bmc_hub/app/ticket/frontend/dashboard.html

287 lines
13 KiB
HTML
Raw Normal View History

{% extends "shared/frontend/base.html" %}
{% block title %}Ticket Dashboard - BMC Hub{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Header -->
<div class="row mb-4">
<div class="col">
<h1 class="h3 mb-0">🎫 Support Dashboard</h1>
<p class="text-muted">Oversigt over alle support tickets og aktivitet</p>
</div>
<div class="col-auto">
<button class="btn btn-outline-primary me-2" onclick="window.location.href='/ticket/dashboard/technician'">
<i class="bi bi-tools"></i> Tekniker Dashboard (3 forslag)
</button>
<button class="btn btn-primary" onclick="window.location.href='/ticket/tickets/new'">
<i class="bi bi-plus-circle"></i> Ny Ticket
</button>
</div>
</div>
<!-- Status Overview -->
<div class="row g-3 mb-4">
<div class="col-md-2">
<div class="card border-0 shadow-sm h-100" style="cursor: pointer;" onclick="filterByStatus('open')">
<div class="card-body text-center">
<div class="rounded-circle bg-info bg-opacity-10 p-3 d-inline-flex mb-3">
<i class="bi bi-inbox-fill text-info fs-4"></i>
</div>
<h2 class="mb-1 text-info">{{ stats.open_count or 0 }}</h2>
<p class="text-muted small mb-0">Åbne</p>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card border-0 shadow-sm h-100" style="cursor: pointer;" onclick="filterByStatus('in_progress')">
<div class="card-body text-center">
<div class="rounded-circle bg-warning bg-opacity-10 p-3 d-inline-flex mb-3">
<i class="bi bi-hourglass-split text-warning fs-4"></i>
</div>
<h2 class="mb-1 text-warning">{{ stats.in_progress_count or 0 }}</h2>
<p class="text-muted small mb-0">I Gang</p>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card border-0 shadow-sm h-100" style="cursor: pointer;" onclick="filterByStatus('pending_customer')">
<div class="card-body text-center">
<div class="rounded-circle bg-secondary bg-opacity-10 p-3 d-inline-flex mb-3">
<i class="bi bi-clock-fill text-secondary fs-4"></i>
</div>
<h2 class="mb-1 text-secondary">{{ stats.pending_count or 0 }}</h2>
<p class="text-muted small mb-0">Afventer</p>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card border-0 shadow-sm h-100" style="cursor: pointer;" onclick="filterByStatus('resolved')">
<div class="card-body text-center">
<div class="rounded-circle bg-success bg-opacity-10 p-3 d-inline-flex mb-3">
<i class="bi bi-check-circle-fill text-success fs-4"></i>
</div>
<h2 class="mb-1 text-success">{{ stats.resolved_count or 0 }}</h2>
<p class="text-muted small mb-0">Løst</p>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card border-0 shadow-sm h-100" style="cursor: pointer;" onclick="filterByStatus('closed')">
<div class="card-body text-center">
<div class="rounded-circle bg-dark bg-opacity-10 p-3 d-inline-flex mb-3">
<i class="bi bi-archive-fill text-dark fs-4"></i>
</div>
<h2 class="mb-1 text-dark">{{ stats.closed_count or 0 }}</h2>
<p class="text-muted small mb-0">Lukket</p>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card border-0 shadow-sm h-100 bg-primary text-white">
<div class="card-body text-center">
<div class="rounded-circle bg-white bg-opacity-25 p-3 d-inline-flex mb-3">
<i class="bi bi-ticket-detailed-fill fs-4"></i>
</div>
<h2 class="mb-1">{{ stats.total_count or 0 }}</h2>
<p class="small mb-0 opacity-75">I Alt</p>
</div>
</div>
</div>
</div>
<!-- Worklog & Prepaid Overview -->
<div class="row g-4 mb-4">
<div class="col-md-6">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0">⏱️ Worklog Status</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-6 border-end">
<div class="text-center p-3">
<h3 class="text-warning mb-2">{{ worklog_stats.draft_count or 0 }}</h3>
<p class="text-muted small mb-1">Kladder</p>
<p class="mb-0"><strong>{{ "%.1f"|format(worklog_stats.draft_hours|float if worklog_stats.draft_hours else 0) }} timer</strong></p>
</div>
</div>
<div class="col-6">
<div class="text-center p-3">
<h3 class="text-success mb-2">{{ worklog_stats.billable_count or 0 }}</h3>
<p class="text-muted small mb-1">Fakturerbare</p>
<p class="mb-0"><strong>{{ "%.1f"|format(worklog_stats.billable_hours|float if worklog_stats.billable_hours else 0) }} timer</strong></p>
</div>
</div>
</div>
<div class="text-center pt-3 border-top">
<a href="/ticket/worklog/review" class="btn btn-outline-primary btn-sm">
<i class="bi bi-check-square"></i> Godkend Worklog
</a>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0">💳 Prepaid Cards</h5>
</div>
<div class="card-body" id="prepaidStats">
<div class="text-center py-3">
<div class="spinner-border spinner-border-sm text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Recent Tickets -->
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0">📋 Seneste Tickets</h5>
<a href="/ticket/tickets" class="btn btn-sm btn-outline-secondary">
Se Alle <i class="bi bi-arrow-right"></i>
</a>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Ticket #</th>
<th>Emne</th>
<th>Kunde</th>
<th>Status</th>
<th>Prioritet</th>
<th>Oprettet</th>
<th></th>
</tr>
</thead>
<tbody>
{% if recent_tickets %}
{% for ticket in recent_tickets %}
<tr onclick="window.location.href='/ticket/tickets/{{ ticket.id }}'" style="cursor: pointer;">
<td><strong>{{ ticket.ticket_number }}</strong></td>
<td>{{ ticket.subject }}</td>
<td>{{ ticket.customer_name or '-' }}</td>
<td>
{% if ticket.status == 'open' %}
<span class="badge bg-info">Åben</span>
{% elif ticket.status == 'in_progress' %}
<span class="badge bg-warning">I Gang</span>
{% elif ticket.status == 'pending_customer' %}
<span class="badge bg-secondary">Afventer</span>
{% elif ticket.status == 'resolved' %}
<span class="badge bg-success">Løst</span>
{% elif ticket.status == 'closed' %}
<span class="badge bg-dark">Lukket</span>
{% else %}
<span class="badge bg-secondary">{{ ticket.status }}</span>
{% endif %}
</td>
<td>
{% if ticket.priority == 'urgent' %}
<span class="badge bg-danger">Akut</span>
{% elif ticket.priority == 'high' %}
<span class="badge bg-warning">Høj</span>
{% elif ticket.priority == 'normal' %}
<span class="badge bg-info">Normal</span>
{% else %}
<span class="badge bg-secondary">Lav</span>
{% endif %}
</td>
<td>{{ ticket.created_at.strftime('%d/%m/%Y %H:%M') if ticket.created_at else '-' }}</td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="event.stopPropagation(); window.location.href='/ticket/tickets/{{ ticket.id }}'">
<i class="bi bi-arrow-right"></i>
</button>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="7" class="text-center text-muted py-5">
Ingen tickets endnu
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
// Load prepaid stats
document.addEventListener('DOMContentLoaded', () => {
loadPrepaidStats();
});
async function loadPrepaidStats() {
try {
const response = await fetch('/api/v1/prepaid-cards/stats/summary');
const stats = await response.json();
document.getElementById('prepaidStats').innerHTML = `
<div class="row text-center">
<div class="col-6 border-end">
<h4 class="text-success mb-1">${stats.active_count || 0}</h4>
<p class="text-muted small mb-0">Aktive Kort</p>
</div>
<div class="col-6">
<h4 class="text-primary mb-1">${parseFloat(stats.total_remaining_hours || 0).toFixed(1)} t</h4>
<p class="text-muted small mb-0">Timer Tilbage</p>
</div>
</div>
<div class="text-center pt-3 border-top">
<a href="/prepaid-cards" class="btn btn-outline-primary btn-sm">
<i class="bi bi-credit-card-2-front"></i> Se Alle Kort
</a>
</div>
`;
} catch (error) {
console.error('Error loading prepaid stats:', error);
document.getElementById('prepaidStats').innerHTML = `
<p class="text-center text-muted mb-0">Kunne ikke indlæse data</p>
`;
}
}
function filterByStatus(status) {
window.location.href = `/ticket/tickets?status=${status}`;
}
</script>
<style>
.card {
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
.table th {
font-weight: 600;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--bs-secondary);
}
.table tbody tr {
transition: background-color 0.2s;
}
.table tbody tr:hover {
background-color: rgba(15, 76, 117, 0.05);
}
</style>
{% endblock %}