hd-commerce: neutrales SQLite-Commerce-Backend (Admin + API + Demo-Storefront)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
---
|
||||
import Admin from '../../layouts/Admin.astro';
|
||||
import { dashboard, formatPrice } from '../../lib/store.js';
|
||||
const d = dashboard();
|
||||
const statusMap = { fulfilled: ['green', 'Erfüllt'], pending: ['amber', 'Offen'], cancelled: ['gray', 'Storniert'], refunded: ['red', 'Erstattet'] };
|
||||
const fmtDate = (s) => new Date(s).toLocaleDateString('de-DE', { day: '2-digit', month: 'short', year: 'numeric' });
|
||||
const kpis = [
|
||||
{ label: 'Umsatz (gesamt)', val: formatPrice(d.revenueCents), sub: `${d.orderCount} Bestellungen` },
|
||||
{ label: 'Bestellungen', val: d.orderCount, sub: `${d.pending} offen` },
|
||||
{ label: 'Produkte', val: d.productCount, sub: 'aktiv im Shop' },
|
||||
{ label: 'Kunden', val: d.customerCount, sub: 'registriert' },
|
||||
];
|
||||
---
|
||||
<Admin title="Dashboard" active="dashboard">
|
||||
<a slot="actions" class="s-btn s-btn-primary" href="/admin/produkte/neu">+ Produkt</a>
|
||||
<div class="s-stack">
|
||||
<div class="s-kpis">
|
||||
{kpis.map((k) => (
|
||||
<div class="s-kpi"><div class="s-kpi-label">{k.label}</div><div class="s-kpi-val">{k.val}</div><div class="s-kpi-sub">{k.sub}</div></div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div class="s-card">
|
||||
<div class="s-card-head">First-Party-Funnel (30 Tage)<a class="s-link" href="/admin/analytics">Details</a></div>
|
||||
<div class="s-card-pad">
|
||||
<div class="s-funnel-mini">
|
||||
<div class="s-fm-step"><div class="v">{d.funnelMini.views}</div><div class="l">Aufrufe</div></div>
|
||||
<div class="s-fm-arrow">→</div>
|
||||
<div class="s-fm-step"><div class="v">{d.funnelMini.cart}</div><div class="l">In den Korb</div></div>
|
||||
<div class="s-fm-arrow">→</div>
|
||||
<div class="s-fm-step"><div class="v">{d.funnelMini.buy}</div><div class="l">Kauf</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="s-grid" style="grid-template-columns:1.4fr 1fr">
|
||||
<div class="s-card">
|
||||
<div class="s-card-head">Neueste Bestellungen<a class="s-link" href="/admin/bestellungen">Alle</a></div>
|
||||
<div class="s-table-wrap">
|
||||
<table class="s-table">
|
||||
<thead><tr><th>Bestellung</th><th>Kunde</th><th>Status</th><th class="num">Betrag</th></tr></thead>
|
||||
<tbody>
|
||||
{d.recentOrders.map((o) => (
|
||||
<tr class="clk" onclick={`location.href='/admin/bestellungen/${o.id}'`}>
|
||||
<td><b>{o.number}</b><div class="s-muted" style="font-size:12px">{fmtDate(o.created_at)}</div></td>
|
||||
<td>{o.customer_name || '—'}</td>
|
||||
<td><span class={`s-badge ${(statusMap[o.status]||['gray',o.status])[0]}`}>{(statusMap[o.status]||['',o.status])[1]}</span></td>
|
||||
<td class="num">{formatPrice(o.total_cents)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="s-card">
|
||||
<div class="s-card-head">Geringer Bestand</div>
|
||||
<div class="s-table-wrap">
|
||||
<table class="s-table">
|
||||
<thead><tr><th>Produkt</th><th class="num">Bestand</th></tr></thead>
|
||||
<tbody>
|
||||
{d.lowStock.length === 0 ? (<tr><td colspan="2" class="s-empty">Alles gut bestückt 👍</td></tr>) :
|
||||
d.lowStock.map((p) => (
|
||||
<tr class="clk" onclick={`location.href='/admin/produkte/${p.id}'`}>
|
||||
<td><div class="s-prodcell">{p.cardImage && <img src={p.cardImage} alt="" />}<span class="nm">{p.shortName || p.name}</span></div></td>
|
||||
<td class="num"><span class={`s-badge ${p.stock <= 10 ? 'red' : 'amber'}`}>{p.stock}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Admin>
|
||||
Reference in New Issue
Block a user