Skip to content

Commit

Permalink
Merge pull request #20 from kcsry/pos-embetter
Browse files Browse the repository at this point in the history
POS improvements
  • Loading branch information
akx authored Feb 6, 2024
2 parents 4eb05f1 + 29736de commit e0993c6
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 24 deletions.
68 changes: 65 additions & 3 deletions lippukala/static/lippukala/pos.css
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
html {
font:
36pt/1.2 system-ui,
32pt/1.2 system-ui,
sans-serif;
}

body {
overflow: hidden;
transition: background 0.5s;
margin: 0;
}

* {
Expand All @@ -23,15 +24,33 @@ body.code-unused {
background: #00c300;
}

body.code-localused {
body.code-used-not-synced {
background: #f95;
}

body.code-used-here {
background: cornflowerblue;
}

body.code-used {
background: #e51e1f;
color: yellow;
}

.statustext {
font-weight: bold;
transition: color 1s;
margin-bottom: 0.5em;
}

body.code-used .statustext {
animation: blink 1s infinite;
}

body.code-used-here .statustext {
color: #fff;
}

.cd {
font-family: consolas, monospace;
background: #000;
Expand All @@ -52,6 +71,12 @@ body.code-used {
color: #fff;
}

.check-year .product {
background: rgb(255, 255, 0, 0.8);
color: #000;
animation: blink 0.7s infinite;
}

.fulladdr {
font-size: 75%;
margin-top: 0.5em;
Expand All @@ -69,10 +94,11 @@ body.code-used {
text-align: center;
}

#code {
input#code {
width: 100%;
margin-bottom: 0.5rem;
padding: 0.25rem;
border: none;
}

::backdrop {
Expand Down Expand Up @@ -110,3 +136,39 @@ body.code-used {
background: orangered;
border-bottom-color: firebrick;
}

textarea#log {
position: absolute;
right: 5px;
bottom: 5px;
min-width: 30em;
height: 15em;
background: transparent;
border: none;
padding: 0;
font-size: 8pt;
opacity: 0.3;
transition:
height 0.2s,
opacity 0.2s;
}

textarea#log:hover {
opacity: 1;
height: 25em;
}

@keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
86 changes: 70 additions & 16 deletions lippukala/static/lippukala/pos.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* @typedef {Object} Code
* @property {number} id
* @property {boolean} used
* @property {string} code
* @property {string} prefix
* @property {string} lit
* @property {string|null} used_ts
* @property {number} id
* @property {string} [name]
* @property {string} code
* @property {string} comment
* @property {string} lit
* @property {string} prefix
* @property {string} prod
* @property {boolean} [localUsed]
*/

/**
Expand All @@ -22,12 +22,22 @@ let codeToConfirm = null;
/** @type {Record<number, Code>} */
const codes = {};

/**
* A set of all code IDs used locally (not persistent
* between reloads)
* @type {Set<number>}
*/
const codeIdsUsedLocally = new Set();

/** @type {number[]} */
let useQueue = [];

/** @type {number|null} */
let currentlyShownId = null;

/** The current year, as a string. */
const thisYearString = String(new Date().getFullYear());

/**
* @param {string} id ID or selector
* @returns {HTMLElement}
Expand All @@ -38,14 +48,21 @@ function $(id) {
return el;
}

function addLogEntry(string) {
const time = new Date().toLocaleTimeString("fi-FI");
const messageWithTime = `${time}: ${string}\n`;
const logTextArea = $("log");
logTextArea.value += messageWithTime;
logTextArea.scroll(0, 90000);
}

/**
* @param {CodesResponse} data
*/
function parseData(data) {
for (const code of data.codes) {
codes[code.id] = { ...(codes[code.id] || {}), ...code };
}
console.log(`Got ${data.codes.length} codes`);
}

async function download() {
Expand Down Expand Up @@ -74,6 +91,41 @@ function Tee(template, env) {
return template.replace(/\{(.+?)\}/g, (_, m) => escapeHtml(env[m] || "").replace(/\n/g, "<br>"));
}

/**
* @param code {Code}
*/
function isCodeLocallyUsed(code) {
return codeIdsUsedLocally.has(code.id);
}

function getCodeCSSClass(code) {
const isLocallyUsed = isCodeLocallyUsed(code);
if (code.used) {
return isLocallyUsed ? "code-used-here" : "code-used";
}
return isLocallyUsed ? "code-used-not-synced" : "code-unused";
}

function getCodeStatusText(code) {
const prettierTime = code.used_ts ? code.used_ts.replace(/T/, "\u2009") : "";
if (isCodeLocallyUsed(code)) {
return `Käytetty tässä ${prettierTime}`;
}
if (code.used) {
return `Käytetty ${prettierTime}`;
}
return "Ei käytetty";
}

/**
* Return if the code seems to be for a product that is not for this year.
* @param code {Code}
*/
function isPossiblyOldCode(code) {
const yearMatch = /\b(20\d{2})\b/g.exec(code.prod);
return yearMatch && yearMatch[1] !== thisYearString;
}

/**
* @param {Code|null} code
*/
Expand All @@ -82,13 +134,11 @@ function showCode(code) {
if (code) {
currentlyShownId = code.id;
statusDiv.innerHTML = Tee(
"<div class=cd><span class=pfx>{prefix}</span>{code}</div>{lit}<div class=product>{prod}</div><div class=addr>{short_name}<div class=fulladdr>{name}</div></div><div class=comment>{comment}</div>",
{ ...code, short_name: shortenName(code.name) },
"<div class=cd><span class=pfx>{prefix}</span>{code}</div>{lit}<div class=product>{prod}</div><div class=statustext>{statusText}</div></div><div class=addr>{short_name}<div class=fulladdr>{name}</div></div><div class=comment>{comment}</div>",
{ ...code, short_name: shortenName(code.name), statusText: getCodeStatusText(code) },
);
let cls = "code-unused";
if (code.used) cls = "code-used";
else if (code.localUsed) cls = "code-localused";
document.body.className = cls;
statusDiv.classList.toggle("check-year", isPossiblyOldCode(code));
document.body.className = getCodeCSSClass(code);
} else {
currentlyShownId = null;
statusDiv.innerHTML = "";
Expand All @@ -100,7 +150,7 @@ function showCode(code) {
* @param {Code} code
*/
function useCode(code) {
code.localUsed = true;
codeIdsUsedLocally.add(code.id);
useQueue.push(code.id);
showCode(code);
}
Expand Down Expand Up @@ -206,7 +256,7 @@ function cancelConfirm() {
}

async function syncUseQueue() {
useQueue = useQueue.filter((id) => codes[id].localUsed && !codes[id].used);
useQueue = useQueue.filter((id) => isCodeLocallyUsed(codes[id]) && !codes[id].used);
if (!useQueue.length) {
return;
}
Expand All @@ -217,9 +267,12 @@ async function syncUseQueue() {
method: "POST",
body: formData,
});
if (!resp.ok) throw new Error(`Failed to sync use queue`);
if (!resp.ok) {
addLogEntry("Koodien synkronointi epäonnistui");
throw new Error(`Failed to sync use queue`);
}
const data = await resp.json();
console.log(`successfully synced ${useQueue.length} code uses`);
addLogEntry(`Käytettiin ${useQueue.length} koodia`);
parseData(data);
if (currentlyShownId) showCode(codes[currentlyShownId]);
}
Expand All @@ -242,6 +295,7 @@ function debounce(fn, delay) {

window.init = async function init() {
await download();
addLogEntry(`${Object.keys(codes).length} koodia`);
setInterval(download, (50 + Math.random() * 20) * 1000);
setInterval(syncUseQueue, 5000);
$("#code").addEventListener("input", () => search(false), true);
Expand Down
1 change: 1 addition & 0 deletions lippukala/templates/lippukala/pos.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
<button id="cancel-button" onclick="cancelConfirm()">Peruuta</button>
</div>
</dialog>
<textarea id="log" readonly></textarea>
</body>
</html>
11 changes: 6 additions & 5 deletions lippukala/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
from lippukala.models import Code


def serialize_code(code):
def serialize_code(code: Code) -> dict:
return {
"id": code.id,
"used": bool(code.used_at),
"code": code.code,
"prefix": code.prefix,
"comment": code.order.comment,
"id": code.id,
"lit": code.literate_code,
"name": code.order.address_text,
"comment": code.order.comment,
"prefix": code.prefix,
"prod": code.product_text,
"used": code.is_used,
"used_ts": code.used_on.isoformat(timespec="minutes") if code.used_on else None,
}


Expand Down

0 comments on commit e0993c6

Please sign in to comment.