A wrapping client and label printing

For now, the server does the printing.
Later it should probably be split into a different service.
This commit is contained in:
Paul Mathieu 2022-09-07 19:08:49 +02:00
parent 0c9c893237
commit 235d9272ed
5 changed files with 203 additions and 13 deletions

45
client/wrap.html Normal file
View File

@ -0,0 +1,45 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>chikin wrap 0.1</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Compiled and minified JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="wrap.js"></script>
<style>
body {
display: flex;
min-height: 100vh;
flex-direction: column;
}
main {
flex: 1 0 auto;
}
</style>
</head>
<body>
<header class="page-header center amber brown-text">
<h1>Chikin wrap</h1>
</header>
<main class="container row">
<div class="col m6 offset-m3 s12">
<div id="appbody"></div>
</div>
</main>
<footer class="page-footer">
<div class="container row">
<span class="col right">displayed with recycled electrons.</span>
</div>
</footer>
</body>
</html>

102
client/wrap.js Normal file
View File

@ -0,0 +1,102 @@
const config = 'dev';
const backend_api = {
'prod': '/zetikettes/srv/',
'dev': 'http://scrawny.local:3000',
}[config];
const token = localStorage.getItem('token');
async function getBandz() {
const uri = `${backend_api}/bandz`;
const res = await $.ajax(uri, {headers: {Authorization: `Bearer ${token}`}});
return res.bandz;
}
async function wrapAChikin(req) {
const uri = `${backend_api}/wrap`;
const res = await $.ajax(uri, {
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(req),
headers: {Authorization: `Bearer ${token}`},
});
return res.chikinId;
}
function printLabel(chikinId) {
const uri = `${backend_api}/print`;
$.ajax(uri, {
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({chikinId}),
headers: {Authorization: `Bearer ${token}`},
});
}
$(document).ready(() => {
const appbody = $("#appbody");
const bandSelect = $('<div class="input-field col s12">')
.append(
$('<select>')
.append('<option disabled selected>Choose a band</option>')
)
.append('<label>Band</label>');
const block = $('<div class="section">');
block
.append(bandSelect)
.append($('<div class="input-field col s12">')
.append('<input type="text" class="datepicker">')
.append('<label>Date of wrapping</label>')
);
const loader = $('<div class="progress"><div class="indeterminate"></div></div>')
.hide();
const action = $('<div class="section">')
.append(
$('<a class="btn">wrap-a-chikin<i class="material-icons right">send</i></a>')
.click(async () => {
loader.show();
$('.btn').addClass("disabled");
const req = {
bandId: $('select').val(),
wrapDate: $('.datepicker').val(),
weight: $('#weight').val(),
};
try {
const chikinId = await wrapAChikin(req);
printLabel(chikinId);
} finally {
loader.hide();
$('#weight').val('');
$('.btn').removeClass('disabled');
}
})
);
block
.append(
$('<div class="input-field col s12">')
.append('<input type="text" inputmode="decimal" id="weight">')
.append('<label>weight (g)</label>')
)
.append(
$('<div class="input-field col s12">')
.append(action)
.append(loader)
)
;
appbody.append(block);
getBandz().then(bandz => {
$('select').append(bandz.map(band => `<option value="${band.id}">${band.name}</option>`));
$('select').formSelect();
});
$('select').formSelect();
$('.datepicker').datepicker({setDefaultDate: true, defaultDate: new Date()});
});

View File

@ -1,4 +1,6 @@
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const child_process = require('child_process');
const cors = require('cors');
const express = require('express'); const express = require('express');
require('express-async-errors'); require('express-async-errors');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
@ -28,9 +30,9 @@ CREATE TABLE IF NOT EXISTS Chikinz (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
bandId INTEGER NOT NULL, bandId INTEGER NOT NULL,
weight REAL NOT NULL, weight REAL NOT NULL,
killedDate DATE, killDate DATE,
wrappedDate DATE NOT NULL, wrapDate DATE NOT NULL,
soldDate DATE, sellDate DATE,
misc TEXT misc TEXT
);`); );`);
@ -38,17 +40,15 @@ makeTable(`
CREATE TABLE IF NOT EXISTS Bandz ( CREATE TABLE IF NOT EXISTS Bandz (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT, name TEXT,
receivedDate DATE, receiveDate DATE,
misc TEXT misc TEXT
);`); );`);
const app = express();
function addChikin(db, chikin) { function addChikin(db, chikin) {
const sql_insert = 'INSERT INTO Chikinz (bandId, weight, killedDate, wrappedDate) VALUES (?, ?, ?, ?);'; const sql_insert = 'INSERT INTO Chikinz (bandId, weight, killDate, wrapDate) VALUES (?, ?, ?, ?);';
const {bandId, weight, killedDate, wrappedDate} = chikin; const {bandId, weight, killDate, wrapDate} = chikin;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.run(sql_insert, [bandId, weight, killedDate, wrappedDate], function(err) { db.run(sql_insert, [bandId, weight, killDate, wrapDate], function(err) {
if (err) { if (err) {
return reject(err); return reject(err);
} }
@ -74,6 +74,17 @@ function getBandz(db) {
}); });
} }
function getChikin(db, chikinId) {
return new Promise((resolve, reject) => {
db.get('SELECT * from Chikinz where id=?', chikinId, (err, row) => {
if (err) {
return reject(err);
}
resolve(row);
});
});
}
const authenticateJWT = (req, res, next) => { const authenticateJWT = (req, res, next) => {
const authHeader = req.headers.authorization; const authHeader = req.headers.authorization;
@ -98,7 +109,10 @@ const authenticateJWT = (req, res, next) => {
} }
}; };
const app = express();
app.use(bodyParser.json()); app.use(bodyParser.json());
app.use(cors());
app.listen(3000, () => { app.listen(3000, () => {
console.log('Server started (http://localhost:3000/)!'); console.log('Server started (http://localhost:3000/)!');
@ -118,7 +132,6 @@ app.get('/bandz', authenticateJWT, async (req, res) => {
app.post('/toktok', (req, res) => { app.post('/toktok', (req, res) => {
const {password} = req.body; const {password} = req.body;
console.log(req.body);
if (password != 'goldchocoboisbestchocobo.goldchocoboisonlychocobo') { if (password != 'goldchocoboisbestchocobo.goldchocoboisonlychocobo') {
return res.json({message: 'This is not the way.'}); return res.json({message: 'This is not the way.'});
@ -134,9 +147,30 @@ app.post('/wrap', authenticateJWT, async (req, res) => {
const chikin = req.body; const chikin = req.body;
console.log(chikin); console.log(chikin);
const {lastID} = await addChikin(db, chikin); const {lastID} = await addChikin(db, chikin);
res.json({message: 'This is the way.', id: lastID}); res.json({message: 'This is the way.', chikinId: lastID});
}); });
app.post('/print', authenticateJWT, async (req, res) => {
const {chikinId} = req.body;
const qr_url = `http://lafermedumalpas.fr/chikinz/${chikinId}`;
const chikin = await getChikin(db, chikinId);
const line0 = 'Poulet fermier bio';
const line1 = `${chikin.weight.toFixed(2)} kg`;
const pricePerKg = 12;
const price = chikin.weight * pricePerKg;
const line2 = `${pricePerKg.toFixed(2)} €/kg - ${price.toFixed(2)}`;
const args = ['/Users/paul/scratch/printer/catprint.py',
'--template0', [qr_url, line0, line1, line2].join(';')];
const proc = child_process.spawn('python', args);
proc.stdout.on('data', data => console.log(`stdout: ${data}`));
proc.stderr.on('data', data => console.log(`stderr: ${data}`));
res.json({message: 'This is the way.'});
});
// catch errors // catch errors

View File

@ -6,6 +6,7 @@
"author": "Paul Mathieu <paul@ponteilla.net>", "author": "Paul Mathieu <paul@ponteilla.net>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cors": "^2.8.5",
"express": "^4.18.1", "express": "^4.18.1",
"express-async-errors": "^3.1.1", "express-async-errors": "^3.1.1",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",

View File

@ -266,6 +266,14 @@ cookie@0.5.0:
resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
debug@2.6.9: debug@2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
@ -980,7 +988,7 @@ npmlog@^6.0.0:
gauge "^4.0.3" gauge "^4.0.3"
set-blocking "^2.0.0" set-blocking "^2.0.0"
object-assign@^4.1.1: object-assign@^4, object-assign@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
@ -1352,7 +1360,7 @@ utils-merge@1.0.1:
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
vary@~1.1.2: vary@^1, vary@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==