158 lines
5.3 KiB
HTML
158 lines
5.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Product Changes</title>
|
|
<link rel="stylesheet" href="css/style.css">
|
|
<script src="js/jquery-3.5.1.min.js"></script>
|
|
<style>
|
|
#changesPanel { flex: 1; overflow: auto; }
|
|
#changesPanel th { position: sticky; top: 0; z-index: 1; }
|
|
#changesPanel td.num, #changesPanel th.num { text-align: right; }
|
|
.chg-new { color: #88ff88; font-weight: bold; }
|
|
.chg-cost { color: #ffcc44; font-weight: bold; }
|
|
</style>
|
|
</head>
|
|
<body class="archive-page">
|
|
|
|
<h1>
|
|
Product Changes
|
|
<a href="index.html">Home / Upload</a>
|
|
<a href="pending.html">Pending</a>
|
|
<a href="archive.html">Archive</a>
|
|
<a href="changes.html" class="active">Changes</a>
|
|
</h1>
|
|
|
|
<div id="filterPanel">
|
|
<form id="filterForm" onsubmit="return false">
|
|
<div class="filter-field">
|
|
<label>From</label>
|
|
<input type="date" id="fDateFrom">
|
|
</div>
|
|
<div class="filter-field">
|
|
<label>To</label>
|
|
<input type="date" id="fDateTo">
|
|
</div>
|
|
<div class="filter-field">
|
|
<label>Type</label>
|
|
<select id="fType">
|
|
<option value="">All</option>
|
|
<option value="NEW">New products</option>
|
|
<option value="COST">Cost changes</option>
|
|
</select>
|
|
</div>
|
|
<div class="filter-actions">
|
|
<button class="filter-btn primary" id="btnFilter" type="submit">Show</button>
|
|
<button class="filter-btn" id="btnReset" type="button">Last 7 days</button>
|
|
<span id="resultCount" class="result-count"></span>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div id="changesPanel">
|
|
<table class="list">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Type</th>
|
|
<th>UPC</th>
|
|
<th>Description</th>
|
|
<th>Vendor</th>
|
|
<th>Invoice #</th>
|
|
<th>PO #</th>
|
|
<th>Batch #</th>
|
|
<th class="num">Old Cost</th>
|
|
<th class="num">New Cost</th>
|
|
<th class="num">Retail</th>
|
|
<th class="num">Pack</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="changesBody"></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<script>
|
|
var CHANGES_LIMIT = 2000 // TOP 2000 in DataService.ProductChanges
|
|
|
|
$(document).ready(function () {
|
|
setLastWeek()
|
|
loadChanges()
|
|
$('#btnFilter').on('click', loadChanges)
|
|
$('#fType').on('change', loadChanges)
|
|
$('#btnReset').on('click', function () { setLastWeek(); loadChanges() })
|
|
})
|
|
|
|
function isoDate(d) {
|
|
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0')
|
|
}
|
|
|
|
function setLastWeek() {
|
|
var to = new Date(), from = new Date()
|
|
from.setDate(to.getDate() - 6)
|
|
$('#fDateFrom').val(isoDate(from))
|
|
$('#fDateTo').val(isoDate(to))
|
|
}
|
|
|
|
async function loadChanges() {
|
|
var params = new URLSearchParams({
|
|
date_from: $('#fDateFrom').val() || '',
|
|
date_to: $('#fDateTo').val() || '',
|
|
type: $('#fType').val() || ''
|
|
})
|
|
$('#resultCount').text('Loading...')
|
|
try {
|
|
var list = await (await fetch('/api/changes?' + params)).json()
|
|
if (list.error) throw new Error(list.error)
|
|
$('#dbWarning').hide()
|
|
render(list)
|
|
} catch (e) {
|
|
$('#resultCount').text('')
|
|
$('#changesBody').empty()
|
|
showDbError('Could not load changes: ' + e.message)
|
|
}
|
|
}
|
|
|
|
function render(list) {
|
|
var body = $('#changesBody').empty()
|
|
var nNew = 0, nCost = 0
|
|
list.forEach(function (r) {
|
|
if (r.type === 'NEW') nNew++; else if (r.type === 'COST') nCost++
|
|
body.append('<tr>' +
|
|
'<td>' + esc(r.date) + '</td>' +
|
|
'<td class="' + (r.type === 'NEW' ? 'chg-new' : 'chg-cost') + '">' + (r.type === 'NEW' ? 'New' : r.type === 'COST' ? 'Cost' : esc(r.type)) + '</td>' +
|
|
'<td>' + esc(r.upc) + '</td>' +
|
|
'<td>' + esc(r.description) + '</td>' +
|
|
'<td>' + esc(r.vendor_name || r.vendor) + '</td>' +
|
|
'<td>' + esc(r.invoice_number) + '</td>' +
|
|
'<td>' + esc(r.po_number) + '</td>' +
|
|
'<td>' + esc(r.batch_no) + '</td>' +
|
|
'<td class="num">' + money(r.old_cost) + '</td>' +
|
|
'<td class="num">' + money(r.new_cost) + '</td>' +
|
|
'<td class="num">' + money(r.retail) + '</td>' +
|
|
'<td class="num">' + (r.pack != null ? r.pack : '') + '</td>' +
|
|
'</tr>')
|
|
})
|
|
if (!list.length)
|
|
body.append('<tr><td colspan="12" style="color:#777;text-align:center;padding:12px">No changes in this range.</td></tr>')
|
|
$('#resultCount').text(list.length >= CHANGES_LIMIT
|
|
? 'Showing the newest ' + CHANGES_LIMIT + ' - narrow the dates to see the rest'
|
|
: list.length + ' change' + (list.length === 1 ? '' : 's') + ' - ' + nNew + ' new, ' + nCost + ' cost')
|
|
}
|
|
|
|
function money(v) {
|
|
return v != null && v !== '' ? '$' + parseFloat(v).toFixed(3).replace(/0$/, '') : ''
|
|
}
|
|
|
|
function showDbError(msg) {
|
|
var el = $('#dbWarning')
|
|
if (!el.length) el = $('<div id="dbWarning" class="db-warning"></div>').insertAfter('h1')
|
|
el.text(msg).show()
|
|
}
|
|
|
|
function esc(s) {
|
|
return String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
|
}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|