bmc_hub/app/modules/orders/templates/list.html

225 lines
8.9 KiB
HTML
Raw Permalink Normal View History

{% extends "shared/frontend/base.html" %}
{% block title %}Ordre - BMC Hub{% endblock %}
{% block extra_css %}
<style>
.ordre-card {
background: var(--bg-card);
border-radius: 12px;
padding: 1rem 1.25rem;
border: 1px solid rgba(0,0,0,0.06);
}
.ordre-title {
font-size: 0.8rem;
text-transform: uppercase;
color: var(--text-secondary);
margin-bottom: 0.35rem;
letter-spacing: 0.6px;
}
.ordre-value {
font-size: 1.35rem;
font-weight: 700;
color: var(--text-primary);
}
.table thead th {
font-size: 0.8rem;
text-transform: uppercase;
color: var(--text-secondary);
background: var(--accent);
color: white;
}
.order-row {
cursor: pointer;
transition: background-color 0.2s;
}
.order-row:hover {
background-color: rgba(var(--accent-rgb, 15, 76, 117), 0.05);
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3">
<div>
<h2 class="mb-1"><i class="bi bi-receipt me-2"></i>Ordre</h2>
<div class="text-muted">Oversigt over alle ordre</div>
</div>
<div class="d-flex gap-2">
<a href="/ordre/create/new" class="btn btn-success"><i class="bi bi-plus-circle me-1"></i>Opret ny ordre</a>
<button class="btn btn-outline-primary" onclick="loadOrders()"><i class="bi bi-arrow-clockwise me-1"></i>Opdater</button>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-3"><div class="ordre-card"><div class="ordre-title">Total ordre</div><div id="sumOrders" class="ordre-value">0</div></div></div>
<div class="col-md-3"><div class="ordre-card"><div class="ordre-title">Seneste måned</div><div id="sumRecent" class="ordre-value">0</div></div></div>
<div class="col-md-3"><div class="ordre-card"><div class="ordre-title">Eksporteret</div><div id="sumExported" class="ordre-value">0</div></div></div>
<div class="col-md-3"><div class="ordre-card"><div class="ordre-title">Ikke eksporteret</div><div id="sumNotExported" class="ordre-value">0</div></div></div>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Ordre #</th>
<th>Titel</th>
<th>Kunde</th>
<th>Linjer</th>
<th>Oprettet</th>
<th>Sidst opdateret</th>
<th>Sidst eksporteret</th>
<th>Status</th>
<th>Handlinger</th>
</tr>
</thead>
<tbody id="ordersTableBody">
<tr><td colspan="9" class="text-muted text-center py-4">Indlæser...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let orders = [];
function formatDate(dateStr) {
if (!dateStr) return '-';
const date = new Date(dateStr);
return date.toLocaleDateString('da-DK', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' });
}
function renderOrders() {
const tbody = document.getElementById('ordersTableBody');
if (!orders.length) {
tbody.innerHTML = '<tr><td colspan="9" class="text-muted text-center py-4">Ingen ordre fundet</td></tr>';
updateSummary();
return;
}
tbody.innerHTML = orders.map(order => {
const lines = Array.isArray(order.lines_json) ? order.lines_json : [];
const hasExported = order.last_exported_at ? true : false;
const statusBadge = hasExported
? '<span class="badge bg-success">Eksporteret</span>'
: '<span class="badge bg-warning text-dark">Ikke eksporteret</span>';
return `
<tr class="order-row" onclick="window.location.href='/ordre/${order.id}'">
<td><strong>#${order.id}</strong></td>
<td>${order.title || '-'}</td>
<td>${order.customer_id ? `Kunde ${order.customer_id}` : '-'}</td>
<td><span class="badge bg-primary">${lines.length} linjer</span></td>
<td>${formatDate(order.created_at)}</td>
<td>${formatDate(order.updated_at)}</td>
<td>${formatDate(order.last_exported_at)}</td>
<td>${statusBadge}</td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="event.stopPropagation(); window.location.href='/ordre/${order.id}'">
<i class="bi bi-eye"></i>
</button>
<button class="btn btn-sm btn-outline-danger" onclick="event.stopPropagation(); deleteOrder(${order.id})">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
`;
}).join('');
updateSummary();
}
function updateSummary() {
const now = new Date();
const oneMonthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
const recentOrders = orders.filter(order => new Date(order.created_at) >= oneMonthAgo);
const exportedOrders = orders.filter(order => order.last_exported_at);
const notExportedOrders = orders.filter(order => !order.last_exported_at);
document.getElementById('sumOrders').textContent = orders.length;
document.getElementById('sumRecent').textContent = recentOrders.length;
document.getElementById('sumExported').textContent = exportedOrders.length;
document.getElementById('sumNotExported').textContent = notExportedOrders.length;
}
async function loadOrders() {
const tbody = document.getElementById('ordersTableBody');
tbody.innerHTML = '<tr><td colspan="9" class="text-muted text-center py-4">Indlæser...</td></tr>';
try {
const res = await fetch('/api/v1/ordre/drafts?limit=100');
if (!res.ok) {
const errorData = await res.json().catch(() => ({}));
console.error('API Error:', res.status, errorData);
throw new Error(errorData.detail || `HTTP ${res.status}: Kunne ikke hente ordre`);
}
const data = await res.json();
console.log('Fetched orders:', data);
orders = Array.isArray(data) ? data : [];
if (orders.length === 0) {
tbody.innerHTML = '<tr><td colspan="9" class="text-muted text-center py-4">Ingen ordre fundet. <a href="/ordre/create/new" class="btn btn-sm btn-success ms-2">Opret første ordre</a></td></tr>';
updateSummary();
return;
}
// Fetch lines_json for each order to get line count
const detailPromises = orders.map(async (order) => {
try {
const detailRes = await fetch(`/api/v1/ordre/drafts/${order.id}`);
if (detailRes.ok) {
const detail = await detailRes.json();
order.lines_json = detail.lines_json || [];
} else {
order.lines_json = [];
}
} catch (e) {
console.error(`Failed to fetch details for order ${order.id}:`, e);
order.lines_json = [];
}
});
await Promise.all(detailPromises);
renderOrders();
} catch (error) {
console.error('Load orders error:', error);
tbody.innerHTML = `<tr><td colspan="9" class="text-danger text-center py-4">${error.message || 'Kunne ikke hente ordre'}</td></tr>`;
orders = [];
updateSummary();
}
}
async function deleteOrder(orderId) {
if (!confirm('Er du sikker på, at du vil slette denne ordre?')) return;
try {
const res = await fetch(`/api/v1/ordre/drafts/${orderId}`, { method: 'DELETE' });
if (!res.ok) {
const error = await res.json();
throw new Error(error.detail || 'Kunne ikke slette ordre');
}
await loadOrders();
alert('Ordre slettet');
} catch (error) {
alert(`Fejl: ${error.message}`);
}
}
document.addEventListener('DOMContentLoaded', () => {
loadOrders();
});
</script>
{% endblock %}