Compare commits
31 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afae2f9605 | ||
|
|
bca3c65ead | ||
|
|
d7976fa4b6 | ||
|
|
52e53330f6 | ||
|
|
4a2a52376f | ||
|
|
193703ff5a | ||
|
|
717624a922 | ||
|
|
7f2e4bf848 | ||
|
|
4ae196156b | ||
|
|
3bed47458f | ||
|
|
1c056d85ef | ||
|
|
f817f5b56f | ||
|
|
dab46b862d | ||
|
|
16a7f2ba79 | ||
|
|
30e7d5a164 | ||
|
|
50a262e549 | ||
|
|
9287bb9a2b | ||
|
|
f6cf4e5695 | ||
|
|
1b71fdb0ce | ||
|
|
01acc0617c | ||
|
|
aeef56e663 | ||
|
|
db06ec4504 | ||
|
|
c478e40ade | ||
|
|
3304a162ef | ||
|
|
b51a555392 | ||
|
|
aa921cc88d | ||
|
|
a47127b4fb | ||
|
|
47dc83fd55 | ||
|
|
d25bd44d15 | ||
|
|
26fb8073ed | ||
|
|
a50ba6f68a |
9 changed files with 438 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.vscode/
|
||||||
|
__pycache__/
|
||||||
|
venv/
|
||||||
18
app.py
Normal file
18
app.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
from flask import Flask, render_template
|
||||||
|
from markupsafe import escape
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
api_url = "https://api.toddlershop.yarnecoppens.com"
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def start():
|
||||||
|
return render_template('index.jinja', api_url=api_url)
|
||||||
|
|
||||||
|
@app.route("/pay_cash/<price>")
|
||||||
|
def pay_cash(price: int):
|
||||||
|
return render_template('paycash.jinja', price=escape(price), api_url=api_url)
|
||||||
|
|
||||||
|
@app.route("/pay_card/<price>")
|
||||||
|
def pay_card(price: int):
|
||||||
|
return render_template('paycard.jinja', price=escape(price), api_url=api_url)
|
||||||
7
requirements.txt
Normal file
7
requirements.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
blinker==1.8.2
|
||||||
|
click==8.1.7
|
||||||
|
Flask==3.0.3
|
||||||
|
itsdangerous==2.2.0
|
||||||
|
Jinja2==3.1.4
|
||||||
|
MarkupSafe==2.1.5
|
||||||
|
Werkzeug==3.0.4
|
||||||
BIN
static/favicon.ico
Normal file
BIN
static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 549 B |
161
static/scripts/main.js
Normal file
161
static/scripts/main.js
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
var all_products
|
||||||
|
|
||||||
|
let barcodeForm
|
||||||
|
|
||||||
|
const api_url = "https://api.toddlershop.yarnecoppens.com"
|
||||||
|
|
||||||
|
var to_fill_product_index = 0
|
||||||
|
|
||||||
|
var total_price = 0
|
||||||
|
|
||||||
|
async function makeAPIRequest(request) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(request);
|
||||||
|
const result = await response.json();
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCash(price) {
|
||||||
|
const cashAmountRequest = new Request(api_url + '/price_to_cash/' + price)
|
||||||
|
const cashAmount = await makeAPIRequest(cashAmountRequest)
|
||||||
|
|
||||||
|
const cash_bills_row = document.getElementById('cash_bills')
|
||||||
|
|
||||||
|
for (var bill_type in cashAmount){
|
||||||
|
bill_amount = cashAmount[bill_type]
|
||||||
|
console.log(bill_type, bill_amount)
|
||||||
|
for (var x = 0; x < bill_amount; x++){
|
||||||
|
const new_column = document.createElement('div')
|
||||||
|
new_column.classList.add('col')
|
||||||
|
const bill_image = document.createElement('img')
|
||||||
|
bill_image.classList.add('cash_image')
|
||||||
|
bill_image.src = api_url + '/icons/cash/' + bill_type
|
||||||
|
new_column.appendChild(bill_image)
|
||||||
|
|
||||||
|
cash_bills_row.appendChild(new_column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function indexOnLoad(){
|
||||||
|
// document.body.addEventListener('click', focusBarcodeInput, true);
|
||||||
|
loadProducts();
|
||||||
|
|
||||||
|
const bardcodeInputField = document.getElementById('barcode_form')
|
||||||
|
bardcodeInputField.focus()
|
||||||
|
|
||||||
|
bardcodeInputField.addEventListener('blur', function() {
|
||||||
|
setTimeout(() => {
|
||||||
|
bardcodeInputField.focus();
|
||||||
|
}, 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusBarcodeInput(){
|
||||||
|
document.getElementById('barcode_input').focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadProducts() {
|
||||||
|
const loadProductRequest = new Request(api_url + "/products")
|
||||||
|
all_products = await makeAPIRequest(loadProductRequest)
|
||||||
|
console.log("Loaded products:", all_products)
|
||||||
|
|
||||||
|
barcodeForm = document.getElementById("barcode_form");
|
||||||
|
|
||||||
|
barcodeForm.addEventListener("submit", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (to_fill_product_index < 5) {
|
||||||
|
|
||||||
|
const barcode = document.getElementById('barcode_input').value
|
||||||
|
var chosen_product
|
||||||
|
for (index = 0; index < all_products.length; index++) {
|
||||||
|
if (all_products[index].barcode == barcode) {
|
||||||
|
chosen_product = all_products[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
barcode_input.value = ""
|
||||||
|
|
||||||
|
const product_placeholders = document.getElementsByClassName('product_col')
|
||||||
|
const chosen_product_placeholder = product_placeholders[to_fill_product_index]
|
||||||
|
const image_product = chosen_product_placeholder.getElementsByClassName('product_image')[0]
|
||||||
|
const price_product = chosen_product_placeholder.getElementsByClassName('product_price')[0]
|
||||||
|
const total_price_holder = document.getElementById("totalprice")
|
||||||
|
|
||||||
|
total_price += chosen_product.price
|
||||||
|
|
||||||
|
image_product.setAttribute('src', api_url + "/icons/" + chosen_product.image_filename);
|
||||||
|
image_product.style.visibility = 'visible';
|
||||||
|
price_product.textContent = "\u20AC " + chosen_product.price
|
||||||
|
total_price_holder.textContent = "\u20AC " + total_price
|
||||||
|
|
||||||
|
to_fill_product_index += 1
|
||||||
|
}else{
|
||||||
|
barcode_input.value = ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeProduct(element_to_remove){
|
||||||
|
await umami.track('Remove Product');
|
||||||
|
const product_row = document.getElementById('product_row')
|
||||||
|
const products = product_row.children
|
||||||
|
|
||||||
|
let found_removed_product = false
|
||||||
|
|
||||||
|
for (var i = 0; i < products.length; i++) {
|
||||||
|
var product = products[i];
|
||||||
|
if (product.getElementsByClassName('product_image')[0] == element_to_remove){
|
||||||
|
found_removed_product = true
|
||||||
|
const total_price_holder = document.getElementById("totalprice")
|
||||||
|
total_price -= product.getElementsByClassName('product_price')[0].textContent.replace('€ ','')
|
||||||
|
console.log(product.getElementsByClassName('product_price')[0].textContent.replace('€ ',''))
|
||||||
|
|
||||||
|
total_price_holder.textContent = "\u20AC " + total_price
|
||||||
|
|
||||||
|
const price_holder = element_to_remove.parentNode.getElementsByClassName('product_price')[0]
|
||||||
|
price_holder.innerHTML = ''
|
||||||
|
}
|
||||||
|
if (found_removed_product){
|
||||||
|
if (i < to_fill_product_index - 1){
|
||||||
|
const next_product = products[i+1]
|
||||||
|
const current_price_holder = product.getElementsByClassName('product_price')[0]
|
||||||
|
const next_price_holder = next_product.getElementsByClassName('product_price')[0]
|
||||||
|
product.getElementsByClassName('product_image')[0].src = next_product.getElementsByClassName('product_image')[0].src
|
||||||
|
current_price_holder.innerHTML = next_price_holder.innerHTML
|
||||||
|
}else{
|
||||||
|
product.getElementsByClassName('product_image')[0].style.visibility = 'hidden';
|
||||||
|
const current_price_holder = product.getElementsByClassName('product_price')[0]
|
||||||
|
current_price_holder.innerHTML = ''
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
to_fill_product_index -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
async function payCash() {
|
||||||
|
await umami.track('Pay Cash button');
|
||||||
|
const total_cost = document.getElementById('totalprice').innerHTML.replace('€ ','')
|
||||||
|
window.location.href = "/pay_cash/" + total_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
async function payCard() {
|
||||||
|
await umami.track('Pay Card button');
|
||||||
|
const total_cost = document.getElementById('totalprice').innerHTML.replace('€ ','')
|
||||||
|
window.location.href = "/pay_card/" + total_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toMain() {
|
||||||
|
await umami.track('To Main button');
|
||||||
|
window.location.href = "/";
|
||||||
|
}
|
||||||
31
static/style/main.css
Normal file
31
static/style/main.css
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body{
|
||||||
|
background-color: #D9F2D0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.card{
|
||||||
|
background-color: #afda8e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#price_container {
|
||||||
|
background-color: white;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cash_bills{
|
||||||
|
background-color: #afda8e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cash_image{
|
||||||
|
float: left;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
98
templates/index.jinja
Normal file
98
templates/index.jinja
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html data-theme="light">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
<title>
|
||||||
|
Toddler Shop
|
||||||
|
</title>
|
||||||
|
<script src="{{url_for('static', filename='scripts/main.js')}}"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='style/main.css')}}">
|
||||||
|
<script defer src="https://umami.yarnecoppens.com/script.js" data-website-id="1d8c47c1-acee-47ff-a067-f5363a0514a2"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="indexOnLoad()" onclick="focusBarcodeInput()">
|
||||||
|
<form action="" id="barcode_form" style="opacity: 0;">
|
||||||
|
<input id="barcode_input" type="text" autofocus>
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<img class="product_image">
|
||||||
|
|
||||||
|
<div class="text-center" id="product_placeholder">
|
||||||
|
<div class="row" style="margin-bottom: 5rem;" id="product_row">
|
||||||
|
<div class="col product_col">
|
||||||
|
<div class="card h-100">
|
||||||
|
<input type="image" onclick="removeProduct(this)" class="card-img-top product_image" alt="">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title product_price"></h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col product_col">
|
||||||
|
<div class="card h-100">
|
||||||
|
<input type="image" onclick="removeProduct(this)" class="card-img-top product_image" alt="">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title product_price"></h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col product_col">
|
||||||
|
<div class="card h-100">
|
||||||
|
<input type="image" onclick="removeProduct(this)" class="card-img-top product_image" alt="">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title product_price"></h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col product_col">
|
||||||
|
<div class="card h-100">
|
||||||
|
<input type="image" onclick="removeProduct(this)" class="card-img-top product_image" alt="">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title product_price"></h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col product_col">
|
||||||
|
<div class="card h-100">
|
||||||
|
<input type="image" onclick="removeProduct(this)" class="card-img-top product_image" alt="">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title product_price"></h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="container text-center">
|
||||||
|
<div class="row" style="width: 70%;margin-left: auto;margin-right: auto;">
|
||||||
|
<div class="col">
|
||||||
|
<img src="{{api_url}}/icons/cart" height="100rem" width="100rem" class="card-img-top" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="col" id="price_container">
|
||||||
|
<h2 class="card-title" id="totalprice">€ 0</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="payCash()" src="{{api_url}}/icons/cash" height="100rem" width="100rem" class="card-img-top" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="payCard()" src="{{api_url}}/icons/creditcard" height="100rem" width="100rem" class="card-img-top" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
||||||
62
templates/paycard.jinja
Normal file
62
templates/paycard.jinja
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html data-theme="light">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
<title>
|
||||||
|
Toddler Shop
|
||||||
|
</title>
|
||||||
|
<script src="{{url_for('static', filename='scripts/main.js')}}"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='style/main.css')}}">
|
||||||
|
<script defer src="https://umami.yarnecoppens.com/script.js" data-website-id="1d8c47c1-acee-47ff-a067-f5363a0514a2"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="row m-5">
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="payCash()" src="{{api_url}}/icons/cash" height="100rem" width="100rem" class="card-img-top" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="container text-center product_placeholder">
|
||||||
|
<div class="row" style="width: 70%;margin-left: auto;margin-right: auto;">
|
||||||
|
<div class="col">
|
||||||
|
<img src="{{api_url}}/icons/cart" height="100rem" width="100rem" class="card-img-top" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="col" id="price_container">
|
||||||
|
<h2 class="card-title" id="totalprice">€ {{price}}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="payCard()" src="{{api_url}}/icons/creditcard" height="100rem" width="100rem" class="card-img-top" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-center m-5">
|
||||||
|
<div class="col-1">
|
||||||
|
<img src="{{api_url}}/icons/paycard" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-end m-5">
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="toMain()" src="{{api_url}}/icons/shop" height="100rem" width="100rem" class="card-img-top" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
||||||
58
templates/paycash.jinja
Normal file
58
templates/paycash.jinja
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html data-theme="light">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
<title>
|
||||||
|
Toddler Shop
|
||||||
|
</title>
|
||||||
|
<script src="{{url_for('static', filename='scripts/main.js')}}"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='style/main.css')}}">
|
||||||
|
<script defer src="https://umami.yarnecoppens.com/script.js" data-website-id="1d8c47c1-acee-47ff-a067-f5363a0514a2"></script>
|
||||||
|
</head>
|
||||||
|
<body onload="loadCash({{price}})">
|
||||||
|
|
||||||
|
<div class="row m-5">
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="payCash()" src="{{api_url}}/icons/cash" height="100rem" width="100rem" class="card-img-top" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="container text-center product_placeholder">
|
||||||
|
<div class="row" style="width: 70%;margin-left: auto;margin-right: auto;">
|
||||||
|
<div class="col">
|
||||||
|
<img src="{{api_url}}/icons/cart" height="100rem" width="100rem" class="card-img-top" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="col" id="price_container">
|
||||||
|
<h2 class="card-title" id="totalprice">€ {{price}}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="payCard()" src="{{api_url}}/icons/creditcard" height="100rem" width="100rem" class="card-img-top" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row m-5" id="cash_bills">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-end m-5">
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<input type="image" onclick="toMain()" src="{{api_url}}/icons/shop" height="100rem" width="100rem" class="card-img-top" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
Reference in a new issue