Steuerkram/src/Views/dashboard.php

187 lines
8.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use Steuerkram\View;
$h = fn($v) => View::h($v);
$fmt = fn($v) => View::formatMoney($v);
?>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Umsatz <?= $h($stats['year']) ?></div>
<div class="stat-value" style="color:var(--color-success)"><?= $fmt($stats['umsatz']) ?></div>
<div class="stat-meta">Betriebseinnahmen (netto)</div>
<div class="stat-meta" style="font-size:.75rem;margin-top:.25rem;color:var(--color-muted)">
<?= $h($stats['vormonat_label']) ?>: <?= $fmt($stats['umsatz_vormonat']) ?>
&nbsp;·&nbsp;
<?= $h($stats['month_label']) ?>: <?= $fmt($stats['umsatz_monat']) ?>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Ausgaben <?= $h($stats['year']) ?></div>
<div class="stat-value" style="color:var(--color-danger)"><?= $fmt($stats['ausgaben']) ?></div>
<div class="stat-meta">Betriebsausgaben (netto)</div>
<div class="stat-meta" style="font-size:.75rem;margin-top:.25rem;color:var(--color-muted)">
<?= $h($stats['vormonat_label']) ?>: <?= $fmt($stats['ausgaben_vormonat']) ?>
&nbsp;·&nbsp;
<?= $h($stats['month_label']) ?>: <?= $fmt($stats['ausgaben_monat']) ?>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Gewinn / Verlust <?= $h($stats['year']) ?></div>
<div class="stat-value" style="color:<?= $stats['gewinn'] >= 0 ? 'var(--color-success)' : 'var(--color-danger)' ?>">
<?= $fmt($stats['gewinn']) ?>
</div>
<div class="stat-meta">EÜR-Ergebnis</div>
<div class="stat-meta" style="font-size:.75rem;margin-top:.25rem;color:var(--color-muted)">
<?= $h($stats['vormonat_label']) ?>: <span style="color:<?= $stats['gewinn_vormonat'] >= 0 ? 'var(--color-success)' : 'var(--color-danger)' ?>"><?= $fmt($stats['gewinn_vormonat']) ?></span>
&nbsp;·&nbsp;
<?= $h($stats['month_label']) ?>: <span style="color:<?= $stats['gewinn_monat'] >= 0 ? 'var(--color-success)' : 'var(--color-danger)' ?>"><?= $fmt($stats['gewinn_monat']) ?></span>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Offene Rechnungen</div>
<div class="stat-value"><?= $h($stats['offene_rechnungen']) ?></div>
<div class="stat-meta"><?= $fmt($stats['offene_rechnungen_sum']) ?> ausstehend</div>
</div>
<div class="stat-card">
<div class="stat-label">Ungebuchte Belege</div>
<div class="stat-value" style="color:<?= $stats['ungebuchte_belege'] > 0 ? 'var(--color-warning)' : 'inherit' ?>">
<?= $h($stats['ungebuchte_belege']) ?>
</div>
<div class="stat-meta">
<?php if ($stats['ungebuchte_belege'] > 0): ?>
<a href="/belege?booked=0">Belege buchen →</a>
<?php else: ?>
Alle Belege gebucht
<?php endif; ?>
</div>
</div>
</div>
<!-- Drag & Drop Upload -->
<div id="dnd-zone" style="
border:2px dashed var(--color-border);
border-radius:var(--radius);
background:var(--color-surface);
padding:2rem;
text-align:center;
margin-bottom:1.5rem;
cursor:pointer;
transition:border-color .15s, background .15s;
position:relative;
">
<input type="file" id="dnd-file-input" accept=".pdf,.png,.jpg,.jpeg,.tif,.tiff,.xml"
style="position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%">
<div id="dnd-icon" style="font-size:2rem;line-height:1;margin-bottom:.5rem;pointer-events:none">📎</div>
<div style="font-weight:600;pointer-events:none">Beleg hierher ziehen oder klicken</div>
<div class="text-muted text-sm" style="pointer-events:none">PDF, PNG, JPEG, TIFF, XML · max. 20 MB</div>
<div id="dnd-status" style="margin-top:.75rem;min-height:1.5rem;pointer-events:none"></div>
</div>
<script>
(function () {
const zone = document.getElementById('dnd-zone');
const input = document.getElementById('dnd-file-input');
const status = document.getElementById('dnd-status');
const icon = document.getElementById('dnd-icon');
function setActive(on) {
zone.style.borderColor = on ? 'var(--color-primary)' : 'var(--color-border)';
zone.style.background = on ? '#eff6ff' : 'var(--color-surface)';
}
function setStatus(msg, color) {
status.innerHTML = `<span style="color:${color || 'var(--color-muted)'}">${msg}</span>`;
}
zone.addEventListener('dragover', (e) => { e.preventDefault(); setActive(true); });
zone.addEventListener('dragleave', (e) => { if (!zone.contains(e.relatedTarget)) setActive(false); });
zone.addEventListener('drop', (e) => {
e.preventDefault();
setActive(false);
const file = e.dataTransfer.files[0];
if (file) uploadFile(file);
});
input.addEventListener('change', () => {
if (input.files[0]) uploadFile(input.files[0]);
input.value = '';
});
function uploadFile(file) {
const allowed = ['application/pdf','image/png','image/jpeg','image/tiff',
'application/xml','text/xml'];
if (!allowed.includes(file.type) && !file.name.match(/\.(pdf|png|jpe?g|tif{1,2}|xml)$/i)) {
setStatus('Nicht unterstütztes Dateiformat.', 'var(--color-danger)');
return;
}
icon.textContent = '⏳';
setStatus('Wird hochgeladen…');
zone.style.pointerEvents = 'none';
const fd = new FormData();
fd.append('beleg_file', file);
fd.append('belegtyp', 'Eingangsrechnung');
fd.append('beleg_datum', new Date().toISOString().slice(0,10));
fetch('/belege/upload', {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
body: fd,
})
.then(async (res) => {
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Unbekannter Fehler');
icon.textContent = '✅';
setStatus(`${data.beleg_nr} hochgeladen – weiterleiten…`, 'var(--color-success)');
setTimeout(() => { window.location.href = '/belege/' + data.id; }, 600);
})
.catch((err) => {
icon.textContent = '📎';
zone.style.pointerEvents = '';
setStatus('Fehler: ' + err.message, 'var(--color-danger)');
});
}
})();
</script>
<!-- Letzte Buchungen -->
<div class="card">
<div class="card-header">
<div class="card-title">Letzte Buchungen</div>
<a href="/journal" class="btn btn-secondary btn-sm">Alle anzeigen →</a>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Datum</th>
<th>Belegnr.</th>
<th>Typ</th>
<th>Kategorie</th>
<th>Zahlungskonto</th>
<th>Buchungstext</th>
<th class="td-right">Brutto</th>
</tr>
</thead>
<tbody>
<?php foreach ($recentBookings as $b): ?>
<tr>
<td class="td-muted"><?= View::formatDate($b['buchungs_datum']) ?></td>
<td><a href="/belege/<?= $h($b['beleg_id']) ?>"><?= $h($b['beleg_nr']) ?></a></td>
<td><span class="badge <?= $b['buchungstyp'] === 'Einnahme' ? 'badge-green' : 'badge-red' ?>"><?= $h($b['buchungstyp']) ?></span></td>
<td class="td-mono"><?= $h($b['kategorie_nr']) ?> <span class="text-muted">– <?= $h($b['kategorie_name']) ?></span></td>
<td class="td-mono"><?= $h($b['zahlungs_nr']) ?> <span class="text-muted">– <?= $h($b['zahlungs_name']) ?></span></td>
<td><?= $h($b['buchungstext']) ?><?php if ($b['is_reversal']): ?> <span class="badge badge-red">Storno</span><?php endif; ?></td>
<td class="td-right td-mono"><?= $fmt($b['betrag_brutto']) ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($recentBookings)): ?>
<tr><td colspan="7" class="text-center text-muted" style="padding:2rem">Noch keine Buchungen vorhanden.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>