Show sourcecode
The following files exists in this folder. Click to view.
public_html/crumbs/admin/orders/
api.php
orders.php
sendmail.php
statusdisplay.php
orders.php
159 lines UTF-8 Windows (CRLF)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
<?php
$basePath = '../../';
$pageTitle = 'Hantera Beställningar';
$activePage = 'admin-orders';
include('../../includes/header.php');
?>
<div class="page-header">
<div class="page-header-text">
<h1>Beställningar</h1>
<p>Hantera och uppdatera orderstatus. <span id="update-indicator" class="muted">Uppdateras automatiskt...</span></p>
</div>
</div>
<div id="orders-container">
<p class="muted">Laddar beställningar...</p>
</div>
<script>
const statusLabels = {
'pending': 'Väntar',
'preparing': 'Tillagas',
'done': 'Klar',
'delivered': 'Levererad'
};
const statusClasses = {
'pending': 'status-pending',
'preparing': 'status-preparing',
'done': 'status-done',
'delivered': 'status-delivered'
};
function buildOrderCard(order) {
const chips = order.items.map(item =>
`<span class="order-item-chip"><span class="item-qty">${item.amount}x</span> ${escapeHtml(item.name)}</span>`
).join('');
const instructions = order.custom_instructions ?
`<span class="order-instructions"><span class="material-symbols-rounded" style="font-size:16px;vertical-align:middle">edit_note</span> ${escapeHtml(order.custom_instructions)}</span>` :
'<span></span>';
return `
<div class="card order-card ${statusClasses[order.status]}" data-order-id="${order.order_id}">
<div class="order-header">
<div>
<h3>#${order.order_id} — ${escapeHtml(order.fullname || order.username)}</h3>
<span class="order-meta">${order.order_time}</span>
</div>
<div class="order-status-area">
<div class="status-btn-group">
<button class="status-btn status-btn-pending${order.status === 'pending' ? ' active' : ''}" onclick="updateStatus(this, ${order.order_id}, 'pending')">Väntar</button>
<button class="status-btn status-btn-preparing${order.status === 'preparing' ? ' active' : ''}" onclick="updateStatus(this, ${order.order_id}, 'preparing')">Tillagas</button>
<button class="status-btn status-btn-done${order.status === 'done' ? ' active' : ''}" onclick="updateStatus(this, ${order.order_id}, 'done')">Klar</button>
<button class="status-btn status-btn-delivered${order.status === 'delivered' ? ' active' : ''}" onclick="updateStatus(this, ${order.order_id}, 'delivered')">Levererad</button>
</div>
</div>
</div>
<div style="display:inline-flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:4px 10px;margin-bottom:8px;">
<span class="material-symbols-rounded" style="font-size:16px;color:var(--primary)">confirmation_number</span>
<span style="font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;">Orderkod</span>
<span style="font-size:15px;font-weight:700;letter-spacing:.14em;">${String(order.order_id).padStart(4, '0')}</span>
</div>
<div class="order-items-compact">${chips}</div>
<div class="order-footer">
${instructions}
<span class="order-total">${order.total_price} kr</span>
</div>
</div>`;
}
function renderOrders(orders) {
const container = document.getElementById('orders-container');
const archiveOpen = container.querySelector('details')?.open ?? false;
if (orders.length === 0) {
container.innerHTML = '<div class="alert alert-warning"><span class="material-symbols-rounded" style="font-size:18px">info</span> Inga beställningar hittades.</div>';
return;
}
const active = orders.filter(o => o.status !== 'delivered');
const delivered = orders.filter(o => o.status === 'delivered');
let html = '';
if (active.length === 0) {
html += '<div class="alert alert-warning" style="margin-bottom:16px"><span class="material-symbols-rounded" style="font-size:18px">info</span> Inga aktiva beställningar.</div>';
} else {
active.forEach(o => {
html += buildOrderCard(o);
});
}
if (delivered.length > 0) {
html += `
<details${archiveOpen ? ' open' : ''} style="margin-top:24px">
<summary style="cursor:pointer;font-weight:600;color:var(--muted);padding:8px 0;list-style:none;display:flex;align-items:center;gap:8px;user-select:none;">
<span class="material-symbols-rounded" style="font-size:18px">inventory_2</span>
Arkiverade beställningar (${delivered.length})
</summary>
<div style="margin-top:12px">
${delivered.map(o => buildOrderCard(o)).join('')}
</div>
</details>`;
}
container.innerHTML = html;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Hämta orders från api och sedan kör redner orders för att visa kort.
function fetchOrders() {
fetch('api.php')
.then(res => res.json())
.then(orders => {
renderOrders(orders);
document.getElementById('update-indicator').textContent = 'Senast uppdaterad: ' + new Date().toLocaleTimeString('sv-SE');
})
.catch(err => {
console.error('Kunde inte hämta beställningar:', err);
});
}
function updateStatus(btnEl, orderId, newStatus) {
fetch('api.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
order_id: parseInt(orderId),
status: newStatus
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
const card = btnEl.closest('.order-card');
card.querySelectorAll('.status-btn').forEach(b => b.classList.remove('active'));
btnEl.classList.add('active');
Object.values(statusClasses).forEach(cls => card.classList.remove(cls));
card.classList.add(statusClasses[newStatus]);
}
})
.catch(err => {
console.error('Kunde inte uppdatera status:', err);
});
}
fetchOrders();
setInterval(fetchOrders, 5000);
</script>
<?php include('../../includes/footer.php'); ?>