Compare commits

..

3 Commits

Author SHA1 Message Date
475690ca2b Minor bugfixes 2025-03-19 23:16:11 +01:00
03fec1ebd7 reports view 2025-03-19 23:15:28 +01:00
8cd011fc01 added old transactions to payup view 2025-03-19 23:15:12 +01:00
14 changed files with 105 additions and 12 deletions

View File

@ -164,7 +164,7 @@ async function patch(req: Request, res: Response) {
data: {
userId: value.user_id,
paid: value.paid,
// !!!!! paidAt: value.paid ? new Date() : undefined
paidAt: value.paid ? new Date() : undefined
},
select: {
id: true

View File

@ -4,6 +4,7 @@ import express from 'express';
import dashboard_Route from './dashboard.js';
import users from './users.js';
import products from './products.js';
import report from './report.js';
// Router base is '/admin'
const Router = express.Router({ strict: false });
@ -11,6 +12,7 @@ const Router = express.Router({ strict: false });
Router.route('/').get(dashboard_Route.get);
Router.route('/users').get(users.get);
Router.route('/products').get(products.get);
Router.route('/report').get(report.get);
// Router.route('/user_select').get(user_select_Route.get);
// Router.route('/product_select').get(product_select_Route.get);
// Router.route('/pay_up').get(pay_up_Route.get);

View File

@ -0,0 +1,7 @@
import express, { Request, Response } from 'express';
function get(req: Request, res: Response) {
res.render("admin/reports")
}
export default { get };

View File

@ -172,7 +172,7 @@ function getApiDescriptionByTable(tableName) {
}
}
function returnTableDataByTableName(tableName, search="", orderBy="asc", sort="", take=-1, skip=0) {
function returnTableDataByTableName(tableName, search="", orderBy="asc", sort="", take=-1, skip=0, filters=[]) {
var orderBy = orderBy.toLowerCase();
if(orderBy == "") {
orderBy = "asc";
@ -187,6 +187,10 @@ function returnTableDataByTableName(tableName, search="", orderBy="asc", sort=""
if(skip > 0) {
baseString += "&skip=" + skip;
}
filterKeys = Object.keys(filters);
for(var i = 0; i < filterKeys.length; i++) {
baseString += "&" + filterKeys[i] + "=" + filters[filterKeys[i]];
}
if (search && search.length > 0) {
return _api.get(baseString + '&search=' + search);
@ -214,6 +218,7 @@ async function getCountByTable(tableName, search="") {
function _testPageFail(reason) {
return;
document.getElementById('heroStatus').classList.remove('is-success');
document.getElementById('heroStatus').classList.add('is-danger');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 KiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -74,6 +74,9 @@ tables.forEach(async (table) => {
refreshTable(table);
});
});
if(table.getAttribute("data-loadmode") == "manual") {
return;
}
refreshTable(table);
});
@ -289,6 +292,7 @@ async function refreshTable(table) {
});
let order = '';
let column = '';
let filters = JSON.parse(table.getAttribute('data-filters')) || {};
ths.forEach((th) => {
if (th.hasAttribute('data-order')) {
order = th.getAttribute('data-order');
@ -317,7 +321,7 @@ async function refreshTable(table) {
if (searchField) {
const value = searchField.value;
const dbTable = table.getAttribute('data-dataSource');
const result = await returnTableDataByTableName(dbTable, value, order, column, take=maxLinesPerPage, skip= start);
const result = await returnTableDataByTableName(dbTable, value, order, column, take=maxLinesPerPage, skip=start, filters);
const totalResultCount = await getCountByTable(dbTable, value);
paginationPassOnPre['dataLength'] = totalResultCount;
var magMiddl = managePaginationMiddleware(result, paginationPassOnPre);
@ -326,7 +330,7 @@ async function refreshTable(table) {
clearTable(table);
writeDataToTable(table, data, paginationPassOn);
} else {
const result = await returnTableDataByTableName(table.getAttribute('data-dataSource'), undefined, order, column, take= maxLinesPerPage, skip= start);
const result = await returnTableDataByTableName(table.getAttribute('data-dataSource'), undefined, order, column, take= maxLinesPerPage, skip= start, filters);
const resultCount = await getCountByTable(table.getAttribute('data-dataSource'));
paginationPassOnPre['dataLength'] = resultCount;
var magMiddl = managePaginationMiddleware(result, paginationPassOnPre);
@ -510,8 +514,15 @@ function writeDataToTable(table, data, paginationPassOn) {
if(header.getAttribute('data-type') == "bool") {
td.innerHTML = row[column] ? '<i class="bi bi-check"></i>' : '<i class="bi bi-x"></i>';
} else {
} else if(header.getAttribute('data-type') == "datetime"){
if(row[column] == null) {
td.innerHTML = "";
} else {
td.innerHTML = formatTimestamp(row[column]);
}
}
else {
td.innerHTML = row[column];
}
tr.appendChild(td);

View File

@ -125,7 +125,7 @@ scannerField.addEventListener('keydown', async function(event) {
console.log('Barcode scanned:', barcode);
scannerField.value = "";
// createTemporaryNotification(`Barcode ${barcode} gescannt`, 'is-info');
waitingForScan = false;
// Check if barcode is in the database
let product = globalData.find(p => p.gtin == barcode);
@ -144,7 +144,7 @@ scannerField.addEventListener('keydown', async function(event) {
form_gtin.value = barcode;
}
// modal_stage_2_result.innerHTML = product ? `<i class="bi bi-check"></i> Produkt gefunden: ${product.name}` : `<i class="bi bi-x"></i> Produkt nicht gefunden`;
waitingForScan = false;
// let product = globalData.find(p => p.gtin == barcode);

View File

@ -11,6 +11,8 @@ const btn_paynow = document.getElementById("paynow");
const btn_confirm = document.getElementById("confirmCheckout");
const btn_logout = document.getElementById("logout");
const table_old = document.getElementById("alltransactions");
errorIfAnyUndefined([isEmptyAlert, tableDiv, payTable, tableCnt, tableSum, modal_sum])
// Current user
@ -20,6 +22,9 @@ if(cookieUser == undefined) {
createTemporaryNotification('Fehler: Nutzer nicht angemeldet.', 'is-danger');
window.location.href = '/user_select';
}
table_old.setAttribute('data-filters', `{"user_id": ${cookieUser}}`);
refreshTable(table_old);
console.log("Table refreshed");
let transactionIds = [];

View File

@ -216,7 +216,7 @@ scannerField.addEventListener('keydown', async function(event) {
let product = globalData.find(p => p.gtin == barcode);
if(product) {
let event = new Event('click');
createTemporaryNotification(`<i class="bi bi-upc-scan"></i> Barcode scan: GTIN ${barcode} gefunden`, 'is-success');
createTemporaryNotification(`<i class="bi bi-upc-scan"></i> Barcode scan: GTIN ${barcode} gefunden`, 'is-success', 2000);
document.getElementById(`product_${product.id}`).dispatchEvent(event);
} else {
createTemporaryNotification( `<i class="bi bi-upc-scan"></i> Barcode scan: GTIN ${barcode} nicht gefunden`, 'is-danger');

View File

@ -13,7 +13,7 @@
<a href="/admin/users" class="button is-large is-fullwidth is-primary">Benutzer</a>
</div>
<div class="column is-4">
<a href="/admin/reports" class="button is-large is-fullwidth is-primary">Berichte</a>
<a href="/admin/report" class="button is-large is-fullwidth is-primary">Berichte</a>
</div>
</div>

View File

@ -7,7 +7,7 @@
<button class="js-modal-trigger button" data-target="modal-js-example">
Neues Produkt anlegen
</button><button class="js-modal-trigger button" data-target="modal-restock" id="btn_restock">
Lager nachfüllen
Lager nachfüllen / Anpassen
</button><br></p>
<input class="input" type="text" data-searchTargetId="productTable" placeholder="Nach Produkt suchen.." />
@ -164,6 +164,8 @@
<h2 class="title">Scan erfolgreich - Produktmenge eingeben</h1>
<h3 class="subtitle" id="stage-2-amount">Aktuelle Menge: 0</h3>
<div class="buttons">
<button class="button is-info" onclick="restock(-1)">-1</button>
<button class="button is-info" onclick="restock(1)">+1</button>
<button class="button is-info" onclick="restock(6)">+6</button>
<button class="button is-info" onclick="restock(10)">+10</button>
<button class="button is-info" onclick="restock(12)">+12</button>

36
views/admin/reports.eta Normal file
View File

@ -0,0 +1,36 @@
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
<%~ include("partials/nav.eta") %>
<section class="section container" id="mainSelect">
<h1 class="title">Berichte</h1>
<!-- Big buttons linking to the different admin pages (Produkte, Benutzer, Bericht) -->
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">Benutzer</p>
<p class="title"><span data-dataSource="user" data-dataAction="COUNT" class="is-skeleton">Load.</span></p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Transaktionen</p>
<p class="title"><span data-dataSource="transaction" data-dataAction="COUNT" class="is-skeleton">Load.</span></p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Produkte</p>
<p class="title"><span data-dataSource="products" data-dataAction="COUNT" class="is-skeleton">Load.</span></p>
</div>
</div>
</nav>
<div class="columns is-centered">
</div>
</section>
<%~ include("partials/footer.eta") %>
<!-- <script src="/static/pages/admin_.js"></script>-->
<%~ include("partials/base_foot.eta") %>

View File

@ -32,6 +32,28 @@
<button class="button is-success is-large" id="paynow">Jetzt bezahlen <i class="bi bi-wallet2"></i></button>
</div>
<details>
<summary>Alle Transaktionen</summary>
<table class="table is-striped is-fullwidth is-hoverable" data-dataSource="transaction" data-pageSize="10" data-filters='{"user_id":-1}' data-loadmode="manual" id="alltransactions">
<thead>
<tr>
<th data-dataCol = "id">Id</th>
<th data-dataCol = "total">Name</th>
<th data-dataCol = "paid" data-type="bool">Bezahlt</th>
<th data-dataCol = "createdAt" data-type="datetime">Ausgestellt am</th>
<th data-dataCol = "paidAt" data-type="datetime" data-order="DESC">Bezahlt am</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<nav class="pagination is-hidden" role="navigation" aria-label="pagination" data-targetTable="alltransactions">
<ul class="pagination-list">
</ul>
</nav>
</details>
</section>
<!-- Confirmation modal -->
@ -56,6 +78,9 @@
</footer>
</div>
</div>
<%~ include("partials/footer.eta") %>
<script src="/static/pages/payup.js"></script>
<%~ include("partials/base_foot.eta") %>

View File

@ -11,7 +11,7 @@
</div>
<!-- Empty sidebar on the right -->
<div class="column is-one-quarter">
<h2 class="title is-4" >Ausgewählte Produkte</h2>
<h2 class="title is-4">Ausgewählte Produkte</h2>
<table class="table">
<thead>
<tr>