initial commit
This commit is contained in:
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
require_once "config/session.php";
|
||||
|
||||
if (!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true) {
|
||||
header("location: index.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
include('navbar.php');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ANJUMA • Rechnungen</title>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.10/css/dataTables.bootstrap5.min.css">
|
||||
<style>
|
||||
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
|
||||
@media (max-width: 768px){ .d-md-table-cell{display:none!important;} }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div>
|
||||
<h3 class="mb-0">Rechnungen</h3>
|
||||
</div>
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<button id="btnRefresh" class="btn btn-outline-secondary btn-sm">↻ Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-body">
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-12 col-md-3">
|
||||
<label class="form-label mb-1">Von</label>
|
||||
<input type="date" class="form-control" id="fromDate">
|
||||
</div>
|
||||
<div class="col-12 col-md-3">
|
||||
<label class="form-label mb-1">Bis</label>
|
||||
<input type="date" class="form-control" id="toDate">
|
||||
</div>
|
||||
<div class="col-12 col-md-6 d-flex gap-2">
|
||||
<button id="btnThisMonth" class="btn btn-outline-primary">Dieser Monat</button>
|
||||
<button id="btnThisYear" class="btn btn-outline-primary">Dieses Jahr</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="tblInv" class="table table-striped table-hover align-middle w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nr</th>
|
||||
<th>Datum</th>
|
||||
<th class="d-md-table-cell">Leistung</th>
|
||||
<th>Kunde</th>
|
||||
<th>Status</th>
|
||||
<th>Summe</th>
|
||||
<th>PDF</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="text-muted small mt-2" id="info"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/datatables.net@1.13.10/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.10/js/dataTables.bootstrap5.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script>
|
||||
const API_LIST = '/api/invoice_list.php';
|
||||
const PDF_URL = '/api/invoice_pdf.php?id=';
|
||||
|
||||
let dt;
|
||||
|
||||
function isoLocal(d){ const y=d.getFullYear(), m=String(d.getMonth()+1).padStart(2,'0'), day=String(d.getDate()).padStart(2,'0'); return `${y}-${m}-${day}`; }
|
||||
function thisMonthRange(){ const d=new Date(); return [isoLocal(new Date(d.getFullYear(), d.getMonth(), 1)), isoLocal(new Date(d.getFullYear(), d.getMonth()+1, 0))]; }
|
||||
function thisYearRange(){ const d=new Date(); return [isoLocal(new Date(d.getFullYear(), 0, 1)), isoLocal(new Date(d.getFullYear(), 11, 31))]; }
|
||||
function fmtDateDE(iso){ const [y,m,d]=String(iso||'').split('-'); return (y && m && d) ? `${d}.${m}.${y}` : ''; }
|
||||
function eur(n){ return (Number(n)||0).toLocaleString('de-DE',{style:'currency',currency:'EUR',maximumFractionDigits:0}); }
|
||||
|
||||
function statusBadge(s){
|
||||
s = String(s||'draft');
|
||||
if (s==='paid') return '<span class="badge text-bg-success">PAID</span>';
|
||||
if (s==='sent') return '<span class="badge text-bg-primary">SENT</span>';
|
||||
if (s==='canceled') return '<span class="badge text-bg-danger">CANCELED</span>';
|
||||
return '<span class="badge text-bg-secondary">DRAFT</span>';
|
||||
}
|
||||
|
||||
function pdfBtn(row){
|
||||
return `<a class="btn btn-outline-secondary btn-sm" href="${PDF_URL}${row.id}" target="_blank">PDF</a>`;
|
||||
}
|
||||
|
||||
async function fetchJSON(url){
|
||||
const res = await fetch(url, { cache:'no-store' });
|
||||
const text = await res.text();
|
||||
const json = JSON.parse(text);
|
||||
if (!json.ok) throw new Error(json.error || 'API error');
|
||||
return json;
|
||||
}
|
||||
|
||||
async function refresh(){
|
||||
const qs = new URLSearchParams({ from: $('#fromDate').val(), to: $('#toDate').val() }).toString();
|
||||
const json = await fetchJSON(`${API_LIST}?${qs}`);
|
||||
const items = json.items || [];
|
||||
dt.clear().rows.add(items).draw(false);
|
||||
$('#info').text(`Treffer: ${items.length} • Zeitraum: ${json.from} bis ${json.to}`);
|
||||
}
|
||||
|
||||
$(function(){
|
||||
const [f,t] = thisMonthRange();
|
||||
$('#fromDate').val(f); $('#toDate').val(t);
|
||||
|
||||
dt = $('#tblInv').DataTable({
|
||||
pageLength: 25,
|
||||
order: [[1,'desc']],
|
||||
columns: [
|
||||
{ data:'invoice_no', className:'mono' },
|
||||
{ data:'invoice_date', className:'mono', render:(d)=>fmtDateDE(d) },
|
||||
{ data:'service_date', className:'mono d-md-table-cell', render:(d)=>fmtDateDE(d) },
|
||||
{ data:'customer_name' },
|
||||
{ data:'status', render:(d)=>statusBadge(d) },
|
||||
{ data:'amount_net_eur', className:'mono', render:(d)=>eur(d) },
|
||||
{ data:null, orderable:false, searchable:false, render:(row)=>pdfBtn(row) },
|
||||
]
|
||||
});
|
||||
|
||||
$('#btnRefresh').on('click', refresh);
|
||||
$('#fromDate,#toDate').on('change', refresh);
|
||||
|
||||
$('#btnThisMonth').on('click', () => {
|
||||
const [a,b]=thisMonthRange(); $('#fromDate').val(a); $('#toDate').val(b); refresh();
|
||||
});
|
||||
$('#btnThisYear').on('click', () => {
|
||||
const [a,b]=thisYearRange(); $('#fromDate').val(a); $('#toDate').val(b); refresh();
|
||||
});
|
||||
|
||||
refresh();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user